##// END OF EJS Templates
sync with -stable
Thomas Arendsen Hein -
r4016:a195f11e merge default
parent child Browse files
Show More
@@ -1,515 +1,531 b''
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 On Unix, most of this file will be ignored if it doesn't belong
53 On Unix, most of this file will be ignored if it doesn't belong
54 to a trusted user or to a trusted group. See the documentation
54 to a trusted user or to a trusted group. See the documentation
55 for the trusted section below for more details.
55 for the trusted section below for more details.
56
56
57 SYNTAX
57 SYNTAX
58 ------
58 ------
59
59
60 A configuration file consists of sections, led by a "[section]" header
60 A configuration file consists of sections, led by a "[section]" header
61 and followed by "name: value" entries; "name=value" is also accepted.
61 and followed by "name: value" entries; "name=value" is also accepted.
62
62
63 [spam]
63 [spam]
64 eggs=ham
64 eggs=ham
65 green=
65 green=
66 eggs
66 eggs
67
67
68 Each line contains one entry. If the lines that follow are indented,
68 Each line contains one entry. If the lines that follow are indented,
69 they are treated as continuations of that entry.
69 they are treated as continuations of that entry.
70
70
71 Leading whitespace is removed from values. Empty lines are skipped.
71 Leading whitespace is removed from values. Empty lines are skipped.
72
72
73 The optional values can contain format strings which refer to other
73 The optional values can contain format strings which refer to other
74 values in the same section, or values in a special DEFAULT section.
74 values in the same section, or values in a special DEFAULT section.
75
75
76 Lines beginning with "#" or ";" are ignored and may be used to provide
76 Lines beginning with "#" or ";" are ignored and may be used to provide
77 comments.
77 comments.
78
78
79 SECTIONS
79 SECTIONS
80 --------
80 --------
81
81
82 This section describes the different sections that may appear in a
82 This section describes the different sections that may appear in a
83 Mercurial "hgrc" file, the purpose of each section, its possible
83 Mercurial "hgrc" file, the purpose of each section, its possible
84 keys, and their possible values.
84 keys, and their possible values.
85
85
86 decode/encode::
86 decode/encode::
87 Filters for transforming files on checkout/checkin. This would
87 Filters for transforming files on checkout/checkin. This would
88 typically be used for newline processing or other
88 typically be used for newline processing or other
89 localization/canonicalization of files.
89 localization/canonicalization of files.
90
90
91 Filters consist of a filter pattern followed by a filter command.
91 Filters consist of a filter pattern followed by a filter command.
92 Filter patterns are globs by default, rooted at the repository
92 Filter patterns are globs by default, rooted at the repository
93 root. For example, to match any file ending in ".txt" in the root
93 root. For example, to match any file ending in ".txt" in the root
94 directory only, use the pattern "*.txt". To match any file ending
94 directory only, use the pattern "*.txt". To match any file ending
95 in ".c" anywhere in the repository, use the pattern "**.c".
95 in ".c" anywhere in the repository, use the pattern "**.c".
96
96
97 The filter command can start with a specifier, either "pipe:" or
97 The filter command can start with a specifier, either "pipe:" or
98 "tempfile:". If no specifier is given, "pipe:" is used by default.
98 "tempfile:". If no specifier is given, "pipe:" is used by default.
99
99
100 A "pipe:" command must accept data on stdin and return the
100 A "pipe:" command must accept data on stdin and return the
101 transformed data on stdout.
101 transformed data on stdout.
102
102
103 Pipe example:
103 Pipe example:
104
104
105 [encode]
105 [encode]
106 # uncompress gzip files on checkin to improve delta compression
106 # uncompress gzip files on checkin to improve delta compression
107 # note: not necessarily a good idea, just an example
107 # note: not necessarily a good idea, just an example
108 *.gz = pipe: gunzip
108 *.gz = pipe: gunzip
109
109
110 [decode]
110 [decode]
111 # recompress gzip files when writing them to the working dir (we
111 # recompress gzip files when writing them to the working dir (we
112 # can safely omit "pipe:", because it's the default)
112 # can safely omit "pipe:", because it's the default)
113 *.gz = gzip
113 *.gz = gzip
114
114
115 A "tempfile:" command is a template. The string INFILE is replaced
115 A "tempfile:" command is a template. The string INFILE is replaced
116 with the name of a temporary file that contains the data to be
116 with the name of a temporary file that contains the data to be
117 filtered by the command. The string OUTFILE is replaced with the
117 filtered by the command. The string OUTFILE is replaced with the
118 name of an empty temporary file, where the filtered data must be
118 name of an empty temporary file, where the filtered data must be
119 written by the command.
119 written by the command.
120
120
121 NOTE: the tempfile mechanism is recommended for Windows systems,
121 NOTE: the tempfile mechanism is recommended for Windows systems,
122 where the standard shell I/O redirection operators often have
122 where the standard shell I/O redirection operators often have
123 strange effects. In particular, if you are doing line ending
123 strange effects. In particular, if you are doing line ending
124 conversion on Windows using the popular dos2unix and unix2dos
124 conversion on Windows using the popular dos2unix and unix2dos
125 programs, you *must* use the tempfile mechanism, as using pipes will
125 programs, you *must* use the tempfile mechanism, as using pipes will
126 corrupt the contents of your files.
126 corrupt the contents of your files.
127
127
128 Tempfile example:
128 Tempfile example:
129
129
130 [encode]
130 [encode]
131 # convert files to unix line ending conventions on checkin
131 # convert files to unix line ending conventions on checkin
132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
132 **.txt = tempfile: dos2unix -n INFILE OUTFILE
133
133
134 [decode]
134 [decode]
135 # convert files to windows line ending conventions when writing
135 # convert files to windows line ending conventions when writing
136 # them to the working dir
136 # them to the working dir
137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
137 **.txt = tempfile: unix2dos -n INFILE OUTFILE
138
138
139 defaults::
139 defaults::
140 Use the [defaults] section to define command defaults, i.e. the
140 Use the [defaults] section to define command defaults, i.e. the
141 default options/arguments to pass to the specified commands.
141 default options/arguments to pass to the specified commands.
142
142
143 The following example makes 'hg log' run in verbose mode, and
143 The following example makes 'hg log' run in verbose mode, and
144 'hg status' show only the modified files, by default.
144 'hg status' show only the modified files, by default.
145
145
146 [defaults]
146 [defaults]
147 log = -v
147 log = -v
148 status = -m
148 status = -m
149
149
150 The actual commands, instead of their aliases, must be used when
150 The actual commands, instead of their aliases, must be used when
151 defining command defaults. The command defaults will also be
151 defining command defaults. The command defaults will also be
152 applied to the aliases of the commands defined.
152 applied to the aliases of the commands defined.
153
153
154 diff::
155 Settings used when displaying diffs. They are all boolean and
156 defaults to False.
157 git;;
158 Use git extended diff format.
159 nodates;;
160 Don't include dates in diff headers.
161 showfunc;;
162 Show which function each change is in.
163 ignorews;;
164 Ignore white space when comparing lines.
165 ignorewsamount;;
166 Ignore changes in the amount of white space.
167 ignoreblanklines;;
168 Ignore changes whose lines are all blank.
169
154 email::
170 email::
155 Settings for extensions that send email messages.
171 Settings for extensions that send email messages.
156 from;;
172 from;;
157 Optional. Email address to use in "From" header and SMTP envelope
173 Optional. Email address to use in "From" header and SMTP envelope
158 of outgoing messages.
174 of outgoing messages.
159 to;;
175 to;;
160 Optional. Comma-separated list of recipients' email addresses.
176 Optional. Comma-separated list of recipients' email addresses.
161 cc;;
177 cc;;
162 Optional. Comma-separated list of carbon copy recipients'
178 Optional. Comma-separated list of carbon copy recipients'
163 email addresses.
179 email addresses.
164 bcc;;
180 bcc;;
165 Optional. Comma-separated list of blind carbon copy
181 Optional. Comma-separated list of blind carbon copy
166 recipients' email addresses. Cannot be set interactively.
182 recipients' email addresses. Cannot be set interactively.
167 method;;
183 method;;
168 Optional. Method to use to send email messages. If value is
184 Optional. Method to use to send email messages. If value is
169 "smtp" (default), use SMTP (see section "[smtp]" for
185 "smtp" (default), use SMTP (see section "[smtp]" for
170 configuration). Otherwise, use as name of program to run that
186 configuration). Otherwise, use as name of program to run that
171 acts like sendmail (takes "-f" option for sender, list of
187 acts like sendmail (takes "-f" option for sender, list of
172 recipients on command line, message on stdin). Normally, setting
188 recipients on command line, message on stdin). Normally, setting
173 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
189 this to "sendmail" or "/usr/sbin/sendmail" is enough to use
174 sendmail to send messages.
190 sendmail to send messages.
175
191
176 Email example:
192 Email example:
177
193
178 [email]
194 [email]
179 from = Joseph User <joe.user@example.com>
195 from = Joseph User <joe.user@example.com>
180 method = /usr/sbin/sendmail
196 method = /usr/sbin/sendmail
181
197
182 extensions::
198 extensions::
183 Mercurial has an extension mechanism for adding new features. To
199 Mercurial has an extension mechanism for adding new features. To
184 enable an extension, create an entry for it in this section.
200 enable an extension, create an entry for it in this section.
185
201
186 If you know that the extension is already in Python's search path,
202 If you know that the extension is already in Python's search path,
187 you can give the name of the module, followed by "=", with nothing
203 you can give the name of the module, followed by "=", with nothing
188 after the "=".
204 after the "=".
189
205
190 Otherwise, give a name that you choose, followed by "=", followed by
206 Otherwise, give a name that you choose, followed by "=", followed by
191 the path to the ".py" file (including the file name extension) that
207 the path to the ".py" file (including the file name extension) that
192 defines the extension.
208 defines the extension.
193
209
194 Example for ~/.hgrc:
210 Example for ~/.hgrc:
195
211
196 [extensions]
212 [extensions]
197 # (the mq extension will get loaded from mercurial's path)
213 # (the mq extension will get loaded from mercurial's path)
198 hgext.mq =
214 hgext.mq =
199 # (this extension will get loaded from the file specified)
215 # (this extension will get loaded from the file specified)
200 myfeature = ~/.hgext/myfeature.py
216 myfeature = ~/.hgext/myfeature.py
201
217
202 hooks::
218 hooks::
203 Commands or Python functions that get automatically executed by
219 Commands or Python functions that get automatically executed by
204 various actions such as starting or finishing a commit. Multiple
220 various actions such as starting or finishing a commit. Multiple
205 hooks can be run for the same action by appending a suffix to the
221 hooks can be run for the same action by appending a suffix to the
206 action. Overriding a site-wide hook can be done by changing its
222 action. Overriding a site-wide hook can be done by changing its
207 value or setting it to an empty string.
223 value or setting it to an empty string.
208
224
209 Example .hg/hgrc:
225 Example .hg/hgrc:
210
226
211 [hooks]
227 [hooks]
212 # do not use the site-wide hook
228 # do not use the site-wide hook
213 incoming =
229 incoming =
214 incoming.email = /my/email/hook
230 incoming.email = /my/email/hook
215 incoming.autobuild = /my/build/hook
231 incoming.autobuild = /my/build/hook
216
232
217 Most hooks are run with environment variables set that give added
233 Most hooks are run with environment variables set that give added
218 useful information. For each hook below, the environment variables
234 useful information. For each hook below, the environment variables
219 it is passed are listed with names of the form "$HG_foo".
235 it is passed are listed with names of the form "$HG_foo".
220
236
221 changegroup;;
237 changegroup;;
222 Run after a changegroup has been added via push, pull or
238 Run after a changegroup has been added via push, pull or
223 unbundle. ID of the first new changeset is in $HG_NODE. URL from
239 unbundle. ID of the first new changeset is in $HG_NODE. URL from
224 which changes came is in $HG_URL.
240 which changes came is in $HG_URL.
225 commit;;
241 commit;;
226 Run after a changeset has been created in the local repository.
242 Run after a changeset has been created in the local repository.
227 ID of the newly created changeset is in $HG_NODE. Parent
243 ID of the newly created changeset is in $HG_NODE. Parent
228 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
244 changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
229 incoming;;
245 incoming;;
230 Run after a changeset has been pulled, pushed, or unbundled into
246 Run after a changeset has been pulled, pushed, or unbundled into
231 the local repository. The ID of the newly arrived changeset is in
247 the local repository. The ID of the newly arrived changeset is in
232 $HG_NODE. URL that was source of changes came is in $HG_URL.
248 $HG_NODE. URL that was source of changes came is in $HG_URL.
233 outgoing;;
249 outgoing;;
234 Run after sending changes from local repository to another. ID of
250 Run after sending changes from local repository to another. ID of
235 first changeset sent is in $HG_NODE. Source of operation is in
251 first changeset sent is in $HG_NODE. Source of operation is in
236 $HG_SOURCE; see "preoutgoing" hook for description.
252 $HG_SOURCE; see "preoutgoing" hook for description.
237 prechangegroup;;
253 prechangegroup;;
238 Run before a changegroup is added via push, pull or unbundle.
254 Run before a changegroup is added via push, pull or unbundle.
239 Exit status 0 allows the changegroup to proceed. Non-zero status
255 Exit status 0 allows the changegroup to proceed. Non-zero status
240 will cause the push, pull or unbundle to fail. URL from which
256 will cause the push, pull or unbundle to fail. URL from which
241 changes will come is in $HG_URL.
257 changes will come is in $HG_URL.
242 precommit;;
258 precommit;;
243 Run before starting a local commit. Exit status 0 allows the
259 Run before starting a local commit. Exit status 0 allows the
244 commit to proceed. Non-zero status will cause the commit to fail.
260 commit to proceed. Non-zero status will cause the commit to fail.
245 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
261 Parent changeset IDs are in $HG_PARENT1 and $HG_PARENT2.
246 preoutgoing;;
262 preoutgoing;;
247 Run before computing changes to send from the local repository to
263 Run before computing changes to send from the local repository to
248 another. Non-zero status will cause failure. This lets you
264 another. Non-zero status will cause failure. This lets you
249 prevent pull over http or ssh. Also prevents against local pull,
265 prevent pull over http or ssh. Also prevents against local pull,
250 push (outbound) or bundle commands, but not effective, since you
266 push (outbound) or bundle commands, but not effective, since you
251 can just copy files instead then. Source of operation is in
267 can just copy files instead then. Source of operation is in
252 $HG_SOURCE. If "serve", operation is happening on behalf of
268 $HG_SOURCE. If "serve", operation is happening on behalf of
253 remote ssh or http repository. If "push", "pull" or "bundle",
269 remote ssh or http repository. If "push", "pull" or "bundle",
254 operation is happening on behalf of repository on same system.
270 operation is happening on behalf of repository on same system.
255 pretag;;
271 pretag;;
256 Run before creating a tag. Exit status 0 allows the tag to be
272 Run before creating a tag. Exit status 0 allows the tag to be
257 created. Non-zero status will cause the tag to fail. ID of
273 created. Non-zero status will cause the tag to fail. ID of
258 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
274 changeset to tag is in $HG_NODE. Name of tag is in $HG_TAG. Tag
259 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
275 is local if $HG_LOCAL=1, in repo if $HG_LOCAL=0.
260 pretxnchangegroup;;
276 pretxnchangegroup;;
261 Run after a changegroup has been added via push, pull or unbundle,
277 Run after a changegroup has been added via push, pull or unbundle,
262 but before the transaction has been committed. Changegroup is
278 but before the transaction has been committed. Changegroup is
263 visible to hook program. This lets you validate incoming changes
279 visible to hook program. This lets you validate incoming changes
264 before accepting them. Passed the ID of the first new changeset
280 before accepting them. Passed the ID of the first new changeset
265 in $HG_NODE. Exit status 0 allows the transaction to commit.
281 in $HG_NODE. Exit status 0 allows the transaction to commit.
266 Non-zero status will cause the transaction to be rolled back and
282 Non-zero status will cause the transaction to be rolled back and
267 the push, pull or unbundle will fail. URL that was source of
283 the push, pull or unbundle will fail. URL that was source of
268 changes is in $HG_URL.
284 changes is in $HG_URL.
269 pretxncommit;;
285 pretxncommit;;
270 Run after a changeset has been created but the transaction not yet
286 Run after a changeset has been created but the transaction not yet
271 committed. Changeset is visible to hook program. This lets you
287 committed. Changeset is visible to hook program. This lets you
272 validate commit message and changes. Exit status 0 allows the
288 validate commit message and changes. Exit status 0 allows the
273 commit to proceed. Non-zero status will cause the transaction to
289 commit to proceed. Non-zero status will cause the transaction to
274 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
290 be rolled back. ID of changeset is in $HG_NODE. Parent changeset
275 IDs are in $HG_PARENT1 and $HG_PARENT2.
291 IDs are in $HG_PARENT1 and $HG_PARENT2.
276 preupdate;;
292 preupdate;;
277 Run before updating the working directory. Exit status 0 allows
293 Run before updating the working directory. Exit status 0 allows
278 the update to proceed. Non-zero status will prevent the update.
294 the update to proceed. Non-zero status will prevent the update.
279 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
295 Changeset ID of first new parent is in $HG_PARENT1. If merge, ID
280 of second new parent is in $HG_PARENT2.
296 of second new parent is in $HG_PARENT2.
281 tag;;
297 tag;;
282 Run after a tag is created. ID of tagged changeset is in
298 Run after a tag is created. ID of tagged changeset is in
283 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
299 $HG_NODE. Name of tag is in $HG_TAG. Tag is local if
284 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
300 $HG_LOCAL=1, in repo if $HG_LOCAL=0.
285 update;;
301 update;;
286 Run after updating the working directory. Changeset ID of first
302 Run after updating the working directory. Changeset ID of first
287 new parent is in $HG_PARENT1. If merge, ID of second new parent
303 new parent is in $HG_PARENT1. If merge, ID of second new parent
288 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
304 is in $HG_PARENT2. If update succeeded, $HG_ERROR=0. If update
289 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
305 failed (e.g. because conflicts not resolved), $HG_ERROR=1.
290
306
291 Note: In earlier releases, the names of hook environment variables
307 Note: In earlier releases, the names of hook environment variables
292 did not have a "HG_" prefix. The old unprefixed names are no longer
308 did not have a "HG_" prefix. The old unprefixed names are no longer
293 provided in the environment.
309 provided in the environment.
294
310
295 The syntax for Python hooks is as follows:
311 The syntax for Python hooks is as follows:
296
312
297 hookname = python:modulename.submodule.callable
313 hookname = python:modulename.submodule.callable
298
314
299 Python hooks are run within the Mercurial process. Each hook is
315 Python hooks are run within the Mercurial process. Each hook is
300 called with at least three keyword arguments: a ui object (keyword
316 called with at least three keyword arguments: a ui object (keyword
301 "ui"), a repository object (keyword "repo"), and a "hooktype"
317 "ui"), a repository object (keyword "repo"), and a "hooktype"
302 keyword that tells what kind of hook is used. Arguments listed as
318 keyword that tells what kind of hook is used. Arguments listed as
303 environment variables above are passed as keyword arguments, with no
319 environment variables above are passed as keyword arguments, with no
304 "HG_" prefix, and names in lower case.
320 "HG_" prefix, and names in lower case.
305
321
306 If a Python hook returns a "true" value or raises an exception, this
322 If a Python hook returns a "true" value or raises an exception, this
307 is treated as failure of the hook.
323 is treated as failure of the hook.
308
324
309 http_proxy::
325 http_proxy::
310 Used to access web-based Mercurial repositories through a HTTP
326 Used to access web-based Mercurial repositories through a HTTP
311 proxy.
327 proxy.
312 host;;
328 host;;
313 Host name and (optional) port of the proxy server, for example
329 Host name and (optional) port of the proxy server, for example
314 "myproxy:8000".
330 "myproxy:8000".
315 no;;
331 no;;
316 Optional. Comma-separated list of host names that should bypass
332 Optional. Comma-separated list of host names that should bypass
317 the proxy.
333 the proxy.
318 passwd;;
334 passwd;;
319 Optional. Password to authenticate with at the proxy server.
335 Optional. Password to authenticate with at the proxy server.
320 user;;
336 user;;
321 Optional. User name to authenticate with at the proxy server.
337 Optional. User name to authenticate with at the proxy server.
322
338
323 smtp::
339 smtp::
324 Configuration for extensions that need to send email messages.
340 Configuration for extensions that need to send email messages.
325 host;;
341 host;;
326 Host name of mail server, e.g. "mail.example.com".
342 Host name of mail server, e.g. "mail.example.com".
327 port;;
343 port;;
328 Optional. Port to connect to on mail server. Default: 25.
344 Optional. Port to connect to on mail server. Default: 25.
329 tls;;
345 tls;;
330 Optional. Whether to connect to mail server using TLS. True or
346 Optional. Whether to connect to mail server using TLS. True or
331 False. Default: False.
347 False. Default: False.
332 username;;
348 username;;
333 Optional. User name to authenticate to SMTP server with.
349 Optional. User name to authenticate to SMTP server with.
334 If username is specified, password must also be specified.
350 If username is specified, password must also be specified.
335 Default: none.
351 Default: none.
336 password;;
352 password;;
337 Optional. Password to authenticate to SMTP server with.
353 Optional. Password to authenticate to SMTP server with.
338 If username is specified, password must also be specified.
354 If username is specified, password must also be specified.
339 Default: none.
355 Default: none.
340 local_hostname;;
356 local_hostname;;
341 Optional. It's the hostname that the sender can use to identify itself
357 Optional. It's the hostname that the sender can use to identify itself
342 to the MTA.
358 to the MTA.
343
359
344 paths::
360 paths::
345 Assigns symbolic names to repositories. The left side is the
361 Assigns symbolic names to repositories. The left side is the
346 symbolic name, and the right gives the directory or URL that is the
362 symbolic name, and the right gives the directory or URL that is the
347 location of the repository. Default paths can be declared by
363 location of the repository. Default paths can be declared by
348 setting the following entries.
364 setting the following entries.
349 default;;
365 default;;
350 Directory or URL to use when pulling if no source is specified.
366 Directory or URL to use when pulling if no source is specified.
351 Default is set to repository from which the current repository
367 Default is set to repository from which the current repository
352 was cloned.
368 was cloned.
353 default-push;;
369 default-push;;
354 Optional. Directory or URL to use when pushing if no destination
370 Optional. Directory or URL to use when pushing if no destination
355 is specified.
371 is specified.
356
372
357 server::
373 server::
358 Controls generic server settings.
374 Controls generic server settings.
359 uncompressed;;
375 uncompressed;;
360 Whether to allow clients to clone a repo using the uncompressed
376 Whether to allow clients to clone a repo using the uncompressed
361 streaming protocol. This transfers about 40% more data than a
377 streaming protocol. This transfers about 40% more data than a
362 regular clone, but uses less memory and CPU on both server and
378 regular clone, but uses less memory and CPU on both server and
363 client. Over a LAN (100Mbps or better) or a very fast WAN, an
379 client. Over a LAN (100Mbps or better) or a very fast WAN, an
364 uncompressed streaming clone is a lot faster (~10x) than a regular
380 uncompressed streaming clone is a lot faster (~10x) than a regular
365 clone. Over most WAN connections (anything slower than about
381 clone. Over most WAN connections (anything slower than about
366 6Mbps), uncompressed streaming is slower, because of the extra
382 6Mbps), uncompressed streaming is slower, because of the extra
367 data transfer overhead. Default is False.
383 data transfer overhead. Default is False.
368
384
369 trusted::
385 trusted::
370 For security reasons, Mercurial will not use the settings in
386 For security reasons, Mercurial will not use the settings in
371 the .hg/hgrc file from a repository if it doesn't belong to a
387 the .hg/hgrc file from a repository if it doesn't belong to a
372 trusted user or to a trusted group. The main exception is the
388 trusted user or to a trusted group. The main exception is the
373 web interface, which automatically uses some safe settings, since
389 web interface, which automatically uses some safe settings, since
374 it's common to serve repositories from different users.
390 it's common to serve repositories from different users.
375
391
376 This section specifies what users and groups are trusted. The
392 This section specifies what users and groups are trusted. The
377 current user is always trusted. To trust everybody, list a user
393 current user is always trusted. To trust everybody, list a user
378 or a group with name "*".
394 or a group with name "*".
379
395
380 users;;
396 users;;
381 Comma-separated list of trusted users.
397 Comma-separated list of trusted users.
382 groups;;
398 groups;;
383 Comma-separated list of trusted groups.
399 Comma-separated list of trusted groups.
384
400
385 ui::
401 ui::
386 User interface controls.
402 User interface controls.
387 debug;;
403 debug;;
388 Print debugging information. True or False. Default is False.
404 Print debugging information. True or False. Default is False.
389 editor;;
405 editor;;
390 The editor to use during a commit. Default is $EDITOR or "vi".
406 The editor to use during a commit. Default is $EDITOR or "vi".
391 fallbackencoding;;
407 fallbackencoding;;
392 Encoding to try if it's not possible to decode the changelog using
408 Encoding to try if it's not possible to decode the changelog using
393 UTF-8. Default is ISO-8859-1.
409 UTF-8. Default is ISO-8859-1.
394 ignore;;
410 ignore;;
395 A file to read per-user ignore patterns from. This file should be in
411 A file to read per-user ignore patterns from. This file should be in
396 the same format as a repository-wide .hgignore file. This option
412 the same format as a repository-wide .hgignore file. This option
397 supports hook syntax, so if you want to specify multiple ignore
413 supports hook syntax, so if you want to specify multiple ignore
398 files, you can do so by setting something like
414 files, you can do so by setting something like
399 "ignore.other = ~/.hgignore2". For details of the ignore file
415 "ignore.other = ~/.hgignore2". For details of the ignore file
400 format, see the hgignore(5) man page.
416 format, see the hgignore(5) man page.
401 interactive;;
417 interactive;;
402 Allow to prompt the user. True or False. Default is True.
418 Allow to prompt the user. True or False. Default is True.
403 logtemplate;;
419 logtemplate;;
404 Template string for commands that print changesets.
420 Template string for commands that print changesets.
405 style;;
421 style;;
406 Name of style to use for command output.
422 Name of style to use for command output.
407 merge;;
423 merge;;
408 The conflict resolution program to use during a manual merge.
424 The conflict resolution program to use during a manual merge.
409 Default is "hgmerge".
425 Default is "hgmerge".
410 quiet;;
426 quiet;;
411 Reduce the amount of output printed. True or False. Default is False.
427 Reduce the amount of output printed. True or False. Default is False.
412 remotecmd;;
428 remotecmd;;
413 remote command to use for clone/push/pull operations. Default is 'hg'.
429 remote command to use for clone/push/pull operations. Default is 'hg'.
414 ssh;;
430 ssh;;
415 command to use for SSH connections. Default is 'ssh'.
431 command to use for SSH connections. Default is 'ssh'.
416 strict;;
432 strict;;
417 Require exact command names, instead of allowing unambiguous
433 Require exact command names, instead of allowing unambiguous
418 abbreviations. True or False. Default is False.
434 abbreviations. True or False. Default is False.
419 timeout;;
435 timeout;;
420 The timeout used when a lock is held (in seconds), a negative value
436 The timeout used when a lock is held (in seconds), a negative value
421 means no timeout. Default is 600.
437 means no timeout. Default is 600.
422 username;;
438 username;;
423 The committer of a changeset created when running "commit".
439 The committer of a changeset created when running "commit".
424 Typically a person's name and email address, e.g. "Fred Widget
440 Typically a person's name and email address, e.g. "Fred Widget
425 <fred@example.com>". Default is $EMAIL or username@hostname.
441 <fred@example.com>". Default is $EMAIL or username@hostname.
426 verbose;;
442 verbose;;
427 Increase the amount of output printed. True or False. Default is False.
443 Increase the amount of output printed. True or False. Default is False.
428
444
429
445
430 web::
446 web::
431 Web interface configuration.
447 Web interface configuration.
432 accesslog;;
448 accesslog;;
433 Where to output the access log. Default is stdout.
449 Where to output the access log. Default is stdout.
434 address;;
450 address;;
435 Interface address to bind to. Default is all.
451 Interface address to bind to. Default is all.
436 allow_archive;;
452 allow_archive;;
437 List of archive format (bz2, gz, zip) allowed for downloading.
453 List of archive format (bz2, gz, zip) allowed for downloading.
438 Default is empty.
454 Default is empty.
439 allowbz2;;
455 allowbz2;;
440 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
456 (DEPRECATED) Whether to allow .tar.bz2 downloading of repo revisions.
441 Default is false.
457 Default is false.
442 allowgz;;
458 allowgz;;
443 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
459 (DEPRECATED) Whether to allow .tar.gz downloading of repo revisions.
444 Default is false.
460 Default is false.
445 allowpull;;
461 allowpull;;
446 Whether to allow pulling from the repository. Default is true.
462 Whether to allow pulling from the repository. Default is true.
447 allow_push;;
463 allow_push;;
448 Whether to allow pushing to the repository. If empty or not set,
464 Whether to allow pushing to the repository. If empty or not set,
449 push is not allowed. If the special value "*", any remote user
465 push is not allowed. If the special value "*", any remote user
450 can push, including unauthenticated users. Otherwise, the remote
466 can push, including unauthenticated users. Otherwise, the remote
451 user must have been authenticated, and the authenticated user name
467 user must have been authenticated, and the authenticated user name
452 must be present in this list (separated by whitespace or ",").
468 must be present in this list (separated by whitespace or ",").
453 The contents of the allow_push list are examined after the
469 The contents of the allow_push list are examined after the
454 deny_push list.
470 deny_push list.
455 allowzip;;
471 allowzip;;
456 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
472 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
457 Default is false. This feature creates temporary files.
473 Default is false. This feature creates temporary files.
458 baseurl;;
474 baseurl;;
459 Base URL to use when publishing URLs in other locations, so
475 Base URL to use when publishing URLs in other locations, so
460 third-party tools like email notification hooks can construct URLs.
476 third-party tools like email notification hooks can construct URLs.
461 Example: "http://hgserver/repos/"
477 Example: "http://hgserver/repos/"
462 contact;;
478 contact;;
463 Name or email address of the person in charge of the repository.
479 Name or email address of the person in charge of the repository.
464 Default is "unknown".
480 Default is "unknown".
465 deny_push;;
481 deny_push;;
466 Whether to deny pushing to the repository. If empty or not set,
482 Whether to deny pushing to the repository. If empty or not set,
467 push is not denied. If the special value "*", all remote users
483 push is not denied. If the special value "*", all remote users
468 are denied push. Otherwise, unauthenticated users are all denied,
484 are denied push. Otherwise, unauthenticated users are all denied,
469 and any authenticated user name present in this list (separated by
485 and any authenticated user name present in this list (separated by
470 whitespace or ",") is also denied. The contents of the deny_push
486 whitespace or ",") is also denied. The contents of the deny_push
471 list are examined before the allow_push list.
487 list are examined before the allow_push list.
472 description;;
488 description;;
473 Textual description of the repository's purpose or contents.
489 Textual description of the repository's purpose or contents.
474 Default is "unknown".
490 Default is "unknown".
475 errorlog;;
491 errorlog;;
476 Where to output the error log. Default is stderr.
492 Where to output the error log. Default is stderr.
477 ipv6;;
493 ipv6;;
478 Whether to use IPv6. Default is false.
494 Whether to use IPv6. Default is false.
479 name;;
495 name;;
480 Repository name to use in the web interface. Default is current
496 Repository name to use in the web interface. Default is current
481 working directory.
497 working directory.
482 maxchanges;;
498 maxchanges;;
483 Maximum number of changes to list on the changelog. Default is 10.
499 Maximum number of changes to list on the changelog. Default is 10.
484 maxfiles;;
500 maxfiles;;
485 Maximum number of files to list per changeset. Default is 10.
501 Maximum number of files to list per changeset. Default is 10.
486 port;;
502 port;;
487 Port to listen on. Default is 8000.
503 Port to listen on. Default is 8000.
488 push_ssl;;
504 push_ssl;;
489 Whether to require that inbound pushes be transported over SSL to
505 Whether to require that inbound pushes be transported over SSL to
490 prevent password sniffing. Default is true.
506 prevent password sniffing. Default is true.
491 stripes;;
507 stripes;;
492 How many lines a "zebra stripe" should span in multiline output.
508 How many lines a "zebra stripe" should span in multiline output.
493 Default is 1; set to 0 to disable.
509 Default is 1; set to 0 to disable.
494 style;;
510 style;;
495 Which template map style to use.
511 Which template map style to use.
496 templates;;
512 templates;;
497 Where to find the HTML templates. Default is install path.
513 Where to find the HTML templates. Default is install path.
498
514
499
515
500 AUTHOR
516 AUTHOR
501 ------
517 ------
502 Bryan O'Sullivan <bos@serpentine.com>.
518 Bryan O'Sullivan <bos@serpentine.com>.
503
519
504 Mercurial was written by Matt Mackall <mpm@selenic.com>.
520 Mercurial was written by Matt Mackall <mpm@selenic.com>.
505
521
506 SEE ALSO
522 SEE ALSO
507 --------
523 --------
508 hg(1), hgignore(5)
524 hg(1), hgignore(5)
509
525
510 COPYING
526 COPYING
511 -------
527 -------
512 This manual page is copyright 2005 Bryan O'Sullivan.
528 This manual page is copyright 2005 Bryan O'Sullivan.
513 Mercurial is copyright 2005, 2006 Matt Mackall.
529 Mercurial is copyright 2005, 2006 Matt Mackall.
514 Free use of this software is granted under the terms of the GNU General
530 Free use of this software is granted under the terms of the GNU General
515 Public License (GPL).
531 Public License (GPL).
@@ -1,2181 +1,2181 b''
1 # queue.py - patch queues for mercurial
1 # queue.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.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 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
33 from mercurial import commands, cmdutil, hg, patch, revlog, util, changegroup
34 import os, sys, re, errno
34 import os, sys, re, errno
35
35
36 commands.norepo += " qclone qversion"
36 commands.norepo += " qclone qversion"
37
37
38 class statusentry:
38 class statusentry:
39 def __init__(self, rev, name=None):
39 def __init__(self, rev, name=None):
40 if not name:
40 if not name:
41 fields = rev.split(':', 1)
41 fields = rev.split(':', 1)
42 if len(fields) == 2:
42 if len(fields) == 2:
43 self.rev, self.name = fields
43 self.rev, self.name = fields
44 else:
44 else:
45 self.rev, self.name = None, None
45 self.rev, self.name = None, None
46 else:
46 else:
47 self.rev, self.name = rev, name
47 self.rev, self.name = rev, name
48
48
49 def __str__(self):
49 def __str__(self):
50 return self.rev + ':' + self.name
50 return self.rev + ':' + self.name
51
51
52 class queue:
52 class queue:
53 def __init__(self, ui, path, patchdir=None):
53 def __init__(self, ui, path, patchdir=None):
54 self.basepath = path
54 self.basepath = path
55 self.path = patchdir or os.path.join(path, "patches")
55 self.path = patchdir or os.path.join(path, "patches")
56 self.opener = util.opener(self.path)
56 self.opener = util.opener(self.path)
57 self.ui = ui
57 self.ui = ui
58 self.applied = []
58 self.applied = []
59 self.full_series = []
59 self.full_series = []
60 self.applied_dirty = 0
60 self.applied_dirty = 0
61 self.series_dirty = 0
61 self.series_dirty = 0
62 self.series_path = "series"
62 self.series_path = "series"
63 self.status_path = "status"
63 self.status_path = "status"
64 self.guards_path = "guards"
64 self.guards_path = "guards"
65 self.active_guards = None
65 self.active_guards = None
66 self.guards_dirty = False
66 self.guards_dirty = False
67 self._diffopts = None
67 self._diffopts = None
68
68
69 if os.path.exists(self.join(self.series_path)):
69 if os.path.exists(self.join(self.series_path)):
70 self.full_series = self.opener(self.series_path).read().splitlines()
70 self.full_series = self.opener(self.series_path).read().splitlines()
71 self.parse_series()
71 self.parse_series()
72
72
73 if os.path.exists(self.join(self.status_path)):
73 if os.path.exists(self.join(self.status_path)):
74 lines = self.opener(self.status_path).read().splitlines()
74 lines = self.opener(self.status_path).read().splitlines()
75 self.applied = [statusentry(l) for l in lines]
75 self.applied = [statusentry(l) for l in lines]
76
76
77 def diffopts(self):
77 def diffopts(self):
78 if self._diffopts is None:
78 if self._diffopts is None:
79 self._diffopts = patch.diffopts(self.ui)
79 self._diffopts = patch.diffopts(self.ui)
80 return self._diffopts
80 return self._diffopts
81
81
82 def join(self, *p):
82 def join(self, *p):
83 return os.path.join(self.path, *p)
83 return os.path.join(self.path, *p)
84
84
85 def find_series(self, patch):
85 def find_series(self, patch):
86 pre = re.compile("(\s*)([^#]+)")
86 pre = re.compile("(\s*)([^#]+)")
87 index = 0
87 index = 0
88 for l in self.full_series:
88 for l in self.full_series:
89 m = pre.match(l)
89 m = pre.match(l)
90 if m:
90 if m:
91 s = m.group(2)
91 s = m.group(2)
92 s = s.rstrip()
92 s = s.rstrip()
93 if s == patch:
93 if s == patch:
94 return index
94 return index
95 index += 1
95 index += 1
96 return None
96 return None
97
97
98 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
98 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
99
99
100 def parse_series(self):
100 def parse_series(self):
101 self.series = []
101 self.series = []
102 self.series_guards = []
102 self.series_guards = []
103 for l in self.full_series:
103 for l in self.full_series:
104 h = l.find('#')
104 h = l.find('#')
105 if h == -1:
105 if h == -1:
106 patch = l
106 patch = l
107 comment = ''
107 comment = ''
108 elif h == 0:
108 elif h == 0:
109 continue
109 continue
110 else:
110 else:
111 patch = l[:h]
111 patch = l[:h]
112 comment = l[h:]
112 comment = l[h:]
113 patch = patch.strip()
113 patch = patch.strip()
114 if patch:
114 if patch:
115 if patch in self.series:
115 if patch in self.series:
116 raise util.Abort(_('%s appears more than once in %s') %
116 raise util.Abort(_('%s appears more than once in %s') %
117 (patch, self.join(self.series_path)))
117 (patch, self.join(self.series_path)))
118 self.series.append(patch)
118 self.series.append(patch)
119 self.series_guards.append(self.guard_re.findall(comment))
119 self.series_guards.append(self.guard_re.findall(comment))
120
120
121 def check_guard(self, guard):
121 def check_guard(self, guard):
122 bad_chars = '# \t\r\n\f'
122 bad_chars = '# \t\r\n\f'
123 first = guard[0]
123 first = guard[0]
124 for c in '-+':
124 for c in '-+':
125 if first == c:
125 if first == c:
126 return (_('guard %r starts with invalid character: %r') %
126 return (_('guard %r starts with invalid character: %r') %
127 (guard, c))
127 (guard, c))
128 for c in bad_chars:
128 for c in bad_chars:
129 if c in guard:
129 if c in guard:
130 return _('invalid character in guard %r: %r') % (guard, c)
130 return _('invalid character in guard %r: %r') % (guard, c)
131
131
132 def set_active(self, guards):
132 def set_active(self, guards):
133 for guard in guards:
133 for guard in guards:
134 bad = self.check_guard(guard)
134 bad = self.check_guard(guard)
135 if bad:
135 if bad:
136 raise util.Abort(bad)
136 raise util.Abort(bad)
137 guards = dict.fromkeys(guards).keys()
137 guards = dict.fromkeys(guards).keys()
138 guards.sort()
138 guards.sort()
139 self.ui.debug('active guards: %s\n' % ' '.join(guards))
139 self.ui.debug('active guards: %s\n' % ' '.join(guards))
140 self.active_guards = guards
140 self.active_guards = guards
141 self.guards_dirty = True
141 self.guards_dirty = True
142
142
143 def active(self):
143 def active(self):
144 if self.active_guards is None:
144 if self.active_guards is None:
145 self.active_guards = []
145 self.active_guards = []
146 try:
146 try:
147 guards = self.opener(self.guards_path).read().split()
147 guards = self.opener(self.guards_path).read().split()
148 except IOError, err:
148 except IOError, err:
149 if err.errno != errno.ENOENT: raise
149 if err.errno != errno.ENOENT: raise
150 guards = []
150 guards = []
151 for i, guard in enumerate(guards):
151 for i, guard in enumerate(guards):
152 bad = self.check_guard(guard)
152 bad = self.check_guard(guard)
153 if bad:
153 if bad:
154 self.ui.warn('%s:%d: %s\n' %
154 self.ui.warn('%s:%d: %s\n' %
155 (self.join(self.guards_path), i + 1, bad))
155 (self.join(self.guards_path), i + 1, bad))
156 else:
156 else:
157 self.active_guards.append(guard)
157 self.active_guards.append(guard)
158 return self.active_guards
158 return self.active_guards
159
159
160 def set_guards(self, idx, guards):
160 def set_guards(self, idx, guards):
161 for g in guards:
161 for g in guards:
162 if len(g) < 2:
162 if len(g) < 2:
163 raise util.Abort(_('guard %r too short') % g)
163 raise util.Abort(_('guard %r too short') % g)
164 if g[0] not in '-+':
164 if g[0] not in '-+':
165 raise util.Abort(_('guard %r starts with invalid char') % g)
165 raise util.Abort(_('guard %r starts with invalid char') % g)
166 bad = self.check_guard(g[1:])
166 bad = self.check_guard(g[1:])
167 if bad:
167 if bad:
168 raise util.Abort(bad)
168 raise util.Abort(bad)
169 drop = self.guard_re.sub('', self.full_series[idx])
169 drop = self.guard_re.sub('', self.full_series[idx])
170 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
170 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
171 self.parse_series()
171 self.parse_series()
172 self.series_dirty = True
172 self.series_dirty = True
173
173
174 def pushable(self, idx):
174 def pushable(self, idx):
175 if isinstance(idx, str):
175 if isinstance(idx, str):
176 idx = self.series.index(idx)
176 idx = self.series.index(idx)
177 patchguards = self.series_guards[idx]
177 patchguards = self.series_guards[idx]
178 if not patchguards:
178 if not patchguards:
179 return True, None
179 return True, None
180 default = False
180 default = False
181 guards = self.active()
181 guards = self.active()
182 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
182 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
183 if exactneg:
183 if exactneg:
184 return False, exactneg[0]
184 return False, exactneg[0]
185 pos = [g for g in patchguards if g[0] == '+']
185 pos = [g for g in patchguards if g[0] == '+']
186 exactpos = [g for g in pos if g[1:] in guards]
186 exactpos = [g for g in pos if g[1:] in guards]
187 if pos:
187 if pos:
188 if exactpos:
188 if exactpos:
189 return True, exactpos[0]
189 return True, exactpos[0]
190 return False, pos
190 return False, pos
191 return True, ''
191 return True, ''
192
192
193 def explain_pushable(self, idx, all_patches=False):
193 def explain_pushable(self, idx, all_patches=False):
194 write = all_patches and self.ui.write or self.ui.warn
194 write = all_patches and self.ui.write or self.ui.warn
195 if all_patches or self.ui.verbose:
195 if all_patches or self.ui.verbose:
196 if isinstance(idx, str):
196 if isinstance(idx, str):
197 idx = self.series.index(idx)
197 idx = self.series.index(idx)
198 pushable, why = self.pushable(idx)
198 pushable, why = self.pushable(idx)
199 if all_patches and pushable:
199 if all_patches and pushable:
200 if why is None:
200 if why is None:
201 write(_('allowing %s - no guards in effect\n') %
201 write(_('allowing %s - no guards in effect\n') %
202 self.series[idx])
202 self.series[idx])
203 else:
203 else:
204 if not why:
204 if not why:
205 write(_('allowing %s - no matching negative guards\n') %
205 write(_('allowing %s - no matching negative guards\n') %
206 self.series[idx])
206 self.series[idx])
207 else:
207 else:
208 write(_('allowing %s - guarded by %r\n') %
208 write(_('allowing %s - guarded by %r\n') %
209 (self.series[idx], why))
209 (self.series[idx], why))
210 if not pushable:
210 if not pushable:
211 if why:
211 if why:
212 write(_('skipping %s - guarded by %r\n') %
212 write(_('skipping %s - guarded by %r\n') %
213 (self.series[idx], why))
213 (self.series[idx], why))
214 else:
214 else:
215 write(_('skipping %s - no matching guards\n') %
215 write(_('skipping %s - no matching guards\n') %
216 self.series[idx])
216 self.series[idx])
217
217
218 def save_dirty(self):
218 def save_dirty(self):
219 def write_list(items, path):
219 def write_list(items, path):
220 fp = self.opener(path, 'w')
220 fp = self.opener(path, 'w')
221 for i in items:
221 for i in items:
222 print >> fp, i
222 print >> fp, i
223 fp.close()
223 fp.close()
224 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
224 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
225 if self.series_dirty: write_list(self.full_series, self.series_path)
225 if self.series_dirty: write_list(self.full_series, self.series_path)
226 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
226 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
227
227
228 def readheaders(self, patch):
228 def readheaders(self, patch):
229 def eatdiff(lines):
229 def eatdiff(lines):
230 while lines:
230 while lines:
231 l = lines[-1]
231 l = lines[-1]
232 if (l.startswith("diff -") or
232 if (l.startswith("diff -") or
233 l.startswith("Index:") or
233 l.startswith("Index:") or
234 l.startswith("===========")):
234 l.startswith("===========")):
235 del lines[-1]
235 del lines[-1]
236 else:
236 else:
237 break
237 break
238 def eatempty(lines):
238 def eatempty(lines):
239 while lines:
239 while lines:
240 l = lines[-1]
240 l = lines[-1]
241 if re.match('\s*$', l):
241 if re.match('\s*$', l):
242 del lines[-1]
242 del lines[-1]
243 else:
243 else:
244 break
244 break
245
245
246 pf = self.join(patch)
246 pf = self.join(patch)
247 message = []
247 message = []
248 comments = []
248 comments = []
249 user = None
249 user = None
250 date = None
250 date = None
251 format = None
251 format = None
252 subject = None
252 subject = None
253 diffstart = 0
253 diffstart = 0
254
254
255 for line in file(pf):
255 for line in file(pf):
256 line = line.rstrip()
256 line = line.rstrip()
257 if line.startswith('diff --git'):
257 if line.startswith('diff --git'):
258 diffstart = 2
258 diffstart = 2
259 break
259 break
260 if diffstart:
260 if diffstart:
261 if line.startswith('+++ '):
261 if line.startswith('+++ '):
262 diffstart = 2
262 diffstart = 2
263 break
263 break
264 if line.startswith("--- "):
264 if line.startswith("--- "):
265 diffstart = 1
265 diffstart = 1
266 continue
266 continue
267 elif format == "hgpatch":
267 elif format == "hgpatch":
268 # parse values when importing the result of an hg export
268 # parse values when importing the result of an hg export
269 if line.startswith("# User "):
269 if line.startswith("# User "):
270 user = line[7:]
270 user = line[7:]
271 elif line.startswith("# Date "):
271 elif line.startswith("# Date "):
272 date = line[7:]
272 date = line[7:]
273 elif not line.startswith("# ") and line:
273 elif not line.startswith("# ") and line:
274 message.append(line)
274 message.append(line)
275 format = None
275 format = None
276 elif line == '# HG changeset patch':
276 elif line == '# HG changeset patch':
277 format = "hgpatch"
277 format = "hgpatch"
278 elif (format != "tagdone" and (line.startswith("Subject: ") or
278 elif (format != "tagdone" and (line.startswith("Subject: ") or
279 line.startswith("subject: "))):
279 line.startswith("subject: "))):
280 subject = line[9:]
280 subject = line[9:]
281 format = "tag"
281 format = "tag"
282 elif (format != "tagdone" and (line.startswith("From: ") or
282 elif (format != "tagdone" and (line.startswith("From: ") or
283 line.startswith("from: "))):
283 line.startswith("from: "))):
284 user = line[6:]
284 user = line[6:]
285 format = "tag"
285 format = "tag"
286 elif format == "tag" and line == "":
286 elif format == "tag" and line == "":
287 # when looking for tags (subject: from: etc) they
287 # when looking for tags (subject: from: etc) they
288 # end once you find a blank line in the source
288 # end once you find a blank line in the source
289 format = "tagdone"
289 format = "tagdone"
290 elif message or line:
290 elif message or line:
291 message.append(line)
291 message.append(line)
292 comments.append(line)
292 comments.append(line)
293
293
294 eatdiff(message)
294 eatdiff(message)
295 eatdiff(comments)
295 eatdiff(comments)
296 eatempty(message)
296 eatempty(message)
297 eatempty(comments)
297 eatempty(comments)
298
298
299 # make sure message isn't empty
299 # make sure message isn't empty
300 if format and format.startswith("tag") and subject:
300 if format and format.startswith("tag") and subject:
301 message.insert(0, "")
301 message.insert(0, "")
302 message.insert(0, subject)
302 message.insert(0, subject)
303 return (message, comments, user, date, diffstart > 1)
303 return (message, comments, user, date, diffstart > 1)
304
304
305 def printdiff(self, repo, node1, node2=None, files=None,
305 def printdiff(self, repo, node1, node2=None, files=None,
306 fp=None, changes=None, opts={}):
306 fp=None, changes=None, opts={}):
307 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
307 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
308
308
309 patch.diff(repo, node1, node2, fns, match=matchfn,
309 patch.diff(repo, node1, node2, fns, match=matchfn,
310 fp=fp, changes=changes, opts=self.diffopts())
310 fp=fp, changes=changes, opts=self.diffopts())
311
311
312 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
312 def mergeone(self, repo, mergeq, head, patch, rev, wlock):
313 # first try just applying the patch
313 # first try just applying the patch
314 (err, n) = self.apply(repo, [ patch ], update_status=False,
314 (err, n) = self.apply(repo, [ patch ], update_status=False,
315 strict=True, merge=rev, wlock=wlock)
315 strict=True, merge=rev, wlock=wlock)
316
316
317 if err == 0:
317 if err == 0:
318 return (err, n)
318 return (err, n)
319
319
320 if n is None:
320 if n is None:
321 raise util.Abort(_("apply failed for patch %s") % patch)
321 raise util.Abort(_("apply failed for patch %s") % patch)
322
322
323 self.ui.warn("patch didn't work out, merging %s\n" % patch)
323 self.ui.warn("patch didn't work out, merging %s\n" % patch)
324
324
325 # apply failed, strip away that rev and merge.
325 # apply failed, strip away that rev and merge.
326 hg.clean(repo, head, wlock=wlock)
326 hg.clean(repo, head, wlock=wlock)
327 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
327 self.strip(repo, n, update=False, backup='strip', wlock=wlock)
328
328
329 ctx = repo.changectx(rev)
329 ctx = repo.changectx(rev)
330 ret = hg.merge(repo, rev, wlock=wlock)
330 ret = hg.merge(repo, rev, wlock=wlock)
331 if ret:
331 if ret:
332 raise util.Abort(_("update returned %d") % ret)
332 raise util.Abort(_("update returned %d") % ret)
333 n = repo.commit(None, ctx.description(), ctx.user(),
333 n = repo.commit(None, ctx.description(), ctx.user(),
334 force=1, wlock=wlock)
334 force=1, wlock=wlock)
335 if n == None:
335 if n == None:
336 raise util.Abort(_("repo commit failed"))
336 raise util.Abort(_("repo commit failed"))
337 try:
337 try:
338 message, comments, user, date, patchfound = mergeq.readheaders(patch)
338 message, comments, user, date, patchfound = mergeq.readheaders(patch)
339 except:
339 except:
340 raise util.Abort(_("unable to read %s") % patch)
340 raise util.Abort(_("unable to read %s") % patch)
341
341
342 patchf = self.opener(patch, "w")
342 patchf = self.opener(patch, "w")
343 if comments:
343 if comments:
344 comments = "\n".join(comments) + '\n\n'
344 comments = "\n".join(comments) + '\n\n'
345 patchf.write(comments)
345 patchf.write(comments)
346 self.printdiff(repo, head, n, fp=patchf)
346 self.printdiff(repo, head, n, fp=patchf)
347 patchf.close()
347 patchf.close()
348 return (0, n)
348 return (0, n)
349
349
350 def qparents(self, repo, rev=None):
350 def qparents(self, repo, rev=None):
351 if rev is None:
351 if rev is None:
352 (p1, p2) = repo.dirstate.parents()
352 (p1, p2) = repo.dirstate.parents()
353 if p2 == revlog.nullid:
353 if p2 == revlog.nullid:
354 return p1
354 return p1
355 if len(self.applied) == 0:
355 if len(self.applied) == 0:
356 return None
356 return None
357 return revlog.bin(self.applied[-1].rev)
357 return revlog.bin(self.applied[-1].rev)
358 pp = repo.changelog.parents(rev)
358 pp = repo.changelog.parents(rev)
359 if pp[1] != revlog.nullid:
359 if pp[1] != revlog.nullid:
360 arevs = [ x.rev for x in self.applied ]
360 arevs = [ x.rev for x in self.applied ]
361 p0 = revlog.hex(pp[0])
361 p0 = revlog.hex(pp[0])
362 p1 = revlog.hex(pp[1])
362 p1 = revlog.hex(pp[1])
363 if p0 in arevs:
363 if p0 in arevs:
364 return pp[0]
364 return pp[0]
365 if p1 in arevs:
365 if p1 in arevs:
366 return pp[1]
366 return pp[1]
367 return pp[0]
367 return pp[0]
368
368
369 def mergepatch(self, repo, mergeq, series, wlock):
369 def mergepatch(self, repo, mergeq, series, wlock):
370 if len(self.applied) == 0:
370 if len(self.applied) == 0:
371 # each of the patches merged in will have two parents. This
371 # each of the patches merged in will have two parents. This
372 # can confuse the qrefresh, qdiff, and strip code because it
372 # can confuse the qrefresh, qdiff, and strip code because it
373 # needs to know which parent is actually in the patch queue.
373 # needs to know which parent is actually in the patch queue.
374 # so, we insert a merge marker with only one parent. This way
374 # so, we insert a merge marker with only one parent. This way
375 # the first patch in the queue is never a merge patch
375 # the first patch in the queue is never a merge patch
376 #
376 #
377 pname = ".hg.patches.merge.marker"
377 pname = ".hg.patches.merge.marker"
378 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
378 n = repo.commit(None, '[mq]: merge marker', user=None, force=1,
379 wlock=wlock)
379 wlock=wlock)
380 self.applied.append(statusentry(revlog.hex(n), pname))
380 self.applied.append(statusentry(revlog.hex(n), pname))
381 self.applied_dirty = 1
381 self.applied_dirty = 1
382
382
383 head = self.qparents(repo)
383 head = self.qparents(repo)
384
384
385 for patch in series:
385 for patch in series:
386 patch = mergeq.lookup(patch, strict=True)
386 patch = mergeq.lookup(patch, strict=True)
387 if not patch:
387 if not patch:
388 self.ui.warn("patch %s does not exist\n" % patch)
388 self.ui.warn("patch %s does not exist\n" % patch)
389 return (1, None)
389 return (1, None)
390 pushable, reason = self.pushable(patch)
390 pushable, reason = self.pushable(patch)
391 if not pushable:
391 if not pushable:
392 self.explain_pushable(patch, all_patches=True)
392 self.explain_pushable(patch, all_patches=True)
393 continue
393 continue
394 info = mergeq.isapplied(patch)
394 info = mergeq.isapplied(patch)
395 if not info:
395 if not info:
396 self.ui.warn("patch %s is not applied\n" % patch)
396 self.ui.warn("patch %s is not applied\n" % patch)
397 return (1, None)
397 return (1, None)
398 rev = revlog.bin(info[1])
398 rev = revlog.bin(info[1])
399 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
399 (err, head) = self.mergeone(repo, mergeq, head, patch, rev, wlock)
400 if head:
400 if head:
401 self.applied.append(statusentry(revlog.hex(head), patch))
401 self.applied.append(statusentry(revlog.hex(head), patch))
402 self.applied_dirty = 1
402 self.applied_dirty = 1
403 if err:
403 if err:
404 return (err, head)
404 return (err, head)
405 return (0, head)
405 return (0, head)
406
406
407 def patch(self, repo, patchfile):
407 def patch(self, repo, patchfile):
408 '''Apply patchfile to the working directory.
408 '''Apply patchfile to the working directory.
409 patchfile: file name of patch'''
409 patchfile: file name of patch'''
410 files = {}
410 files = {}
411 try:
411 try:
412 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
412 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
413 files=files)
413 files=files)
414 except Exception, inst:
414 except Exception, inst:
415 self.ui.note(str(inst) + '\n')
415 self.ui.note(str(inst) + '\n')
416 if not self.ui.verbose:
416 if not self.ui.verbose:
417 self.ui.warn("patch failed, unable to continue (try -v)\n")
417 self.ui.warn("patch failed, unable to continue (try -v)\n")
418 return (False, files, False)
418 return (False, files, False)
419
419
420 return (True, files, fuzz)
420 return (True, files, fuzz)
421
421
422 def apply(self, repo, series, list=False, update_status=True,
422 def apply(self, repo, series, list=False, update_status=True,
423 strict=False, patchdir=None, merge=None, wlock=None):
423 strict=False, patchdir=None, merge=None, wlock=None):
424 # TODO unify with commands.py
424 # TODO unify with commands.py
425 if not patchdir:
425 if not patchdir:
426 patchdir = self.path
426 patchdir = self.path
427 err = 0
427 err = 0
428 if not wlock:
428 if not wlock:
429 wlock = repo.wlock()
429 wlock = repo.wlock()
430 lock = repo.lock()
430 lock = repo.lock()
431 tr = repo.transaction()
431 tr = repo.transaction()
432 n = None
432 n = None
433 for patchname in series:
433 for patchname in series:
434 pushable, reason = self.pushable(patchname)
434 pushable, reason = self.pushable(patchname)
435 if not pushable:
435 if not pushable:
436 self.explain_pushable(patchname, all_patches=True)
436 self.explain_pushable(patchname, all_patches=True)
437 continue
437 continue
438 self.ui.warn("applying %s\n" % patchname)
438 self.ui.warn("applying %s\n" % patchname)
439 pf = os.path.join(patchdir, patchname)
439 pf = os.path.join(patchdir, patchname)
440
440
441 try:
441 try:
442 message, comments, user, date, patchfound = self.readheaders(patchname)
442 message, comments, user, date, patchfound = self.readheaders(patchname)
443 except:
443 except:
444 self.ui.warn("Unable to read %s\n" % patchname)
444 self.ui.warn("Unable to read %s\n" % patchname)
445 err = 1
445 err = 1
446 break
446 break
447
447
448 if not message:
448 if not message:
449 message = "imported patch %s\n" % patchname
449 message = "imported patch %s\n" % patchname
450 else:
450 else:
451 if list:
451 if list:
452 message.append("\nimported patch %s" % patchname)
452 message.append("\nimported patch %s" % patchname)
453 message = '\n'.join(message)
453 message = '\n'.join(message)
454
454
455 (patcherr, files, fuzz) = self.patch(repo, pf)
455 (patcherr, files, fuzz) = self.patch(repo, pf)
456 patcherr = not patcherr
456 patcherr = not patcherr
457
457
458 if merge and files:
458 if merge and files:
459 # Mark as merged and update dirstate parent info
459 # Mark as merged and update dirstate parent info
460 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
460 repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm')
461 p1, p2 = repo.dirstate.parents()
461 p1, p2 = repo.dirstate.parents()
462 repo.dirstate.setparents(p1, merge)
462 repo.dirstate.setparents(p1, merge)
463 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
463 files = patch.updatedir(self.ui, repo, files, wlock=wlock)
464 n = repo.commit(files, message, user, date, force=1, lock=lock,
464 n = repo.commit(files, message, user, date, force=1, lock=lock,
465 wlock=wlock)
465 wlock=wlock)
466
466
467 if n == None:
467 if n == None:
468 raise util.Abort(_("repo commit failed"))
468 raise util.Abort(_("repo commit failed"))
469
469
470 if update_status:
470 if update_status:
471 self.applied.append(statusentry(revlog.hex(n), patchname))
471 self.applied.append(statusentry(revlog.hex(n), patchname))
472
472
473 if patcherr:
473 if patcherr:
474 if not patchfound:
474 if not patchfound:
475 self.ui.warn("patch %s is empty\n" % patchname)
475 self.ui.warn("patch %s is empty\n" % patchname)
476 err = 0
476 err = 0
477 else:
477 else:
478 self.ui.warn("patch failed, rejects left in working dir\n")
478 self.ui.warn("patch failed, rejects left in working dir\n")
479 err = 1
479 err = 1
480 break
480 break
481
481
482 if fuzz and strict:
482 if fuzz and strict:
483 self.ui.warn("fuzz found when applying patch, stopping\n")
483 self.ui.warn("fuzz found when applying patch, stopping\n")
484 err = 1
484 err = 1
485 break
485 break
486 tr.close()
486 tr.close()
487 return (err, n)
487 return (err, n)
488
488
489 def delete(self, repo, patches, opts):
489 def delete(self, repo, patches, opts):
490 realpatches = []
490 realpatches = []
491 for patch in patches:
491 for patch in patches:
492 patch = self.lookup(patch, strict=True)
492 patch = self.lookup(patch, strict=True)
493 info = self.isapplied(patch)
493 info = self.isapplied(patch)
494 if info:
494 if info:
495 raise util.Abort(_("cannot delete applied patch %s") % patch)
495 raise util.Abort(_("cannot delete applied patch %s") % patch)
496 if patch not in self.series:
496 if patch not in self.series:
497 raise util.Abort(_("patch %s not in series file") % patch)
497 raise util.Abort(_("patch %s not in series file") % patch)
498 realpatches.append(patch)
498 realpatches.append(patch)
499
499
500 appliedbase = 0
500 appliedbase = 0
501 if opts.get('rev'):
501 if opts.get('rev'):
502 if not self.applied:
502 if not self.applied:
503 raise util.Abort(_('no patches applied'))
503 raise util.Abort(_('no patches applied'))
504 revs = cmdutil.revrange(repo, opts['rev'])
504 revs = cmdutil.revrange(repo, opts['rev'])
505 if len(revs) > 1 and revs[0] > revs[1]:
505 if len(revs) > 1 and revs[0] > revs[1]:
506 revs.reverse()
506 revs.reverse()
507 for rev in revs:
507 for rev in revs:
508 if appliedbase >= len(self.applied):
508 if appliedbase >= len(self.applied):
509 raise util.Abort(_("revision %d is not managed") % rev)
509 raise util.Abort(_("revision %d is not managed") % rev)
510
510
511 base = revlog.bin(self.applied[appliedbase].rev)
511 base = revlog.bin(self.applied[appliedbase].rev)
512 node = repo.changelog.node(rev)
512 node = repo.changelog.node(rev)
513 if node != base:
513 if node != base:
514 raise util.Abort(_("cannot delete revision %d above "
514 raise util.Abort(_("cannot delete revision %d above "
515 "applied patches") % rev)
515 "applied patches") % rev)
516 realpatches.append(self.applied[appliedbase].name)
516 realpatches.append(self.applied[appliedbase].name)
517 appliedbase += 1
517 appliedbase += 1
518
518
519 if not opts.get('keep'):
519 if not opts.get('keep'):
520 r = self.qrepo()
520 r = self.qrepo()
521 if r:
521 if r:
522 r.remove(realpatches, True)
522 r.remove(realpatches, True)
523 else:
523 else:
524 for p in realpatches:
524 for p in realpatches:
525 os.unlink(self.join(p))
525 os.unlink(self.join(p))
526
526
527 if appliedbase:
527 if appliedbase:
528 del self.applied[:appliedbase]
528 del self.applied[:appliedbase]
529 self.applied_dirty = 1
529 self.applied_dirty = 1
530 indices = [self.find_series(p) for p in realpatches]
530 indices = [self.find_series(p) for p in realpatches]
531 indices.sort()
531 indices.sort()
532 for i in indices[-1::-1]:
532 for i in indices[-1::-1]:
533 del self.full_series[i]
533 del self.full_series[i]
534 self.parse_series()
534 self.parse_series()
535 self.series_dirty = 1
535 self.series_dirty = 1
536
536
537 def check_toppatch(self, repo):
537 def check_toppatch(self, repo):
538 if len(self.applied) > 0:
538 if len(self.applied) > 0:
539 top = revlog.bin(self.applied[-1].rev)
539 top = revlog.bin(self.applied[-1].rev)
540 pp = repo.dirstate.parents()
540 pp = repo.dirstate.parents()
541 if top not in pp:
541 if top not in pp:
542 raise util.Abort(_("queue top not at same revision as working directory"))
542 raise util.Abort(_("queue top not at same revision as working directory"))
543 return top
543 return top
544 return None
544 return None
545 def check_localchanges(self, repo, force=False, refresh=True):
545 def check_localchanges(self, repo, force=False, refresh=True):
546 m, a, r, d = repo.status()[:4]
546 m, a, r, d = repo.status()[:4]
547 if m or a or r or d:
547 if m or a or r or d:
548 if not force:
548 if not force:
549 if refresh:
549 if refresh:
550 raise util.Abort(_("local changes found, refresh first"))
550 raise util.Abort(_("local changes found, refresh first"))
551 else:
551 else:
552 raise util.Abort(_("local changes found"))
552 raise util.Abort(_("local changes found"))
553 return m, a, r, d
553 return m, a, r, d
554 def new(self, repo, patch, msg=None, force=None):
554 def new(self, repo, patch, msg=None, force=None):
555 if os.path.exists(self.join(patch)):
555 if os.path.exists(self.join(patch)):
556 raise util.Abort(_('patch "%s" already exists') % patch)
556 raise util.Abort(_('patch "%s" already exists') % patch)
557 m, a, r, d = self.check_localchanges(repo, force)
557 m, a, r, d = self.check_localchanges(repo, force)
558 commitfiles = m + a + r
558 commitfiles = m + a + r
559 self.check_toppatch(repo)
559 self.check_toppatch(repo)
560 wlock = repo.wlock()
560 wlock = repo.wlock()
561 insert = self.full_series_end()
561 insert = self.full_series_end()
562 if msg:
562 if msg:
563 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
563 n = repo.commit(commitfiles, "[mq]: %s" % msg, force=True,
564 wlock=wlock)
564 wlock=wlock)
565 else:
565 else:
566 n = repo.commit(commitfiles,
566 n = repo.commit(commitfiles,
567 "New patch: %s" % patch, force=True, wlock=wlock)
567 "New patch: %s" % patch, force=True, wlock=wlock)
568 if n == None:
568 if n == None:
569 raise util.Abort(_("repo commit failed"))
569 raise util.Abort(_("repo commit failed"))
570 self.full_series[insert:insert] = [patch]
570 self.full_series[insert:insert] = [patch]
571 self.applied.append(statusentry(revlog.hex(n), patch))
571 self.applied.append(statusentry(revlog.hex(n), patch))
572 self.parse_series()
572 self.parse_series()
573 self.series_dirty = 1
573 self.series_dirty = 1
574 self.applied_dirty = 1
574 self.applied_dirty = 1
575 p = self.opener(patch, "w")
575 p = self.opener(patch, "w")
576 if msg:
576 if msg:
577 msg = msg + "\n"
577 msg = msg + "\n"
578 p.write(msg)
578 p.write(msg)
579 p.close()
579 p.close()
580 wlock = None
580 wlock = None
581 r = self.qrepo()
581 r = self.qrepo()
582 if r: r.add([patch])
582 if r: r.add([patch])
583 if commitfiles:
583 if commitfiles:
584 self.refresh(repo, short=True)
584 self.refresh(repo, short=True)
585
585
586 def strip(self, repo, rev, update=True, backup="all", wlock=None):
586 def strip(self, repo, rev, update=True, backup="all", wlock=None):
587 def limitheads(chlog, stop):
587 def limitheads(chlog, stop):
588 """return the list of all nodes that have no children"""
588 """return the list of all nodes that have no children"""
589 p = {}
589 p = {}
590 h = []
590 h = []
591 stoprev = 0
591 stoprev = 0
592 if stop in chlog.nodemap:
592 if stop in chlog.nodemap:
593 stoprev = chlog.rev(stop)
593 stoprev = chlog.rev(stop)
594
594
595 for r in xrange(chlog.count() - 1, -1, -1):
595 for r in xrange(chlog.count() - 1, -1, -1):
596 n = chlog.node(r)
596 n = chlog.node(r)
597 if n not in p:
597 if n not in p:
598 h.append(n)
598 h.append(n)
599 if n == stop:
599 if n == stop:
600 break
600 break
601 if r < stoprev:
601 if r < stoprev:
602 break
602 break
603 for pn in chlog.parents(n):
603 for pn in chlog.parents(n):
604 p[pn] = 1
604 p[pn] = 1
605 return h
605 return h
606
606
607 def bundle(cg):
607 def bundle(cg):
608 backupdir = repo.join("strip-backup")
608 backupdir = repo.join("strip-backup")
609 if not os.path.isdir(backupdir):
609 if not os.path.isdir(backupdir):
610 os.mkdir(backupdir)
610 os.mkdir(backupdir)
611 name = os.path.join(backupdir, "%s" % revlog.short(rev))
611 name = os.path.join(backupdir, "%s" % revlog.short(rev))
612 name = savename(name)
612 name = savename(name)
613 self.ui.warn("saving bundle to %s\n" % name)
613 self.ui.warn("saving bundle to %s\n" % name)
614 return changegroup.writebundle(cg, name, "HG10BZ")
614 return changegroup.writebundle(cg, name, "HG10BZ")
615
615
616 def stripall(revnum):
616 def stripall(revnum):
617 mm = repo.changectx(rev).manifest()
617 mm = repo.changectx(rev).manifest()
618 seen = {}
618 seen = {}
619
619
620 for x in xrange(revnum, repo.changelog.count()):
620 for x in xrange(revnum, repo.changelog.count()):
621 for f in repo.changectx(x).files():
621 for f in repo.changectx(x).files():
622 if f in seen:
622 if f in seen:
623 continue
623 continue
624 seen[f] = 1
624 seen[f] = 1
625 if f in mm:
625 if f in mm:
626 filerev = mm[f]
626 filerev = mm[f]
627 else:
627 else:
628 filerev = 0
628 filerev = 0
629 seen[f] = filerev
629 seen[f] = filerev
630 # we go in two steps here so the strip loop happens in a
630 # we go in two steps here so the strip loop happens in a
631 # sensible order. When stripping many files, this helps keep
631 # sensible order. When stripping many files, this helps keep
632 # our disk access patterns under control.
632 # our disk access patterns under control.
633 seen_list = seen.keys()
633 seen_list = seen.keys()
634 seen_list.sort()
634 seen_list.sort()
635 for f in seen_list:
635 for f in seen_list:
636 ff = repo.file(f)
636 ff = repo.file(f)
637 filerev = seen[f]
637 filerev = seen[f]
638 if filerev != 0:
638 if filerev != 0:
639 if filerev in ff.nodemap:
639 if filerev in ff.nodemap:
640 filerev = ff.rev(filerev)
640 filerev = ff.rev(filerev)
641 else:
641 else:
642 filerev = 0
642 filerev = 0
643 ff.strip(filerev, revnum)
643 ff.strip(filerev, revnum)
644
644
645 if not wlock:
645 if not wlock:
646 wlock = repo.wlock()
646 wlock = repo.wlock()
647 lock = repo.lock()
647 lock = repo.lock()
648 chlog = repo.changelog
648 chlog = repo.changelog
649 # TODO delete the undo files, and handle undo of merge sets
649 # TODO delete the undo files, and handle undo of merge sets
650 pp = chlog.parents(rev)
650 pp = chlog.parents(rev)
651 revnum = chlog.rev(rev)
651 revnum = chlog.rev(rev)
652
652
653 if update:
653 if update:
654 self.check_localchanges(repo, refresh=False)
654 self.check_localchanges(repo, refresh=False)
655 urev = self.qparents(repo, rev)
655 urev = self.qparents(repo, rev)
656 hg.clean(repo, urev, wlock=wlock)
656 hg.clean(repo, urev, wlock=wlock)
657 repo.dirstate.write()
657 repo.dirstate.write()
658
658
659 # save is a list of all the branches we are truncating away
659 # save is a list of all the branches we are truncating away
660 # that we actually want to keep. changegroup will be used
660 # that we actually want to keep. changegroup will be used
661 # to preserve them and add them back after the truncate
661 # to preserve them and add them back after the truncate
662 saveheads = []
662 saveheads = []
663 savebases = {}
663 savebases = {}
664
664
665 heads = limitheads(chlog, rev)
665 heads = limitheads(chlog, rev)
666 seen = {}
666 seen = {}
667
667
668 # search through all the heads, finding those where the revision
668 # search through all the heads, finding those where the revision
669 # we want to strip away is an ancestor. Also look for merges
669 # we want to strip away is an ancestor. Also look for merges
670 # that might be turned into new heads by the strip.
670 # that might be turned into new heads by the strip.
671 while heads:
671 while heads:
672 h = heads.pop()
672 h = heads.pop()
673 n = h
673 n = h
674 while True:
674 while True:
675 seen[n] = 1
675 seen[n] = 1
676 pp = chlog.parents(n)
676 pp = chlog.parents(n)
677 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
677 if pp[1] != revlog.nullid and chlog.rev(pp[1]) > revnum:
678 if pp[1] not in seen:
678 if pp[1] not in seen:
679 heads.append(pp[1])
679 heads.append(pp[1])
680 if pp[0] == revlog.nullid:
680 if pp[0] == revlog.nullid:
681 break
681 break
682 if chlog.rev(pp[0]) < revnum:
682 if chlog.rev(pp[0]) < revnum:
683 break
683 break
684 n = pp[0]
684 n = pp[0]
685 if n == rev:
685 if n == rev:
686 break
686 break
687 r = chlog.reachable(h, rev)
687 r = chlog.reachable(h, rev)
688 if rev not in r:
688 if rev not in r:
689 saveheads.append(h)
689 saveheads.append(h)
690 for x in r:
690 for x in r:
691 if chlog.rev(x) > revnum:
691 if chlog.rev(x) > revnum:
692 savebases[x] = 1
692 savebases[x] = 1
693
693
694 # create a changegroup for all the branches we need to keep
694 # create a changegroup for all the branches we need to keep
695 if backup == "all":
695 if backup == "all":
696 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
696 backupch = repo.changegroupsubset([rev], chlog.heads(), 'strip')
697 bundle(backupch)
697 bundle(backupch)
698 if saveheads:
698 if saveheads:
699 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
699 backupch = repo.changegroupsubset(savebases.keys(), saveheads, 'strip')
700 chgrpfile = bundle(backupch)
700 chgrpfile = bundle(backupch)
701 chgrpfile = 'file:%s' % chgrpfile
702
701
703 stripall(revnum)
702 stripall(revnum)
704
703
705 change = chlog.read(rev)
704 change = chlog.read(rev)
706 chlog.strip(revnum, revnum)
705 chlog.strip(revnum, revnum)
707 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
706 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
708 if saveheads:
707 if saveheads:
709 self.ui.status("adding branch\n")
708 self.ui.status("adding branch\n")
710 commands.unbundle(self.ui, repo, chgrpfile, update=False)
709 commands.unbundle(self.ui, repo, "file:%s" % chgrpfile,
710 update=False)
711 if backup != "strip":
711 if backup != "strip":
712 os.unlink(chgrpfile)
712 os.unlink(chgrpfile)
713
713
714 def isapplied(self, patch):
714 def isapplied(self, patch):
715 """returns (index, rev, patch)"""
715 """returns (index, rev, patch)"""
716 for i in xrange(len(self.applied)):
716 for i in xrange(len(self.applied)):
717 a = self.applied[i]
717 a = self.applied[i]
718 if a.name == patch:
718 if a.name == patch:
719 return (i, a.rev, a.name)
719 return (i, a.rev, a.name)
720 return None
720 return None
721
721
722 # if the exact patch name does not exist, we try a few
722 # if the exact patch name does not exist, we try a few
723 # variations. If strict is passed, we try only #1
723 # variations. If strict is passed, we try only #1
724 #
724 #
725 # 1) a number to indicate an offset in the series file
725 # 1) a number to indicate an offset in the series file
726 # 2) a unique substring of the patch name was given
726 # 2) a unique substring of the patch name was given
727 # 3) patchname[-+]num to indicate an offset in the series file
727 # 3) patchname[-+]num to indicate an offset in the series file
728 def lookup(self, patch, strict=False):
728 def lookup(self, patch, strict=False):
729 patch = patch and str(patch)
729 patch = patch and str(patch)
730
730
731 def partial_name(s):
731 def partial_name(s):
732 if s in self.series:
732 if s in self.series:
733 return s
733 return s
734 matches = [x for x in self.series if s in x]
734 matches = [x for x in self.series if s in x]
735 if len(matches) > 1:
735 if len(matches) > 1:
736 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
736 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
737 for m in matches:
737 for m in matches:
738 self.ui.warn(' %s\n' % m)
738 self.ui.warn(' %s\n' % m)
739 return None
739 return None
740 if matches:
740 if matches:
741 return matches[0]
741 return matches[0]
742 if len(self.series) > 0 and len(self.applied) > 0:
742 if len(self.series) > 0 and len(self.applied) > 0:
743 if s == 'qtip':
743 if s == 'qtip':
744 return self.series[self.series_end(True)-1]
744 return self.series[self.series_end(True)-1]
745 if s == 'qbase':
745 if s == 'qbase':
746 return self.series[0]
746 return self.series[0]
747 return None
747 return None
748 if patch == None:
748 if patch == None:
749 return None
749 return None
750
750
751 # we don't want to return a partial match until we make
751 # we don't want to return a partial match until we make
752 # sure the file name passed in does not exist (checked below)
752 # sure the file name passed in does not exist (checked below)
753 res = partial_name(patch)
753 res = partial_name(patch)
754 if res and res == patch:
754 if res and res == patch:
755 return res
755 return res
756
756
757 if not os.path.isfile(self.join(patch)):
757 if not os.path.isfile(self.join(patch)):
758 try:
758 try:
759 sno = int(patch)
759 sno = int(patch)
760 except(ValueError, OverflowError):
760 except(ValueError, OverflowError):
761 pass
761 pass
762 else:
762 else:
763 if sno < len(self.series):
763 if sno < len(self.series):
764 return self.series[sno]
764 return self.series[sno]
765 if not strict:
765 if not strict:
766 # return any partial match made above
766 # return any partial match made above
767 if res:
767 if res:
768 return res
768 return res
769 minus = patch.rfind('-')
769 minus = patch.rfind('-')
770 if minus >= 0:
770 if minus >= 0:
771 res = partial_name(patch[:minus])
771 res = partial_name(patch[:minus])
772 if res:
772 if res:
773 i = self.series.index(res)
773 i = self.series.index(res)
774 try:
774 try:
775 off = int(patch[minus+1:] or 1)
775 off = int(patch[minus+1:] or 1)
776 except(ValueError, OverflowError):
776 except(ValueError, OverflowError):
777 pass
777 pass
778 else:
778 else:
779 if i - off >= 0:
779 if i - off >= 0:
780 return self.series[i - off]
780 return self.series[i - off]
781 plus = patch.rfind('+')
781 plus = patch.rfind('+')
782 if plus >= 0:
782 if plus >= 0:
783 res = partial_name(patch[:plus])
783 res = partial_name(patch[:plus])
784 if res:
784 if res:
785 i = self.series.index(res)
785 i = self.series.index(res)
786 try:
786 try:
787 off = int(patch[plus+1:] or 1)
787 off = int(patch[plus+1:] or 1)
788 except(ValueError, OverflowError):
788 except(ValueError, OverflowError):
789 pass
789 pass
790 else:
790 else:
791 if i + off < len(self.series):
791 if i + off < len(self.series):
792 return self.series[i + off]
792 return self.series[i + off]
793 raise util.Abort(_("patch %s not in series") % patch)
793 raise util.Abort(_("patch %s not in series") % patch)
794
794
795 def push(self, repo, patch=None, force=False, list=False,
795 def push(self, repo, patch=None, force=False, list=False,
796 mergeq=None, wlock=None):
796 mergeq=None, wlock=None):
797 if not wlock:
797 if not wlock:
798 wlock = repo.wlock()
798 wlock = repo.wlock()
799 patch = self.lookup(patch)
799 patch = self.lookup(patch)
800 if patch and self.isapplied(patch):
800 if patch and self.isapplied(patch):
801 raise util.Abort(_("patch %s is already applied") % patch)
801 raise util.Abort(_("patch %s is already applied") % patch)
802 if self.series_end() == len(self.series):
802 if self.series_end() == len(self.series):
803 raise util.Abort(_("patch series fully applied"))
803 raise util.Abort(_("patch series fully applied"))
804 if not force:
804 if not force:
805 self.check_localchanges(repo)
805 self.check_localchanges(repo)
806
806
807 self.applied_dirty = 1;
807 self.applied_dirty = 1;
808 start = self.series_end()
808 start = self.series_end()
809 if start > 0:
809 if start > 0:
810 self.check_toppatch(repo)
810 self.check_toppatch(repo)
811 if not patch:
811 if not patch:
812 patch = self.series[start]
812 patch = self.series[start]
813 end = start + 1
813 end = start + 1
814 else:
814 else:
815 end = self.series.index(patch, start) + 1
815 end = self.series.index(patch, start) + 1
816 s = self.series[start:end]
816 s = self.series[start:end]
817 if mergeq:
817 if mergeq:
818 ret = self.mergepatch(repo, mergeq, s, wlock)
818 ret = self.mergepatch(repo, mergeq, s, wlock)
819 else:
819 else:
820 ret = self.apply(repo, s, list, wlock=wlock)
820 ret = self.apply(repo, s, list, wlock=wlock)
821 top = self.applied[-1].name
821 top = self.applied[-1].name
822 if ret[0]:
822 if ret[0]:
823 self.ui.write("Errors during apply, please fix and refresh %s\n" %
823 self.ui.write("Errors during apply, please fix and refresh %s\n" %
824 top)
824 top)
825 else:
825 else:
826 self.ui.write("Now at: %s\n" % top)
826 self.ui.write("Now at: %s\n" % top)
827 return ret[0]
827 return ret[0]
828
828
829 def pop(self, repo, patch=None, force=False, update=True, all=False,
829 def pop(self, repo, patch=None, force=False, update=True, all=False,
830 wlock=None):
830 wlock=None):
831 def getfile(f, rev):
831 def getfile(f, rev):
832 t = repo.file(f).read(rev)
832 t = repo.file(f).read(rev)
833 repo.wfile(f, "w").write(t)
833 repo.wfile(f, "w").write(t)
834
834
835 if not wlock:
835 if not wlock:
836 wlock = repo.wlock()
836 wlock = repo.wlock()
837 if patch:
837 if patch:
838 # index, rev, patch
838 # index, rev, patch
839 info = self.isapplied(patch)
839 info = self.isapplied(patch)
840 if not info:
840 if not info:
841 patch = self.lookup(patch)
841 patch = self.lookup(patch)
842 info = self.isapplied(patch)
842 info = self.isapplied(patch)
843 if not info:
843 if not info:
844 raise util.Abort(_("patch %s is not applied") % patch)
844 raise util.Abort(_("patch %s is not applied") % patch)
845 if len(self.applied) == 0:
845 if len(self.applied) == 0:
846 raise util.Abort(_("no patches applied"))
846 raise util.Abort(_("no patches applied"))
847
847
848 if not update:
848 if not update:
849 parents = repo.dirstate.parents()
849 parents = repo.dirstate.parents()
850 rr = [ revlog.bin(x.rev) for x in self.applied ]
850 rr = [ revlog.bin(x.rev) for x in self.applied ]
851 for p in parents:
851 for p in parents:
852 if p in rr:
852 if p in rr:
853 self.ui.warn("qpop: forcing dirstate update\n")
853 self.ui.warn("qpop: forcing dirstate update\n")
854 update = True
854 update = True
855
855
856 if not force and update:
856 if not force and update:
857 self.check_localchanges(repo)
857 self.check_localchanges(repo)
858
858
859 self.applied_dirty = 1;
859 self.applied_dirty = 1;
860 end = len(self.applied)
860 end = len(self.applied)
861 if not patch:
861 if not patch:
862 if all:
862 if all:
863 popi = 0
863 popi = 0
864 else:
864 else:
865 popi = len(self.applied) - 1
865 popi = len(self.applied) - 1
866 else:
866 else:
867 popi = info[0] + 1
867 popi = info[0] + 1
868 if popi >= end:
868 if popi >= end:
869 self.ui.warn("qpop: %s is already at the top\n" % patch)
869 self.ui.warn("qpop: %s is already at the top\n" % patch)
870 return
870 return
871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
871 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
872
872
873 start = info[0]
873 start = info[0]
874 rev = revlog.bin(info[1])
874 rev = revlog.bin(info[1])
875
875
876 # we know there are no local changes, so we can make a simplified
876 # we know there are no local changes, so we can make a simplified
877 # form of hg.update.
877 # form of hg.update.
878 if update:
878 if update:
879 top = self.check_toppatch(repo)
879 top = self.check_toppatch(repo)
880 qp = self.qparents(repo, rev)
880 qp = self.qparents(repo, rev)
881 changes = repo.changelog.read(qp)
881 changes = repo.changelog.read(qp)
882 mmap = repo.manifest.read(changes[0])
882 mmap = repo.manifest.read(changes[0])
883 m, a, r, d, u = repo.status(qp, top)[:5]
883 m, a, r, d, u = repo.status(qp, top)[:5]
884 if d:
884 if d:
885 raise util.Abort("deletions found between repo revs")
885 raise util.Abort("deletions found between repo revs")
886 for f in m:
886 for f in m:
887 getfile(f, mmap[f])
887 getfile(f, mmap[f])
888 for f in r:
888 for f in r:
889 getfile(f, mmap[f])
889 getfile(f, mmap[f])
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
890 util.set_exec(repo.wjoin(f), mmap.execf(f))
891 repo.dirstate.update(m + r, 'n')
891 repo.dirstate.update(m + r, 'n')
892 for f in a:
892 for f in a:
893 try:
893 try:
894 os.unlink(repo.wjoin(f))
894 os.unlink(repo.wjoin(f))
895 except OSError, e:
895 except OSError, e:
896 if e.errno != errno.ENOENT:
896 if e.errno != errno.ENOENT:
897 raise
897 raise
898 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
898 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
899 except: pass
899 except: pass
900 if a:
900 if a:
901 repo.dirstate.forget(a)
901 repo.dirstate.forget(a)
902 repo.dirstate.setparents(qp, revlog.nullid)
902 repo.dirstate.setparents(qp, revlog.nullid)
903 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
903 self.strip(repo, rev, update=False, backup='strip', wlock=wlock)
904 del self.applied[start:end]
904 del self.applied[start:end]
905 if len(self.applied):
905 if len(self.applied):
906 self.ui.write("Now at: %s\n" % self.applied[-1].name)
906 self.ui.write("Now at: %s\n" % self.applied[-1].name)
907 else:
907 else:
908 self.ui.write("Patch queue now empty\n")
908 self.ui.write("Patch queue now empty\n")
909
909
910 def diff(self, repo, pats, opts):
910 def diff(self, repo, pats, opts):
911 top = self.check_toppatch(repo)
911 top = self.check_toppatch(repo)
912 if not top:
912 if not top:
913 self.ui.write("No patches applied\n")
913 self.ui.write("No patches applied\n")
914 return
914 return
915 qp = self.qparents(repo, top)
915 qp = self.qparents(repo, top)
916 if opts.get('git'):
916 if opts.get('git'):
917 self.diffopts().git = True
917 self.diffopts().git = True
918 self.printdiff(repo, qp, files=pats, opts=opts)
918 self.printdiff(repo, qp, files=pats, opts=opts)
919
919
920 def refresh(self, repo, pats=None, **opts):
920 def refresh(self, repo, pats=None, **opts):
921 if len(self.applied) == 0:
921 if len(self.applied) == 0:
922 self.ui.write("No patches applied\n")
922 self.ui.write("No patches applied\n")
923 return 1
923 return 1
924 wlock = repo.wlock()
924 wlock = repo.wlock()
925 self.check_toppatch(repo)
925 self.check_toppatch(repo)
926 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
926 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
927 top = revlog.bin(top)
927 top = revlog.bin(top)
928 cparents = repo.changelog.parents(top)
928 cparents = repo.changelog.parents(top)
929 patchparent = self.qparents(repo, top)
929 patchparent = self.qparents(repo, top)
930 message, comments, user, date, patchfound = self.readheaders(patchfn)
930 message, comments, user, date, patchfound = self.readheaders(patchfn)
931
931
932 patchf = self.opener(patchfn, "w")
932 patchf = self.opener(patchfn, "w")
933 msg = opts.get('msg', '').rstrip()
933 msg = opts.get('msg', '').rstrip()
934 if msg:
934 if msg:
935 if comments:
935 if comments:
936 # Remove existing message.
936 # Remove existing message.
937 ci = 0
937 ci = 0
938 for mi in xrange(len(message)):
938 for mi in xrange(len(message)):
939 while message[mi] != comments[ci]:
939 while message[mi] != comments[ci]:
940 ci += 1
940 ci += 1
941 del comments[ci]
941 del comments[ci]
942 comments.append(msg)
942 comments.append(msg)
943 if comments:
943 if comments:
944 comments = "\n".join(comments) + '\n\n'
944 comments = "\n".join(comments) + '\n\n'
945 patchf.write(comments)
945 patchf.write(comments)
946
946
947 if opts.get('git'):
947 if opts.get('git'):
948 self.diffopts().git = True
948 self.diffopts().git = True
949 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
949 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
950 tip = repo.changelog.tip()
950 tip = repo.changelog.tip()
951 if top == tip:
951 if top == tip:
952 # if the top of our patch queue is also the tip, there is an
952 # if the top of our patch queue is also the tip, there is an
953 # optimization here. We update the dirstate in place and strip
953 # optimization here. We update the dirstate in place and strip
954 # off the tip commit. Then just commit the current directory
954 # off the tip commit. Then just commit the current directory
955 # tree. We can also send repo.commit the list of files
955 # tree. We can also send repo.commit the list of files
956 # changed to speed up the diff
956 # changed to speed up the diff
957 #
957 #
958 # in short mode, we only diff the files included in the
958 # in short mode, we only diff the files included in the
959 # patch already
959 # patch already
960 #
960 #
961 # this should really read:
961 # this should really read:
962 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
962 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
963 # but we do it backwards to take advantage of manifest/chlog
963 # but we do it backwards to take advantage of manifest/chlog
964 # caching against the next repo.status call
964 # caching against the next repo.status call
965 #
965 #
966 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
966 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
967 changes = repo.changelog.read(tip)
967 changes = repo.changelog.read(tip)
968 man = repo.manifest.read(changes[0])
968 man = repo.manifest.read(changes[0])
969 aaa = aa[:]
969 aaa = aa[:]
970 if opts.get('short'):
970 if opts.get('short'):
971 filelist = mm + aa + dd
971 filelist = mm + aa + dd
972 else:
972 else:
973 filelist = None
973 filelist = None
974 m, a, r, d, u = repo.status(files=filelist)[:5]
974 m, a, r, d, u = repo.status(files=filelist)[:5]
975
975
976 # we might end up with files that were added between tip and
976 # we might end up with files that were added between tip and
977 # the dirstate parent, but then changed in the local dirstate.
977 # the dirstate parent, but then changed in the local dirstate.
978 # in this case, we want them to only show up in the added section
978 # in this case, we want them to only show up in the added section
979 for x in m:
979 for x in m:
980 if x not in aa:
980 if x not in aa:
981 mm.append(x)
981 mm.append(x)
982 # we might end up with files added by the local dirstate that
982 # we might end up with files added by the local dirstate that
983 # were deleted by the patch. In this case, they should only
983 # were deleted by the patch. In this case, they should only
984 # show up in the changed section.
984 # show up in the changed section.
985 for x in a:
985 for x in a:
986 if x in dd:
986 if x in dd:
987 del dd[dd.index(x)]
987 del dd[dd.index(x)]
988 mm.append(x)
988 mm.append(x)
989 else:
989 else:
990 aa.append(x)
990 aa.append(x)
991 # make sure any files deleted in the local dirstate
991 # make sure any files deleted in the local dirstate
992 # are not in the add or change column of the patch
992 # are not in the add or change column of the patch
993 forget = []
993 forget = []
994 for x in d + r:
994 for x in d + r:
995 if x in aa:
995 if x in aa:
996 del aa[aa.index(x)]
996 del aa[aa.index(x)]
997 forget.append(x)
997 forget.append(x)
998 continue
998 continue
999 elif x in mm:
999 elif x in mm:
1000 del mm[mm.index(x)]
1000 del mm[mm.index(x)]
1001 dd.append(x)
1001 dd.append(x)
1002
1002
1003 m = util.unique(mm)
1003 m = util.unique(mm)
1004 r = util.unique(dd)
1004 r = util.unique(dd)
1005 a = util.unique(aa)
1005 a = util.unique(aa)
1006 filelist = filter(matchfn, util.unique(m + r + a))
1006 filelist = filter(matchfn, util.unique(m + r + a))
1007 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1007 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1008 fp=patchf, changes=(m, a, r, [], u),
1008 fp=patchf, changes=(m, a, r, [], u),
1009 opts=self.diffopts())
1009 opts=self.diffopts())
1010 patchf.close()
1010 patchf.close()
1011
1011
1012 repo.dirstate.setparents(*cparents)
1012 repo.dirstate.setparents(*cparents)
1013 copies = {}
1013 copies = {}
1014 for dst in a:
1014 for dst in a:
1015 src = repo.dirstate.copied(dst)
1015 src = repo.dirstate.copied(dst)
1016 if src is None:
1016 if src is None:
1017 continue
1017 continue
1018 copies.setdefault(src, []).append(dst)
1018 copies.setdefault(src, []).append(dst)
1019 repo.dirstate.update(a, 'a')
1019 repo.dirstate.update(a, 'a')
1020 # remember the copies between patchparent and tip
1020 # remember the copies between patchparent and tip
1021 # this may be slow, so don't do it if we're not tracking copies
1021 # this may be slow, so don't do it if we're not tracking copies
1022 if self.diffopts().git:
1022 if self.diffopts().git:
1023 for dst in aaa:
1023 for dst in aaa:
1024 f = repo.file(dst)
1024 f = repo.file(dst)
1025 src = f.renamed(man[dst])
1025 src = f.renamed(man[dst])
1026 if src:
1026 if src:
1027 copies[src[0]] = copies.get(dst, [])
1027 copies[src[0]] = copies.get(dst, [])
1028 if dst in a:
1028 if dst in a:
1029 copies[src[0]].append(dst)
1029 copies[src[0]].append(dst)
1030 # we can't copy a file created by the patch itself
1030 # we can't copy a file created by the patch itself
1031 if dst in copies:
1031 if dst in copies:
1032 del copies[dst]
1032 del copies[dst]
1033 for src, dsts in copies.iteritems():
1033 for src, dsts in copies.iteritems():
1034 for dst in dsts:
1034 for dst in dsts:
1035 repo.dirstate.copy(src, dst)
1035 repo.dirstate.copy(src, dst)
1036 repo.dirstate.update(r, 'r')
1036 repo.dirstate.update(r, 'r')
1037 # if the patch excludes a modified file, mark that file with mtime=0
1037 # if the patch excludes a modified file, mark that file with mtime=0
1038 # so status can see it.
1038 # so status can see it.
1039 mm = []
1039 mm = []
1040 for i in xrange(len(m)-1, -1, -1):
1040 for i in xrange(len(m)-1, -1, -1):
1041 if not matchfn(m[i]):
1041 if not matchfn(m[i]):
1042 mm.append(m[i])
1042 mm.append(m[i])
1043 del m[i]
1043 del m[i]
1044 repo.dirstate.update(m, 'n')
1044 repo.dirstate.update(m, 'n')
1045 repo.dirstate.update(mm, 'n', st_mtime=0)
1045 repo.dirstate.update(mm, 'n', st_mtime=0)
1046 repo.dirstate.forget(forget)
1046 repo.dirstate.forget(forget)
1047
1047
1048 if not msg:
1048 if not msg:
1049 if not message:
1049 if not message:
1050 message = "patch queue: %s\n" % patchfn
1050 message = "patch queue: %s\n" % patchfn
1051 else:
1051 else:
1052 message = "\n".join(message)
1052 message = "\n".join(message)
1053 else:
1053 else:
1054 message = msg
1054 message = msg
1055
1055
1056 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1056 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1057 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1057 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1058 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1058 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1059 self.applied_dirty = 1
1059 self.applied_dirty = 1
1060 else:
1060 else:
1061 self.printdiff(repo, patchparent, fp=patchf)
1061 self.printdiff(repo, patchparent, fp=patchf)
1062 patchf.close()
1062 patchf.close()
1063 added = repo.status()[1]
1063 added = repo.status()[1]
1064 for a in added:
1064 for a in added:
1065 f = repo.wjoin(a)
1065 f = repo.wjoin(a)
1066 try:
1066 try:
1067 os.unlink(f)
1067 os.unlink(f)
1068 except OSError, e:
1068 except OSError, e:
1069 if e.errno != errno.ENOENT:
1069 if e.errno != errno.ENOENT:
1070 raise
1070 raise
1071 try: os.removedirs(os.path.dirname(f))
1071 try: os.removedirs(os.path.dirname(f))
1072 except: pass
1072 except: pass
1073 # forget the file copies in the dirstate
1073 # forget the file copies in the dirstate
1074 # push should readd the files later on
1074 # push should readd the files later on
1075 repo.dirstate.forget(added)
1075 repo.dirstate.forget(added)
1076 self.pop(repo, force=True, wlock=wlock)
1076 self.pop(repo, force=True, wlock=wlock)
1077 self.push(repo, force=True, wlock=wlock)
1077 self.push(repo, force=True, wlock=wlock)
1078
1078
1079 def init(self, repo, create=False):
1079 def init(self, repo, create=False):
1080 if os.path.isdir(self.path):
1080 if os.path.isdir(self.path):
1081 raise util.Abort(_("patch queue directory already exists"))
1081 raise util.Abort(_("patch queue directory already exists"))
1082 os.mkdir(self.path)
1082 os.mkdir(self.path)
1083 if create:
1083 if create:
1084 return self.qrepo(create=True)
1084 return self.qrepo(create=True)
1085
1085
1086 def unapplied(self, repo, patch=None):
1086 def unapplied(self, repo, patch=None):
1087 if patch and patch not in self.series:
1087 if patch and patch not in self.series:
1088 raise util.Abort(_("patch %s is not in series file") % patch)
1088 raise util.Abort(_("patch %s is not in series file") % patch)
1089 if not patch:
1089 if not patch:
1090 start = self.series_end()
1090 start = self.series_end()
1091 else:
1091 else:
1092 start = self.series.index(patch) + 1
1092 start = self.series.index(patch) + 1
1093 unapplied = []
1093 unapplied = []
1094 for i in xrange(start, len(self.series)):
1094 for i in xrange(start, len(self.series)):
1095 pushable, reason = self.pushable(i)
1095 pushable, reason = self.pushable(i)
1096 if pushable:
1096 if pushable:
1097 unapplied.append((i, self.series[i]))
1097 unapplied.append((i, self.series[i]))
1098 self.explain_pushable(i)
1098 self.explain_pushable(i)
1099 return unapplied
1099 return unapplied
1100
1100
1101 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1101 def qseries(self, repo, missing=None, start=0, length=0, status=None,
1102 summary=False):
1102 summary=False):
1103 def displayname(patchname):
1103 def displayname(patchname):
1104 if summary:
1104 if summary:
1105 msg = self.readheaders(patchname)[0]
1105 msg = self.readheaders(patchname)[0]
1106 msg = msg and ': ' + msg[0] or ': '
1106 msg = msg and ': ' + msg[0] or ': '
1107 else:
1107 else:
1108 msg = ''
1108 msg = ''
1109 return '%s%s' % (patchname, msg)
1109 return '%s%s' % (patchname, msg)
1110
1110
1111 def pname(i):
1111 def pname(i):
1112 if status == 'A':
1112 if status == 'A':
1113 return self.applied[i].name
1113 return self.applied[i].name
1114 else:
1114 else:
1115 return self.series[i]
1115 return self.series[i]
1116
1116
1117 applied = dict.fromkeys([p.name for p in self.applied])
1117 applied = dict.fromkeys([p.name for p in self.applied])
1118 if not length:
1118 if not length:
1119 length = len(self.series) - start
1119 length = len(self.series) - start
1120 if not missing:
1120 if not missing:
1121 for i in xrange(start, start+length):
1121 for i in xrange(start, start+length):
1122 pfx = ''
1122 pfx = ''
1123 patch = pname(i)
1123 patch = pname(i)
1124 if self.ui.verbose:
1124 if self.ui.verbose:
1125 if patch in applied:
1125 if patch in applied:
1126 stat = 'A'
1126 stat = 'A'
1127 elif self.pushable(i)[0]:
1127 elif self.pushable(i)[0]:
1128 stat = 'U'
1128 stat = 'U'
1129 else:
1129 else:
1130 stat = 'G'
1130 stat = 'G'
1131 pfx = '%d %s ' % (i, stat)
1131 pfx = '%d %s ' % (i, stat)
1132 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1132 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1133 else:
1133 else:
1134 msng_list = []
1134 msng_list = []
1135 for root, dirs, files in os.walk(self.path):
1135 for root, dirs, files in os.walk(self.path):
1136 d = root[len(self.path) + 1:]
1136 d = root[len(self.path) + 1:]
1137 for f in files:
1137 for f in files:
1138 fl = os.path.join(d, f)
1138 fl = os.path.join(d, f)
1139 if (fl not in self.series and
1139 if (fl not in self.series and
1140 fl not in (self.status_path, self.series_path)
1140 fl not in (self.status_path, self.series_path)
1141 and not fl.startswith('.')):
1141 and not fl.startswith('.')):
1142 msng_list.append(fl)
1142 msng_list.append(fl)
1143 msng_list.sort()
1143 msng_list.sort()
1144 for x in msng_list:
1144 for x in msng_list:
1145 pfx = self.ui.verbose and ('D ') or ''
1145 pfx = self.ui.verbose and ('D ') or ''
1146 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1146 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1147
1147
1148 def issaveline(self, l):
1148 def issaveline(self, l):
1149 if l.name == '.hg.patches.save.line':
1149 if l.name == '.hg.patches.save.line':
1150 return True
1150 return True
1151
1151
1152 def qrepo(self, create=False):
1152 def qrepo(self, create=False):
1153 if create or os.path.isdir(self.join(".hg")):
1153 if create or os.path.isdir(self.join(".hg")):
1154 return hg.repository(self.ui, path=self.path, create=create)
1154 return hg.repository(self.ui, path=self.path, create=create)
1155
1155
1156 def restore(self, repo, rev, delete=None, qupdate=None):
1156 def restore(self, repo, rev, delete=None, qupdate=None):
1157 c = repo.changelog.read(rev)
1157 c = repo.changelog.read(rev)
1158 desc = c[4].strip()
1158 desc = c[4].strip()
1159 lines = desc.splitlines()
1159 lines = desc.splitlines()
1160 i = 0
1160 i = 0
1161 datastart = None
1161 datastart = None
1162 series = []
1162 series = []
1163 applied = []
1163 applied = []
1164 qpp = None
1164 qpp = None
1165 for i in xrange(0, len(lines)):
1165 for i in xrange(0, len(lines)):
1166 if lines[i] == 'Patch Data:':
1166 if lines[i] == 'Patch Data:':
1167 datastart = i + 1
1167 datastart = i + 1
1168 elif lines[i].startswith('Dirstate:'):
1168 elif lines[i].startswith('Dirstate:'):
1169 l = lines[i].rstrip()
1169 l = lines[i].rstrip()
1170 l = l[10:].split(' ')
1170 l = l[10:].split(' ')
1171 qpp = [ hg.bin(x) for x in l ]
1171 qpp = [ hg.bin(x) for x in l ]
1172 elif datastart != None:
1172 elif datastart != None:
1173 l = lines[i].rstrip()
1173 l = lines[i].rstrip()
1174 se = statusentry(l)
1174 se = statusentry(l)
1175 file_ = se.name
1175 file_ = se.name
1176 if se.rev:
1176 if se.rev:
1177 applied.append(se)
1177 applied.append(se)
1178 else:
1178 else:
1179 series.append(file_)
1179 series.append(file_)
1180 if datastart == None:
1180 if datastart == None:
1181 self.ui.warn("No saved patch data found\n")
1181 self.ui.warn("No saved patch data found\n")
1182 return 1
1182 return 1
1183 self.ui.warn("restoring status: %s\n" % lines[0])
1183 self.ui.warn("restoring status: %s\n" % lines[0])
1184 self.full_series = series
1184 self.full_series = series
1185 self.applied = applied
1185 self.applied = applied
1186 self.parse_series()
1186 self.parse_series()
1187 self.series_dirty = 1
1187 self.series_dirty = 1
1188 self.applied_dirty = 1
1188 self.applied_dirty = 1
1189 heads = repo.changelog.heads()
1189 heads = repo.changelog.heads()
1190 if delete:
1190 if delete:
1191 if rev not in heads:
1191 if rev not in heads:
1192 self.ui.warn("save entry has children, leaving it alone\n")
1192 self.ui.warn("save entry has children, leaving it alone\n")
1193 else:
1193 else:
1194 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1194 self.ui.warn("removing save entry %s\n" % hg.short(rev))
1195 pp = repo.dirstate.parents()
1195 pp = repo.dirstate.parents()
1196 if rev in pp:
1196 if rev in pp:
1197 update = True
1197 update = True
1198 else:
1198 else:
1199 update = False
1199 update = False
1200 self.strip(repo, rev, update=update, backup='strip')
1200 self.strip(repo, rev, update=update, backup='strip')
1201 if qpp:
1201 if qpp:
1202 self.ui.warn("saved queue repository parents: %s %s\n" %
1202 self.ui.warn("saved queue repository parents: %s %s\n" %
1203 (hg.short(qpp[0]), hg.short(qpp[1])))
1203 (hg.short(qpp[0]), hg.short(qpp[1])))
1204 if qupdate:
1204 if qupdate:
1205 print "queue directory updating"
1205 print "queue directory updating"
1206 r = self.qrepo()
1206 r = self.qrepo()
1207 if not r:
1207 if not r:
1208 self.ui.warn("Unable to load queue repository\n")
1208 self.ui.warn("Unable to load queue repository\n")
1209 return 1
1209 return 1
1210 hg.clean(r, qpp[0])
1210 hg.clean(r, qpp[0])
1211
1211
1212 def save(self, repo, msg=None):
1212 def save(self, repo, msg=None):
1213 if len(self.applied) == 0:
1213 if len(self.applied) == 0:
1214 self.ui.warn("save: no patches applied, exiting\n")
1214 self.ui.warn("save: no patches applied, exiting\n")
1215 return 1
1215 return 1
1216 if self.issaveline(self.applied[-1]):
1216 if self.issaveline(self.applied[-1]):
1217 self.ui.warn("status is already saved\n")
1217 self.ui.warn("status is already saved\n")
1218 return 1
1218 return 1
1219
1219
1220 ar = [ ':' + x for x in self.full_series ]
1220 ar = [ ':' + x for x in self.full_series ]
1221 if not msg:
1221 if not msg:
1222 msg = "hg patches saved state"
1222 msg = "hg patches saved state"
1223 else:
1223 else:
1224 msg = "hg patches: " + msg.rstrip('\r\n')
1224 msg = "hg patches: " + msg.rstrip('\r\n')
1225 r = self.qrepo()
1225 r = self.qrepo()
1226 if r:
1226 if r:
1227 pp = r.dirstate.parents()
1227 pp = r.dirstate.parents()
1228 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1228 msg += "\nDirstate: %s %s" % (hg.hex(pp[0]), hg.hex(pp[1]))
1229 msg += "\n\nPatch Data:\n"
1229 msg += "\n\nPatch Data:\n"
1230 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1230 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1231 "\n".join(ar) + '\n' or "")
1231 "\n".join(ar) + '\n' or "")
1232 n = repo.commit(None, text, user=None, force=1)
1232 n = repo.commit(None, text, user=None, force=1)
1233 if not n:
1233 if not n:
1234 self.ui.warn("repo commit failed\n")
1234 self.ui.warn("repo commit failed\n")
1235 return 1
1235 return 1
1236 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1236 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1237 self.applied_dirty = 1
1237 self.applied_dirty = 1
1238
1238
1239 def full_series_end(self):
1239 def full_series_end(self):
1240 if len(self.applied) > 0:
1240 if len(self.applied) > 0:
1241 p = self.applied[-1].name
1241 p = self.applied[-1].name
1242 end = self.find_series(p)
1242 end = self.find_series(p)
1243 if end == None:
1243 if end == None:
1244 return len(self.full_series)
1244 return len(self.full_series)
1245 return end + 1
1245 return end + 1
1246 return 0
1246 return 0
1247
1247
1248 def series_end(self, all_patches=False):
1248 def series_end(self, all_patches=False):
1249 end = 0
1249 end = 0
1250 def next(start):
1250 def next(start):
1251 if all_patches:
1251 if all_patches:
1252 return start
1252 return start
1253 i = start
1253 i = start
1254 while i < len(self.series):
1254 while i < len(self.series):
1255 p, reason = self.pushable(i)
1255 p, reason = self.pushable(i)
1256 if p:
1256 if p:
1257 break
1257 break
1258 self.explain_pushable(i)
1258 self.explain_pushable(i)
1259 i += 1
1259 i += 1
1260 return i
1260 return i
1261 if len(self.applied) > 0:
1261 if len(self.applied) > 0:
1262 p = self.applied[-1].name
1262 p = self.applied[-1].name
1263 try:
1263 try:
1264 end = self.series.index(p)
1264 end = self.series.index(p)
1265 except ValueError:
1265 except ValueError:
1266 return 0
1266 return 0
1267 return next(end + 1)
1267 return next(end + 1)
1268 return next(end)
1268 return next(end)
1269
1269
1270 def appliedname(self, index):
1270 def appliedname(self, index):
1271 pname = self.applied[index].name
1271 pname = self.applied[index].name
1272 if not self.ui.verbose:
1272 if not self.ui.verbose:
1273 p = pname
1273 p = pname
1274 else:
1274 else:
1275 p = str(self.series.index(pname)) + " " + pname
1275 p = str(self.series.index(pname)) + " " + pname
1276 return p
1276 return p
1277
1277
1278 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1278 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1279 force=None, git=False):
1279 force=None, git=False):
1280 def checkseries(patchname):
1280 def checkseries(patchname):
1281 if patchname in self.series:
1281 if patchname in self.series:
1282 raise util.Abort(_('patch %s is already in the series file')
1282 raise util.Abort(_('patch %s is already in the series file')
1283 % patchname)
1283 % patchname)
1284 def checkfile(patchname):
1284 def checkfile(patchname):
1285 if not force and os.path.exists(self.join(patchname)):
1285 if not force and os.path.exists(self.join(patchname)):
1286 raise util.Abort(_('patch "%s" already exists')
1286 raise util.Abort(_('patch "%s" already exists')
1287 % patchname)
1287 % patchname)
1288
1288
1289 if rev:
1289 if rev:
1290 if files:
1290 if files:
1291 raise util.Abort(_('option "-r" not valid when importing '
1291 raise util.Abort(_('option "-r" not valid when importing '
1292 'files'))
1292 'files'))
1293 rev = cmdutil.revrange(repo, rev)
1293 rev = cmdutil.revrange(repo, rev)
1294 rev.sort(lambda x, y: cmp(y, x))
1294 rev.sort(lambda x, y: cmp(y, x))
1295 if (len(files) > 1 or len(rev) > 1) and patchname:
1295 if (len(files) > 1 or len(rev) > 1) and patchname:
1296 raise util.Abort(_('option "-n" not valid when importing multiple '
1296 raise util.Abort(_('option "-n" not valid when importing multiple '
1297 'patches'))
1297 'patches'))
1298 i = 0
1298 i = 0
1299 added = []
1299 added = []
1300 if rev:
1300 if rev:
1301 # If mq patches are applied, we can only import revisions
1301 # If mq patches are applied, we can only import revisions
1302 # that form a linear path to qbase.
1302 # that form a linear path to qbase.
1303 # Otherwise, they should form a linear path to a head.
1303 # Otherwise, they should form a linear path to a head.
1304 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1304 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1305 if len(heads) > 1:
1305 if len(heads) > 1:
1306 raise util.Abort(_('revision %d is the root of more than one '
1306 raise util.Abort(_('revision %d is the root of more than one '
1307 'branch') % rev[-1])
1307 'branch') % rev[-1])
1308 if self.applied:
1308 if self.applied:
1309 base = revlog.hex(repo.changelog.node(rev[0]))
1309 base = revlog.hex(repo.changelog.node(rev[0]))
1310 if base in [n.rev for n in self.applied]:
1310 if base in [n.rev for n in self.applied]:
1311 raise util.Abort(_('revision %d is already managed')
1311 raise util.Abort(_('revision %d is already managed')
1312 % rev[0])
1312 % rev[0])
1313 if heads != [revlog.bin(self.applied[-1].rev)]:
1313 if heads != [revlog.bin(self.applied[-1].rev)]:
1314 raise util.Abort(_('revision %d is not the parent of '
1314 raise util.Abort(_('revision %d is not the parent of '
1315 'the queue') % rev[0])
1315 'the queue') % rev[0])
1316 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1316 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1317 lastparent = repo.changelog.parentrevs(base)[0]
1317 lastparent = repo.changelog.parentrevs(base)[0]
1318 else:
1318 else:
1319 if heads != [repo.changelog.node(rev[0])]:
1319 if heads != [repo.changelog.node(rev[0])]:
1320 raise util.Abort(_('revision %d has unmanaged children')
1320 raise util.Abort(_('revision %d has unmanaged children')
1321 % rev[0])
1321 % rev[0])
1322 lastparent = None
1322 lastparent = None
1323
1323
1324 if git:
1324 if git:
1325 self.diffopts().git = True
1325 self.diffopts().git = True
1326
1326
1327 for r in rev:
1327 for r in rev:
1328 p1, p2 = repo.changelog.parentrevs(r)
1328 p1, p2 = repo.changelog.parentrevs(r)
1329 n = repo.changelog.node(r)
1329 n = repo.changelog.node(r)
1330 if p2 != revlog.nullrev:
1330 if p2 != revlog.nullrev:
1331 raise util.Abort(_('cannot import merge revision %d') % r)
1331 raise util.Abort(_('cannot import merge revision %d') % r)
1332 if lastparent and lastparent != r:
1332 if lastparent and lastparent != r:
1333 raise util.Abort(_('revision %d is not the parent of %d')
1333 raise util.Abort(_('revision %d is not the parent of %d')
1334 % (r, lastparent))
1334 % (r, lastparent))
1335 lastparent = p1
1335 lastparent = p1
1336
1336
1337 if not patchname:
1337 if not patchname:
1338 patchname = '%d.diff' % r
1338 patchname = '%d.diff' % r
1339 checkseries(patchname)
1339 checkseries(patchname)
1340 checkfile(patchname)
1340 checkfile(patchname)
1341 self.full_series.insert(0, patchname)
1341 self.full_series.insert(0, patchname)
1342
1342
1343 patchf = self.opener(patchname, "w")
1343 patchf = self.opener(patchname, "w")
1344 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1344 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1345 patchf.close()
1345 patchf.close()
1346
1346
1347 se = statusentry(revlog.hex(n), patchname)
1347 se = statusentry(revlog.hex(n), patchname)
1348 self.applied.insert(0, se)
1348 self.applied.insert(0, se)
1349
1349
1350 added.append(patchname)
1350 added.append(patchname)
1351 patchname = None
1351 patchname = None
1352 self.parse_series()
1352 self.parse_series()
1353 self.applied_dirty = 1
1353 self.applied_dirty = 1
1354
1354
1355 for filename in files:
1355 for filename in files:
1356 if existing:
1356 if existing:
1357 if filename == '-':
1357 if filename == '-':
1358 raise util.Abort(_('-e is incompatible with import from -'))
1358 raise util.Abort(_('-e is incompatible with import from -'))
1359 if not patchname:
1359 if not patchname:
1360 patchname = filename
1360 patchname = filename
1361 if not os.path.isfile(self.join(patchname)):
1361 if not os.path.isfile(self.join(patchname)):
1362 raise util.Abort(_("patch %s does not exist") % patchname)
1362 raise util.Abort(_("patch %s does not exist") % patchname)
1363 else:
1363 else:
1364 try:
1364 try:
1365 if filename == '-':
1365 if filename == '-':
1366 if not patchname:
1366 if not patchname:
1367 raise util.Abort(_('need --name to import a patch from -'))
1367 raise util.Abort(_('need --name to import a patch from -'))
1368 text = sys.stdin.read()
1368 text = sys.stdin.read()
1369 else:
1369 else:
1370 text = file(filename).read()
1370 text = file(filename).read()
1371 except IOError:
1371 except IOError:
1372 raise util.Abort(_("unable to read %s") % patchname)
1372 raise util.Abort(_("unable to read %s") % patchname)
1373 if not patchname:
1373 if not patchname:
1374 patchname = os.path.basename(filename)
1374 patchname = os.path.basename(filename)
1375 checkfile(patchname)
1375 checkfile(patchname)
1376 patchf = self.opener(patchname, "w")
1376 patchf = self.opener(patchname, "w")
1377 patchf.write(text)
1377 patchf.write(text)
1378 checkseries(patchname)
1378 checkseries(patchname)
1379 index = self.full_series_end() + i
1379 index = self.full_series_end() + i
1380 self.full_series[index:index] = [patchname]
1380 self.full_series[index:index] = [patchname]
1381 self.parse_series()
1381 self.parse_series()
1382 self.ui.warn("adding %s to series file\n" % patchname)
1382 self.ui.warn("adding %s to series file\n" % patchname)
1383 i += 1
1383 i += 1
1384 added.append(patchname)
1384 added.append(patchname)
1385 patchname = None
1385 patchname = None
1386 self.series_dirty = 1
1386 self.series_dirty = 1
1387 qrepo = self.qrepo()
1387 qrepo = self.qrepo()
1388 if qrepo:
1388 if qrepo:
1389 qrepo.add(added)
1389 qrepo.add(added)
1390
1390
1391 def delete(ui, repo, *patches, **opts):
1391 def delete(ui, repo, *patches, **opts):
1392 """remove patches from queue
1392 """remove patches from queue
1393
1393
1394 With --rev, mq will stop managing the named revisions. The
1394 With --rev, mq will stop managing the named revisions. The
1395 patches must be applied and at the base of the stack. This option
1395 patches must be applied and at the base of the stack. This option
1396 is useful when the patches have been applied upstream.
1396 is useful when the patches have been applied upstream.
1397
1397
1398 Otherwise, the patches must not be applied.
1398 Otherwise, the patches must not be applied.
1399
1399
1400 With --keep, the patch files are preserved in the patch directory."""
1400 With --keep, the patch files are preserved in the patch directory."""
1401 q = repo.mq
1401 q = repo.mq
1402 q.delete(repo, patches, opts)
1402 q.delete(repo, patches, opts)
1403 q.save_dirty()
1403 q.save_dirty()
1404 return 0
1404 return 0
1405
1405
1406 def applied(ui, repo, patch=None, **opts):
1406 def applied(ui, repo, patch=None, **opts):
1407 """print the patches already applied"""
1407 """print the patches already applied"""
1408 q = repo.mq
1408 q = repo.mq
1409 if patch:
1409 if patch:
1410 if patch not in q.series:
1410 if patch not in q.series:
1411 raise util.Abort(_("patch %s is not in series file") % patch)
1411 raise util.Abort(_("patch %s is not in series file") % patch)
1412 end = q.series.index(patch) + 1
1412 end = q.series.index(patch) + 1
1413 else:
1413 else:
1414 end = len(q.applied)
1414 end = len(q.applied)
1415 if not end:
1415 if not end:
1416 return
1416 return
1417
1417
1418 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1418 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1419
1419
1420 def unapplied(ui, repo, patch=None, **opts):
1420 def unapplied(ui, repo, patch=None, **opts):
1421 """print the patches not yet applied"""
1421 """print the patches not yet applied"""
1422 q = repo.mq
1422 q = repo.mq
1423 if patch:
1423 if patch:
1424 if patch not in q.series:
1424 if patch not in q.series:
1425 raise util.Abort(_("patch %s is not in series file") % patch)
1425 raise util.Abort(_("patch %s is not in series file") % patch)
1426 start = q.series.index(patch) + 1
1426 start = q.series.index(patch) + 1
1427 else:
1427 else:
1428 start = q.series_end()
1428 start = q.series_end()
1429 q.qseries(repo, start=start, summary=opts.get('summary'))
1429 q.qseries(repo, start=start, summary=opts.get('summary'))
1430
1430
1431 def qimport(ui, repo, *filename, **opts):
1431 def qimport(ui, repo, *filename, **opts):
1432 """import a patch
1432 """import a patch
1433
1433
1434 The patch will have the same name as its source file unless you
1434 The patch will have the same name as its source file unless you
1435 give it a new one with --name.
1435 give it a new one with --name.
1436
1436
1437 You can register an existing patch inside the patch directory
1437 You can register an existing patch inside the patch directory
1438 with the --existing flag.
1438 with the --existing flag.
1439
1439
1440 With --force, an existing patch of the same name will be overwritten.
1440 With --force, an existing patch of the same name will be overwritten.
1441
1441
1442 An existing changeset may be placed under mq control with --rev
1442 An existing changeset may be placed under mq control with --rev
1443 (e.g. qimport --rev tip -n patch will place tip under mq control).
1443 (e.g. qimport --rev tip -n patch will place tip under mq control).
1444 With --git, patches imported with --rev will use the git diff
1444 With --git, patches imported with --rev will use the git diff
1445 format.
1445 format.
1446 """
1446 """
1447 q = repo.mq
1447 q = repo.mq
1448 q.qimport(repo, filename, patchname=opts['name'],
1448 q.qimport(repo, filename, patchname=opts['name'],
1449 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1449 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1450 git=opts['git'])
1450 git=opts['git'])
1451 q.save_dirty()
1451 q.save_dirty()
1452 return 0
1452 return 0
1453
1453
1454 def init(ui, repo, **opts):
1454 def init(ui, repo, **opts):
1455 """init a new queue repository
1455 """init a new queue repository
1456
1456
1457 The queue repository is unversioned by default. If -c is
1457 The queue repository is unversioned by default. If -c is
1458 specified, qinit will create a separate nested repository
1458 specified, qinit will create a separate nested repository
1459 for patches. Use qcommit to commit changes to this queue
1459 for patches. Use qcommit to commit changes to this queue
1460 repository."""
1460 repository."""
1461 q = repo.mq
1461 q = repo.mq
1462 r = q.init(repo, create=opts['create_repo'])
1462 r = q.init(repo, create=opts['create_repo'])
1463 q.save_dirty()
1463 q.save_dirty()
1464 if r:
1464 if r:
1465 fp = r.wopener('.hgignore', 'w')
1465 fp = r.wopener('.hgignore', 'w')
1466 print >> fp, 'syntax: glob'
1466 print >> fp, 'syntax: glob'
1467 print >> fp, 'status'
1467 print >> fp, 'status'
1468 print >> fp, 'guards'
1468 print >> fp, 'guards'
1469 fp.close()
1469 fp.close()
1470 r.wopener('series', 'w').close()
1470 r.wopener('series', 'w').close()
1471 r.add(['.hgignore', 'series'])
1471 r.add(['.hgignore', 'series'])
1472 return 0
1472 return 0
1473
1473
1474 def clone(ui, source, dest=None, **opts):
1474 def clone(ui, source, dest=None, **opts):
1475 '''clone main and patch repository at same time
1475 '''clone main and patch repository at same time
1476
1476
1477 If source is local, destination will have no patches applied. If
1477 If source is local, destination will have no patches applied. If
1478 source is remote, this command can not check if patches are
1478 source is remote, this command can not check if patches are
1479 applied in source, so cannot guarantee that patches are not
1479 applied in source, so cannot guarantee that patches are not
1480 applied in destination. If you clone remote repository, be sure
1480 applied in destination. If you clone remote repository, be sure
1481 before that it has no patches applied.
1481 before that it has no patches applied.
1482
1482
1483 Source patch repository is looked for in <src>/.hg/patches by
1483 Source patch repository is looked for in <src>/.hg/patches by
1484 default. Use -p <url> to change.
1484 default. Use -p <url> to change.
1485 '''
1485 '''
1486 commands.setremoteconfig(ui, opts)
1486 commands.setremoteconfig(ui, opts)
1487 if dest is None:
1487 if dest is None:
1488 dest = hg.defaultdest(source)
1488 dest = hg.defaultdest(source)
1489 sr = hg.repository(ui, ui.expandpath(source))
1489 sr = hg.repository(ui, ui.expandpath(source))
1490 qbase, destrev = None, None
1490 qbase, destrev = None, None
1491 if sr.local():
1491 if sr.local():
1492 reposetup(ui, sr)
1492 reposetup(ui, sr)
1493 if sr.mq.applied:
1493 if sr.mq.applied:
1494 qbase = revlog.bin(sr.mq.applied[0].rev)
1494 qbase = revlog.bin(sr.mq.applied[0].rev)
1495 if not hg.islocal(dest):
1495 if not hg.islocal(dest):
1496 destrev = sr.parents(qbase)[0]
1496 destrev = sr.parents(qbase)[0]
1497 ui.note(_('cloning main repo\n'))
1497 ui.note(_('cloning main repo\n'))
1498 sr, dr = hg.clone(ui, sr, dest,
1498 sr, dr = hg.clone(ui, sr, dest,
1499 pull=opts['pull'],
1499 pull=opts['pull'],
1500 rev=destrev,
1500 rev=destrev,
1501 update=False,
1501 update=False,
1502 stream=opts['uncompressed'])
1502 stream=opts['uncompressed'])
1503 ui.note(_('cloning patch repo\n'))
1503 ui.note(_('cloning patch repo\n'))
1504 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1504 spr, dpr = hg.clone(ui, opts['patches'] or (sr.url() + '/.hg/patches'),
1505 dr.url() + '/.hg/patches',
1505 dr.url() + '/.hg/patches',
1506 pull=opts['pull'],
1506 pull=opts['pull'],
1507 update=not opts['noupdate'],
1507 update=not opts['noupdate'],
1508 stream=opts['uncompressed'])
1508 stream=opts['uncompressed'])
1509 if dr.local():
1509 if dr.local():
1510 if qbase:
1510 if qbase:
1511 ui.note(_('stripping applied patches from destination repo\n'))
1511 ui.note(_('stripping applied patches from destination repo\n'))
1512 reposetup(ui, dr)
1512 reposetup(ui, dr)
1513 dr.mq.strip(dr, qbase, update=False, backup=None)
1513 dr.mq.strip(dr, qbase, update=False, backup=None)
1514 if not opts['noupdate']:
1514 if not opts['noupdate']:
1515 ui.note(_('updating destination repo\n'))
1515 ui.note(_('updating destination repo\n'))
1516 hg.update(dr, dr.changelog.tip())
1516 hg.update(dr, dr.changelog.tip())
1517
1517
1518 def commit(ui, repo, *pats, **opts):
1518 def commit(ui, repo, *pats, **opts):
1519 """commit changes in the queue repository"""
1519 """commit changes in the queue repository"""
1520 q = repo.mq
1520 q = repo.mq
1521 r = q.qrepo()
1521 r = q.qrepo()
1522 if not r: raise util.Abort('no queue repository')
1522 if not r: raise util.Abort('no queue repository')
1523 commands.commit(r.ui, r, *pats, **opts)
1523 commands.commit(r.ui, r, *pats, **opts)
1524
1524
1525 def series(ui, repo, **opts):
1525 def series(ui, repo, **opts):
1526 """print the entire series file"""
1526 """print the entire series file"""
1527 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1527 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1528 return 0
1528 return 0
1529
1529
1530 def top(ui, repo, **opts):
1530 def top(ui, repo, **opts):
1531 """print the name of the current patch"""
1531 """print the name of the current patch"""
1532 q = repo.mq
1532 q = repo.mq
1533 t = len(q.applied)
1533 t = len(q.applied)
1534 if t:
1534 if t:
1535 return q.qseries(repo, start=t-1, length=1, status='A',
1535 return q.qseries(repo, start=t-1, length=1, status='A',
1536 summary=opts.get('summary'))
1536 summary=opts.get('summary'))
1537 else:
1537 else:
1538 ui.write("No patches applied\n")
1538 ui.write("No patches applied\n")
1539 return 1
1539 return 1
1540
1540
1541 def next(ui, repo, **opts):
1541 def next(ui, repo, **opts):
1542 """print the name of the next patch"""
1542 """print the name of the next patch"""
1543 q = repo.mq
1543 q = repo.mq
1544 end = q.series_end()
1544 end = q.series_end()
1545 if end == len(q.series):
1545 if end == len(q.series):
1546 ui.write("All patches applied\n")
1546 ui.write("All patches applied\n")
1547 return 1
1547 return 1
1548 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1548 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1549
1549
1550 def prev(ui, repo, **opts):
1550 def prev(ui, repo, **opts):
1551 """print the name of the previous patch"""
1551 """print the name of the previous patch"""
1552 q = repo.mq
1552 q = repo.mq
1553 l = len(q.applied)
1553 l = len(q.applied)
1554 if l == 1:
1554 if l == 1:
1555 ui.write("Only one patch applied\n")
1555 ui.write("Only one patch applied\n")
1556 return 1
1556 return 1
1557 if not l:
1557 if not l:
1558 ui.write("No patches applied\n")
1558 ui.write("No patches applied\n")
1559 return 1
1559 return 1
1560 return q.qseries(repo, start=l-2, length=1, status='A',
1560 return q.qseries(repo, start=l-2, length=1, status='A',
1561 summary=opts.get('summary'))
1561 summary=opts.get('summary'))
1562
1562
1563 def new(ui, repo, patch, **opts):
1563 def new(ui, repo, patch, **opts):
1564 """create a new patch
1564 """create a new patch
1565
1565
1566 qnew creates a new patch on top of the currently-applied patch
1566 qnew creates a new patch on top of the currently-applied patch
1567 (if any). It will refuse to run if there are any outstanding
1567 (if any). It will refuse to run if there are any outstanding
1568 changes unless -f is specified, in which case the patch will
1568 changes unless -f is specified, in which case the patch will
1569 be initialised with them.
1569 be initialised with them.
1570
1570
1571 -e, -m or -l set the patch header as well as the commit message.
1571 -e, -m or -l set the patch header as well as the commit message.
1572 If none is specified, the patch header is empty and the
1572 If none is specified, the patch header is empty and the
1573 commit message is 'New patch: PATCH'"""
1573 commit message is 'New patch: PATCH'"""
1574 q = repo.mq
1574 q = repo.mq
1575 message = commands.logmessage(opts)
1575 message = commands.logmessage(opts)
1576 if opts['edit']:
1576 if opts['edit']:
1577 message = ui.edit(message, ui.username())
1577 message = ui.edit(message, ui.username())
1578 q.new(repo, patch, msg=message, force=opts['force'])
1578 q.new(repo, patch, msg=message, force=opts['force'])
1579 q.save_dirty()
1579 q.save_dirty()
1580 return 0
1580 return 0
1581
1581
1582 def refresh(ui, repo, *pats, **opts):
1582 def refresh(ui, repo, *pats, **opts):
1583 """update the current patch
1583 """update the current patch
1584
1584
1585 If any file patterns are provided, the refreshed patch will contain only
1585 If any file patterns are provided, the refreshed patch will contain only
1586 the modifications that match those patterns; the remaining modifications
1586 the modifications that match those patterns; the remaining modifications
1587 will remain in the working directory.
1587 will remain in the working directory.
1588 """
1588 """
1589 q = repo.mq
1589 q = repo.mq
1590 message = commands.logmessage(opts)
1590 message = commands.logmessage(opts)
1591 if opts['edit']:
1591 if opts['edit']:
1592 if message:
1592 if message:
1593 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1593 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1594 patch = q.applied[-1].name
1594 patch = q.applied[-1].name
1595 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1595 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1596 message = ui.edit('\n'.join(message), user or ui.username())
1596 message = ui.edit('\n'.join(message), user or ui.username())
1597 ret = q.refresh(repo, pats, msg=message, **opts)
1597 ret = q.refresh(repo, pats, msg=message, **opts)
1598 q.save_dirty()
1598 q.save_dirty()
1599 return ret
1599 return ret
1600
1600
1601 def diff(ui, repo, *pats, **opts):
1601 def diff(ui, repo, *pats, **opts):
1602 """diff of the current patch"""
1602 """diff of the current patch"""
1603 repo.mq.diff(repo, pats, opts)
1603 repo.mq.diff(repo, pats, opts)
1604 return 0
1604 return 0
1605
1605
1606 def fold(ui, repo, *files, **opts):
1606 def fold(ui, repo, *files, **opts):
1607 """fold the named patches into the current patch
1607 """fold the named patches into the current patch
1608
1608
1609 Patches must not yet be applied. Each patch will be successively
1609 Patches must not yet be applied. Each patch will be successively
1610 applied to the current patch in the order given. If all the
1610 applied to the current patch in the order given. If all the
1611 patches apply successfully, the current patch will be refreshed
1611 patches apply successfully, the current patch will be refreshed
1612 with the new cumulative patch, and the folded patches will
1612 with the new cumulative patch, and the folded patches will
1613 be deleted. With -k/--keep, the folded patch files will not
1613 be deleted. With -k/--keep, the folded patch files will not
1614 be removed afterwards.
1614 be removed afterwards.
1615
1615
1616 The header for each folded patch will be concatenated with
1616 The header for each folded patch will be concatenated with
1617 the current patch header, separated by a line of '* * *'."""
1617 the current patch header, separated by a line of '* * *'."""
1618
1618
1619 q = repo.mq
1619 q = repo.mq
1620
1620
1621 if not files:
1621 if not files:
1622 raise util.Abort(_('qfold requires at least one patch name'))
1622 raise util.Abort(_('qfold requires at least one patch name'))
1623 if not q.check_toppatch(repo):
1623 if not q.check_toppatch(repo):
1624 raise util.Abort(_('No patches applied'))
1624 raise util.Abort(_('No patches applied'))
1625
1625
1626 message = commands.logmessage(opts)
1626 message = commands.logmessage(opts)
1627 if opts['edit']:
1627 if opts['edit']:
1628 if message:
1628 if message:
1629 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1629 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1630
1630
1631 parent = q.lookup('qtip')
1631 parent = q.lookup('qtip')
1632 patches = []
1632 patches = []
1633 messages = []
1633 messages = []
1634 for f in files:
1634 for f in files:
1635 p = q.lookup(f)
1635 p = q.lookup(f)
1636 if p in patches or p == parent:
1636 if p in patches or p == parent:
1637 ui.warn(_('Skipping already folded patch %s') % p)
1637 ui.warn(_('Skipping already folded patch %s') % p)
1638 if q.isapplied(p):
1638 if q.isapplied(p):
1639 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1639 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1640 patches.append(p)
1640 patches.append(p)
1641
1641
1642 for p in patches:
1642 for p in patches:
1643 if not message:
1643 if not message:
1644 messages.append(q.readheaders(p)[0])
1644 messages.append(q.readheaders(p)[0])
1645 pf = q.join(p)
1645 pf = q.join(p)
1646 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1646 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1647 if not patchsuccess:
1647 if not patchsuccess:
1648 raise util.Abort(_('Error folding patch %s') % p)
1648 raise util.Abort(_('Error folding patch %s') % p)
1649 patch.updatedir(ui, repo, files)
1649 patch.updatedir(ui, repo, files)
1650
1650
1651 if not message:
1651 if not message:
1652 message, comments, user = q.readheaders(parent)[0:3]
1652 message, comments, user = q.readheaders(parent)[0:3]
1653 for msg in messages:
1653 for msg in messages:
1654 message.append('* * *')
1654 message.append('* * *')
1655 message.extend(msg)
1655 message.extend(msg)
1656 message = '\n'.join(message)
1656 message = '\n'.join(message)
1657
1657
1658 if opts['edit']:
1658 if opts['edit']:
1659 message = ui.edit(message, user or ui.username())
1659 message = ui.edit(message, user or ui.username())
1660
1660
1661 q.refresh(repo, msg=message)
1661 q.refresh(repo, msg=message)
1662 q.delete(repo, patches, opts)
1662 q.delete(repo, patches, opts)
1663 q.save_dirty()
1663 q.save_dirty()
1664
1664
1665 def guard(ui, repo, *args, **opts):
1665 def guard(ui, repo, *args, **opts):
1666 '''set or print guards for a patch
1666 '''set or print guards for a patch
1667
1667
1668 Guards control whether a patch can be pushed. A patch with no
1668 Guards control whether a patch can be pushed. A patch with no
1669 guards is always pushed. A patch with a positive guard ("+foo") is
1669 guards is always pushed. A patch with a positive guard ("+foo") is
1670 pushed only if the qselect command has activated it. A patch with
1670 pushed only if the qselect command has activated it. A patch with
1671 a negative guard ("-foo") is never pushed if the qselect command
1671 a negative guard ("-foo") is never pushed if the qselect command
1672 has activated it.
1672 has activated it.
1673
1673
1674 With no arguments, print the currently active guards.
1674 With no arguments, print the currently active guards.
1675 With arguments, set guards for the named patch.
1675 With arguments, set guards for the named patch.
1676
1676
1677 To set a negative guard "-foo" on topmost patch ("--" is needed so
1677 To set a negative guard "-foo" on topmost patch ("--" is needed so
1678 hg will not interpret "-foo" as an option):
1678 hg will not interpret "-foo" as an option):
1679 hg qguard -- -foo
1679 hg qguard -- -foo
1680
1680
1681 To set guards on another patch:
1681 To set guards on another patch:
1682 hg qguard other.patch +2.6.17 -stable
1682 hg qguard other.patch +2.6.17 -stable
1683 '''
1683 '''
1684 def status(idx):
1684 def status(idx):
1685 guards = q.series_guards[idx] or ['unguarded']
1685 guards = q.series_guards[idx] or ['unguarded']
1686 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1686 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1687 q = repo.mq
1687 q = repo.mq
1688 patch = None
1688 patch = None
1689 args = list(args)
1689 args = list(args)
1690 if opts['list']:
1690 if opts['list']:
1691 if args or opts['none']:
1691 if args or opts['none']:
1692 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1692 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1693 for i in xrange(len(q.series)):
1693 for i in xrange(len(q.series)):
1694 status(i)
1694 status(i)
1695 return
1695 return
1696 if not args or args[0][0:1] in '-+':
1696 if not args or args[0][0:1] in '-+':
1697 if not q.applied:
1697 if not q.applied:
1698 raise util.Abort(_('no patches applied'))
1698 raise util.Abort(_('no patches applied'))
1699 patch = q.applied[-1].name
1699 patch = q.applied[-1].name
1700 if patch is None and args[0][0:1] not in '-+':
1700 if patch is None and args[0][0:1] not in '-+':
1701 patch = args.pop(0)
1701 patch = args.pop(0)
1702 if patch is None:
1702 if patch is None:
1703 raise util.Abort(_('no patch to work with'))
1703 raise util.Abort(_('no patch to work with'))
1704 if args or opts['none']:
1704 if args or opts['none']:
1705 q.set_guards(q.find_series(patch), args)
1705 q.set_guards(q.find_series(patch), args)
1706 q.save_dirty()
1706 q.save_dirty()
1707 else:
1707 else:
1708 status(q.series.index(q.lookup(patch)))
1708 status(q.series.index(q.lookup(patch)))
1709
1709
1710 def header(ui, repo, patch=None):
1710 def header(ui, repo, patch=None):
1711 """Print the header of the topmost or specified patch"""
1711 """Print the header of the topmost or specified patch"""
1712 q = repo.mq
1712 q = repo.mq
1713
1713
1714 if patch:
1714 if patch:
1715 patch = q.lookup(patch)
1715 patch = q.lookup(patch)
1716 else:
1716 else:
1717 if not q.applied:
1717 if not q.applied:
1718 ui.write('No patches applied\n')
1718 ui.write('No patches applied\n')
1719 return 1
1719 return 1
1720 patch = q.lookup('qtip')
1720 patch = q.lookup('qtip')
1721 message = repo.mq.readheaders(patch)[0]
1721 message = repo.mq.readheaders(patch)[0]
1722
1722
1723 ui.write('\n'.join(message) + '\n')
1723 ui.write('\n'.join(message) + '\n')
1724
1724
1725 def lastsavename(path):
1725 def lastsavename(path):
1726 (directory, base) = os.path.split(path)
1726 (directory, base) = os.path.split(path)
1727 names = os.listdir(directory)
1727 names = os.listdir(directory)
1728 namere = re.compile("%s.([0-9]+)" % base)
1728 namere = re.compile("%s.([0-9]+)" % base)
1729 maxindex = None
1729 maxindex = None
1730 maxname = None
1730 maxname = None
1731 for f in names:
1731 for f in names:
1732 m = namere.match(f)
1732 m = namere.match(f)
1733 if m:
1733 if m:
1734 index = int(m.group(1))
1734 index = int(m.group(1))
1735 if maxindex == None or index > maxindex:
1735 if maxindex == None or index > maxindex:
1736 maxindex = index
1736 maxindex = index
1737 maxname = f
1737 maxname = f
1738 if maxname:
1738 if maxname:
1739 return (os.path.join(directory, maxname), maxindex)
1739 return (os.path.join(directory, maxname), maxindex)
1740 return (None, None)
1740 return (None, None)
1741
1741
1742 def savename(path):
1742 def savename(path):
1743 (last, index) = lastsavename(path)
1743 (last, index) = lastsavename(path)
1744 if last is None:
1744 if last is None:
1745 index = 0
1745 index = 0
1746 newpath = path + ".%d" % (index + 1)
1746 newpath = path + ".%d" % (index + 1)
1747 return newpath
1747 return newpath
1748
1748
1749 def push(ui, repo, patch=None, **opts):
1749 def push(ui, repo, patch=None, **opts):
1750 """push the next patch onto the stack"""
1750 """push the next patch onto the stack"""
1751 q = repo.mq
1751 q = repo.mq
1752 mergeq = None
1752 mergeq = None
1753
1753
1754 if opts['all']:
1754 if opts['all']:
1755 if not q.series:
1755 if not q.series:
1756 raise util.Abort(_('no patches in series'))
1756 raise util.Abort(_('no patches in series'))
1757 patch = q.series[-1]
1757 patch = q.series[-1]
1758 if opts['merge']:
1758 if opts['merge']:
1759 if opts['name']:
1759 if opts['name']:
1760 newpath = opts['name']
1760 newpath = opts['name']
1761 else:
1761 else:
1762 newpath, i = lastsavename(q.path)
1762 newpath, i = lastsavename(q.path)
1763 if not newpath:
1763 if not newpath:
1764 ui.warn("no saved queues found, please use -n\n")
1764 ui.warn("no saved queues found, please use -n\n")
1765 return 1
1765 return 1
1766 mergeq = queue(ui, repo.join(""), newpath)
1766 mergeq = queue(ui, repo.join(""), newpath)
1767 ui.warn("merging with queue at: %s\n" % mergeq.path)
1767 ui.warn("merging with queue at: %s\n" % mergeq.path)
1768 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1768 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1769 mergeq=mergeq)
1769 mergeq=mergeq)
1770 q.save_dirty()
1770 q.save_dirty()
1771 return ret
1771 return ret
1772
1772
1773 def pop(ui, repo, patch=None, **opts):
1773 def pop(ui, repo, patch=None, **opts):
1774 """pop the current patch off the stack"""
1774 """pop the current patch off the stack"""
1775 localupdate = True
1775 localupdate = True
1776 if opts['name']:
1776 if opts['name']:
1777 q = queue(ui, repo.join(""), repo.join(opts['name']))
1777 q = queue(ui, repo.join(""), repo.join(opts['name']))
1778 ui.warn('using patch queue: %s\n' % q.path)
1778 ui.warn('using patch queue: %s\n' % q.path)
1779 localupdate = False
1779 localupdate = False
1780 else:
1780 else:
1781 q = repo.mq
1781 q = repo.mq
1782 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1782 q.pop(repo, patch, force=opts['force'], update=localupdate, all=opts['all'])
1783 q.save_dirty()
1783 q.save_dirty()
1784 return 0
1784 return 0
1785
1785
1786 def rename(ui, repo, patch, name=None, **opts):
1786 def rename(ui, repo, patch, name=None, **opts):
1787 """rename a patch
1787 """rename a patch
1788
1788
1789 With one argument, renames the current patch to PATCH1.
1789 With one argument, renames the current patch to PATCH1.
1790 With two arguments, renames PATCH1 to PATCH2."""
1790 With two arguments, renames PATCH1 to PATCH2."""
1791
1791
1792 q = repo.mq
1792 q = repo.mq
1793
1793
1794 if not name:
1794 if not name:
1795 name = patch
1795 name = patch
1796 patch = None
1796 patch = None
1797
1797
1798 if patch:
1798 if patch:
1799 patch = q.lookup(patch)
1799 patch = q.lookup(patch)
1800 else:
1800 else:
1801 if not q.applied:
1801 if not q.applied:
1802 ui.write(_('No patches applied\n'))
1802 ui.write(_('No patches applied\n'))
1803 return
1803 return
1804 patch = q.lookup('qtip')
1804 patch = q.lookup('qtip')
1805 absdest = q.join(name)
1805 absdest = q.join(name)
1806 if os.path.isdir(absdest):
1806 if os.path.isdir(absdest):
1807 name = os.path.join(name, os.path.basename(patch))
1807 name = os.path.join(name, os.path.basename(patch))
1808 absdest = q.join(name)
1808 absdest = q.join(name)
1809 if os.path.exists(absdest):
1809 if os.path.exists(absdest):
1810 raise util.Abort(_('%s already exists') % absdest)
1810 raise util.Abort(_('%s already exists') % absdest)
1811
1811
1812 if name in q.series:
1812 if name in q.series:
1813 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1813 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1814
1814
1815 if ui.verbose:
1815 if ui.verbose:
1816 ui.write('Renaming %s to %s\n' % (patch, name))
1816 ui.write('Renaming %s to %s\n' % (patch, name))
1817 i = q.find_series(patch)
1817 i = q.find_series(patch)
1818 guards = q.guard_re.findall(q.full_series[i])
1818 guards = q.guard_re.findall(q.full_series[i])
1819 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1819 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1820 q.parse_series()
1820 q.parse_series()
1821 q.series_dirty = 1
1821 q.series_dirty = 1
1822
1822
1823 info = q.isapplied(patch)
1823 info = q.isapplied(patch)
1824 if info:
1824 if info:
1825 q.applied[info[0]] = statusentry(info[1], name)
1825 q.applied[info[0]] = statusentry(info[1], name)
1826 q.applied_dirty = 1
1826 q.applied_dirty = 1
1827
1827
1828 util.rename(q.join(patch), absdest)
1828 util.rename(q.join(patch), absdest)
1829 r = q.qrepo()
1829 r = q.qrepo()
1830 if r:
1830 if r:
1831 wlock = r.wlock()
1831 wlock = r.wlock()
1832 if r.dirstate.state(name) == 'r':
1832 if r.dirstate.state(name) == 'r':
1833 r.undelete([name], wlock)
1833 r.undelete([name], wlock)
1834 r.copy(patch, name, wlock)
1834 r.copy(patch, name, wlock)
1835 r.remove([patch], False, wlock)
1835 r.remove([patch], False, wlock)
1836
1836
1837 q.save_dirty()
1837 q.save_dirty()
1838
1838
1839 def restore(ui, repo, rev, **opts):
1839 def restore(ui, repo, rev, **opts):
1840 """restore the queue state saved by a rev"""
1840 """restore the queue state saved by a rev"""
1841 rev = repo.lookup(rev)
1841 rev = repo.lookup(rev)
1842 q = repo.mq
1842 q = repo.mq
1843 q.restore(repo, rev, delete=opts['delete'],
1843 q.restore(repo, rev, delete=opts['delete'],
1844 qupdate=opts['update'])
1844 qupdate=opts['update'])
1845 q.save_dirty()
1845 q.save_dirty()
1846 return 0
1846 return 0
1847
1847
1848 def save(ui, repo, **opts):
1848 def save(ui, repo, **opts):
1849 """save current queue state"""
1849 """save current queue state"""
1850 q = repo.mq
1850 q = repo.mq
1851 message = commands.logmessage(opts)
1851 message = commands.logmessage(opts)
1852 ret = q.save(repo, msg=message)
1852 ret = q.save(repo, msg=message)
1853 if ret:
1853 if ret:
1854 return ret
1854 return ret
1855 q.save_dirty()
1855 q.save_dirty()
1856 if opts['copy']:
1856 if opts['copy']:
1857 path = q.path
1857 path = q.path
1858 if opts['name']:
1858 if opts['name']:
1859 newpath = os.path.join(q.basepath, opts['name'])
1859 newpath = os.path.join(q.basepath, opts['name'])
1860 if os.path.exists(newpath):
1860 if os.path.exists(newpath):
1861 if not os.path.isdir(newpath):
1861 if not os.path.isdir(newpath):
1862 raise util.Abort(_('destination %s exists and is not '
1862 raise util.Abort(_('destination %s exists and is not '
1863 'a directory') % newpath)
1863 'a directory') % newpath)
1864 if not opts['force']:
1864 if not opts['force']:
1865 raise util.Abort(_('destination %s exists, '
1865 raise util.Abort(_('destination %s exists, '
1866 'use -f to force') % newpath)
1866 'use -f to force') % newpath)
1867 else:
1867 else:
1868 newpath = savename(path)
1868 newpath = savename(path)
1869 ui.warn("copy %s to %s\n" % (path, newpath))
1869 ui.warn("copy %s to %s\n" % (path, newpath))
1870 util.copyfiles(path, newpath)
1870 util.copyfiles(path, newpath)
1871 if opts['empty']:
1871 if opts['empty']:
1872 try:
1872 try:
1873 os.unlink(q.join(q.status_path))
1873 os.unlink(q.join(q.status_path))
1874 except:
1874 except:
1875 pass
1875 pass
1876 return 0
1876 return 0
1877
1877
1878 def strip(ui, repo, rev, **opts):
1878 def strip(ui, repo, rev, **opts):
1879 """strip a revision and all later revs on the same branch"""
1879 """strip a revision and all later revs on the same branch"""
1880 rev = repo.lookup(rev)
1880 rev = repo.lookup(rev)
1881 backup = 'all'
1881 backup = 'all'
1882 if opts['backup']:
1882 if opts['backup']:
1883 backup = 'strip'
1883 backup = 'strip'
1884 elif opts['nobackup']:
1884 elif opts['nobackup']:
1885 backup = 'none'
1885 backup = 'none'
1886 update = repo.dirstate.parents()[0] != revlog.nullid
1886 update = repo.dirstate.parents()[0] != revlog.nullid
1887 repo.mq.strip(repo, rev, backup=backup, update=update)
1887 repo.mq.strip(repo, rev, backup=backup, update=update)
1888 return 0
1888 return 0
1889
1889
1890 def select(ui, repo, *args, **opts):
1890 def select(ui, repo, *args, **opts):
1891 '''set or print guarded patches to push
1891 '''set or print guarded patches to push
1892
1892
1893 Use the qguard command to set or print guards on patch, then use
1893 Use the qguard command to set or print guards on patch, then use
1894 qselect to tell mq which guards to use. A patch will be pushed if it
1894 qselect to tell mq which guards to use. A patch will be pushed if it
1895 has no guards or any positive guards match the currently selected guard,
1895 has no guards or any positive guards match the currently selected guard,
1896 but will not be pushed if any negative guards match the current guard.
1896 but will not be pushed if any negative guards match the current guard.
1897 For example:
1897 For example:
1898
1898
1899 qguard foo.patch -stable (negative guard)
1899 qguard foo.patch -stable (negative guard)
1900 qguard bar.patch +stable (positive guard)
1900 qguard bar.patch +stable (positive guard)
1901 qselect stable
1901 qselect stable
1902
1902
1903 This activates the "stable" guard. mq will skip foo.patch (because
1903 This activates the "stable" guard. mq will skip foo.patch (because
1904 it has a negative match) but push bar.patch (because it
1904 it has a negative match) but push bar.patch (because it
1905 has a positive match).
1905 has a positive match).
1906
1906
1907 With no arguments, prints the currently active guards.
1907 With no arguments, prints the currently active guards.
1908 With one argument, sets the active guard.
1908 With one argument, sets the active guard.
1909
1909
1910 Use -n/--none to deactivate guards (no other arguments needed).
1910 Use -n/--none to deactivate guards (no other arguments needed).
1911 When no guards are active, patches with positive guards are skipped
1911 When no guards are active, patches with positive guards are skipped
1912 and patches with negative guards are pushed.
1912 and patches with negative guards are pushed.
1913
1913
1914 qselect can change the guards on applied patches. It does not pop
1914 qselect can change the guards on applied patches. It does not pop
1915 guarded patches by default. Use --pop to pop back to the last applied
1915 guarded patches by default. Use --pop to pop back to the last applied
1916 patch that is not guarded. Use --reapply (which implies --pop) to push
1916 patch that is not guarded. Use --reapply (which implies --pop) to push
1917 back to the current patch afterwards, but skip guarded patches.
1917 back to the current patch afterwards, but skip guarded patches.
1918
1918
1919 Use -s/--series to print a list of all guards in the series file (no
1919 Use -s/--series to print a list of all guards in the series file (no
1920 other arguments needed). Use -v for more information.'''
1920 other arguments needed). Use -v for more information.'''
1921
1921
1922 q = repo.mq
1922 q = repo.mq
1923 guards = q.active()
1923 guards = q.active()
1924 if args or opts['none']:
1924 if args or opts['none']:
1925 old_unapplied = q.unapplied(repo)
1925 old_unapplied = q.unapplied(repo)
1926 old_guarded = [i for i in xrange(len(q.applied)) if
1926 old_guarded = [i for i in xrange(len(q.applied)) if
1927 not q.pushable(i)[0]]
1927 not q.pushable(i)[0]]
1928 q.set_active(args)
1928 q.set_active(args)
1929 q.save_dirty()
1929 q.save_dirty()
1930 if not args:
1930 if not args:
1931 ui.status(_('guards deactivated\n'))
1931 ui.status(_('guards deactivated\n'))
1932 if not opts['pop'] and not opts['reapply']:
1932 if not opts['pop'] and not opts['reapply']:
1933 unapplied = q.unapplied(repo)
1933 unapplied = q.unapplied(repo)
1934 guarded = [i for i in xrange(len(q.applied))
1934 guarded = [i for i in xrange(len(q.applied))
1935 if not q.pushable(i)[0]]
1935 if not q.pushable(i)[0]]
1936 if len(unapplied) != len(old_unapplied):
1936 if len(unapplied) != len(old_unapplied):
1937 ui.status(_('number of unguarded, unapplied patches has '
1937 ui.status(_('number of unguarded, unapplied patches has '
1938 'changed from %d to %d\n') %
1938 'changed from %d to %d\n') %
1939 (len(old_unapplied), len(unapplied)))
1939 (len(old_unapplied), len(unapplied)))
1940 if len(guarded) != len(old_guarded):
1940 if len(guarded) != len(old_guarded):
1941 ui.status(_('number of guarded, applied patches has changed '
1941 ui.status(_('number of guarded, applied patches has changed '
1942 'from %d to %d\n') %
1942 'from %d to %d\n') %
1943 (len(old_guarded), len(guarded)))
1943 (len(old_guarded), len(guarded)))
1944 elif opts['series']:
1944 elif opts['series']:
1945 guards = {}
1945 guards = {}
1946 noguards = 0
1946 noguards = 0
1947 for gs in q.series_guards:
1947 for gs in q.series_guards:
1948 if not gs:
1948 if not gs:
1949 noguards += 1
1949 noguards += 1
1950 for g in gs:
1950 for g in gs:
1951 guards.setdefault(g, 0)
1951 guards.setdefault(g, 0)
1952 guards[g] += 1
1952 guards[g] += 1
1953 if ui.verbose:
1953 if ui.verbose:
1954 guards['NONE'] = noguards
1954 guards['NONE'] = noguards
1955 guards = guards.items()
1955 guards = guards.items()
1956 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1956 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
1957 if guards:
1957 if guards:
1958 ui.note(_('guards in series file:\n'))
1958 ui.note(_('guards in series file:\n'))
1959 for guard, count in guards:
1959 for guard, count in guards:
1960 ui.note('%2d ' % count)
1960 ui.note('%2d ' % count)
1961 ui.write(guard, '\n')
1961 ui.write(guard, '\n')
1962 else:
1962 else:
1963 ui.note(_('no guards in series file\n'))
1963 ui.note(_('no guards in series file\n'))
1964 else:
1964 else:
1965 if guards:
1965 if guards:
1966 ui.note(_('active guards:\n'))
1966 ui.note(_('active guards:\n'))
1967 for g in guards:
1967 for g in guards:
1968 ui.write(g, '\n')
1968 ui.write(g, '\n')
1969 else:
1969 else:
1970 ui.write(_('no active guards\n'))
1970 ui.write(_('no active guards\n'))
1971 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1971 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
1972 popped = False
1972 popped = False
1973 if opts['pop'] or opts['reapply']:
1973 if opts['pop'] or opts['reapply']:
1974 for i in xrange(len(q.applied)):
1974 for i in xrange(len(q.applied)):
1975 pushable, reason = q.pushable(i)
1975 pushable, reason = q.pushable(i)
1976 if not pushable:
1976 if not pushable:
1977 ui.status(_('popping guarded patches\n'))
1977 ui.status(_('popping guarded patches\n'))
1978 popped = True
1978 popped = True
1979 if i == 0:
1979 if i == 0:
1980 q.pop(repo, all=True)
1980 q.pop(repo, all=True)
1981 else:
1981 else:
1982 q.pop(repo, i-1)
1982 q.pop(repo, i-1)
1983 break
1983 break
1984 if popped:
1984 if popped:
1985 try:
1985 try:
1986 if reapply:
1986 if reapply:
1987 ui.status(_('reapplying unguarded patches\n'))
1987 ui.status(_('reapplying unguarded patches\n'))
1988 q.push(repo, reapply)
1988 q.push(repo, reapply)
1989 finally:
1989 finally:
1990 q.save_dirty()
1990 q.save_dirty()
1991
1991
1992 def reposetup(ui, repo):
1992 def reposetup(ui, repo):
1993 class mqrepo(repo.__class__):
1993 class mqrepo(repo.__class__):
1994 def abort_if_wdir_patched(self, errmsg, force=False):
1994 def abort_if_wdir_patched(self, errmsg, force=False):
1995 if self.mq.applied and not force:
1995 if self.mq.applied and not force:
1996 parent = revlog.hex(self.dirstate.parents()[0])
1996 parent = revlog.hex(self.dirstate.parents()[0])
1997 if parent in [s.rev for s in self.mq.applied]:
1997 if parent in [s.rev for s in self.mq.applied]:
1998 raise util.Abort(errmsg)
1998 raise util.Abort(errmsg)
1999
1999
2000 def commit(self, *args, **opts):
2000 def commit(self, *args, **opts):
2001 if len(args) >= 6:
2001 if len(args) >= 6:
2002 force = args[5]
2002 force = args[5]
2003 else:
2003 else:
2004 force = opts.get('force')
2004 force = opts.get('force')
2005 self.abort_if_wdir_patched(
2005 self.abort_if_wdir_patched(
2006 _('cannot commit over an applied mq patch'),
2006 _('cannot commit over an applied mq patch'),
2007 force)
2007 force)
2008
2008
2009 return super(mqrepo, self).commit(*args, **opts)
2009 return super(mqrepo, self).commit(*args, **opts)
2010
2010
2011 def push(self, remote, force=False, revs=None):
2011 def push(self, remote, force=False, revs=None):
2012 if self.mq.applied and not force:
2012 if self.mq.applied and not force:
2013 raise util.Abort(_('source has mq patches applied'))
2013 raise util.Abort(_('source has mq patches applied'))
2014 return super(mqrepo, self).push(remote, force, revs)
2014 return super(mqrepo, self).push(remote, force, revs)
2015
2015
2016 def tags(self):
2016 def tags(self):
2017 if self.tagscache:
2017 if self.tagscache:
2018 return self.tagscache
2018 return self.tagscache
2019
2019
2020 tagscache = super(mqrepo, self).tags()
2020 tagscache = super(mqrepo, self).tags()
2021
2021
2022 q = self.mq
2022 q = self.mq
2023 if not q.applied:
2023 if not q.applied:
2024 return tagscache
2024 return tagscache
2025
2025
2026 mqtags = [(patch.rev, patch.name) for patch in q.applied]
2026 mqtags = [(patch.rev, patch.name) for patch in q.applied]
2027 mqtags.append((mqtags[-1][0], 'qtip'))
2027 mqtags.append((mqtags[-1][0], 'qtip'))
2028 mqtags.append((mqtags[0][0], 'qbase'))
2028 mqtags.append((mqtags[0][0], 'qbase'))
2029 for patch in mqtags:
2029 for patch in mqtags:
2030 if patch[1] in tagscache:
2030 if patch[1] in tagscache:
2031 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2031 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2032 else:
2032 else:
2033 tagscache[patch[1]] = revlog.bin(patch[0])
2033 tagscache[patch[1]] = revlog.bin(patch[0])
2034
2034
2035 return tagscache
2035 return tagscache
2036
2036
2037 def _branchtags(self):
2037 def _branchtags(self):
2038 q = self.mq
2038 q = self.mq
2039 if not q.applied:
2039 if not q.applied:
2040 return super(mqrepo, self)._branchtags()
2040 return super(mqrepo, self)._branchtags()
2041
2041
2042 self.branchcache = {} # avoid recursion in changectx
2042 self.branchcache = {} # avoid recursion in changectx
2043 cl = self.changelog
2043 cl = self.changelog
2044 partial, last, lrev = self._readbranchcache()
2044 partial, last, lrev = self._readbranchcache()
2045
2045
2046 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2046 qbase = cl.rev(revlog.bin(q.applied[0].rev))
2047 start = lrev + 1
2047 start = lrev + 1
2048 if start < qbase:
2048 if start < qbase:
2049 # update the cache (excluding the patches) and save it
2049 # update the cache (excluding the patches) and save it
2050 self._updatebranchcache(partial, lrev+1, qbase)
2050 self._updatebranchcache(partial, lrev+1, qbase)
2051 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2051 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2052 start = qbase
2052 start = qbase
2053 # if start = qbase, the cache is as updated as it should be.
2053 # if start = qbase, the cache is as updated as it should be.
2054 # if start > qbase, the cache includes (part of) the patches.
2054 # if start > qbase, the cache includes (part of) the patches.
2055 # we might as well use it, but we won't save it.
2055 # we might as well use it, but we won't save it.
2056
2056
2057 # update the cache up to the tip
2057 # update the cache up to the tip
2058 self._updatebranchcache(partial, start, cl.count())
2058 self._updatebranchcache(partial, start, cl.count())
2059
2059
2060 return partial
2060 return partial
2061
2061
2062 if repo.local():
2062 if repo.local():
2063 repo.__class__ = mqrepo
2063 repo.__class__ = mqrepo
2064 repo.mq = queue(ui, repo.join(""))
2064 repo.mq = queue(ui, repo.join(""))
2065
2065
2066 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2066 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2067
2067
2068 cmdtable = {
2068 cmdtable = {
2069 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2069 "qapplied": (applied, [] + seriesopts, 'hg qapplied [-s] [PATCH]'),
2070 "qclone": (clone,
2070 "qclone": (clone,
2071 [('', 'pull', None, _('use pull protocol to copy metadata')),
2071 [('', 'pull', None, _('use pull protocol to copy metadata')),
2072 ('U', 'noupdate', None, _('do not update the new working directories')),
2072 ('U', 'noupdate', None, _('do not update the new working directories')),
2073 ('', 'uncompressed', None,
2073 ('', 'uncompressed', None,
2074 _('use uncompressed transfer (fast over LAN)')),
2074 _('use uncompressed transfer (fast over LAN)')),
2075 ('e', 'ssh', '', _('specify ssh command to use')),
2075 ('e', 'ssh', '', _('specify ssh command to use')),
2076 ('p', 'patches', '', _('location of source patch repo')),
2076 ('p', 'patches', '', _('location of source patch repo')),
2077 ('', 'remotecmd', '',
2077 ('', 'remotecmd', '',
2078 _('specify hg command to run on the remote side'))],
2078 _('specify hg command to run on the remote side'))],
2079 'hg qclone [OPTION]... SOURCE [DEST]'),
2079 'hg qclone [OPTION]... SOURCE [DEST]'),
2080 "qcommit|qci":
2080 "qcommit|qci":
2081 (commit,
2081 (commit,
2082 commands.table["^commit|ci"][1],
2082 commands.table["^commit|ci"][1],
2083 'hg qcommit [OPTION]... [FILE]...'),
2083 'hg qcommit [OPTION]... [FILE]...'),
2084 "^qdiff": (diff,
2084 "^qdiff": (diff,
2085 [('g', 'git', None, _('use git extended diff format')),
2085 [('g', 'git', None, _('use git extended diff format')),
2086 ('I', 'include', [], _('include names matching the given patterns')),
2086 ('I', 'include', [], _('include names matching the given patterns')),
2087 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2087 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2088 'hg qdiff [-I] [-X] [FILE]...'),
2088 'hg qdiff [-I] [-X] [FILE]...'),
2089 "qdelete|qremove|qrm":
2089 "qdelete|qremove|qrm":
2090 (delete,
2090 (delete,
2091 [('k', 'keep', None, _('keep patch file')),
2091 [('k', 'keep', None, _('keep patch file')),
2092 ('r', 'rev', [], _('stop managing a revision'))],
2092 ('r', 'rev', [], _('stop managing a revision'))],
2093 'hg qdelete [-k] [-r REV]... PATCH...'),
2093 'hg qdelete [-k] [-r REV]... PATCH...'),
2094 'qfold':
2094 'qfold':
2095 (fold,
2095 (fold,
2096 [('e', 'edit', None, _('edit patch header')),
2096 [('e', 'edit', None, _('edit patch header')),
2097 ('k', 'keep', None, _('keep folded patch files'))
2097 ('k', 'keep', None, _('keep folded patch files'))
2098 ] + commands.commitopts,
2098 ] + commands.commitopts,
2099 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2099 'hg qfold [-e] [-m <text>] [-l <file] PATCH...'),
2100 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2100 'qguard': (guard, [('l', 'list', None, _('list all patches and guards')),
2101 ('n', 'none', None, _('drop all guards'))],
2101 ('n', 'none', None, _('drop all guards'))],
2102 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2102 'hg qguard [PATCH] [+GUARD...] [-GUARD...]'),
2103 'qheader': (header, [],
2103 'qheader': (header, [],
2104 _('hg qheader [PATCH]')),
2104 _('hg qheader [PATCH]')),
2105 "^qimport":
2105 "^qimport":
2106 (qimport,
2106 (qimport,
2107 [('e', 'existing', None, 'import file in patch dir'),
2107 [('e', 'existing', None, 'import file in patch dir'),
2108 ('n', 'name', '', 'patch file name'),
2108 ('n', 'name', '', 'patch file name'),
2109 ('f', 'force', None, 'overwrite existing files'),
2109 ('f', 'force', None, 'overwrite existing files'),
2110 ('r', 'rev', [], 'place existing revisions under mq control'),
2110 ('r', 'rev', [], 'place existing revisions under mq control'),
2111 ('g', 'git', None, _('use git extended diff format'))],
2111 ('g', 'git', None, _('use git extended diff format'))],
2112 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2112 'hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...'),
2113 "^qinit":
2113 "^qinit":
2114 (init,
2114 (init,
2115 [('c', 'create-repo', None, 'create queue repository')],
2115 [('c', 'create-repo', None, 'create queue repository')],
2116 'hg qinit [-c]'),
2116 'hg qinit [-c]'),
2117 "qnew":
2117 "qnew":
2118 (new,
2118 (new,
2119 [('e', 'edit', None, _('edit commit message')),
2119 [('e', 'edit', None, _('edit commit message')),
2120 ('f', 'force', None, _('import uncommitted changes into patch'))
2120 ('f', 'force', None, _('import uncommitted changes into patch'))
2121 ] + commands.commitopts,
2121 ] + commands.commitopts,
2122 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2122 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'),
2123 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2123 "qnext": (next, [] + seriesopts, 'hg qnext [-s]'),
2124 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2124 "qprev": (prev, [] + seriesopts, 'hg qprev [-s]'),
2125 "^qpop":
2125 "^qpop":
2126 (pop,
2126 (pop,
2127 [('a', 'all', None, 'pop all patches'),
2127 [('a', 'all', None, 'pop all patches'),
2128 ('n', 'name', '', 'queue name to pop'),
2128 ('n', 'name', '', 'queue name to pop'),
2129 ('f', 'force', None, 'forget any local changes')],
2129 ('f', 'force', None, 'forget any local changes')],
2130 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2130 'hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]'),
2131 "^qpush":
2131 "^qpush":
2132 (push,
2132 (push,
2133 [('f', 'force', None, 'apply if the patch has rejects'),
2133 [('f', 'force', None, 'apply if the patch has rejects'),
2134 ('l', 'list', None, 'list patch name in commit text'),
2134 ('l', 'list', None, 'list patch name in commit text'),
2135 ('a', 'all', None, 'apply all patches'),
2135 ('a', 'all', None, 'apply all patches'),
2136 ('m', 'merge', None, 'merge from another queue'),
2136 ('m', 'merge', None, 'merge from another queue'),
2137 ('n', 'name', '', 'merge queue name')],
2137 ('n', 'name', '', 'merge queue name')],
2138 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2138 'hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]'),
2139 "^qrefresh":
2139 "^qrefresh":
2140 (refresh,
2140 (refresh,
2141 [('e', 'edit', None, _('edit commit message')),
2141 [('e', 'edit', None, _('edit commit message')),
2142 ('g', 'git', None, _('use git extended diff format')),
2142 ('g', 'git', None, _('use git extended diff format')),
2143 ('s', 'short', None, 'refresh only files already in the patch'),
2143 ('s', 'short', None, 'refresh only files already in the patch'),
2144 ('I', 'include', [], _('include names matching the given patterns')),
2144 ('I', 'include', [], _('include names matching the given patterns')),
2145 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2145 ('X', 'exclude', [], _('exclude names matching the given patterns'))
2146 ] + commands.commitopts,
2146 ] + commands.commitopts,
2147 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2147 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'),
2148 'qrename|qmv':
2148 'qrename|qmv':
2149 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2149 (rename, [], 'hg qrename PATCH1 [PATCH2]'),
2150 "qrestore":
2150 "qrestore":
2151 (restore,
2151 (restore,
2152 [('d', 'delete', None, 'delete save entry'),
2152 [('d', 'delete', None, 'delete save entry'),
2153 ('u', 'update', None, 'update queue working dir')],
2153 ('u', 'update', None, 'update queue working dir')],
2154 'hg qrestore [-d] [-u] REV'),
2154 'hg qrestore [-d] [-u] REV'),
2155 "qsave":
2155 "qsave":
2156 (save,
2156 (save,
2157 [('c', 'copy', None, 'copy patch directory'),
2157 [('c', 'copy', None, 'copy patch directory'),
2158 ('n', 'name', '', 'copy directory name'),
2158 ('n', 'name', '', 'copy directory name'),
2159 ('e', 'empty', None, 'clear queue status file'),
2159 ('e', 'empty', None, 'clear queue status file'),
2160 ('f', 'force', None, 'force copy')] + commands.commitopts,
2160 ('f', 'force', None, 'force copy')] + commands.commitopts,
2161 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2161 'hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]'),
2162 "qselect": (select,
2162 "qselect": (select,
2163 [('n', 'none', None, _('disable all guards')),
2163 [('n', 'none', None, _('disable all guards')),
2164 ('s', 'series', None, _('list all guards in series file')),
2164 ('s', 'series', None, _('list all guards in series file')),
2165 ('', 'pop', None,
2165 ('', 'pop', None,
2166 _('pop to before first guarded applied patch')),
2166 _('pop to before first guarded applied patch')),
2167 ('', 'reapply', None, _('pop, then reapply patches'))],
2167 ('', 'reapply', None, _('pop, then reapply patches'))],
2168 'hg qselect [OPTION...] [GUARD...]'),
2168 'hg qselect [OPTION...] [GUARD...]'),
2169 "qseries":
2169 "qseries":
2170 (series,
2170 (series,
2171 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2171 [('m', 'missing', None, 'print patches not in series')] + seriesopts,
2172 'hg qseries [-ms]'),
2172 'hg qseries [-ms]'),
2173 "^strip":
2173 "^strip":
2174 (strip,
2174 (strip,
2175 [('f', 'force', None, 'force multi-head removal'),
2175 [('f', 'force', None, 'force multi-head removal'),
2176 ('b', 'backup', None, 'bundle unrelated changesets'),
2176 ('b', 'backup', None, 'bundle unrelated changesets'),
2177 ('n', 'nobackup', None, 'no backups')],
2177 ('n', 'nobackup', None, 'no backups')],
2178 'hg strip [-f] [-b] [-n] REV'),
2178 'hg strip [-f] [-b] [-n] REV'),
2179 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2179 "qtop": (top, [] + seriesopts, 'hg qtop [-s]'),
2180 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2180 "qunapplied": (unapplied, [] + seriesopts, 'hg qunapplied [-s] [PATCH]'),
2181 }
2181 }
@@ -1,3284 +1,3297 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing 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 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import traceback, errno, version, atexit
14 import traceback, errno, version, atexit, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 hg.merge(repo, hex(op1))
243 hg.merge(repo, hex(op1))
244 else:
244 else:
245 ui.status(_('the backout changeset is a new head - '
245 ui.status(_('the backout changeset is a new head - '
246 'do not forget to merge\n'))
246 'do not forget to merge\n'))
247 ui.status(_('(use "backout --merge" '
247 ui.status(_('(use "backout --merge" '
248 'if you want to auto-merge)\n'))
248 'if you want to auto-merge)\n'))
249
249
250 def branch(ui, repo, label=None):
250 def branch(ui, repo, label=None):
251 """set or show the current branch name
251 """set or show the current branch name
252
252
253 With <name>, set the current branch name. Otherwise, show the
253 With <name>, set the current branch name. Otherwise, show the
254 current branch name.
254 current branch name.
255 """
255 """
256
256
257 if label is not None:
257 if label is not None:
258 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
258 repo.opener("branch", "w").write(util.fromlocal(label) + '\n')
259 else:
259 else:
260 b = util.tolocal(repo.workingctx().branch())
260 b = util.tolocal(repo.workingctx().branch())
261 if b:
261 if b:
262 ui.write("%s\n" % b)
262 ui.write("%s\n" % b)
263
263
264 def branches(ui, repo):
264 def branches(ui, repo):
265 """list repository named branches
265 """list repository named branches
266
266
267 List the repository's named branches.
267 List the repository's named branches.
268 """
268 """
269 b = repo.branchtags()
269 b = repo.branchtags()
270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
270 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
271 l.sort()
271 l.sort()
272 for r, n, t in l:
272 for r, n, t in l:
273 hexfunc = ui.debugflag and hex or short
273 hexfunc = ui.debugflag and hex or short
274 if ui.quiet:
274 if ui.quiet:
275 ui.write("%s\n" % t)
275 ui.write("%s\n" % t)
276 else:
276 else:
277 t = util.localsub(t, 30)
277 t = util.localsub(t, 30)
278 t += " " * (30 - util.locallen(t))
278 t += " " * (30 - util.locallen(t))
279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
279 ui.write("%s %s:%s\n" % (t, -r, hexfunc(n)))
280
280
281 def bundle(ui, repo, fname, dest=None, **opts):
281 def bundle(ui, repo, fname, dest=None, **opts):
282 """create a changegroup file
282 """create a changegroup file
283
283
284 Generate a compressed changegroup file collecting changesets not
284 Generate a compressed changegroup file collecting changesets not
285 found in the other repository.
285 found in the other repository.
286
286
287 If no destination repository is specified the destination is assumed
287 If no destination repository is specified the destination is assumed
288 to have all the nodes specified by one or more --base parameters.
288 to have all the nodes specified by one or more --base parameters.
289
289
290 The bundle file can then be transferred using conventional means and
290 The bundle file can then be transferred using conventional means and
291 applied to another repository with the unbundle or pull command.
291 applied to another repository with the unbundle or pull command.
292 This is useful when direct push and pull are not available or when
292 This is useful when direct push and pull are not available or when
293 exporting an entire repository is undesirable.
293 exporting an entire repository is undesirable.
294
294
295 Applying bundles preserves all changeset contents including
295 Applying bundles preserves all changeset contents including
296 permissions, copy/rename information, and revision history.
296 permissions, copy/rename information, and revision history.
297 """
297 """
298 revs = opts.get('rev') or None
298 revs = opts.get('rev') or None
299 if revs:
299 if revs:
300 revs = [repo.lookup(rev) for rev in revs]
300 revs = [repo.lookup(rev) for rev in revs]
301 base = opts.get('base')
301 base = opts.get('base')
302 if base:
302 if base:
303 if dest:
303 if dest:
304 raise util.Abort(_("--base is incompatible with specifiying "
304 raise util.Abort(_("--base is incompatible with specifiying "
305 "a destination"))
305 "a destination"))
306 base = [repo.lookup(rev) for rev in base]
306 base = [repo.lookup(rev) for rev in base]
307 # create the right base
307 # create the right base
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
308 # XXX: nodesbetween / changegroup* should be "fixed" instead
309 o = []
309 o = []
310 has = {nullid: None}
310 has = {nullid: None}
311 for n in base:
311 for n in base:
312 has.update(repo.changelog.reachable(n))
312 has.update(repo.changelog.reachable(n))
313 if revs:
313 if revs:
314 visit = list(revs)
314 visit = list(revs)
315 else:
315 else:
316 visit = repo.changelog.heads()
316 visit = repo.changelog.heads()
317 seen = {}
317 seen = {}
318 while visit:
318 while visit:
319 n = visit.pop(0)
319 n = visit.pop(0)
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
320 parents = [p for p in repo.changelog.parents(n) if p not in has]
321 if len(parents) == 0:
321 if len(parents) == 0:
322 o.insert(0, n)
322 o.insert(0, n)
323 else:
323 else:
324 for p in parents:
324 for p in parents:
325 if p not in seen:
325 if p not in seen:
326 seen[p] = 1
326 seen[p] = 1
327 visit.append(p)
327 visit.append(p)
328 else:
328 else:
329 setremoteconfig(ui, opts)
329 setremoteconfig(ui, opts)
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
330 dest = ui.expandpath(dest or 'default-push', dest or 'default')
331 other = hg.repository(ui, dest)
331 other = hg.repository(ui, dest)
332 o = repo.findoutgoing(other, force=opts['force'])
332 o = repo.findoutgoing(other, force=opts['force'])
333
333
334 if revs:
334 if revs:
335 cg = repo.changegroupsubset(o, revs, 'bundle')
335 cg = repo.changegroupsubset(o, revs, 'bundle')
336 else:
336 else:
337 cg = repo.changegroup(o, 'bundle')
337 cg = repo.changegroup(o, 'bundle')
338 changegroup.writebundle(cg, fname, "HG10BZ")
338 changegroup.writebundle(cg, fname, "HG10BZ")
339
339
340 def cat(ui, repo, file1, *pats, **opts):
340 def cat(ui, repo, file1, *pats, **opts):
341 """output the current or given revision of files
341 """output the current or given revision of files
342
342
343 Print the specified files as they were at the given revision.
343 Print the specified files as they were at the given revision.
344 If no revision is given, the parent of the working directory is used,
344 If no revision is given, the parent of the working directory is used,
345 or tip if no revision is checked out.
345 or tip if no revision is checked out.
346
346
347 Output may be to a file, in which case the name of the file is
347 Output may be to a file, in which case the name of the file is
348 given using a format string. The formatting rules are the same as
348 given using a format string. The formatting rules are the same as
349 for the export command, with the following additions:
349 for the export command, with the following additions:
350
350
351 %s basename of file being printed
351 %s basename of file being printed
352 %d dirname of file being printed, or '.' if in repo root
352 %d dirname of file being printed, or '.' if in repo root
353 %p root-relative path name of file being printed
353 %p root-relative path name of file being printed
354 """
354 """
355 ctx = repo.changectx(opts['rev'])
355 ctx = repo.changectx(opts['rev'])
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
356 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
357 ctx.node()):
357 ctx.node()):
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
358 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
359 fp.write(ctx.filectx(abs).data())
359 fp.write(ctx.filectx(abs).data())
360
360
361 def clone(ui, source, dest=None, **opts):
361 def clone(ui, source, dest=None, **opts):
362 """make a copy of an existing repository
362 """make a copy of an existing repository
363
363
364 Create a copy of an existing repository in a new directory.
364 Create a copy of an existing repository in a new directory.
365
365
366 If no destination directory name is specified, it defaults to the
366 If no destination directory name is specified, it defaults to the
367 basename of the source.
367 basename of the source.
368
368
369 The location of the source is added to the new repository's
369 The location of the source is added to the new repository's
370 .hg/hgrc file, as the default to be used for future pulls.
370 .hg/hgrc file, as the default to be used for future pulls.
371
371
372 For efficiency, hardlinks are used for cloning whenever the source
372 For efficiency, hardlinks are used for cloning whenever the source
373 and destination are on the same filesystem (note this applies only
373 and destination are on the same filesystem (note this applies only
374 to the repository data, not to the checked out files). Some
374 to the repository data, not to the checked out files). Some
375 filesystems, such as AFS, implement hardlinking incorrectly, but
375 filesystems, such as AFS, implement hardlinking incorrectly, but
376 do not report errors. In these cases, use the --pull option to
376 do not report errors. In these cases, use the --pull option to
377 avoid hardlinking.
377 avoid hardlinking.
378
378
379 You can safely clone repositories and checked out files using full
379 You can safely clone repositories and checked out files using full
380 hardlinks with
380 hardlinks with
381
381
382 $ cp -al REPO REPOCLONE
382 $ cp -al REPO REPOCLONE
383
383
384 which is the fastest way to clone. However, the operation is not
384 which is the fastest way to clone. However, the operation is not
385 atomic (making sure REPO is not modified during the operation is
385 atomic (making sure REPO is not modified during the operation is
386 up to you) and you have to make sure your editor breaks hardlinks
386 up to you) and you have to make sure your editor breaks hardlinks
387 (Emacs and most Linux Kernel tools do so).
387 (Emacs and most Linux Kernel tools do so).
388
388
389 If you use the -r option to clone up to a specific revision, no
389 If you use the -r option to clone up to a specific revision, no
390 subsequent revisions will be present in the cloned repository.
390 subsequent revisions will be present in the cloned repository.
391 This option implies --pull, even on local repositories.
391 This option implies --pull, even on local repositories.
392
392
393 See pull for valid source format details.
393 See pull for valid source format details.
394
394
395 It is possible to specify an ssh:// URL as the destination, but no
395 It is possible to specify an ssh:// URL as the destination, but no
396 .hg/hgrc and working directory will be created on the remote side.
396 .hg/hgrc and working directory will be created on the remote side.
397 Look at the help text for the pull command for important details
397 Look at the help text for the pull command for important details
398 about ssh:// URLs.
398 about ssh:// URLs.
399 """
399 """
400 setremoteconfig(ui, opts)
400 setremoteconfig(ui, opts)
401 hg.clone(ui, ui.expandpath(source), dest,
401 hg.clone(ui, ui.expandpath(source), dest,
402 pull=opts['pull'],
402 pull=opts['pull'],
403 stream=opts['uncompressed'],
403 stream=opts['uncompressed'],
404 rev=opts['rev'],
404 rev=opts['rev'],
405 update=not opts['noupdate'])
405 update=not opts['noupdate'])
406
406
407 def commit(ui, repo, *pats, **opts):
407 def commit(ui, repo, *pats, **opts):
408 """commit the specified files or all outstanding changes
408 """commit the specified files or all outstanding changes
409
409
410 Commit changes to the given files into the repository.
410 Commit changes to the given files into the repository.
411
411
412 If a list of files is omitted, all changes reported by "hg status"
412 If a list of files is omitted, all changes reported by "hg status"
413 will be committed.
413 will be committed.
414
414
415 If no commit message is specified, the editor configured in your hgrc
415 If no commit message is specified, the editor configured in your hgrc
416 or in the EDITOR environment variable is started to enter a message.
416 or in the EDITOR environment variable is started to enter a message.
417 """
417 """
418 message = logmessage(opts)
418 message = logmessage(opts)
419
419
420 if opts['addremove']:
420 if opts['addremove']:
421 cmdutil.addremove(repo, pats, opts)
421 cmdutil.addremove(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
422 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
423 if pats:
423 if pats:
424 status = repo.status(files=fns, match=match)
424 status = repo.status(files=fns, match=match)
425 modified, added, removed, deleted, unknown = status[:5]
425 modified, added, removed, deleted, unknown = status[:5]
426 files = modified + added + removed
426 files = modified + added + removed
427 slist = None
427 slist = None
428 for f in fns:
428 for f in fns:
429 if f not in files:
429 if f not in files:
430 rf = repo.wjoin(f)
430 rf = repo.wjoin(f)
431 if f in unknown:
431 if f in unknown:
432 raise util.Abort(_("file %s not tracked!") % rf)
432 raise util.Abort(_("file %s not tracked!") % rf)
433 try:
433 try:
434 mode = os.lstat(rf)[stat.ST_MODE]
434 mode = os.lstat(rf)[stat.ST_MODE]
435 except OSError:
435 except OSError:
436 raise util.Abort(_("file %s not found!") % rf)
436 raise util.Abort(_("file %s not found!") % rf)
437 if stat.S_ISDIR(mode):
437 if stat.S_ISDIR(mode):
438 name = f + '/'
438 name = f + '/'
439 if slist is None:
439 if slist is None:
440 slist = list(files)
440 slist = list(files)
441 slist.sort()
441 slist.sort()
442 i = bisect.bisect(slist, name)
442 i = bisect.bisect(slist, name)
443 if i >= len(slist) or not slist[i].startswith(name):
443 if i >= len(slist) or not slist[i].startswith(name):
444 raise util.Abort(_("no match under directory %s!")
444 raise util.Abort(_("no match under directory %s!")
445 % rf)
445 % rf)
446 elif not stat.S_ISREG(mode):
446 elif not stat.S_ISREG(mode):
447 raise util.Abort(_("can't commit %s: "
447 raise util.Abort(_("can't commit %s: "
448 "unsupported file type!") % rf)
448 "unsupported file type!") % rf)
449 else:
449 else:
450 files = []
450 files = []
451 try:
451 try:
452 repo.commit(files, message, opts['user'], opts['date'], match,
452 repo.commit(files, message, opts['user'], opts['date'], match,
453 force_editor=opts.get('force_editor'))
453 force_editor=opts.get('force_editor'))
454 except ValueError, inst:
454 except ValueError, inst:
455 raise util.Abort(str(inst))
455 raise util.Abort(str(inst))
456
456
457 def docopy(ui, repo, pats, opts, wlock):
457 def docopy(ui, repo, pats, opts, wlock):
458 # called with the repo lock held
458 # called with the repo lock held
459 #
459 #
460 # hgsep => pathname that uses "/" to separate directories
460 # hgsep => pathname that uses "/" to separate directories
461 # ossep => pathname that uses os.sep to separate directories
461 # ossep => pathname that uses os.sep to separate directories
462 cwd = repo.getcwd()
462 cwd = repo.getcwd()
463 errors = 0
463 errors = 0
464 copied = []
464 copied = []
465 targets = {}
465 targets = {}
466
466
467 # abs: hgsep
467 # abs: hgsep
468 # rel: ossep
468 # rel: ossep
469 # return: hgsep
469 # return: hgsep
470 def okaytocopy(abs, rel, exact):
470 def okaytocopy(abs, rel, exact):
471 reasons = {'?': _('is not managed'),
471 reasons = {'?': _('is not managed'),
472 'a': _('has been marked for add'),
472 'a': _('has been marked for add'),
473 'r': _('has been marked for remove')}
473 'r': _('has been marked for remove')}
474 state = repo.dirstate.state(abs)
474 state = repo.dirstate.state(abs)
475 reason = reasons.get(state)
475 reason = reasons.get(state)
476 if reason:
476 if reason:
477 if state == 'a':
477 if state == 'a':
478 origsrc = repo.dirstate.copied(abs)
478 origsrc = repo.dirstate.copied(abs)
479 if origsrc is not None:
479 if origsrc is not None:
480 return origsrc
480 return origsrc
481 if exact:
481 if exact:
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
482 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
483 else:
483 else:
484 return abs
484 return abs
485
485
486 # origsrc: hgsep
486 # origsrc: hgsep
487 # abssrc: hgsep
487 # abssrc: hgsep
488 # relsrc: ossep
488 # relsrc: ossep
489 # target: ossep
489 # target: ossep
490 def copy(origsrc, abssrc, relsrc, target, exact):
490 def copy(origsrc, abssrc, relsrc, target, exact):
491 abstarget = util.canonpath(repo.root, cwd, target)
491 abstarget = util.canonpath(repo.root, cwd, target)
492 reltarget = util.pathto(cwd, abstarget)
492 reltarget = util.pathto(cwd, abstarget)
493 prevsrc = targets.get(abstarget)
493 prevsrc = targets.get(abstarget)
494 if prevsrc is not None:
494 if prevsrc is not None:
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
496 (reltarget, util.localpath(abssrc),
496 (reltarget, util.localpath(abssrc),
497 util.localpath(prevsrc)))
497 util.localpath(prevsrc)))
498 return
498 return
499 if (not opts['after'] and os.path.exists(reltarget) or
499 if (not opts['after'] and os.path.exists(reltarget) or
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
500 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
501 if not opts['force']:
501 if not opts['force']:
502 ui.warn(_('%s: not overwriting - file exists\n') %
502 ui.warn(_('%s: not overwriting - file exists\n') %
503 reltarget)
503 reltarget)
504 return
504 return
505 if not opts['after'] and not opts.get('dry_run'):
505 if not opts['after'] and not opts.get('dry_run'):
506 os.unlink(reltarget)
506 os.unlink(reltarget)
507 if opts['after']:
507 if opts['after']:
508 if not os.path.exists(reltarget):
508 if not os.path.exists(reltarget):
509 return
509 return
510 else:
510 else:
511 targetdir = os.path.dirname(reltarget) or '.'
511 targetdir = os.path.dirname(reltarget) or '.'
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
513 os.makedirs(targetdir)
513 os.makedirs(targetdir)
514 try:
514 try:
515 restore = repo.dirstate.state(abstarget) == 'r'
515 restore = repo.dirstate.state(abstarget) == 'r'
516 if restore and not opts.get('dry_run'):
516 if restore and not opts.get('dry_run'):
517 repo.undelete([abstarget], wlock)
517 repo.undelete([abstarget], wlock)
518 try:
518 try:
519 if not opts.get('dry_run'):
519 if not opts.get('dry_run'):
520 util.copyfile(relsrc, reltarget)
520 util.copyfile(relsrc, reltarget)
521 restore = False
521 restore = False
522 finally:
522 finally:
523 if restore:
523 if restore:
524 repo.remove([abstarget], wlock)
524 repo.remove([abstarget], wlock)
525 except IOError, inst:
525 except IOError, inst:
526 if inst.errno == errno.ENOENT:
526 if inst.errno == errno.ENOENT:
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 ui.warn(_('%s: deleted in working copy\n') % relsrc)
528 else:
528 else:
529 ui.warn(_('%s: cannot copy - %s\n') %
529 ui.warn(_('%s: cannot copy - %s\n') %
530 (relsrc, inst.strerror))
530 (relsrc, inst.strerror))
531 errors += 1
531 errors += 1
532 return
532 return
533 if ui.verbose or not exact:
533 if ui.verbose or not exact:
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
535 targets[abstarget] = abssrc
535 targets[abstarget] = abssrc
536 if abstarget != origsrc and not opts.get('dry_run'):
536 if abstarget != origsrc and not opts.get('dry_run'):
537 repo.copy(origsrc, abstarget, wlock)
537 repo.copy(origsrc, abstarget, wlock)
538 copied.append((abssrc, relsrc, exact))
538 copied.append((abssrc, relsrc, exact))
539
539
540 # pat: ossep
540 # pat: ossep
541 # dest ossep
541 # dest ossep
542 # srcs: list of (hgsep, hgsep, ossep, bool)
542 # srcs: list of (hgsep, hgsep, ossep, bool)
543 # return: function that takes hgsep and returns ossep
543 # return: function that takes hgsep and returns ossep
544 def targetpathfn(pat, dest, srcs):
544 def targetpathfn(pat, dest, srcs):
545 if os.path.isdir(pat):
545 if os.path.isdir(pat):
546 abspfx = util.canonpath(repo.root, cwd, pat)
546 abspfx = util.canonpath(repo.root, cwd, pat)
547 abspfx = util.localpath(abspfx)
547 abspfx = util.localpath(abspfx)
548 if destdirexists:
548 if destdirexists:
549 striplen = len(os.path.split(abspfx)[0])
549 striplen = len(os.path.split(abspfx)[0])
550 else:
550 else:
551 striplen = len(abspfx)
551 striplen = len(abspfx)
552 if striplen:
552 if striplen:
553 striplen += len(os.sep)
553 striplen += len(os.sep)
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
555 elif destdirexists:
555 elif destdirexists:
556 res = lambda p: os.path.join(dest,
556 res = lambda p: os.path.join(dest,
557 os.path.basename(util.localpath(p)))
557 os.path.basename(util.localpath(p)))
558 else:
558 else:
559 res = lambda p: dest
559 res = lambda p: dest
560 return res
560 return res
561
561
562 # pat: ossep
562 # pat: ossep
563 # dest ossep
563 # dest ossep
564 # srcs: list of (hgsep, hgsep, ossep, bool)
564 # srcs: list of (hgsep, hgsep, ossep, bool)
565 # return: function that takes hgsep and returns ossep
565 # return: function that takes hgsep and returns ossep
566 def targetpathafterfn(pat, dest, srcs):
566 def targetpathafterfn(pat, dest, srcs):
567 if util.patkind(pat, None)[0]:
567 if util.patkind(pat, None)[0]:
568 # a mercurial pattern
568 # a mercurial pattern
569 res = lambda p: os.path.join(dest,
569 res = lambda p: os.path.join(dest,
570 os.path.basename(util.localpath(p)))
570 os.path.basename(util.localpath(p)))
571 else:
571 else:
572 abspfx = util.canonpath(repo.root, cwd, pat)
572 abspfx = util.canonpath(repo.root, cwd, pat)
573 if len(abspfx) < len(srcs[0][0]):
573 if len(abspfx) < len(srcs[0][0]):
574 # A directory. Either the target path contains the last
574 # A directory. Either the target path contains the last
575 # component of the source path or it does not.
575 # component of the source path or it does not.
576 def evalpath(striplen):
576 def evalpath(striplen):
577 score = 0
577 score = 0
578 for s in srcs:
578 for s in srcs:
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 t = os.path.join(dest, util.localpath(s[0])[striplen:])
580 if os.path.exists(t):
580 if os.path.exists(t):
581 score += 1
581 score += 1
582 return score
582 return score
583
583
584 abspfx = util.localpath(abspfx)
584 abspfx = util.localpath(abspfx)
585 striplen = len(abspfx)
585 striplen = len(abspfx)
586 if striplen:
586 if striplen:
587 striplen += len(os.sep)
587 striplen += len(os.sep)
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
589 score = evalpath(striplen)
589 score = evalpath(striplen)
590 striplen1 = len(os.path.split(abspfx)[0])
590 striplen1 = len(os.path.split(abspfx)[0])
591 if striplen1:
591 if striplen1:
592 striplen1 += len(os.sep)
592 striplen1 += len(os.sep)
593 if evalpath(striplen1) > score:
593 if evalpath(striplen1) > score:
594 striplen = striplen1
594 striplen = striplen1
595 res = lambda p: os.path.join(dest,
595 res = lambda p: os.path.join(dest,
596 util.localpath(p)[striplen:])
596 util.localpath(p)[striplen:])
597 else:
597 else:
598 # a file
598 # a file
599 if destdirexists:
599 if destdirexists:
600 res = lambda p: os.path.join(dest,
600 res = lambda p: os.path.join(dest,
601 os.path.basename(util.localpath(p)))
601 os.path.basename(util.localpath(p)))
602 else:
602 else:
603 res = lambda p: dest
603 res = lambda p: dest
604 return res
604 return res
605
605
606
606
607 pats = list(pats)
607 pats = list(pats)
608 if not pats:
608 if not pats:
609 raise util.Abort(_('no source or destination specified'))
609 raise util.Abort(_('no source or destination specified'))
610 if len(pats) == 1:
610 if len(pats) == 1:
611 raise util.Abort(_('no destination specified'))
611 raise util.Abort(_('no destination specified'))
612 dest = pats.pop()
612 dest = pats.pop()
613 destdirexists = os.path.isdir(dest)
613 destdirexists = os.path.isdir(dest)
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
615 raise util.Abort(_('with multiple sources, destination must be an '
615 raise util.Abort(_('with multiple sources, destination must be an '
616 'existing directory'))
616 'existing directory'))
617 if opts['after']:
617 if opts['after']:
618 tfn = targetpathafterfn
618 tfn = targetpathafterfn
619 else:
619 else:
620 tfn = targetpathfn
620 tfn = targetpathfn
621 copylist = []
621 copylist = []
622 for pat in pats:
622 for pat in pats:
623 srcs = []
623 srcs = []
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
624 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts):
625 origsrc = okaytocopy(abssrc, relsrc, exact)
625 origsrc = okaytocopy(abssrc, relsrc, exact)
626 if origsrc:
626 if origsrc:
627 srcs.append((origsrc, abssrc, relsrc, exact))
627 srcs.append((origsrc, abssrc, relsrc, exact))
628 if not srcs:
628 if not srcs:
629 continue
629 continue
630 copylist.append((tfn(pat, dest, srcs), srcs))
630 copylist.append((tfn(pat, dest, srcs), srcs))
631 if not copylist:
631 if not copylist:
632 raise util.Abort(_('no files to copy'))
632 raise util.Abort(_('no files to copy'))
633
633
634 for targetpath, srcs in copylist:
634 for targetpath, srcs in copylist:
635 for origsrc, abssrc, relsrc, exact in srcs:
635 for origsrc, abssrc, relsrc, exact in srcs:
636 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
636 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637
637
638 if errors:
638 if errors:
639 ui.warn(_('(consider using --after)\n'))
639 ui.warn(_('(consider using --after)\n'))
640 return errors, copied
640 return errors, copied
641
641
642 def copy(ui, repo, *pats, **opts):
642 def copy(ui, repo, *pats, **opts):
643 """mark files as copied for the next commit
643 """mark files as copied for the next commit
644
644
645 Mark dest as having copies of source files. If dest is a
645 Mark dest as having copies of source files. If dest is a
646 directory, copies are put in that directory. If dest is a file,
646 directory, copies are put in that directory. If dest is a file,
647 there can only be one source.
647 there can only be one source.
648
648
649 By default, this command copies the contents of files as they
649 By default, this command copies the contents of files as they
650 stand in the working directory. If invoked with --after, the
650 stand in the working directory. If invoked with --after, the
651 operation is recorded, but no copying is performed.
651 operation is recorded, but no copying is performed.
652
652
653 This command takes effect in the next commit. To undo a copy
653 This command takes effect in the next commit. To undo a copy
654 before that, see hg revert.
654 before that, see hg revert.
655 """
655 """
656 wlock = repo.wlock(0)
656 wlock = repo.wlock(0)
657 errs, copied = docopy(ui, repo, pats, opts, wlock)
657 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 return errs
658 return errs
659
659
660 def debugancestor(ui, index, rev1, rev2):
660 def debugancestor(ui, index, rev1, rev2):
661 """find the ancestor revision of two revisions in a given index"""
661 """find the ancestor revision of two revisions in a given index"""
662 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
662 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
663 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
663 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
664 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665
665
666 def debugcomplete(ui, cmd='', **opts):
666 def debugcomplete(ui, cmd='', **opts):
667 """returns the completion list associated with the given command"""
667 """returns the completion list associated with the given command"""
668
668
669 if opts['options']:
669 if opts['options']:
670 options = []
670 options = []
671 otables = [globalopts]
671 otables = [globalopts]
672 if cmd:
672 if cmd:
673 aliases, entry = findcmd(ui, cmd)
673 aliases, entry = findcmd(ui, cmd)
674 otables.append(entry[1])
674 otables.append(entry[1])
675 for t in otables:
675 for t in otables:
676 for o in t:
676 for o in t:
677 if o[0]:
677 if o[0]:
678 options.append('-%s' % o[0])
678 options.append('-%s' % o[0])
679 options.append('--%s' % o[1])
679 options.append('--%s' % o[1])
680 ui.write("%s\n" % "\n".join(options))
680 ui.write("%s\n" % "\n".join(options))
681 return
681 return
682
682
683 clist = findpossible(ui, cmd).keys()
683 clist = findpossible(ui, cmd).keys()
684 clist.sort()
684 clist.sort()
685 ui.write("%s\n" % "\n".join(clist))
685 ui.write("%s\n" % "\n".join(clist))
686
686
687 def debugrebuildstate(ui, repo, rev=""):
687 def debugrebuildstate(ui, repo, rev=""):
688 """rebuild the dirstate as it would look like for the given revision"""
688 """rebuild the dirstate as it would look like for the given revision"""
689 if rev == "":
689 if rev == "":
690 rev = repo.changelog.tip()
690 rev = repo.changelog.tip()
691 ctx = repo.changectx(rev)
691 ctx = repo.changectx(rev)
692 files = ctx.manifest()
692 files = ctx.manifest()
693 wlock = repo.wlock()
693 wlock = repo.wlock()
694 repo.dirstate.rebuild(rev, files)
694 repo.dirstate.rebuild(rev, files)
695
695
696 def debugcheckstate(ui, repo):
696 def debugcheckstate(ui, repo):
697 """validate the correctness of the current dirstate"""
697 """validate the correctness of the current dirstate"""
698 parent1, parent2 = repo.dirstate.parents()
698 parent1, parent2 = repo.dirstate.parents()
699 repo.dirstate.read()
699 repo.dirstate.read()
700 dc = repo.dirstate.map
700 dc = repo.dirstate.map
701 keys = dc.keys()
701 keys = dc.keys()
702 keys.sort()
702 keys.sort()
703 m1 = repo.changectx(parent1).manifest()
703 m1 = repo.changectx(parent1).manifest()
704 m2 = repo.changectx(parent2).manifest()
704 m2 = repo.changectx(parent2).manifest()
705 errors = 0
705 errors = 0
706 for f in dc:
706 for f in dc:
707 state = repo.dirstate.state(f)
707 state = repo.dirstate.state(f)
708 if state in "nr" and f not in m1:
708 if state in "nr" and f not in m1:
709 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
709 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
710 errors += 1
710 errors += 1
711 if state in "a" and f in m1:
711 if state in "a" and f in m1:
712 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
712 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
713 errors += 1
713 errors += 1
714 if state in "m" and f not in m1 and f not in m2:
714 if state in "m" and f not in m1 and f not in m2:
715 ui.warn(_("%s in state %s, but not in either manifest\n") %
715 ui.warn(_("%s in state %s, but not in either manifest\n") %
716 (f, state))
716 (f, state))
717 errors += 1
717 errors += 1
718 for f in m1:
718 for f in m1:
719 state = repo.dirstate.state(f)
719 state = repo.dirstate.state(f)
720 if state not in "nrm":
720 if state not in "nrm":
721 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
721 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
722 errors += 1
722 errors += 1
723 if errors:
723 if errors:
724 error = _(".hg/dirstate inconsistent with current parent's manifest")
724 error = _(".hg/dirstate inconsistent with current parent's manifest")
725 raise util.Abort(error)
725 raise util.Abort(error)
726
726
727 def showconfig(ui, repo, *values, **opts):
727 def showconfig(ui, repo, *values, **opts):
728 """show combined config settings from all hgrc files
728 """show combined config settings from all hgrc files
729
729
730 With no args, print names and values of all config items.
730 With no args, print names and values of all config items.
731
731
732 With one arg of the form section.name, print just the value of
732 With one arg of the form section.name, print just the value of
733 that config item.
733 that config item.
734
734
735 With multiple args, print names and values of all config items
735 With multiple args, print names and values of all config items
736 with matching section names."""
736 with matching section names."""
737
737
738 untrusted = bool(opts.get('untrusted'))
738 untrusted = bool(opts.get('untrusted'))
739 if values:
739 if values:
740 if len([v for v in values if '.' in v]) > 1:
740 if len([v for v in values if '.' in v]) > 1:
741 raise util.Abort(_('only one config item permitted'))
741 raise util.Abort(_('only one config item permitted'))
742 for section, name, value in ui.walkconfig(untrusted=untrusted):
742 for section, name, value in ui.walkconfig(untrusted=untrusted):
743 sectname = section + '.' + name
743 sectname = section + '.' + name
744 if values:
744 if values:
745 for v in values:
745 for v in values:
746 if v == section:
746 if v == section:
747 ui.write('%s=%s\n' % (sectname, value))
747 ui.write('%s=%s\n' % (sectname, value))
748 elif v == sectname:
748 elif v == sectname:
749 ui.write(value, '\n')
749 ui.write(value, '\n')
750 else:
750 else:
751 ui.write('%s=%s\n' % (sectname, value))
751 ui.write('%s=%s\n' % (sectname, value))
752
752
753 def debugsetparents(ui, repo, rev1, rev2=None):
753 def debugsetparents(ui, repo, rev1, rev2=None):
754 """manually set the parents of the current working directory
754 """manually set the parents of the current working directory
755
755
756 This is useful for writing repository conversion tools, but should
756 This is useful for writing repository conversion tools, but should
757 be used with care.
757 be used with care.
758 """
758 """
759
759
760 if not rev2:
760 if not rev2:
761 rev2 = hex(nullid)
761 rev2 = hex(nullid)
762
762
763 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
763 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
764
764
765 def debugstate(ui, repo):
765 def debugstate(ui, repo):
766 """show the contents of the current dirstate"""
766 """show the contents of the current dirstate"""
767 repo.dirstate.read()
767 repo.dirstate.read()
768 dc = repo.dirstate.map
768 dc = repo.dirstate.map
769 keys = dc.keys()
769 keys = dc.keys()
770 keys.sort()
770 keys.sort()
771 for file_ in keys:
771 for file_ in keys:
772 ui.write("%c %3o %10d %s %s\n"
772 ui.write("%c %3o %10d %s %s\n"
773 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
773 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
774 time.strftime("%x %X",
774 time.strftime("%x %X",
775 time.localtime(dc[file_][3])), file_))
775 time.localtime(dc[file_][3])), file_))
776 for f in repo.dirstate.copies():
776 for f in repo.dirstate.copies():
777 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
777 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
778
778
779 def debugdata(ui, file_, rev):
779 def debugdata(ui, file_, rev):
780 """dump the contents of an data file revision"""
780 """dump the contents of an data file revision"""
781 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
781 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
782 file_[:-2] + ".i", file_, 0)
782 file_[:-2] + ".i", file_, 0)
783 try:
783 try:
784 ui.write(r.revision(r.lookup(rev)))
784 ui.write(r.revision(r.lookup(rev)))
785 except KeyError:
785 except KeyError:
786 raise util.Abort(_('invalid revision identifier %s') % rev)
786 raise util.Abort(_('invalid revision identifier %s') % rev)
787
787
788 def debugdate(ui, date, range=None, **opts):
788 def debugdate(ui, date, range=None, **opts):
789 """parse and display a date"""
789 """parse and display a date"""
790 if opts["extended"]:
790 if opts["extended"]:
791 d = util.parsedate(date, util.extendeddateformats)
791 d = util.parsedate(date, util.extendeddateformats)
792 else:
792 else:
793 d = util.parsedate(date)
793 d = util.parsedate(date)
794 ui.write("internal: %s %s\n" % d)
794 ui.write("internal: %s %s\n" % d)
795 ui.write("standard: %s\n" % util.datestr(d))
795 ui.write("standard: %s\n" % util.datestr(d))
796 if range:
796 if range:
797 m = util.matchdate(range)
797 m = util.matchdate(range)
798 ui.write("match: %s\n" % m(d[0]))
798 ui.write("match: %s\n" % m(d[0]))
799
799
800 def debugindex(ui, file_):
800 def debugindex(ui, file_):
801 """dump the contents of an index file"""
801 """dump the contents of an index file"""
802 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
802 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
803 ui.write(" rev offset length base linkrev" +
803 ui.write(" rev offset length base linkrev" +
804 " nodeid p1 p2\n")
804 " nodeid p1 p2\n")
805 for i in xrange(r.count()):
805 for i in xrange(r.count()):
806 node = r.node(i)
806 node = r.node(i)
807 pp = r.parents(node)
807 pp = r.parents(node)
808 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
808 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
809 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
809 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
810 short(node), short(pp[0]), short(pp[1])))
810 short(node), short(pp[0]), short(pp[1])))
811
811
812 def debugindexdot(ui, file_):
812 def debugindexdot(ui, file_):
813 """dump an index DAG as a .dot file"""
813 """dump an index DAG as a .dot file"""
814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
814 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
815 ui.write("digraph G {\n")
815 ui.write("digraph G {\n")
816 for i in xrange(r.count()):
816 for i in xrange(r.count()):
817 node = r.node(i)
817 node = r.node(i)
818 pp = r.parents(node)
818 pp = r.parents(node)
819 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
819 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
820 if pp[1] != nullid:
820 if pp[1] != nullid:
821 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
821 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
822 ui.write("}\n")
822 ui.write("}\n")
823
823
824 def debuginstall(ui):
824 def debuginstall(ui):
825 '''test Mercurial installation'''
825 '''test Mercurial installation'''
826
826
827 def writetemp(contents):
827 def writetemp(contents):
828 (fd, name) = tempfile.mkstemp()
828 (fd, name) = tempfile.mkstemp()
829 f = os.fdopen(fd, "wb")
829 f = os.fdopen(fd, "wb")
830 f.write(contents)
830 f.write(contents)
831 f.close()
831 f.close()
832 return name
832 return name
833
833
834 problems = 0
834 problems = 0
835
835
836 # encoding
836 # encoding
837 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
837 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
838 try:
838 try:
839 util.fromlocal("test")
839 util.fromlocal("test")
840 except util.Abort, inst:
840 except util.Abort, inst:
841 ui.write(" %s\n" % inst)
841 ui.write(" %s\n" % inst)
842 ui.write(_(" (check that your locale is properly set)\n"))
842 ui.write(_(" (check that your locale is properly set)\n"))
843 problems += 1
843 problems += 1
844
844
845 # compiled modules
845 # compiled modules
846 ui.status(_("Checking extensions...\n"))
846 ui.status(_("Checking extensions...\n"))
847 try:
847 try:
848 import bdiff, mpatch, base85
848 import bdiff, mpatch, base85
849 except Exception, inst:
849 except Exception, inst:
850 ui.write(" %s\n" % inst)
850 ui.write(" %s\n" % inst)
851 ui.write(_(" One or more extensions could not be found"))
851 ui.write(_(" One or more extensions could not be found"))
852 ui.write(_(" (check that you compiled the extensions)\n"))
852 ui.write(_(" (check that you compiled the extensions)\n"))
853 problems += 1
853 problems += 1
854
854
855 # templates
855 # templates
856 ui.status(_("Checking templates...\n"))
856 ui.status(_("Checking templates...\n"))
857 try:
857 try:
858 import templater
858 import templater
859 t = templater.templater(templater.templatepath("map-cmdline.default"))
859 t = templater.templater(templater.templatepath("map-cmdline.default"))
860 except Exception, inst:
860 except Exception, inst:
861 ui.write(" %s\n" % inst)
861 ui.write(" %s\n" % inst)
862 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
862 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
863 problems += 1
863 problems += 1
864
864
865 # patch
865 # patch
866 ui.status(_("Checking patch...\n"))
866 ui.status(_("Checking patch...\n"))
867 path = os.environ.get('PATH', '')
867 path = os.environ.get('PATH', '')
868 patcher = util.find_in_path('gpatch', path,
868 patcher = util.find_in_path('gpatch', path,
869 util.find_in_path('patch', path, None))
869 util.find_in_path('patch', path, None))
870 if not patcher:
870 if not patcher:
871 ui.write(_(" Can't find patch or gpatch in PATH\n"))
871 ui.write(_(" Can't find patch or gpatch in PATH\n"))
872 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
872 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
873 problems += 1
873 problems += 1
874 else:
874 else:
875 # actually attempt a patch here
875 # actually attempt a patch here
876 a = "1\n2\n3\n4\n"
876 a = "1\n2\n3\n4\n"
877 b = "1\n2\n3\ninsert\n4\n"
877 b = "1\n2\n3\ninsert\n4\n"
878 d = mdiff.unidiff(a, None, b, None, "a")
878 d = mdiff.unidiff(a, None, b, None, "a")
879 fa = writetemp(a)
879 fa = writetemp(a)
880 fd = writetemp(d)
880 fd = writetemp(d)
881 fp = os.popen('%s %s %s' % (patcher, fa, fd))
881 fp = os.popen('%s %s %s' % (patcher, fa, fd))
882 files = []
882 files = []
883 output = ""
883 output = ""
884 for line in fp:
884 for line in fp:
885 output += line
885 output += line
886 if line.startswith('patching file '):
886 if line.startswith('patching file '):
887 pf = util.parse_patch_output(line.rstrip())
887 pf = util.parse_patch_output(line.rstrip())
888 files.append(pf)
888 files.append(pf)
889 if files != [fa]:
889 if files != [fa]:
890 ui.write(_(" unexpected patch output!"))
890 ui.write(_(" unexpected patch output!"))
891 ui.write(_(" (you may have an incompatible version of patch)\n"))
891 ui.write(_(" (you may have an incompatible version of patch)\n"))
892 ui.write(output)
892 ui.write(output)
893 problems += 1
893 problems += 1
894 a = file(fa).read()
894 a = file(fa).read()
895 if a != b:
895 if a != b:
896 ui.write(_(" patch test failed!"))
896 ui.write(_(" patch test failed!"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
897 ui.write(_(" (you may have an incompatible version of patch)\n"))
898 problems += 1
898 problems += 1
899 os.unlink(fa)
899 os.unlink(fa)
900 os.unlink(fd)
900 os.unlink(fd)
901
901
902 # merge helper
902 # merge helper
903 ui.status(_("Checking merge helper...\n"))
903 ui.status(_("Checking merge helper...\n"))
904 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
904 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
905 or "hgmerge")
905 or "hgmerge")
906 cmdpath = util.find_in_path(cmd, path)
906 cmdpath = util.find_in_path(cmd, path)
907 if not cmdpath:
907 if not cmdpath:
908 cmdpath = util.find_in_path(cmd.split()[0], path)
908 cmdpath = util.find_in_path(cmd.split()[0], path)
909 if not cmdpath:
909 if not cmdpath:
910 if cmd == 'hgmerge':
910 if cmd == 'hgmerge':
911 ui.write(_(" No merge helper set and can't find default"
911 ui.write(_(" No merge helper set and can't find default"
912 " hgmerge script in PATH\n"))
912 " hgmerge script in PATH\n"))
913 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
913 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
914 else:
914 else:
915 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
915 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
916 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
916 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
917 problems += 1
917 problems += 1
918 else:
918 else:
919 # actually attempt a patch here
919 # actually attempt a patch here
920 fa = writetemp("1\n2\n3\n4\n")
920 fa = writetemp("1\n2\n3\n4\n")
921 fl = writetemp("1\n2\n3\ninsert\n4\n")
921 fl = writetemp("1\n2\n3\ninsert\n4\n")
922 fr = writetemp("begin\n1\n2\n3\n4\n")
922 fr = writetemp("begin\n1\n2\n3\n4\n")
923 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
923 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
924 if r:
924 if r:
925 ui.write(_(" got unexpected merge error %d!") % r)
925 ui.write(_(" got unexpected merge error %d!") % r)
926 problems += 1
926 problems += 1
927 m = file(fl).read()
927 m = file(fl).read()
928 if m != "begin\n1\n2\n3\ninsert\n4\n":
928 if m != "begin\n1\n2\n3\ninsert\n4\n":
929 ui.write(_(" got unexpected merge results!") % r)
929 ui.write(_(" got unexpected merge results!") % r)
930 ui.write(_(" (your merge helper may have the"
930 ui.write(_(" (your merge helper may have the"
931 " wrong argument order)\n"))
931 " wrong argument order)\n"))
932 ui.write(m)
932 ui.write(m)
933 os.unlink(fa)
933 os.unlink(fa)
934 os.unlink(fl)
934 os.unlink(fl)
935 os.unlink(fr)
935 os.unlink(fr)
936
936
937 # editor
937 # editor
938 ui.status(_("Checking commit editor...\n"))
938 ui.status(_("Checking commit editor...\n"))
939 editor = (os.environ.get("HGEDITOR") or
939 editor = (os.environ.get("HGEDITOR") or
940 ui.config("ui", "editor") or
940 ui.config("ui", "editor") or
941 os.environ.get("EDITOR", "vi"))
941 os.environ.get("EDITOR", "vi"))
942 cmdpath = util.find_in_path(editor, path)
942 cmdpath = util.find_in_path(editor, path)
943 if not cmdpath:
943 if not cmdpath:
944 cmdpath = util.find_in_path(editor.split()[0], path)
944 cmdpath = util.find_in_path(editor.split()[0], path)
945 if not cmdpath:
945 if not cmdpath:
946 if editor == 'vi':
946 if editor == 'vi':
947 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
947 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
948 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
948 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
949 else:
949 else:
950 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
950 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
951 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
951 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
952 problems += 1
952 problems += 1
953
953
954 # check username
954 # check username
955 ui.status(_("Checking username...\n"))
955 ui.status(_("Checking username...\n"))
956 user = os.environ.get("HGUSER")
956 user = os.environ.get("HGUSER")
957 if user is None:
957 if user is None:
958 user = ui.config("ui", "username")
958 user = ui.config("ui", "username")
959 if user is None:
959 if user is None:
960 user = os.environ.get("EMAIL")
960 user = os.environ.get("EMAIL")
961 if not user:
961 if not user:
962 ui.warn(" ")
962 ui.warn(" ")
963 ui.username()
963 ui.username()
964 ui.write(_(" (specify a username in your .hgrc file)\n"))
964 ui.write(_(" (specify a username in your .hgrc file)\n"))
965
965
966 if not problems:
966 if not problems:
967 ui.status(_("No problems detected\n"))
967 ui.status(_("No problems detected\n"))
968 else:
968 else:
969 ui.write(_("%s problems detected,"
969 ui.write(_("%s problems detected,"
970 " please check your install!\n") % problems)
970 " please check your install!\n") % problems)
971
971
972 return problems
972 return problems
973
973
974 def debugrename(ui, repo, file1, *pats, **opts):
974 def debugrename(ui, repo, file1, *pats, **opts):
975 """dump rename information"""
975 """dump rename information"""
976
976
977 ctx = repo.changectx(opts.get('rev', 'tip'))
977 ctx = repo.changectx(opts.get('rev', 'tip'))
978 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
978 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
979 ctx.node()):
979 ctx.node()):
980 m = ctx.filectx(abs).renamed()
980 m = ctx.filectx(abs).renamed()
981 if m:
981 if m:
982 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
982 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
983 else:
983 else:
984 ui.write(_("%s not renamed\n") % rel)
984 ui.write(_("%s not renamed\n") % rel)
985
985
986 def debugwalk(ui, repo, *pats, **opts):
986 def debugwalk(ui, repo, *pats, **opts):
987 """show how files match on given patterns"""
987 """show how files match on given patterns"""
988 items = list(cmdutil.walk(repo, pats, opts))
988 items = list(cmdutil.walk(repo, pats, opts))
989 if not items:
989 if not items:
990 return
990 return
991 fmt = '%%s %%-%ds %%-%ds %%s' % (
991 fmt = '%%s %%-%ds %%-%ds %%s' % (
992 max([len(abs) for (src, abs, rel, exact) in items]),
992 max([len(abs) for (src, abs, rel, exact) in items]),
993 max([len(rel) for (src, abs, rel, exact) in items]))
993 max([len(rel) for (src, abs, rel, exact) in items]))
994 for src, abs, rel, exact in items:
994 for src, abs, rel, exact in items:
995 line = fmt % (src, abs, rel, exact and 'exact' or '')
995 line = fmt % (src, abs, rel, exact and 'exact' or '')
996 ui.write("%s\n" % line.rstrip())
996 ui.write("%s\n" % line.rstrip())
997
997
998 def diff(ui, repo, *pats, **opts):
998 def diff(ui, repo, *pats, **opts):
999 """diff repository (or selected files)
999 """diff repository (or selected files)
1000
1000
1001 Show differences between revisions for the specified files.
1001 Show differences between revisions for the specified files.
1002
1002
1003 Differences between files are shown using the unified diff format.
1003 Differences between files are shown using the unified diff format.
1004
1004
1005 NOTE: diff may generate unexpected results for merges, as it will
1005 NOTE: diff may generate unexpected results for merges, as it will
1006 default to comparing against the working directory's first parent
1006 default to comparing against the working directory's first parent
1007 changeset if no revisions are specified.
1007 changeset if no revisions are specified.
1008
1008
1009 When two revision arguments are given, then changes are shown
1009 When two revision arguments are given, then changes are shown
1010 between those revisions. If only one revision is specified then
1010 between those revisions. If only one revision is specified then
1011 that revision is compared to the working directory, and, when no
1011 that revision is compared to the working directory, and, when no
1012 revisions are specified, the working directory files are compared
1012 revisions are specified, the working directory files are compared
1013 to its parent.
1013 to its parent.
1014
1014
1015 Without the -a option, diff will avoid generating diffs of files
1015 Without the -a option, diff will avoid generating diffs of files
1016 it detects as binary. With -a, diff will generate a diff anyway,
1016 it detects as binary. With -a, diff will generate a diff anyway,
1017 probably with undesirable results.
1017 probably with undesirable results.
1018 """
1018 """
1019 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1019 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1020
1020
1021 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1021 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1022
1022
1023 patch.diff(repo, node1, node2, fns, match=matchfn,
1023 patch.diff(repo, node1, node2, fns, match=matchfn,
1024 opts=patch.diffopts(ui, opts))
1024 opts=patch.diffopts(ui, opts))
1025
1025
1026 def export(ui, repo, *changesets, **opts):
1026 def export(ui, repo, *changesets, **opts):
1027 """dump the header and diffs for one or more changesets
1027 """dump the header and diffs for one or more changesets
1028
1028
1029 Print the changeset header and diffs for one or more revisions.
1029 Print the changeset header and diffs for one or more revisions.
1030
1030
1031 The information shown in the changeset header is: author,
1031 The information shown in the changeset header is: author,
1032 changeset hash, parent(s) and commit comment.
1032 changeset hash, parent(s) and commit comment.
1033
1033
1034 NOTE: export may generate unexpected diff output for merge changesets,
1034 NOTE: export may generate unexpected diff output for merge changesets,
1035 as it will compare the merge changeset against its first parent only.
1035 as it will compare the merge changeset against its first parent only.
1036
1036
1037 Output may be to a file, in which case the name of the file is
1037 Output may be to a file, in which case the name of the file is
1038 given using a format string. The formatting rules are as follows:
1038 given using a format string. The formatting rules are as follows:
1039
1039
1040 %% literal "%" character
1040 %% literal "%" character
1041 %H changeset hash (40 bytes of hexadecimal)
1041 %H changeset hash (40 bytes of hexadecimal)
1042 %N number of patches being generated
1042 %N number of patches being generated
1043 %R changeset revision number
1043 %R changeset revision number
1044 %b basename of the exporting repository
1044 %b basename of the exporting repository
1045 %h short-form changeset hash (12 bytes of hexadecimal)
1045 %h short-form changeset hash (12 bytes of hexadecimal)
1046 %n zero-padded sequence number, starting at 1
1046 %n zero-padded sequence number, starting at 1
1047 %r zero-padded changeset revision number
1047 %r zero-padded changeset revision number
1048
1048
1049 Without the -a option, export will avoid generating diffs of files
1049 Without the -a option, export will avoid generating diffs of files
1050 it detects as binary. With -a, export will generate a diff anyway,
1050 it detects as binary. With -a, export will generate a diff anyway,
1051 probably with undesirable results.
1051 probably with undesirable results.
1052
1052
1053 With the --switch-parent option, the diff will be against the second
1053 With the --switch-parent option, the diff will be against the second
1054 parent. It can be useful to review a merge.
1054 parent. It can be useful to review a merge.
1055 """
1055 """
1056 if not changesets:
1056 if not changesets:
1057 raise util.Abort(_("export requires at least one changeset"))
1057 raise util.Abort(_("export requires at least one changeset"))
1058 revs = cmdutil.revrange(repo, changesets)
1058 revs = cmdutil.revrange(repo, changesets)
1059 if len(revs) > 1:
1059 if len(revs) > 1:
1060 ui.note(_('exporting patches:\n'))
1060 ui.note(_('exporting patches:\n'))
1061 else:
1061 else:
1062 ui.note(_('exporting patch:\n'))
1062 ui.note(_('exporting patch:\n'))
1063 patch.export(repo, revs, template=opts['output'],
1063 patch.export(repo, revs, template=opts['output'],
1064 switch_parent=opts['switch_parent'],
1064 switch_parent=opts['switch_parent'],
1065 opts=patch.diffopts(ui, opts))
1065 opts=patch.diffopts(ui, opts))
1066
1066
1067 def grep(ui, repo, pattern, *pats, **opts):
1067 def grep(ui, repo, pattern, *pats, **opts):
1068 """search for a pattern in specified files and revisions
1068 """search for a pattern in specified files and revisions
1069
1069
1070 Search revisions of files for a regular expression.
1070 Search revisions of files for a regular expression.
1071
1071
1072 This command behaves differently than Unix grep. It only accepts
1072 This command behaves differently than Unix grep. It only accepts
1073 Python/Perl regexps. It searches repository history, not the
1073 Python/Perl regexps. It searches repository history, not the
1074 working directory. It always prints the revision number in which
1074 working directory. It always prints the revision number in which
1075 a match appears.
1075 a match appears.
1076
1076
1077 By default, grep only prints output for the first revision of a
1077 By default, grep only prints output for the first revision of a
1078 file in which it finds a match. To get it to print every revision
1078 file in which it finds a match. To get it to print every revision
1079 that contains a change in match status ("-" for a match that
1079 that contains a change in match status ("-" for a match that
1080 becomes a non-match, or "+" for a non-match that becomes a match),
1080 becomes a non-match, or "+" for a non-match that becomes a match),
1081 use the --all flag.
1081 use the --all flag.
1082 """
1082 """
1083 reflags = 0
1083 reflags = 0
1084 if opts['ignore_case']:
1084 if opts['ignore_case']:
1085 reflags |= re.I
1085 reflags |= re.I
1086 regexp = re.compile(pattern, reflags)
1086 regexp = re.compile(pattern, reflags)
1087 sep, eol = ':', '\n'
1087 sep, eol = ':', '\n'
1088 if opts['print0']:
1088 if opts['print0']:
1089 sep = eol = '\0'
1089 sep = eol = '\0'
1090
1090
1091 fcache = {}
1091 fcache = {}
1092 def getfile(fn):
1092 def getfile(fn):
1093 if fn not in fcache:
1093 if fn not in fcache:
1094 fcache[fn] = repo.file(fn)
1094 fcache[fn] = repo.file(fn)
1095 return fcache[fn]
1095 return fcache[fn]
1096
1096
1097 def matchlines(body):
1097 def matchlines(body):
1098 begin = 0
1098 begin = 0
1099 linenum = 0
1099 linenum = 0
1100 while True:
1100 while True:
1101 match = regexp.search(body, begin)
1101 match = regexp.search(body, begin)
1102 if not match:
1102 if not match:
1103 break
1103 break
1104 mstart, mend = match.span()
1104 mstart, mend = match.span()
1105 linenum += body.count('\n', begin, mstart) + 1
1105 linenum += body.count('\n', begin, mstart) + 1
1106 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1106 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1107 lend = body.find('\n', mend)
1107 lend = body.find('\n', mend)
1108 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1108 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1109 begin = lend + 1
1109 begin = lend + 1
1110
1110
1111 class linestate(object):
1111 class linestate(object):
1112 def __init__(self, line, linenum, colstart, colend):
1112 def __init__(self, line, linenum, colstart, colend):
1113 self.line = line
1113 self.line = line
1114 self.linenum = linenum
1114 self.linenum = linenum
1115 self.colstart = colstart
1115 self.colstart = colstart
1116 self.colend = colend
1116 self.colend = colend
1117
1117
1118 def __eq__(self, other):
1118 def __eq__(self, other):
1119 return self.line == other.line
1119 return self.line == other.line
1120
1120
1121 matches = {}
1121 matches = {}
1122 copies = {}
1122 copies = {}
1123 def grepbody(fn, rev, body):
1123 def grepbody(fn, rev, body):
1124 matches[rev].setdefault(fn, [])
1124 matches[rev].setdefault(fn, [])
1125 m = matches[rev][fn]
1125 m = matches[rev][fn]
1126 for lnum, cstart, cend, line in matchlines(body):
1126 for lnum, cstart, cend, line in matchlines(body):
1127 s = linestate(line, lnum, cstart, cend)
1127 s = linestate(line, lnum, cstart, cend)
1128 m.append(s)
1128 m.append(s)
1129
1129
1130 def difflinestates(a, b):
1130 def difflinestates(a, b):
1131 sm = difflib.SequenceMatcher(None, a, b)
1131 sm = difflib.SequenceMatcher(None, a, b)
1132 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1132 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1133 if tag == 'insert':
1133 if tag == 'insert':
1134 for i in xrange(blo, bhi):
1134 for i in xrange(blo, bhi):
1135 yield ('+', b[i])
1135 yield ('+', b[i])
1136 elif tag == 'delete':
1136 elif tag == 'delete':
1137 for i in xrange(alo, ahi):
1137 for i in xrange(alo, ahi):
1138 yield ('-', a[i])
1138 yield ('-', a[i])
1139 elif tag == 'replace':
1139 elif tag == 'replace':
1140 for i in xrange(alo, ahi):
1140 for i in xrange(alo, ahi):
1141 yield ('-', a[i])
1141 yield ('-', a[i])
1142 for i in xrange(blo, bhi):
1142 for i in xrange(blo, bhi):
1143 yield ('+', b[i])
1143 yield ('+', b[i])
1144
1144
1145 prev = {}
1145 prev = {}
1146 def display(fn, rev, states, prevstates):
1146 def display(fn, rev, states, prevstates):
1147 found = False
1147 found = False
1148 filerevmatches = {}
1148 filerevmatches = {}
1149 r = prev.get(fn, -1)
1149 r = prev.get(fn, -1)
1150 if opts['all']:
1150 if opts['all']:
1151 iter = difflinestates(states, prevstates)
1151 iter = difflinestates(states, prevstates)
1152 else:
1152 else:
1153 iter = [('', l) for l in prevstates]
1153 iter = [('', l) for l in prevstates]
1154 for change, l in iter:
1154 for change, l in iter:
1155 cols = [fn, str(r)]
1155 cols = [fn, str(r)]
1156 if opts['line_number']:
1156 if opts['line_number']:
1157 cols.append(str(l.linenum))
1157 cols.append(str(l.linenum))
1158 if opts['all']:
1158 if opts['all']:
1159 cols.append(change)
1159 cols.append(change)
1160 if opts['user']:
1160 if opts['user']:
1161 cols.append(ui.shortuser(get(r)[1]))
1161 cols.append(ui.shortuser(get(r)[1]))
1162 if opts['files_with_matches']:
1162 if opts['files_with_matches']:
1163 c = (fn, r)
1163 c = (fn, r)
1164 if c in filerevmatches:
1164 if c in filerevmatches:
1165 continue
1165 continue
1166 filerevmatches[c] = 1
1166 filerevmatches[c] = 1
1167 else:
1167 else:
1168 cols.append(l.line)
1168 cols.append(l.line)
1169 ui.write(sep.join(cols), eol)
1169 ui.write(sep.join(cols), eol)
1170 found = True
1170 found = True
1171 return found
1171 return found
1172
1172
1173 fstate = {}
1173 fstate = {}
1174 skip = {}
1174 skip = {}
1175 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1175 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1176 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1176 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1177 found = False
1177 found = False
1178 follow = opts.get('follow')
1178 follow = opts.get('follow')
1179 for st, rev, fns in changeiter:
1179 for st, rev, fns in changeiter:
1180 if st == 'window':
1180 if st == 'window':
1181 matches.clear()
1181 matches.clear()
1182 elif st == 'add':
1182 elif st == 'add':
1183 mf = repo.changectx(rev).manifest()
1183 mf = repo.changectx(rev).manifest()
1184 matches[rev] = {}
1184 matches[rev] = {}
1185 for fn in fns:
1185 for fn in fns:
1186 if fn in skip:
1186 if fn in skip:
1187 continue
1187 continue
1188 fstate.setdefault(fn, {})
1188 fstate.setdefault(fn, {})
1189 try:
1189 try:
1190 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1190 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1191 if follow:
1191 if follow:
1192 copied = getfile(fn).renamed(mf[fn])
1192 copied = getfile(fn).renamed(mf[fn])
1193 if copied:
1193 if copied:
1194 copies.setdefault(rev, {})[fn] = copied[0]
1194 copies.setdefault(rev, {})[fn] = copied[0]
1195 except KeyError:
1195 except KeyError:
1196 pass
1196 pass
1197 elif st == 'iter':
1197 elif st == 'iter':
1198 states = matches[rev].items()
1198 states = matches[rev].items()
1199 states.sort()
1199 states.sort()
1200 for fn, m in states:
1200 for fn, m in states:
1201 copy = copies.get(rev, {}).get(fn)
1201 copy = copies.get(rev, {}).get(fn)
1202 if fn in skip:
1202 if fn in skip:
1203 if copy:
1203 if copy:
1204 skip[copy] = True
1204 skip[copy] = True
1205 continue
1205 continue
1206 if fn in prev or fstate[fn]:
1206 if fn in prev or fstate[fn]:
1207 r = display(fn, rev, m, fstate[fn])
1207 r = display(fn, rev, m, fstate[fn])
1208 found = found or r
1208 found = found or r
1209 if r and not opts['all']:
1209 if r and not opts['all']:
1210 skip[fn] = True
1210 skip[fn] = True
1211 if copy:
1211 if copy:
1212 skip[copy] = True
1212 skip[copy] = True
1213 fstate[fn] = m
1213 fstate[fn] = m
1214 if copy:
1214 if copy:
1215 fstate[copy] = m
1215 fstate[copy] = m
1216 prev[fn] = rev
1216 prev[fn] = rev
1217
1217
1218 fstate = fstate.items()
1218 fstate = fstate.items()
1219 fstate.sort()
1219 fstate.sort()
1220 for fn, state in fstate:
1220 for fn, state in fstate:
1221 if fn in skip:
1221 if fn in skip:
1222 continue
1222 continue
1223 if fn not in copies.get(prev[fn], {}):
1223 if fn not in copies.get(prev[fn], {}):
1224 found = display(fn, rev, {}, state) or found
1224 found = display(fn, rev, {}, state) or found
1225 return (not found and 1) or 0
1225 return (not found and 1) or 0
1226
1226
1227 def heads(ui, repo, **opts):
1227 def heads(ui, repo, **opts):
1228 """show current repository heads
1228 """show current repository heads
1229
1229
1230 Show all repository head changesets.
1230 Show all repository head changesets.
1231
1231
1232 Repository "heads" are changesets that don't have children
1232 Repository "heads" are changesets that don't have children
1233 changesets. They are where development generally takes place and
1233 changesets. They are where development generally takes place and
1234 are the usual targets for update and merge operations.
1234 are the usual targets for update and merge operations.
1235 """
1235 """
1236 if opts['rev']:
1236 if opts['rev']:
1237 heads = repo.heads(repo.lookup(opts['rev']))
1237 heads = repo.heads(repo.lookup(opts['rev']))
1238 else:
1238 else:
1239 heads = repo.heads()
1239 heads = repo.heads()
1240 displayer = cmdutil.show_changeset(ui, repo, opts)
1240 displayer = cmdutil.show_changeset(ui, repo, opts)
1241 for n in heads:
1241 for n in heads:
1242 displayer.show(changenode=n)
1242 displayer.show(changenode=n)
1243
1243
1244 def help_(ui, name=None, with_version=False):
1244 def help_(ui, name=None, with_version=False):
1245 """show help for a command, extension, or list of commands
1245 """show help for a command, extension, or list of commands
1246
1246
1247 With no arguments, print a list of commands and short help.
1247 With no arguments, print a list of commands and short help.
1248
1248
1249 Given a command name, print help for that command.
1249 Given a command name, print help for that command.
1250
1250
1251 Given an extension name, print help for that extension, and the
1251 Given an extension name, print help for that extension, and the
1252 commands it provides."""
1252 commands it provides."""
1253 option_lists = []
1253 option_lists = []
1254
1254
1255 def helpcmd(name):
1255 def helpcmd(name):
1256 if with_version:
1256 if with_version:
1257 version_(ui)
1257 version_(ui)
1258 ui.write('\n')
1258 ui.write('\n')
1259 aliases, i = findcmd(ui, name)
1259 aliases, i = findcmd(ui, name)
1260 # synopsis
1260 # synopsis
1261 ui.write("%s\n\n" % i[2])
1261 ui.write("%s\n\n" % i[2])
1262
1262
1263 # description
1263 # description
1264 doc = i[0].__doc__
1264 doc = i[0].__doc__
1265 if not doc:
1265 if not doc:
1266 doc = _("(No help text available)")
1266 doc = _("(No help text available)")
1267 if ui.quiet:
1267 if ui.quiet:
1268 doc = doc.splitlines(0)[0]
1268 doc = doc.splitlines(0)[0]
1269 ui.write("%s\n" % doc.rstrip())
1269 ui.write("%s\n" % doc.rstrip())
1270
1270
1271 if not ui.quiet:
1271 if not ui.quiet:
1272 # aliases
1272 # aliases
1273 if len(aliases) > 1:
1273 if len(aliases) > 1:
1274 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1274 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1275
1275
1276 # options
1276 # options
1277 if i[1]:
1277 if i[1]:
1278 option_lists.append(("options", i[1]))
1278 option_lists.append(("options", i[1]))
1279
1279
1280 def helplist(select=None):
1280 def helplist(select=None):
1281 h = {}
1281 h = {}
1282 cmds = {}
1282 cmds = {}
1283 for c, e in table.items():
1283 for c, e in table.items():
1284 f = c.split("|", 1)[0]
1284 f = c.split("|", 1)[0]
1285 if select and not select(f):
1285 if select and not select(f):
1286 continue
1286 continue
1287 if name == "shortlist" and not f.startswith("^"):
1287 if name == "shortlist" and not f.startswith("^"):
1288 continue
1288 continue
1289 f = f.lstrip("^")
1289 f = f.lstrip("^")
1290 if not ui.debugflag and f.startswith("debug"):
1290 if not ui.debugflag and f.startswith("debug"):
1291 continue
1291 continue
1292 doc = e[0].__doc__
1292 doc = e[0].__doc__
1293 if not doc:
1293 if not doc:
1294 doc = _("(No help text available)")
1294 doc = _("(No help text available)")
1295 h[f] = doc.splitlines(0)[0].rstrip()
1295 h[f] = doc.splitlines(0)[0].rstrip()
1296 cmds[f] = c.lstrip("^")
1296 cmds[f] = c.lstrip("^")
1297
1297
1298 fns = h.keys()
1298 fns = h.keys()
1299 fns.sort()
1299 fns.sort()
1300 m = max(map(len, fns))
1300 m = max(map(len, fns))
1301 for f in fns:
1301 for f in fns:
1302 if ui.verbose:
1302 if ui.verbose:
1303 commands = cmds[f].replace("|",", ")
1303 commands = cmds[f].replace("|",", ")
1304 ui.write(" %s:\n %s\n"%(commands, h[f]))
1304 ui.write(" %s:\n %s\n"%(commands, h[f]))
1305 else:
1305 else:
1306 ui.write(' %-*s %s\n' % (m, f, h[f]))
1306 ui.write(' %-*s %s\n' % (m, f, h[f]))
1307
1307
1308 def helptopic(name):
1308 def helptopic(name):
1309 v = None
1309 v = None
1310 for i in help.helptable:
1310 for i in help.helptable:
1311 l = i.split('|')
1311 l = i.split('|')
1312 if name in l:
1312 if name in l:
1313 v = i
1313 v = i
1314 header = l[-1]
1314 header = l[-1]
1315 if not v:
1315 if not v:
1316 raise UnknownCommand(name)
1316 raise UnknownCommand(name)
1317
1317
1318 # description
1318 # description
1319 doc = help.helptable[v]
1319 doc = help.helptable[v]
1320 if not doc:
1320 if not doc:
1321 doc = _("(No help text available)")
1321 doc = _("(No help text available)")
1322 if callable(doc):
1322 if callable(doc):
1323 doc = doc()
1323 doc = doc()
1324
1324
1325 ui.write("%s\n" % header)
1325 ui.write("%s\n" % header)
1326 ui.write("%s\n" % doc.rstrip())
1326 ui.write("%s\n" % doc.rstrip())
1327
1327
1328 def helpext(name):
1328 def helpext(name):
1329 try:
1329 try:
1330 mod = findext(name)
1330 mod = findext(name)
1331 except KeyError:
1331 except KeyError:
1332 raise UnknownCommand(name)
1332 raise UnknownCommand(name)
1333
1333
1334 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1334 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1335 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1335 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1336 for d in doc[1:]:
1336 for d in doc[1:]:
1337 ui.write(d, '\n')
1337 ui.write(d, '\n')
1338
1338
1339 ui.status('\n')
1339 ui.status('\n')
1340
1341 try:
1342 ct = mod.cmdtable
1343 except AttributeError:
1344 ui.status(_('no commands defined\n'))
1345 return
1346
1340 if ui.verbose:
1347 if ui.verbose:
1341 ui.status(_('list of commands:\n\n'))
1348 ui.status(_('list of commands:\n\n'))
1342 else:
1349 else:
1343 ui.status(_('list of commands (use "hg help -v %s" '
1350 ui.status(_('list of commands (use "hg help -v %s" '
1344 'to show aliases and global options):\n\n') % name)
1351 'to show aliases and global options):\n\n') % name)
1345
1352
1346 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in mod.cmdtable])
1353 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1347 helplist(modcmds.has_key)
1354 helplist(modcmds.has_key)
1348
1355
1349 if name and name != 'shortlist':
1356 if name and name != 'shortlist':
1350 i = None
1357 i = None
1351 for f in (helpcmd, helptopic, helpext):
1358 for f in (helpcmd, helptopic, helpext):
1352 try:
1359 try:
1353 f(name)
1360 f(name)
1354 i = None
1361 i = None
1355 break
1362 break
1356 except UnknownCommand, inst:
1363 except UnknownCommand, inst:
1357 i = inst
1364 i = inst
1358 if i:
1365 if i:
1359 raise i
1366 raise i
1360
1367
1361 else:
1368 else:
1362 # program name
1369 # program name
1363 if ui.verbose or with_version:
1370 if ui.verbose or with_version:
1364 version_(ui)
1371 version_(ui)
1365 else:
1372 else:
1366 ui.status(_("Mercurial Distributed SCM\n"))
1373 ui.status(_("Mercurial Distributed SCM\n"))
1367 ui.status('\n')
1374 ui.status('\n')
1368
1375
1369 # list of commands
1376 # list of commands
1370 if name == "shortlist":
1377 if name == "shortlist":
1371 ui.status(_('basic commands (use "hg help" '
1378 ui.status(_('basic commands (use "hg help" '
1372 'for the full list or option "-v" for details):\n\n'))
1379 'for the full list or option "-v" for details):\n\n'))
1373 elif ui.verbose:
1380 elif ui.verbose:
1374 ui.status(_('list of commands:\n\n'))
1381 ui.status(_('list of commands:\n\n'))
1375 else:
1382 else:
1376 ui.status(_('list of commands (use "hg help -v" '
1383 ui.status(_('list of commands (use "hg help -v" '
1377 'to show aliases and global options):\n\n'))
1384 'to show aliases and global options):\n\n'))
1378
1385
1379 helplist()
1386 helplist()
1380
1387
1381 # global options
1388 # global options
1382 if ui.verbose:
1389 if ui.verbose:
1383 option_lists.append(("global options", globalopts))
1390 option_lists.append(("global options", globalopts))
1384
1391
1385 # list all option lists
1392 # list all option lists
1386 opt_output = []
1393 opt_output = []
1387 for title, options in option_lists:
1394 for title, options in option_lists:
1388 opt_output.append(("\n%s:\n" % title, None))
1395 opt_output.append(("\n%s:\n" % title, None))
1389 for shortopt, longopt, default, desc in options:
1396 for shortopt, longopt, default, desc in options:
1390 if "DEPRECATED" in desc and not ui.verbose: continue
1397 if "DEPRECATED" in desc and not ui.verbose: continue
1391 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1398 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1392 longopt and " --%s" % longopt),
1399 longopt and " --%s" % longopt),
1393 "%s%s" % (desc,
1400 "%s%s" % (desc,
1394 default
1401 default
1395 and _(" (default: %s)") % default
1402 and _(" (default: %s)") % default
1396 or "")))
1403 or "")))
1397
1404
1398 if opt_output:
1405 if opt_output:
1399 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1406 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1400 for first, second in opt_output:
1407 for first, second in opt_output:
1401 if second:
1408 if second:
1402 ui.write(" %-*s %s\n" % (opts_len, first, second))
1409 ui.write(" %-*s %s\n" % (opts_len, first, second))
1403 else:
1410 else:
1404 ui.write("%s\n" % first)
1411 ui.write("%s\n" % first)
1405
1412
1406 def identify(ui, repo):
1413 def identify(ui, repo):
1407 """print information about the working copy
1414 """print information about the working copy
1408
1415
1409 Print a short summary of the current state of the repo.
1416 Print a short summary of the current state of the repo.
1410
1417
1411 This summary identifies the repository state using one or two parent
1418 This summary identifies the repository state using one or two parent
1412 hash identifiers, followed by a "+" if there are uncommitted changes
1419 hash identifiers, followed by a "+" if there are uncommitted changes
1413 in the working directory, followed by a list of tags for this revision.
1420 in the working directory, followed by a list of tags for this revision.
1414 """
1421 """
1415 parents = [p for p in repo.dirstate.parents() if p != nullid]
1422 parents = [p for p in repo.dirstate.parents() if p != nullid]
1416 if not parents:
1423 if not parents:
1417 ui.write(_("unknown\n"))
1424 ui.write(_("unknown\n"))
1418 return
1425 return
1419
1426
1420 hexfunc = ui.debugflag and hex or short
1427 hexfunc = ui.debugflag and hex or short
1421 modified, added, removed, deleted = repo.status()[:4]
1428 modified, added, removed, deleted = repo.status()[:4]
1422 output = ["%s%s" %
1429 output = ["%s%s" %
1423 ('+'.join([hexfunc(parent) for parent in parents]),
1430 ('+'.join([hexfunc(parent) for parent in parents]),
1424 (modified or added or removed or deleted) and "+" or "")]
1431 (modified or added or removed or deleted) and "+" or "")]
1425
1432
1426 if not ui.quiet:
1433 if not ui.quiet:
1427
1434
1428 branch = util.tolocal(repo.workingctx().branch())
1435 branch = util.tolocal(repo.workingctx().branch())
1429 if branch:
1436 if branch:
1430 output.append("(%s)" % branch)
1437 output.append("(%s)" % branch)
1431
1438
1432 # multiple tags for a single parent separated by '/'
1439 # multiple tags for a single parent separated by '/'
1433 parenttags = ['/'.join(tags)
1440 parenttags = ['/'.join(tags)
1434 for tags in map(repo.nodetags, parents) if tags]
1441 for tags in map(repo.nodetags, parents) if tags]
1435 # tags for multiple parents separated by ' + '
1442 # tags for multiple parents separated by ' + '
1436 if parenttags:
1443 if parenttags:
1437 output.append(' + '.join(parenttags))
1444 output.append(' + '.join(parenttags))
1438
1445
1439 ui.write("%s\n" % ' '.join(output))
1446 ui.write("%s\n" % ' '.join(output))
1440
1447
1441 def import_(ui, repo, patch1, *patches, **opts):
1448 def import_(ui, repo, patch1, *patches, **opts):
1442 """import an ordered set of patches
1449 """import an ordered set of patches
1443
1450
1444 Import a list of patches and commit them individually.
1451 Import a list of patches and commit them individually.
1445
1452
1446 If there are outstanding changes in the working directory, import
1453 If there are outstanding changes in the working directory, import
1447 will abort unless given the -f flag.
1454 will abort unless given the -f flag.
1448
1455
1449 You can import a patch straight from a mail message. Even patches
1456 You can import a patch straight from a mail message. Even patches
1450 as attachments work (body part must be type text/plain or
1457 as attachments work (body part must be type text/plain or
1451 text/x-patch to be used). From and Subject headers of email
1458 text/x-patch to be used). From and Subject headers of email
1452 message are used as default committer and commit message. All
1459 message are used as default committer and commit message. All
1453 text/plain body parts before first diff are added to commit
1460 text/plain body parts before first diff are added to commit
1454 message.
1461 message.
1455
1462
1456 If imported patch was generated by hg export, user and description
1463 If imported patch was generated by hg export, user and description
1457 from patch override values from message headers and body. Values
1464 from patch override values from message headers and body. Values
1458 given on command line with -m and -u override these.
1465 given on command line with -m and -u override these.
1459
1466
1460 To read a patch from standard input, use patch name "-".
1467 To read a patch from standard input, use patch name "-".
1461 """
1468 """
1462 patches = (patch1,) + patches
1469 patches = (patch1,) + patches
1463
1470
1464 if not opts['force']:
1471 if not opts['force']:
1465 bail_if_changed(repo)
1472 bail_if_changed(repo)
1466
1473
1467 d = opts["base"]
1474 d = opts["base"]
1468 strip = opts["strip"]
1475 strip = opts["strip"]
1469
1476
1470 wlock = repo.wlock()
1477 wlock = repo.wlock()
1471 lock = repo.lock()
1478 lock = repo.lock()
1472
1479
1473 for p in patches:
1480 for p in patches:
1474 pf = os.path.join(d, p)
1481 pf = os.path.join(d, p)
1475
1482
1476 if pf == '-':
1483 if pf == '-':
1477 ui.status(_("applying patch from stdin\n"))
1484 ui.status(_("applying patch from stdin\n"))
1478 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1485 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1479 else:
1486 else:
1480 ui.status(_("applying %s\n") % p)
1487 ui.status(_("applying %s\n") % p)
1481 tmpname, message, user, date = patch.extract(ui, file(pf))
1488 tmpname, message, user, date = patch.extract(ui, file(pf))
1482
1489
1483 if tmpname is None:
1490 if tmpname is None:
1484 raise util.Abort(_('no diffs found'))
1491 raise util.Abort(_('no diffs found'))
1485
1492
1486 try:
1493 try:
1487 cmdline_message = logmessage(opts)
1494 cmdline_message = logmessage(opts)
1488 if cmdline_message:
1495 if cmdline_message:
1489 # pickup the cmdline msg
1496 # pickup the cmdline msg
1490 message = cmdline_message
1497 message = cmdline_message
1491 elif message:
1498 elif message:
1492 # pickup the patch msg
1499 # pickup the patch msg
1493 message = message.strip()
1500 message = message.strip()
1494 else:
1501 else:
1495 # launch the editor
1502 # launch the editor
1496 message = None
1503 message = None
1497 ui.debug(_('message:\n%s\n') % message)
1504 ui.debug(_('message:\n%s\n') % message)
1498
1505
1499 files = {}
1506 files = {}
1500 try:
1507 try:
1501 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1508 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1502 files=files)
1509 files=files)
1503 finally:
1510 finally:
1504 files = patch.updatedir(ui, repo, files, wlock=wlock)
1511 files = patch.updatedir(ui, repo, files, wlock=wlock)
1505 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1512 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1506 finally:
1513 finally:
1507 os.unlink(tmpname)
1514 os.unlink(tmpname)
1508
1515
1509 def incoming(ui, repo, source="default", **opts):
1516 def incoming(ui, repo, source="default", **opts):
1510 """show new changesets found in source
1517 """show new changesets found in source
1511
1518
1512 Show new changesets found in the specified path/URL or the default
1519 Show new changesets found in the specified path/URL or the default
1513 pull location. These are the changesets that would be pulled if a pull
1520 pull location. These are the changesets that would be pulled if a pull
1514 was requested.
1521 was requested.
1515
1522
1516 For remote repository, using --bundle avoids downloading the changesets
1523 For remote repository, using --bundle avoids downloading the changesets
1517 twice if the incoming is followed by a pull.
1524 twice if the incoming is followed by a pull.
1518
1525
1519 See pull for valid source format details.
1526 See pull for valid source format details.
1520 """
1527 """
1521 source = ui.expandpath(source)
1528 source = ui.expandpath(source)
1522 setremoteconfig(ui, opts)
1529 setremoteconfig(ui, opts)
1523
1530
1524 other = hg.repository(ui, source)
1531 other = hg.repository(ui, source)
1525 incoming = repo.findincoming(other, force=opts["force"])
1532 incoming = repo.findincoming(other, force=opts["force"])
1526 if not incoming:
1533 if not incoming:
1527 try:
1534 try:
1528 os.unlink(opts["bundle"])
1535 os.unlink(opts["bundle"])
1529 except:
1536 except:
1530 pass
1537 pass
1531 ui.status(_("no changes found\n"))
1538 ui.status(_("no changes found\n"))
1532 return 1
1539 return 1
1533
1540
1534 cleanup = None
1541 cleanup = None
1535 try:
1542 try:
1536 fname = opts["bundle"]
1543 fname = opts["bundle"]
1537 if fname or not other.local():
1544 if fname or not other.local():
1538 # create a bundle (uncompressed if other repo is not local)
1545 # create a bundle (uncompressed if other repo is not local)
1539 cg = other.changegroup(incoming, "incoming")
1546 cg = other.changegroup(incoming, "incoming")
1540 bundletype = other.local() and "HG10BZ" or "HG10UN"
1547 bundletype = other.local() and "HG10BZ" or "HG10UN"
1541 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1548 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1542 # keep written bundle?
1549 # keep written bundle?
1543 if opts["bundle"]:
1550 if opts["bundle"]:
1544 cleanup = None
1551 cleanup = None
1545 if not other.local():
1552 if not other.local():
1546 # use the created uncompressed bundlerepo
1553 # use the created uncompressed bundlerepo
1547 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1554 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1548
1555
1549 revs = None
1556 revs = None
1550 if opts['rev']:
1557 if opts['rev']:
1551 revs = [other.lookup(rev) for rev in opts['rev']]
1558 revs = [other.lookup(rev) for rev in opts['rev']]
1552 o = other.changelog.nodesbetween(incoming, revs)[0]
1559 o = other.changelog.nodesbetween(incoming, revs)[0]
1553 if opts['newest_first']:
1560 if opts['newest_first']:
1554 o.reverse()
1561 o.reverse()
1555 displayer = cmdutil.show_changeset(ui, other, opts)
1562 displayer = cmdutil.show_changeset(ui, other, opts)
1556 for n in o:
1563 for n in o:
1557 parents = [p for p in other.changelog.parents(n) if p != nullid]
1564 parents = [p for p in other.changelog.parents(n) if p != nullid]
1558 if opts['no_merges'] and len(parents) == 2:
1565 if opts['no_merges'] and len(parents) == 2:
1559 continue
1566 continue
1560 displayer.show(changenode=n)
1567 displayer.show(changenode=n)
1561 finally:
1568 finally:
1562 if hasattr(other, 'close'):
1569 if hasattr(other, 'close'):
1563 other.close()
1570 other.close()
1564 if cleanup:
1571 if cleanup:
1565 os.unlink(cleanup)
1572 os.unlink(cleanup)
1566
1573
1567 def init(ui, dest=".", **opts):
1574 def init(ui, dest=".", **opts):
1568 """create a new repository in the given directory
1575 """create a new repository in the given directory
1569
1576
1570 Initialize a new repository in the given directory. If the given
1577 Initialize a new repository in the given directory. If the given
1571 directory does not exist, it is created.
1578 directory does not exist, it is created.
1572
1579
1573 If no directory is given, the current directory is used.
1580 If no directory is given, the current directory is used.
1574
1581
1575 It is possible to specify an ssh:// URL as the destination.
1582 It is possible to specify an ssh:// URL as the destination.
1576 Look at the help text for the pull command for important details
1583 Look at the help text for the pull command for important details
1577 about ssh:// URLs.
1584 about ssh:// URLs.
1578 """
1585 """
1579 setremoteconfig(ui, opts)
1586 setremoteconfig(ui, opts)
1580 hg.repository(ui, dest, create=1)
1587 hg.repository(ui, dest, create=1)
1581
1588
1582 def locate(ui, repo, *pats, **opts):
1589 def locate(ui, repo, *pats, **opts):
1583 """locate files matching specific patterns
1590 """locate files matching specific patterns
1584
1591
1585 Print all files under Mercurial control whose names match the
1592 Print all files under Mercurial control whose names match the
1586 given patterns.
1593 given patterns.
1587
1594
1588 This command searches the current directory and its
1595 This command searches the current directory and its
1589 subdirectories. To search an entire repository, move to the root
1596 subdirectories. To search an entire repository, move to the root
1590 of the repository.
1597 of the repository.
1591
1598
1592 If no patterns are given to match, this command prints all file
1599 If no patterns are given to match, this command prints all file
1593 names.
1600 names.
1594
1601
1595 If you want to feed the output of this command into the "xargs"
1602 If you want to feed the output of this command into the "xargs"
1596 command, use the "-0" option to both this command and "xargs".
1603 command, use the "-0" option to both this command and "xargs".
1597 This will avoid the problem of "xargs" treating single filenames
1604 This will avoid the problem of "xargs" treating single filenames
1598 that contain white space as multiple filenames.
1605 that contain white space as multiple filenames.
1599 """
1606 """
1600 end = opts['print0'] and '\0' or '\n'
1607 end = opts['print0'] and '\0' or '\n'
1601 rev = opts['rev']
1608 rev = opts['rev']
1602 if rev:
1609 if rev:
1603 node = repo.lookup(rev)
1610 node = repo.lookup(rev)
1604 else:
1611 else:
1605 node = None
1612 node = None
1606
1613
1607 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1614 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1608 head='(?:.*/|)'):
1615 head='(?:.*/|)'):
1609 if not node and repo.dirstate.state(abs) == '?':
1616 if not node and repo.dirstate.state(abs) == '?':
1610 continue
1617 continue
1611 if opts['fullpath']:
1618 if opts['fullpath']:
1612 ui.write(os.path.join(repo.root, abs), end)
1619 ui.write(os.path.join(repo.root, abs), end)
1613 else:
1620 else:
1614 ui.write(((pats and rel) or abs), end)
1621 ui.write(((pats and rel) or abs), end)
1615
1622
1616 def log(ui, repo, *pats, **opts):
1623 def log(ui, repo, *pats, **opts):
1617 """show revision history of entire repository or files
1624 """show revision history of entire repository or files
1618
1625
1619 Print the revision history of the specified files or the entire
1626 Print the revision history of the specified files or the entire
1620 project.
1627 project.
1621
1628
1622 File history is shown without following rename or copy history of
1629 File history is shown without following rename or copy history of
1623 files. Use -f/--follow with a file name to follow history across
1630 files. Use -f/--follow with a file name to follow history across
1624 renames and copies. --follow without a file name will only show
1631 renames and copies. --follow without a file name will only show
1625 ancestors or descendants of the starting revision. --follow-first
1632 ancestors or descendants of the starting revision. --follow-first
1626 only follows the first parent of merge revisions.
1633 only follows the first parent of merge revisions.
1627
1634
1628 If no revision range is specified, the default is tip:0 unless
1635 If no revision range is specified, the default is tip:0 unless
1629 --follow is set, in which case the working directory parent is
1636 --follow is set, in which case the working directory parent is
1630 used as the starting revision.
1637 used as the starting revision.
1631
1638
1632 By default this command outputs: changeset id and hash, tags,
1639 By default this command outputs: changeset id and hash, tags,
1633 non-trivial parents, user, date and time, and a summary for each
1640 non-trivial parents, user, date and time, and a summary for each
1634 commit. When the -v/--verbose switch is used, the list of changed
1641 commit. When the -v/--verbose switch is used, the list of changed
1635 files and full commit message is shown.
1642 files and full commit message is shown.
1636
1643
1637 NOTE: log -p may generate unexpected diff output for merge
1644 NOTE: log -p may generate unexpected diff output for merge
1638 changesets, as it will compare the merge changeset against its
1645 changesets, as it will compare the merge changeset against its
1639 first parent only. Also, the files: list will only reflect files
1646 first parent only. Also, the files: list will only reflect files
1640 that are different from BOTH parents.
1647 that are different from BOTH parents.
1641
1648
1642 """
1649 """
1643
1650
1644 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1651 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1645 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1652 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1646
1653
1647 if opts['limit']:
1654 if opts['limit']:
1648 try:
1655 try:
1649 limit = int(opts['limit'])
1656 limit = int(opts['limit'])
1650 except ValueError:
1657 except ValueError:
1651 raise util.Abort(_('limit must be a positive integer'))
1658 raise util.Abort(_('limit must be a positive integer'))
1652 if limit <= 0: raise util.Abort(_('limit must be positive'))
1659 if limit <= 0: raise util.Abort(_('limit must be positive'))
1653 else:
1660 else:
1654 limit = sys.maxint
1661 limit = sys.maxint
1655 count = 0
1662 count = 0
1656
1663
1657 if opts['copies'] and opts['rev']:
1664 if opts['copies'] and opts['rev']:
1658 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1665 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1659 else:
1666 else:
1660 endrev = repo.changelog.count()
1667 endrev = repo.changelog.count()
1661 rcache = {}
1668 rcache = {}
1662 ncache = {}
1669 ncache = {}
1663 dcache = []
1670 dcache = []
1664 def getrenamed(fn, rev, man):
1671 def getrenamed(fn, rev, man):
1665 '''looks up all renames for a file (up to endrev) the first
1672 '''looks up all renames for a file (up to endrev) the first
1666 time the file is given. It indexes on the changerev and only
1673 time the file is given. It indexes on the changerev and only
1667 parses the manifest if linkrev != changerev.
1674 parses the manifest if linkrev != changerev.
1668 Returns rename info for fn at changerev rev.'''
1675 Returns rename info for fn at changerev rev.'''
1669 if fn not in rcache:
1676 if fn not in rcache:
1670 rcache[fn] = {}
1677 rcache[fn] = {}
1671 ncache[fn] = {}
1678 ncache[fn] = {}
1672 fl = repo.file(fn)
1679 fl = repo.file(fn)
1673 for i in xrange(fl.count()):
1680 for i in xrange(fl.count()):
1674 node = fl.node(i)
1681 node = fl.node(i)
1675 lr = fl.linkrev(node)
1682 lr = fl.linkrev(node)
1676 renamed = fl.renamed(node)
1683 renamed = fl.renamed(node)
1677 rcache[fn][lr] = renamed
1684 rcache[fn][lr] = renamed
1678 if renamed:
1685 if renamed:
1679 ncache[fn][node] = renamed
1686 ncache[fn][node] = renamed
1680 if lr >= endrev:
1687 if lr >= endrev:
1681 break
1688 break
1682 if rev in rcache[fn]:
1689 if rev in rcache[fn]:
1683 return rcache[fn][rev]
1690 return rcache[fn][rev]
1684 mr = repo.manifest.rev(man)
1691 mr = repo.manifest.rev(man)
1685 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1692 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1686 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1693 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1687 if not dcache or dcache[0] != man:
1694 if not dcache or dcache[0] != man:
1688 dcache[:] = [man, repo.manifest.readdelta(man)]
1695 dcache[:] = [man, repo.manifest.readdelta(man)]
1689 if fn in dcache[1]:
1696 if fn in dcache[1]:
1690 return ncache[fn].get(dcache[1][fn])
1697 return ncache[fn].get(dcache[1][fn])
1691 return None
1698 return None
1692
1699
1693 df = False
1700 df = False
1694 if opts["date"]:
1701 if opts["date"]:
1695 df = util.matchdate(opts["date"])
1702 df = util.matchdate(opts["date"])
1696
1703
1697 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1704 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1698 for st, rev, fns in changeiter:
1705 for st, rev, fns in changeiter:
1699 if st == 'add':
1706 if st == 'add':
1700 changenode = repo.changelog.node(rev)
1707 changenode = repo.changelog.node(rev)
1701 parents = [p for p in repo.changelog.parentrevs(rev)
1708 parents = [p for p in repo.changelog.parentrevs(rev)
1702 if p != nullrev]
1709 if p != nullrev]
1703 if opts['no_merges'] and len(parents) == 2:
1710 if opts['no_merges'] and len(parents) == 2:
1704 continue
1711 continue
1705 if opts['only_merges'] and len(parents) != 2:
1712 if opts['only_merges'] and len(parents) != 2:
1706 continue
1713 continue
1707
1714
1708 if df:
1715 if df:
1709 changes = get(rev)
1716 changes = get(rev)
1710 if not df(changes[2][0]):
1717 if not df(changes[2][0]):
1711 continue
1718 continue
1712
1719
1713 if opts['keyword']:
1720 if opts['keyword']:
1714 changes = get(rev)
1721 changes = get(rev)
1715 miss = 0
1722 miss = 0
1716 for k in [kw.lower() for kw in opts['keyword']]:
1723 for k in [kw.lower() for kw in opts['keyword']]:
1717 if not (k in changes[1].lower() or
1724 if not (k in changes[1].lower() or
1718 k in changes[4].lower() or
1725 k in changes[4].lower() or
1719 k in " ".join(changes[3][:20]).lower()):
1726 k in " ".join(changes[3][:20]).lower()):
1720 miss = 1
1727 miss = 1
1721 break
1728 break
1722 if miss:
1729 if miss:
1723 continue
1730 continue
1724
1731
1725 copies = []
1732 copies = []
1726 if opts.get('copies') and rev:
1733 if opts.get('copies') and rev:
1727 mf = get(rev)[0]
1734 mf = get(rev)[0]
1728 for fn in get(rev)[3]:
1735 for fn in get(rev)[3]:
1729 rename = getrenamed(fn, rev, mf)
1736 rename = getrenamed(fn, rev, mf)
1730 if rename:
1737 if rename:
1731 copies.append((fn, rename[0]))
1738 copies.append((fn, rename[0]))
1732 displayer.show(rev, changenode, copies=copies)
1739 displayer.show(rev, changenode, copies=copies)
1733 elif st == 'iter':
1740 elif st == 'iter':
1734 if count == limit: break
1741 if count == limit: break
1735 if displayer.flush(rev):
1742 if displayer.flush(rev):
1736 count += 1
1743 count += 1
1737
1744
1738 def manifest(ui, repo, rev=None):
1745 def manifest(ui, repo, rev=None):
1739 """output the current or given revision of the project manifest
1746 """output the current or given revision of the project manifest
1740
1747
1741 Print a list of version controlled files for the given revision.
1748 Print a list of version controlled files for the given revision.
1742 If no revision is given, the parent of the working directory is used,
1749 If no revision is given, the parent of the working directory is used,
1743 or tip if no revision is checked out.
1750 or tip if no revision is checked out.
1744
1751
1745 The manifest is the list of files being version controlled. If no revision
1752 The manifest is the list of files being version controlled. If no revision
1746 is given then the first parent of the working directory is used.
1753 is given then the first parent of the working directory is used.
1747
1754
1748 With -v flag, print file permissions. With --debug flag, print
1755 With -v flag, print file permissions. With --debug flag, print
1749 file revision hashes.
1756 file revision hashes.
1750 """
1757 """
1751
1758
1752 m = repo.changectx(rev).manifest()
1759 m = repo.changectx(rev).manifest()
1753 files = m.keys()
1760 files = m.keys()
1754 files.sort()
1761 files.sort()
1755
1762
1756 for f in files:
1763 for f in files:
1757 if ui.debugflag:
1764 if ui.debugflag:
1758 ui.write("%40s " % hex(m[f]))
1765 ui.write("%40s " % hex(m[f]))
1759 if ui.verbose:
1766 if ui.verbose:
1760 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1767 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1761 ui.write("%s\n" % f)
1768 ui.write("%s\n" % f)
1762
1769
1763 def merge(ui, repo, node=None, force=None):
1770 def merge(ui, repo, node=None, force=None):
1764 """Merge working directory with another revision
1771 """merge working directory with another revision
1765
1772
1766 Merge the contents of the current working directory and the
1773 Merge the contents of the current working directory and the
1767 requested revision. Files that changed between either parent are
1774 requested revision. Files that changed between either parent are
1768 marked as changed for the next commit and a commit must be
1775 marked as changed for the next commit and a commit must be
1769 performed before any further updates are allowed.
1776 performed before any further updates are allowed.
1770
1777
1771 If no revision is specified, the working directory's parent is a
1778 If no revision is specified, the working directory's parent is a
1772 head revision, and the repository contains exactly one other head,
1779 head revision, and the repository contains exactly one other head,
1773 the other head is merged with by default. Otherwise, an explicit
1780 the other head is merged with by default. Otherwise, an explicit
1774 revision to merge with must be provided.
1781 revision to merge with must be provided.
1775 """
1782 """
1776
1783
1777 if not node:
1784 if not node:
1778 heads = repo.heads()
1785 heads = repo.heads()
1779 if len(heads) > 2:
1786 if len(heads) > 2:
1780 raise util.Abort(_('repo has %d heads - '
1787 raise util.Abort(_('repo has %d heads - '
1781 'please merge with an explicit rev') %
1788 'please merge with an explicit rev') %
1782 len(heads))
1789 len(heads))
1783 if len(heads) == 1:
1790 if len(heads) == 1:
1784 raise util.Abort(_('there is nothing to merge - '
1791 raise util.Abort(_('there is nothing to merge - '
1785 'use "hg update" instead'))
1792 'use "hg update" instead'))
1786 parent = repo.dirstate.parents()[0]
1793 parent = repo.dirstate.parents()[0]
1787 if parent not in heads:
1794 if parent not in heads:
1788 raise util.Abort(_('working dir not at a head rev - '
1795 raise util.Abort(_('working dir not at a head rev - '
1789 'use "hg update" or merge with an explicit rev'))
1796 'use "hg update" or merge with an explicit rev'))
1790 node = parent == heads[0] and heads[-1] or heads[0]
1797 node = parent == heads[0] and heads[-1] or heads[0]
1791 return hg.merge(repo, node, force=force)
1798 return hg.merge(repo, node, force=force)
1792
1799
1793 def outgoing(ui, repo, dest=None, **opts):
1800 def outgoing(ui, repo, dest=None, **opts):
1794 """show changesets not found in destination
1801 """show changesets not found in destination
1795
1802
1796 Show changesets not found in the specified destination repository or
1803 Show changesets not found in the specified destination repository or
1797 the default push location. These are the changesets that would be pushed
1804 the default push location. These are the changesets that would be pushed
1798 if a push was requested.
1805 if a push was requested.
1799
1806
1800 See pull for valid destination format details.
1807 See pull for valid destination format details.
1801 """
1808 """
1802 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1809 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1803 setremoteconfig(ui, opts)
1810 setremoteconfig(ui, opts)
1804 revs = None
1811 revs = None
1805 if opts['rev']:
1812 if opts['rev']:
1806 revs = [repo.lookup(rev) for rev in opts['rev']]
1813 revs = [repo.lookup(rev) for rev in opts['rev']]
1807
1814
1808 other = hg.repository(ui, dest)
1815 other = hg.repository(ui, dest)
1809 o = repo.findoutgoing(other, force=opts['force'])
1816 o = repo.findoutgoing(other, force=opts['force'])
1810 if not o:
1817 if not o:
1811 ui.status(_("no changes found\n"))
1818 ui.status(_("no changes found\n"))
1812 return 1
1819 return 1
1813 o = repo.changelog.nodesbetween(o, revs)[0]
1820 o = repo.changelog.nodesbetween(o, revs)[0]
1814 if opts['newest_first']:
1821 if opts['newest_first']:
1815 o.reverse()
1822 o.reverse()
1816 displayer = cmdutil.show_changeset(ui, repo, opts)
1823 displayer = cmdutil.show_changeset(ui, repo, opts)
1817 for n in o:
1824 for n in o:
1818 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1825 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1819 if opts['no_merges'] and len(parents) == 2:
1826 if opts['no_merges'] and len(parents) == 2:
1820 continue
1827 continue
1821 displayer.show(changenode=n)
1828 displayer.show(changenode=n)
1822
1829
1823 def parents(ui, repo, file_=None, **opts):
1830 def parents(ui, repo, file_=None, **opts):
1824 """show the parents of the working dir or revision
1831 """show the parents of the working dir or revision
1825
1832
1826 Print the working directory's parent revisions.
1833 Print the working directory's parent revisions.
1827 """
1834 """
1828 rev = opts.get('rev')
1835 rev = opts.get('rev')
1829 if rev:
1836 if rev:
1830 if file_:
1837 if file_:
1831 ctx = repo.filectx(file_, changeid=rev)
1838 ctx = repo.filectx(file_, changeid=rev)
1832 else:
1839 else:
1833 ctx = repo.changectx(rev)
1840 ctx = repo.changectx(rev)
1834 p = [cp.node() for cp in ctx.parents()]
1841 p = [cp.node() for cp in ctx.parents()]
1835 else:
1842 else:
1836 p = repo.dirstate.parents()
1843 p = repo.dirstate.parents()
1837
1844
1838 displayer = cmdutil.show_changeset(ui, repo, opts)
1845 displayer = cmdutil.show_changeset(ui, repo, opts)
1839 for n in p:
1846 for n in p:
1840 if n != nullid:
1847 if n != nullid:
1841 displayer.show(changenode=n)
1848 displayer.show(changenode=n)
1842
1849
1843 def paths(ui, repo, search=None):
1850 def paths(ui, repo, search=None):
1844 """show definition of symbolic path names
1851 """show definition of symbolic path names
1845
1852
1846 Show definition of symbolic path name NAME. If no name is given, show
1853 Show definition of symbolic path name NAME. If no name is given, show
1847 definition of available names.
1854 definition of available names.
1848
1855
1849 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1856 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1850 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1857 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1851 """
1858 """
1852 if search:
1859 if search:
1853 for name, path in ui.configitems("paths"):
1860 for name, path in ui.configitems("paths"):
1854 if name == search:
1861 if name == search:
1855 ui.write("%s\n" % path)
1862 ui.write("%s\n" % path)
1856 return
1863 return
1857 ui.warn(_("not found!\n"))
1864 ui.warn(_("not found!\n"))
1858 return 1
1865 return 1
1859 else:
1866 else:
1860 for name, path in ui.configitems("paths"):
1867 for name, path in ui.configitems("paths"):
1861 ui.write("%s = %s\n" % (name, path))
1868 ui.write("%s = %s\n" % (name, path))
1862
1869
1863 def postincoming(ui, repo, modheads, optupdate):
1870 def postincoming(ui, repo, modheads, optupdate):
1864 if modheads == 0:
1871 if modheads == 0:
1865 return
1872 return
1866 if optupdate:
1873 if optupdate:
1867 if modheads == 1:
1874 if modheads == 1:
1868 return hg.update(repo, repo.changelog.tip()) # update
1875 return hg.update(repo, repo.changelog.tip()) # update
1869 else:
1876 else:
1870 ui.status(_("not updating, since new heads added\n"))
1877 ui.status(_("not updating, since new heads added\n"))
1871 if modheads > 1:
1878 if modheads > 1:
1872 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1879 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1873 else:
1880 else:
1874 ui.status(_("(run 'hg update' to get a working copy)\n"))
1881 ui.status(_("(run 'hg update' to get a working copy)\n"))
1875
1882
1876 def pull(ui, repo, source="default", **opts):
1883 def pull(ui, repo, source="default", **opts):
1877 """pull changes from the specified source
1884 """pull changes from the specified source
1878
1885
1879 Pull changes from a remote repository to a local one.
1886 Pull changes from a remote repository to a local one.
1880
1887
1881 This finds all changes from the repository at the specified path
1888 This finds all changes from the repository at the specified path
1882 or URL and adds them to the local repository. By default, this
1889 or URL and adds them to the local repository. By default, this
1883 does not update the copy of the project in the working directory.
1890 does not update the copy of the project in the working directory.
1884
1891
1885 Valid URLs are of the form:
1892 Valid URLs are of the form:
1886
1893
1887 local/filesystem/path (or file://local/filesystem/path)
1894 local/filesystem/path (or file://local/filesystem/path)
1888 http://[user@]host[:port]/[path]
1895 http://[user@]host[:port]/[path]
1889 https://[user@]host[:port]/[path]
1896 https://[user@]host[:port]/[path]
1890 ssh://[user@]host[:port]/[path]
1897 ssh://[user@]host[:port]/[path]
1891 static-http://host[:port]/[path]
1898 static-http://host[:port]/[path]
1892
1899
1893 Paths in the local filesystem can either point to Mercurial
1900 Paths in the local filesystem can either point to Mercurial
1894 repositories or to bundle files (as created by 'hg bundle' or
1901 repositories or to bundle files (as created by 'hg bundle' or
1895 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1902 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1896 allows access to a Mercurial repository where you simply use a web
1903 allows access to a Mercurial repository where you simply use a web
1897 server to publish the .hg directory as static content.
1904 server to publish the .hg directory as static content.
1898
1905
1899 Some notes about using SSH with Mercurial:
1906 Some notes about using SSH with Mercurial:
1900 - SSH requires an accessible shell account on the destination machine
1907 - SSH requires an accessible shell account on the destination machine
1901 and a copy of hg in the remote path or specified with as remotecmd.
1908 and a copy of hg in the remote path or specified with as remotecmd.
1902 - path is relative to the remote user's home directory by default.
1909 - path is relative to the remote user's home directory by default.
1903 Use an extra slash at the start of a path to specify an absolute path:
1910 Use an extra slash at the start of a path to specify an absolute path:
1904 ssh://example.com//tmp/repository
1911 ssh://example.com//tmp/repository
1905 - Mercurial doesn't use its own compression via SSH; the right thing
1912 - Mercurial doesn't use its own compression via SSH; the right thing
1906 to do is to configure it in your ~/.ssh/config, e.g.:
1913 to do is to configure it in your ~/.ssh/config, e.g.:
1907 Host *.mylocalnetwork.example.com
1914 Host *.mylocalnetwork.example.com
1908 Compression no
1915 Compression no
1909 Host *
1916 Host *
1910 Compression yes
1917 Compression yes
1911 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1918 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1912 with the --ssh command line option.
1919 with the --ssh command line option.
1913 """
1920 """
1914 source = ui.expandpath(source)
1921 source = ui.expandpath(source)
1915 setremoteconfig(ui, opts)
1922 setremoteconfig(ui, opts)
1916
1923
1917 other = hg.repository(ui, source)
1924 other = hg.repository(ui, source)
1918 ui.status(_('pulling from %s\n') % (source))
1925 ui.status(_('pulling from %s\n') % (source))
1919 revs = None
1926 revs = None
1920 if opts['rev']:
1927 if opts['rev']:
1921 if 'lookup' in other.capabilities:
1928 if 'lookup' in other.capabilities:
1922 revs = [other.lookup(rev) for rev in opts['rev']]
1929 revs = [other.lookup(rev) for rev in opts['rev']]
1923 else:
1930 else:
1924 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1931 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1925 raise util.Abort(error)
1932 raise util.Abort(error)
1926 modheads = repo.pull(other, heads=revs, force=opts['force'])
1933 modheads = repo.pull(other, heads=revs, force=opts['force'])
1927 return postincoming(ui, repo, modheads, opts['update'])
1934 return postincoming(ui, repo, modheads, opts['update'])
1928
1935
1929 def push(ui, repo, dest=None, **opts):
1936 def push(ui, repo, dest=None, **opts):
1930 """push changes to the specified destination
1937 """push changes to the specified destination
1931
1938
1932 Push changes from the local repository to the given destination.
1939 Push changes from the local repository to the given destination.
1933
1940
1934 This is the symmetrical operation for pull. It helps to move
1941 This is the symmetrical operation for pull. It helps to move
1935 changes from the current repository to a different one. If the
1942 changes from the current repository to a different one. If the
1936 destination is local this is identical to a pull in that directory
1943 destination is local this is identical to a pull in that directory
1937 from the current one.
1944 from the current one.
1938
1945
1939 By default, push will refuse to run if it detects the result would
1946 By default, push will refuse to run if it detects the result would
1940 increase the number of remote heads. This generally indicates the
1947 increase the number of remote heads. This generally indicates the
1941 the client has forgotten to sync and merge before pushing.
1948 the client has forgotten to sync and merge before pushing.
1942
1949
1943 Valid URLs are of the form:
1950 Valid URLs are of the form:
1944
1951
1945 local/filesystem/path (or file://local/filesystem/path)
1952 local/filesystem/path (or file://local/filesystem/path)
1946 ssh://[user@]host[:port]/[path]
1953 ssh://[user@]host[:port]/[path]
1947 http://[user@]host[:port]/[path]
1954 http://[user@]host[:port]/[path]
1948 https://[user@]host[:port]/[path]
1955 https://[user@]host[:port]/[path]
1949
1956
1950 Look at the help text for the pull command for important details
1957 Look at the help text for the pull command for important details
1951 about ssh:// URLs.
1958 about ssh:// URLs.
1952
1959
1953 Pushing to http:// and https:// URLs is only possible, if this
1960 Pushing to http:// and https:// URLs is only possible, if this
1954 feature is explicitly enabled on the remote Mercurial server.
1961 feature is explicitly enabled on the remote Mercurial server.
1955 """
1962 """
1956 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1963 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1957 setremoteconfig(ui, opts)
1964 setremoteconfig(ui, opts)
1958
1965
1959 other = hg.repository(ui, dest)
1966 other = hg.repository(ui, dest)
1960 ui.status('pushing to %s\n' % (dest))
1967 ui.status('pushing to %s\n' % (dest))
1961 revs = None
1968 revs = None
1962 if opts['rev']:
1969 if opts['rev']:
1963 revs = [repo.lookup(rev) for rev in opts['rev']]
1970 revs = [repo.lookup(rev) for rev in opts['rev']]
1964 r = repo.push(other, opts['force'], revs=revs)
1971 r = repo.push(other, opts['force'], revs=revs)
1965 return r == 0
1972 return r == 0
1966
1973
1967 def rawcommit(ui, repo, *pats, **opts):
1974 def rawcommit(ui, repo, *pats, **opts):
1968 """raw commit interface (DEPRECATED)
1975 """raw commit interface (DEPRECATED)
1969
1976
1970 (DEPRECATED)
1977 (DEPRECATED)
1971 Lowlevel commit, for use in helper scripts.
1978 Lowlevel commit, for use in helper scripts.
1972
1979
1973 This command is not intended to be used by normal users, as it is
1980 This command is not intended to be used by normal users, as it is
1974 primarily useful for importing from other SCMs.
1981 primarily useful for importing from other SCMs.
1975
1982
1976 This command is now deprecated and will be removed in a future
1983 This command is now deprecated and will be removed in a future
1977 release, please use debugsetparents and commit instead.
1984 release, please use debugsetparents and commit instead.
1978 """
1985 """
1979
1986
1980 ui.warn(_("(the rawcommit command is deprecated)\n"))
1987 ui.warn(_("(the rawcommit command is deprecated)\n"))
1981
1988
1982 message = logmessage(opts)
1989 message = logmessage(opts)
1983
1990
1984 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1991 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
1985 if opts['files']:
1992 if opts['files']:
1986 files += open(opts['files']).read().splitlines()
1993 files += open(opts['files']).read().splitlines()
1987
1994
1988 parents = [repo.lookup(p) for p in opts['parent']]
1995 parents = [repo.lookup(p) for p in opts['parent']]
1989
1996
1990 try:
1997 try:
1991 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1998 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
1992 except ValueError, inst:
1999 except ValueError, inst:
1993 raise util.Abort(str(inst))
2000 raise util.Abort(str(inst))
1994
2001
1995 def recover(ui, repo):
2002 def recover(ui, repo):
1996 """roll back an interrupted transaction
2003 """roll back an interrupted transaction
1997
2004
1998 Recover from an interrupted commit or pull.
2005 Recover from an interrupted commit or pull.
1999
2006
2000 This command tries to fix the repository status after an interrupted
2007 This command tries to fix the repository status after an interrupted
2001 operation. It should only be necessary when Mercurial suggests it.
2008 operation. It should only be necessary when Mercurial suggests it.
2002 """
2009 """
2003 if repo.recover():
2010 if repo.recover():
2004 return hg.verify(repo)
2011 return hg.verify(repo)
2005 return 1
2012 return 1
2006
2013
2007 def remove(ui, repo, *pats, **opts):
2014 def remove(ui, repo, *pats, **opts):
2008 """remove the specified files on the next commit
2015 """remove the specified files on the next commit
2009
2016
2010 Schedule the indicated files for removal from the repository.
2017 Schedule the indicated files for removal from the repository.
2011
2018
2012 This only removes files from the current branch, not from the
2019 This only removes files from the current branch, not from the
2013 entire project history. If the files still exist in the working
2020 entire project history. If the files still exist in the working
2014 directory, they will be deleted from it. If invoked with --after,
2021 directory, they will be deleted from it. If invoked with --after,
2015 files that have been manually deleted are marked as removed.
2022 files that have been manually deleted are marked as removed.
2016
2023
2017 This command schedules the files to be removed at the next commit.
2024 This command schedules the files to be removed at the next commit.
2018 To undo a remove before that, see hg revert.
2025 To undo a remove before that, see hg revert.
2019
2026
2020 Modified files and added files are not removed by default. To
2027 Modified files and added files are not removed by default. To
2021 remove them, use the -f/--force option.
2028 remove them, use the -f/--force option.
2022 """
2029 """
2023 names = []
2030 names = []
2024 if not opts['after'] and not pats:
2031 if not opts['after'] and not pats:
2025 raise util.Abort(_('no files specified'))
2032 raise util.Abort(_('no files specified'))
2026 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2033 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2027 exact = dict.fromkeys(files)
2034 exact = dict.fromkeys(files)
2028 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2035 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2029 modified, added, removed, deleted, unknown = mardu
2036 modified, added, removed, deleted, unknown = mardu
2030 remove, forget = [], []
2037 remove, forget = [], []
2031 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2038 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2032 reason = None
2039 reason = None
2033 if abs not in deleted and opts['after']:
2040 if abs not in deleted and opts['after']:
2034 reason = _('is still present')
2041 reason = _('is still present')
2035 elif abs in modified and not opts['force']:
2042 elif abs in modified and not opts['force']:
2036 reason = _('is modified (use -f to force removal)')
2043 reason = _('is modified (use -f to force removal)')
2037 elif abs in added:
2044 elif abs in added:
2038 if opts['force']:
2045 if opts['force']:
2039 forget.append(abs)
2046 forget.append(abs)
2040 continue
2047 continue
2041 reason = _('has been marked for add (use -f to force removal)')
2048 reason = _('has been marked for add (use -f to force removal)')
2042 elif abs in unknown:
2049 elif abs in unknown:
2043 reason = _('is not managed')
2050 reason = _('is not managed')
2044 elif abs in removed:
2051 elif abs in removed:
2045 continue
2052 continue
2046 if reason:
2053 if reason:
2047 if exact:
2054 if exact:
2048 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2055 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2049 else:
2056 else:
2050 if ui.verbose or not exact:
2057 if ui.verbose or not exact:
2051 ui.status(_('removing %s\n') % rel)
2058 ui.status(_('removing %s\n') % rel)
2052 remove.append(abs)
2059 remove.append(abs)
2053 repo.forget(forget)
2060 repo.forget(forget)
2054 repo.remove(remove, unlink=not opts['after'])
2061 repo.remove(remove, unlink=not opts['after'])
2055
2062
2056 def rename(ui, repo, *pats, **opts):
2063 def rename(ui, repo, *pats, **opts):
2057 """rename files; equivalent of copy + remove
2064 """rename files; equivalent of copy + remove
2058
2065
2059 Mark dest as copies of sources; mark sources for deletion. If
2066 Mark dest as copies of sources; mark sources for deletion. If
2060 dest is a directory, copies are put in that directory. If dest is
2067 dest is a directory, copies are put in that directory. If dest is
2061 a file, there can only be one source.
2068 a file, there can only be one source.
2062
2069
2063 By default, this command copies the contents of files as they
2070 By default, this command copies the contents of files as they
2064 stand in the working directory. If invoked with --after, the
2071 stand in the working directory. If invoked with --after, the
2065 operation is recorded, but no copying is performed.
2072 operation is recorded, but no copying is performed.
2066
2073
2067 This command takes effect in the next commit. To undo a rename
2074 This command takes effect in the next commit. To undo a rename
2068 before that, see hg revert.
2075 before that, see hg revert.
2069 """
2076 """
2070 wlock = repo.wlock(0)
2077 wlock = repo.wlock(0)
2071 errs, copied = docopy(ui, repo, pats, opts, wlock)
2078 errs, copied = docopy(ui, repo, pats, opts, wlock)
2072 names = []
2079 names = []
2073 for abs, rel, exact in copied:
2080 for abs, rel, exact in copied:
2074 if ui.verbose or not exact:
2081 if ui.verbose or not exact:
2075 ui.status(_('removing %s\n') % rel)
2082 ui.status(_('removing %s\n') % rel)
2076 names.append(abs)
2083 names.append(abs)
2077 if not opts.get('dry_run'):
2084 if not opts.get('dry_run'):
2078 repo.remove(names, True, wlock)
2085 repo.remove(names, True, wlock)
2079 return errs
2086 return errs
2080
2087
2081 def revert(ui, repo, *pats, **opts):
2088 def revert(ui, repo, *pats, **opts):
2082 """revert files or dirs to their states as of some revision
2089 """revert files or dirs to their states as of some revision
2083
2090
2084 With no revision specified, revert the named files or directories
2091 With no revision specified, revert the named files or directories
2085 to the contents they had in the parent of the working directory.
2092 to the contents they had in the parent of the working directory.
2086 This restores the contents of the affected files to an unmodified
2093 This restores the contents of the affected files to an unmodified
2087 state and unschedules adds, removes, copies, and renames. If the
2094 state and unschedules adds, removes, copies, and renames. If the
2088 working directory has two parents, you must explicitly specify the
2095 working directory has two parents, you must explicitly specify the
2089 revision to revert to.
2096 revision to revert to.
2090
2097
2091 Modified files are saved with a .orig suffix before reverting.
2098 Modified files are saved with a .orig suffix before reverting.
2092 To disable these backups, use --no-backup.
2099 To disable these backups, use --no-backup.
2093
2100
2094 Using the -r option, revert the given files or directories to their
2101 Using the -r option, revert the given files or directories to their
2095 contents as of a specific revision. This can be helpful to "roll
2102 contents as of a specific revision. This can be helpful to "roll
2096 back" some or all of a change that should not have been committed.
2103 back" some or all of a change that should not have been committed.
2097
2104
2098 Revert modifies the working directory. It does not commit any
2105 Revert modifies the working directory. It does not commit any
2099 changes, or change the parent of the working directory. If you
2106 changes, or change the parent of the working directory. If you
2100 revert to a revision other than the parent of the working
2107 revert to a revision other than the parent of the working
2101 directory, the reverted files will thus appear modified
2108 directory, the reverted files will thus appear modified
2102 afterwards.
2109 afterwards.
2103
2110
2104 If a file has been deleted, it is recreated. If the executable
2111 If a file has been deleted, it is recreated. If the executable
2105 mode of a file was changed, it is reset.
2112 mode of a file was changed, it is reset.
2106
2113
2107 If names are given, all files matching the names are reverted.
2114 If names are given, all files matching the names are reverted.
2108
2115
2109 If no arguments are given, no files are reverted.
2116 If no arguments are given, no files are reverted.
2110 """
2117 """
2111
2118
2112 if opts["date"]:
2119 if opts["date"]:
2113 if opts["rev"]:
2120 if opts["rev"]:
2114 raise util.Abort(_("you can't specify a revision and a date"))
2121 raise util.Abort(_("you can't specify a revision and a date"))
2115 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2122 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2116
2123
2117 if not pats and not opts['all']:
2124 if not pats and not opts['all']:
2118 raise util.Abort(_('no files or directories specified; '
2125 raise util.Abort(_('no files or directories specified; '
2119 'use --all to revert the whole repo'))
2126 'use --all to revert the whole repo'))
2120
2127
2121 parent, p2 = repo.dirstate.parents()
2128 parent, p2 = repo.dirstate.parents()
2122 if not opts['rev'] and p2 != nullid:
2129 if not opts['rev'] and p2 != nullid:
2123 raise util.Abort(_('uncommitted merge - please provide a '
2130 raise util.Abort(_('uncommitted merge - please provide a '
2124 'specific revision'))
2131 'specific revision'))
2125 ctx = repo.changectx(opts['rev'])
2132 ctx = repo.changectx(opts['rev'])
2126 node = ctx.node()
2133 node = ctx.node()
2127 mf = ctx.manifest()
2134 mf = ctx.manifest()
2128 if node == parent:
2135 if node == parent:
2129 pmf = mf
2136 pmf = mf
2130 else:
2137 else:
2131 pmf = None
2138 pmf = None
2132
2139
2133 wlock = repo.wlock()
2140 wlock = repo.wlock()
2134
2141
2135 # need all matching names in dirstate and manifest of target rev,
2142 # need all matching names in dirstate and manifest of target rev,
2136 # so have to walk both. do not print errors if files exist in one
2143 # so have to walk both. do not print errors if files exist in one
2137 # but not other.
2144 # but not other.
2138
2145
2139 names = {}
2146 names = {}
2140 target_only = {}
2147 target_only = {}
2141
2148
2142 # walk dirstate.
2149 # walk dirstate.
2143
2150
2144 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2151 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2145 badmatch=mf.has_key):
2152 badmatch=mf.has_key):
2146 names[abs] = (rel, exact)
2153 names[abs] = (rel, exact)
2147 if src == 'b':
2154 if src == 'b':
2148 target_only[abs] = True
2155 target_only[abs] = True
2149
2156
2150 # walk target manifest.
2157 # walk target manifest.
2151
2158
2152 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2159 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2153 badmatch=names.has_key):
2160 badmatch=names.has_key):
2154 if abs in names: continue
2161 if abs in names: continue
2155 names[abs] = (rel, exact)
2162 names[abs] = (rel, exact)
2156 target_only[abs] = True
2163 target_only[abs] = True
2157
2164
2158 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2165 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2159 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2166 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2160
2167
2161 revert = ([], _('reverting %s\n'))
2168 revert = ([], _('reverting %s\n'))
2162 add = ([], _('adding %s\n'))
2169 add = ([], _('adding %s\n'))
2163 remove = ([], _('removing %s\n'))
2170 remove = ([], _('removing %s\n'))
2164 forget = ([], _('forgetting %s\n'))
2171 forget = ([], _('forgetting %s\n'))
2165 undelete = ([], _('undeleting %s\n'))
2172 undelete = ([], _('undeleting %s\n'))
2166 update = {}
2173 update = {}
2167
2174
2168 disptable = (
2175 disptable = (
2169 # dispatch table:
2176 # dispatch table:
2170 # file state
2177 # file state
2171 # action if in target manifest
2178 # action if in target manifest
2172 # action if not in target manifest
2179 # action if not in target manifest
2173 # make backup if in target manifest
2180 # make backup if in target manifest
2174 # make backup if not in target manifest
2181 # make backup if not in target manifest
2175 (modified, revert, remove, True, True),
2182 (modified, revert, remove, True, True),
2176 (added, revert, forget, True, False),
2183 (added, revert, forget, True, False),
2177 (removed, undelete, None, False, False),
2184 (removed, undelete, None, False, False),
2178 (deleted, revert, remove, False, False),
2185 (deleted, revert, remove, False, False),
2179 (unknown, add, None, True, False),
2186 (unknown, add, None, True, False),
2180 (target_only, add, None, False, False),
2187 (target_only, add, None, False, False),
2181 )
2188 )
2182
2189
2183 entries = names.items()
2190 entries = names.items()
2184 entries.sort()
2191 entries.sort()
2185
2192
2186 for abs, (rel, exact) in entries:
2193 for abs, (rel, exact) in entries:
2187 mfentry = mf.get(abs)
2194 mfentry = mf.get(abs)
2188 def handle(xlist, dobackup):
2195 def handle(xlist, dobackup):
2189 xlist[0].append(abs)
2196 xlist[0].append(abs)
2190 update[abs] = 1
2197 update[abs] = 1
2191 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2198 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2192 bakname = "%s.orig" % rel
2199 bakname = "%s.orig" % rel
2193 ui.note(_('saving current version of %s as %s\n') %
2200 ui.note(_('saving current version of %s as %s\n') %
2194 (rel, bakname))
2201 (rel, bakname))
2195 if not opts.get('dry_run'):
2202 if not opts.get('dry_run'):
2196 util.copyfile(rel, bakname)
2203 util.copyfile(rel, bakname)
2197 if ui.verbose or not exact:
2204 if ui.verbose or not exact:
2198 ui.status(xlist[1] % rel)
2205 ui.status(xlist[1] % rel)
2199 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2206 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2200 if abs not in table: continue
2207 if abs not in table: continue
2201 # file has changed in dirstate
2208 # file has changed in dirstate
2202 if mfentry:
2209 if mfentry:
2203 handle(hitlist, backuphit)
2210 handle(hitlist, backuphit)
2204 elif misslist is not None:
2211 elif misslist is not None:
2205 handle(misslist, backupmiss)
2212 handle(misslist, backupmiss)
2206 else:
2213 else:
2207 if exact: ui.warn(_('file not managed: %s\n') % rel)
2214 if exact: ui.warn(_('file not managed: %s\n') % rel)
2208 break
2215 break
2209 else:
2216 else:
2210 # file has not changed in dirstate
2217 # file has not changed in dirstate
2211 if node == parent:
2218 if node == parent:
2212 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2219 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2213 continue
2220 continue
2214 if pmf is None:
2221 if pmf is None:
2215 # only need parent manifest in this unlikely case,
2222 # only need parent manifest in this unlikely case,
2216 # so do not read by default
2223 # so do not read by default
2217 pmf = repo.changectx(parent).manifest()
2224 pmf = repo.changectx(parent).manifest()
2218 if abs in pmf:
2225 if abs in pmf:
2219 if mfentry:
2226 if mfentry:
2220 # if version of file is same in parent and target
2227 # if version of file is same in parent and target
2221 # manifests, do nothing
2228 # manifests, do nothing
2222 if pmf[abs] != mfentry:
2229 if pmf[abs] != mfentry:
2223 handle(revert, False)
2230 handle(revert, False)
2224 else:
2231 else:
2225 handle(remove, False)
2232 handle(remove, False)
2226
2233
2227 if not opts.get('dry_run'):
2234 if not opts.get('dry_run'):
2228 repo.dirstate.forget(forget[0])
2235 repo.dirstate.forget(forget[0])
2229 r = hg.revert(repo, node, update.has_key, wlock)
2236 r = hg.revert(repo, node, update.has_key, wlock)
2230 repo.dirstate.update(add[0], 'a')
2237 repo.dirstate.update(add[0], 'a')
2231 repo.dirstate.update(undelete[0], 'n')
2238 repo.dirstate.update(undelete[0], 'n')
2232 repo.dirstate.update(remove[0], 'r')
2239 repo.dirstate.update(remove[0], 'r')
2233 return r
2240 return r
2234
2241
2235 def rollback(ui, repo):
2242 def rollback(ui, repo):
2236 """roll back the last transaction in this repository
2243 """roll back the last transaction in this repository
2237
2244
2238 Roll back the last transaction in this repository, restoring the
2245 Roll back the last transaction in this repository, restoring the
2239 project to its state prior to the transaction.
2246 project to its state prior to the transaction.
2240
2247
2241 Transactions are used to encapsulate the effects of all commands
2248 Transactions are used to encapsulate the effects of all commands
2242 that create new changesets or propagate existing changesets into a
2249 that create new changesets or propagate existing changesets into a
2243 repository. For example, the following commands are transactional,
2250 repository. For example, the following commands are transactional,
2244 and their effects can be rolled back:
2251 and their effects can be rolled back:
2245
2252
2246 commit
2253 commit
2247 import
2254 import
2248 pull
2255 pull
2249 push (with this repository as destination)
2256 push (with this repository as destination)
2250 unbundle
2257 unbundle
2251
2258
2252 This command should be used with care. There is only one level of
2259 This command should be used with care. There is only one level of
2253 rollback, and there is no way to undo a rollback.
2260 rollback, and there is no way to undo a rollback.
2254
2261
2255 This command is not intended for use on public repositories. Once
2262 This command is not intended for use on public repositories. Once
2256 changes are visible for pull by other users, rolling a transaction
2263 changes are visible for pull by other users, rolling a transaction
2257 back locally is ineffective (someone else may already have pulled
2264 back locally is ineffective (someone else may already have pulled
2258 the changes). Furthermore, a race is possible with readers of the
2265 the changes). Furthermore, a race is possible with readers of the
2259 repository; for example an in-progress pull from the repository
2266 repository; for example an in-progress pull from the repository
2260 may fail if a rollback is performed.
2267 may fail if a rollback is performed.
2261 """
2268 """
2262 repo.rollback()
2269 repo.rollback()
2263
2270
2264 def root(ui, repo):
2271 def root(ui, repo):
2265 """print the root (top) of the current working dir
2272 """print the root (top) of the current working dir
2266
2273
2267 Print the root directory of the current repository.
2274 Print the root directory of the current repository.
2268 """
2275 """
2269 ui.write(repo.root + "\n")
2276 ui.write(repo.root + "\n")
2270
2277
2271 def serve(ui, repo, **opts):
2278 def serve(ui, repo, **opts):
2272 """export the repository via HTTP
2279 """export the repository via HTTP
2273
2280
2274 Start a local HTTP repository browser and pull server.
2281 Start a local HTTP repository browser and pull server.
2275
2282
2276 By default, the server logs accesses to stdout and errors to
2283 By default, the server logs accesses to stdout and errors to
2277 stderr. Use the "-A" and "-E" options to log to files.
2284 stderr. Use the "-A" and "-E" options to log to files.
2278 """
2285 """
2279
2286
2280 if opts["stdio"]:
2287 if opts["stdio"]:
2281 if repo is None:
2288 if repo is None:
2282 raise hg.RepoError(_("There is no Mercurial repository here"
2289 raise hg.RepoError(_("There is no Mercurial repository here"
2283 " (.hg not found)"))
2290 " (.hg not found)"))
2284 s = sshserver.sshserver(ui, repo)
2291 s = sshserver.sshserver(ui, repo)
2285 s.serve_forever()
2292 s.serve_forever()
2286
2293
2287 optlist = ("name templates style address port ipv6"
2294 optlist = ("name templates style address port ipv6"
2288 " accesslog errorlog webdir_conf")
2295 " accesslog errorlog webdir_conf")
2289 for o in optlist.split():
2296 for o in optlist.split():
2290 if opts[o]:
2297 if opts[o]:
2291 ui.setconfig("web", o, str(opts[o]))
2298 ui.setconfig("web", o, str(opts[o]))
2292
2299
2293 if repo is None and not ui.config("web", "webdir_conf"):
2300 if repo is None and not ui.config("web", "webdir_conf"):
2294 raise hg.RepoError(_("There is no Mercurial repository here"
2301 raise hg.RepoError(_("There is no Mercurial repository here"
2295 " (.hg not found)"))
2302 " (.hg not found)"))
2296
2303
2297 if opts['daemon'] and not opts['daemon_pipefds']:
2304 if opts['daemon'] and not opts['daemon_pipefds']:
2298 rfd, wfd = os.pipe()
2305 rfd, wfd = os.pipe()
2299 args = sys.argv[:]
2306 args = sys.argv[:]
2300 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2307 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2301 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2308 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2302 args[0], args)
2309 args[0], args)
2303 os.close(wfd)
2310 os.close(wfd)
2304 os.read(rfd, 1)
2311 os.read(rfd, 1)
2305 os._exit(0)
2312 os._exit(0)
2306
2313
2307 httpd = hgweb.server.create_server(ui, repo)
2314 httpd = hgweb.server.create_server(ui, repo)
2308
2315
2309 if ui.verbose:
2316 if ui.verbose:
2310 if httpd.port != 80:
2317 if httpd.port != 80:
2311 ui.status(_('listening at http://%s:%d/\n') %
2318 ui.status(_('listening at http://%s:%d/\n') %
2312 (httpd.addr, httpd.port))
2319 (httpd.addr, httpd.port))
2313 else:
2320 else:
2314 ui.status(_('listening at http://%s/\n') % httpd.addr)
2321 ui.status(_('listening at http://%s/\n') % httpd.addr)
2315
2322
2316 if opts['pid_file']:
2323 if opts['pid_file']:
2317 fp = open(opts['pid_file'], 'w')
2324 fp = open(opts['pid_file'], 'w')
2318 fp.write(str(os.getpid()) + '\n')
2325 fp.write(str(os.getpid()) + '\n')
2319 fp.close()
2326 fp.close()
2320
2327
2321 if opts['daemon_pipefds']:
2328 if opts['daemon_pipefds']:
2322 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2329 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2323 os.close(rfd)
2330 os.close(rfd)
2324 os.write(wfd, 'y')
2331 os.write(wfd, 'y')
2325 os.close(wfd)
2332 os.close(wfd)
2326 sys.stdout.flush()
2333 sys.stdout.flush()
2327 sys.stderr.flush()
2334 sys.stderr.flush()
2328 fd = os.open(util.nulldev, os.O_RDWR)
2335 fd = os.open(util.nulldev, os.O_RDWR)
2329 if fd != 0: os.dup2(fd, 0)
2336 if fd != 0: os.dup2(fd, 0)
2330 if fd != 1: os.dup2(fd, 1)
2337 if fd != 1: os.dup2(fd, 1)
2331 if fd != 2: os.dup2(fd, 2)
2338 if fd != 2: os.dup2(fd, 2)
2332 if fd not in (0, 1, 2): os.close(fd)
2339 if fd not in (0, 1, 2): os.close(fd)
2333
2340
2334 httpd.serve_forever()
2341 httpd.serve_forever()
2335
2342
2336 def status(ui, repo, *pats, **opts):
2343 def status(ui, repo, *pats, **opts):
2337 """show changed files in the working directory
2344 """show changed files in the working directory
2338
2345
2339 Show status of files in the repository. If names are given, only
2346 Show status of files in the repository. If names are given, only
2340 files that match are shown. Files that are clean or ignored, are
2347 files that match are shown. Files that are clean or ignored, are
2341 not listed unless -c (clean), -i (ignored) or -A is given.
2348 not listed unless -c (clean), -i (ignored) or -A is given.
2342
2349
2343 NOTE: status may appear to disagree with diff if permissions have
2350 NOTE: status may appear to disagree with diff if permissions have
2344 changed or a merge has occurred. The standard diff format does not
2351 changed or a merge has occurred. The standard diff format does not
2345 report permission changes and diff only reports changes relative
2352 report permission changes and diff only reports changes relative
2346 to one merge parent.
2353 to one merge parent.
2347
2354
2348 If one revision is given, it is used as the base revision.
2355 If one revision is given, it is used as the base revision.
2349 If two revisions are given, the difference between them is shown.
2356 If two revisions are given, the difference between them is shown.
2350
2357
2351 The codes used to show the status of files are:
2358 The codes used to show the status of files are:
2352 M = modified
2359 M = modified
2353 A = added
2360 A = added
2354 R = removed
2361 R = removed
2355 C = clean
2362 C = clean
2356 ! = deleted, but still tracked
2363 ! = deleted, but still tracked
2357 ? = not tracked
2364 ? = not tracked
2358 I = ignored (not shown by default)
2365 I = ignored (not shown by default)
2359 = the previous added file was copied from here
2366 = the previous added file was copied from here
2360 """
2367 """
2361
2368
2362 all = opts['all']
2369 all = opts['all']
2363 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2370 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2364
2371
2365 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2372 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2366 cwd = (pats and repo.getcwd()) or ''
2373 cwd = (pats and repo.getcwd()) or ''
2367 modified, added, removed, deleted, unknown, ignored, clean = [
2374 modified, added, removed, deleted, unknown, ignored, clean = [
2368 [util.pathto(cwd, x) for x in n]
2375 [util.pathto(cwd, x) for x in n]
2369 for n in repo.status(node1=node1, node2=node2, files=files,
2376 for n in repo.status(node1=node1, node2=node2, files=files,
2370 match=matchfn,
2377 match=matchfn,
2371 list_ignored=all or opts['ignored'],
2378 list_ignored=all or opts['ignored'],
2372 list_clean=all or opts['clean'])]
2379 list_clean=all or opts['clean'])]
2373
2380
2374 changetypes = (('modified', 'M', modified),
2381 changetypes = (('modified', 'M', modified),
2375 ('added', 'A', added),
2382 ('added', 'A', added),
2376 ('removed', 'R', removed),
2383 ('removed', 'R', removed),
2377 ('deleted', '!', deleted),
2384 ('deleted', '!', deleted),
2378 ('unknown', '?', unknown),
2385 ('unknown', '?', unknown),
2379 ('ignored', 'I', ignored))
2386 ('ignored', 'I', ignored))
2380
2387
2381 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2388 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2382
2389
2383 end = opts['print0'] and '\0' or '\n'
2390 end = opts['print0'] and '\0' or '\n'
2384
2391
2385 for opt, char, changes in ([ct for ct in explicit_changetypes
2392 for opt, char, changes in ([ct for ct in explicit_changetypes
2386 if all or opts[ct[0]]]
2393 if all or opts[ct[0]]]
2387 or changetypes):
2394 or changetypes):
2388 if opts['no_status']:
2395 if opts['no_status']:
2389 format = "%%s%s" % end
2396 format = "%%s%s" % end
2390 else:
2397 else:
2391 format = "%s %%s%s" % (char, end)
2398 format = "%s %%s%s" % (char, end)
2392
2399
2393 for f in changes:
2400 for f in changes:
2394 ui.write(format % f)
2401 ui.write(format % f)
2395 if ((all or opts.get('copies')) and not opts.get('no_status')):
2402 if ((all or opts.get('copies')) and not opts.get('no_status')):
2396 copied = repo.dirstate.copied(f)
2403 copied = repo.dirstate.copied(f)
2397 if copied:
2404 if copied:
2398 ui.write(' %s%s' % (copied, end))
2405 ui.write(' %s%s' % (copied, end))
2399
2406
2400 def tag(ui, repo, name, rev_=None, **opts):
2407 def tag(ui, repo, name, rev_=None, **opts):
2401 """add a tag for the current or given revision
2408 """add a tag for the current or given revision
2402
2409
2403 Name a particular revision using <name>.
2410 Name a particular revision using <name>.
2404
2411
2405 Tags are used to name particular revisions of the repository and are
2412 Tags are used to name particular revisions of the repository and are
2406 very useful to compare different revision, to go back to significant
2413 very useful to compare different revision, to go back to significant
2407 earlier versions or to mark branch points as releases, etc.
2414 earlier versions or to mark branch points as releases, etc.
2408
2415
2409 If no revision is given, the parent of the working directory is used,
2416 If no revision is given, the parent of the working directory is used,
2410 or tip if no revision is checked out.
2417 or tip if no revision is checked out.
2411
2418
2412 To facilitate version control, distribution, and merging of tags,
2419 To facilitate version control, distribution, and merging of tags,
2413 they are stored as a file named ".hgtags" which is managed
2420 they are stored as a file named ".hgtags" which is managed
2414 similarly to other project files and can be hand-edited if
2421 similarly to other project files and can be hand-edited if
2415 necessary. The file '.hg/localtags' is used for local tags (not
2422 necessary. The file '.hg/localtags' is used for local tags (not
2416 shared among repositories).
2423 shared among repositories).
2417 """
2424 """
2418 if name in ['tip', '.', 'null']:
2425 if name in ['tip', '.', 'null']:
2419 raise util.Abort(_("the name '%s' is reserved") % name)
2426 raise util.Abort(_("the name '%s' is reserved") % name)
2420 if rev_ is not None:
2427 if rev_ is not None:
2421 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2428 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2422 "please use 'hg tag [-r REV] NAME' instead\n"))
2429 "please use 'hg tag [-r REV] NAME' instead\n"))
2423 if opts['rev']:
2430 if opts['rev']:
2424 raise util.Abort(_("use only one form to specify the revision"))
2431 raise util.Abort(_("use only one form to specify the revision"))
2425 if opts['rev']:
2432 if opts['rev']:
2426 rev_ = opts['rev']
2433 rev_ = opts['rev']
2427 if not rev_ and repo.dirstate.parents()[1] != nullid:
2434 if not rev_ and repo.dirstate.parents()[1] != nullid:
2428 raise util.Abort(_('uncommitted merge - please provide a '
2435 raise util.Abort(_('uncommitted merge - please provide a '
2429 'specific revision'))
2436 'specific revision'))
2430 r = repo.changectx(rev_).node()
2437 r = repo.changectx(rev_).node()
2431
2438
2432 message = opts['message']
2439 message = opts['message']
2433 if not message:
2440 if not message:
2434 message = _('Added tag %s for changeset %s') % (name, short(r))
2441 message = _('Added tag %s for changeset %s') % (name, short(r))
2435
2442
2436 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2443 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2437
2444
2438 def tags(ui, repo):
2445 def tags(ui, repo):
2439 """list repository tags
2446 """list repository tags
2440
2447
2441 List the repository tags.
2448 List the repository tags.
2442
2449
2443 This lists both regular and local tags.
2450 This lists both regular and local tags.
2444 """
2451 """
2445
2452
2446 l = repo.tagslist()
2453 l = repo.tagslist()
2447 l.reverse()
2454 l.reverse()
2448 hexfunc = ui.debugflag and hex or short
2455 hexfunc = ui.debugflag and hex or short
2449 for t, n in l:
2456 for t, n in l:
2450 try:
2457 try:
2451 hn = hexfunc(n)
2458 hn = hexfunc(n)
2452 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2459 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2453 except revlog.LookupError:
2460 except revlog.LookupError:
2454 r = " ?:%s" % hn
2461 r = " ?:%s" % hn
2455 if ui.quiet:
2462 if ui.quiet:
2456 ui.write("%s\n" % t)
2463 ui.write("%s\n" % t)
2457 else:
2464 else:
2458 t = util.localsub(t, 30)
2465 t = util.localsub(t, 30)
2459 t += " " * (30 - util.locallen(t))
2466 t += " " * (30 - util.locallen(t))
2460 ui.write("%s %s\n" % (t, r))
2467 ui.write("%s %s\n" % (t, r))
2461
2468
2462 def tip(ui, repo, **opts):
2469 def tip(ui, repo, **opts):
2463 """show the tip revision
2470 """show the tip revision
2464
2471
2465 Show the tip revision.
2472 Show the tip revision.
2466 """
2473 """
2467 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2474 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2468
2475
2469 def unbundle(ui, repo, fname, **opts):
2476 def unbundle(ui, repo, fname, **opts):
2470 """apply a changegroup file
2477 """apply a changegroup file
2471
2478
2472 Apply a compressed changegroup file generated by the bundle
2479 Apply a compressed changegroup file generated by the bundle
2473 command.
2480 command.
2474 """
2481 """
2475 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2482 gen = changegroup.readbundle(urllib.urlopen(fname), fname)
2476 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2483 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2477 return postincoming(ui, repo, modheads, opts['update'])
2484 return postincoming(ui, repo, modheads, opts['update'])
2478
2485
2479 def update(ui, repo, node=None, clean=False, date=None):
2486 def update(ui, repo, node=None, clean=False, date=None):
2480 """update or merge working directory
2487 """update working directory
2481
2488
2482 Update the working directory to the specified revision.
2489 Update the working directory to the specified revision.
2483
2490
2484 If there are no outstanding changes in the working directory and
2491 If there are no outstanding changes in the working directory and
2485 there is a linear relationship between the current version and the
2492 there is a linear relationship between the current version and the
2486 requested version, the result is the requested version.
2493 requested version, the result is the requested version.
2487
2494
2488 To merge the working directory with another revision, use the
2495 To merge the working directory with another revision, use the
2489 merge command.
2496 merge command.
2490
2497
2491 By default, update will refuse to run if doing so would require
2498 By default, update will refuse to run if doing so would require
2492 merging or discarding local changes.
2499 discarding local changes.
2493 """
2500 """
2494 if date:
2501 if date:
2495 if node:
2502 if node:
2496 raise util.Abort(_("you can't specify a revision and a date"))
2503 raise util.Abort(_("you can't specify a revision and a date"))
2497 node = cmdutil.finddate(ui, repo, date)
2504 node = cmdutil.finddate(ui, repo, date)
2498
2505
2499 if clean:
2506 if clean:
2500 return hg.clean(repo, node)
2507 return hg.clean(repo, node)
2501 else:
2508 else:
2502 return hg.update(repo, node)
2509 return hg.update(repo, node)
2503
2510
2504 def verify(ui, repo):
2511 def verify(ui, repo):
2505 """verify the integrity of the repository
2512 """verify the integrity of the repository
2506
2513
2507 Verify the integrity of the current repository.
2514 Verify the integrity of the current repository.
2508
2515
2509 This will perform an extensive check of the repository's
2516 This will perform an extensive check of the repository's
2510 integrity, validating the hashes and checksums of each entry in
2517 integrity, validating the hashes and checksums of each entry in
2511 the changelog, manifest, and tracked files, as well as the
2518 the changelog, manifest, and tracked files, as well as the
2512 integrity of their crosslinks and indices.
2519 integrity of their crosslinks and indices.
2513 """
2520 """
2514 return hg.verify(repo)
2521 return hg.verify(repo)
2515
2522
2516 def version_(ui):
2523 def version_(ui):
2517 """output version and copyright information"""
2524 """output version and copyright information"""
2518 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2525 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2519 % version.get_version())
2526 % version.get_version())
2520 ui.status(_(
2527 ui.status(_(
2521 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2528 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2522 "This is free software; see the source for copying conditions. "
2529 "This is free software; see the source for copying conditions. "
2523 "There is NO\nwarranty; "
2530 "There is NO\nwarranty; "
2524 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2531 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2525 ))
2532 ))
2526
2533
2527 # Command options and aliases are listed here, alphabetically
2534 # Command options and aliases are listed here, alphabetically
2528
2535
2529 globalopts = [
2536 globalopts = [
2530 ('R', 'repository', '',
2537 ('R', 'repository', '',
2531 _('repository root directory or symbolic path name')),
2538 _('repository root directory or symbolic path name')),
2532 ('', 'cwd', '', _('change working directory')),
2539 ('', 'cwd', '', _('change working directory')),
2533 ('y', 'noninteractive', None,
2540 ('y', 'noninteractive', None,
2534 _('do not prompt, assume \'yes\' for any required answers')),
2541 _('do not prompt, assume \'yes\' for any required answers')),
2535 ('q', 'quiet', None, _('suppress output')),
2542 ('q', 'quiet', None, _('suppress output')),
2536 ('v', 'verbose', None, _('enable additional output')),
2543 ('v', 'verbose', None, _('enable additional output')),
2537 ('', 'config', [], _('set/override config option')),
2544 ('', 'config', [], _('set/override config option')),
2538 ('', 'debug', None, _('enable debugging output')),
2545 ('', 'debug', None, _('enable debugging output')),
2539 ('', 'debugger', None, _('start debugger')),
2546 ('', 'debugger', None, _('start debugger')),
2540 ('', 'encoding', util._encoding, _('set the charset encoding')),
2547 ('', 'encoding', util._encoding, _('set the charset encoding')),
2541 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2548 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2542 ('', 'lsprof', None, _('print improved command execution profile')),
2549 ('', 'lsprof', None, _('print improved command execution profile')),
2543 ('', 'traceback', None, _('print traceback on exception')),
2550 ('', 'traceback', None, _('print traceback on exception')),
2544 ('', 'time', None, _('time how long the command takes')),
2551 ('', 'time', None, _('time how long the command takes')),
2545 ('', 'profile', None, _('print command execution profile')),
2552 ('', 'profile', None, _('print command execution profile')),
2546 ('', 'version', None, _('output version information and exit')),
2553 ('', 'version', None, _('output version information and exit')),
2547 ('h', 'help', None, _('display help and exit')),
2554 ('h', 'help', None, _('display help and exit')),
2548 ]
2555 ]
2549
2556
2550 dryrunopts = [('n', 'dry-run', None,
2557 dryrunopts = [('n', 'dry-run', None,
2551 _('do not perform actions, just print output'))]
2558 _('do not perform actions, just print output'))]
2552
2559
2553 remoteopts = [
2560 remoteopts = [
2554 ('e', 'ssh', '', _('specify ssh command to use')),
2561 ('e', 'ssh', '', _('specify ssh command to use')),
2555 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2562 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2556 ]
2563 ]
2557
2564
2558 walkopts = [
2565 walkopts = [
2559 ('I', 'include', [], _('include names matching the given patterns')),
2566 ('I', 'include', [], _('include names matching the given patterns')),
2560 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2567 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2561 ]
2568 ]
2562
2569
2563 commitopts = [
2570 commitopts = [
2564 ('m', 'message', '', _('use <text> as commit message')),
2571 ('m', 'message', '', _('use <text> as commit message')),
2565 ('l', 'logfile', '', _('read commit message from <file>')),
2572 ('l', 'logfile', '', _('read commit message from <file>')),
2566 ]
2573 ]
2567
2574
2568 table = {
2575 table = {
2569 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2576 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2570 "addremove":
2577 "addremove":
2571 (addremove,
2578 (addremove,
2572 [('s', 'similarity', '',
2579 [('s', 'similarity', '',
2573 _('guess renamed files by similarity (0<=s<=100)')),
2580 _('guess renamed files by similarity (0<=s<=100)')),
2574 ] + walkopts + dryrunopts,
2581 ] + walkopts + dryrunopts,
2575 _('hg addremove [OPTION]... [FILE]...')),
2582 _('hg addremove [OPTION]... [FILE]...')),
2576 "^annotate":
2583 "^annotate":
2577 (annotate,
2584 (annotate,
2578 [('r', 'rev', '', _('annotate the specified revision')),
2585 [('r', 'rev', '', _('annotate the specified revision')),
2579 ('f', 'follow', None, _('follow file copies and renames')),
2586 ('f', 'follow', None, _('follow file copies and renames')),
2580 ('a', 'text', None, _('treat all files as text')),
2587 ('a', 'text', None, _('treat all files as text')),
2581 ('u', 'user', None, _('list the author')),
2588 ('u', 'user', None, _('list the author')),
2582 ('d', 'date', None, _('list the date')),
2589 ('d', 'date', None, _('list the date')),
2583 ('n', 'number', None, _('list the revision number (default)')),
2590 ('n', 'number', None, _('list the revision number (default)')),
2584 ('c', 'changeset', None, _('list the changeset')),
2591 ('c', 'changeset', None, _('list the changeset')),
2585 ] + walkopts,
2592 ] + walkopts,
2586 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2593 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2587 "archive":
2594 "archive":
2588 (archive,
2595 (archive,
2589 [('', 'no-decode', None, _('do not pass files through decoders')),
2596 [('', 'no-decode', None, _('do not pass files through decoders')),
2590 ('p', 'prefix', '', _('directory prefix for files in archive')),
2597 ('p', 'prefix', '', _('directory prefix for files in archive')),
2591 ('r', 'rev', '', _('revision to distribute')),
2598 ('r', 'rev', '', _('revision to distribute')),
2592 ('t', 'type', '', _('type of distribution to create')),
2599 ('t', 'type', '', _('type of distribution to create')),
2593 ] + walkopts,
2600 ] + walkopts,
2594 _('hg archive [OPTION]... DEST')),
2601 _('hg archive [OPTION]... DEST')),
2595 "backout":
2602 "backout":
2596 (backout,
2603 (backout,
2597 [('', 'merge', None,
2604 [('', 'merge', None,
2598 _('merge with old dirstate parent after backout')),
2605 _('merge with old dirstate parent after backout')),
2599 ('d', 'date', '', _('record datecode as commit date')),
2606 ('d', 'date', '', _('record datecode as commit date')),
2600 ('', 'parent', '', _('parent to choose when backing out merge')),
2607 ('', 'parent', '', _('parent to choose when backing out merge')),
2601 ('u', 'user', '', _('record user as committer')),
2608 ('u', 'user', '', _('record user as committer')),
2602 ] + walkopts + commitopts,
2609 ] + walkopts + commitopts,
2603 _('hg backout [OPTION]... REV')),
2610 _('hg backout [OPTION]... REV')),
2604 "branch": (branch, [], _('hg branch [NAME]')),
2611 "branch": (branch, [], _('hg branch [NAME]')),
2605 "branches": (branches, [], _('hg branches')),
2612 "branches": (branches, [], _('hg branches')),
2606 "bundle":
2613 "bundle":
2607 (bundle,
2614 (bundle,
2608 [('f', 'force', None,
2615 [('f', 'force', None,
2609 _('run even when remote repository is unrelated')),
2616 _('run even when remote repository is unrelated')),
2610 ('r', 'rev', [],
2617 ('r', 'rev', [],
2611 _('a changeset you would like to bundle')),
2618 _('a changeset you would like to bundle')),
2612 ('', 'base', [],
2619 ('', 'base', [],
2613 _('a base changeset to specify instead of a destination')),
2620 _('a base changeset to specify instead of a destination')),
2614 ] + remoteopts,
2621 ] + remoteopts,
2615 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2622 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2616 "cat":
2623 "cat":
2617 (cat,
2624 (cat,
2618 [('o', 'output', '', _('print output to file with formatted name')),
2625 [('o', 'output', '', _('print output to file with formatted name')),
2619 ('r', 'rev', '', _('print the given revision')),
2626 ('r', 'rev', '', _('print the given revision')),
2620 ] + walkopts,
2627 ] + walkopts,
2621 _('hg cat [OPTION]... FILE...')),
2628 _('hg cat [OPTION]... FILE...')),
2622 "^clone":
2629 "^clone":
2623 (clone,
2630 (clone,
2624 [('U', 'noupdate', None, _('do not update the new working directory')),
2631 [('U', 'noupdate', None, _('do not update the new working directory')),
2625 ('r', 'rev', [],
2632 ('r', 'rev', [],
2626 _('a changeset you would like to have after cloning')),
2633 _('a changeset you would like to have after cloning')),
2627 ('', 'pull', None, _('use pull protocol to copy metadata')),
2634 ('', 'pull', None, _('use pull protocol to copy metadata')),
2628 ('', 'uncompressed', None,
2635 ('', 'uncompressed', None,
2629 _('use uncompressed transfer (fast over LAN)')),
2636 _('use uncompressed transfer (fast over LAN)')),
2630 ] + remoteopts,
2637 ] + remoteopts,
2631 _('hg clone [OPTION]... SOURCE [DEST]')),
2638 _('hg clone [OPTION]... SOURCE [DEST]')),
2632 "^commit|ci":
2639 "^commit|ci":
2633 (commit,
2640 (commit,
2634 [('A', 'addremove', None,
2641 [('A', 'addremove', None,
2635 _('mark new/missing files as added/removed before committing')),
2642 _('mark new/missing files as added/removed before committing')),
2636 ('d', 'date', '', _('record datecode as commit date')),
2643 ('d', 'date', '', _('record datecode as commit date')),
2637 ('u', 'user', '', _('record user as commiter')),
2644 ('u', 'user', '', _('record user as commiter')),
2638 ] + walkopts + commitopts,
2645 ] + walkopts + commitopts,
2639 _('hg commit [OPTION]... [FILE]...')),
2646 _('hg commit [OPTION]... [FILE]...')),
2640 "copy|cp":
2647 "copy|cp":
2641 (copy,
2648 (copy,
2642 [('A', 'after', None, _('record a copy that has already occurred')),
2649 [('A', 'after', None, _('record a copy that has already occurred')),
2643 ('f', 'force', None,
2650 ('f', 'force', None,
2644 _('forcibly copy over an existing managed file')),
2651 _('forcibly copy over an existing managed file')),
2645 ] + walkopts + dryrunopts,
2652 ] + walkopts + dryrunopts,
2646 _('hg copy [OPTION]... [SOURCE]... DEST')),
2653 _('hg copy [OPTION]... [SOURCE]... DEST')),
2647 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2654 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2648 "debugcomplete":
2655 "debugcomplete":
2649 (debugcomplete,
2656 (debugcomplete,
2650 [('o', 'options', None, _('show the command options'))],
2657 [('o', 'options', None, _('show the command options'))],
2651 _('debugcomplete [-o] CMD')),
2658 _('debugcomplete [-o] CMD')),
2652 "debuginstall": (debuginstall, [], _('debuginstall')),
2659 "debuginstall": (debuginstall, [], _('debuginstall')),
2653 "debugrebuildstate":
2660 "debugrebuildstate":
2654 (debugrebuildstate,
2661 (debugrebuildstate,
2655 [('r', 'rev', '', _('revision to rebuild to'))],
2662 [('r', 'rev', '', _('revision to rebuild to'))],
2656 _('debugrebuildstate [-r REV] [REV]')),
2663 _('debugrebuildstate [-r REV] [REV]')),
2657 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2664 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2658 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2665 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2659 "debugstate": (debugstate, [], _('debugstate')),
2666 "debugstate": (debugstate, [], _('debugstate')),
2660 "debugdate":
2667 "debugdate":
2661 (debugdate,
2668 (debugdate,
2662 [('e', 'extended', None, _('try extended date formats'))],
2669 [('e', 'extended', None, _('try extended date formats'))],
2663 _('debugdate [-e] DATE [RANGE]')),
2670 _('debugdate [-e] DATE [RANGE]')),
2664 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2671 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2665 "debugindex": (debugindex, [], _('debugindex FILE')),
2672 "debugindex": (debugindex, [], _('debugindex FILE')),
2666 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2673 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2667 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2674 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2668 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2675 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2669 "^diff":
2676 "^diff":
2670 (diff,
2677 (diff,
2671 [('r', 'rev', [], _('revision')),
2678 [('r', 'rev', [], _('revision')),
2672 ('a', 'text', None, _('treat all files as text')),
2679 ('a', 'text', None, _('treat all files as text')),
2673 ('p', 'show-function', None,
2680 ('p', 'show-function', None,
2674 _('show which function each change is in')),
2681 _('show which function each change is in')),
2675 ('g', 'git', None, _('use git extended diff format')),
2682 ('g', 'git', None, _('use git extended diff format')),
2676 ('', 'nodates', None, _("don't include dates in diff headers")),
2683 ('', 'nodates', None, _("don't include dates in diff headers")),
2677 ('w', 'ignore-all-space', None,
2684 ('w', 'ignore-all-space', None,
2678 _('ignore white space when comparing lines')),
2685 _('ignore white space when comparing lines')),
2679 ('b', 'ignore-space-change', None,
2686 ('b', 'ignore-space-change', None,
2680 _('ignore changes in the amount of white space')),
2687 _('ignore changes in the amount of white space')),
2681 ('B', 'ignore-blank-lines', None,
2688 ('B', 'ignore-blank-lines', None,
2682 _('ignore changes whose lines are all blank')),
2689 _('ignore changes whose lines are all blank')),
2683 ] + walkopts,
2690 ] + walkopts,
2684 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2691 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2685 "^export":
2692 "^export":
2686 (export,
2693 (export,
2687 [('o', 'output', '', _('print output to file with formatted name')),
2694 [('o', 'output', '', _('print output to file with formatted name')),
2688 ('a', 'text', None, _('treat all files as text')),
2695 ('a', 'text', None, _('treat all files as text')),
2689 ('g', 'git', None, _('use git extended diff format')),
2696 ('g', 'git', None, _('use git extended diff format')),
2690 ('', 'nodates', None, _("don't include dates in diff headers")),
2697 ('', 'nodates', None, _("don't include dates in diff headers")),
2691 ('', 'switch-parent', None, _('diff against the second parent'))],
2698 ('', 'switch-parent', None, _('diff against the second parent'))],
2692 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2699 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2693 "grep":
2700 "grep":
2694 (grep,
2701 (grep,
2695 [('0', 'print0', None, _('end fields with NUL')),
2702 [('0', 'print0', None, _('end fields with NUL')),
2696 ('', 'all', None, _('print all revisions that match')),
2703 ('', 'all', None, _('print all revisions that match')),
2697 ('f', 'follow', None,
2704 ('f', 'follow', None,
2698 _('follow changeset history, or file history across copies and renames')),
2705 _('follow changeset history, or file history across copies and renames')),
2699 ('i', 'ignore-case', None, _('ignore case when matching')),
2706 ('i', 'ignore-case', None, _('ignore case when matching')),
2700 ('l', 'files-with-matches', None,
2707 ('l', 'files-with-matches', None,
2701 _('print only filenames and revs that match')),
2708 _('print only filenames and revs that match')),
2702 ('n', 'line-number', None, _('print matching line numbers')),
2709 ('n', 'line-number', None, _('print matching line numbers')),
2703 ('r', 'rev', [], _('search in given revision range')),
2710 ('r', 'rev', [], _('search in given revision range')),
2704 ('u', 'user', None, _('print user who committed change')),
2711 ('u', 'user', None, _('print user who committed change')),
2705 ] + walkopts,
2712 ] + walkopts,
2706 _('hg grep [OPTION]... PATTERN [FILE]...')),
2713 _('hg grep [OPTION]... PATTERN [FILE]...')),
2707 "heads":
2714 "heads":
2708 (heads,
2715 (heads,
2709 [('', 'style', '', _('display using template map file')),
2716 [('', 'style', '', _('display using template map file')),
2710 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2717 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2711 ('', 'template', '', _('display with template'))],
2718 ('', 'template', '', _('display with template'))],
2712 _('hg heads [-r REV]')),
2719 _('hg heads [-r REV]')),
2713 "help": (help_, [], _('hg help [COMMAND]')),
2720 "help": (help_, [], _('hg help [COMMAND]')),
2714 "identify|id": (identify, [], _('hg identify')),
2721 "identify|id": (identify, [], _('hg identify')),
2715 "import|patch":
2722 "import|patch":
2716 (import_,
2723 (import_,
2717 [('p', 'strip', 1,
2724 [('p', 'strip', 1,
2718 _('directory strip option for patch. This has the same\n'
2725 _('directory strip option for patch. This has the same\n'
2719 'meaning as the corresponding patch option')),
2726 'meaning as the corresponding patch option')),
2720 ('b', 'base', '', _('base path')),
2727 ('b', 'base', '', _('base path')),
2721 ('f', 'force', None,
2728 ('f', 'force', None,
2722 _('skip check for outstanding uncommitted changes'))] + commitopts,
2729 _('skip check for outstanding uncommitted changes'))] + commitopts,
2723 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2730 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2724 "incoming|in": (incoming,
2731 "incoming|in": (incoming,
2725 [('M', 'no-merges', None, _('do not show merges')),
2732 [('M', 'no-merges', None, _('do not show merges')),
2726 ('f', 'force', None,
2733 ('f', 'force', None,
2727 _('run even when remote repository is unrelated')),
2734 _('run even when remote repository is unrelated')),
2728 ('', 'style', '', _('display using template map file')),
2735 ('', 'style', '', _('display using template map file')),
2729 ('n', 'newest-first', None, _('show newest record first')),
2736 ('n', 'newest-first', None, _('show newest record first')),
2730 ('', 'bundle', '', _('file to store the bundles into')),
2737 ('', 'bundle', '', _('file to store the bundles into')),
2731 ('p', 'patch', None, _('show patch')),
2738 ('p', 'patch', None, _('show patch')),
2732 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2739 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2733 ('', 'template', '', _('display with template')),
2740 ('', 'template', '', _('display with template')),
2734 ] + remoteopts,
2741 ] + remoteopts,
2735 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2742 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2736 ' [--bundle FILENAME] [SOURCE]')),
2743 ' [--bundle FILENAME] [SOURCE]')),
2737 "^init":
2744 "^init":
2738 (init,
2745 (init,
2739 remoteopts,
2746 remoteopts,
2740 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2747 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2741 "locate":
2748 "locate":
2742 (locate,
2749 (locate,
2743 [('r', 'rev', '', _('search the repository as it stood at rev')),
2750 [('r', 'rev', '', _('search the repository as it stood at rev')),
2744 ('0', 'print0', None,
2751 ('0', 'print0', None,
2745 _('end filenames with NUL, for use with xargs')),
2752 _('end filenames with NUL, for use with xargs')),
2746 ('f', 'fullpath', None,
2753 ('f', 'fullpath', None,
2747 _('print complete paths from the filesystem root')),
2754 _('print complete paths from the filesystem root')),
2748 ] + walkopts,
2755 ] + walkopts,
2749 _('hg locate [OPTION]... [PATTERN]...')),
2756 _('hg locate [OPTION]... [PATTERN]...')),
2750 "^log|history":
2757 "^log|history":
2751 (log,
2758 (log,
2752 [('f', 'follow', None,
2759 [('f', 'follow', None,
2753 _('follow changeset history, or file history across copies and renames')),
2760 _('follow changeset history, or file history across copies and renames')),
2754 ('', 'follow-first', None,
2761 ('', 'follow-first', None,
2755 _('only follow the first parent of merge changesets')),
2762 _('only follow the first parent of merge changesets')),
2756 ('d', 'date', '', _('show revs matching date spec')),
2763 ('d', 'date', '', _('show revs matching date spec')),
2757 ('C', 'copies', None, _('show copied files')),
2764 ('C', 'copies', None, _('show copied files')),
2758 ('k', 'keyword', [], _('search for a keyword')),
2765 ('k', 'keyword', [], _('search for a keyword')),
2759 ('l', 'limit', '', _('limit number of changes displayed')),
2766 ('l', 'limit', '', _('limit number of changes displayed')),
2760 ('r', 'rev', [], _('show the specified revision or range')),
2767 ('r', 'rev', [], _('show the specified revision or range')),
2761 ('', 'removed', None, _('include revs where files were removed')),
2768 ('', 'removed', None, _('include revs where files were removed')),
2762 ('M', 'no-merges', None, _('do not show merges')),
2769 ('M', 'no-merges', None, _('do not show merges')),
2763 ('', 'style', '', _('display using template map file')),
2770 ('', 'style', '', _('display using template map file')),
2764 ('m', 'only-merges', None, _('show only merges')),
2771 ('m', 'only-merges', None, _('show only merges')),
2765 ('p', 'patch', None, _('show patch')),
2772 ('p', 'patch', None, _('show patch')),
2766 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2773 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2767 ('', 'template', '', _('display with template')),
2774 ('', 'template', '', _('display with template')),
2768 ] + walkopts,
2775 ] + walkopts,
2769 _('hg log [OPTION]... [FILE]')),
2776 _('hg log [OPTION]... [FILE]')),
2770 "manifest": (manifest, [], _('hg manifest [REV]')),
2777 "manifest": (manifest, [], _('hg manifest [REV]')),
2771 "merge":
2778 "^merge":
2772 (merge,
2779 (merge,
2773 [('f', 'force', None, _('force a merge with outstanding changes'))],
2780 [('f', 'force', None, _('force a merge with outstanding changes'))],
2774 _('hg merge [-f] [REV]')),
2781 _('hg merge [-f] [REV]')),
2775 "outgoing|out": (outgoing,
2782 "outgoing|out": (outgoing,
2776 [('M', 'no-merges', None, _('do not show merges')),
2783 [('M', 'no-merges', None, _('do not show merges')),
2777 ('f', 'force', None,
2784 ('f', 'force', None,
2778 _('run even when remote repository is unrelated')),
2785 _('run even when remote repository is unrelated')),
2779 ('p', 'patch', None, _('show patch')),
2786 ('p', 'patch', None, _('show patch')),
2780 ('', 'style', '', _('display using template map file')),
2787 ('', 'style', '', _('display using template map file')),
2781 ('r', 'rev', [], _('a specific revision you would like to push')),
2788 ('r', 'rev', [], _('a specific revision you would like to push')),
2782 ('n', 'newest-first', None, _('show newest record first')),
2789 ('n', 'newest-first', None, _('show newest record first')),
2783 ('', 'template', '', _('display with template')),
2790 ('', 'template', '', _('display with template')),
2784 ] + remoteopts,
2791 ] + remoteopts,
2785 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2792 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2786 "^parents":
2793 "^parents":
2787 (parents,
2794 (parents,
2788 [('r', 'rev', '', _('show parents from the specified rev')),
2795 [('r', 'rev', '', _('show parents from the specified rev')),
2789 ('', 'style', '', _('display using template map file')),
2796 ('', 'style', '', _('display using template map file')),
2790 ('', 'template', '', _('display with template'))],
2797 ('', 'template', '', _('display with template'))],
2791 _('hg parents [-r REV] [FILE]')),
2798 _('hg parents [-r REV] [FILE]')),
2792 "paths": (paths, [], _('hg paths [NAME]')),
2799 "paths": (paths, [], _('hg paths [NAME]')),
2793 "^pull":
2800 "^pull":
2794 (pull,
2801 (pull,
2795 [('u', 'update', None,
2802 [('u', 'update', None,
2796 _('update to new tip if changesets were pulled')),
2803 _('update to new tip if changesets were pulled')),
2797 ('f', 'force', None,
2804 ('f', 'force', None,
2798 _('run even when remote repository is unrelated')),
2805 _('run even when remote repository is unrelated')),
2799 ('r', 'rev', [],
2806 ('r', 'rev', [],
2800 _('a specific revision up to which you would like to pull')),
2807 _('a specific revision up to which you would like to pull')),
2801 ] + remoteopts,
2808 ] + remoteopts,
2802 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2809 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2803 "^push":
2810 "^push":
2804 (push,
2811 (push,
2805 [('f', 'force', None, _('force push')),
2812 [('f', 'force', None, _('force push')),
2806 ('r', 'rev', [], _('a specific revision you would like to push')),
2813 ('r', 'rev', [], _('a specific revision you would like to push')),
2807 ] + remoteopts,
2814 ] + remoteopts,
2808 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2815 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2809 "debugrawcommit|rawcommit":
2816 "debugrawcommit|rawcommit":
2810 (rawcommit,
2817 (rawcommit,
2811 [('p', 'parent', [], _('parent')),
2818 [('p', 'parent', [], _('parent')),
2812 ('d', 'date', '', _('date code')),
2819 ('d', 'date', '', _('date code')),
2813 ('u', 'user', '', _('user')),
2820 ('u', 'user', '', _('user')),
2814 ('F', 'files', '', _('file list'))
2821 ('F', 'files', '', _('file list'))
2815 ] + commitopts,
2822 ] + commitopts,
2816 _('hg debugrawcommit [OPTION]... [FILE]...')),
2823 _('hg debugrawcommit [OPTION]... [FILE]...')),
2817 "recover": (recover, [], _('hg recover')),
2824 "recover": (recover, [], _('hg recover')),
2818 "^remove|rm":
2825 "^remove|rm":
2819 (remove,
2826 (remove,
2820 [('A', 'after', None, _('record remove that has already occurred')),
2827 [('A', 'after', None, _('record remove that has already occurred')),
2821 ('f', 'force', None, _('remove file even if modified')),
2828 ('f', 'force', None, _('remove file even if modified')),
2822 ] + walkopts,
2829 ] + walkopts,
2823 _('hg remove [OPTION]... FILE...')),
2830 _('hg remove [OPTION]... FILE...')),
2824 "rename|mv":
2831 "rename|mv":
2825 (rename,
2832 (rename,
2826 [('A', 'after', None, _('record a rename that has already occurred')),
2833 [('A', 'after', None, _('record a rename that has already occurred')),
2827 ('f', 'force', None,
2834 ('f', 'force', None,
2828 _('forcibly copy over an existing managed file')),
2835 _('forcibly copy over an existing managed file')),
2829 ] + walkopts + dryrunopts,
2836 ] + walkopts + dryrunopts,
2830 _('hg rename [OPTION]... SOURCE... DEST')),
2837 _('hg rename [OPTION]... SOURCE... DEST')),
2831 "^revert":
2838 "^revert":
2832 (revert,
2839 (revert,
2833 [('a', 'all', None, _('revert all changes when no arguments given')),
2840 [('a', 'all', None, _('revert all changes when no arguments given')),
2834 ('d', 'date', '', _('tipmost revision matching date')),
2841 ('d', 'date', '', _('tipmost revision matching date')),
2835 ('r', 'rev', '', _('revision to revert to')),
2842 ('r', 'rev', '', _('revision to revert to')),
2836 ('', 'no-backup', None, _('do not save backup copies of files')),
2843 ('', 'no-backup', None, _('do not save backup copies of files')),
2837 ] + walkopts + dryrunopts,
2844 ] + walkopts + dryrunopts,
2838 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2845 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2839 "rollback": (rollback, [], _('hg rollback')),
2846 "rollback": (rollback, [], _('hg rollback')),
2840 "root": (root, [], _('hg root')),
2847 "root": (root, [], _('hg root')),
2841 "showconfig|debugconfig":
2848 "showconfig|debugconfig":
2842 (showconfig,
2849 (showconfig,
2843 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2850 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2844 _('showconfig [-u] [NAME]...')),
2851 _('showconfig [-u] [NAME]...')),
2845 "^serve":
2852 "^serve":
2846 (serve,
2853 (serve,
2847 [('A', 'accesslog', '', _('name of access log file to write to')),
2854 [('A', 'accesslog', '', _('name of access log file to write to')),
2848 ('d', 'daemon', None, _('run server in background')),
2855 ('d', 'daemon', None, _('run server in background')),
2849 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2856 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2850 ('E', 'errorlog', '', _('name of error log file to write to')),
2857 ('E', 'errorlog', '', _('name of error log file to write to')),
2851 ('p', 'port', 0, _('port to use (default: 8000)')),
2858 ('p', 'port', 0, _('port to use (default: 8000)')),
2852 ('a', 'address', '', _('address to use')),
2859 ('a', 'address', '', _('address to use')),
2853 ('n', 'name', '',
2860 ('n', 'name', '',
2854 _('name to show in web pages (default: working dir)')),
2861 _('name to show in web pages (default: working dir)')),
2855 ('', 'webdir-conf', '', _('name of the webdir config file'
2862 ('', 'webdir-conf', '', _('name of the webdir config file'
2856 ' (serve more than one repo)')),
2863 ' (serve more than one repo)')),
2857 ('', 'pid-file', '', _('name of file to write process ID to')),
2864 ('', 'pid-file', '', _('name of file to write process ID to')),
2858 ('', 'stdio', None, _('for remote clients')),
2865 ('', 'stdio', None, _('for remote clients')),
2859 ('t', 'templates', '', _('web templates to use')),
2866 ('t', 'templates', '', _('web templates to use')),
2860 ('', 'style', '', _('template style to use')),
2867 ('', 'style', '', _('template style to use')),
2861 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2868 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2862 _('hg serve [OPTION]...')),
2869 _('hg serve [OPTION]...')),
2863 "^status|st":
2870 "^status|st":
2864 (status,
2871 (status,
2865 [('A', 'all', None, _('show status of all files')),
2872 [('A', 'all', None, _('show status of all files')),
2866 ('m', 'modified', None, _('show only modified files')),
2873 ('m', 'modified', None, _('show only modified files')),
2867 ('a', 'added', None, _('show only added files')),
2874 ('a', 'added', None, _('show only added files')),
2868 ('r', 'removed', None, _('show only removed files')),
2875 ('r', 'removed', None, _('show only removed files')),
2869 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2876 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2870 ('c', 'clean', None, _('show only files without changes')),
2877 ('c', 'clean', None, _('show only files without changes')),
2871 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2878 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2872 ('i', 'ignored', None, _('show ignored files')),
2879 ('i', 'ignored', None, _('show ignored files')),
2873 ('n', 'no-status', None, _('hide status prefix')),
2880 ('n', 'no-status', None, _('hide status prefix')),
2874 ('C', 'copies', None, _('show source of copied files')),
2881 ('C', 'copies', None, _('show source of copied files')),
2875 ('0', 'print0', None,
2882 ('0', 'print0', None,
2876 _('end filenames with NUL, for use with xargs')),
2883 _('end filenames with NUL, for use with xargs')),
2877 ('', 'rev', [], _('show difference from revision')),
2884 ('', 'rev', [], _('show difference from revision')),
2878 ] + walkopts,
2885 ] + walkopts,
2879 _('hg status [OPTION]... [FILE]...')),
2886 _('hg status [OPTION]... [FILE]...')),
2880 "tag":
2887 "tag":
2881 (tag,
2888 (tag,
2882 [('l', 'local', None, _('make the tag local')),
2889 [('l', 'local', None, _('make the tag local')),
2883 ('m', 'message', '', _('message for tag commit log entry')),
2890 ('m', 'message', '', _('message for tag commit log entry')),
2884 ('d', 'date', '', _('record datecode as commit date')),
2891 ('d', 'date', '', _('record datecode as commit date')),
2885 ('u', 'user', '', _('record user as commiter')),
2892 ('u', 'user', '', _('record user as commiter')),
2886 ('r', 'rev', '', _('revision to tag'))],
2893 ('r', 'rev', '', _('revision to tag'))],
2887 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2894 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2888 "tags": (tags, [], _('hg tags')),
2895 "tags": (tags, [], _('hg tags')),
2889 "tip":
2896 "tip":
2890 (tip,
2897 (tip,
2891 [('', 'style', '', _('display using template map file')),
2898 [('', 'style', '', _('display using template map file')),
2892 ('p', 'patch', None, _('show patch')),
2899 ('p', 'patch', None, _('show patch')),
2893 ('', 'template', '', _('display with template'))],
2900 ('', 'template', '', _('display with template'))],
2894 _('hg tip [-p]')),
2901 _('hg tip [-p]')),
2895 "unbundle":
2902 "unbundle":
2896 (unbundle,
2903 (unbundle,
2897 [('u', 'update', None,
2904 [('u', 'update', None,
2898 _('update to new tip if changesets were unbundled'))],
2905 _('update to new tip if changesets were unbundled'))],
2899 _('hg unbundle [-u] FILE')),
2906 _('hg unbundle [-u] FILE')),
2900 "^update|up|checkout|co":
2907 "^update|up|checkout|co":
2901 (update,
2908 (update,
2902 [('C', 'clean', None, _('overwrite locally modified files')),
2909 [('C', 'clean', None, _('overwrite locally modified files')),
2903 ('d', 'date', '', _('tipmost revision matching date'))],
2910 ('d', 'date', '', _('tipmost revision matching date'))],
2904 _('hg update [-C] [-d DATE] [REV]')),
2911 _('hg update [-C] [-d DATE] [REV]')),
2905 "verify": (verify, [], _('hg verify')),
2912 "verify": (verify, [], _('hg verify')),
2906 "version": (version_, [], _('hg version')),
2913 "version": (version_, [], _('hg version')),
2907 }
2914 }
2908
2915
2909 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2916 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2910 " debugindex debugindexdot debugdate debuginstall")
2917 " debugindex debugindexdot debugdate debuginstall")
2911 optionalrepo = ("paths serve showconfig")
2918 optionalrepo = ("paths serve showconfig")
2912
2919
2913 def findpossible(ui, cmd):
2920 def findpossible(ui, cmd):
2914 """
2921 """
2915 Return cmd -> (aliases, command table entry)
2922 Return cmd -> (aliases, command table entry)
2916 for each matching command.
2923 for each matching command.
2917 Return debug commands (or their aliases) only if no normal command matches.
2924 Return debug commands (or their aliases) only if no normal command matches.
2918 """
2925 """
2919 choice = {}
2926 choice = {}
2920 debugchoice = {}
2927 debugchoice = {}
2921 for e in table.keys():
2928 for e in table.keys():
2922 aliases = e.lstrip("^").split("|")
2929 aliases = e.lstrip("^").split("|")
2923 found = None
2930 found = None
2924 if cmd in aliases:
2931 if cmd in aliases:
2925 found = cmd
2932 found = cmd
2926 elif not ui.config("ui", "strict"):
2933 elif not ui.config("ui", "strict"):
2927 for a in aliases:
2934 for a in aliases:
2928 if a.startswith(cmd):
2935 if a.startswith(cmd):
2929 found = a
2936 found = a
2930 break
2937 break
2931 if found is not None:
2938 if found is not None:
2932 if aliases[0].startswith("debug") or found.startswith("debug"):
2939 if aliases[0].startswith("debug") or found.startswith("debug"):
2933 debugchoice[found] = (aliases, table[e])
2940 debugchoice[found] = (aliases, table[e])
2934 else:
2941 else:
2935 choice[found] = (aliases, table[e])
2942 choice[found] = (aliases, table[e])
2936
2943
2937 if not choice and debugchoice:
2944 if not choice and debugchoice:
2938 choice = debugchoice
2945 choice = debugchoice
2939
2946
2940 return choice
2947 return choice
2941
2948
2942 def findcmd(ui, cmd):
2949 def findcmd(ui, cmd):
2943 """Return (aliases, command table entry) for command string."""
2950 """Return (aliases, command table entry) for command string."""
2944 choice = findpossible(ui, cmd)
2951 choice = findpossible(ui, cmd)
2945
2952
2946 if choice.has_key(cmd):
2953 if choice.has_key(cmd):
2947 return choice[cmd]
2954 return choice[cmd]
2948
2955
2949 if len(choice) > 1:
2956 if len(choice) > 1:
2950 clist = choice.keys()
2957 clist = choice.keys()
2951 clist.sort()
2958 clist.sort()
2952 raise AmbiguousCommand(cmd, clist)
2959 raise AmbiguousCommand(cmd, clist)
2953
2960
2954 if choice:
2961 if choice:
2955 return choice.values()[0]
2962 return choice.values()[0]
2956
2963
2957 raise UnknownCommand(cmd)
2964 raise UnknownCommand(cmd)
2958
2965
2959 def catchterm(*args):
2966 def catchterm(*args):
2960 raise util.SignalInterrupt
2967 raise util.SignalInterrupt
2961
2968
2962 def run():
2969 def run():
2963 sys.exit(dispatch(sys.argv[1:]))
2970 sys.exit(dispatch(sys.argv[1:]))
2964
2971
2965 class ParseError(Exception):
2972 class ParseError(Exception):
2966 """Exception raised on errors in parsing the command line."""
2973 """Exception raised on errors in parsing the command line."""
2967
2974
2968 def parse(ui, args):
2975 def parse(ui, args):
2969 options = {}
2976 options = {}
2970 cmdoptions = {}
2977 cmdoptions = {}
2971
2978
2972 try:
2979 try:
2973 args = fancyopts.fancyopts(args, globalopts, options)
2980 args = fancyopts.fancyopts(args, globalopts, options)
2974 except fancyopts.getopt.GetoptError, inst:
2981 except fancyopts.getopt.GetoptError, inst:
2975 raise ParseError(None, inst)
2982 raise ParseError(None, inst)
2976
2983
2977 if args:
2984 if args:
2978 cmd, args = args[0], args[1:]
2985 cmd, args = args[0], args[1:]
2979 aliases, i = findcmd(ui, cmd)
2986 aliases, i = findcmd(ui, cmd)
2980 cmd = aliases[0]
2987 cmd = aliases[0]
2981 defaults = ui.config("defaults", cmd)
2988 defaults = ui.config("defaults", cmd)
2982 if defaults:
2989 if defaults:
2983 args = shlex.split(defaults) + args
2990 args = shlex.split(defaults) + args
2984 c = list(i[1])
2991 c = list(i[1])
2985 else:
2992 else:
2986 cmd = None
2993 cmd = None
2987 c = []
2994 c = []
2988
2995
2989 # combine global options into local
2996 # combine global options into local
2990 for o in globalopts:
2997 for o in globalopts:
2991 c.append((o[0], o[1], options[o[1]], o[3]))
2998 c.append((o[0], o[1], options[o[1]], o[3]))
2992
2999
2993 try:
3000 try:
2994 args = fancyopts.fancyopts(args, c, cmdoptions)
3001 args = fancyopts.fancyopts(args, c, cmdoptions)
2995 except fancyopts.getopt.GetoptError, inst:
3002 except fancyopts.getopt.GetoptError, inst:
2996 raise ParseError(cmd, inst)
3003 raise ParseError(cmd, inst)
2997
3004
2998 # separate global options back out
3005 # separate global options back out
2999 for o in globalopts:
3006 for o in globalopts:
3000 n = o[1]
3007 n = o[1]
3001 options[n] = cmdoptions[n]
3008 options[n] = cmdoptions[n]
3002 del cmdoptions[n]
3009 del cmdoptions[n]
3003
3010
3004 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3011 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3005
3012
3006 external = {}
3013 external = {}
3007
3014
3008 def findext(name):
3015 def findext(name):
3009 '''return module with given extension name'''
3016 '''return module with given extension name'''
3010 try:
3017 try:
3011 return sys.modules[external[name]]
3018 return sys.modules[external[name]]
3012 except KeyError:
3019 except KeyError:
3013 for k, v in external.iteritems():
3020 for k, v in external.iteritems():
3014 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3021 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3015 return sys.modules[v]
3022 return sys.modules[v]
3016 raise KeyError(name)
3023 raise KeyError(name)
3017
3024
3018 def load_extensions(ui):
3025 def load_extensions(ui):
3019 added = []
3026 added = []
3020 for ext_name, load_from_name in ui.extensions():
3027 for ext_name, load_from_name in ui.extensions():
3021 if ext_name in external:
3028 if ext_name in external:
3022 continue
3029 continue
3023 try:
3030 try:
3024 if load_from_name:
3031 if load_from_name:
3025 # the module will be loaded in sys.modules
3032 # the module will be loaded in sys.modules
3026 # choose an unique name so that it doesn't
3033 # choose an unique name so that it doesn't
3027 # conflicts with other modules
3034 # conflicts with other modules
3028 module_name = "hgext_%s" % ext_name.replace('.', '_')
3035 module_name = "hgext_%s" % ext_name.replace('.', '_')
3029 mod = imp.load_source(module_name, load_from_name)
3036 mod = imp.load_source(module_name, load_from_name)
3030 else:
3037 else:
3031 def importh(name):
3038 def importh(name):
3032 mod = __import__(name)
3039 mod = __import__(name)
3033 components = name.split('.')
3040 components = name.split('.')
3034 for comp in components[1:]:
3041 for comp in components[1:]:
3035 mod = getattr(mod, comp)
3042 mod = getattr(mod, comp)
3036 return mod
3043 return mod
3037 try:
3044 try:
3038 mod = importh("hgext.%s" % ext_name)
3045 mod = importh("hgext.%s" % ext_name)
3039 except ImportError:
3046 except ImportError:
3040 mod = importh(ext_name)
3047 mod = importh(ext_name)
3041 external[ext_name] = mod.__name__
3048 external[ext_name] = mod.__name__
3042 added.append((mod, ext_name))
3049 added.append((mod, ext_name))
3043 except (util.SignalInterrupt, KeyboardInterrupt):
3050 except (util.SignalInterrupt, KeyboardInterrupt):
3044 raise
3051 raise
3045 except Exception, inst:
3052 except Exception, inst:
3046 ui.warn(_("*** failed to import extension %s: %s\n") %
3053 ui.warn(_("*** failed to import extension %s: %s\n") %
3047 (ext_name, inst))
3054 (ext_name, inst))
3048 if ui.print_exc():
3055 if ui.print_exc():
3049 return 1
3056 return 1
3050
3057
3051 for mod, name in added:
3058 for mod, name in added:
3052 uisetup = getattr(mod, 'uisetup', None)
3059 uisetup = getattr(mod, 'uisetup', None)
3053 if uisetup:
3060 if uisetup:
3054 uisetup(ui)
3061 uisetup(ui)
3055 cmdtable = getattr(mod, 'cmdtable', {})
3062 cmdtable = getattr(mod, 'cmdtable', {})
3056 overrides = [cmd for cmd in cmdtable if cmd in table]
3063 overrides = [cmd for cmd in cmdtable if cmd in table]
3057 if overrides:
3064 if overrides:
3058 ui.warn(_("extension '%s' overrides commands: %s\n")
3065 ui.warn(_("extension '%s' overrides commands: %s\n")
3059 % (name, " ".join(overrides)))
3066 % (name, " ".join(overrides)))
3060 table.update(cmdtable)
3067 table.update(cmdtable)
3061
3068
3062 def parseconfig(config):
3069 def parseconfig(config):
3063 """parse the --config options from the command line"""
3070 """parse the --config options from the command line"""
3064 parsed = []
3071 parsed = []
3065 for cfg in config:
3072 for cfg in config:
3066 try:
3073 try:
3067 name, value = cfg.split('=', 1)
3074 name, value = cfg.split('=', 1)
3068 section, name = name.split('.', 1)
3075 section, name = name.split('.', 1)
3069 if not section or not name:
3076 if not section or not name:
3070 raise IndexError
3077 raise IndexError
3071 parsed.append((section, name, value))
3078 parsed.append((section, name, value))
3072 except (IndexError, ValueError):
3079 except (IndexError, ValueError):
3073 raise util.Abort(_('malformed --config option: %s') % cfg)
3080 raise util.Abort(_('malformed --config option: %s') % cfg)
3074 return parsed
3081 return parsed
3075
3082
3076 def dispatch(args):
3083 def dispatch(args):
3077 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3084 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3078 num = getattr(signal, name, None)
3085 num = getattr(signal, name, None)
3079 if num: signal.signal(num, catchterm)
3086 if num: signal.signal(num, catchterm)
3080
3087
3081 try:
3088 try:
3082 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3089 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3083 except util.Abort, inst:
3090 except util.Abort, inst:
3084 sys.stderr.write(_("abort: %s\n") % inst)
3091 sys.stderr.write(_("abort: %s\n") % inst)
3085 return -1
3092 return -1
3086
3093
3087 load_extensions(u)
3094 load_extensions(u)
3088 u.addreadhook(load_extensions)
3095 u.addreadhook(load_extensions)
3089
3096
3090 try:
3097 try:
3091 cmd, func, args, options, cmdoptions = parse(u, args)
3098 cmd, func, args, options, cmdoptions = parse(u, args)
3092 if options["encoding"]:
3099 if options["encoding"]:
3093 util._encoding = options["encoding"]
3100 util._encoding = options["encoding"]
3094 if options["encodingmode"]:
3101 if options["encodingmode"]:
3095 util._encodingmode = options["encodingmode"]
3102 util._encodingmode = options["encodingmode"]
3096 if options["time"]:
3103 if options["time"]:
3097 def get_times():
3104 def get_times():
3098 t = os.times()
3105 t = os.times()
3099 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3106 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3100 t = (t[0], t[1], t[2], t[3], time.clock())
3107 t = (t[0], t[1], t[2], t[3], time.clock())
3101 return t
3108 return t
3102 s = get_times()
3109 s = get_times()
3103 def print_time():
3110 def print_time():
3104 t = get_times()
3111 t = get_times()
3105 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3112 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3106 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3113 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3107 atexit.register(print_time)
3114 atexit.register(print_time)
3108
3115
3109 # enter the debugger before command execution
3116 # enter the debugger before command execution
3110 if options['debugger']:
3117 if options['debugger']:
3111 pdb.set_trace()
3118 pdb.set_trace()
3112
3119
3113 try:
3120 try:
3114 if options['cwd']:
3121 if options['cwd']:
3115 os.chdir(options['cwd'])
3122 os.chdir(options['cwd'])
3116
3123
3117 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3124 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3118 not options["noninteractive"], options["traceback"],
3125 not options["noninteractive"], options["traceback"],
3119 parseconfig(options["config"]))
3126 parseconfig(options["config"]))
3120
3127
3121 path = u.expandpath(options["repository"]) or ""
3128 path = u.expandpath(options["repository"]) or ""
3122 repo = path and hg.repository(u, path=path) or None
3129 repo = path and hg.repository(u, path=path) or None
3123 if repo and not repo.local():
3130 if repo and not repo.local():
3124 raise util.Abort(_("repository '%s' is not local") % path)
3131 raise util.Abort(_("repository '%s' is not local") % path)
3125
3132
3126 if options['help']:
3133 if options['help']:
3127 return help_(u, cmd, options['version'])
3134 return help_(u, cmd, options['version'])
3128 elif options['version']:
3135 elif options['version']:
3129 return version_(u)
3136 return version_(u)
3130 elif not cmd:
3137 elif not cmd:
3131 return help_(u, 'shortlist')
3138 return help_(u, 'shortlist')
3132
3139
3133 if cmd not in norepo.split():
3140 if cmd not in norepo.split():
3134 try:
3141 try:
3135 if not repo:
3142 if not repo:
3136 repo = hg.repository(u, path=path)
3143 repo = hg.repository(u, path=path)
3137 u = repo.ui
3144 u = repo.ui
3138 for name in external.itervalues():
3145 for name in external.itervalues():
3139 mod = sys.modules[name]
3146 mod = sys.modules[name]
3140 if hasattr(mod, 'reposetup'):
3147 if hasattr(mod, 'reposetup'):
3141 mod.reposetup(u, repo)
3148 mod.reposetup(u, repo)
3142 hg.repo_setup_hooks.append(mod.reposetup)
3149 hg.repo_setup_hooks.append(mod.reposetup)
3143 except hg.RepoError:
3150 except hg.RepoError:
3144 if cmd not in optionalrepo.split():
3151 if cmd not in optionalrepo.split():
3145 raise
3152 raise
3146 d = lambda: func(u, repo, *args, **cmdoptions)
3153 d = lambda: func(u, repo, *args, **cmdoptions)
3147 else:
3154 else:
3148 d = lambda: func(u, *args, **cmdoptions)
3155 d = lambda: func(u, *args, **cmdoptions)
3149
3156
3150 try:
3157 try:
3151 if options['profile']:
3158 if options['profile']:
3152 import hotshot, hotshot.stats
3159 import hotshot, hotshot.stats
3153 prof = hotshot.Profile("hg.prof")
3160 prof = hotshot.Profile("hg.prof")
3154 try:
3161 try:
3155 try:
3162 try:
3156 return prof.runcall(d)
3163 return prof.runcall(d)
3157 except:
3164 except:
3158 try:
3165 try:
3159 u.warn(_('exception raised - generating '
3166 u.warn(_('exception raised - generating '
3160 'profile anyway\n'))
3167 'profile anyway\n'))
3161 except:
3168 except:
3162 pass
3169 pass
3163 raise
3170 raise
3164 finally:
3171 finally:
3165 prof.close()
3172 prof.close()
3166 stats = hotshot.stats.load("hg.prof")
3173 stats = hotshot.stats.load("hg.prof")
3167 stats.strip_dirs()
3174 stats.strip_dirs()
3168 stats.sort_stats('time', 'calls')
3175 stats.sort_stats('time', 'calls')
3169 stats.print_stats(40)
3176 stats.print_stats(40)
3170 elif options['lsprof']:
3177 elif options['lsprof']:
3171 try:
3178 try:
3172 from mercurial import lsprof
3179 from mercurial import lsprof
3173 except ImportError:
3180 except ImportError:
3174 raise util.Abort(_(
3181 raise util.Abort(_(
3175 'lsprof not available - install from '
3182 'lsprof not available - install from '
3176 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3183 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3177 p = lsprof.Profiler()
3184 p = lsprof.Profiler()
3178 p.enable(subcalls=True)
3185 p.enable(subcalls=True)
3179 try:
3186 try:
3180 return d()
3187 return d()
3181 finally:
3188 finally:
3182 p.disable()
3189 p.disable()
3183 stats = lsprof.Stats(p.getstats())
3190 stats = lsprof.Stats(p.getstats())
3184 stats.sort()
3191 stats.sort()
3185 stats.pprint(top=10, file=sys.stderr, climit=5)
3192 stats.pprint(top=10, file=sys.stderr, climit=5)
3186 else:
3193 else:
3187 return d()
3194 return d()
3188 finally:
3195 finally:
3189 u.flush()
3196 u.flush()
3190 except:
3197 except:
3191 # enter the debugger when we hit an exception
3198 # enter the debugger when we hit an exception
3192 if options['debugger']:
3199 if options['debugger']:
3193 pdb.post_mortem(sys.exc_info()[2])
3200 pdb.post_mortem(sys.exc_info()[2])
3194 u.print_exc()
3201 u.print_exc()
3195 raise
3202 raise
3196 except ParseError, inst:
3203 except ParseError, inst:
3197 if inst.args[0]:
3204 if inst.args[0]:
3198 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3205 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3199 help_(u, inst.args[0])
3206 help_(u, inst.args[0])
3200 else:
3207 else:
3201 u.warn(_("hg: %s\n") % inst.args[1])
3208 u.warn(_("hg: %s\n") % inst.args[1])
3202 help_(u, 'shortlist')
3209 help_(u, 'shortlist')
3203 except AmbiguousCommand, inst:
3210 except AmbiguousCommand, inst:
3204 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3211 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3205 (inst.args[0], " ".join(inst.args[1])))
3212 (inst.args[0], " ".join(inst.args[1])))
3206 except UnknownCommand, inst:
3213 except UnknownCommand, inst:
3207 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3214 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3208 help_(u, 'shortlist')
3215 help_(u, 'shortlist')
3209 except hg.RepoError, inst:
3216 except hg.RepoError, inst:
3210 u.warn(_("abort: %s!\n") % inst)
3217 u.warn(_("abort: %s!\n") % inst)
3211 except lock.LockHeld, inst:
3218 except lock.LockHeld, inst:
3212 if inst.errno == errno.ETIMEDOUT:
3219 if inst.errno == errno.ETIMEDOUT:
3213 reason = _('timed out waiting for lock held by %s') % inst.locker
3220 reason = _('timed out waiting for lock held by %s') % inst.locker
3214 else:
3221 else:
3215 reason = _('lock held by %s') % inst.locker
3222 reason = _('lock held by %s') % inst.locker
3216 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3223 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3217 except lock.LockUnavailable, inst:
3224 except lock.LockUnavailable, inst:
3218 u.warn(_("abort: could not lock %s: %s\n") %
3225 u.warn(_("abort: could not lock %s: %s\n") %
3219 (inst.desc or inst.filename, inst.strerror))
3226 (inst.desc or inst.filename, inst.strerror))
3220 except revlog.RevlogError, inst:
3227 except revlog.RevlogError, inst:
3221 u.warn(_("abort: %s!\n") % inst)
3228 u.warn(_("abort: %s!\n") % inst)
3222 except util.SignalInterrupt:
3229 except util.SignalInterrupt:
3223 u.warn(_("killed!\n"))
3230 u.warn(_("killed!\n"))
3224 except KeyboardInterrupt:
3231 except KeyboardInterrupt:
3225 try:
3232 try:
3226 u.warn(_("interrupted!\n"))
3233 u.warn(_("interrupted!\n"))
3227 except IOError, inst:
3234 except IOError, inst:
3228 if inst.errno == errno.EPIPE:
3235 if inst.errno == errno.EPIPE:
3229 if u.debugflag:
3236 if u.debugflag:
3230 u.warn(_("\nbroken pipe\n"))
3237 u.warn(_("\nbroken pipe\n"))
3231 else:
3238 else:
3232 raise
3239 raise
3240 except socket.error, inst:
3241 u.warn(_("abort: %s\n") % inst[1])
3233 except IOError, inst:
3242 except IOError, inst:
3234 if hasattr(inst, "code"):
3243 if hasattr(inst, "code"):
3235 u.warn(_("abort: %s\n") % inst)
3244 u.warn(_("abort: %s\n") % inst)
3236 elif hasattr(inst, "reason"):
3245 elif hasattr(inst, "reason"):
3237 u.warn(_("abort: error: %s\n") % inst.reason[1])
3246 try: # usually it is in the form (errno, strerror)
3247 reason = inst.reason.args[1]
3248 except: # it might be anything, for example a string
3249 reason = inst.reason
3250 u.warn(_("abort: error: %s\n") % reason)
3238 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3251 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3239 if u.debugflag:
3252 if u.debugflag:
3240 u.warn(_("broken pipe\n"))
3253 u.warn(_("broken pipe\n"))
3241 elif getattr(inst, "strerror", None):
3254 elif getattr(inst, "strerror", None):
3242 if getattr(inst, "filename", None):
3255 if getattr(inst, "filename", None):
3243 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3256 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3244 else:
3257 else:
3245 u.warn(_("abort: %s\n") % inst.strerror)
3258 u.warn(_("abort: %s\n") % inst.strerror)
3246 else:
3259 else:
3247 raise
3260 raise
3248 except OSError, inst:
3261 except OSError, inst:
3249 if getattr(inst, "filename", None):
3262 if getattr(inst, "filename", None):
3250 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3263 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3251 else:
3264 else:
3252 u.warn(_("abort: %s\n") % inst.strerror)
3265 u.warn(_("abort: %s\n") % inst.strerror)
3253 except util.UnexpectedOutput, inst:
3266 except util.UnexpectedOutput, inst:
3254 u.warn(_("abort: %s") % inst[0])
3267 u.warn(_("abort: %s") % inst[0])
3255 if not isinstance(inst[1], basestring):
3268 if not isinstance(inst[1], basestring):
3256 u.warn(" %r\n" % (inst[1],))
3269 u.warn(" %r\n" % (inst[1],))
3257 elif not inst[1]:
3270 elif not inst[1]:
3258 u.warn(_(" empty string\n"))
3271 u.warn(_(" empty string\n"))
3259 else:
3272 else:
3260 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3273 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3261 except util.Abort, inst:
3274 except util.Abort, inst:
3262 u.warn(_("abort: %s\n") % inst)
3275 u.warn(_("abort: %s\n") % inst)
3263 except TypeError, inst:
3276 except TypeError, inst:
3264 # was this an argument error?
3277 # was this an argument error?
3265 tb = traceback.extract_tb(sys.exc_info()[2])
3278 tb = traceback.extract_tb(sys.exc_info()[2])
3266 if len(tb) > 2: # no
3279 if len(tb) > 2: # no
3267 raise
3280 raise
3268 u.debug(inst, "\n")
3281 u.debug(inst, "\n")
3269 u.warn(_("%s: invalid arguments\n") % cmd)
3282 u.warn(_("%s: invalid arguments\n") % cmd)
3270 help_(u, cmd)
3283 help_(u, cmd)
3271 except SystemExit, inst:
3284 except SystemExit, inst:
3272 # Commands shouldn't sys.exit directly, but give a return code.
3285 # Commands shouldn't sys.exit directly, but give a return code.
3273 # Just in case catch this and and pass exit code to caller.
3286 # Just in case catch this and and pass exit code to caller.
3274 return inst.code
3287 return inst.code
3275 except:
3288 except:
3276 u.warn(_("** unknown exception encountered, details follow\n"))
3289 u.warn(_("** unknown exception encountered, details follow\n"))
3277 u.warn(_("** report bug details to "
3290 u.warn(_("** report bug details to "
3278 "http://www.selenic.com/mercurial/bts\n"))
3291 "http://www.selenic.com/mercurial/bts\n"))
3279 u.warn(_("** or mercurial@selenic.com\n"))
3292 u.warn(_("** or mercurial@selenic.com\n"))
3280 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3293 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3281 % version.get_version())
3294 % version.get_version())
3282 raise
3295 raise
3283
3296
3284 return -1
3297 return -1
@@ -1,239 +1,246 b''
1 # hgweb/server.py - The standalone hg web server.
1 # hgweb/server.py - The standalone hg web server.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer
9 import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
10 from mercurial import ui, hg, util, templater
10 from mercurial import ui, hg, util, templater
11 from hgweb_mod import hgweb
11 from hgweb_mod import hgweb
12 from hgwebdir_mod import hgwebdir
12 from hgwebdir_mod import hgwebdir
13 from request import wsgiapplication
13 from request import wsgiapplication
14 from mercurial.i18n import gettext as _
14 from mercurial.i18n import gettext as _
15
15
16 def _splitURI(uri):
16 def _splitURI(uri):
17 """ Return path and query splited from uri
17 """ Return path and query splited from uri
18
18
19 Just like CGI environment, the path is unquoted, the query is
19 Just like CGI environment, the path is unquoted, the query is
20 not.
20 not.
21 """
21 """
22 if '?' in uri:
22 if '?' in uri:
23 path, query = uri.split('?', 1)
23 path, query = uri.split('?', 1)
24 else:
24 else:
25 path, query = uri, ''
25 path, query = uri, ''
26 return urllib.unquote(path), query
26 return urllib.unquote(path), query
27
27
28 class _error_logger(object):
28 class _error_logger(object):
29 def __init__(self, handler):
29 def __init__(self, handler):
30 self.handler = handler
30 self.handler = handler
31 def flush(self):
31 def flush(self):
32 pass
32 pass
33 def write(self, str):
33 def write(self, str):
34 self.writelines(str.split('\n'))
34 self.writelines(str.split('\n'))
35 def writelines(self, seq):
35 def writelines(self, seq):
36 for msg in seq:
36 for msg in seq:
37 self.handler.log_error("HG error: %s", msg)
37 self.handler.log_error("HG error: %s", msg)
38
38
39 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
39 class _hgwebhandler(object, BaseHTTPServer.BaseHTTPRequestHandler):
40 def __init__(self, *args, **kargs):
40 def __init__(self, *args, **kargs):
41 self.protocol_version = 'HTTP/1.1'
41 self.protocol_version = 'HTTP/1.1'
42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
42 BaseHTTPServer.BaseHTTPRequestHandler.__init__(self, *args, **kargs)
43
43
44 def log_error(self, format, *args):
44 def log_error(self, format, *args):
45 errorlog = self.server.errorlog
45 errorlog = self.server.errorlog
46 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
46 errorlog.write("%s - - [%s] %s\n" % (self.address_string(),
47 self.log_date_time_string(),
47 self.log_date_time_string(),
48 format % args))
48 format % args))
49
49
50 def log_message(self, format, *args):
50 def log_message(self, format, *args):
51 accesslog = self.server.accesslog
51 accesslog = self.server.accesslog
52 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
52 accesslog.write("%s - - [%s] %s\n" % (self.address_string(),
53 self.log_date_time_string(),
53 self.log_date_time_string(),
54 format % args))
54 format % args))
55
55
56 def do_POST(self):
56 def do_POST(self):
57 try:
57 try:
58 self.do_hgweb()
58 try:
59 except socket.error, inst:
59 self.do_hgweb()
60 if inst[0] != errno.EPIPE:
60 except socket.error, inst:
61 raise
61 if inst[0] != errno.EPIPE:
62 raise
63 except StandardError, inst:
64 self._start_response("500 Internal Server Error", [])
65 self._write("Internal Server Error")
66 tb = "".join(traceback.format_exception(*sys.exc_info()))
67 self.log_error("Exception happened during processing request '%s':\n%s",
68 self.path, tb)
62
69
63 def do_GET(self):
70 def do_GET(self):
64 self.do_POST()
71 self.do_POST()
65
72
66 def do_hgweb(self):
73 def do_hgweb(self):
67 path_info, query = _splitURI(self.path)
74 path_info, query = _splitURI(self.path)
68
75
69 env = {}
76 env = {}
70 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
77 env['GATEWAY_INTERFACE'] = 'CGI/1.1'
71 env['REQUEST_METHOD'] = self.command
78 env['REQUEST_METHOD'] = self.command
72 env['SERVER_NAME'] = self.server.server_name
79 env['SERVER_NAME'] = self.server.server_name
73 env['SERVER_PORT'] = str(self.server.server_port)
80 env['SERVER_PORT'] = str(self.server.server_port)
74 env['REQUEST_URI'] = self.path
81 env['REQUEST_URI'] = self.path
75 env['PATH_INFO'] = path_info
82 env['PATH_INFO'] = path_info
76 if query:
83 if query:
77 env['QUERY_STRING'] = query
84 env['QUERY_STRING'] = query
78 host = self.address_string()
85 host = self.address_string()
79 if host != self.client_address[0]:
86 if host != self.client_address[0]:
80 env['REMOTE_HOST'] = host
87 env['REMOTE_HOST'] = host
81 env['REMOTE_ADDR'] = self.client_address[0]
88 env['REMOTE_ADDR'] = self.client_address[0]
82
89
83 if self.headers.typeheader is None:
90 if self.headers.typeheader is None:
84 env['CONTENT_TYPE'] = self.headers.type
91 env['CONTENT_TYPE'] = self.headers.type
85 else:
92 else:
86 env['CONTENT_TYPE'] = self.headers.typeheader
93 env['CONTENT_TYPE'] = self.headers.typeheader
87 length = self.headers.getheader('content-length')
94 length = self.headers.getheader('content-length')
88 if length:
95 if length:
89 env['CONTENT_LENGTH'] = length
96 env['CONTENT_LENGTH'] = length
90 for header in [h for h in self.headers.keys() \
97 for header in [h for h in self.headers.keys() \
91 if h not in ('content-type', 'content-length')]:
98 if h not in ('content-type', 'content-length')]:
92 hkey = 'HTTP_' + header.replace('-', '_').upper()
99 hkey = 'HTTP_' + header.replace('-', '_').upper()
93 hval = self.headers.getheader(header)
100 hval = self.headers.getheader(header)
94 hval = hval.replace('\n', '').strip()
101 hval = hval.replace('\n', '').strip()
95 if hval:
102 if hval:
96 env[hkey] = hval
103 env[hkey] = hval
97 env['SERVER_PROTOCOL'] = self.request_version
104 env['SERVER_PROTOCOL'] = self.request_version
98 env['wsgi.version'] = (1, 0)
105 env['wsgi.version'] = (1, 0)
99 env['wsgi.url_scheme'] = 'http'
106 env['wsgi.url_scheme'] = 'http'
100 env['wsgi.input'] = self.rfile
107 env['wsgi.input'] = self.rfile
101 env['wsgi.errors'] = _error_logger(self)
108 env['wsgi.errors'] = _error_logger(self)
102 env['wsgi.multithread'] = isinstance(self.server,
109 env['wsgi.multithread'] = isinstance(self.server,
103 SocketServer.ThreadingMixIn)
110 SocketServer.ThreadingMixIn)
104 env['wsgi.multiprocess'] = isinstance(self.server,
111 env['wsgi.multiprocess'] = isinstance(self.server,
105 SocketServer.ForkingMixIn)
112 SocketServer.ForkingMixIn)
106 env['wsgi.run_once'] = 0
113 env['wsgi.run_once'] = 0
107
114
108 self.close_connection = True
115 self.close_connection = True
109 self.saved_status = None
116 self.saved_status = None
110 self.saved_headers = []
117 self.saved_headers = []
111 self.sent_headers = False
118 self.sent_headers = False
112 self.length = None
119 self.length = None
113 req = self.server.reqmaker(env, self._start_response)
120 req = self.server.reqmaker(env, self._start_response)
114 for data in req:
121 for data in req:
115 if data:
122 if data:
116 self._write(data)
123 self._write(data)
117
124
118 def send_headers(self):
125 def send_headers(self):
119 if not self.saved_status:
126 if not self.saved_status:
120 raise AssertionError("Sending headers before start_response() called")
127 raise AssertionError("Sending headers before start_response() called")
121 saved_status = self.saved_status.split(None, 1)
128 saved_status = self.saved_status.split(None, 1)
122 saved_status[0] = int(saved_status[0])
129 saved_status[0] = int(saved_status[0])
123 self.send_response(*saved_status)
130 self.send_response(*saved_status)
124 should_close = True
131 should_close = True
125 for h in self.saved_headers:
132 for h in self.saved_headers:
126 self.send_header(*h)
133 self.send_header(*h)
127 if h[0].lower() == 'content-length':
134 if h[0].lower() == 'content-length':
128 should_close = False
135 should_close = False
129 self.length = int(h[1])
136 self.length = int(h[1])
130 # The value of the Connection header is a list of case-insensitive
137 # The value of the Connection header is a list of case-insensitive
131 # tokens separated by commas and optional whitespace.
138 # tokens separated by commas and optional whitespace.
132 if 'close' in [token.strip().lower() for token in
139 if 'close' in [token.strip().lower() for token in
133 self.headers.get('connection', '').split(',')]:
140 self.headers.get('connection', '').split(',')]:
134 should_close = True
141 should_close = True
135 if should_close:
142 if should_close:
136 self.send_header('Connection', 'close')
143 self.send_header('Connection', 'close')
137 self.close_connection = should_close
144 self.close_connection = should_close
138 self.end_headers()
145 self.end_headers()
139 self.sent_headers = True
146 self.sent_headers = True
140
147
141 def _start_response(self, http_status, headers, exc_info=None):
148 def _start_response(self, http_status, headers, exc_info=None):
142 code, msg = http_status.split(None, 1)
149 code, msg = http_status.split(None, 1)
143 code = int(code)
150 code = int(code)
144 self.saved_status = http_status
151 self.saved_status = http_status
145 bad_headers = ('connection', 'transfer-encoding')
152 bad_headers = ('connection', 'transfer-encoding')
146 self.saved_headers = [ h for h in headers \
153 self.saved_headers = [ h for h in headers \
147 if h[0].lower() not in bad_headers ]
154 if h[0].lower() not in bad_headers ]
148 return self._write
155 return self._write
149
156
150 def _write(self, data):
157 def _write(self, data):
151 if not self.saved_status:
158 if not self.saved_status:
152 raise AssertionError("data written before start_response() called")
159 raise AssertionError("data written before start_response() called")
153 elif not self.sent_headers:
160 elif not self.sent_headers:
154 self.send_headers()
161 self.send_headers()
155 if self.length is not None:
162 if self.length is not None:
156 if len(data) > self.length:
163 if len(data) > self.length:
157 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
164 raise AssertionError("Content-length header sent, but more bytes than specified are being written.")
158 self.length = self.length - len(data)
165 self.length = self.length - len(data)
159 self.wfile.write(data)
166 self.wfile.write(data)
160 self.wfile.flush()
167 self.wfile.flush()
161
168
162 def create_server(ui, repo):
169 def create_server(ui, repo):
163 use_threads = True
170 use_threads = True
164
171
165 def openlog(opt, default):
172 def openlog(opt, default):
166 if opt and opt != '-':
173 if opt and opt != '-':
167 return open(opt, 'w')
174 return open(opt, 'w')
168 return default
175 return default
169
176
170 address = ui.config("web", "address", "")
177 address = ui.config("web", "address", "")
171 port = int(ui.config("web", "port", 8000))
178 port = int(ui.config("web", "port", 8000))
172 use_ipv6 = ui.configbool("web", "ipv6")
179 use_ipv6 = ui.configbool("web", "ipv6")
173 webdir_conf = ui.config("web", "webdir_conf")
180 webdir_conf = ui.config("web", "webdir_conf")
174 accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
181 accesslog = openlog(ui.config("web", "accesslog", "-"), sys.stdout)
175 errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
182 errorlog = openlog(ui.config("web", "errorlog", "-"), sys.stderr)
176
183
177 if use_threads:
184 if use_threads:
178 try:
185 try:
179 from threading import activeCount
186 from threading import activeCount
180 except ImportError:
187 except ImportError:
181 use_threads = False
188 use_threads = False
182
189
183 if use_threads:
190 if use_threads:
184 _mixin = SocketServer.ThreadingMixIn
191 _mixin = SocketServer.ThreadingMixIn
185 else:
192 else:
186 if hasattr(os, "fork"):
193 if hasattr(os, "fork"):
187 _mixin = SocketServer.ForkingMixIn
194 _mixin = SocketServer.ForkingMixIn
188 else:
195 else:
189 class _mixin:
196 class _mixin:
190 pass
197 pass
191
198
192 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
199 class MercurialHTTPServer(object, _mixin, BaseHTTPServer.HTTPServer):
193 def __init__(self, *args, **kargs):
200 def __init__(self, *args, **kargs):
194 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
201 BaseHTTPServer.HTTPServer.__init__(self, *args, **kargs)
195 self.accesslog = accesslog
202 self.accesslog = accesslog
196 self.errorlog = errorlog
203 self.errorlog = errorlog
197 self.repo = repo
204 self.repo = repo
198 self.webdir_conf = webdir_conf
205 self.webdir_conf = webdir_conf
199 self.webdirmaker = hgwebdir
206 self.webdirmaker = hgwebdir
200 self.repoviewmaker = hgweb
207 self.repoviewmaker = hgweb
201 self.reqmaker = wsgiapplication(self.make_handler)
208 self.reqmaker = wsgiapplication(self.make_handler)
202 self.daemon_threads = True
209 self.daemon_threads = True
203
210
204 addr, port = self.socket.getsockname()[:2]
211 addr, port = self.socket.getsockname()[:2]
205 if addr in ('0.0.0.0', '::'):
212 if addr in ('0.0.0.0', '::'):
206 addr = socket.gethostname()
213 addr = socket.gethostname()
207 else:
214 else:
208 try:
215 try:
209 addr = socket.gethostbyaddr(addr)[0]
216 addr = socket.gethostbyaddr(addr)[0]
210 except socket.error:
217 except socket.error:
211 pass
218 pass
212 self.addr, self.port = addr, port
219 self.addr, self.port = addr, port
213
220
214 def make_handler(self):
221 def make_handler(self):
215 if self.webdir_conf:
222 if self.webdir_conf:
216 hgwebobj = self.webdirmaker(self.webdir_conf)
223 hgwebobj = self.webdirmaker(self.webdir_conf)
217 elif self.repo is not None:
224 elif self.repo is not None:
218 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
225 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
219 repo.origroot))
226 repo.origroot))
220 else:
227 else:
221 raise hg.RepoError(_("There is no Mercurial repository here"
228 raise hg.RepoError(_("There is no Mercurial repository here"
222 " (.hg not found)"))
229 " (.hg not found)"))
223 return hgwebobj
230 return hgwebobj
224
231
225 class IPv6HTTPServer(MercurialHTTPServer):
232 class IPv6HTTPServer(MercurialHTTPServer):
226 address_family = getattr(socket, 'AF_INET6', None)
233 address_family = getattr(socket, 'AF_INET6', None)
227
234
228 def __init__(self, *args, **kwargs):
235 def __init__(self, *args, **kwargs):
229 if self.address_family is None:
236 if self.address_family is None:
230 raise hg.RepoError(_('IPv6 not available on this system'))
237 raise hg.RepoError(_('IPv6 not available on this system'))
231 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
238 super(IPv6HTTPServer, self).__init__(*args, **kwargs)
232
239
233 try:
240 try:
234 if use_ipv6:
241 if use_ipv6:
235 return IPv6HTTPServer((address, port), _hgwebhandler)
242 return IPv6HTTPServer((address, port), _hgwebhandler)
236 else:
243 else:
237 return MercurialHTTPServer((address, port), _hgwebhandler)
244 return MercurialHTTPServer((address, port), _hgwebhandler)
238 except socket.error, inst:
245 except socket.error, inst:
239 raise util.Abort(_('cannot start server: %s') % inst.args[1])
246 raise util.Abort(_('cannot start server: %s') % inst.args[1])
@@ -1,384 +1,388 b''
1 # httprepo.py - HTTP repository proxy classes for mercurial
1 # httprepo.py - HTTP repository proxy classes for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 from node import *
9 from node import *
10 from remoterepo import *
10 from remoterepo import *
11 from i18n import _
11 from i18n import _
12 import hg, os, urllib, urllib2, urlparse, zlib, util, httplib
12 import hg, os, urllib, urllib2, urlparse, zlib, util, httplib
13 import errno, keepalive, tempfile, socket, changegroup
13 import errno, keepalive, tempfile, socket, changegroup
14
14
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 def __init__(self, ui):
16 def __init__(self, ui):
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 self.ui = ui
18 self.ui = ui
19
19
20 def find_user_password(self, realm, authuri):
20 def find_user_password(self, realm, authuri):
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 self, realm, authuri)
22 self, realm, authuri)
23 user, passwd = authinfo
23 user, passwd = authinfo
24 if user and passwd:
24 if user and passwd:
25 return (user, passwd)
25 return (user, passwd)
26
26
27 if not self.ui.interactive:
27 if not self.ui.interactive:
28 raise util.Abort(_('http authorization required'))
28 raise util.Abort(_('http authorization required'))
29
29
30 self.ui.write(_("http authorization required\n"))
30 self.ui.write(_("http authorization required\n"))
31 self.ui.status(_("realm: %s\n") % realm)
31 self.ui.status(_("realm: %s\n") % realm)
32 if user:
32 if user:
33 self.ui.status(_("user: %s\n") % user)
33 self.ui.status(_("user: %s\n") % user)
34 else:
34 else:
35 user = self.ui.prompt(_("user:"), default=None)
35 user = self.ui.prompt(_("user:"), default=None)
36
36
37 if not passwd:
37 if not passwd:
38 passwd = self.ui.getpass()
38 passwd = self.ui.getpass()
39
39
40 self.add_password(realm, authuri, user, passwd)
40 self.add_password(realm, authuri, user, passwd)
41 return (user, passwd)
41 return (user, passwd)
42
42
43 def netlocsplit(netloc):
43 def netlocsplit(netloc):
44 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
44 '''split [user[:passwd]@]host[:port] into 4-tuple.'''
45
45
46 a = netloc.find('@')
46 a = netloc.find('@')
47 if a == -1:
47 if a == -1:
48 user, passwd = None, None
48 user, passwd = None, None
49 else:
49 else:
50 userpass, netloc = netloc[:a], netloc[a+1:]
50 userpass, netloc = netloc[:a], netloc[a+1:]
51 c = userpass.find(':')
51 c = userpass.find(':')
52 if c == -1:
52 if c == -1:
53 user, passwd = urllib.unquote(userpass), None
53 user, passwd = urllib.unquote(userpass), None
54 else:
54 else:
55 user = urllib.unquote(userpass[:c])
55 user = urllib.unquote(userpass[:c])
56 passwd = urllib.unquote(userpass[c+1:])
56 passwd = urllib.unquote(userpass[c+1:])
57 c = netloc.find(':')
57 c = netloc.find(':')
58 if c == -1:
58 if c == -1:
59 host, port = netloc, None
59 host, port = netloc, None
60 else:
60 else:
61 host, port = netloc[:c], netloc[c+1:]
61 host, port = netloc[:c], netloc[c+1:]
62 return host, port, user, passwd
62 return host, port, user, passwd
63
63
64 def netlocunsplit(host, port, user=None, passwd=None):
64 def netlocunsplit(host, port, user=None, passwd=None):
65 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
65 '''turn host, port, user, passwd into [user[:passwd]@]host[:port].'''
66 if port:
66 if port:
67 hostport = host + ':' + port
67 hostport = host + ':' + port
68 else:
68 else:
69 hostport = host
69 hostport = host
70 if user:
70 if user:
71 if passwd:
71 if passwd:
72 userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
72 userpass = urllib.quote(user) + ':' + urllib.quote(passwd)
73 else:
73 else:
74 userpass = urllib.quote(user)
74 userpass = urllib.quote(user)
75 return userpass + '@' + hostport
75 return userpass + '@' + hostport
76 return hostport
76 return hostport
77
77
78 class httpconnection(keepalive.HTTPConnection):
78 class httpconnection(keepalive.HTTPConnection):
79 # must be able to send big bundle as stream.
79 # must be able to send big bundle as stream.
80
80
81 def send(self, data):
81 def send(self, data):
82 if isinstance(data, str):
82 if isinstance(data, str):
83 keepalive.HTTPConnection.send(self, data)
83 keepalive.HTTPConnection.send(self, data)
84 else:
84 else:
85 # if auth required, some data sent twice, so rewind here
85 # if auth required, some data sent twice, so rewind here
86 data.seek(0)
86 data.seek(0)
87 for chunk in util.filechunkiter(data):
87 for chunk in util.filechunkiter(data):
88 keepalive.HTTPConnection.send(self, chunk)
88 keepalive.HTTPConnection.send(self, chunk)
89
89
90 class basehttphandler(keepalive.HTTPHandler):
90 class basehttphandler(keepalive.HTTPHandler):
91 def http_open(self, req):
91 def http_open(self, req):
92 return self.do_open(httpconnection, req)
92 return self.do_open(httpconnection, req)
93
93
94 has_https = hasattr(urllib2, 'HTTPSHandler')
94 has_https = hasattr(urllib2, 'HTTPSHandler')
95 if has_https:
95 if has_https:
96 class httpsconnection(httplib.HTTPSConnection):
96 class httpsconnection(httplib.HTTPSConnection):
97 response_class = keepalive.HTTPResponse
97 response_class = keepalive.HTTPResponse
98 # must be able to send big bundle as stream.
98 # must be able to send big bundle as stream.
99
99
100 def send(self, data):
100 def send(self, data):
101 if isinstance(data, str):
101 if isinstance(data, str):
102 httplib.HTTPSConnection.send(self, data)
102 httplib.HTTPSConnection.send(self, data)
103 else:
103 else:
104 # if auth required, some data sent twice, so rewind here
104 # if auth required, some data sent twice, so rewind here
105 data.seek(0)
105 data.seek(0)
106 for chunk in util.filechunkiter(data):
106 for chunk in util.filechunkiter(data):
107 httplib.HTTPSConnection.send(self, chunk)
107 httplib.HTTPSConnection.send(self, chunk)
108
108
109 class httphandler(basehttphandler, urllib2.HTTPSHandler):
109 class httphandler(basehttphandler, urllib2.HTTPSHandler):
110 def https_open(self, req):
110 def https_open(self, req):
111 return self.do_open(httpsconnection, req)
111 return self.do_open(httpsconnection, req)
112 else:
112 else:
113 class httphandler(basehttphandler):
113 class httphandler(basehttphandler):
114 pass
114 pass
115
115
116 def zgenerator(f):
116 def zgenerator(f):
117 zd = zlib.decompressobj()
117 zd = zlib.decompressobj()
118 try:
118 try:
119 for chunk in util.filechunkiter(f):
119 for chunk in util.filechunkiter(f):
120 yield zd.decompress(chunk)
120 yield zd.decompress(chunk)
121 except httplib.HTTPException, inst:
121 except httplib.HTTPException, inst:
122 raise IOError(None, _('connection ended unexpectedly'))
122 raise IOError(None, _('connection ended unexpectedly'))
123 yield zd.flush()
123 yield zd.flush()
124
124
125 class httprepository(remoterepository):
125 class httprepository(remoterepository):
126 def __init__(self, ui, path):
126 def __init__(self, ui, path):
127 self.path = path
127 self.path = path
128 self.caps = None
128 self.caps = None
129 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
129 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path)
130 if query or frag:
130 if query or frag:
131 raise util.Abort(_('unsupported URL component: "%s"') %
131 raise util.Abort(_('unsupported URL component: "%s"') %
132 (query or frag))
132 (query or frag))
133 if not urlpath: urlpath = '/'
133 if not urlpath: urlpath = '/'
134 host, port, user, passwd = netlocsplit(netloc)
134 host, port, user, passwd = netlocsplit(netloc)
135
135
136 # urllib cannot handle URLs with embedded user or passwd
136 # urllib cannot handle URLs with embedded user or passwd
137 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
137 self._url = urlparse.urlunsplit((scheme, netlocunsplit(host, port),
138 urlpath, '', ''))
138 urlpath, '', ''))
139 self.ui = ui
139 self.ui = ui
140
140
141 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
141 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
142 # XXX proxyauthinfo = None
142 # XXX proxyauthinfo = None
143 handlers = [httphandler()]
143 handlers = [httphandler()]
144
144
145 if proxyurl:
145 if proxyurl:
146 # proxy can be proper url or host[:port]
146 # proxy can be proper url or host[:port]
147 if not (proxyurl.startswith('http:') or
147 if not (proxyurl.startswith('http:') or
148 proxyurl.startswith('https:')):
148 proxyurl.startswith('https:')):
149 proxyurl = 'http://' + proxyurl + '/'
149 proxyurl = 'http://' + proxyurl + '/'
150 snpqf = urlparse.urlsplit(proxyurl)
150 snpqf = urlparse.urlsplit(proxyurl)
151 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
151 proxyscheme, proxynetloc, proxypath, proxyquery, proxyfrag = snpqf
152 hpup = netlocsplit(proxynetloc)
152 hpup = netlocsplit(proxynetloc)
153
153
154 proxyhost, proxyport, proxyuser, proxypasswd = hpup
154 proxyhost, proxyport, proxyuser, proxypasswd = hpup
155 if not proxyuser:
155 if not proxyuser:
156 proxyuser = ui.config("http_proxy", "user")
156 proxyuser = ui.config("http_proxy", "user")
157 proxypasswd = ui.config("http_proxy", "passwd")
157 proxypasswd = ui.config("http_proxy", "passwd")
158
158
159 # see if we should use a proxy for this url
159 # see if we should use a proxy for this url
160 no_list = [ "localhost", "127.0.0.1" ]
160 no_list = [ "localhost", "127.0.0.1" ]
161 no_list.extend([p.lower() for
161 no_list.extend([p.lower() for
162 p in ui.configlist("http_proxy", "no")])
162 p in ui.configlist("http_proxy", "no")])
163 no_list.extend([p.strip().lower() for
163 no_list.extend([p.strip().lower() for
164 p in os.getenv("no_proxy", '').split(',')
164 p in os.getenv("no_proxy", '').split(',')
165 if p.strip()])
165 if p.strip()])
166 # "http_proxy.always" config is for running tests on localhost
166 # "http_proxy.always" config is for running tests on localhost
167 if (not ui.configbool("http_proxy", "always") and
167 if (not ui.configbool("http_proxy", "always") and
168 host.lower() in no_list):
168 host.lower() in no_list):
169 ui.debug(_('disabling proxy for %s\n') % host)
169 ui.debug(_('disabling proxy for %s\n') % host)
170 else:
170 else:
171 proxyurl = urlparse.urlunsplit((
171 proxyurl = urlparse.urlunsplit((
172 proxyscheme, netlocunsplit(proxyhost, proxyport,
172 proxyscheme, netlocunsplit(proxyhost, proxyport,
173 proxyuser, proxypasswd or ''),
173 proxyuser, proxypasswd or ''),
174 proxypath, proxyquery, proxyfrag))
174 proxypath, proxyquery, proxyfrag))
175 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
175 handlers.append(urllib2.ProxyHandler({scheme: proxyurl}))
176 ui.debug(_('proxying through http://%s:%s\n') %
176 ui.debug(_('proxying through http://%s:%s\n') %
177 (proxyhost, proxyport))
177 (proxyhost, proxyport))
178
178
179 # urllib2 takes proxy values from the environment and those
179 # urllib2 takes proxy values from the environment and those
180 # will take precedence if found, so drop them
180 # will take precedence if found, so drop them
181 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
181 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
182 try:
182 try:
183 if os.environ.has_key(env):
183 if os.environ.has_key(env):
184 del os.environ[env]
184 del os.environ[env]
185 except OSError:
185 except OSError:
186 pass
186 pass
187
187
188 passmgr = passwordmgr(ui)
188 passmgr = passwordmgr(ui)
189 if user:
189 if user:
190 ui.debug(_('http auth: user %s, password %s\n') %
190 ui.debug(_('http auth: user %s, password %s\n') %
191 (user, passwd and '*' * len(passwd) or 'not set'))
191 (user, passwd and '*' * len(passwd) or 'not set'))
192 passmgr.add_password(None, host, user, passwd or '')
192 passmgr.add_password(None, host, user, passwd or '')
193
193
194 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
194 handlers.extend((urllib2.HTTPBasicAuthHandler(passmgr),
195 urllib2.HTTPDigestAuthHandler(passmgr)))
195 urllib2.HTTPDigestAuthHandler(passmgr)))
196 opener = urllib2.build_opener(*handlers)
196 opener = urllib2.build_opener(*handlers)
197
197
198 # 1.0 here is the _protocol_ version
198 # 1.0 here is the _protocol_ version
199 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
199 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
200 urllib2.install_opener(opener)
200 urllib2.install_opener(opener)
201
201
202 def url(self):
202 def url(self):
203 return self.path
203 return self.path
204
204
205 # look up capabilities only when needed
205 # look up capabilities only when needed
206
206
207 def get_caps(self):
207 def get_caps(self):
208 if self.caps is None:
208 if self.caps is None:
209 try:
209 try:
210 self.caps = self.do_read('capabilities').split()
210 self.caps = self.do_read('capabilities').split()
211 except hg.RepoError:
211 except hg.RepoError:
212 self.caps = ()
212 self.caps = ()
213 self.ui.debug(_('capabilities: %s\n') %
213 self.ui.debug(_('capabilities: %s\n') %
214 (' '.join(self.caps or ['none'])))
214 (' '.join(self.caps or ['none'])))
215 return self.caps
215 return self.caps
216
216
217 capabilities = property(get_caps)
217 capabilities = property(get_caps)
218
218
219 def lock(self):
219 def lock(self):
220 raise util.Abort(_('operation not supported over http'))
220 raise util.Abort(_('operation not supported over http'))
221
221
222 def do_cmd(self, cmd, **args):
222 def do_cmd(self, cmd, **args):
223 data = args.pop('data', None)
223 data = args.pop('data', None)
224 headers = args.pop('headers', {})
224 headers = args.pop('headers', {})
225 self.ui.debug(_("sending %s command\n") % cmd)
225 self.ui.debug(_("sending %s command\n") % cmd)
226 q = {"cmd": cmd}
226 q = {"cmd": cmd}
227 q.update(args)
227 q.update(args)
228 qs = '?%s' % urllib.urlencode(q)
228 qs = '?%s' % urllib.urlencode(q)
229 cu = "%s%s" % (self._url, qs)
229 cu = "%s%s" % (self._url, qs)
230 try:
230 try:
231 if data:
231 if data:
232 self.ui.debug(_("sending %s bytes\n") %
232 self.ui.debug(_("sending %s bytes\n") %
233 headers.get('content-length', 'X'))
233 headers.get('content-length', 'X'))
234 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
234 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
235 except urllib2.HTTPError, inst:
235 except urllib2.HTTPError, inst:
236 if inst.code == 401:
236 if inst.code == 401:
237 raise util.Abort(_('authorization failed'))
237 raise util.Abort(_('authorization failed'))
238 raise
238 raise
239 except httplib.HTTPException, inst:
239 except httplib.HTTPException, inst:
240 self.ui.debug(_('http error while sending %s command\n') % cmd)
240 self.ui.debug(_('http error while sending %s command\n') % cmd)
241 self.ui.print_exc()
241 self.ui.print_exc()
242 raise IOError(None, inst)
242 raise IOError(None, inst)
243 except IndexError:
243 except IndexError:
244 # this only happens with Python 2.3, later versions raise URLError
244 # this only happens with Python 2.3, later versions raise URLError
245 raise util.Abort(_('http error, possibly caused by proxy setting'))
245 raise util.Abort(_('http error, possibly caused by proxy setting'))
246 # record the url we got redirected to
246 # record the url we got redirected to
247 resp_url = resp.geturl()
247 resp_url = resp.geturl()
248 if resp_url.endswith(qs):
248 if resp_url.endswith(qs):
249 resp_url = resp_url[:-len(qs)]
249 resp_url = resp_url[:-len(qs)]
250 if self._url != resp_url:
250 if self._url != resp_url:
251 self.ui.status(_('real URL is %s\n') % resp_url)
251 self.ui.status(_('real URL is %s\n') % resp_url)
252 self._url = resp_url
252 self._url = resp_url
253 try:
253 try:
254 proto = resp.getheader('content-type')
254 proto = resp.getheader('content-type')
255 except AttributeError:
255 except AttributeError:
256 proto = resp.headers['content-type']
256 proto = resp.headers['content-type']
257
257
258 # accept old "text/plain" and "application/hg-changegroup" for now
258 # accept old "text/plain" and "application/hg-changegroup" for now
259 if not proto.startswith('application/mercurial') and \
259 if not proto.startswith('application/mercurial-') and \
260 not proto.startswith('text/plain') and \
260 not proto.startswith('text/plain') and \
261 not proto.startswith('application/hg-changegroup'):
261 not proto.startswith('application/hg-changegroup'):
262 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
262 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
263 self._url)
263 self._url)
264
264
265 if proto.startswith('application/mercurial'):
265 if proto.startswith('application/mercurial-'):
266 version = proto[22:]
266 try:
267 if float(version) > 0.1:
267 version = float(proto[22:])
268 except ValueError:
269 raise hg.RepoError(_("'%s' sent a broken Content-type "
270 "header (%s)") % (self._url, proto))
271 if version > 0.1:
268 raise hg.RepoError(_("'%s' uses newer protocol %s") %
272 raise hg.RepoError(_("'%s' uses newer protocol %s") %
269 (self._url, version))
273 (self._url, version))
270
274
271 return resp
275 return resp
272
276
273 def do_read(self, cmd, **args):
277 def do_read(self, cmd, **args):
274 fp = self.do_cmd(cmd, **args)
278 fp = self.do_cmd(cmd, **args)
275 try:
279 try:
276 return fp.read()
280 return fp.read()
277 finally:
281 finally:
278 # if using keepalive, allow connection to be reused
282 # if using keepalive, allow connection to be reused
279 fp.close()
283 fp.close()
280
284
281 def lookup(self, key):
285 def lookup(self, key):
282 d = self.do_cmd("lookup", key = key).read()
286 d = self.do_cmd("lookup", key = key).read()
283 success, data = d[:-1].split(' ', 1)
287 success, data = d[:-1].split(' ', 1)
284 if int(success):
288 if int(success):
285 return bin(data)
289 return bin(data)
286 raise hg.RepoError(data)
290 raise hg.RepoError(data)
287
291
288 def heads(self):
292 def heads(self):
289 d = self.do_read("heads")
293 d = self.do_read("heads")
290 try:
294 try:
291 return map(bin, d[:-1].split(" "))
295 return map(bin, d[:-1].split(" "))
292 except:
296 except:
293 raise util.UnexpectedOutput(_("unexpected response:"), d)
297 raise util.UnexpectedOutput(_("unexpected response:"), d)
294
298
295 def branches(self, nodes):
299 def branches(self, nodes):
296 n = " ".join(map(hex, nodes))
300 n = " ".join(map(hex, nodes))
297 d = self.do_read("branches", nodes=n)
301 d = self.do_read("branches", nodes=n)
298 try:
302 try:
299 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
303 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
300 return br
304 return br
301 except:
305 except:
302 raise util.UnexpectedOutput(_("unexpected response:"), d)
306 raise util.UnexpectedOutput(_("unexpected response:"), d)
303
307
304 def between(self, pairs):
308 def between(self, pairs):
305 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
309 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
306 d = self.do_read("between", pairs=n)
310 d = self.do_read("between", pairs=n)
307 try:
311 try:
308 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
312 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
309 return p
313 return p
310 except:
314 except:
311 raise util.UnexpectedOutput(_("unexpected response:"), d)
315 raise util.UnexpectedOutput(_("unexpected response:"), d)
312
316
313 def changegroup(self, nodes, kind):
317 def changegroup(self, nodes, kind):
314 n = " ".join(map(hex, nodes))
318 n = " ".join(map(hex, nodes))
315 f = self.do_cmd("changegroup", roots=n)
319 f = self.do_cmd("changegroup", roots=n)
316 return util.chunkbuffer(zgenerator(f))
320 return util.chunkbuffer(zgenerator(f))
317
321
318 def changegroupsubset(self, bases, heads, source):
322 def changegroupsubset(self, bases, heads, source):
319 baselst = " ".join([hex(n) for n in bases])
323 baselst = " ".join([hex(n) for n in bases])
320 headlst = " ".join([hex(n) for n in heads])
324 headlst = " ".join([hex(n) for n in heads])
321 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
325 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst)
322 return util.chunkbuffer(zgenerator(f))
326 return util.chunkbuffer(zgenerator(f))
323
327
324 def unbundle(self, cg, heads, source):
328 def unbundle(self, cg, heads, source):
325 # have to stream bundle to a temp file because we do not have
329 # have to stream bundle to a temp file because we do not have
326 # http 1.1 chunked transfer.
330 # http 1.1 chunked transfer.
327
331
328 type = ""
332 type = ""
329 types = self.capable('unbundle')
333 types = self.capable('unbundle')
330 # servers older than d1b16a746db6 will send 'unbundle' as a
334 # servers older than d1b16a746db6 will send 'unbundle' as a
331 # boolean capability
335 # boolean capability
332 try:
336 try:
333 types = types.split(',')
337 types = types.split(',')
334 except AttributeError:
338 except AttributeError:
335 types = [""]
339 types = [""]
336 if types:
340 if types:
337 for x in types:
341 for x in types:
338 if x in changegroup.bundletypes:
342 if x in changegroup.bundletypes:
339 type = x
343 type = x
340 break
344 break
341
345
342 tempname = changegroup.writebundle(cg, None, type)
346 tempname = changegroup.writebundle(cg, None, type)
343 fp = file(tempname, "rb")
347 fp = file(tempname, "rb")
344 try:
348 try:
345 length = os.stat(tempname).st_size
349 length = os.stat(tempname).st_size
346 try:
350 try:
347 rfp = self.do_cmd(
351 rfp = self.do_cmd(
348 'unbundle', data=fp,
352 'unbundle', data=fp,
349 headers={'content-length': str(length),
353 headers={'content-length': str(length),
350 'content-type': 'application/octet-stream'},
354 'content-type': 'application/octet-stream'},
351 heads=' '.join(map(hex, heads)))
355 heads=' '.join(map(hex, heads)))
352 try:
356 try:
353 ret = int(rfp.readline())
357 ret = int(rfp.readline())
354 self.ui.write(rfp.read())
358 self.ui.write(rfp.read())
355 return ret
359 return ret
356 finally:
360 finally:
357 rfp.close()
361 rfp.close()
358 except socket.error, err:
362 except socket.error, err:
359 if err[0] in (errno.ECONNRESET, errno.EPIPE):
363 if err[0] in (errno.ECONNRESET, errno.EPIPE):
360 raise util.Abort(_('push failed: %s') % err[1])
364 raise util.Abort(_('push failed: %s') % err[1])
361 raise util.Abort(err[1])
365 raise util.Abort(err[1])
362 finally:
366 finally:
363 fp.close()
367 fp.close()
364 os.unlink(tempname)
368 os.unlink(tempname)
365
369
366 def stream_out(self):
370 def stream_out(self):
367 return self.do_cmd('stream_out')
371 return self.do_cmd('stream_out')
368
372
369 class httpsrepository(httprepository):
373 class httpsrepository(httprepository):
370 def __init__(self, ui, path):
374 def __init__(self, ui, path):
371 if not has_https:
375 if not has_https:
372 raise util.Abort(_('Python support for SSL and HTTPS '
376 raise util.Abort(_('Python support for SSL and HTTPS '
373 'is not installed'))
377 'is not installed'))
374 httprepository.__init__(self, ui, path)
378 httprepository.__init__(self, ui, path)
375
379
376 def instance(ui, path, create):
380 def instance(ui, path, create):
377 if create:
381 if create:
378 raise util.Abort(_('cannot create new http repository'))
382 raise util.Abort(_('cannot create new http repository'))
379 if path.startswith('hg:'):
383 if path.startswith('hg:'):
380 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
384 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
381 path = 'http:' + path[3:]
385 path = 'http:' + path[3:]
382 if path.startswith('https:'):
386 if path.startswith('https:'):
383 return httpsrepository(ui, path)
387 return httpsrepository(ui, path)
384 return httprepository(ui, path)
388 return httprepository(ui, path)
@@ -1,216 +1,216 b''
1 adding a
1 adding a
2 adding b
2 adding b
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 pulling from ../b
4 pulling from ../b
5 searching for changes
5 searching for changes
6 warning: repository is unrelated
6 warning: repository is unrelated
7 adding changesets
7 adding changesets
8 adding manifests
8 adding manifests
9 adding file changes
9 adding file changes
10 added 1 changesets with 1 changes to 1 files (+1 heads)
10 added 1 changesets with 1 changes to 1 files (+1 heads)
11 (run 'hg heads' to see heads, 'hg merge' to merge)
11 (run 'hg heads' to see heads, 'hg merge' to merge)
12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
13 (branch merge, don't forget to commit)
13 (branch merge, don't forget to commit)
14 %% -R/--repository
14 %% -R/--repository
15 changeset: 0:8580ff50825a
15 changeset: 0:8580ff50825a
16 tag: tip
16 tag: tip
17 user: test
17 user: test
18 date: Thu Jan 01 00:00:01 1970 +0000
18 date: Thu Jan 01 00:00:01 1970 +0000
19 summary: a
19 summary: a
20
20
21 changeset: 0:b6c483daf290
21 changeset: 0:b6c483daf290
22 tag: tip
22 tag: tip
23 user: test
23 user: test
24 date: Thu Jan 01 00:00:01 1970 +0000
24 date: Thu Jan 01 00:00:01 1970 +0000
25 summary: b
25 summary: b
26
26
27 %% abbrev of long option
27 %% abbrev of long option
28 changeset: 1:b6c483daf290
28 changeset: 1:b6c483daf290
29 tag: tip
29 tag: tip
30 parent: -1:000000000000
30 parent: -1:000000000000
31 user: test
31 user: test
32 date: Thu Jan 01 00:00:01 1970 +0000
32 date: Thu Jan 01 00:00:01 1970 +0000
33 summary: b
33 summary: b
34
34
35 %% --cwd
35 %% --cwd
36 changeset: 0:8580ff50825a
36 changeset: 0:8580ff50825a
37 tag: tip
37 tag: tip
38 user: test
38 user: test
39 date: Thu Jan 01 00:00:01 1970 +0000
39 date: Thu Jan 01 00:00:01 1970 +0000
40 summary: a
40 summary: a
41
41
42 %% -y/--noninteractive - just be sure it is parsed
42 %% -y/--noninteractive - just be sure it is parsed
43 0:8580ff50825a
43 0:8580ff50825a
44 0:8580ff50825a
44 0:8580ff50825a
45 %% -q/--quiet
45 %% -q/--quiet
46 0:8580ff50825a
46 0:8580ff50825a
47 0:b6c483daf290
47 0:b6c483daf290
48 0:8580ff50825a
48 0:8580ff50825a
49 1:b6c483daf290
49 1:b6c483daf290
50 %% -v/--verbose
50 %% -v/--verbose
51 changeset: 1:b6c483daf290
51 changeset: 1:b6c483daf290
52 tag: tip
52 tag: tip
53 parent: -1:000000000000
53 parent: -1:000000000000
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:01 1970 +0000
55 date: Thu Jan 01 00:00:01 1970 +0000
56 files: b
56 files: b
57 description:
57 description:
58 b
58 b
59
59
60
60
61 changeset: 0:8580ff50825a
61 changeset: 0:8580ff50825a
62 user: test
62 user: test
63 date: Thu Jan 01 00:00:01 1970 +0000
63 date: Thu Jan 01 00:00:01 1970 +0000
64 files: a
64 files: a
65 description:
65 description:
66 a
66 a
67
67
68
68
69 changeset: 0:b6c483daf290
69 changeset: 0:b6c483daf290
70 tag: tip
70 tag: tip
71 user: test
71 user: test
72 date: Thu Jan 01 00:00:01 1970 +0000
72 date: Thu Jan 01 00:00:01 1970 +0000
73 files: b
73 files: b
74 description:
74 description:
75 b
75 b
76
76
77
77
78 %% --config
78 %% --config
79 quuxfoo
79 quuxfoo
80 abort: malformed --config option:
80 abort: malformed --config option:
81 abort: malformed --config option: a.b
81 abort: malformed --config option: a.b
82 abort: malformed --config option: a
82 abort: malformed --config option: a
83 abort: malformed --config option: a.=
83 abort: malformed --config option: a.=
84 abort: malformed --config option: .b=
84 abort: malformed --config option: .b=
85 %% --debug
85 %% --debug
86 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
86 changeset: 1:b6c483daf2907ce5825c0bb50f5716226281cc1a
87 tag: tip
87 tag: tip
88 parent: -1:0000000000000000000000000000000000000000
88 parent: -1:0000000000000000000000000000000000000000
89 parent: -1:0000000000000000000000000000000000000000
89 parent: -1:0000000000000000000000000000000000000000
90 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
90 manifest: 1:23226e7a252cacdc2d99e4fbdc3653441056de49
91 user: test
91 user: test
92 date: Thu Jan 01 00:00:01 1970 +0000
92 date: Thu Jan 01 00:00:01 1970 +0000
93 files+: b
93 files+: b
94 description:
94 description:
95 b
95 b
96
96
97
97
98 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
98 changeset: 0:8580ff50825a50c8f716709acdf8de0deddcd6ab
99 parent: -1:0000000000000000000000000000000000000000
99 parent: -1:0000000000000000000000000000000000000000
100 parent: -1:0000000000000000000000000000000000000000
100 parent: -1:0000000000000000000000000000000000000000
101 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
101 manifest: 0:a0c8bcbbb45c63b90b70ad007bf38961f64f2af0
102 user: test
102 user: test
103 date: Thu Jan 01 00:00:01 1970 +0000
103 date: Thu Jan 01 00:00:01 1970 +0000
104 files+: a
104 files+: a
105 description:
105 description:
106 a
106 a
107
107
108
108
109 %% --traceback
109 %% --traceback
110 Traceback (most recent call last):
110 Traceback (most recent call last):
111 %% --time
111 %% --time
112 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
112 Time: real x.x secs (user x.x+x.x sys x.x+x.x)
113 %% --version
113 %% --version
114 Mercurial Distributed SCM (version xxx)
114 Mercurial Distributed SCM (version xxx)
115 %% -h/--help
115 %% -h/--help
116 Mercurial Distributed SCM
116 Mercurial Distributed SCM
117
117
118 list of commands (use "hg help -v" to show aliases and global options):
118 list of commands (use "hg help -v" to show aliases and global options):
119
119
120 add add the specified files on the next commit
120 add add the specified files on the next commit
121 addremove add all new files, delete all missing files
121 addremove add all new files, delete all missing files
122 annotate show changeset information per file line
122 annotate show changeset information per file line
123 archive create unversioned archive of a repository revision
123 archive create unversioned archive of a repository revision
124 backout reverse effect of earlier changeset
124 backout reverse effect of earlier changeset
125 branch set or show the current branch name
125 branch set or show the current branch name
126 branches list repository named branches
126 branches list repository named branches
127 bundle create a changegroup file
127 bundle create a changegroup file
128 cat output the current or given revision of files
128 cat output the current or given revision of files
129 clone make a copy of an existing repository
129 clone make a copy of an existing repository
130 commit commit the specified files or all outstanding changes
130 commit commit the specified files or all outstanding changes
131 copy mark files as copied for the next commit
131 copy mark files as copied for the next commit
132 diff diff repository (or selected files)
132 diff diff repository (or selected files)
133 export dump the header and diffs for one or more changesets
133 export dump the header and diffs for one or more changesets
134 grep search for a pattern in specified files and revisions
134 grep search for a pattern in specified files and revisions
135 heads show current repository heads
135 heads show current repository heads
136 help show help for a command, extension, or list of commands
136 help show help for a command, extension, or list of commands
137 identify print information about the working copy
137 identify print information about the working copy
138 import import an ordered set of patches
138 import import an ordered set of patches
139 incoming show new changesets found in source
139 incoming show new changesets found in source
140 init create a new repository in the given directory
140 init create a new repository in the given directory
141 locate locate files matching specific patterns
141 locate locate files matching specific patterns
142 log show revision history of entire repository or files
142 log show revision history of entire repository or files
143 manifest output the current or given revision of the project manifest
143 manifest output the current or given revision of the project manifest
144 merge Merge working directory with another revision
144 merge merge working directory with another revision
145 outgoing show changesets not found in destination
145 outgoing show changesets not found in destination
146 parents show the parents of the working dir or revision
146 parents show the parents of the working dir or revision
147 paths show definition of symbolic path names
147 paths show definition of symbolic path names
148 pull pull changes from the specified source
148 pull pull changes from the specified source
149 push push changes to the specified destination
149 push push changes to the specified destination
150 recover roll back an interrupted transaction
150 recover roll back an interrupted transaction
151 remove remove the specified files on the next commit
151 remove remove the specified files on the next commit
152 rename rename files; equivalent of copy + remove
152 rename rename files; equivalent of copy + remove
153 revert revert files or dirs to their states as of some revision
153 revert revert files or dirs to their states as of some revision
154 rollback roll back the last transaction in this repository
154 rollback roll back the last transaction in this repository
155 root print the root (top) of the current working dir
155 root print the root (top) of the current working dir
156 serve export the repository via HTTP
156 serve export the repository via HTTP
157 showconfig show combined config settings from all hgrc files
157 showconfig show combined config settings from all hgrc files
158 status show changed files in the working directory
158 status show changed files in the working directory
159 tag add a tag for the current or given revision
159 tag add a tag for the current or given revision
160 tags list repository tags
160 tags list repository tags
161 tip show the tip revision
161 tip show the tip revision
162 unbundle apply a changegroup file
162 unbundle apply a changegroup file
163 update update or merge working directory
163 update update working directory
164 verify verify the integrity of the repository
164 verify verify the integrity of the repository
165 version output version and copyright information
165 version output version and copyright information
166 Mercurial Distributed SCM
166 Mercurial Distributed SCM
167
167
168 list of commands (use "hg help -v" to show aliases and global options):
168 list of commands (use "hg help -v" to show aliases and global options):
169
169
170 add add the specified files on the next commit
170 add add the specified files on the next commit
171 addremove add all new files, delete all missing files
171 addremove add all new files, delete all missing files
172 annotate show changeset information per file line
172 annotate show changeset information per file line
173 archive create unversioned archive of a repository revision
173 archive create unversioned archive of a repository revision
174 backout reverse effect of earlier changeset
174 backout reverse effect of earlier changeset
175 branch set or show the current branch name
175 branch set or show the current branch name
176 branches list repository named branches
176 branches list repository named branches
177 bundle create a changegroup file
177 bundle create a changegroup file
178 cat output the current or given revision of files
178 cat output the current or given revision of files
179 clone make a copy of an existing repository
179 clone make a copy of an existing repository
180 commit commit the specified files or all outstanding changes
180 commit commit the specified files or all outstanding changes
181 copy mark files as copied for the next commit
181 copy mark files as copied for the next commit
182 diff diff repository (or selected files)
182 diff diff repository (or selected files)
183 export dump the header and diffs for one or more changesets
183 export dump the header and diffs for one or more changesets
184 grep search for a pattern in specified files and revisions
184 grep search for a pattern in specified files and revisions
185 heads show current repository heads
185 heads show current repository heads
186 help show help for a command, extension, or list of commands
186 help show help for a command, extension, or list of commands
187 identify print information about the working copy
187 identify print information about the working copy
188 import import an ordered set of patches
188 import import an ordered set of patches
189 incoming show new changesets found in source
189 incoming show new changesets found in source
190 init create a new repository in the given directory
190 init create a new repository in the given directory
191 locate locate files matching specific patterns
191 locate locate files matching specific patterns
192 log show revision history of entire repository or files
192 log show revision history of entire repository or files
193 manifest output the current or given revision of the project manifest
193 manifest output the current or given revision of the project manifest
194 merge Merge working directory with another revision
194 merge merge working directory with another revision
195 outgoing show changesets not found in destination
195 outgoing show changesets not found in destination
196 parents show the parents of the working dir or revision
196 parents show the parents of the working dir or revision
197 paths show definition of symbolic path names
197 paths show definition of symbolic path names
198 pull pull changes from the specified source
198 pull pull changes from the specified source
199 push push changes to the specified destination
199 push push changes to the specified destination
200 recover roll back an interrupted transaction
200 recover roll back an interrupted transaction
201 remove remove the specified files on the next commit
201 remove remove the specified files on the next commit
202 rename rename files; equivalent of copy + remove
202 rename rename files; equivalent of copy + remove
203 revert revert files or dirs to their states as of some revision
203 revert revert files or dirs to their states as of some revision
204 rollback roll back the last transaction in this repository
204 rollback roll back the last transaction in this repository
205 root print the root (top) of the current working dir
205 root print the root (top) of the current working dir
206 serve export the repository via HTTP
206 serve export the repository via HTTP
207 showconfig show combined config settings from all hgrc files
207 showconfig show combined config settings from all hgrc files
208 status show changed files in the working directory
208 status show changed files in the working directory
209 tag add a tag for the current or given revision
209 tag add a tag for the current or given revision
210 tags list repository tags
210 tags list repository tags
211 tip show the tip revision
211 tip show the tip revision
212 unbundle apply a changegroup file
212 unbundle apply a changegroup file
213 update update or merge working directory
213 update update working directory
214 verify verify the integrity of the repository
214 verify verify the integrity of the repository
215 version output version and copyright information
215 version output version and copyright information
216 %% not tested: --debugger
216 %% not tested: --debugger
@@ -1,288 +1,292 b''
1 Mercurial Distributed SCM
1 Mercurial Distributed SCM
2
2
3 basic commands (use "hg help" for the full list or option "-v" for details):
3 basic commands (use "hg help" for the full list or option "-v" for details):
4
4
5 add add the specified files on the next commit
5 add add the specified files on the next commit
6 annotate show changeset information per file line
6 annotate show changeset information per file line
7 clone make a copy of an existing repository
7 clone make a copy of an existing repository
8 commit commit the specified files or all outstanding changes
8 commit commit the specified files or all outstanding changes
9 diff diff repository (or selected files)
9 diff diff repository (or selected files)
10 export dump the header and diffs for one or more changesets
10 export dump the header and diffs for one or more changesets
11 init create a new repository in the given directory
11 init create a new repository in the given directory
12 log show revision history of entire repository or files
12 log show revision history of entire repository or files
13 merge merge working directory with another revision
13 parents show the parents of the working dir or revision
14 parents show the parents of the working dir or revision
14 pull pull changes from the specified source
15 pull pull changes from the specified source
15 push push changes to the specified destination
16 push push changes to the specified destination
16 remove remove the specified files on the next commit
17 remove remove the specified files on the next commit
17 revert revert files or dirs to their states as of some revision
18 revert revert files or dirs to their states as of some revision
18 serve export the repository via HTTP
19 serve export the repository via HTTP
19 status show changed files in the working directory
20 status show changed files in the working directory
20 update update or merge working directory
21 update update working directory
21 add add the specified files on the next commit
22 add add the specified files on the next commit
22 annotate show changeset information per file line
23 annotate show changeset information per file line
23 clone make a copy of an existing repository
24 clone make a copy of an existing repository
24 commit commit the specified files or all outstanding changes
25 commit commit the specified files or all outstanding changes
25 diff diff repository (or selected files)
26 diff diff repository (or selected files)
26 export dump the header and diffs for one or more changesets
27 export dump the header and diffs for one or more changesets
27 init create a new repository in the given directory
28 init create a new repository in the given directory
28 log show revision history of entire repository or files
29 log show revision history of entire repository or files
30 merge merge working directory with another revision
29 parents show the parents of the working dir or revision
31 parents show the parents of the working dir or revision
30 pull pull changes from the specified source
32 pull pull changes from the specified source
31 push push changes to the specified destination
33 push push changes to the specified destination
32 remove remove the specified files on the next commit
34 remove remove the specified files on the next commit
33 revert revert files or dirs to their states as of some revision
35 revert revert files or dirs to their states as of some revision
34 serve export the repository via HTTP
36 serve export the repository via HTTP
35 status show changed files in the working directory
37 status show changed files in the working directory
36 update update or merge working directory
38 update update working directory
37 Mercurial Distributed SCM
39 Mercurial Distributed SCM
38
40
39 list of commands (use "hg help -v" to show aliases and global options):
41 list of commands (use "hg help -v" to show aliases and global options):
40
42
41 add add the specified files on the next commit
43 add add the specified files on the next commit
42 addremove add all new files, delete all missing files
44 addremove add all new files, delete all missing files
43 annotate show changeset information per file line
45 annotate show changeset information per file line
44 archive create unversioned archive of a repository revision
46 archive create unversioned archive of a repository revision
45 backout reverse effect of earlier changeset
47 backout reverse effect of earlier changeset
46 branch set or show the current branch name
48 branch set or show the current branch name
47 branches list repository named branches
49 branches list repository named branches
48 bundle create a changegroup file
50 bundle create a changegroup file
49 cat output the current or given revision of files
51 cat output the current or given revision of files
50 clone make a copy of an existing repository
52 clone make a copy of an existing repository
51 commit commit the specified files or all outstanding changes
53 commit commit the specified files or all outstanding changes
52 copy mark files as copied for the next commit
54 copy mark files as copied for the next commit
53 diff diff repository (or selected files)
55 diff diff repository (or selected files)
54 export dump the header and diffs for one or more changesets
56 export dump the header and diffs for one or more changesets
55 grep search for a pattern in specified files and revisions
57 grep search for a pattern in specified files and revisions
56 heads show current repository heads
58 heads show current repository heads
57 help show help for a command, extension, or list of commands
59 help show help for a command, extension, or list of commands
58 identify print information about the working copy
60 identify print information about the working copy
59 import import an ordered set of patches
61 import import an ordered set of patches
60 incoming show new changesets found in source
62 incoming show new changesets found in source
61 init create a new repository in the given directory
63 init create a new repository in the given directory
62 locate locate files matching specific patterns
64 locate locate files matching specific patterns
63 log show revision history of entire repository or files
65 log show revision history of entire repository or files
64 manifest output the current or given revision of the project manifest
66 manifest output the current or given revision of the project manifest
65 merge Merge working directory with another revision
67 merge merge working directory with another revision
66 outgoing show changesets not found in destination
68 outgoing show changesets not found in destination
67 parents show the parents of the working dir or revision
69 parents show the parents of the working dir or revision
68 paths show definition of symbolic path names
70 paths show definition of symbolic path names
69 pull pull changes from the specified source
71 pull pull changes from the specified source
70 push push changes to the specified destination
72 push push changes to the specified destination
71 recover roll back an interrupted transaction
73 recover roll back an interrupted transaction
72 remove remove the specified files on the next commit
74 remove remove the specified files on the next commit
73 rename rename files; equivalent of copy + remove
75 rename rename files; equivalent of copy + remove
74 revert revert files or dirs to their states as of some revision
76 revert revert files or dirs to their states as of some revision
75 rollback roll back the last transaction in this repository
77 rollback roll back the last transaction in this repository
76 root print the root (top) of the current working dir
78 root print the root (top) of the current working dir
77 serve export the repository via HTTP
79 serve export the repository via HTTP
78 showconfig show combined config settings from all hgrc files
80 showconfig show combined config settings from all hgrc files
79 status show changed files in the working directory
81 status show changed files in the working directory
80 tag add a tag for the current or given revision
82 tag add a tag for the current or given revision
81 tags list repository tags
83 tags list repository tags
82 tip show the tip revision
84 tip show the tip revision
83 unbundle apply a changegroup file
85 unbundle apply a changegroup file
84 update update or merge working directory
86 update update working directory
85 verify verify the integrity of the repository
87 verify verify the integrity of the repository
86 version output version and copyright information
88 version output version and copyright information
87 add add the specified files on the next commit
89 add add the specified files on the next commit
88 addremove add all new files, delete all missing files
90 addremove add all new files, delete all missing files
89 annotate show changeset information per file line
91 annotate show changeset information per file line
90 archive create unversioned archive of a repository revision
92 archive create unversioned archive of a repository revision
91 backout reverse effect of earlier changeset
93 backout reverse effect of earlier changeset
92 branch set or show the current branch name
94 branch set or show the current branch name
93 branches list repository named branches
95 branches list repository named branches
94 bundle create a changegroup file
96 bundle create a changegroup file
95 cat output the current or given revision of files
97 cat output the current or given revision of files
96 clone make a copy of an existing repository
98 clone make a copy of an existing repository
97 commit commit the specified files or all outstanding changes
99 commit commit the specified files or all outstanding changes
98 copy mark files as copied for the next commit
100 copy mark files as copied for the next commit
99 diff diff repository (or selected files)
101 diff diff repository (or selected files)
100 export dump the header and diffs for one or more changesets
102 export dump the header and diffs for one or more changesets
101 grep search for a pattern in specified files and revisions
103 grep search for a pattern in specified files and revisions
102 heads show current repository heads
104 heads show current repository heads
103 help show help for a command, extension, or list of commands
105 help show help for a command, extension, or list of commands
104 identify print information about the working copy
106 identify print information about the working copy
105 import import an ordered set of patches
107 import import an ordered set of patches
106 incoming show new changesets found in source
108 incoming show new changesets found in source
107 init create a new repository in the given directory
109 init create a new repository in the given directory
108 locate locate files matching specific patterns
110 locate locate files matching specific patterns
109 log show revision history of entire repository or files
111 log show revision history of entire repository or files
110 manifest output the current or given revision of the project manifest
112 manifest output the current or given revision of the project manifest
111 merge Merge working directory with another revision
113 merge merge working directory with another revision
112 outgoing show changesets not found in destination
114 outgoing show changesets not found in destination
113 parents show the parents of the working dir or revision
115 parents show the parents of the working dir or revision
114 paths show definition of symbolic path names
116 paths show definition of symbolic path names
115 pull pull changes from the specified source
117 pull pull changes from the specified source
116 push push changes to the specified destination
118 push push changes to the specified destination
117 recover roll back an interrupted transaction
119 recover roll back an interrupted transaction
118 remove remove the specified files on the next commit
120 remove remove the specified files on the next commit
119 rename rename files; equivalent of copy + remove
121 rename rename files; equivalent of copy + remove
120 revert revert files or dirs to their states as of some revision
122 revert revert files or dirs to their states as of some revision
121 rollback roll back the last transaction in this repository
123 rollback roll back the last transaction in this repository
122 root print the root (top) of the current working dir
124 root print the root (top) of the current working dir
123 serve export the repository via HTTP
125 serve export the repository via HTTP
124 showconfig show combined config settings from all hgrc files
126 showconfig show combined config settings from all hgrc files
125 status show changed files in the working directory
127 status show changed files in the working directory
126 tag add a tag for the current or given revision
128 tag add a tag for the current or given revision
127 tags list repository tags
129 tags list repository tags
128 tip show the tip revision
130 tip show the tip revision
129 unbundle apply a changegroup file
131 unbundle apply a changegroup file
130 update update or merge working directory
132 update update working directory
131 verify verify the integrity of the repository
133 verify verify the integrity of the repository
132 version output version and copyright information
134 version output version and copyright information
133 hg add [OPTION]... [FILE]...
135 hg add [OPTION]... [FILE]...
134
136
135 add the specified files on the next commit
137 add the specified files on the next commit
136
138
137 Schedule files to be version controlled and added to the repository.
139 Schedule files to be version controlled and added to the repository.
138
140
139 The files will be added to the repository at the next commit. To
141 The files will be added to the repository at the next commit. To
140 undo an add before that, see hg revert.
142 undo an add before that, see hg revert.
141
143
142 If no names are given, add all files in the repository.
144 If no names are given, add all files in the repository.
143
145
144 options:
146 options:
145
147
146 -I --include include names matching the given patterns
148 -I --include include names matching the given patterns
147 -X --exclude exclude names matching the given patterns
149 -X --exclude exclude names matching the given patterns
148 -n --dry-run do not perform actions, just print output
150 -n --dry-run do not perform actions, just print output
149 hg add: option --skjdfks not recognized
151 hg add: option --skjdfks not recognized
150 hg add [OPTION]... [FILE]...
152 hg add [OPTION]... [FILE]...
151
153
152 add the specified files on the next commit
154 add the specified files on the next commit
153
155
154 Schedule files to be version controlled and added to the repository.
156 Schedule files to be version controlled and added to the repository.
155
157
156 The files will be added to the repository at the next commit. To
158 The files will be added to the repository at the next commit. To
157 undo an add before that, see hg revert.
159 undo an add before that, see hg revert.
158
160
159 If no names are given, add all files in the repository.
161 If no names are given, add all files in the repository.
160
162
161 options:
163 options:
162
164
163 -I --include include names matching the given patterns
165 -I --include include names matching the given patterns
164 -X --exclude exclude names matching the given patterns
166 -X --exclude exclude names matching the given patterns
165 -n --dry-run do not perform actions, just print output
167 -n --dry-run do not perform actions, just print output
166 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
168 hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...
167
169
168 diff repository (or selected files)
170 diff repository (or selected files)
169
171
170 Show differences between revisions for the specified files.
172 Show differences between revisions for the specified files.
171
173
172 Differences between files are shown using the unified diff format.
174 Differences between files are shown using the unified diff format.
173
175
174 NOTE: diff may generate unexpected results for merges, as it will
176 NOTE: diff may generate unexpected results for merges, as it will
175 default to comparing against the working directory's first parent
177 default to comparing against the working directory's first parent
176 changeset if no revisions are specified.
178 changeset if no revisions are specified.
177
179
178 When two revision arguments are given, then changes are shown
180 When two revision arguments are given, then changes are shown
179 between those revisions. If only one revision is specified then
181 between those revisions. If only one revision is specified then
180 that revision is compared to the working directory, and, when no
182 that revision is compared to the working directory, and, when no
181 revisions are specified, the working directory files are compared
183 revisions are specified, the working directory files are compared
182 to its parent.
184 to its parent.
183
185
184 Without the -a option, diff will avoid generating diffs of files
186 Without the -a option, diff will avoid generating diffs of files
185 it detects as binary. With -a, diff will generate a diff anyway,
187 it detects as binary. With -a, diff will generate a diff anyway,
186 probably with undesirable results.
188 probably with undesirable results.
187
189
188 options:
190 options:
189
191
190 -r --rev revision
192 -r --rev revision
191 -a --text treat all files as text
193 -a --text treat all files as text
192 -p --show-function show which function each change is in
194 -p --show-function show which function each change is in
193 -g --git use git extended diff format
195 -g --git use git extended diff format
194 --nodates don't include dates in diff headers
196 --nodates don't include dates in diff headers
195 -w --ignore-all-space ignore white space when comparing lines
197 -w --ignore-all-space ignore white space when comparing lines
196 -b --ignore-space-change ignore changes in the amount of white space
198 -b --ignore-space-change ignore changes in the amount of white space
197 -B --ignore-blank-lines ignore changes whose lines are all blank
199 -B --ignore-blank-lines ignore changes whose lines are all blank
198 -I --include include names matching the given patterns
200 -I --include include names matching the given patterns
199 -X --exclude exclude names matching the given patterns
201 -X --exclude exclude names matching the given patterns
200 hg status [OPTION]... [FILE]...
202 hg status [OPTION]... [FILE]...
201
203
202 show changed files in the working directory
204 show changed files in the working directory
203
205
204 Show status of files in the repository. If names are given, only
206 Show status of files in the repository. If names are given, only
205 files that match are shown. Files that are clean or ignored, are
207 files that match are shown. Files that are clean or ignored, are
206 not listed unless -c (clean), -i (ignored) or -A is given.
208 not listed unless -c (clean), -i (ignored) or -A is given.
207
209
208 NOTE: status may appear to disagree with diff if permissions have
210 NOTE: status may appear to disagree with diff if permissions have
209 changed or a merge has occurred. The standard diff format does not
211 changed or a merge has occurred. The standard diff format does not
210 report permission changes and diff only reports changes relative
212 report permission changes and diff only reports changes relative
211 to one merge parent.
213 to one merge parent.
212
214
213 If one revision is given, it is used as the base revision.
215 If one revision is given, it is used as the base revision.
214 If two revisions are given, the difference between them is shown.
216 If two revisions are given, the difference between them is shown.
215
217
216 The codes used to show the status of files are:
218 The codes used to show the status of files are:
217 M = modified
219 M = modified
218 A = added
220 A = added
219 R = removed
221 R = removed
220 C = clean
222 C = clean
221 ! = deleted, but still tracked
223 ! = deleted, but still tracked
222 ? = not tracked
224 ? = not tracked
223 I = ignored (not shown by default)
225 I = ignored (not shown by default)
224 = the previous added file was copied from here
226 = the previous added file was copied from here
225
227
226 aliases: st
228 aliases: st
227
229
228 options:
230 options:
229
231
230 -A --all show status of all files
232 -A --all show status of all files
231 -m --modified show only modified files
233 -m --modified show only modified files
232 -a --added show only added files
234 -a --added show only added files
233 -r --removed show only removed files
235 -r --removed show only removed files
234 -d --deleted show only deleted (but tracked) files
236 -d --deleted show only deleted (but tracked) files
235 -c --clean show only files without changes
237 -c --clean show only files without changes
236 -u --unknown show only unknown (not tracked) files
238 -u --unknown show only unknown (not tracked) files
237 -i --ignored show ignored files
239 -i --ignored show ignored files
238 -n --no-status hide status prefix
240 -n --no-status hide status prefix
239 -C --copies show source of copied files
241 -C --copies show source of copied files
240 -0 --print0 end filenames with NUL, for use with xargs
242 -0 --print0 end filenames with NUL, for use with xargs
241 --rev show difference from revision
243 --rev show difference from revision
242 -I --include include names matching the given patterns
244 -I --include include names matching the given patterns
243 -X --exclude exclude names matching the given patterns
245 -X --exclude exclude names matching the given patterns
244 hg status [OPTION]... [FILE]...
246 hg status [OPTION]... [FILE]...
245
247
246 show changed files in the working directory
248 show changed files in the working directory
247 hg: unknown command 'foo'
249 hg: unknown command 'foo'
248 Mercurial Distributed SCM
250 Mercurial Distributed SCM
249
251
250 basic commands (use "hg help" for the full list or option "-v" for details):
252 basic commands (use "hg help" for the full list or option "-v" for details):
251
253
252 add add the specified files on the next commit
254 add add the specified files on the next commit
253 annotate show changeset information per file line
255 annotate show changeset information per file line
254 clone make a copy of an existing repository
256 clone make a copy of an existing repository
255 commit commit the specified files or all outstanding changes
257 commit commit the specified files or all outstanding changes
256 diff diff repository (or selected files)
258 diff diff repository (or selected files)
257 export dump the header and diffs for one or more changesets
259 export dump the header and diffs for one or more changesets
258 init create a new repository in the given directory
260 init create a new repository in the given directory
259 log show revision history of entire repository or files
261 log show revision history of entire repository or files
262 merge merge working directory with another revision
260 parents show the parents of the working dir or revision
263 parents show the parents of the working dir or revision
261 pull pull changes from the specified source
264 pull pull changes from the specified source
262 push push changes to the specified destination
265 push push changes to the specified destination
263 remove remove the specified files on the next commit
266 remove remove the specified files on the next commit
264 revert revert files or dirs to their states as of some revision
267 revert revert files or dirs to their states as of some revision
265 serve export the repository via HTTP
268 serve export the repository via HTTP
266 status show changed files in the working directory
269 status show changed files in the working directory
267 update update or merge working directory
270 update update working directory
268 hg: unknown command 'skjdfks'
271 hg: unknown command 'skjdfks'
269 Mercurial Distributed SCM
272 Mercurial Distributed SCM
270
273
271 basic commands (use "hg help" for the full list or option "-v" for details):
274 basic commands (use "hg help" for the full list or option "-v" for details):
272
275
273 add add the specified files on the next commit
276 add add the specified files on the next commit
274 annotate show changeset information per file line
277 annotate show changeset information per file line
275 clone make a copy of an existing repository
278 clone make a copy of an existing repository
276 commit commit the specified files or all outstanding changes
279 commit commit the specified files or all outstanding changes
277 diff diff repository (or selected files)
280 diff diff repository (or selected files)
278 export dump the header and diffs for one or more changesets
281 export dump the header and diffs for one or more changesets
279 init create a new repository in the given directory
282 init create a new repository in the given directory
280 log show revision history of entire repository or files
283 log show revision history of entire repository or files
284 merge merge working directory with another revision
281 parents show the parents of the working dir or revision
285 parents show the parents of the working dir or revision
282 pull pull changes from the specified source
286 pull pull changes from the specified source
283 push push changes to the specified destination
287 push push changes to the specified destination
284 remove remove the specified files on the next commit
288 remove remove the specified files on the next commit
285 revert revert files or dirs to their states as of some revision
289 revert revert files or dirs to their states as of some revision
286 serve export the repository via HTTP
290 serve export the repository via HTTP
287 status show changed files in the working directory
291 status show changed files in the working directory
288 update update or merge working directory
292 update update working directory
@@ -1,39 +1,40 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cat <<EOF >> $HGRCPATH
3 cat <<EOF >> $HGRCPATH
4 [extensions]
4 [extensions]
5 notify=
5 notify=
6
6
7 [hooks]
7 [hooks]
8 incoming.notify = python:hgext.notify.hook
8 incoming.notify = python:hgext.notify.hook
9
9
10 [notify]
10 [notify]
11 config = $HGTMP/.notify.conf
11 config = $HGTMP/.notify.conf
12 sources = pull
12 sources = pull
13 domain = test.com
13 domain = test.com
14 strip = 3
14 strip = 3
15 template = Subject: {desc|firstline|strip}\nFrom: {author}\n\nchangeset {node|short} in {webroot}\ndescription:\n\t{desc|tabindent|strip}
15 template = Subject: {desc|firstline|strip}\nFrom: {author}\n\nchangeset {node|short} in {webroot}\ndescription:\n\t{desc|tabindent|strip}
16 diffstat = False
16 diffstat = False
17
17
18 [web]
18 [web]
19 baseurl = http://test/
19 baseurl = http://test/
20
20
21 [usersubs]
21 [usersubs]
22 foo@bar = *
22 foo@bar = *
23 EOF
23 EOF
24
24
25 hg help notify
25 hg init a
26 hg init a
26 echo a > a/a
27 echo a > a/a
27 echo % commit
28 echo % commit
28 hg --traceback --cwd a commit -Ama -d '0 0'
29 hg --traceback --cwd a commit -Ama -d '0 0'
29
30
30 echo % clone
31 echo % clone
31 hg --traceback clone a b
32 hg --traceback clone a b
32
33
33 echo a >> a/a
34 echo a >> a/a
34 echo % commit
35 echo % commit
35 hg --traceback --cwd a commit -Amb -d '1 0'
36 hg --traceback --cwd a commit -Amb -d '1 0'
36
37
37 echo % pull
38 echo % pull
38 hg --traceback --cwd b pull ../a 2>&1 | sed -e 's/\(Message-Id:\).*/\1/' \
39 hg --traceback --cwd b pull ../a 2>&1 | sed -e 's/\(Message-Id:\).*/\1/' \
39 -e 's/changeset \([0-9a-f]*\) in .*/changeset \1/'
40 -e 's/changeset \([0-9a-f]*\) in .*/changeset \1/'
@@ -1,30 +1,33 b''
1 notify extension - No help text available
2
3 no commands defined
1 % commit
4 % commit
2 adding a
5 adding a
3 % clone
6 % clone
4 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
7 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
5 % commit
8 % commit
6 % pull
9 % pull
7 pulling from ../a
10 pulling from ../a
8 searching for changes
11 searching for changes
9 adding changesets
12 adding changesets
10 adding manifests
13 adding manifests
11 adding file changes
14 adding file changes
12 added 1 changesets with 1 changes to 1 files
15 added 1 changesets with 1 changes to 1 files
13 Subject: b
16 Subject: b
14 From: test@test.com
17 From: test@test.com
15 X-Hg-Notification: changeset 0647d048b600
18 X-Hg-Notification: changeset 0647d048b600
16 Message-Id:
19 Message-Id:
17 To: foo@bar
20 To: foo@bar
18
21
19 changeset 0647d048b600
22 changeset 0647d048b600
20 description:
23 description:
21 b
24 b
22 diffs (6 lines):
25 diffs (6 lines):
23
26
24 diff -r cb9a9f314b8b -r 0647d048b600 a
27 diff -r cb9a9f314b8b -r 0647d048b600 a
25 --- a/a Thu Jan 01 00:00:00 1970 +0000
28 --- a/a Thu Jan 01 00:00:00 1970 +0000
26 +++ b/a Thu Jan 01 00:00:01 1970 +0000
29 +++ b/a Thu Jan 01 00:00:01 1970 +0000
27 @@ -1,1 +1,2 @@ a
30 @@ -1,1 +1,2 @@ a
28 a
31 a
29 +a
32 +a
30 (run 'hg update' to get a working copy)
33 (run 'hg update' to get a working copy)
@@ -1,26 +1,27 b''
1 adding a
1 adding a
2 0: a
2 0: a
3 hg: unknown command 'an'
3 hg: unknown command 'an'
4 Mercurial Distributed SCM
4 Mercurial Distributed SCM
5
5
6 basic commands (use "hg help" for the full list or option "-v" for details):
6 basic commands (use "hg help" for the full list or option "-v" for details):
7
7
8 add add the specified files on the next commit
8 add add the specified files on the next commit
9 annotate show changeset information per file line
9 annotate show changeset information per file line
10 clone make a copy of an existing repository
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
13 export dump the header and diffs for one or more changesets
14 init create a new repository in the given directory
14 init create a new repository in the given directory
15 log show revision history of entire repository or files
15 log show revision history of entire repository or files
16 merge merge working directory with another revision
16 parents show the parents of the working dir or revision
17 parents show the parents of the working dir or revision
17 pull pull changes from the specified source
18 pull pull changes from the specified source
18 push push changes to the specified destination
19 push push changes to the specified destination
19 remove remove the specified files on the next commit
20 remove remove the specified files on the next commit
20 revert revert files or dirs to their states as of some revision
21 revert revert files or dirs to their states as of some revision
21 serve export the repository via HTTP
22 serve export the repository via HTTP
22 status show changed files in the working directory
23 status show changed files in the working directory
23 update update or merge working directory
24 update update working directory
24 0: a
25 0: a
25 % should succeed - up is an alias, not an abbreviation
26 % should succeed - up is an alias, not an abbreviation
26 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
27 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
General Comments 0
You need to be logged in to leave comments. Login now