##// END OF EJS Templates
Add patch.eol to ignore EOLs when patching (issue1019)...
Patrick Mezard -
r8810:ac92775b default
parent child Browse files
Show More
@@ -0,0 +1,53 b''
1 #!/bin/sh
2
3 cat > makepatch.py <<EOF
4 f = file('eol.diff', 'wb')
5 w = f.write
6 w('test message\n')
7 w('diff --git a/a b/a\n')
8 w('--- a/a\n')
9 w('+++ b/a\n')
10 w('@@ -1,5 +1,5 @@\n')
11 w(' a\n')
12 w('-b\r\n')
13 w('+y\r\n')
14 w(' c\r\n')
15 w(' d\n')
16 w('-e\n')
17 w('\ No newline at end of file\n')
18 w('+z\r\n')
19 w('\ No newline at end of file\r\n')
20 EOF
21
22 hg init repo
23 cd repo
24 echo '\.diff' > .hgignore
25
26 # Test different --eol values
27 python -c 'file("a", "wb").write("a\nb\nc\nd\ne")'
28 hg ci -Am adda
29 python ../makepatch.py
30 echo % invalid eol
31 hg --config patch.eol='LFCR' import eol.diff
32 hg revert -a
33 echo % force LF
34 hg --traceback --config patch.eol='LF' import eol.diff
35 python -c 'print repr(file("a","rb").read())'
36 hg st
37 echo % force CRLF
38 hg up -C 0
39 hg --traceback --config patch.eol='CRLF' import eol.diff
40 python -c 'print repr(file("a","rb").read())'
41 hg st
42
43 # Test --eol and binary patches
44 python -c 'file("b", "wb").write("a\x00\nb")'
45 hg ci -Am addb
46 python -c 'file("b", "wb").write("a\x00\nc")'
47 hg diff --git > bin.diff
48 hg revert --no-backup b
49 echo % binary patch with --eol
50 hg import --config patch.eol='CRLF' -m changeb bin.diff
51 python -c 'print repr(file("b","rb").read())'
52 hg st
53 cd ..
@@ -0,0 +1,16 b''
1 adding .hgignore
2 adding a
3 % invalid eol
4 applying eol.diff
5 abort: Unsupported line endings type: LFCR
6 % force LF
7 applying eol.diff
8 'a\ny\nc\nd\nz'
9 % force CRLF
10 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 applying eol.diff
12 'a\r\ny\r\nc\r\nd\r\nz'
13 adding b
14 % binary patch with --eol
15 applying bin.diff
16 'a\x00\nc'
@@ -1,893 +1,904 b''
1 HGRC(5)
1 HGRC(5)
2 =======
2 =======
3 Bryan O'Sullivan <bos@serpentine.com>
3 Bryan O'Sullivan <bos@serpentine.com>
4 :man source: Mercurial
4 :man source: Mercurial
5 :man manual: Mercurial Manual
5 :man manual: Mercurial Manual
6
6
7 NAME
7 NAME
8 ----
8 ----
9 hgrc - configuration files for Mercurial
9 hgrc - configuration files for Mercurial
10
10
11 SYNOPSIS
11 SYNOPSIS
12 --------
12 --------
13
13
14 The Mercurial system uses a set of configuration files to control
14 The Mercurial system uses a set of configuration files to control
15 aspects of its behavior.
15 aspects of its behavior.
16
16
17 FILES
17 FILES
18 -----
18 -----
19
19
20 Mercurial reads configuration data from several files, if they exist.
20 Mercurial reads configuration data from several files, if they exist.
21 The names of these files depend on the system on which Mercurial is
21 The names of these files depend on the system on which Mercurial is
22 installed. `*.rc` files from a single directory are read in
22 installed. `*.rc` files from a single directory are read in
23 alphabetical order, later ones overriding earlier ones. Where multiple
23 alphabetical order, later ones overriding earlier ones. Where multiple
24 paths are given below, settings from later paths override earlier
24 paths are given below, settings from later paths override earlier
25 ones.
25 ones.
26
26
27 (Unix) `<install-root>/etc/mercurial/hgrc.d/*.rc`::
27 (Unix) `<install-root>/etc/mercurial/hgrc.d/*.rc`::
28 (Unix) `<install-root>/etc/mercurial/hgrc`::
28 (Unix) `<install-root>/etc/mercurial/hgrc`::
29 Per-installation configuration files, searched for in the
29 Per-installation configuration files, searched for in the
30 directory where Mercurial is installed. `<install-root>` is the
30 directory where Mercurial is installed. `<install-root>` is the
31 parent directory of the hg executable (or symlink) being run. For
31 parent directory of the hg executable (or symlink) being run. For
32 example, if installed in `/shared/tools/bin/hg`, Mercurial will look
32 example, if installed in `/shared/tools/bin/hg`, Mercurial will look
33 in `/shared/tools/etc/mercurial/hgrc`. Options in these files apply
33 in `/shared/tools/etc/mercurial/hgrc`. Options in these files apply
34 to all Mercurial commands executed by any user in any directory.
34 to all Mercurial commands executed by any user in any directory.
35
35
36 (Unix) `/etc/mercurial/hgrc.d/*.rc`::
36 (Unix) `/etc/mercurial/hgrc.d/*.rc`::
37 (Unix) `/etc/mercurial/hgrc`::
37 (Unix) `/etc/mercurial/hgrc`::
38 Per-system configuration files, for the system on which Mercurial
38 Per-system configuration files, for the system on which Mercurial
39 is running. Options in these files apply to all Mercurial commands
39 is running. Options in these files apply to all Mercurial commands
40 executed by any user in any directory. Options in these files
40 executed by any user in any directory. Options in these files
41 override per-installation options.
41 override per-installation options.
42
42
43 (Windows) `<install-dir>\Mercurial.ini`::
43 (Windows) `<install-dir>\Mercurial.ini`::
44 or else::
44 or else::
45 (Windows) `HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`::
45 (Windows) `HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`::
46 or else::
46 or else::
47 (Windows) `C:\Mercurial\Mercurial.ini`::
47 (Windows) `C:\Mercurial\Mercurial.ini`::
48 Per-installation/system configuration files, for the system on
48 Per-installation/system configuration files, for the system on
49 which Mercurial is running. Options in these files apply to all
49 which Mercurial is running. Options in these files apply to all
50 Mercurial commands executed by any user in any directory. Registry
50 Mercurial commands executed by any user in any directory. Registry
51 keys contain PATH-like strings, every part of which must reference
51 keys contain PATH-like strings, every part of which must reference
52 a `Mercurial.ini` file or be a directory where `*.rc` files will
52 a `Mercurial.ini` file or be a directory where `*.rc` files will
53 be read.
53 be read.
54
54
55 (Unix) `$HOME/.hgrc`::
55 (Unix) `$HOME/.hgrc`::
56 (Windows) `%HOME%\Mercurial.ini`::
56 (Windows) `%HOME%\Mercurial.ini`::
57 (Windows) `%HOME%\.hgrc`::
57 (Windows) `%HOME%\.hgrc`::
58 (Windows) `%USERPROFILE%\Mercurial.ini`::
58 (Windows) `%USERPROFILE%\Mercurial.ini`::
59 (Windows) `%USERPROFILE%\.hgrc`::
59 (Windows) `%USERPROFILE%\.hgrc`::
60 Per-user configuration file(s), for the user running Mercurial. On
60 Per-user configuration file(s), for the user running Mercurial. On
61 Windows 9x, `%HOME%` is replaced by `%APPDATA%`. Options in these
61 Windows 9x, `%HOME%` is replaced by `%APPDATA%`. Options in these
62 files apply to all Mercurial commands executed by this user in any
62 files apply to all Mercurial commands executed by this user in any
63 directory. Options in these files override per-installation and
63 directory. Options in these files override per-installation and
64 per-system options.
64 per-system options.
65
65
66 (Unix, Windows) `<repo>/.hg/hgrc`::
66 (Unix, Windows) `<repo>/.hg/hgrc`::
67 Per-repository configuration options that only apply in a
67 Per-repository configuration options that only apply in a
68 particular repository. This file is not version-controlled, and
68 particular repository. This file is not version-controlled, and
69 will not get transferred during a "clone" operation. Options in
69 will not get transferred during a "clone" operation. Options in
70 this file override options in all other configuration files. On
70 this file override options in all other configuration files. On
71 Unix, most of this file will be ignored if it doesn't belong to a
71 Unix, most of this file will be ignored if it doesn't belong to a
72 trusted user or to a trusted group. See the documentation for the
72 trusted user or to a trusted group. See the documentation for the
73 trusted section below for more details.
73 trusted section below for more details.
74
74
75 SYNTAX
75 SYNTAX
76 ------
76 ------
77
77
78 A configuration file consists of sections, led by a "[section]" header
78 A configuration file consists of sections, led by a "[section]" header
79 and followed by "name: value" entries; "name=value" is also accepted.
79 and followed by "name: value" entries; "name=value" is also accepted.
80
80
81 [spam]
81 [spam]
82 eggs=ham
82 eggs=ham
83 green=
83 green=
84 eggs
84 eggs
85
85
86 Each line contains one entry. If the lines that follow are indented,
86 Each line contains one entry. If the lines that follow are indented,
87 they are treated as continuations of that entry.
87 they are treated as continuations of that entry.
88
88
89 Leading whitespace is removed from values. Empty lines are skipped.
89 Leading whitespace is removed from values. Empty lines are skipped.
90
90
91 The optional values can contain format strings which refer to other
91 The optional values can contain format strings which refer to other
92 values in the same section, or values in a special DEFAULT section.
92 values in the same section, or values in a special DEFAULT section.
93
93
94 Lines beginning with "#" or ";" are ignored and may be used to provide
94 Lines beginning with "#" or ";" are ignored and may be used to provide
95 comments.
95 comments.
96
96
97 SECTIONS
97 SECTIONS
98 --------
98 --------
99
99
100 This section describes the different sections that may appear in a
100 This section describes the different sections that may appear in a
101 Mercurial "hgrc" file, the purpose of each section, its possible keys,
101 Mercurial "hgrc" file, the purpose of each section, its possible keys,
102 and their possible values.
102 and their possible values.
103
103
104 [[alias]]
104 [[alias]]
105 alias::
105 alias::
106 Defines command aliases.
106 Defines command aliases.
107 Aliases allow you to define your own commands in terms of other
107 Aliases allow you to define your own commands in terms of other
108 commands (or aliases), optionally including arguments.
108 commands (or aliases), optionally including arguments.
109 +
109 +
110 --
110 --
111 Alias definitions consist of lines of the form:
111 Alias definitions consist of lines of the form:
112
112
113 <alias> = <command> [<argument]...
113 <alias> = <command> [<argument]...
114
114
115 For example, this definition:
115 For example, this definition:
116
116
117 latest = log --limit 5
117 latest = log --limit 5
118
118
119 creates a new command `latest` that shows only the five most recent
119 creates a new command `latest` that shows only the five most recent
120 changesets. You can define subsequent aliases using earlier ones:
120 changesets. You can define subsequent aliases using earlier ones:
121
121
122 stable5 = latest -b stable
122 stable5 = latest -b stable
123
123
124 NOTE: It is possible to create aliases with the same names as existing
124 NOTE: It is possible to create aliases with the same names as existing
125 commands, which will then override the original definitions. This is
125 commands, which will then override the original definitions. This is
126 almost always a bad idea!
126 almost always a bad idea!
127 --
127 --
128
128
129 [[auth]]
129 [[auth]]
130 auth::
130 auth::
131 Authentication credentials for HTTP authentication. Each line has
131 Authentication credentials for HTTP authentication. Each line has
132 the following format:
132 the following format:
133
133
134 <name>.<argument> = <value>
134 <name>.<argument> = <value>
135 +
135 +
136 --
136 --
137 where <name> is used to group arguments into authentication entries.
137 where <name> is used to group arguments into authentication entries.
138 Example:
138 Example:
139
139
140 foo.prefix = hg.intevation.org/mercurial
140 foo.prefix = hg.intevation.org/mercurial
141 foo.username = foo
141 foo.username = foo
142 foo.password = bar
142 foo.password = bar
143 foo.schemes = http https
143 foo.schemes = http https
144
144
145 Supported arguments:
145 Supported arguments:
146
146
147 prefix;;
147 prefix;;
148 Either "++\*++" or a URI prefix with or without the scheme part.
148 Either "++\*++" or a URI prefix with or without the scheme part.
149 The authentication entry with the longest matching prefix is used
149 The authentication entry with the longest matching prefix is used
150 (where "++*++" matches everything and counts as a match of length
150 (where "++*++" matches everything and counts as a match of length
151 1). If the prefix doesn't include a scheme, the match is performed
151 1). If the prefix doesn't include a scheme, the match is performed
152 against the URI with its scheme stripped as well, and the schemes
152 against the URI with its scheme stripped as well, and the schemes
153 argument, q.v., is then subsequently consulted.
153 argument, q.v., is then subsequently consulted.
154 username;;
154 username;;
155 Username to authenticate with.
155 Username to authenticate with.
156 password;;
156 password;;
157 Optional. Password to authenticate with. If not given the user
157 Optional. Password to authenticate with. If not given the user
158 will be prompted for it.
158 will be prompted for it.
159 schemes;;
159 schemes;;
160 Optional. Space separated list of URI schemes to use this
160 Optional. Space separated list of URI schemes to use this
161 authentication entry with. Only used if the prefix doesn't include
161 authentication entry with. Only used if the prefix doesn't include
162 a scheme. Supported schemes are http and https. They will match
162 a scheme. Supported schemes are http and https. They will match
163 static-http and static-https respectively, as well.
163 static-http and static-https respectively, as well.
164 Default: https.
164 Default: https.
165
165
166 If no suitable authentication entry is found, the user is prompted
166 If no suitable authentication entry is found, the user is prompted
167 for credentials as usual if required by the remote.
167 for credentials as usual if required by the remote.
168 --
168 --
169
169
170 [[decode]]
170 [[decode]]
171 decode/encode::
171 decode/encode::
172 Filters for transforming files on checkout/checkin. This would
172 Filters for transforming files on checkout/checkin. This would
173 typically be used for newline processing or other
173 typically be used for newline processing or other
174 localization/canonicalization of files.
174 localization/canonicalization of files.
175 +
175 +
176 --
176 --
177 Filters consist of a filter pattern followed by a filter command.
177 Filters consist of a filter pattern followed by a filter command.
178 Filter patterns are globs by default, rooted at the repository root.
178 Filter patterns are globs by default, rooted at the repository root.
179 For example, to match any file ending in "`.txt`" in the root
179 For example, to match any file ending in "`.txt`" in the root
180 directory only, use the pattern "++\*.txt++". To match any file ending
180 directory only, use the pattern "++\*.txt++". To match any file ending
181 in "`.c`" anywhere in the repository, use the pattern "++**.c++".
181 in "`.c`" anywhere in the repository, use the pattern "++**.c++".
182
182
183 The filter command can start with a specifier, either "pipe:" or
183 The filter command can start with a specifier, either "pipe:" or
184 "tempfile:". If no specifier is given, "pipe:" is used by default.
184 "tempfile:". If no specifier is given, "pipe:" is used by default.
185
185
186 A "pipe:" command must accept data on stdin and return the transformed
186 A "pipe:" command must accept data on stdin and return the transformed
187 data on stdout.
187 data on stdout.
188
188
189 Pipe example:
189 Pipe example:
190
190
191 [encode]
191 [encode]
192 # uncompress gzip files on checkin to improve delta compression
192 # uncompress gzip files on checkin to improve delta compression
193 # note: not necessarily a good idea, just an example
193 # note: not necessarily a good idea, just an example
194 *.gz = pipe: gunzip
194 *.gz = pipe: gunzip
195
195
196 [decode]
196 [decode]
197 # recompress gzip files when writing them to the working dir (we
197 # recompress gzip files when writing them to the working dir (we
198 # can safely omit "pipe:", because it's the default)
198 # can safely omit "pipe:", because it's the default)
199 *.gz = gzip
199 *.gz = gzip
200
200
201 A "tempfile:" command is a template. The string INFILE is replaced
201 A "tempfile:" command is a template. The string INFILE is replaced
202 with the name of a temporary file that contains the data to be
202 with the name of a temporary file that contains the data to be
203 filtered by the command. The string OUTFILE is replaced with the name
203 filtered by the command. The string OUTFILE is replaced with the name
204 of an empty temporary file, where the filtered data must be written by
204 of an empty temporary file, where the filtered data must be written by
205 the command.
205 the command.
206
206
207 NOTE: the tempfile mechanism is recommended for Windows systems, where
207 NOTE: the tempfile mechanism is recommended for Windows systems, where
208 the standard shell I/O redirection operators often have strange
208 the standard shell I/O redirection operators often have strange
209 effects and may corrupt the contents of your files.
209 effects and may corrupt the contents of your files.
210
210
211 The most common usage is for LF <-> CRLF translation on Windows. For
211 The most common usage is for LF <-> CRLF translation on Windows. For
212 this, use the "smart" converters which check for binary files:
212 this, use the "smart" converters which check for binary files:
213
213
214 [extensions]
214 [extensions]
215 hgext.win32text =
215 hgext.win32text =
216 [encode]
216 [encode]
217 ** = cleverencode:
217 ** = cleverencode:
218 [decode]
218 [decode]
219 ** = cleverdecode:
219 ** = cleverdecode:
220
220
221 or if you only want to translate certain files:
221 or if you only want to translate certain files:
222
222
223 [extensions]
223 [extensions]
224 hgext.win32text =
224 hgext.win32text =
225 [encode]
225 [encode]
226 **.txt = dumbencode:
226 **.txt = dumbencode:
227 [decode]
227 [decode]
228 **.txt = dumbdecode:
228 **.txt = dumbdecode:
229 --
229 --
230
230
231 [[defaults]]
231 [[defaults]]
232 defaults::
232 defaults::
233 Use the [defaults] section to define command defaults, i.e. the
233 Use the [defaults] section to define command defaults, i.e. the
234 default options/arguments to pass to the specified commands.
234 default options/arguments to pass to the specified commands.
235 +
235 +
236 --
236 --
237 The following example makes 'hg log' run in verbose mode, and 'hg
237 The following example makes 'hg log' run in verbose mode, and 'hg
238 status' show only the modified files, by default.
238 status' show only the modified files, by default.
239
239
240 [defaults]
240 [defaults]
241 log = -v
241 log = -v
242 status = -m
242 status = -m
243
243
244 The actual commands, instead of their aliases, must be used when
244 The actual commands, instead of their aliases, must be used when
245 defining command defaults. The command defaults will also be applied
245 defining command defaults. The command defaults will also be applied
246 to the aliases of the commands defined.
246 to the aliases of the commands defined.
247 --
247 --
248
248
249 [[diff]]
249 [[diff]]
250 diff::
250 diff::
251 Settings used when displaying diffs. They are all Boolean and
251 Settings used when displaying diffs. They are all Boolean and
252 defaults to False.
252 defaults to False.
253 git;;
253 git;;
254 Use git extended diff format.
254 Use git extended diff format.
255 nodates;;
255 nodates;;
256 Don't include dates in diff headers.
256 Don't include dates in diff headers.
257 showfunc;;
257 showfunc;;
258 Show which function each change is in.
258 Show which function each change is in.
259 ignorews;;
259 ignorews;;
260 Ignore white space when comparing lines.
260 Ignore white space when comparing lines.
261 ignorewsamount;;
261 ignorewsamount;;
262 Ignore changes in the amount of white space.
262 Ignore changes in the amount of white space.
263 ignoreblanklines;;
263 ignoreblanklines;;
264 Ignore changes whose lines are all blank.
264 Ignore changes whose lines are all blank.
265
265
266 [[email]]
266 [[email]]
267 email::
267 email::
268 Settings for extensions that send email messages.
268 Settings for extensions that send email messages.
269 from;;
269 from;;
270 Optional. Email address to use in "From" header and SMTP envelope
270 Optional. Email address to use in "From" header and SMTP envelope
271 of outgoing messages.
271 of outgoing messages.
272 to;;
272 to;;
273 Optional. Comma-separated list of recipients' email addresses.
273 Optional. Comma-separated list of recipients' email addresses.
274 cc;;
274 cc;;
275 Optional. Comma-separated list of carbon copy recipients'
275 Optional. Comma-separated list of carbon copy recipients'
276 email addresses.
276 email addresses.
277 bcc;;
277 bcc;;
278 Optional. Comma-separated list of blind carbon copy recipients'
278 Optional. Comma-separated list of blind carbon copy recipients'
279 email addresses. Cannot be set interactively.
279 email addresses. Cannot be set interactively.
280 method;;
280 method;;
281 Optional. Method to use to send email messages. If value is "smtp"
281 Optional. Method to use to send email messages. If value is "smtp"
282 (default), use SMTP (see section "[smtp]" for configuration).
282 (default), use SMTP (see section "[smtp]" for configuration).
283 Otherwise, use as name of program to run that acts like sendmail
283 Otherwise, use as name of program to run that acts like sendmail
284 (takes "-f" option for sender, list of recipients on command line,
284 (takes "-f" option for sender, list of recipients on command line,
285 message on stdin). Normally, setting this to "sendmail" or
285 message on stdin). Normally, setting this to "sendmail" or
286 "/usr/sbin/sendmail" is enough to use sendmail to send messages.
286 "/usr/sbin/sendmail" is enough to use sendmail to send messages.
287 charsets;;
287 charsets;;
288 Optional. Comma-separated list of character sets considered
288 Optional. Comma-separated list of character sets considered
289 convenient for recipients. Addresses, headers, and parts not
289 convenient for recipients. Addresses, headers, and parts not
290 containing patches of outgoing messages will be encoded in the
290 containing patches of outgoing messages will be encoded in the
291 first character set to which conversion from local encoding
291 first character set to which conversion from local encoding
292 (`$HGENCODING`, `ui.fallbackencoding`) succeeds. If correct
292 (`$HGENCODING`, `ui.fallbackencoding`) succeeds. If correct
293 conversion fails, the text in question is sent as is. Defaults to
293 conversion fails, the text in question is sent as is. Defaults to
294 empty (explicit) list.
294 empty (explicit) list.
295 +
295 +
296 --
296 --
297 Order of outgoing email character sets:
297 Order of outgoing email character sets:
298
298
299 us-ascii always first, regardless of settings
299 us-ascii always first, regardless of settings
300 email.charsets in order given by user
300 email.charsets in order given by user
301 ui.fallbackencoding if not in email.charsets
301 ui.fallbackencoding if not in email.charsets
302 $HGENCODING if not in email.charsets
302 $HGENCODING if not in email.charsets
303 utf-8 always last, regardless of settings
303 utf-8 always last, regardless of settings
304
304
305 Email example:
305 Email example:
306
306
307 [email]
307 [email]
308 from = Joseph User <joe.user@example.com>
308 from = Joseph User <joe.user@example.com>
309 method = /usr/sbin/sendmail
309 method = /usr/sbin/sendmail
310 # charsets for western Europeans
310 # charsets for western Europeans
311 # us-ascii, utf-8 omitted, as they are tried first and last
311 # us-ascii, utf-8 omitted, as they are tried first and last
312 charsets = iso-8859-1, iso-8859-15, windows-1252
312 charsets = iso-8859-1, iso-8859-15, windows-1252
313 --
313 --
314
314
315 [[extensions]]
315 [[extensions]]
316 extensions::
316 extensions::
317 Mercurial has an extension mechanism for adding new features. To
317 Mercurial has an extension mechanism for adding new features. To
318 enable an extension, create an entry for it in this section.
318 enable an extension, create an entry for it in this section.
319 +
319 +
320 --
320 --
321 If you know that the extension is already in Python's search path,
321 If you know that the extension is already in Python's search path,
322 you can give the name of the module, followed by "=", with nothing
322 you can give the name of the module, followed by "=", with nothing
323 after the "=".
323 after the "=".
324
324
325 Otherwise, give a name that you choose, followed by "=", followed by
325 Otherwise, give a name that you choose, followed by "=", followed by
326 the path to the ".py" file (including the file name extension) that
326 the path to the ".py" file (including the file name extension) that
327 defines the extension.
327 defines the extension.
328
328
329 To explicitly disable an extension that is enabled in an hgrc of
329 To explicitly disable an extension that is enabled in an hgrc of
330 broader scope, prepend its path with '!', as in
330 broader scope, prepend its path with '!', as in
331 'hgext.foo = !/ext/path' or 'hgext.foo = !' when path is not
331 'hgext.foo = !/ext/path' or 'hgext.foo = !' when path is not
332 supplied.
332 supplied.
333
333
334 Example for `~/.hgrc`:
334 Example for `~/.hgrc`:
335
335
336 [extensions]
336 [extensions]
337 # (the mq extension will get loaded from Mercurial's path)
337 # (the mq extension will get loaded from Mercurial's path)
338 hgext.mq =
338 hgext.mq =
339 # (this extension will get loaded from the file specified)
339 # (this extension will get loaded from the file specified)
340 myfeature = ~/.hgext/myfeature.py
340 myfeature = ~/.hgext/myfeature.py
341 --
341 --
342
342
343 [[format]]
343 [[format]]
344 format::
344 format::
345
345
346 usestore;;
346 usestore;;
347 Enable or disable the "store" repository format which improves
347 Enable or disable the "store" repository format which improves
348 compatibility with systems that fold case or otherwise mangle
348 compatibility with systems that fold case or otherwise mangle
349 filenames. Enabled by default. Disabling this option will allow
349 filenames. Enabled by default. Disabling this option will allow
350 you to store longer filenames in some situations at the expense of
350 you to store longer filenames in some situations at the expense of
351 compatibility and ensures that the on-disk format of newly created
351 compatibility and ensures that the on-disk format of newly created
352 repositories will be compatible with Mercurial before version 0.9.4.
352 repositories will be compatible with Mercurial before version 0.9.4.
353
353
354 usefncache;;
354 usefncache;;
355 Enable or disable the "fncache" repository format which enhances
355 Enable or disable the "fncache" repository format which enhances
356 the "store" repository format (which has to be enabled to use
356 the "store" repository format (which has to be enabled to use
357 fncache) to allow longer filenames and avoids using Windows
357 fncache) to allow longer filenames and avoids using Windows
358 reserved names, e.g. "nul". Enabled by default. Disabling this
358 reserved names, e.g. "nul". Enabled by default. Disabling this
359 option ensures that the on-disk format of newly created
359 option ensures that the on-disk format of newly created
360 repositories will be compatible with Mercurial before version 1.1.
360 repositories will be compatible with Mercurial before version 1.1.
361
361
362 [[merge-patterns]]
362 [[merge-patterns]]
363 merge-patterns::
363 merge-patterns::
364 This section specifies merge tools to associate with particular file
364 This section specifies merge tools to associate with particular file
365 patterns. Tools matched here will take precedence over the default
365 patterns. Tools matched here will take precedence over the default
366 merge tool. Patterns are globs by default, rooted at the repository
366 merge tool. Patterns are globs by default, rooted at the repository
367 root.
367 root.
368 +
368 +
369 Example:
369 Example:
370 +
370 +
371 [merge-patterns]
371 [merge-patterns]
372 **.c = kdiff3
372 **.c = kdiff3
373 **.jpg = myimgmerge
373 **.jpg = myimgmerge
374
374
375 [[merge-tools]]
375 [[merge-tools]]
376 merge-tools::
376 merge-tools::
377 This section configures external merge tools to use for file-level
377 This section configures external merge tools to use for file-level
378 merges.
378 merges.
379 +
379 +
380 --
380 --
381 Example `~/.hgrc`:
381 Example `~/.hgrc`:
382
382
383 [merge-tools]
383 [merge-tools]
384 # Override stock tool location
384 # Override stock tool location
385 kdiff3.executable = ~/bin/kdiff3
385 kdiff3.executable = ~/bin/kdiff3
386 # Specify command line
386 # Specify command line
387 kdiff3.args = $base $local $other -o $output
387 kdiff3.args = $base $local $other -o $output
388 # Give higher priority
388 # Give higher priority
389 kdiff3.priority = 1
389 kdiff3.priority = 1
390
390
391 # Define new tool
391 # Define new tool
392 myHtmlTool.args = -m $local $other $base $output
392 myHtmlTool.args = -m $local $other $base $output
393 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
393 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
394 myHtmlTool.priority = 1
394 myHtmlTool.priority = 1
395
395
396 Supported arguments:
396 Supported arguments:
397
397
398 priority;;
398 priority;;
399 The priority in which to evaluate this tool.
399 The priority in which to evaluate this tool.
400 Default: 0.
400 Default: 0.
401 executable;;
401 executable;;
402 Either just the name of the executable or its pathname.
402 Either just the name of the executable or its pathname.
403 Default: the tool name.
403 Default: the tool name.
404 args;;
404 args;;
405 The arguments to pass to the tool executable. You can refer to the
405 The arguments to pass to the tool executable. You can refer to the
406 files being merged as well as the output file through these
406 files being merged as well as the output file through these
407 variables: `$base`, `$local`, `$other`, `$output`.
407 variables: `$base`, `$local`, `$other`, `$output`.
408 Default: `$local $base $other`
408 Default: `$local $base $other`
409 premerge;;
409 premerge;;
410 Attempt to run internal non-interactive 3-way merge tool before
410 Attempt to run internal non-interactive 3-way merge tool before
411 launching external tool.
411 launching external tool.
412 Default: True
412 Default: True
413 binary;;
413 binary;;
414 This tool can merge binary files. Defaults to False, unless tool
414 This tool can merge binary files. Defaults to False, unless tool
415 was selected by file pattern match.
415 was selected by file pattern match.
416 symlink;;
416 symlink;;
417 This tool can merge symlinks. Defaults to False, even if tool was
417 This tool can merge symlinks. Defaults to False, even if tool was
418 selected by file pattern match.
418 selected by file pattern match.
419 checkconflicts;;
419 checkconflicts;;
420 Check whether there are conflicts even though the tool reported
420 Check whether there are conflicts even though the tool reported
421 success.
421 success.
422 Default: False
422 Default: False
423 checkchanged;;
423 checkchanged;;
424 Check whether outputs were written even though the tool reported
424 Check whether outputs were written even though the tool reported
425 success.
425 success.
426 Default: False
426 Default: False
427 fixeol;;
427 fixeol;;
428 Attempt to fix up EOL changes caused by the merge tool.
428 Attempt to fix up EOL changes caused by the merge tool.
429 Default: False
429 Default: False
430 gui;;
430 gui;;
431 This tool requires a graphical interface to run. Default: False
431 This tool requires a graphical interface to run. Default: False
432 regkey;;
432 regkey;;
433 Windows registry key which describes install location of this
433 Windows registry key which describes install location of this
434 tool. Mercurial will search for this key first under
434 tool. Mercurial will search for this key first under
435 `HKEY_CURRENT_USER` and then under `HKEY_LOCAL_MACHINE`.
435 `HKEY_CURRENT_USER` and then under `HKEY_LOCAL_MACHINE`.
436 Default: None
436 Default: None
437 regname;;
437 regname;;
438 Name of value to read from specified registry key. Defaults to the
438 Name of value to read from specified registry key. Defaults to the
439 unnamed (default) value.
439 unnamed (default) value.
440 regappend;;
440 regappend;;
441 String to append to the value read from the registry, typically
441 String to append to the value read from the registry, typically
442 the executable name of the tool.
442 the executable name of the tool.
443 Default: None
443 Default: None
444 --
444 --
445
445
446 [[hooks]]
446 [[hooks]]
447 hooks::
447 hooks::
448 Commands or Python functions that get automatically executed by
448 Commands or Python functions that get automatically executed by
449 various actions such as starting or finishing a commit. Multiple
449 various actions such as starting or finishing a commit. Multiple
450 hooks can be run for the same action by appending a suffix to the
450 hooks can be run for the same action by appending a suffix to the
451 action. Overriding a site-wide hook can be done by changing its
451 action. Overriding a site-wide hook can be done by changing its
452 value or setting it to an empty string.
452 value or setting it to an empty string.
453 +
453 +
454 --
454 --
455 Example `.hg/hgrc`:
455 Example `.hg/hgrc`:
456
456
457 [hooks]
457 [hooks]
458 # do not use the site-wide hook
458 # do not use the site-wide hook
459 incoming =
459 incoming =
460 incoming.email = /my/email/hook
460 incoming.email = /my/email/hook
461 incoming.autobuild = /my/build/hook
461 incoming.autobuild = /my/build/hook
462
462
463 Most hooks are run with environment variables set that give useful
463 Most hooks are run with environment variables set that give useful
464 additional information. For each hook below, the environment
464 additional information. For each hook below, the environment
465 variables it is passed are listed with names of the form "$HG_foo".
465 variables it is passed are listed with names of the form "$HG_foo".
466
466
467 changegroup;;
467 changegroup;;
468 Run after a changegroup has been added via push, pull or unbundle.
468 Run after a changegroup has been added via push, pull or unbundle.
469 ID of the first new changeset is in `$HG_NODE`. URL from which
469 ID of the first new changeset is in `$HG_NODE`. URL from which
470 changes came is in `$HG_URL`.
470 changes came is in `$HG_URL`.
471 commit;;
471 commit;;
472 Run after a changeset has been created in the local repository. ID
472 Run after a changeset has been created in the local repository. ID
473 of the newly created changeset is in `$HG_NODE`. Parent changeset
473 of the newly created changeset is in `$HG_NODE`. Parent changeset
474 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
474 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
475 incoming;;
475 incoming;;
476 Run after a changeset has been pulled, pushed, or unbundled into
476 Run after a changeset has been pulled, pushed, or unbundled into
477 the local repository. The ID of the newly arrived changeset is in
477 the local repository. The ID of the newly arrived changeset is in
478 `$HG_NODE`. URL that was source of changes came is in `$HG_URL`.
478 `$HG_NODE`. URL that was source of changes came is in `$HG_URL`.
479 outgoing;;
479 outgoing;;
480 Run after sending changes from local repository to another. ID of
480 Run after sending changes from local repository to another. ID of
481 first changeset sent is in `$HG_NODE`. Source of operation is in
481 first changeset sent is in `$HG_NODE`. Source of operation is in
482 `$HG_SOURCE`; see "preoutgoing" hook for description.
482 `$HG_SOURCE`; see "preoutgoing" hook for description.
483 post-<command>;;
483 post-<command>;;
484 Run after successful invocations of the associated command. The
484 Run after successful invocations of the associated command. The
485 contents of the command line are passed as `$HG_ARGS` and the result
485 contents of the command line are passed as `$HG_ARGS` and the result
486 code in `$HG_RESULT`. Hook failure is ignored.
486 code in `$HG_RESULT`. Hook failure is ignored.
487 pre-<command>;;
487 pre-<command>;;
488 Run before executing the associated command. The contents of the
488 Run before executing the associated command. The contents of the
489 command line are passed as `$HG_ARGS`. If the hook returns failure,
489 command line are passed as `$HG_ARGS`. If the hook returns failure,
490 the command doesn't execute and Mercurial returns the failure
490 the command doesn't execute and Mercurial returns the failure
491 code.
491 code.
492 prechangegroup;;
492 prechangegroup;;
493 Run before a changegroup is added via push, pull or unbundle. Exit
493 Run before a changegroup is added via push, pull or unbundle. Exit
494 status 0 allows the changegroup to proceed. Non-zero status will
494 status 0 allows the changegroup to proceed. Non-zero status will
495 cause the push, pull or unbundle to fail. URL from which changes
495 cause the push, pull or unbundle to fail. URL from which changes
496 will come is in `$HG_URL`.
496 will come is in `$HG_URL`.
497 precommit;;
497 precommit;;
498 Run before starting a local commit. Exit status 0 allows the
498 Run before starting a local commit. Exit status 0 allows the
499 commit to proceed. Non-zero status will cause the commit to fail.
499 commit to proceed. Non-zero status will cause the commit to fail.
500 Parent changeset IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
500 Parent changeset IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
501 preoutgoing;;
501 preoutgoing;;
502 Run before collecting changes to send from the local repository to
502 Run before collecting changes to send from the local repository to
503 another. Non-zero status will cause failure. This lets you prevent
503 another. Non-zero status will cause failure. This lets you prevent
504 pull over HTTP or SSH. Also prevents against local pull, push
504 pull over HTTP or SSH. Also prevents against local pull, push
505 (outbound) or bundle commands, but not effective, since you can
505 (outbound) or bundle commands, but not effective, since you can
506 just copy files instead then. Source of operation is in
506 just copy files instead then. Source of operation is in
507 `$HG_SOURCE`. If "serve", operation is happening on behalf of remote
507 `$HG_SOURCE`. If "serve", operation is happening on behalf of remote
508 SSH or HTTP repository. If "push", "pull" or "bundle", operation
508 SSH or HTTP repository. If "push", "pull" or "bundle", operation
509 is happening on behalf of repository on same system.
509 is happening on behalf of repository on same system.
510 pretag;;
510 pretag;;
511 Run before creating a tag. Exit status 0 allows the tag to be
511 Run before creating a tag. Exit status 0 allows the tag to be
512 created. Non-zero status will cause the tag to fail. ID of
512 created. Non-zero status will cause the tag to fail. ID of
513 changeset to tag is in `$HG_NODE`. Name of tag is in `$HG_TAG`. Tag is
513 changeset to tag is in `$HG_NODE`. Name of tag is in `$HG_TAG`. Tag is
514 local if `$HG_LOCAL=1`, in repository if `$HG_LOCAL=0`.
514 local if `$HG_LOCAL=1`, in repository if `$HG_LOCAL=0`.
515 pretxnchangegroup;;
515 pretxnchangegroup;;
516 Run after a changegroup has been added via push, pull or unbundle,
516 Run after a changegroup has been added via push, pull or unbundle,
517 but before the transaction has been committed. Changegroup is
517 but before the transaction has been committed. Changegroup is
518 visible to hook program. This lets you validate incoming changes
518 visible to hook program. This lets you validate incoming changes
519 before accepting them. Passed the ID of the first new changeset in
519 before accepting them. Passed the ID of the first new changeset in
520 `$HG_NODE`. Exit status 0 allows the transaction to commit. Non-zero
520 `$HG_NODE`. Exit status 0 allows the transaction to commit. Non-zero
521 status will cause the transaction to be rolled back and the push,
521 status will cause the transaction to be rolled back and the push,
522 pull or unbundle will fail. URL that was source of changes is in
522 pull or unbundle will fail. URL that was source of changes is in
523 `$HG_URL`.
523 `$HG_URL`.
524 pretxncommit;;
524 pretxncommit;;
525 Run after a changeset has been created but the transaction not yet
525 Run after a changeset has been created but the transaction not yet
526 committed. Changeset is visible to hook program. This lets you
526 committed. Changeset is visible to hook program. This lets you
527 validate commit message and changes. Exit status 0 allows the
527 validate commit message and changes. Exit status 0 allows the
528 commit to proceed. Non-zero status will cause the transaction to
528 commit to proceed. Non-zero status will cause the transaction to
529 be rolled back. ID of changeset is in `$HG_NODE`. Parent changeset
529 be rolled back. ID of changeset is in `$HG_NODE`. Parent changeset
530 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
530 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
531 preupdate;;
531 preupdate;;
532 Run before updating the working directory. Exit status 0 allows
532 Run before updating the working directory. Exit status 0 allows
533 the update to proceed. Non-zero status will prevent the update.
533 the update to proceed. Non-zero status will prevent the update.
534 Changeset ID of first new parent is in `$HG_PARENT1`. If merge, ID
534 Changeset ID of first new parent is in `$HG_PARENT1`. If merge, ID
535 of second new parent is in `$HG_PARENT2`.
535 of second new parent is in `$HG_PARENT2`.
536 tag;;
536 tag;;
537 Run after a tag is created. ID of tagged changeset is in `$HG_NODE`.
537 Run after a tag is created. ID of tagged changeset is in `$HG_NODE`.
538 Name of tag is in `$HG_TAG`. Tag is local if `$HG_LOCAL=1`, in
538 Name of tag is in `$HG_TAG`. Tag is local if `$HG_LOCAL=1`, in
539 repository if `$HG_LOCAL=0`.
539 repository if `$HG_LOCAL=0`.
540 update;;
540 update;;
541 Run after updating the working directory. Changeset ID of first
541 Run after updating the working directory. Changeset ID of first
542 new parent is in `$HG_PARENT1`. If merge, ID of second new parent is
542 new parent is in `$HG_PARENT1`. If merge, ID of second new parent is
543 in `$HG_PARENT2`. If the update succeeded, `$HG_ERROR=0`. If the
543 in `$HG_PARENT2`. If the update succeeded, `$HG_ERROR=0`. If the
544 update failed (e.g. because conflicts not resolved), `$HG_ERROR=1`.
544 update failed (e.g. because conflicts not resolved), `$HG_ERROR=1`.
545
545
546 NOTE: it is generally better to use standard hooks rather than the
546 NOTE: it is generally better to use standard hooks rather than the
547 generic pre- and post- command hooks as they are guaranteed to be
547 generic pre- and post- command hooks as they are guaranteed to be
548 called in the appropriate contexts for influencing transactions.
548 called in the appropriate contexts for influencing transactions.
549 Also, hooks like "commit" will be called in all contexts that
549 Also, hooks like "commit" will be called in all contexts that
550 generate a commit (e.g. tag) and not just the commit command.
550 generate a commit (e.g. tag) and not just the commit command.
551
551
552 NOTE: Environment variables with empty values may not be passed to
552 NOTE: Environment variables with empty values may not be passed to
553 hooks on platforms such as Windows. As an example, `$HG_PARENT2` will
553 hooks on platforms such as Windows. As an example, `$HG_PARENT2` will
554 have an empty value under Unix-like platforms for non-merge
554 have an empty value under Unix-like platforms for non-merge
555 changesets, while it will not be available at all under Windows.
555 changesets, while it will not be available at all under Windows.
556
556
557 The syntax for Python hooks is as follows:
557 The syntax for Python hooks is as follows:
558
558
559 hookname = python:modulename.submodule.callable
559 hookname = python:modulename.submodule.callable
560 hookname = python:/path/to/python/module.py:callable
560 hookname = python:/path/to/python/module.py:callable
561
561
562 Python hooks are run within the Mercurial process. Each hook is
562 Python hooks are run within the Mercurial process. Each hook is
563 called with at least three keyword arguments: a ui object (keyword
563 called with at least three keyword arguments: a ui object (keyword
564 "ui"), a repository object (keyword "repo"), and a "hooktype"
564 "ui"), a repository object (keyword "repo"), and a "hooktype"
565 keyword that tells what kind of hook is used. Arguments listed as
565 keyword that tells what kind of hook is used. Arguments listed as
566 environment variables above are passed as keyword arguments, with no
566 environment variables above are passed as keyword arguments, with no
567 "HG_" prefix, and names in lower case.
567 "HG_" prefix, and names in lower case.
568
568
569 If a Python hook returns a "true" value or raises an exception, this
569 If a Python hook returns a "true" value or raises an exception, this
570 is treated as a failure.
570 is treated as a failure.
571 --
571 --
572
572
573 [[http_proxy]]
573 [[http_proxy]]
574 http_proxy::
574 http_proxy::
575 Used to access web-based Mercurial repositories through a HTTP
575 Used to access web-based Mercurial repositories through a HTTP
576 proxy.
576 proxy.
577 host;;
577 host;;
578 Host name and (optional) port of the proxy server, for example
578 Host name and (optional) port of the proxy server, for example
579 "myproxy:8000".
579 "myproxy:8000".
580 no;;
580 no;;
581 Optional. Comma-separated list of host names that should bypass
581 Optional. Comma-separated list of host names that should bypass
582 the proxy.
582 the proxy.
583 passwd;;
583 passwd;;
584 Optional. Password to authenticate with at the proxy server.
584 Optional. Password to authenticate with at the proxy server.
585 user;;
585 user;;
586 Optional. User name to authenticate with at the proxy server.
586 Optional. User name to authenticate with at the proxy server.
587
587
588 [[smtp]]
588 [[smtp]]
589 smtp::
589 smtp::
590 Configuration for extensions that need to send email messages.
590 Configuration for extensions that need to send email messages.
591 host;;
591 host;;
592 Host name of mail server, e.g. "mail.example.com".
592 Host name of mail server, e.g. "mail.example.com".
593 port;;
593 port;;
594 Optional. Port to connect to on mail server. Default: 25.
594 Optional. Port to connect to on mail server. Default: 25.
595 tls;;
595 tls;;
596 Optional. Whether to connect to mail server using TLS. True or
596 Optional. Whether to connect to mail server using TLS. True or
597 False. Default: False.
597 False. Default: False.
598 username;;
598 username;;
599 Optional. User name to authenticate to SMTP server with. If
599 Optional. User name to authenticate to SMTP server with. If
600 username is specified, password must also be specified.
600 username is specified, password must also be specified.
601 Default: none.
601 Default: none.
602 password;;
602 password;;
603 Optional. Password to authenticate to SMTP server with. If
603 Optional. Password to authenticate to SMTP server with. If
604 username is specified, password must also be specified.
604 username is specified, password must also be specified.
605 Default: none.
605 Default: none.
606 local_hostname;;
606 local_hostname;;
607 Optional. It's the hostname that the sender can use to identify
607 Optional. It's the hostname that the sender can use to identify
608 itself to the MTA.
608 itself to the MTA.
609
609
610 [[patch]]
611 patch::
612 Settings used when applying patches, for instance through the 'import'
613 command or with Mercurial Queues extension.
614 eol;;
615 When set to 'strict' patch content and patched files end of lines
616 are preserved. When set to 'lf' or 'crlf', both files end of lines
617 are ignored when patching and the result line endings are
618 normalized to either LF (Unix) or CRLF (Windows).
619 Default: strict.
620
610 [[paths]]
621 [[paths]]
611 paths::
622 paths::
612 Assigns symbolic names to repositories. The left side is the
623 Assigns symbolic names to repositories. The left side is the
613 symbolic name, and the right gives the directory or URL that is the
624 symbolic name, and the right gives the directory or URL that is the
614 location of the repository. Default paths can be declared by setting
625 location of the repository. Default paths can be declared by setting
615 the following entries.
626 the following entries.
616 default;;
627 default;;
617 Directory or URL to use when pulling if no source is specified.
628 Directory or URL to use when pulling if no source is specified.
618 Default is set to repository from which the current repository was
629 Default is set to repository from which the current repository was
619 cloned.
630 cloned.
620 default-push;;
631 default-push;;
621 Optional. Directory or URL to use when pushing if no destination
632 Optional. Directory or URL to use when pushing if no destination
622 is specified.
633 is specified.
623
634
624 [[profiling]]
635 [[profiling]]
625 profiling::
636 profiling::
626 Specifies profiling format and file output. In this section
637 Specifies profiling format and file output. In this section
627 description, 'profiling data' stands for the raw data collected
638 description, 'profiling data' stands for the raw data collected
628 during profiling, while 'profiling report' stands for a statistical
639 during profiling, while 'profiling report' stands for a statistical
629 text report generated from the profiling data. The profiling is done
640 text report generated from the profiling data. The profiling is done
630 using lsprof.
641 using lsprof.
631 format;;
642 format;;
632 Profiling format.
643 Profiling format.
633 Default: text.
644 Default: text.
634 text;;
645 text;;
635 Generate a profiling report. When saving to a file, it should be
646 Generate a profiling report. When saving to a file, it should be
636 noted that only the report is saved, and the profiling data is
647 noted that only the report is saved, and the profiling data is
637 not kept.
648 not kept.
638 kcachegrind;;
649 kcachegrind;;
639 Format profiling data for kcachegrind use: when saving to a
650 Format profiling data for kcachegrind use: when saving to a
640 file, the generated file can directly be loaded into
651 file, the generated file can directly be loaded into
641 kcachegrind.
652 kcachegrind.
642 output;;
653 output;;
643 File path where profiling data or report should be saved. If the
654 File path where profiling data or report should be saved. If the
644 file exists, it is replaced. Default: None, data is printed on
655 file exists, it is replaced. Default: None, data is printed on
645 stderr
656 stderr
646
657
647 [[server]]
658 [[server]]
648 server::
659 server::
649 Controls generic server settings.
660 Controls generic server settings.
650 uncompressed;;
661 uncompressed;;
651 Whether to allow clients to clone a repository using the
662 Whether to allow clients to clone a repository using the
652 uncompressed streaming protocol. This transfers about 40% more
663 uncompressed streaming protocol. This transfers about 40% more
653 data than a regular clone, but uses less memory and CPU on both
664 data than a regular clone, but uses less memory and CPU on both
654 server and client. Over a LAN (100 Mbps or better) or a very fast
665 server and client. Over a LAN (100 Mbps or better) or a very fast
655 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
666 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
656 regular clone. Over most WAN connections (anything slower than
667 regular clone. Over most WAN connections (anything slower than
657 about 6 Mbps), uncompressed streaming is slower, because of the
668 about 6 Mbps), uncompressed streaming is slower, because of the
658 extra data transfer overhead. Default is False.
669 extra data transfer overhead. Default is False.
659
670
660 [[trusted]]
671 [[trusted]]
661 trusted::
672 trusted::
662 For security reasons, Mercurial will not use the settings in the
673 For security reasons, Mercurial will not use the settings in the
663 `.hg/hgrc` file from a repository if it doesn't belong to a trusted
674 `.hg/hgrc` file from a repository if it doesn't belong to a trusted
664 user or to a trusted group. The main exception is the web interface,
675 user or to a trusted group. The main exception is the web interface,
665 which automatically uses some safe settings, since it's common to
676 which automatically uses some safe settings, since it's common to
666 serve repositories from different users.
677 serve repositories from different users.
667 +
678 +
668 --
679 --
669 This section specifies what users and groups are trusted. The
680 This section specifies what users and groups are trusted. The
670 current user is always trusted. To trust everybody, list a user or a
681 current user is always trusted. To trust everybody, list a user or a
671 group with name "`*`".
682 group with name "`*`".
672
683
673 users;;
684 users;;
674 Comma-separated list of trusted users.
685 Comma-separated list of trusted users.
675 groups;;
686 groups;;
676 Comma-separated list of trusted groups.
687 Comma-separated list of trusted groups.
677 --
688 --
678
689
679 [[ui]]
690 [[ui]]
680 ui::
691 ui::
681 User interface controls.
692 User interface controls.
682 +
693 +
683 --
694 --
684 archivemeta;;
695 archivemeta;;
685 Whether to include the .hg_archival.txt file containing meta data
696 Whether to include the .hg_archival.txt file containing meta data
686 (hashes for the repository base and for tip) in archives created
697 (hashes for the repository base and for tip) in archives created
687 by the hg archive command or downloaded via hgweb.
698 by the hg archive command or downloaded via hgweb.
688 Default is true.
699 Default is true.
689 askusername;;
700 askusername;;
690 Whether to prompt for a username when committing. If True, and
701 Whether to prompt for a username when committing. If True, and
691 neither `$HGUSER` nor `$EMAIL` has been specified, then the user will
702 neither `$HGUSER` nor `$EMAIL` has been specified, then the user will
692 be prompted to enter a username. If no username is entered, the
703 be prompted to enter a username. If no username is entered, the
693 default USER@HOST is used instead.
704 default USER@HOST is used instead.
694 Default is False.
705 Default is False.
695 debug;;
706 debug;;
696 Print debugging information. True or False. Default is False.
707 Print debugging information. True or False. Default is False.
697 editor;;
708 editor;;
698 The editor to use during a commit. Default is `$EDITOR` or "vi".
709 The editor to use during a commit. Default is `$EDITOR` or "vi".
699 fallbackencoding;;
710 fallbackencoding;;
700 Encoding to try if it's not possible to decode the changelog using
711 Encoding to try if it's not possible to decode the changelog using
701 UTF-8. Default is ISO-8859-1.
712 UTF-8. Default is ISO-8859-1.
702 ignore;;
713 ignore;;
703 A file to read per-user ignore patterns from. This file should be
714 A file to read per-user ignore patterns from. This file should be
704 in the same format as a repository-wide .hgignore file. This
715 in the same format as a repository-wide .hgignore file. This
705 option supports hook syntax, so if you want to specify multiple
716 option supports hook syntax, so if you want to specify multiple
706 ignore files, you can do so by setting something like
717 ignore files, you can do so by setting something like
707 "ignore.other = ~/.hgignore2". For details of the ignore file
718 "ignore.other = ~/.hgignore2". For details of the ignore file
708 format, see the hgignore(5) man page.
719 format, see the hgignore(5) man page.
709 interactive;;
720 interactive;;
710 Allow to prompt the user. True or False. Default is True.
721 Allow to prompt the user. True or False. Default is True.
711 logtemplate;;
722 logtemplate;;
712 Template string for commands that print changesets.
723 Template string for commands that print changesets.
713 merge;;
724 merge;;
714 The conflict resolution program to use during a manual merge.
725 The conflict resolution program to use during a manual merge.
715 There are some internal tools available:
726 There are some internal tools available:
716 +
727 +
717 internal:local;;
728 internal:local;;
718 keep the local version
729 keep the local version
719 internal:other;;
730 internal:other;;
720 use the other version
731 use the other version
721 internal:merge;;
732 internal:merge;;
722 use the internal non-interactive merge tool
733 use the internal non-interactive merge tool
723 internal:fail;;
734 internal:fail;;
724 fail to merge
735 fail to merge
725 +
736 +
726 For more information on configuring merge tools see the
737 For more information on configuring merge tools see the
727 merge-tools section.
738 merge-tools section.
728
739
729 patch;;
740 patch;;
730 command to use to apply patches. Look for 'gpatch' or 'patch' in
741 command to use to apply patches. Look for 'gpatch' or 'patch' in
731 PATH if unset.
742 PATH if unset.
732 quiet;;
743 quiet;;
733 Reduce the amount of output printed. True or False. Default is False.
744 Reduce the amount of output printed. True or False. Default is False.
734 remotecmd;;
745 remotecmd;;
735 remote command to use for clone/push/pull operations. Default is 'hg'.
746 remote command to use for clone/push/pull operations. Default is 'hg'.
736 report_untrusted;;
747 report_untrusted;;
737 Warn if a `.hg/hgrc` file is ignored due to not being owned by a
748 Warn if a `.hg/hgrc` file is ignored due to not being owned by a
738 trusted user or group. True or False. Default is True.
749 trusted user or group. True or False. Default is True.
739 slash;;
750 slash;;
740 Display paths using a slash ("++/++") as the path separator. This
751 Display paths using a slash ("++/++") as the path separator. This
741 only makes a difference on systems where the default path
752 only makes a difference on systems where the default path
742 separator is not the slash character (e.g. Windows uses the
753 separator is not the slash character (e.g. Windows uses the
743 backslash character ("++\++")).
754 backslash character ("++\++")).
744 Default is False.
755 Default is False.
745 ssh;;
756 ssh;;
746 command to use for SSH connections. Default is 'ssh'.
757 command to use for SSH connections. Default is 'ssh'.
747 strict;;
758 strict;;
748 Require exact command names, instead of allowing unambiguous
759 Require exact command names, instead of allowing unambiguous
749 abbreviations. True or False. Default is False.
760 abbreviations. True or False. Default is False.
750 style;;
761 style;;
751 Name of style to use for command output.
762 Name of style to use for command output.
752 timeout;;
763 timeout;;
753 The timeout used when a lock is held (in seconds), a negative value
764 The timeout used when a lock is held (in seconds), a negative value
754 means no timeout. Default is 600.
765 means no timeout. Default is 600.
755 username;;
766 username;;
756 The committer of a changeset created when running "commit".
767 The committer of a changeset created when running "commit".
757 Typically a person's name and email address, e.g. "Fred Widget
768 Typically a person's name and email address, e.g. "Fred Widget
758 <fred@example.com>". Default is `$EMAIL` or username@hostname. If
769 <fred@example.com>". Default is `$EMAIL` or username@hostname. If
759 the username in hgrc is empty, it has to be specified manually or
770 the username in hgrc is empty, it has to be specified manually or
760 in a different hgrc file (e.g. `$HOME/.hgrc`, if the admin set
771 in a different hgrc file (e.g. `$HOME/.hgrc`, if the admin set
761 "username =" in the system hgrc).
772 "username =" in the system hgrc).
762 verbose;;
773 verbose;;
763 Increase the amount of output printed. True or False. Default is False.
774 Increase the amount of output printed. True or False. Default is False.
764 --
775 --
765
776
766 [[web]]
777 [[web]]
767 web::
778 web::
768 Web interface configuration.
779 Web interface configuration.
769 accesslog;;
780 accesslog;;
770 Where to output the access log. Default is stdout.
781 Where to output the access log. Default is stdout.
771 address;;
782 address;;
772 Interface address to bind to. Default is all.
783 Interface address to bind to. Default is all.
773 allow_archive;;
784 allow_archive;;
774 List of archive format (bz2, gz, zip) allowed for downloading.
785 List of archive format (bz2, gz, zip) allowed for downloading.
775 Default is empty.
786 Default is empty.
776 allowbz2;;
787 allowbz2;;
777 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
788 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
778 revisions.
789 revisions.
779 Default is false.
790 Default is false.
780 allowgz;;
791 allowgz;;
781 (DEPRECATED) Whether to allow .tar.gz downloading of repository
792 (DEPRECATED) Whether to allow .tar.gz downloading of repository
782 revisions.
793 revisions.
783 Default is false.
794 Default is false.
784 allowpull;;
795 allowpull;;
785 Whether to allow pulling from the repository. Default is true.
796 Whether to allow pulling from the repository. Default is true.
786 allow_push;;
797 allow_push;;
787 Whether to allow pushing to the repository. If empty or not set,
798 Whether to allow pushing to the repository. If empty or not set,
788 push is not allowed. If the special value "`*`", any remote user can
799 push is not allowed. If the special value "`*`", any remote user can
789 push, including unauthenticated users. Otherwise, the remote user
800 push, including unauthenticated users. Otherwise, the remote user
790 must have been authenticated, and the authenticated user name must
801 must have been authenticated, and the authenticated user name must
791 be present in this list (separated by whitespace or ","). The
802 be present in this list (separated by whitespace or ","). The
792 contents of the allow_push list are examined after the deny_push
803 contents of the allow_push list are examined after the deny_push
793 list.
804 list.
794 allow_read;;
805 allow_read;;
795 If the user has not already been denied repository access due to
806 If the user has not already been denied repository access due to
796 the contents of deny_read, this list determines whether to grant
807 the contents of deny_read, this list determines whether to grant
797 repository access to the user. If this list is not empty, and the
808 repository access to the user. If this list is not empty, and the
798 user is unauthenticated or not present in the list (separated by
809 user is unauthenticated or not present in the list (separated by
799 whitespace or ","), then access is denied for the user. If the
810 whitespace or ","), then access is denied for the user. If the
800 list is empty or not set, then access is permitted to all users by
811 list is empty or not set, then access is permitted to all users by
801 default. Setting allow_read to the special value "`*`" is equivalent
812 default. Setting allow_read to the special value "`*`" is equivalent
802 to it not being set (i.e. access is permitted to all users). The
813 to it not being set (i.e. access is permitted to all users). The
803 contents of the allow_read list are examined after the deny_read
814 contents of the allow_read list are examined after the deny_read
804 list.
815 list.
805 allowzip;;
816 allowzip;;
806 (DEPRECATED) Whether to allow .zip downloading of repository
817 (DEPRECATED) Whether to allow .zip downloading of repository
807 revisions. Default is false. This feature creates temporary files.
818 revisions. Default is false. This feature creates temporary files.
808 baseurl;;
819 baseurl;;
809 Base URL to use when publishing URLs in other locations, so
820 Base URL to use when publishing URLs in other locations, so
810 third-party tools like email notification hooks can construct
821 third-party tools like email notification hooks can construct
811 URLs. Example: "http://hgserver/repos/"
822 URLs. Example: "http://hgserver/repos/"
812 contact;;
823 contact;;
813 Name or email address of the person in charge of the repository.
824 Name or email address of the person in charge of the repository.
814 Defaults to ui.username or `$EMAIL` or "unknown" if unset or empty.
825 Defaults to ui.username or `$EMAIL` or "unknown" if unset or empty.
815 deny_push;;
826 deny_push;;
816 Whether to deny pushing to the repository. If empty or not set,
827 Whether to deny pushing to the repository. If empty or not set,
817 push is not denied. If the special value "`*`", all remote users are
828 push is not denied. If the special value "`*`", all remote users are
818 denied push. Otherwise, unauthenticated users are all denied, and
829 denied push. Otherwise, unauthenticated users are all denied, and
819 any authenticated user name present in this list (separated by
830 any authenticated user name present in this list (separated by
820 whitespace or ",") is also denied. The contents of the deny_push
831 whitespace or ",") is also denied. The contents of the deny_push
821 list are examined before the allow_push list.
832 list are examined before the allow_push list.
822 deny_read;;
833 deny_read;;
823 Whether to deny reading/viewing of the repository. If this list is
834 Whether to deny reading/viewing of the repository. If this list is
824 not empty, unauthenticated users are all denied, and any
835 not empty, unauthenticated users are all denied, and any
825 authenticated user name present in this list (separated by
836 authenticated user name present in this list (separated by
826 whitespace or ",") is also denied access to the repository. If set
837 whitespace or ",") is also denied access to the repository. If set
827 to the special value "`*`", all remote users are denied access
838 to the special value "`*`", all remote users are denied access
828 (rarely needed ;). If deny_read is empty or not set, the
839 (rarely needed ;). If deny_read is empty or not set, the
829 determination of repository access depends on the presence and
840 determination of repository access depends on the presence and
830 content of the allow_read list (see description). If both
841 content of the allow_read list (see description). If both
831 deny_read and allow_read are empty or not set, then access is
842 deny_read and allow_read are empty or not set, then access is
832 permitted to all users by default. If the repository is being
843 permitted to all users by default. If the repository is being
833 served via hgwebdir, denied users will not be able to see it in
844 served via hgwebdir, denied users will not be able to see it in
834 the list of repositories. The contents of the deny_read list have
845 the list of repositories. The contents of the deny_read list have
835 priority over (are examined before) the contents of the allow_read
846 priority over (are examined before) the contents of the allow_read
836 list.
847 list.
837 description;;
848 description;;
838 Textual description of the repository's purpose or contents.
849 Textual description of the repository's purpose or contents.
839 Default is "unknown".
850 Default is "unknown".
840 encoding;;
851 encoding;;
841 Character encoding name.
852 Character encoding name.
842 Example: "UTF-8"
853 Example: "UTF-8"
843 errorlog;;
854 errorlog;;
844 Where to output the error log. Default is stderr.
855 Where to output the error log. Default is stderr.
845 hidden;;
856 hidden;;
846 Whether to hide the repository in the hgwebdir index.
857 Whether to hide the repository in the hgwebdir index.
847 Default is false.
858 Default is false.
848 ipv6;;
859 ipv6;;
849 Whether to use IPv6. Default is false.
860 Whether to use IPv6. Default is false.
850 name;;
861 name;;
851 Repository name to use in the web interface. Default is current
862 Repository name to use in the web interface. Default is current
852 working directory.
863 working directory.
853 maxchanges;;
864 maxchanges;;
854 Maximum number of changes to list on the changelog. Default is 10.
865 Maximum number of changes to list on the changelog. Default is 10.
855 maxfiles;;
866 maxfiles;;
856 Maximum number of files to list per changeset. Default is 10.
867 Maximum number of files to list per changeset. Default is 10.
857 port;;
868 port;;
858 Port to listen on. Default is 8000.
869 Port to listen on. Default is 8000.
859 prefix;;
870 prefix;;
860 Prefix path to serve from. Default is '' (server root).
871 Prefix path to serve from. Default is '' (server root).
861 push_ssl;;
872 push_ssl;;
862 Whether to require that inbound pushes be transported over SSL to
873 Whether to require that inbound pushes be transported over SSL to
863 prevent password sniffing. Default is true.
874 prevent password sniffing. Default is true.
864 staticurl;;
875 staticurl;;
865 Base URL to use for static files. If unset, static files (e.g. the
876 Base URL to use for static files. If unset, static files (e.g. the
866 hgicon.png favicon) will be served by the CGI script itself. Use
877 hgicon.png favicon) will be served by the CGI script itself. Use
867 this setting to serve them directly with the HTTP server.
878 this setting to serve them directly with the HTTP server.
868 Example: "http://hgserver/static/"
879 Example: "http://hgserver/static/"
869 stripes;;
880 stripes;;
870 How many lines a "zebra stripe" should span in multiline output.
881 How many lines a "zebra stripe" should span in multiline output.
871 Default is 1; set to 0 to disable.
882 Default is 1; set to 0 to disable.
872 style;;
883 style;;
873 Which template map style to use.
884 Which template map style to use.
874 templates;;
885 templates;;
875 Where to find the HTML templates. Default is install path.
886 Where to find the HTML templates. Default is install path.
876
887
877
888
878 AUTHOR
889 AUTHOR
879 ------
890 ------
880 Bryan O'Sullivan <bos@serpentine.com>.
891 Bryan O'Sullivan <bos@serpentine.com>.
881
892
882 Mercurial was written by Matt Mackall <mpm@selenic.com>.
893 Mercurial was written by Matt Mackall <mpm@selenic.com>.
883
894
884 SEE ALSO
895 SEE ALSO
885 --------
896 --------
886 hg(1), hgignore(5)
897 hg(1), hgignore(5)
887
898
888 COPYING
899 COPYING
889 -------
900 -------
890 This manual page is copyright 2005 Bryan O'Sullivan.
901 This manual page is copyright 2005 Bryan O'Sullivan.
891 Mercurial is copyright 2005-2009 Matt Mackall.
902 Mercurial is copyright 2005-2009 Matt Mackall.
892 Free use of this software is granted under the terms of the GNU General
903 Free use of this software is granted under the terms of the GNU General
893 Public License (GPL).
904 Public License (GPL).
@@ -1,534 +1,534 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7 #
7 #
8 # $Id$
8 # $Id$
9 #
9 #
10 # Keyword expansion hack against the grain of a DSCM
10 # Keyword expansion hack against the grain of a DSCM
11 #
11 #
12 # There are many good reasons why this is not needed in a distributed
12 # There are many good reasons why this is not needed in a distributed
13 # SCM, still it may be useful in very small projects based on single
13 # SCM, still it may be useful in very small projects based on single
14 # files (like LaTeX packages), that are mostly addressed to an
14 # files (like LaTeX packages), that are mostly addressed to an
15 # audience not running a version control system.
15 # audience not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 #
19 #
20 # Keyword expansion is based on Mercurial's changeset template mappings.
20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 #
21 #
22 # Binary files are not touched.
22 # Binary files are not touched.
23 #
23 #
24 # Setup in hgrc:
24 # Setup in hgrc:
25 #
25 #
26 # [extensions]
26 # [extensions]
27 # # enable extension
27 # # enable extension
28 # hgext.keyword =
28 # hgext.keyword =
29 #
29 #
30 # Files to act upon/ignore are specified in the [keyword] section.
30 # Files to act upon/ignore are specified in the [keyword] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
32 #
32 #
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34
34
35 '''keyword expansion in local repositories
35 '''keyword expansion in local repositories
36
36
37 This extension expands RCS/CVS-like or self-customized $Keywords$ in
37 This extension expands RCS/CVS-like or self-customized $Keywords$ in
38 tracked text files selected by your configuration.
38 tracked text files selected by your configuration.
39
39
40 Keywords are only expanded in local repositories and not stored in the
40 Keywords are only expanded in local repositories and not stored in the
41 change history. The mechanism can be regarded as a convenience for the
41 change history. The mechanism can be regarded as a convenience for the
42 current user or for archive distribution.
42 current user or for archive distribution.
43
43
44 Configuration is done in the [keyword] and [keywordmaps] sections of
44 Configuration is done in the [keyword] and [keywordmaps] sections of
45 hgrc files.
45 hgrc files.
46
46
47 Example:
47 Example:
48
48
49 [keyword]
49 [keyword]
50 # expand keywords in every python file except those matching "x*"
50 # expand keywords in every python file except those matching "x*"
51 **.py =
51 **.py =
52 x* = ignore
52 x* = ignore
53
53
54 Note: the more specific you are in your filename patterns
54 Note: the more specific you are in your filename patterns
55 the less you lose speed in huge repositories.
55 the less you lose speed in huge repositories.
56
56
57 For [keywordmaps] template mapping and expansion demonstration and
57 For [keywordmaps] template mapping and expansion demonstration and
58 control run "hg kwdemo".
58 control run "hg kwdemo".
59
59
60 An additional date template filter {date|utcdate} is provided.
60 An additional date template filter {date|utcdate} is provided.
61
61
62 The default template mappings (view with "hg kwdemo -d") can be
62 The default template mappings (view with "hg kwdemo -d") can be
63 replaced with customized keywords and templates. Again, run "hg
63 replaced with customized keywords and templates. Again, run "hg
64 kwdemo" to control the results of your config changes.
64 kwdemo" to control the results of your config changes.
65
65
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 the risk of inadvertently storing expanded keywords in the change
67 the risk of inadvertently storing expanded keywords in the change
68 history.
68 history.
69
69
70 To force expansion after enabling it, or a configuration change, run
70 To force expansion after enabling it, or a configuration change, run
71 "hg kwexpand".
71 "hg kwexpand".
72
72
73 Also, when committing with the record extension or using mq's qrecord,
73 Also, when committing with the record extension or using mq's qrecord,
74 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
74 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
75 the files in question to update keyword expansions after all changes
75 the files in question to update keyword expansions after all changes
76 have been checked in.
76 have been checked in.
77
77
78 Expansions spanning more than one line and incremental expansions,
78 Expansions spanning more than one line and incremental expansions,
79 like CVS' $Log$, are not supported. A keyword template map
79 like CVS' $Log$, are not supported. A keyword template map
80 "Log = {desc}" expands to the first line of the changeset description.
80 "Log = {desc}" expands to the first line of the changeset description.
81 '''
81 '''
82
82
83 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
83 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
84 from mercurial import patch, localrepo, templater, templatefilters, util, match
84 from mercurial import patch, localrepo, templater, templatefilters, util, match
85 from mercurial.hgweb import webcommands
85 from mercurial.hgweb import webcommands
86 from mercurial.lock import release
86 from mercurial.lock import release
87 from mercurial.node import nullid, hex
87 from mercurial.node import nullid, hex
88 from mercurial.i18n import _
88 from mercurial.i18n import _
89 import re, shutil, tempfile, time
89 import re, shutil, tempfile, time
90
90
91 commands.optionalrepo += ' kwdemo'
91 commands.optionalrepo += ' kwdemo'
92
92
93 # hg commands that do not act on keywords
93 # hg commands that do not act on keywords
94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
95 ' log outgoing push rename rollback tip verify'
95 ' log outgoing push rename rollback tip verify'
96 ' convert email glog')
96 ' convert email glog')
97
97
98 # hg commands that trigger expansion only when writing to working dir,
98 # hg commands that trigger expansion only when writing to working dir,
99 # not when reading filelog, and unexpand when reading from working dir
99 # not when reading filelog, and unexpand when reading from working dir
100 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
100 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
101
101
102 def utcdate(date):
102 def utcdate(date):
103 '''Returns hgdate in cvs-like UTC format.'''
103 '''Returns hgdate in cvs-like UTC format.'''
104 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
104 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
105
105
106 # make keyword tools accessible
106 # make keyword tools accessible
107 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
107 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
108
108
109
109
110 class kwtemplater(object):
110 class kwtemplater(object):
111 '''
111 '''
112 Sets up keyword templates, corresponding keyword regex, and
112 Sets up keyword templates, corresponding keyword regex, and
113 provides keyword substitution functions.
113 provides keyword substitution functions.
114 '''
114 '''
115 templates = {
115 templates = {
116 'Revision': '{node|short}',
116 'Revision': '{node|short}',
117 'Author': '{author|user}',
117 'Author': '{author|user}',
118 'Date': '{date|utcdate}',
118 'Date': '{date|utcdate}',
119 'RCSFile': '{file|basename},v',
119 'RCSFile': '{file|basename},v',
120 'Source': '{root}/{file},v',
120 'Source': '{root}/{file},v',
121 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
121 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
122 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
122 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
123 }
123 }
124
124
125 def __init__(self, ui, repo):
125 def __init__(self, ui, repo):
126 self.ui = ui
126 self.ui = ui
127 self.repo = repo
127 self.repo = repo
128 self.match = match.match(repo.root, '', [],
128 self.match = match.match(repo.root, '', [],
129 kwtools['inc'], kwtools['exc'])
129 kwtools['inc'], kwtools['exc'])
130 self.restrict = kwtools['hgcmd'] in restricted.split()
130 self.restrict = kwtools['hgcmd'] in restricted.split()
131
131
132 kwmaps = self.ui.configitems('keywordmaps')
132 kwmaps = self.ui.configitems('keywordmaps')
133 if kwmaps: # override default templates
133 if kwmaps: # override default templates
134 kwmaps = [(k, templater.parsestring(v, False))
134 kwmaps = [(k, templater.parsestring(v, False))
135 for (k, v) in kwmaps]
135 for (k, v) in kwmaps]
136 self.templates = dict(kwmaps)
136 self.templates = dict(kwmaps)
137 escaped = map(re.escape, self.templates.keys())
137 escaped = map(re.escape, self.templates.keys())
138 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
138 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
139 self.re_kw = re.compile(kwpat)
139 self.re_kw = re.compile(kwpat)
140
140
141 templatefilters.filters['utcdate'] = utcdate
141 templatefilters.filters['utcdate'] = utcdate
142 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
142 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
143 False, None, '', False)
143 False, None, '', False)
144
144
145 def substitute(self, data, path, ctx, subfunc):
145 def substitute(self, data, path, ctx, subfunc):
146 '''Replaces keywords in data with expanded template.'''
146 '''Replaces keywords in data with expanded template.'''
147 def kwsub(mobj):
147 def kwsub(mobj):
148 kw = mobj.group(1)
148 kw = mobj.group(1)
149 self.ct.use_template(self.templates[kw])
149 self.ct.use_template(self.templates[kw])
150 self.ui.pushbuffer()
150 self.ui.pushbuffer()
151 self.ct.show(ctx, root=self.repo.root, file=path)
151 self.ct.show(ctx, root=self.repo.root, file=path)
152 ekw = templatefilters.firstline(self.ui.popbuffer())
152 ekw = templatefilters.firstline(self.ui.popbuffer())
153 return '$%s: %s $' % (kw, ekw)
153 return '$%s: %s $' % (kw, ekw)
154 return subfunc(kwsub, data)
154 return subfunc(kwsub, data)
155
155
156 def expand(self, path, node, data):
156 def expand(self, path, node, data):
157 '''Returns data with keywords expanded.'''
157 '''Returns data with keywords expanded.'''
158 if not self.restrict and self.match(path) and not util.binary(data):
158 if not self.restrict and self.match(path) and not util.binary(data):
159 ctx = self.repo.filectx(path, fileid=node).changectx()
159 ctx = self.repo.filectx(path, fileid=node).changectx()
160 return self.substitute(data, path, ctx, self.re_kw.sub)
160 return self.substitute(data, path, ctx, self.re_kw.sub)
161 return data
161 return data
162
162
163 def iskwfile(self, path, flagfunc):
163 def iskwfile(self, path, flagfunc):
164 '''Returns true if path matches [keyword] pattern
164 '''Returns true if path matches [keyword] pattern
165 and is not a symbolic link.
165 and is not a symbolic link.
166 Caveat: localrepository._link fails on Windows.'''
166 Caveat: localrepository._link fails on Windows.'''
167 return self.match(path) and not 'l' in flagfunc(path)
167 return self.match(path) and not 'l' in flagfunc(path)
168
168
169 def overwrite(self, node, expand, files):
169 def overwrite(self, node, expand, files):
170 '''Overwrites selected files expanding/shrinking keywords.'''
170 '''Overwrites selected files expanding/shrinking keywords.'''
171 ctx = self.repo[node]
171 ctx = self.repo[node]
172 mf = ctx.manifest()
172 mf = ctx.manifest()
173 if node is not None: # commit
173 if node is not None: # commit
174 files = [f for f in ctx.files() if f in mf]
174 files = [f for f in ctx.files() if f in mf]
175 notify = self.ui.debug
175 notify = self.ui.debug
176 else: # kwexpand/kwshrink
176 else: # kwexpand/kwshrink
177 notify = self.ui.note
177 notify = self.ui.note
178 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
178 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
179 if candidates:
179 if candidates:
180 self.restrict = True # do not expand when reading
180 self.restrict = True # do not expand when reading
181 msg = (expand and _('overwriting %s expanding keywords\n')
181 msg = (expand and _('overwriting %s expanding keywords\n')
182 or _('overwriting %s shrinking keywords\n'))
182 or _('overwriting %s shrinking keywords\n'))
183 for f in candidates:
183 for f in candidates:
184 fp = self.repo.file(f)
184 fp = self.repo.file(f)
185 data = fp.read(mf[f])
185 data = fp.read(mf[f])
186 if util.binary(data):
186 if util.binary(data):
187 continue
187 continue
188 if expand:
188 if expand:
189 if node is None:
189 if node is None:
190 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
190 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
191 data, found = self.substitute(data, f, ctx,
191 data, found = self.substitute(data, f, ctx,
192 self.re_kw.subn)
192 self.re_kw.subn)
193 else:
193 else:
194 found = self.re_kw.search(data)
194 found = self.re_kw.search(data)
195 if found:
195 if found:
196 notify(msg % f)
196 notify(msg % f)
197 self.repo.wwrite(f, data, mf.flags(f))
197 self.repo.wwrite(f, data, mf.flags(f))
198 self.repo.dirstate.normal(f)
198 self.repo.dirstate.normal(f)
199 self.restrict = False
199 self.restrict = False
200
200
201 def shrinktext(self, text):
201 def shrinktext(self, text):
202 '''Unconditionally removes all keyword substitutions from text.'''
202 '''Unconditionally removes all keyword substitutions from text.'''
203 return self.re_kw.sub(r'$\1$', text)
203 return self.re_kw.sub(r'$\1$', text)
204
204
205 def shrink(self, fname, text):
205 def shrink(self, fname, text):
206 '''Returns text with all keyword substitutions removed.'''
206 '''Returns text with all keyword substitutions removed.'''
207 if self.match(fname) and not util.binary(text):
207 if self.match(fname) and not util.binary(text):
208 return self.shrinktext(text)
208 return self.shrinktext(text)
209 return text
209 return text
210
210
211 def shrinklines(self, fname, lines):
211 def shrinklines(self, fname, lines):
212 '''Returns lines with keyword substitutions removed.'''
212 '''Returns lines with keyword substitutions removed.'''
213 if self.match(fname):
213 if self.match(fname):
214 text = ''.join(lines)
214 text = ''.join(lines)
215 if not util.binary(text):
215 if not util.binary(text):
216 return self.shrinktext(text).splitlines(True)
216 return self.shrinktext(text).splitlines(True)
217 return lines
217 return lines
218
218
219 def wread(self, fname, data):
219 def wread(self, fname, data):
220 '''If in restricted mode returns data read from wdir with
220 '''If in restricted mode returns data read from wdir with
221 keyword substitutions removed.'''
221 keyword substitutions removed.'''
222 return self.restrict and self.shrink(fname, data) or data
222 return self.restrict and self.shrink(fname, data) or data
223
223
224 class kwfilelog(filelog.filelog):
224 class kwfilelog(filelog.filelog):
225 '''
225 '''
226 Subclass of filelog to hook into its read, add, cmp methods.
226 Subclass of filelog to hook into its read, add, cmp methods.
227 Keywords are "stored" unexpanded, and processed on reading.
227 Keywords are "stored" unexpanded, and processed on reading.
228 '''
228 '''
229 def __init__(self, opener, kwt, path):
229 def __init__(self, opener, kwt, path):
230 super(kwfilelog, self).__init__(opener, path)
230 super(kwfilelog, self).__init__(opener, path)
231 self.kwt = kwt
231 self.kwt = kwt
232 self.path = path
232 self.path = path
233
233
234 def read(self, node):
234 def read(self, node):
235 '''Expands keywords when reading filelog.'''
235 '''Expands keywords when reading filelog.'''
236 data = super(kwfilelog, self).read(node)
236 data = super(kwfilelog, self).read(node)
237 return self.kwt.expand(self.path, node, data)
237 return self.kwt.expand(self.path, node, data)
238
238
239 def add(self, text, meta, tr, link, p1=None, p2=None):
239 def add(self, text, meta, tr, link, p1=None, p2=None):
240 '''Removes keyword substitutions when adding to filelog.'''
240 '''Removes keyword substitutions when adding to filelog.'''
241 text = self.kwt.shrink(self.path, text)
241 text = self.kwt.shrink(self.path, text)
242 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
242 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
243
243
244 def cmp(self, node, text):
244 def cmp(self, node, text):
245 '''Removes keyword substitutions for comparison.'''
245 '''Removes keyword substitutions for comparison.'''
246 text = self.kwt.shrink(self.path, text)
246 text = self.kwt.shrink(self.path, text)
247 if self.renamed(node):
247 if self.renamed(node):
248 t2 = super(kwfilelog, self).read(node)
248 t2 = super(kwfilelog, self).read(node)
249 return t2 != text
249 return t2 != text
250 return revlog.revlog.cmp(self, node, text)
250 return revlog.revlog.cmp(self, node, text)
251
251
252 def _status(ui, repo, kwt, unknown, *pats, **opts):
252 def _status(ui, repo, kwt, unknown, *pats, **opts):
253 '''Bails out if [keyword] configuration is not active.
253 '''Bails out if [keyword] configuration is not active.
254 Returns status of working directory.'''
254 Returns status of working directory.'''
255 if kwt:
255 if kwt:
256 match = cmdutil.match(repo, pats, opts)
256 match = cmdutil.match(repo, pats, opts)
257 return repo.status(match=match, unknown=unknown, clean=True)
257 return repo.status(match=match, unknown=unknown, clean=True)
258 if ui.configitems('keyword'):
258 if ui.configitems('keyword'):
259 raise util.Abort(_('[keyword] patterns cannot match'))
259 raise util.Abort(_('[keyword] patterns cannot match'))
260 raise util.Abort(_('no [keyword] patterns configured'))
260 raise util.Abort(_('no [keyword] patterns configured'))
261
261
262 def _kwfwrite(ui, repo, expand, *pats, **opts):
262 def _kwfwrite(ui, repo, expand, *pats, **opts):
263 '''Selects files and passes them to kwtemplater.overwrite.'''
263 '''Selects files and passes them to kwtemplater.overwrite.'''
264 if repo.dirstate.parents()[1] != nullid:
264 if repo.dirstate.parents()[1] != nullid:
265 raise util.Abort(_('outstanding uncommitted merge'))
265 raise util.Abort(_('outstanding uncommitted merge'))
266 kwt = kwtools['templater']
266 kwt = kwtools['templater']
267 status = _status(ui, repo, kwt, False, *pats, **opts)
267 status = _status(ui, repo, kwt, False, *pats, **opts)
268 modified, added, removed, deleted = status[:4]
268 modified, added, removed, deleted = status[:4]
269 if modified or added or removed or deleted:
269 if modified or added or removed or deleted:
270 raise util.Abort(_('outstanding uncommitted changes'))
270 raise util.Abort(_('outstanding uncommitted changes'))
271 wlock = lock = None
271 wlock = lock = None
272 try:
272 try:
273 wlock = repo.wlock()
273 wlock = repo.wlock()
274 lock = repo.lock()
274 lock = repo.lock()
275 kwt.overwrite(None, expand, status[6])
275 kwt.overwrite(None, expand, status[6])
276 finally:
276 finally:
277 release(lock, wlock)
277 release(lock, wlock)
278
278
279 def demo(ui, repo, *args, **opts):
279 def demo(ui, repo, *args, **opts):
280 '''print [keywordmaps] configuration and an expansion example
280 '''print [keywordmaps] configuration and an expansion example
281
281
282 Show current, custom, or default keyword template maps and their
282 Show current, custom, or default keyword template maps and their
283 expansions.
283 expansions.
284
284
285 Extend current configuration by specifying maps as arguments and
285 Extend current configuration by specifying maps as arguments and
286 optionally by reading from an additional hgrc file.
286 optionally by reading from an additional hgrc file.
287
287
288 Override current keyword template maps with "default" option.
288 Override current keyword template maps with "default" option.
289 '''
289 '''
290 def demostatus(stat):
290 def demostatus(stat):
291 ui.status(_('\n\t%s\n') % stat)
291 ui.status(_('\n\t%s\n') % stat)
292
292
293 def demoitems(section, items):
293 def demoitems(section, items):
294 ui.write('[%s]\n' % section)
294 ui.write('[%s]\n' % section)
295 for k, v in items:
295 for k, v in items:
296 ui.write('%s = %s\n' % (k, v))
296 ui.write('%s = %s\n' % (k, v))
297
297
298 msg = 'hg keyword config and expansion example'
298 msg = 'hg keyword config and expansion example'
299 kwstatus = 'current'
299 kwstatus = 'current'
300 fn = 'demo.txt'
300 fn = 'demo.txt'
301 branchname = 'demobranch'
301 branchname = 'demobranch'
302 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
302 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
303 ui.note(_('creating temporary repository at %s\n') % tmpdir)
303 ui.note(_('creating temporary repository at %s\n') % tmpdir)
304 repo = localrepo.localrepository(ui, tmpdir, True)
304 repo = localrepo.localrepository(ui, tmpdir, True)
305 ui.setconfig('keyword', fn, '')
305 ui.setconfig('keyword', fn, '')
306 if args or opts.get('rcfile'):
306 if args or opts.get('rcfile'):
307 kwstatus = 'custom'
307 kwstatus = 'custom'
308 if opts.get('rcfile'):
308 if opts.get('rcfile'):
309 ui.readconfig(opts.get('rcfile'))
309 ui.readconfig(opts.get('rcfile'))
310 if opts.get('default'):
310 if opts.get('default'):
311 kwstatus = 'default'
311 kwstatus = 'default'
312 kwmaps = kwtemplater.templates
312 kwmaps = kwtemplater.templates
313 if ui.configitems('keywordmaps'):
313 if ui.configitems('keywordmaps'):
314 # override maps from optional rcfile
314 # override maps from optional rcfile
315 for k, v in kwmaps.iteritems():
315 for k, v in kwmaps.iteritems():
316 ui.setconfig('keywordmaps', k, v)
316 ui.setconfig('keywordmaps', k, v)
317 elif args:
317 elif args:
318 # simulate hgrc parsing
318 # simulate hgrc parsing
319 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
319 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
320 fp = repo.opener('hgrc', 'w')
320 fp = repo.opener('hgrc', 'w')
321 fp.writelines(rcmaps)
321 fp.writelines(rcmaps)
322 fp.close()
322 fp.close()
323 ui.readconfig(repo.join('hgrc'))
323 ui.readconfig(repo.join('hgrc'))
324 if not opts.get('default'):
324 if not opts.get('default'):
325 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
325 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
326 uisetup(ui)
326 uisetup(ui)
327 reposetup(ui, repo)
327 reposetup(ui, repo)
328 for k, v in ui.configitems('extensions'):
328 for k, v in ui.configitems('extensions'):
329 if k.endswith('keyword'):
329 if k.endswith('keyword'):
330 extension = '%s = %s' % (k, v)
330 extension = '%s = %s' % (k, v)
331 break
331 break
332 demostatus('config using %s keyword template maps' % kwstatus)
332 demostatus('config using %s keyword template maps' % kwstatus)
333 ui.write('[extensions]\n%s\n' % extension)
333 ui.write('[extensions]\n%s\n' % extension)
334 demoitems('keyword', ui.configitems('keyword'))
334 demoitems('keyword', ui.configitems('keyword'))
335 demoitems('keywordmaps', kwmaps.iteritems())
335 demoitems('keywordmaps', kwmaps.iteritems())
336 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
336 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
337 repo.wopener(fn, 'w').write(keywords)
337 repo.wopener(fn, 'w').write(keywords)
338 repo.add([fn])
338 repo.add([fn])
339 path = repo.wjoin(fn)
339 path = repo.wjoin(fn)
340 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
340 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
341 ui.note(keywords)
341 ui.note(keywords)
342 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
342 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
343 # silence branch command if not verbose
343 # silence branch command if not verbose
344 quiet = ui.quiet
344 quiet = ui.quiet
345 ui.quiet = not ui.verbose
345 ui.quiet = not ui.verbose
346 commands.branch(ui, repo, branchname)
346 commands.branch(ui, repo, branchname)
347 ui.quiet = quiet
347 ui.quiet = quiet
348 for name, cmd in ui.configitems('hooks'):
348 for name, cmd in ui.configitems('hooks'):
349 if name.split('.', 1)[0].find('commit') > -1:
349 if name.split('.', 1)[0].find('commit') > -1:
350 repo.ui.setconfig('hooks', name, '')
350 repo.ui.setconfig('hooks', name, '')
351 ui.note(_('unhooked all commit hooks\n'))
351 ui.note(_('unhooked all commit hooks\n'))
352 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
352 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
353 repo.commit(text=msg)
353 repo.commit(text=msg)
354 fmt = ui.verbose and ' in %s' % path or ''
354 fmt = ui.verbose and ' in %s' % path or ''
355 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
355 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
356 ui.write(repo.wread(fn))
356 ui.write(repo.wread(fn))
357 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
357 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
358 shutil.rmtree(tmpdir, ignore_errors=True)
358 shutil.rmtree(tmpdir, ignore_errors=True)
359
359
360 def expand(ui, repo, *pats, **opts):
360 def expand(ui, repo, *pats, **opts):
361 '''expand keywords in the working directory
361 '''expand keywords in the working directory
362
362
363 Run after (re)enabling keyword expansion.
363 Run after (re)enabling keyword expansion.
364
364
365 kwexpand refuses to run if given files contain local changes.
365 kwexpand refuses to run if given files contain local changes.
366 '''
366 '''
367 # 3rd argument sets expansion to True
367 # 3rd argument sets expansion to True
368 _kwfwrite(ui, repo, True, *pats, **opts)
368 _kwfwrite(ui, repo, True, *pats, **opts)
369
369
370 def files(ui, repo, *pats, **opts):
370 def files(ui, repo, *pats, **opts):
371 '''print files currently configured for keyword expansion
371 '''print files currently configured for keyword expansion
372
372
373 Crosscheck which files in working directory are potential targets
373 Crosscheck which files in working directory are potential targets
374 for keyword expansion. That is, files matched by [keyword] config
374 for keyword expansion. That is, files matched by [keyword] config
375 patterns but not symlinks.
375 patterns but not symlinks.
376 '''
376 '''
377 kwt = kwtools['templater']
377 kwt = kwtools['templater']
378 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
378 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
379 modified, added, removed, deleted, unknown, ignored, clean = status
379 modified, added, removed, deleted, unknown, ignored, clean = status
380 files = sorted(modified + added + clean + unknown)
380 files = sorted(modified + added + clean + unknown)
381 wctx = repo[None]
381 wctx = repo[None]
382 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
382 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
383 cwd = pats and repo.getcwd() or ''
383 cwd = pats and repo.getcwd() or ''
384 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
384 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
385 if opts.get('all') or opts.get('ignore'):
385 if opts.get('all') or opts.get('ignore'):
386 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
386 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
387 for char, filenames in kwfstats:
387 for char, filenames in kwfstats:
388 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
388 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
389 for f in filenames:
389 for f in filenames:
390 ui.write(fmt % repo.pathto(f, cwd))
390 ui.write(fmt % repo.pathto(f, cwd))
391
391
392 def shrink(ui, repo, *pats, **opts):
392 def shrink(ui, repo, *pats, **opts):
393 '''revert expanded keywords in the working directory
393 '''revert expanded keywords in the working directory
394
394
395 Run before changing/disabling active keywords or if you experience
395 Run before changing/disabling active keywords or if you experience
396 problems with "hg import" or "hg merge".
396 problems with "hg import" or "hg merge".
397
397
398 kwshrink refuses to run if given files contain local changes.
398 kwshrink refuses to run if given files contain local changes.
399 '''
399 '''
400 # 3rd argument sets expansion to False
400 # 3rd argument sets expansion to False
401 _kwfwrite(ui, repo, False, *pats, **opts)
401 _kwfwrite(ui, repo, False, *pats, **opts)
402
402
403
403
404 def uisetup(ui):
404 def uisetup(ui):
405 '''Collects [keyword] config in kwtools.
405 '''Collects [keyword] config in kwtools.
406 Monkeypatches dispatch._parse if needed.'''
406 Monkeypatches dispatch._parse if needed.'''
407
407
408 for pat, opt in ui.configitems('keyword'):
408 for pat, opt in ui.configitems('keyword'):
409 if opt != 'ignore':
409 if opt != 'ignore':
410 kwtools['inc'].append(pat)
410 kwtools['inc'].append(pat)
411 else:
411 else:
412 kwtools['exc'].append(pat)
412 kwtools['exc'].append(pat)
413
413
414 if kwtools['inc']:
414 if kwtools['inc']:
415 def kwdispatch_parse(orig, ui, args):
415 def kwdispatch_parse(orig, ui, args):
416 '''Monkeypatch dispatch._parse to obtain running hg command.'''
416 '''Monkeypatch dispatch._parse to obtain running hg command.'''
417 cmd, func, args, options, cmdoptions = orig(ui, args)
417 cmd, func, args, options, cmdoptions = orig(ui, args)
418 kwtools['hgcmd'] = cmd
418 kwtools['hgcmd'] = cmd
419 return cmd, func, args, options, cmdoptions
419 return cmd, func, args, options, cmdoptions
420
420
421 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
421 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
422
422
423 def reposetup(ui, repo):
423 def reposetup(ui, repo):
424 '''Sets up repo as kwrepo for keyword substitution.
424 '''Sets up repo as kwrepo for keyword substitution.
425 Overrides file method to return kwfilelog instead of filelog
425 Overrides file method to return kwfilelog instead of filelog
426 if file matches user configuration.
426 if file matches user configuration.
427 Wraps commit to overwrite configured files with updated
427 Wraps commit to overwrite configured files with updated
428 keyword substitutions.
428 keyword substitutions.
429 Monkeypatches patch and webcommands.'''
429 Monkeypatches patch and webcommands.'''
430
430
431 try:
431 try:
432 if (not repo.local() or not kwtools['inc']
432 if (not repo.local() or not kwtools['inc']
433 or kwtools['hgcmd'] in nokwcommands.split()
433 or kwtools['hgcmd'] in nokwcommands.split()
434 or '.hg' in util.splitpath(repo.root)
434 or '.hg' in util.splitpath(repo.root)
435 or repo._url.startswith('bundle:')):
435 or repo._url.startswith('bundle:')):
436 return
436 return
437 except AttributeError:
437 except AttributeError:
438 pass
438 pass
439
439
440 kwtools['templater'] = kwt = kwtemplater(ui, repo)
440 kwtools['templater'] = kwt = kwtemplater(ui, repo)
441
441
442 class kwrepo(repo.__class__):
442 class kwrepo(repo.__class__):
443 def file(self, f):
443 def file(self, f):
444 if f[0] == '/':
444 if f[0] == '/':
445 f = f[1:]
445 f = f[1:]
446 return kwfilelog(self.sopener, kwt, f)
446 return kwfilelog(self.sopener, kwt, f)
447
447
448 def wread(self, filename):
448 def wread(self, filename):
449 data = super(kwrepo, self).wread(filename)
449 data = super(kwrepo, self).wread(filename)
450 return kwt.wread(filename, data)
450 return kwt.wread(filename, data)
451
451
452 def commit(self, text='', user=None, date=None, match=None,
452 def commit(self, text='', user=None, date=None, match=None,
453 force=False, editor=None, extra={}):
453 force=False, editor=None, extra={}):
454 wlock = lock = None
454 wlock = lock = None
455 _p1 = _p2 = None
455 _p1 = _p2 = None
456 try:
456 try:
457 wlock = self.wlock()
457 wlock = self.wlock()
458 lock = self.lock()
458 lock = self.lock()
459 # store and postpone commit hooks
459 # store and postpone commit hooks
460 commithooks = {}
460 commithooks = {}
461 for name, cmd in ui.configitems('hooks'):
461 for name, cmd in ui.configitems('hooks'):
462 if name.split('.', 1)[0] == 'commit':
462 if name.split('.', 1)[0] == 'commit':
463 commithooks[name] = cmd
463 commithooks[name] = cmd
464 ui.setconfig('hooks', name, None)
464 ui.setconfig('hooks', name, None)
465 if commithooks:
465 if commithooks:
466 # store parents for commit hook environment
466 # store parents for commit hook environment
467 _p1, _p2 = repo.dirstate.parents()
467 _p1, _p2 = repo.dirstate.parents()
468 _p1 = hex(_p1)
468 _p1 = hex(_p1)
469 if _p2 == nullid:
469 if _p2 == nullid:
470 _p2 = ''
470 _p2 = ''
471 else:
471 else:
472 _p2 = hex(_p2)
472 _p2 = hex(_p2)
473
473
474 n = super(kwrepo, self).commit(text, user, date, match, force,
474 n = super(kwrepo, self).commit(text, user, date, match, force,
475 editor, extra)
475 editor, extra)
476
476
477 # restore commit hooks
477 # restore commit hooks
478 for name, cmd in commithooks.iteritems():
478 for name, cmd in commithooks.iteritems():
479 ui.setconfig('hooks', name, cmd)
479 ui.setconfig('hooks', name, cmd)
480 if n is not None:
480 if n is not None:
481 kwt.overwrite(n, True, None)
481 kwt.overwrite(n, True, None)
482 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
482 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
483 return n
483 return n
484 finally:
484 finally:
485 release(lock, wlock)
485 release(lock, wlock)
486
486
487 # monkeypatches
487 # monkeypatches
488 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
488 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False, eol=None):
489 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
489 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
490 rejects or conflicts due to expanded keywords in working dir.'''
490 rejects or conflicts due to expanded keywords in working dir.'''
491 orig(self, ui, fname, opener, missing)
491 orig(self, ui, fname, opener, missing, eol)
492 # shrink keywords read from working dir
492 # shrink keywords read from working dir
493 self.lines = kwt.shrinklines(self.fname, self.lines)
493 self.lines = kwt.shrinklines(self.fname, self.lines)
494
494
495 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
495 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
496 opts=None):
496 opts=None):
497 '''Monkeypatch patch.diff to avoid expansion except when
497 '''Monkeypatch patch.diff to avoid expansion except when
498 comparing against working dir.'''
498 comparing against working dir.'''
499 if node2 is not None:
499 if node2 is not None:
500 kwt.match = util.never
500 kwt.match = util.never
501 elif node1 is not None and node1 != repo['.'].node():
501 elif node1 is not None and node1 != repo['.'].node():
502 kwt.restrict = True
502 kwt.restrict = True
503 return orig(repo, node1, node2, match, changes, opts)
503 return orig(repo, node1, node2, match, changes, opts)
504
504
505 def kwweb_skip(orig, web, req, tmpl):
505 def kwweb_skip(orig, web, req, tmpl):
506 '''Wraps webcommands.x turning off keyword expansion.'''
506 '''Wraps webcommands.x turning off keyword expansion.'''
507 kwt.match = util.never
507 kwt.match = util.never
508 return orig(web, req, tmpl)
508 return orig(web, req, tmpl)
509
509
510 repo.__class__ = kwrepo
510 repo.__class__ = kwrepo
511
511
512 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
512 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
513 extensions.wrapfunction(patch, 'diff', kw_diff)
513 extensions.wrapfunction(patch, 'diff', kw_diff)
514 for c in 'annotate changeset rev filediff diff'.split():
514 for c in 'annotate changeset rev filediff diff'.split():
515 extensions.wrapfunction(webcommands, c, kwweb_skip)
515 extensions.wrapfunction(webcommands, c, kwweb_skip)
516
516
517 cmdtable = {
517 cmdtable = {
518 'kwdemo':
518 'kwdemo':
519 (demo,
519 (demo,
520 [('d', 'default', None, _('show default keyword template maps')),
520 [('d', 'default', None, _('show default keyword template maps')),
521 ('f', 'rcfile', [], _('read maps from rcfile'))],
521 ('f', 'rcfile', [], _('read maps from rcfile'))],
522 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
522 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
523 'kwexpand': (expand, commands.walkopts,
523 'kwexpand': (expand, commands.walkopts,
524 _('hg kwexpand [OPTION]... [FILE]...')),
524 _('hg kwexpand [OPTION]... [FILE]...')),
525 'kwfiles':
525 'kwfiles':
526 (files,
526 (files,
527 [('a', 'all', None, _('show keyword status flags of all files')),
527 [('a', 'all', None, _('show keyword status flags of all files')),
528 ('i', 'ignore', None, _('show files excluded from expansion')),
528 ('i', 'ignore', None, _('show files excluded from expansion')),
529 ('u', 'untracked', None, _('additionally show untracked files')),
529 ('u', 'untracked', None, _('additionally show untracked files')),
530 ] + commands.walkopts,
530 ] + commands.walkopts,
531 _('hg kwfiles [OPTION]... [FILE]...')),
531 _('hg kwfiles [OPTION]... [FILE]...')),
532 'kwshrink': (shrink, commands.walkopts,
532 'kwshrink': (shrink, commands.walkopts,
533 _('hg kwshrink [OPTION]... [FILE]...')),
533 _('hg kwshrink [OPTION]... [FILE]...')),
534 }
534 }
@@ -1,3485 +1,3485 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, textwrap, subprocess, difflib, time
11 import os, re, sys, textwrap, subprocess, difflib, time
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 import patch, help, mdiff, tempfile, url, encoding
13 import patch, help, mdiff, tempfile, url, encoding
14 import archival, changegroup, cmdutil, sshserver, hbisect
14 import archival, changegroup, cmdutil, sshserver, hbisect
15 from hgweb import server
15 from hgweb import server
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the
23 Schedule files to be version controlled and added to the
24 repository.
24 repository.
25
25
26 The files will be added to the repository at the next commit. To
26 The files will be added to the repository at the next commit. To
27 undo an add before that, see hg revert.
27 undo an add before that, see hg revert.
28
28
29 If no names are given, add all files to the repository.
29 If no names are given, add all files to the repository.
30 """
30 """
31
31
32 bad = []
32 bad = []
33 exacts = {}
33 exacts = {}
34 names = []
34 names = []
35 m = cmdutil.match(repo, pats, opts)
35 m = cmdutil.match(repo, pats, opts)
36 oldbad = m.bad
36 oldbad = m.bad
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38
38
39 for f in repo.walk(m):
39 for f in repo.walk(m):
40 exact = m.exact(f)
40 exact = m.exact(f)
41 if exact or f not in repo.dirstate:
41 if exact or f not in repo.dirstate:
42 names.append(f)
42 names.append(f)
43 if ui.verbose or not exact:
43 if ui.verbose or not exact:
44 ui.status(_('adding %s\n') % m.rel(f))
44 ui.status(_('adding %s\n') % m.rel(f))
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 bad += [f for f in repo.add(names) if f in m.files()]
46 bad += [f for f in repo.add(names) if f in m.files()]
47 return bad and 1 or 0
47 return bad and 1 or 0
48
48
49 def addremove(ui, repo, *pats, **opts):
49 def addremove(ui, repo, *pats, **opts):
50 """add all new files, delete all missing files
50 """add all new files, delete all missing files
51
51
52 Add all new files and remove all missing files from the
52 Add all new files and remove all missing files from the
53 repository.
53 repository.
54
54
55 New files are ignored if they match any of the patterns in
55 New files are ignored if they match any of the patterns in
56 .hgignore. As with add, these changes take effect at the next
56 .hgignore. As with add, these changes take effect at the next
57 commit.
57 commit.
58
58
59 Use the -s/--similarity option to detect renamed files. With a
59 Use the -s/--similarity option to detect renamed files. With a
60 parameter > 0, this compares every removed file with every added
60 parameter > 0, this compares every removed file with every added
61 file and records those similar enough as renames. This option
61 file and records those similar enough as renames. This option
62 takes a percentage between 0 (disabled) and 100 (files must be
62 takes a percentage between 0 (disabled) and 100 (files must be
63 identical) as its parameter. Detecting renamed files this way can
63 identical) as its parameter. Detecting renamed files this way can
64 be expensive.
64 be expensive.
65 """
65 """
66 try:
66 try:
67 sim = float(opts.get('similarity') or 0)
67 sim = float(opts.get('similarity') or 0)
68 except ValueError:
68 except ValueError:
69 raise util.Abort(_('similarity must be a number'))
69 raise util.Abort(_('similarity must be a number'))
70 if sim < 0 or sim > 100:
70 if sim < 0 or sim > 100:
71 raise util.Abort(_('similarity must be between 0 and 100'))
71 raise util.Abort(_('similarity must be between 0 and 100'))
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73
73
74 def annotate(ui, repo, *pats, **opts):
74 def annotate(ui, repo, *pats, **opts):
75 """show changeset information by line for each file
75 """show changeset information by line for each file
76
76
77 List changes in files, showing the revision id responsible for
77 List changes in files, showing the revision id responsible for
78 each line
78 each line
79
79
80 This command is useful for discovering when a change was made and
80 This command is useful for discovering when a change was made and
81 by whom.
81 by whom.
82
82
83 Without the -a/--text option, annotate will avoid processing files
83 Without the -a/--text option, annotate will avoid processing files
84 it detects as binary. With -a, annotate will annotate the file
84 it detects as binary. With -a, annotate will annotate the file
85 anyway, although the results will probably be neither useful
85 anyway, although the results will probably be neither useful
86 nor desirable.
86 nor desirable.
87 """
87 """
88 datefunc = ui.quiet and util.shortdate or util.datestr
88 datefunc = ui.quiet and util.shortdate or util.datestr
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90
90
91 if not pats:
91 if not pats:
92 raise util.Abort(_('at least one filename or pattern is required'))
92 raise util.Abort(_('at least one filename or pattern is required'))
93
93
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 ('number', lambda x: str(x[0].rev())),
95 ('number', lambda x: str(x[0].rev())),
96 ('changeset', lambda x: short(x[0].node())),
96 ('changeset', lambda x: short(x[0].node())),
97 ('date', getdate),
97 ('date', getdate),
98 ('follow', lambda x: x[0].path()),
98 ('follow', lambda x: x[0].path()),
99 ]
99 ]
100
100
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 and not opts.get('follow')):
102 and not opts.get('follow')):
103 opts['number'] = 1
103 opts['number'] = 1
104
104
105 linenumber = opts.get('line_number') is not None
105 linenumber = opts.get('line_number') is not None
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108
108
109 funcmap = [func for op, func in opmap if opts.get(op)]
109 funcmap = [func for op, func in opmap if opts.get(op)]
110 if linenumber:
110 if linenumber:
111 lastfunc = funcmap[-1]
111 lastfunc = funcmap[-1]
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113
113
114 ctx = repo[opts.get('rev')]
114 ctx = repo[opts.get('rev')]
115
115
116 m = cmdutil.match(repo, pats, opts)
116 m = cmdutil.match(repo, pats, opts)
117 for abs in ctx.walk(m):
117 for abs in ctx.walk(m):
118 fctx = ctx[abs]
118 fctx = ctx[abs]
119 if not opts.get('text') and util.binary(fctx.data()):
119 if not opts.get('text') and util.binary(fctx.data()):
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 continue
121 continue
122
122
123 lines = fctx.annotate(follow=opts.get('follow'),
123 lines = fctx.annotate(follow=opts.get('follow'),
124 linenumber=linenumber)
124 linenumber=linenumber)
125 pieces = []
125 pieces = []
126
126
127 for f in funcmap:
127 for f in funcmap:
128 l = [f(n) for n, dummy in lines]
128 l = [f(n) for n, dummy in lines]
129 if l:
129 if l:
130 ml = max(map(len, l))
130 ml = max(map(len, l))
131 pieces.append(["%*s" % (ml, x) for x in l])
131 pieces.append(["%*s" % (ml, x) for x in l])
132
132
133 if pieces:
133 if pieces:
134 for p, l in zip(zip(*pieces), lines):
134 for p, l in zip(zip(*pieces), lines):
135 ui.write("%s: %s" % (" ".join(p), l[1]))
135 ui.write("%s: %s" % (" ".join(p), l[1]))
136
136
137 def archive(ui, repo, dest, **opts):
137 def archive(ui, repo, dest, **opts):
138 '''create an unversioned archive of a repository revision
138 '''create an unversioned archive of a repository revision
139
139
140 By default, the revision used is the parent of the working
140 By default, the revision used is the parent of the working
141 directory; use -r/--rev to specify a different revision.
141 directory; use -r/--rev to specify a different revision.
142
142
143 To specify the type of archive to create, use -t/--type. Valid
143 To specify the type of archive to create, use -t/--type. Valid
144 types are:
144 types are:
145
145
146 "files" (default): a directory full of files
146 "files" (default): a directory full of files
147 "tar": tar archive, uncompressed
147 "tar": tar archive, uncompressed
148 "tbz2": tar archive, compressed using bzip2
148 "tbz2": tar archive, compressed using bzip2
149 "tgz": tar archive, compressed using gzip
149 "tgz": tar archive, compressed using gzip
150 "uzip": zip archive, uncompressed
150 "uzip": zip archive, uncompressed
151 "zip": zip archive, compressed using deflate
151 "zip": zip archive, compressed using deflate
152
152
153 The exact name of the destination archive or directory is given
153 The exact name of the destination archive or directory is given
154 using a format string; see 'hg help export' for details.
154 using a format string; see 'hg help export' for details.
155
155
156 Each member added to an archive file has a directory prefix
156 Each member added to an archive file has a directory prefix
157 prepended. Use -p/--prefix to specify a format string for the
157 prepended. Use -p/--prefix to specify a format string for the
158 prefix. The default is the basename of the archive, with suffixes
158 prefix. The default is the basename of the archive, with suffixes
159 removed.
159 removed.
160 '''
160 '''
161
161
162 ctx = repo[opts.get('rev')]
162 ctx = repo[opts.get('rev')]
163 if not ctx:
163 if not ctx:
164 raise util.Abort(_('no working directory: please specify a revision'))
164 raise util.Abort(_('no working directory: please specify a revision'))
165 node = ctx.node()
165 node = ctx.node()
166 dest = cmdutil.make_filename(repo, dest, node)
166 dest = cmdutil.make_filename(repo, dest, node)
167 if os.path.realpath(dest) == repo.root:
167 if os.path.realpath(dest) == repo.root:
168 raise util.Abort(_('repository root cannot be destination'))
168 raise util.Abort(_('repository root cannot be destination'))
169 matchfn = cmdutil.match(repo, [], opts)
169 matchfn = cmdutil.match(repo, [], opts)
170 kind = opts.get('type') or 'files'
170 kind = opts.get('type') or 'files'
171 prefix = opts.get('prefix')
171 prefix = opts.get('prefix')
172 if dest == '-':
172 if dest == '-':
173 if kind == 'files':
173 if kind == 'files':
174 raise util.Abort(_('cannot archive plain files to stdout'))
174 raise util.Abort(_('cannot archive plain files to stdout'))
175 dest = sys.stdout
175 dest = sys.stdout
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 prefix = cmdutil.make_filename(repo, prefix, node)
177 prefix = cmdutil.make_filename(repo, prefix, node)
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 matchfn, prefix)
179 matchfn, prefix)
180
180
181 def backout(ui, repo, node=None, rev=None, **opts):
181 def backout(ui, repo, node=None, rev=None, **opts):
182 '''reverse effect of earlier changeset
182 '''reverse effect of earlier changeset
183
183
184 Commit the backed out changes as a new changeset. The new
184 Commit the backed out changes as a new changeset. The new
185 changeset is a child of the backed out changeset.
185 changeset is a child of the backed out changeset.
186
186
187 If you backout a changeset other than the tip, a new head is
187 If you backout a changeset other than the tip, a new head is
188 created. This head will be the new tip and you should merge this
188 created. This head will be the new tip and you should merge this
189 backout changeset with another head.
189 backout changeset with another head.
190
190
191 The --merge option remembers the parent of the working directory
191 The --merge option remembers the parent of the working directory
192 before starting the backout, then merges the new head with that
192 before starting the backout, then merges the new head with that
193 changeset afterwards. This saves you from doing the merge by hand.
193 changeset afterwards. This saves you from doing the merge by hand.
194 The result of this merge is not committed, as with a normal merge.
194 The result of this merge is not committed, as with a normal merge.
195
195
196 See 'hg help dates' for a list of formats valid for -d/--date.
196 See 'hg help dates' for a list of formats valid for -d/--date.
197 '''
197 '''
198 if rev and node:
198 if rev and node:
199 raise util.Abort(_("please specify just one revision"))
199 raise util.Abort(_("please specify just one revision"))
200
200
201 if not rev:
201 if not rev:
202 rev = node
202 rev = node
203
203
204 if not rev:
204 if not rev:
205 raise util.Abort(_("please specify a revision to backout"))
205 raise util.Abort(_("please specify a revision to backout"))
206
206
207 date = opts.get('date')
207 date = opts.get('date')
208 if date:
208 if date:
209 opts['date'] = util.parsedate(date)
209 opts['date'] = util.parsedate(date)
210
210
211 cmdutil.bail_if_changed(repo)
211 cmdutil.bail_if_changed(repo)
212 node = repo.lookup(rev)
212 node = repo.lookup(rev)
213
213
214 op1, op2 = repo.dirstate.parents()
214 op1, op2 = repo.dirstate.parents()
215 a = repo.changelog.ancestor(op1, node)
215 a = repo.changelog.ancestor(op1, node)
216 if a != node:
216 if a != node:
217 raise util.Abort(_('cannot backout change on a different branch'))
217 raise util.Abort(_('cannot backout change on a different branch'))
218
218
219 p1, p2 = repo.changelog.parents(node)
219 p1, p2 = repo.changelog.parents(node)
220 if p1 == nullid:
220 if p1 == nullid:
221 raise util.Abort(_('cannot backout a change with no parents'))
221 raise util.Abort(_('cannot backout a change with no parents'))
222 if p2 != nullid:
222 if p2 != nullid:
223 if not opts.get('parent'):
223 if not opts.get('parent'):
224 raise util.Abort(_('cannot backout a merge changeset without '
224 raise util.Abort(_('cannot backout a merge changeset without '
225 '--parent'))
225 '--parent'))
226 p = repo.lookup(opts['parent'])
226 p = repo.lookup(opts['parent'])
227 if p not in (p1, p2):
227 if p not in (p1, p2):
228 raise util.Abort(_('%s is not a parent of %s') %
228 raise util.Abort(_('%s is not a parent of %s') %
229 (short(p), short(node)))
229 (short(p), short(node)))
230 parent = p
230 parent = p
231 else:
231 else:
232 if opts.get('parent'):
232 if opts.get('parent'):
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 parent = p1
234 parent = p1
235
235
236 # the backout should appear on the same branch
236 # the backout should appear on the same branch
237 branch = repo.dirstate.branch()
237 branch = repo.dirstate.branch()
238 hg.clean(repo, node, show_stats=False)
238 hg.clean(repo, node, show_stats=False)
239 repo.dirstate.setbranch(branch)
239 repo.dirstate.setbranch(branch)
240 revert_opts = opts.copy()
240 revert_opts = opts.copy()
241 revert_opts['date'] = None
241 revert_opts['date'] = None
242 revert_opts['all'] = True
242 revert_opts['all'] = True
243 revert_opts['rev'] = hex(parent)
243 revert_opts['rev'] = hex(parent)
244 revert_opts['no_backup'] = None
244 revert_opts['no_backup'] = None
245 revert(ui, repo, **revert_opts)
245 revert(ui, repo, **revert_opts)
246 commit_opts = opts.copy()
246 commit_opts = opts.copy()
247 commit_opts['addremove'] = False
247 commit_opts['addremove'] = False
248 if not commit_opts['message'] and not commit_opts['logfile']:
248 if not commit_opts['message'] and not commit_opts['logfile']:
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 commit_opts['force_editor'] = True
250 commit_opts['force_editor'] = True
251 commit(ui, repo, **commit_opts)
251 commit(ui, repo, **commit_opts)
252 def nice(node):
252 def nice(node):
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
253 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 ui.status(_('changeset %s backs out changeset %s\n') %
254 ui.status(_('changeset %s backs out changeset %s\n') %
255 (nice(repo.changelog.tip()), nice(node)))
255 (nice(repo.changelog.tip()), nice(node)))
256 if op1 != node:
256 if op1 != node:
257 hg.clean(repo, op1, show_stats=False)
257 hg.clean(repo, op1, show_stats=False)
258 if opts.get('merge'):
258 if opts.get('merge'):
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
260 hg.merge(repo, hex(repo.changelog.tip()))
261 else:
261 else:
262 ui.status(_('the backout changeset is a new head - '
262 ui.status(_('the backout changeset is a new head - '
263 'do not forget to merge\n'))
263 'do not forget to merge\n'))
264 ui.status(_('(use "backout --merge" '
264 ui.status(_('(use "backout --merge" '
265 'if you want to auto-merge)\n'))
265 'if you want to auto-merge)\n'))
266
266
267 def bisect(ui, repo, rev=None, extra=None, command=None,
267 def bisect(ui, repo, rev=None, extra=None, command=None,
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
268 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 """subdivision search of changesets
269 """subdivision search of changesets
270
270
271 This command helps to find changesets which introduce problems. To
271 This command helps to find changesets which introduce problems. To
272 use, mark the earliest changeset you know exhibits the problem as
272 use, mark the earliest changeset you know exhibits the problem as
273 bad, then mark the latest changeset which is free from the problem
273 bad, then mark the latest changeset which is free from the problem
274 as good. Bisect will update your working directory to a revision
274 as good. Bisect will update your working directory to a revision
275 for testing (unless the -U/--noupdate option is specified). Once
275 for testing (unless the -U/--noupdate option is specified). Once
276 you have performed tests, mark the working directory as good or
276 you have performed tests, mark the working directory as good or
277 bad, and bisect will either update to another candidate changeset
277 bad, and bisect will either update to another candidate changeset
278 or announce that it has found the bad revision.
278 or announce that it has found the bad revision.
279
279
280 As a shortcut, you can also use the revision argument to mark a
280 As a shortcut, you can also use the revision argument to mark a
281 revision as good or bad without checking it out first.
281 revision as good or bad without checking it out first.
282
282
283 If you supply a command, it will be used for automatic bisection.
283 If you supply a command, it will be used for automatic bisection.
284 Its exit status will be used to mark revisions as good or bad:
284 Its exit status will be used to mark revisions as good or bad:
285 status 0 means good, 125 means to skip the revision, 127
285 status 0 means good, 125 means to skip the revision, 127
286 (command not found) will abort the bisection, and any other
286 (command not found) will abort the bisection, and any other
287 non-zero exit status means the revision is bad.
287 non-zero exit status means the revision is bad.
288 """
288 """
289 def print_result(nodes, good):
289 def print_result(nodes, good):
290 displayer = cmdutil.show_changeset(ui, repo, {})
290 displayer = cmdutil.show_changeset(ui, repo, {})
291 if len(nodes) == 1:
291 if len(nodes) == 1:
292 # narrowed it down to a single revision
292 # narrowed it down to a single revision
293 if good:
293 if good:
294 ui.write(_("The first good revision is:\n"))
294 ui.write(_("The first good revision is:\n"))
295 else:
295 else:
296 ui.write(_("The first bad revision is:\n"))
296 ui.write(_("The first bad revision is:\n"))
297 displayer.show(repo[nodes[0]])
297 displayer.show(repo[nodes[0]])
298 else:
298 else:
299 # multiple possible revisions
299 # multiple possible revisions
300 if good:
300 if good:
301 ui.write(_("Due to skipped revisions, the first "
301 ui.write(_("Due to skipped revisions, the first "
302 "good revision could be any of:\n"))
302 "good revision could be any of:\n"))
303 else:
303 else:
304 ui.write(_("Due to skipped revisions, the first "
304 ui.write(_("Due to skipped revisions, the first "
305 "bad revision could be any of:\n"))
305 "bad revision could be any of:\n"))
306 for n in nodes:
306 for n in nodes:
307 displayer.show(repo[n])
307 displayer.show(repo[n])
308
308
309 def check_state(state, interactive=True):
309 def check_state(state, interactive=True):
310 if not state['good'] or not state['bad']:
310 if not state['good'] or not state['bad']:
311 if (good or bad or skip or reset) and interactive:
311 if (good or bad or skip or reset) and interactive:
312 return
312 return
313 if not state['good']:
313 if not state['good']:
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
314 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 else:
315 else:
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 return True
317 return True
318
318
319 # backward compatibility
319 # backward compatibility
320 if rev in "good bad reset init".split():
320 if rev in "good bad reset init".split():
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 cmd, rev, extra = rev, extra, None
322 cmd, rev, extra = rev, extra, None
323 if cmd == "good":
323 if cmd == "good":
324 good = True
324 good = True
325 elif cmd == "bad":
325 elif cmd == "bad":
326 bad = True
326 bad = True
327 else:
327 else:
328 reset = True
328 reset = True
329 elif extra or good + bad + skip + reset + bool(command) > 1:
329 elif extra or good + bad + skip + reset + bool(command) > 1:
330 raise util.Abort(_('incompatible arguments'))
330 raise util.Abort(_('incompatible arguments'))
331
331
332 if reset:
332 if reset:
333 p = repo.join("bisect.state")
333 p = repo.join("bisect.state")
334 if os.path.exists(p):
334 if os.path.exists(p):
335 os.unlink(p)
335 os.unlink(p)
336 return
336 return
337
337
338 state = hbisect.load_state(repo)
338 state = hbisect.load_state(repo)
339
339
340 if command:
340 if command:
341 commandpath = util.find_exe(command)
341 commandpath = util.find_exe(command)
342 if commandpath is None:
342 if commandpath is None:
343 raise util.Abort(_("cannot find executable: %s") % command)
343 raise util.Abort(_("cannot find executable: %s") % command)
344 changesets = 1
344 changesets = 1
345 try:
345 try:
346 while changesets:
346 while changesets:
347 # update state
347 # update state
348 status = subprocess.call([commandpath])
348 status = subprocess.call([commandpath])
349 if status == 125:
349 if status == 125:
350 transition = "skip"
350 transition = "skip"
351 elif status == 0:
351 elif status == 0:
352 transition = "good"
352 transition = "good"
353 # status < 0 means process was killed
353 # status < 0 means process was killed
354 elif status == 127:
354 elif status == 127:
355 raise util.Abort(_("failed to execute %s") % command)
355 raise util.Abort(_("failed to execute %s") % command)
356 elif status < 0:
356 elif status < 0:
357 raise util.Abort(_("%s killed") % command)
357 raise util.Abort(_("%s killed") % command)
358 else:
358 else:
359 transition = "bad"
359 transition = "bad"
360 ctx = repo[rev or '.']
360 ctx = repo[rev or '.']
361 state[transition].append(ctx.node())
361 state[transition].append(ctx.node())
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 check_state(state, interactive=False)
363 check_state(state, interactive=False)
364 # bisect
364 # bisect
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 # update to next check
366 # update to next check
367 cmdutil.bail_if_changed(repo)
367 cmdutil.bail_if_changed(repo)
368 hg.clean(repo, nodes[0], show_stats=False)
368 hg.clean(repo, nodes[0], show_stats=False)
369 finally:
369 finally:
370 hbisect.save_state(repo, state)
370 hbisect.save_state(repo, state)
371 return print_result(nodes, not status)
371 return print_result(nodes, not status)
372
372
373 # update state
373 # update state
374 node = repo.lookup(rev or '.')
374 node = repo.lookup(rev or '.')
375 if good:
375 if good:
376 state['good'].append(node)
376 state['good'].append(node)
377 elif bad:
377 elif bad:
378 state['bad'].append(node)
378 state['bad'].append(node)
379 elif skip:
379 elif skip:
380 state['skip'].append(node)
380 state['skip'].append(node)
381
381
382 hbisect.save_state(repo, state)
382 hbisect.save_state(repo, state)
383
383
384 if not check_state(state):
384 if not check_state(state):
385 return
385 return
386
386
387 # actually bisect
387 # actually bisect
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 if changesets == 0:
389 if changesets == 0:
390 print_result(nodes, good)
390 print_result(nodes, good)
391 else:
391 else:
392 assert len(nodes) == 1 # only a single node can be tested next
392 assert len(nodes) == 1 # only a single node can be tested next
393 node = nodes[0]
393 node = nodes[0]
394 # compute the approximate number of remaining tests
394 # compute the approximate number of remaining tests
395 tests, size = 0, 2
395 tests, size = 0, 2
396 while size <= changesets:
396 while size <= changesets:
397 tests, size = tests + 1, size * 2
397 tests, size = tests + 1, size * 2
398 rev = repo.changelog.rev(node)
398 rev = repo.changelog.rev(node)
399 ui.write(_("Testing changeset %s:%s "
399 ui.write(_("Testing changeset %s:%s "
400 "(%s changesets remaining, ~%s tests)\n")
400 "(%s changesets remaining, ~%s tests)\n")
401 % (rev, short(node), changesets, tests))
401 % (rev, short(node), changesets, tests))
402 if not noupdate:
402 if not noupdate:
403 cmdutil.bail_if_changed(repo)
403 cmdutil.bail_if_changed(repo)
404 return hg.clean(repo, node)
404 return hg.clean(repo, node)
405
405
406 def branch(ui, repo, label=None, **opts):
406 def branch(ui, repo, label=None, **opts):
407 """set or show the current branch name
407 """set or show the current branch name
408
408
409 With no argument, show the current branch name. With one argument,
409 With no argument, show the current branch name. With one argument,
410 set the working directory branch name (the branch will not exist
410 set the working directory branch name (the branch will not exist
411 in the repository until the next commit). Standard practice
411 in the repository until the next commit). Standard practice
412 recommends that primary development take place on the 'default'
412 recommends that primary development take place on the 'default'
413 branch.
413 branch.
414
414
415 Unless -f/--force is specified, branch will not let you set a
415 Unless -f/--force is specified, branch will not let you set a
416 branch name that already exists, even if it's inactive.
416 branch name that already exists, even if it's inactive.
417
417
418 Use -C/--clean to reset the working directory branch to that of
418 Use -C/--clean to reset the working directory branch to that of
419 the parent of the working directory, negating a previous branch
419 the parent of the working directory, negating a previous branch
420 change.
420 change.
421
421
422 Use the command 'hg update' to switch to an existing branch.
422 Use the command 'hg update' to switch to an existing branch.
423 """
423 """
424
424
425 if opts.get('clean'):
425 if opts.get('clean'):
426 label = repo[None].parents()[0].branch()
426 label = repo[None].parents()[0].branch()
427 repo.dirstate.setbranch(label)
427 repo.dirstate.setbranch(label)
428 ui.status(_('reset working directory to branch %s\n') % label)
428 ui.status(_('reset working directory to branch %s\n') % label)
429 elif label:
429 elif label:
430 if not opts.get('force') and label in repo.branchtags():
430 if not opts.get('force') and label in repo.branchtags():
431 if label not in [p.branch() for p in repo.parents()]:
431 if label not in [p.branch() for p in repo.parents()]:
432 raise util.Abort(_('a branch of the same name already exists'
432 raise util.Abort(_('a branch of the same name already exists'
433 ' (use --force to override)'))
433 ' (use --force to override)'))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
434 repo.dirstate.setbranch(encoding.fromlocal(label))
435 ui.status(_('marked working directory as branch %s\n') % label)
435 ui.status(_('marked working directory as branch %s\n') % label)
436 else:
436 else:
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
438
438
439 def branches(ui, repo, active=False):
439 def branches(ui, repo, active=False):
440 """list repository named branches
440 """list repository named branches
441
441
442 List the repository's named branches, indicating which ones are
442 List the repository's named branches, indicating which ones are
443 inactive. If active is specified, only show active branches.
443 inactive. If active is specified, only show active branches.
444
444
445 A branch is considered active if it contains repository heads.
445 A branch is considered active if it contains repository heads.
446
446
447 Use the command 'hg update' to switch to an existing branch.
447 Use the command 'hg update' to switch to an existing branch.
448 """
448 """
449 hexfunc = ui.debugflag and hex or short
449 hexfunc = ui.debugflag and hex or short
450 activebranches = [encoding.tolocal(repo[n].branch())
450 activebranches = [encoding.tolocal(repo[n].branch())
451 for n in repo.heads()]
451 for n in repo.heads()]
452 def testactive(tag, node):
452 def testactive(tag, node):
453 realhead = tag in activebranches
453 realhead = tag in activebranches
454 open = node in repo.branchheads(tag, closed=False)
454 open = node in repo.branchheads(tag, closed=False)
455 return realhead and open
455 return realhead and open
456 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
456 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
457 for tag, node in repo.branchtags().items()],
457 for tag, node in repo.branchtags().items()],
458 reverse=True)
458 reverse=True)
459
459
460 for isactive, node, tag in branches:
460 for isactive, node, tag in branches:
461 if (not active) or isactive:
461 if (not active) or isactive:
462 if ui.quiet:
462 if ui.quiet:
463 ui.write("%s\n" % tag)
463 ui.write("%s\n" % tag)
464 else:
464 else:
465 hn = repo.lookup(node)
465 hn = repo.lookup(node)
466 if isactive:
466 if isactive:
467 notice = ''
467 notice = ''
468 elif hn not in repo.branchheads(tag, closed=False):
468 elif hn not in repo.branchheads(tag, closed=False):
469 notice = ' (closed)'
469 notice = ' (closed)'
470 else:
470 else:
471 notice = ' (inactive)'
471 notice = ' (inactive)'
472 rev = str(node).rjust(31 - encoding.colwidth(tag))
472 rev = str(node).rjust(31 - encoding.colwidth(tag))
473 data = tag, rev, hexfunc(hn), notice
473 data = tag, rev, hexfunc(hn), notice
474 ui.write("%s %s:%s%s\n" % data)
474 ui.write("%s %s:%s%s\n" % data)
475
475
476 def bundle(ui, repo, fname, dest=None, **opts):
476 def bundle(ui, repo, fname, dest=None, **opts):
477 """create a changegroup file
477 """create a changegroup file
478
478
479 Generate a compressed changegroup file collecting changesets not
479 Generate a compressed changegroup file collecting changesets not
480 known to be in another repository.
480 known to be in another repository.
481
481
482 If no destination repository is specified the destination is
482 If no destination repository is specified the destination is
483 assumed to have all the nodes specified by one or more --base
483 assumed to have all the nodes specified by one or more --base
484 parameters. To create a bundle containing all changesets, use
484 parameters. To create a bundle containing all changesets, use
485 -a/--all (or --base null). To change the compression method
485 -a/--all (or --base null). To change the compression method
486 applied, use the -t/--type option (by default, bundles are
486 applied, use the -t/--type option (by default, bundles are
487 compressed using bz2).
487 compressed using bz2).
488
488
489 The bundle file can then be transferred using conventional means
489 The bundle file can then be transferred using conventional means
490 and applied to another repository with the unbundle or pull
490 and applied to another repository with the unbundle or pull
491 command. This is useful when direct push and pull are not
491 command. This is useful when direct push and pull are not
492 available or when exporting an entire repository is undesirable.
492 available or when exporting an entire repository is undesirable.
493
493
494 Applying bundles preserves all changeset contents including
494 Applying bundles preserves all changeset contents including
495 permissions, copy/rename information, and revision history.
495 permissions, copy/rename information, and revision history.
496 """
496 """
497 revs = opts.get('rev') or None
497 revs = opts.get('rev') or None
498 if revs:
498 if revs:
499 revs = [repo.lookup(rev) for rev in revs]
499 revs = [repo.lookup(rev) for rev in revs]
500 if opts.get('all'):
500 if opts.get('all'):
501 base = ['null']
501 base = ['null']
502 else:
502 else:
503 base = opts.get('base')
503 base = opts.get('base')
504 if base:
504 if base:
505 if dest:
505 if dest:
506 raise util.Abort(_("--base is incompatible with specifying "
506 raise util.Abort(_("--base is incompatible with specifying "
507 "a destination"))
507 "a destination"))
508 base = [repo.lookup(rev) for rev in base]
508 base = [repo.lookup(rev) for rev in base]
509 # create the right base
509 # create the right base
510 # XXX: nodesbetween / changegroup* should be "fixed" instead
510 # XXX: nodesbetween / changegroup* should be "fixed" instead
511 o = []
511 o = []
512 has = set((nullid,))
512 has = set((nullid,))
513 for n in base:
513 for n in base:
514 has.update(repo.changelog.reachable(n))
514 has.update(repo.changelog.reachable(n))
515 if revs:
515 if revs:
516 visit = list(revs)
516 visit = list(revs)
517 else:
517 else:
518 visit = repo.changelog.heads()
518 visit = repo.changelog.heads()
519 seen = {}
519 seen = {}
520 while visit:
520 while visit:
521 n = visit.pop(0)
521 n = visit.pop(0)
522 parents = [p for p in repo.changelog.parents(n) if p not in has]
522 parents = [p for p in repo.changelog.parents(n) if p not in has]
523 if len(parents) == 0:
523 if len(parents) == 0:
524 o.insert(0, n)
524 o.insert(0, n)
525 else:
525 else:
526 for p in parents:
526 for p in parents:
527 if p not in seen:
527 if p not in seen:
528 seen[p] = 1
528 seen[p] = 1
529 visit.append(p)
529 visit.append(p)
530 else:
530 else:
531 dest, revs, checkout = hg.parseurl(
531 dest, revs, checkout = hg.parseurl(
532 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
532 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
533 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
533 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
534 o = repo.findoutgoing(other, force=opts.get('force'))
534 o = repo.findoutgoing(other, force=opts.get('force'))
535
535
536 if revs:
536 if revs:
537 cg = repo.changegroupsubset(o, revs, 'bundle')
537 cg = repo.changegroupsubset(o, revs, 'bundle')
538 else:
538 else:
539 cg = repo.changegroup(o, 'bundle')
539 cg = repo.changegroup(o, 'bundle')
540
540
541 bundletype = opts.get('type', 'bzip2').lower()
541 bundletype = opts.get('type', 'bzip2').lower()
542 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
542 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
543 bundletype = btypes.get(bundletype)
543 bundletype = btypes.get(bundletype)
544 if bundletype not in changegroup.bundletypes:
544 if bundletype not in changegroup.bundletypes:
545 raise util.Abort(_('unknown bundle type specified with --type'))
545 raise util.Abort(_('unknown bundle type specified with --type'))
546
546
547 changegroup.writebundle(cg, fname, bundletype)
547 changegroup.writebundle(cg, fname, bundletype)
548
548
549 def cat(ui, repo, file1, *pats, **opts):
549 def cat(ui, repo, file1, *pats, **opts):
550 """output the current or given revision of files
550 """output the current or given revision of files
551
551
552 Print the specified files as they were at the given revision. If
552 Print the specified files as they were at the given revision. If
553 no revision is given, the parent of the working directory is used,
553 no revision is given, the parent of the working directory is used,
554 or tip if no revision is checked out.
554 or tip if no revision is checked out.
555
555
556 Output may be to a file, in which case the name of the file is
556 Output may be to a file, in which case the name of the file is
557 given using a format string. The formatting rules are the same as
557 given using a format string. The formatting rules are the same as
558 for the export command, with the following additions:
558 for the export command, with the following additions:
559
559
560 %s basename of file being printed
560 %s basename of file being printed
561 %d dirname of file being printed, or '.' if in repository root
561 %d dirname of file being printed, or '.' if in repository root
562 %p root-relative path name of file being printed
562 %p root-relative path name of file being printed
563 """
563 """
564 ctx = repo[opts.get('rev')]
564 ctx = repo[opts.get('rev')]
565 err = 1
565 err = 1
566 m = cmdutil.match(repo, (file1,) + pats, opts)
566 m = cmdutil.match(repo, (file1,) + pats, opts)
567 for abs in ctx.walk(m):
567 for abs in ctx.walk(m):
568 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
568 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
569 data = ctx[abs].data()
569 data = ctx[abs].data()
570 if opts.get('decode'):
570 if opts.get('decode'):
571 data = repo.wwritedata(abs, data)
571 data = repo.wwritedata(abs, data)
572 fp.write(data)
572 fp.write(data)
573 err = 0
573 err = 0
574 return err
574 return err
575
575
576 def clone(ui, source, dest=None, **opts):
576 def clone(ui, source, dest=None, **opts):
577 """make a copy of an existing repository
577 """make a copy of an existing repository
578
578
579 Create a copy of an existing repository in a new directory.
579 Create a copy of an existing repository in a new directory.
580
580
581 If no destination directory name is specified, it defaults to the
581 If no destination directory name is specified, it defaults to the
582 basename of the source.
582 basename of the source.
583
583
584 The location of the source is added to the new repository's
584 The location of the source is added to the new repository's
585 .hg/hgrc file, as the default to be used for future pulls.
585 .hg/hgrc file, as the default to be used for future pulls.
586
586
587 If you use the -r/--rev option to clone up to a specific revision,
587 If you use the -r/--rev option to clone up to a specific revision,
588 no subsequent revisions (including subsequent tags) will be
588 no subsequent revisions (including subsequent tags) will be
589 present in the cloned repository. This option implies --pull, even
589 present in the cloned repository. This option implies --pull, even
590 on local repositories.
590 on local repositories.
591
591
592 By default, clone will check out the head of the 'default' branch.
592 By default, clone will check out the head of the 'default' branch.
593 If the -U/--noupdate option is used, the new clone will contain
593 If the -U/--noupdate option is used, the new clone will contain
594 only a repository (.hg) and no working copy (the working copy
594 only a repository (.hg) and no working copy (the working copy
595 parent is the null revision).
595 parent is the null revision).
596
596
597 See 'hg help urls' for valid source format details.
597 See 'hg help urls' for valid source format details.
598
598
599 It is possible to specify an ssh:// URL as the destination, but no
599 It is possible to specify an ssh:// URL as the destination, but no
600 .hg/hgrc and working directory will be created on the remote side.
600 .hg/hgrc and working directory will be created on the remote side.
601 Please see 'hg help urls' for important details about ssh:// URLs.
601 Please see 'hg help urls' for important details about ssh:// URLs.
602
602
603 For efficiency, hardlinks are used for cloning whenever the source
603 For efficiency, hardlinks are used for cloning whenever the source
604 and destination are on the same filesystem (note this applies only
604 and destination are on the same filesystem (note this applies only
605 to the repository data, not to the checked out files). Some
605 to the repository data, not to the checked out files). Some
606 filesystems, such as AFS, implement hardlinking incorrectly, but
606 filesystems, such as AFS, implement hardlinking incorrectly, but
607 do not report errors. In these cases, use the --pull option to
607 do not report errors. In these cases, use the --pull option to
608 avoid hardlinking.
608 avoid hardlinking.
609
609
610 In some cases, you can clone repositories and checked out files
610 In some cases, you can clone repositories and checked out files
611 using full hardlinks with
611 using full hardlinks with
612
612
613 $ cp -al REPO REPOCLONE
613 $ cp -al REPO REPOCLONE
614
614
615 This is the fastest way to clone, but it is not always safe. The
615 This is the fastest way to clone, but it is not always safe. The
616 operation is not atomic (making sure REPO is not modified during
616 operation is not atomic (making sure REPO is not modified during
617 the operation is up to you) and you have to make sure your editor
617 the operation is up to you) and you have to make sure your editor
618 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
618 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
619 this is not compatible with certain extensions that place their
619 this is not compatible with certain extensions that place their
620 metadata under the .hg directory, such as mq.
620 metadata under the .hg directory, such as mq.
621
621
622 """
622 """
623 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
623 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
624 pull=opts.get('pull'),
624 pull=opts.get('pull'),
625 stream=opts.get('uncompressed'),
625 stream=opts.get('uncompressed'),
626 rev=opts.get('rev'),
626 rev=opts.get('rev'),
627 update=not opts.get('noupdate'))
627 update=not opts.get('noupdate'))
628
628
629 def commit(ui, repo, *pats, **opts):
629 def commit(ui, repo, *pats, **opts):
630 """commit the specified files or all outstanding changes
630 """commit the specified files or all outstanding changes
631
631
632 Commit changes to the given files into the repository. Unlike a
632 Commit changes to the given files into the repository. Unlike a
633 centralized RCS, this operation is a local operation. See hg push
633 centralized RCS, this operation is a local operation. See hg push
634 for a way to actively distribute your changes.
634 for a way to actively distribute your changes.
635
635
636 If a list of files is omitted, all changes reported by "hg status"
636 If a list of files is omitted, all changes reported by "hg status"
637 will be committed.
637 will be committed.
638
638
639 If you are committing the result of a merge, do not provide any
639 If you are committing the result of a merge, do not provide any
640 filenames or -I/-X filters.
640 filenames or -I/-X filters.
641
641
642 If no commit message is specified, the configured editor is
642 If no commit message is specified, the configured editor is
643 started to prompt you for a message.
643 started to prompt you for a message.
644
644
645 See 'hg help dates' for a list of formats valid for -d/--date.
645 See 'hg help dates' for a list of formats valid for -d/--date.
646 """
646 """
647 extra = {}
647 extra = {}
648 if opts.get('close_branch'):
648 if opts.get('close_branch'):
649 extra['close'] = 1
649 extra['close'] = 1
650 e = cmdutil.commiteditor
650 e = cmdutil.commiteditor
651 if opts.get('force_editor'):
651 if opts.get('force_editor'):
652 e = cmdutil.commitforceeditor
652 e = cmdutil.commitforceeditor
653
653
654 def commitfunc(ui, repo, message, match, opts):
654 def commitfunc(ui, repo, message, match, opts):
655 return repo.commit(message, opts.get('user'), opts.get('date'), match,
655 return repo.commit(message, opts.get('user'), opts.get('date'), match,
656 editor=e, extra=extra)
656 editor=e, extra=extra)
657
657
658 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
658 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
659 if not node:
659 if not node:
660 return
660 return
661 cl = repo.changelog
661 cl = repo.changelog
662 rev = cl.rev(node)
662 rev = cl.rev(node)
663 parents = cl.parentrevs(rev)
663 parents = cl.parentrevs(rev)
664 if rev - 1 in parents:
664 if rev - 1 in parents:
665 # one of the parents was the old tip
665 # one of the parents was the old tip
666 pass
666 pass
667 elif (parents == (nullrev, nullrev) or
667 elif (parents == (nullrev, nullrev) or
668 len(cl.heads(cl.node(parents[0]))) > 1 and
668 len(cl.heads(cl.node(parents[0]))) > 1 and
669 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
669 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
670 ui.status(_('created new head\n'))
670 ui.status(_('created new head\n'))
671
671
672 if ui.debugflag:
672 if ui.debugflag:
673 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
673 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
674 elif ui.verbose:
674 elif ui.verbose:
675 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
675 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
676
676
677 def copy(ui, repo, *pats, **opts):
677 def copy(ui, repo, *pats, **opts):
678 """mark files as copied for the next commit
678 """mark files as copied for the next commit
679
679
680 Mark dest as having copies of source files. If dest is a
680 Mark dest as having copies of source files. If dest is a
681 directory, copies are put in that directory. If dest is a file,
681 directory, copies are put in that directory. If dest is a file,
682 the source must be a single file.
682 the source must be a single file.
683
683
684 By default, this command copies the contents of files as they
684 By default, this command copies the contents of files as they
685 exist in the working directory. If invoked with -A/--after, the
685 exist in the working directory. If invoked with -A/--after, the
686 operation is recorded, but no copying is performed.
686 operation is recorded, but no copying is performed.
687
687
688 This command takes effect with the next commit. To undo a copy
688 This command takes effect with the next commit. To undo a copy
689 before that, see hg revert.
689 before that, see hg revert.
690 """
690 """
691 wlock = repo.wlock(False)
691 wlock = repo.wlock(False)
692 try:
692 try:
693 return cmdutil.copy(ui, repo, pats, opts)
693 return cmdutil.copy(ui, repo, pats, opts)
694 finally:
694 finally:
695 wlock.release()
695 wlock.release()
696
696
697 def debugancestor(ui, repo, *args):
697 def debugancestor(ui, repo, *args):
698 """find the ancestor revision of two revisions in a given index"""
698 """find the ancestor revision of two revisions in a given index"""
699 if len(args) == 3:
699 if len(args) == 3:
700 index, rev1, rev2 = args
700 index, rev1, rev2 = args
701 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
701 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
702 lookup = r.lookup
702 lookup = r.lookup
703 elif len(args) == 2:
703 elif len(args) == 2:
704 if not repo:
704 if not repo:
705 raise util.Abort(_("There is no Mercurial repository here "
705 raise util.Abort(_("There is no Mercurial repository here "
706 "(.hg not found)"))
706 "(.hg not found)"))
707 rev1, rev2 = args
707 rev1, rev2 = args
708 r = repo.changelog
708 r = repo.changelog
709 lookup = repo.lookup
709 lookup = repo.lookup
710 else:
710 else:
711 raise util.Abort(_('either two or three arguments required'))
711 raise util.Abort(_('either two or three arguments required'))
712 a = r.ancestor(lookup(rev1), lookup(rev2))
712 a = r.ancestor(lookup(rev1), lookup(rev2))
713 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
713 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
714
714
715 def debugcommands(ui, cmd='', *args):
715 def debugcommands(ui, cmd='', *args):
716 for cmd, vals in sorted(table.iteritems()):
716 for cmd, vals in sorted(table.iteritems()):
717 cmd = cmd.split('|')[0].strip('^')
717 cmd = cmd.split('|')[0].strip('^')
718 opts = ', '.join([i[1] for i in vals[1]])
718 opts = ', '.join([i[1] for i in vals[1]])
719 ui.write('%s: %s\n' % (cmd, opts))
719 ui.write('%s: %s\n' % (cmd, opts))
720
720
721 def debugcomplete(ui, cmd='', **opts):
721 def debugcomplete(ui, cmd='', **opts):
722 """returns the completion list associated with the given command"""
722 """returns the completion list associated with the given command"""
723
723
724 if opts.get('options'):
724 if opts.get('options'):
725 options = []
725 options = []
726 otables = [globalopts]
726 otables = [globalopts]
727 if cmd:
727 if cmd:
728 aliases, entry = cmdutil.findcmd(cmd, table, False)
728 aliases, entry = cmdutil.findcmd(cmd, table, False)
729 otables.append(entry[1])
729 otables.append(entry[1])
730 for t in otables:
730 for t in otables:
731 for o in t:
731 for o in t:
732 if o[0]:
732 if o[0]:
733 options.append('-%s' % o[0])
733 options.append('-%s' % o[0])
734 options.append('--%s' % o[1])
734 options.append('--%s' % o[1])
735 ui.write("%s\n" % "\n".join(options))
735 ui.write("%s\n" % "\n".join(options))
736 return
736 return
737
737
738 cmdlist = cmdutil.findpossible(cmd, table)
738 cmdlist = cmdutil.findpossible(cmd, table)
739 if ui.verbose:
739 if ui.verbose:
740 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
740 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
741 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
741 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
742
742
743 def debugfsinfo(ui, path = "."):
743 def debugfsinfo(ui, path = "."):
744 file('.debugfsinfo', 'w').write('')
744 file('.debugfsinfo', 'w').write('')
745 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
745 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
746 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
746 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
747 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
747 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
748 and 'yes' or 'no'))
748 and 'yes' or 'no'))
749 os.unlink('.debugfsinfo')
749 os.unlink('.debugfsinfo')
750
750
751 def debugrebuildstate(ui, repo, rev="tip"):
751 def debugrebuildstate(ui, repo, rev="tip"):
752 """rebuild the dirstate as it would look like for the given revision"""
752 """rebuild the dirstate as it would look like for the given revision"""
753 ctx = repo[rev]
753 ctx = repo[rev]
754 wlock = repo.wlock()
754 wlock = repo.wlock()
755 try:
755 try:
756 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
756 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
757 finally:
757 finally:
758 wlock.release()
758 wlock.release()
759
759
760 def debugcheckstate(ui, repo):
760 def debugcheckstate(ui, repo):
761 """validate the correctness of the current dirstate"""
761 """validate the correctness of the current dirstate"""
762 parent1, parent2 = repo.dirstate.parents()
762 parent1, parent2 = repo.dirstate.parents()
763 m1 = repo[parent1].manifest()
763 m1 = repo[parent1].manifest()
764 m2 = repo[parent2].manifest()
764 m2 = repo[parent2].manifest()
765 errors = 0
765 errors = 0
766 for f in repo.dirstate:
766 for f in repo.dirstate:
767 state = repo.dirstate[f]
767 state = repo.dirstate[f]
768 if state in "nr" and f not in m1:
768 if state in "nr" and f not in m1:
769 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
769 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
770 errors += 1
770 errors += 1
771 if state in "a" and f in m1:
771 if state in "a" and f in m1:
772 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
772 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
773 errors += 1
773 errors += 1
774 if state in "m" and f not in m1 and f not in m2:
774 if state in "m" and f not in m1 and f not in m2:
775 ui.warn(_("%s in state %s, but not in either manifest\n") %
775 ui.warn(_("%s in state %s, but not in either manifest\n") %
776 (f, state))
776 (f, state))
777 errors += 1
777 errors += 1
778 for f in m1:
778 for f in m1:
779 state = repo.dirstate[f]
779 state = repo.dirstate[f]
780 if state not in "nrm":
780 if state not in "nrm":
781 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
781 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
782 errors += 1
782 errors += 1
783 if errors:
783 if errors:
784 error = _(".hg/dirstate inconsistent with current parent's manifest")
784 error = _(".hg/dirstate inconsistent with current parent's manifest")
785 raise util.Abort(error)
785 raise util.Abort(error)
786
786
787 def showconfig(ui, repo, *values, **opts):
787 def showconfig(ui, repo, *values, **opts):
788 """show combined config settings from all hgrc files
788 """show combined config settings from all hgrc files
789
789
790 With no arguments, print names and values of all config items.
790 With no arguments, print names and values of all config items.
791
791
792 With one argument of the form section.name, print just the value
792 With one argument of the form section.name, print just the value
793 of that config item.
793 of that config item.
794
794
795 With multiple arguments, print names and values of all config
795 With multiple arguments, print names and values of all config
796 items with matching section names.
796 items with matching section names.
797
797
798 With --debug, the source (filename and line number) is printed
798 With --debug, the source (filename and line number) is printed
799 for each config item.
799 for each config item.
800 """
800 """
801
801
802 untrusted = bool(opts.get('untrusted'))
802 untrusted = bool(opts.get('untrusted'))
803 if values:
803 if values:
804 if len([v for v in values if '.' in v]) > 1:
804 if len([v for v in values if '.' in v]) > 1:
805 raise util.Abort(_('only one config item permitted'))
805 raise util.Abort(_('only one config item permitted'))
806 for section, name, value in ui.walkconfig(untrusted=untrusted):
806 for section, name, value in ui.walkconfig(untrusted=untrusted):
807 sectname = section + '.' + name
807 sectname = section + '.' + name
808 if values:
808 if values:
809 for v in values:
809 for v in values:
810 if v == section:
810 if v == section:
811 ui.debug('%s: ' %
811 ui.debug('%s: ' %
812 ui.configsource(section, name, untrusted))
812 ui.configsource(section, name, untrusted))
813 ui.write('%s=%s\n' % (sectname, value))
813 ui.write('%s=%s\n' % (sectname, value))
814 elif v == sectname:
814 elif v == sectname:
815 ui.debug('%s: ' %
815 ui.debug('%s: ' %
816 ui.configsource(section, name, untrusted))
816 ui.configsource(section, name, untrusted))
817 ui.write(value, '\n')
817 ui.write(value, '\n')
818 else:
818 else:
819 ui.debug('%s: ' %
819 ui.debug('%s: ' %
820 ui.configsource(section, name, untrusted))
820 ui.configsource(section, name, untrusted))
821 ui.write('%s=%s\n' % (sectname, value))
821 ui.write('%s=%s\n' % (sectname, value))
822
822
823 def debugsetparents(ui, repo, rev1, rev2=None):
823 def debugsetparents(ui, repo, rev1, rev2=None):
824 """manually set the parents of the current working directory
824 """manually set the parents of the current working directory
825
825
826 This is useful for writing repository conversion tools, but should
826 This is useful for writing repository conversion tools, but should
827 be used with care.
827 be used with care.
828 """
828 """
829
829
830 if not rev2:
830 if not rev2:
831 rev2 = hex(nullid)
831 rev2 = hex(nullid)
832
832
833 wlock = repo.wlock()
833 wlock = repo.wlock()
834 try:
834 try:
835 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
835 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
836 finally:
836 finally:
837 wlock.release()
837 wlock.release()
838
838
839 def debugstate(ui, repo, nodates=None):
839 def debugstate(ui, repo, nodates=None):
840 """show the contents of the current dirstate"""
840 """show the contents of the current dirstate"""
841 timestr = ""
841 timestr = ""
842 showdate = not nodates
842 showdate = not nodates
843 for file_, ent in sorted(repo.dirstate._map.iteritems()):
843 for file_, ent in sorted(repo.dirstate._map.iteritems()):
844 if showdate:
844 if showdate:
845 if ent[3] == -1:
845 if ent[3] == -1:
846 # Pad or slice to locale representation
846 # Pad or slice to locale representation
847 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
847 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
848 timestr = 'unset'
848 timestr = 'unset'
849 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
849 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
850 else:
850 else:
851 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
851 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
852 if ent[1] & 020000:
852 if ent[1] & 020000:
853 mode = 'lnk'
853 mode = 'lnk'
854 else:
854 else:
855 mode = '%3o' % (ent[1] & 0777)
855 mode = '%3o' % (ent[1] & 0777)
856 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
856 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
857 for f in repo.dirstate.copies():
857 for f in repo.dirstate.copies():
858 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
858 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
859
859
860 def debugdata(ui, file_, rev):
860 def debugdata(ui, file_, rev):
861 """dump the contents of a data file revision"""
861 """dump the contents of a data file revision"""
862 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
862 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
863 try:
863 try:
864 ui.write(r.revision(r.lookup(rev)))
864 ui.write(r.revision(r.lookup(rev)))
865 except KeyError:
865 except KeyError:
866 raise util.Abort(_('invalid revision identifier %s') % rev)
866 raise util.Abort(_('invalid revision identifier %s') % rev)
867
867
868 def debugdate(ui, date, range=None, **opts):
868 def debugdate(ui, date, range=None, **opts):
869 """parse and display a date"""
869 """parse and display a date"""
870 if opts["extended"]:
870 if opts["extended"]:
871 d = util.parsedate(date, util.extendeddateformats)
871 d = util.parsedate(date, util.extendeddateformats)
872 else:
872 else:
873 d = util.parsedate(date)
873 d = util.parsedate(date)
874 ui.write("internal: %s %s\n" % d)
874 ui.write("internal: %s %s\n" % d)
875 ui.write("standard: %s\n" % util.datestr(d))
875 ui.write("standard: %s\n" % util.datestr(d))
876 if range:
876 if range:
877 m = util.matchdate(range)
877 m = util.matchdate(range)
878 ui.write("match: %s\n" % m(d[0]))
878 ui.write("match: %s\n" % m(d[0]))
879
879
880 def debugindex(ui, file_):
880 def debugindex(ui, file_):
881 """dump the contents of an index file"""
881 """dump the contents of an index file"""
882 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
882 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
883 ui.write(" rev offset length base linkrev"
883 ui.write(" rev offset length base linkrev"
884 " nodeid p1 p2\n")
884 " nodeid p1 p2\n")
885 for i in r:
885 for i in r:
886 node = r.node(i)
886 node = r.node(i)
887 try:
887 try:
888 pp = r.parents(node)
888 pp = r.parents(node)
889 except:
889 except:
890 pp = [nullid, nullid]
890 pp = [nullid, nullid]
891 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
891 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
892 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
892 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
893 short(node), short(pp[0]), short(pp[1])))
893 short(node), short(pp[0]), short(pp[1])))
894
894
895 def debugindexdot(ui, file_):
895 def debugindexdot(ui, file_):
896 """dump an index DAG as a graphviz dot file"""
896 """dump an index DAG as a graphviz dot file"""
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 ui.write("digraph G {\n")
898 ui.write("digraph G {\n")
899 for i in r:
899 for i in r:
900 node = r.node(i)
900 node = r.node(i)
901 pp = r.parents(node)
901 pp = r.parents(node)
902 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
902 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
903 if pp[1] != nullid:
903 if pp[1] != nullid:
904 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
904 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
905 ui.write("}\n")
905 ui.write("}\n")
906
906
907 def debuginstall(ui):
907 def debuginstall(ui):
908 '''test Mercurial installation'''
908 '''test Mercurial installation'''
909
909
910 def writetemp(contents):
910 def writetemp(contents):
911 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
911 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
912 f = os.fdopen(fd, "wb")
912 f = os.fdopen(fd, "wb")
913 f.write(contents)
913 f.write(contents)
914 f.close()
914 f.close()
915 return name
915 return name
916
916
917 problems = 0
917 problems = 0
918
918
919 # encoding
919 # encoding
920 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
920 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
921 try:
921 try:
922 encoding.fromlocal("test")
922 encoding.fromlocal("test")
923 except util.Abort, inst:
923 except util.Abort, inst:
924 ui.write(" %s\n" % inst)
924 ui.write(" %s\n" % inst)
925 ui.write(_(" (check that your locale is properly set)\n"))
925 ui.write(_(" (check that your locale is properly set)\n"))
926 problems += 1
926 problems += 1
927
927
928 # compiled modules
928 # compiled modules
929 ui.status(_("Checking extensions...\n"))
929 ui.status(_("Checking extensions...\n"))
930 try:
930 try:
931 import bdiff, mpatch, base85
931 import bdiff, mpatch, base85
932 except Exception, inst:
932 except Exception, inst:
933 ui.write(" %s\n" % inst)
933 ui.write(" %s\n" % inst)
934 ui.write(_(" One or more extensions could not be found"))
934 ui.write(_(" One or more extensions could not be found"))
935 ui.write(_(" (check that you compiled the extensions)\n"))
935 ui.write(_(" (check that you compiled the extensions)\n"))
936 problems += 1
936 problems += 1
937
937
938 # templates
938 # templates
939 ui.status(_("Checking templates...\n"))
939 ui.status(_("Checking templates...\n"))
940 try:
940 try:
941 import templater
941 import templater
942 templater.templater(templater.templatepath("map-cmdline.default"))
942 templater.templater(templater.templatepath("map-cmdline.default"))
943 except Exception, inst:
943 except Exception, inst:
944 ui.write(" %s\n" % inst)
944 ui.write(" %s\n" % inst)
945 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
945 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
946 problems += 1
946 problems += 1
947
947
948 # patch
948 # patch
949 ui.status(_("Checking patch...\n"))
949 ui.status(_("Checking patch...\n"))
950 patchproblems = 0
950 patchproblems = 0
951 a = "1\n2\n3\n4\n"
951 a = "1\n2\n3\n4\n"
952 b = "1\n2\n3\ninsert\n4\n"
952 b = "1\n2\n3\ninsert\n4\n"
953 fa = writetemp(a)
953 fa = writetemp(a)
954 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
954 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
955 os.path.basename(fa))
955 os.path.basename(fa))
956 fd = writetemp(d)
956 fd = writetemp(d)
957
957
958 files = {}
958 files = {}
959 try:
959 try:
960 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
960 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
961 except util.Abort, e:
961 except util.Abort, e:
962 ui.write(_(" patch call failed:\n"))
962 ui.write(_(" patch call failed:\n"))
963 ui.write(" " + str(e) + "\n")
963 ui.write(" " + str(e) + "\n")
964 patchproblems += 1
964 patchproblems += 1
965 else:
965 else:
966 if list(files) != [os.path.basename(fa)]:
966 if list(files) != [os.path.basename(fa)]:
967 ui.write(_(" unexpected patch output!\n"))
967 ui.write(_(" unexpected patch output!\n"))
968 patchproblems += 1
968 patchproblems += 1
969 a = file(fa).read()
969 a = file(fa).read()
970 if a != b:
970 if a != b:
971 ui.write(_(" patch test failed!\n"))
971 ui.write(_(" patch test failed!\n"))
972 patchproblems += 1
972 patchproblems += 1
973
973
974 if patchproblems:
974 if patchproblems:
975 if ui.config('ui', 'patch'):
975 if ui.config('ui', 'patch'):
976 ui.write(_(" (Current patch tool may be incompatible with patch,"
976 ui.write(_(" (Current patch tool may be incompatible with patch,"
977 " or misconfigured. Please check your .hgrc file)\n"))
977 " or misconfigured. Please check your .hgrc file)\n"))
978 else:
978 else:
979 ui.write(_(" Internal patcher failure, please report this error"
979 ui.write(_(" Internal patcher failure, please report this error"
980 " to http://www.selenic.com/mercurial/bts\n"))
980 " to http://www.selenic.com/mercurial/bts\n"))
981 problems += patchproblems
981 problems += patchproblems
982
982
983 os.unlink(fa)
983 os.unlink(fa)
984 os.unlink(fd)
984 os.unlink(fd)
985
985
986 # editor
986 # editor
987 ui.status(_("Checking commit editor...\n"))
987 ui.status(_("Checking commit editor...\n"))
988 editor = ui.geteditor()
988 editor = ui.geteditor()
989 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
989 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
990 if not cmdpath:
990 if not cmdpath:
991 if editor == 'vi':
991 if editor == 'vi':
992 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
992 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
993 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
993 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
994 else:
994 else:
995 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
995 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
996 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
996 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
997 problems += 1
997 problems += 1
998
998
999 # check username
999 # check username
1000 ui.status(_("Checking username...\n"))
1000 ui.status(_("Checking username...\n"))
1001 user = os.environ.get("HGUSER")
1001 user = os.environ.get("HGUSER")
1002 if user is None:
1002 if user is None:
1003 user = ui.config("ui", "username")
1003 user = ui.config("ui", "username")
1004 if user is None:
1004 if user is None:
1005 user = os.environ.get("EMAIL")
1005 user = os.environ.get("EMAIL")
1006 if not user:
1006 if not user:
1007 ui.warn(" ")
1007 ui.warn(" ")
1008 ui.username()
1008 ui.username()
1009 ui.write(_(" (specify a username in your .hgrc file)\n"))
1009 ui.write(_(" (specify a username in your .hgrc file)\n"))
1010
1010
1011 if not problems:
1011 if not problems:
1012 ui.status(_("No problems detected\n"))
1012 ui.status(_("No problems detected\n"))
1013 else:
1013 else:
1014 ui.write(_("%s problems detected,"
1014 ui.write(_("%s problems detected,"
1015 " please check your install!\n") % problems)
1015 " please check your install!\n") % problems)
1016
1016
1017 return problems
1017 return problems
1018
1018
1019 def debugrename(ui, repo, file1, *pats, **opts):
1019 def debugrename(ui, repo, file1, *pats, **opts):
1020 """dump rename information"""
1020 """dump rename information"""
1021
1021
1022 ctx = repo[opts.get('rev')]
1022 ctx = repo[opts.get('rev')]
1023 m = cmdutil.match(repo, (file1,) + pats, opts)
1023 m = cmdutil.match(repo, (file1,) + pats, opts)
1024 for abs in ctx.walk(m):
1024 for abs in ctx.walk(m):
1025 fctx = ctx[abs]
1025 fctx = ctx[abs]
1026 o = fctx.filelog().renamed(fctx.filenode())
1026 o = fctx.filelog().renamed(fctx.filenode())
1027 rel = m.rel(abs)
1027 rel = m.rel(abs)
1028 if o:
1028 if o:
1029 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1029 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1030 else:
1030 else:
1031 ui.write(_("%s not renamed\n") % rel)
1031 ui.write(_("%s not renamed\n") % rel)
1032
1032
1033 def debugwalk(ui, repo, *pats, **opts):
1033 def debugwalk(ui, repo, *pats, **opts):
1034 """show how files match on given patterns"""
1034 """show how files match on given patterns"""
1035 m = cmdutil.match(repo, pats, opts)
1035 m = cmdutil.match(repo, pats, opts)
1036 items = list(repo.walk(m))
1036 items = list(repo.walk(m))
1037 if not items:
1037 if not items:
1038 return
1038 return
1039 fmt = 'f %%-%ds %%-%ds %%s' % (
1039 fmt = 'f %%-%ds %%-%ds %%s' % (
1040 max([len(abs) for abs in items]),
1040 max([len(abs) for abs in items]),
1041 max([len(m.rel(abs)) for abs in items]))
1041 max([len(m.rel(abs)) for abs in items]))
1042 for abs in items:
1042 for abs in items:
1043 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1043 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1044 ui.write("%s\n" % line.rstrip())
1044 ui.write("%s\n" % line.rstrip())
1045
1045
1046 def diff(ui, repo, *pats, **opts):
1046 def diff(ui, repo, *pats, **opts):
1047 """diff repository (or selected files)
1047 """diff repository (or selected files)
1048
1048
1049 Show differences between revisions for the specified files.
1049 Show differences between revisions for the specified files.
1050
1050
1051 Differences between files are shown using the unified diff format.
1051 Differences between files are shown using the unified diff format.
1052
1052
1053 NOTE: diff may generate unexpected results for merges, as it will
1053 NOTE: diff may generate unexpected results for merges, as it will
1054 default to comparing against the working directory's first parent
1054 default to comparing against the working directory's first parent
1055 changeset if no revisions are specified.
1055 changeset if no revisions are specified.
1056
1056
1057 When two revision arguments are given, then changes are shown
1057 When two revision arguments are given, then changes are shown
1058 between those revisions. If only one revision is specified then
1058 between those revisions. If only one revision is specified then
1059 that revision is compared to the working directory, and, when no
1059 that revision is compared to the working directory, and, when no
1060 revisions are specified, the working directory files are compared
1060 revisions are specified, the working directory files are compared
1061 to its parent.
1061 to its parent.
1062
1062
1063 Without the -a/--text option, diff will avoid generating diffs of
1063 Without the -a/--text option, diff will avoid generating diffs of
1064 files it detects as binary. With -a, diff will generate a diff
1064 files it detects as binary. With -a, diff will generate a diff
1065 anyway, probably with undesirable results.
1065 anyway, probably with undesirable results.
1066
1066
1067 Use the -g/--git option to generate diffs in the git extended diff
1067 Use the -g/--git option to generate diffs in the git extended diff
1068 format. For more information, read 'hg help diffs'.
1068 format. For more information, read 'hg help diffs'.
1069 """
1069 """
1070
1070
1071 revs = opts.get('rev')
1071 revs = opts.get('rev')
1072 change = opts.get('change')
1072 change = opts.get('change')
1073
1073
1074 if revs and change:
1074 if revs and change:
1075 msg = _('cannot specify --rev and --change at the same time')
1075 msg = _('cannot specify --rev and --change at the same time')
1076 raise util.Abort(msg)
1076 raise util.Abort(msg)
1077 elif change:
1077 elif change:
1078 node2 = repo.lookup(change)
1078 node2 = repo.lookup(change)
1079 node1 = repo[node2].parents()[0].node()
1079 node1 = repo[node2].parents()[0].node()
1080 else:
1080 else:
1081 node1, node2 = cmdutil.revpair(repo, revs)
1081 node1, node2 = cmdutil.revpair(repo, revs)
1082
1082
1083 m = cmdutil.match(repo, pats, opts)
1083 m = cmdutil.match(repo, pats, opts)
1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1085 for chunk in it:
1085 for chunk in it:
1086 ui.write(chunk)
1086 ui.write(chunk)
1087
1087
1088 def export(ui, repo, *changesets, **opts):
1088 def export(ui, repo, *changesets, **opts):
1089 """dump the header and diffs for one or more changesets
1089 """dump the header and diffs for one or more changesets
1090
1090
1091 Print the changeset header and diffs for one or more revisions.
1091 Print the changeset header and diffs for one or more revisions.
1092
1092
1093 The information shown in the changeset header is: author,
1093 The information shown in the changeset header is: author,
1094 changeset hash, parent(s) and commit comment.
1094 changeset hash, parent(s) and commit comment.
1095
1095
1096 NOTE: export may generate unexpected diff output for merge
1096 NOTE: export may generate unexpected diff output for merge
1097 changesets, as it will compare the merge changeset against its
1097 changesets, as it will compare the merge changeset against its
1098 first parent only.
1098 first parent only.
1099
1099
1100 Output may be to a file, in which case the name of the file is
1100 Output may be to a file, in which case the name of the file is
1101 given using a format string. The formatting rules are as follows:
1101 given using a format string. The formatting rules are as follows:
1102
1102
1103 %% literal "%" character
1103 %% literal "%" character
1104 %H changeset hash (40 bytes of hexadecimal)
1104 %H changeset hash (40 bytes of hexadecimal)
1105 %N number of patches being generated
1105 %N number of patches being generated
1106 %R changeset revision number
1106 %R changeset revision number
1107 %b basename of the exporting repository
1107 %b basename of the exporting repository
1108 %h short-form changeset hash (12 bytes of hexadecimal)
1108 %h short-form changeset hash (12 bytes of hexadecimal)
1109 %n zero-padded sequence number, starting at 1
1109 %n zero-padded sequence number, starting at 1
1110 %r zero-padded changeset revision number
1110 %r zero-padded changeset revision number
1111
1111
1112 Without the -a/--text option, export will avoid generating diffs
1112 Without the -a/--text option, export will avoid generating diffs
1113 of files it detects as binary. With -a, export will generate a
1113 of files it detects as binary. With -a, export will generate a
1114 diff anyway, probably with undesirable results.
1114 diff anyway, probably with undesirable results.
1115
1115
1116 Use the -g/--git option to generate diffs in the git extended diff
1116 Use the -g/--git option to generate diffs in the git extended diff
1117 format. See 'hg help diffs' for more information.
1117 format. See 'hg help diffs' for more information.
1118
1118
1119 With the --switch-parent option, the diff will be against the
1119 With the --switch-parent option, the diff will be against the
1120 second parent. It can be useful to review a merge.
1120 second parent. It can be useful to review a merge.
1121 """
1121 """
1122 if not changesets:
1122 if not changesets:
1123 raise util.Abort(_("export requires at least one changeset"))
1123 raise util.Abort(_("export requires at least one changeset"))
1124 revs = cmdutil.revrange(repo, changesets)
1124 revs = cmdutil.revrange(repo, changesets)
1125 if len(revs) > 1:
1125 if len(revs) > 1:
1126 ui.note(_('exporting patches:\n'))
1126 ui.note(_('exporting patches:\n'))
1127 else:
1127 else:
1128 ui.note(_('exporting patch:\n'))
1128 ui.note(_('exporting patch:\n'))
1129 patch.export(repo, revs, template=opts.get('output'),
1129 patch.export(repo, revs, template=opts.get('output'),
1130 switch_parent=opts.get('switch_parent'),
1130 switch_parent=opts.get('switch_parent'),
1131 opts=patch.diffopts(ui, opts))
1131 opts=patch.diffopts(ui, opts))
1132
1132
1133 def grep(ui, repo, pattern, *pats, **opts):
1133 def grep(ui, repo, pattern, *pats, **opts):
1134 """search for a pattern in specified files and revisions
1134 """search for a pattern in specified files and revisions
1135
1135
1136 Search revisions of files for a regular expression.
1136 Search revisions of files for a regular expression.
1137
1137
1138 This command behaves differently than Unix grep. It only accepts
1138 This command behaves differently than Unix grep. It only accepts
1139 Python/Perl regexps. It searches repository history, not the
1139 Python/Perl regexps. It searches repository history, not the
1140 working directory. It always prints the revision number in which a
1140 working directory. It always prints the revision number in which a
1141 match appears.
1141 match appears.
1142
1142
1143 By default, grep only prints output for the first revision of a
1143 By default, grep only prints output for the first revision of a
1144 file in which it finds a match. To get it to print every revision
1144 file in which it finds a match. To get it to print every revision
1145 that contains a change in match status ("-" for a match that
1145 that contains a change in match status ("-" for a match that
1146 becomes a non-match, or "+" for a non-match that becomes a match),
1146 becomes a non-match, or "+" for a non-match that becomes a match),
1147 use the --all flag.
1147 use the --all flag.
1148 """
1148 """
1149 reflags = 0
1149 reflags = 0
1150 if opts.get('ignore_case'):
1150 if opts.get('ignore_case'):
1151 reflags |= re.I
1151 reflags |= re.I
1152 try:
1152 try:
1153 regexp = re.compile(pattern, reflags)
1153 regexp = re.compile(pattern, reflags)
1154 except Exception, inst:
1154 except Exception, inst:
1155 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1155 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1156 return None
1156 return None
1157 sep, eol = ':', '\n'
1157 sep, eol = ':', '\n'
1158 if opts.get('print0'):
1158 if opts.get('print0'):
1159 sep = eol = '\0'
1159 sep = eol = '\0'
1160
1160
1161 fcache = {}
1161 fcache = {}
1162 forder = []
1162 forder = []
1163 def getfile(fn):
1163 def getfile(fn):
1164 if fn not in fcache:
1164 if fn not in fcache:
1165 if len(fcache) > 20:
1165 if len(fcache) > 20:
1166 del fcache[forder.pop(0)]
1166 del fcache[forder.pop(0)]
1167 fcache[fn] = repo.file(fn)
1167 fcache[fn] = repo.file(fn)
1168 else:
1168 else:
1169 forder.remove(fn)
1169 forder.remove(fn)
1170
1170
1171 forder.append(fn)
1171 forder.append(fn)
1172 return fcache[fn]
1172 return fcache[fn]
1173
1173
1174 def matchlines(body):
1174 def matchlines(body):
1175 begin = 0
1175 begin = 0
1176 linenum = 0
1176 linenum = 0
1177 while True:
1177 while True:
1178 match = regexp.search(body, begin)
1178 match = regexp.search(body, begin)
1179 if not match:
1179 if not match:
1180 break
1180 break
1181 mstart, mend = match.span()
1181 mstart, mend = match.span()
1182 linenum += body.count('\n', begin, mstart) + 1
1182 linenum += body.count('\n', begin, mstart) + 1
1183 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1183 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1184 begin = body.find('\n', mend) + 1 or len(body)
1184 begin = body.find('\n', mend) + 1 or len(body)
1185 lend = begin - 1
1185 lend = begin - 1
1186 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1186 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1187
1187
1188 class linestate(object):
1188 class linestate(object):
1189 def __init__(self, line, linenum, colstart, colend):
1189 def __init__(self, line, linenum, colstart, colend):
1190 self.line = line
1190 self.line = line
1191 self.linenum = linenum
1191 self.linenum = linenum
1192 self.colstart = colstart
1192 self.colstart = colstart
1193 self.colend = colend
1193 self.colend = colend
1194
1194
1195 def __hash__(self):
1195 def __hash__(self):
1196 return hash((self.linenum, self.line))
1196 return hash((self.linenum, self.line))
1197
1197
1198 def __eq__(self, other):
1198 def __eq__(self, other):
1199 return self.line == other.line
1199 return self.line == other.line
1200
1200
1201 matches = {}
1201 matches = {}
1202 copies = {}
1202 copies = {}
1203 def grepbody(fn, rev, body):
1203 def grepbody(fn, rev, body):
1204 matches[rev].setdefault(fn, [])
1204 matches[rev].setdefault(fn, [])
1205 m = matches[rev][fn]
1205 m = matches[rev][fn]
1206 for lnum, cstart, cend, line in matchlines(body):
1206 for lnum, cstart, cend, line in matchlines(body):
1207 s = linestate(line, lnum, cstart, cend)
1207 s = linestate(line, lnum, cstart, cend)
1208 m.append(s)
1208 m.append(s)
1209
1209
1210 def difflinestates(a, b):
1210 def difflinestates(a, b):
1211 sm = difflib.SequenceMatcher(None, a, b)
1211 sm = difflib.SequenceMatcher(None, a, b)
1212 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1212 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1213 if tag == 'insert':
1213 if tag == 'insert':
1214 for i in xrange(blo, bhi):
1214 for i in xrange(blo, bhi):
1215 yield ('+', b[i])
1215 yield ('+', b[i])
1216 elif tag == 'delete':
1216 elif tag == 'delete':
1217 for i in xrange(alo, ahi):
1217 for i in xrange(alo, ahi):
1218 yield ('-', a[i])
1218 yield ('-', a[i])
1219 elif tag == 'replace':
1219 elif tag == 'replace':
1220 for i in xrange(alo, ahi):
1220 for i in xrange(alo, ahi):
1221 yield ('-', a[i])
1221 yield ('-', a[i])
1222 for i in xrange(blo, bhi):
1222 for i in xrange(blo, bhi):
1223 yield ('+', b[i])
1223 yield ('+', b[i])
1224
1224
1225 prev = {}
1225 prev = {}
1226 def display(fn, rev, states, prevstates):
1226 def display(fn, rev, states, prevstates):
1227 datefunc = ui.quiet and util.shortdate or util.datestr
1227 datefunc = ui.quiet and util.shortdate or util.datestr
1228 found = False
1228 found = False
1229 filerevmatches = {}
1229 filerevmatches = {}
1230 r = prev.get(fn, -1)
1230 r = prev.get(fn, -1)
1231 if opts.get('all'):
1231 if opts.get('all'):
1232 iter = difflinestates(states, prevstates)
1232 iter = difflinestates(states, prevstates)
1233 else:
1233 else:
1234 iter = [('', l) for l in prevstates]
1234 iter = [('', l) for l in prevstates]
1235 for change, l in iter:
1235 for change, l in iter:
1236 cols = [fn, str(r)]
1236 cols = [fn, str(r)]
1237 if opts.get('line_number'):
1237 if opts.get('line_number'):
1238 cols.append(str(l.linenum))
1238 cols.append(str(l.linenum))
1239 if opts.get('all'):
1239 if opts.get('all'):
1240 cols.append(change)
1240 cols.append(change)
1241 if opts.get('user'):
1241 if opts.get('user'):
1242 cols.append(ui.shortuser(get(r)[1]))
1242 cols.append(ui.shortuser(get(r)[1]))
1243 if opts.get('date'):
1243 if opts.get('date'):
1244 cols.append(datefunc(get(r)[2]))
1244 cols.append(datefunc(get(r)[2]))
1245 if opts.get('files_with_matches'):
1245 if opts.get('files_with_matches'):
1246 c = (fn, r)
1246 c = (fn, r)
1247 if c in filerevmatches:
1247 if c in filerevmatches:
1248 continue
1248 continue
1249 filerevmatches[c] = 1
1249 filerevmatches[c] = 1
1250 else:
1250 else:
1251 cols.append(l.line)
1251 cols.append(l.line)
1252 ui.write(sep.join(cols), eol)
1252 ui.write(sep.join(cols), eol)
1253 found = True
1253 found = True
1254 return found
1254 return found
1255
1255
1256 fstate = {}
1256 fstate = {}
1257 skip = {}
1257 skip = {}
1258 get = util.cachefunc(lambda r: repo[r].changeset())
1258 get = util.cachefunc(lambda r: repo[r].changeset())
1259 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1259 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1260 found = False
1260 found = False
1261 follow = opts.get('follow')
1261 follow = opts.get('follow')
1262 for st, rev, fns in changeiter:
1262 for st, rev, fns in changeiter:
1263 if st == 'window':
1263 if st == 'window':
1264 matches.clear()
1264 matches.clear()
1265 elif st == 'add':
1265 elif st == 'add':
1266 ctx = repo[rev]
1266 ctx = repo[rev]
1267 matches[rev] = {}
1267 matches[rev] = {}
1268 for fn in fns:
1268 for fn in fns:
1269 if fn in skip:
1269 if fn in skip:
1270 continue
1270 continue
1271 try:
1271 try:
1272 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1272 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1273 fstate.setdefault(fn, [])
1273 fstate.setdefault(fn, [])
1274 if follow:
1274 if follow:
1275 copied = getfile(fn).renamed(ctx.filenode(fn))
1275 copied = getfile(fn).renamed(ctx.filenode(fn))
1276 if copied:
1276 if copied:
1277 copies.setdefault(rev, {})[fn] = copied[0]
1277 copies.setdefault(rev, {})[fn] = copied[0]
1278 except error.LookupError:
1278 except error.LookupError:
1279 pass
1279 pass
1280 elif st == 'iter':
1280 elif st == 'iter':
1281 for fn, m in sorted(matches[rev].items()):
1281 for fn, m in sorted(matches[rev].items()):
1282 copy = copies.get(rev, {}).get(fn)
1282 copy = copies.get(rev, {}).get(fn)
1283 if fn in skip:
1283 if fn in skip:
1284 if copy:
1284 if copy:
1285 skip[copy] = True
1285 skip[copy] = True
1286 continue
1286 continue
1287 if fn in prev or fstate[fn]:
1287 if fn in prev or fstate[fn]:
1288 r = display(fn, rev, m, fstate[fn])
1288 r = display(fn, rev, m, fstate[fn])
1289 found = found or r
1289 found = found or r
1290 if r and not opts.get('all'):
1290 if r and not opts.get('all'):
1291 skip[fn] = True
1291 skip[fn] = True
1292 if copy:
1292 if copy:
1293 skip[copy] = True
1293 skip[copy] = True
1294 fstate[fn] = m
1294 fstate[fn] = m
1295 if copy:
1295 if copy:
1296 fstate[copy] = m
1296 fstate[copy] = m
1297 prev[fn] = rev
1297 prev[fn] = rev
1298
1298
1299 for fn, state in sorted(fstate.items()):
1299 for fn, state in sorted(fstate.items()):
1300 if fn in skip:
1300 if fn in skip:
1301 continue
1301 continue
1302 if fn not in copies.get(prev[fn], {}):
1302 if fn not in copies.get(prev[fn], {}):
1303 found = display(fn, rev, {}, state) or found
1303 found = display(fn, rev, {}, state) or found
1304 return (not found and 1) or 0
1304 return (not found and 1) or 0
1305
1305
1306 def heads(ui, repo, *branchrevs, **opts):
1306 def heads(ui, repo, *branchrevs, **opts):
1307 """show current repository heads or show branch heads
1307 """show current repository heads or show branch heads
1308
1308
1309 With no arguments, show all repository head changesets.
1309 With no arguments, show all repository head changesets.
1310
1310
1311 Repository "heads" are changesets that don't have child
1311 Repository "heads" are changesets that don't have child
1312 changesets. They are where development generally takes place and
1312 changesets. They are where development generally takes place and
1313 are the usual targets for update and merge operations.
1313 are the usual targets for update and merge operations.
1314
1314
1315 If one or more REV is given, the "branch heads" will be shown for
1315 If one or more REV is given, the "branch heads" will be shown for
1316 the named branch associated with that revision. The name of the
1316 the named branch associated with that revision. The name of the
1317 branch is called the revision's branch tag.
1317 branch is called the revision's branch tag.
1318
1318
1319 Branch heads are revisions on a given named branch that do not have
1319 Branch heads are revisions on a given named branch that do not have
1320 any children on the same branch. A branch head could be a true head
1320 any children on the same branch. A branch head could be a true head
1321 or it could be the last changeset on a branch before a new branch
1321 or it could be the last changeset on a branch before a new branch
1322 was created. If none of the branch heads are true heads, the branch
1322 was created. If none of the branch heads are true heads, the branch
1323 is considered inactive.
1323 is considered inactive.
1324
1324
1325 If STARTREV is specified only those heads (or branch heads) that
1325 If STARTREV is specified only those heads (or branch heads) that
1326 are descendants of STARTREV will be displayed.
1326 are descendants of STARTREV will be displayed.
1327 """
1327 """
1328 if opts.get('rev'):
1328 if opts.get('rev'):
1329 start = repo.lookup(opts['rev'])
1329 start = repo.lookup(opts['rev'])
1330 else:
1330 else:
1331 start = None
1331 start = None
1332 closed = opts.get('closed')
1332 closed = opts.get('closed')
1333 hideinactive, _heads = opts.get('active'), None
1333 hideinactive, _heads = opts.get('active'), None
1334 if not branchrevs:
1334 if not branchrevs:
1335 # Assume we're looking repo-wide heads if no revs were specified.
1335 # Assume we're looking repo-wide heads if no revs were specified.
1336 heads = repo.heads(start)
1336 heads = repo.heads(start)
1337 else:
1337 else:
1338 if hideinactive:
1338 if hideinactive:
1339 _heads = repo.heads(start)
1339 _heads = repo.heads(start)
1340 heads = []
1340 heads = []
1341 visitedset = set()
1341 visitedset = set()
1342 for branchrev in branchrevs:
1342 for branchrev in branchrevs:
1343 branch = repo[branchrev].branch()
1343 branch = repo[branchrev].branch()
1344 if branch in visitedset:
1344 if branch in visitedset:
1345 continue
1345 continue
1346 visitedset.add(branch)
1346 visitedset.add(branch)
1347 bheads = repo.branchheads(branch, start, closed=closed)
1347 bheads = repo.branchheads(branch, start, closed=closed)
1348 if not bheads:
1348 if not bheads:
1349 if not opts.get('rev'):
1349 if not opts.get('rev'):
1350 ui.warn(_("no open branch heads on branch %s\n") % branch)
1350 ui.warn(_("no open branch heads on branch %s\n") % branch)
1351 elif branch != branchrev:
1351 elif branch != branchrev:
1352 ui.warn(_("no changes on branch %s containing %s are "
1352 ui.warn(_("no changes on branch %s containing %s are "
1353 "reachable from %s\n")
1353 "reachable from %s\n")
1354 % (branch, branchrev, opts.get('rev')))
1354 % (branch, branchrev, opts.get('rev')))
1355 else:
1355 else:
1356 ui.warn(_("no changes on branch %s are reachable from %s\n")
1356 ui.warn(_("no changes on branch %s are reachable from %s\n")
1357 % (branch, opts.get('rev')))
1357 % (branch, opts.get('rev')))
1358 if hideinactive:
1358 if hideinactive:
1359 bheads = [bhead for bhead in bheads if bhead in _heads]
1359 bheads = [bhead for bhead in bheads if bhead in _heads]
1360 heads.extend(bheads)
1360 heads.extend(bheads)
1361 if not heads:
1361 if not heads:
1362 return 1
1362 return 1
1363 displayer = cmdutil.show_changeset(ui, repo, opts)
1363 displayer = cmdutil.show_changeset(ui, repo, opts)
1364 for n in heads:
1364 for n in heads:
1365 displayer.show(repo[n])
1365 displayer.show(repo[n])
1366
1366
1367 def help_(ui, name=None, with_version=False):
1367 def help_(ui, name=None, with_version=False):
1368 """show help for a given topic or a help overview
1368 """show help for a given topic or a help overview
1369
1369
1370 With no arguments, print a list of commands with short help messages.
1370 With no arguments, print a list of commands with short help messages.
1371
1371
1372 Given a topic, extension, or command name, print help for that
1372 Given a topic, extension, or command name, print help for that
1373 topic."""
1373 topic."""
1374 option_lists = []
1374 option_lists = []
1375
1375
1376 def addglobalopts(aliases):
1376 def addglobalopts(aliases):
1377 if ui.verbose:
1377 if ui.verbose:
1378 option_lists.append((_("global options:"), globalopts))
1378 option_lists.append((_("global options:"), globalopts))
1379 if name == 'shortlist':
1379 if name == 'shortlist':
1380 option_lists.append((_('use "hg help" for the full list '
1380 option_lists.append((_('use "hg help" for the full list '
1381 'of commands'), ()))
1381 'of commands'), ()))
1382 else:
1382 else:
1383 if name == 'shortlist':
1383 if name == 'shortlist':
1384 msg = _('use "hg help" for the full list of commands '
1384 msg = _('use "hg help" for the full list of commands '
1385 'or "hg -v" for details')
1385 'or "hg -v" for details')
1386 elif aliases:
1386 elif aliases:
1387 msg = _('use "hg -v help%s" to show aliases and '
1387 msg = _('use "hg -v help%s" to show aliases and '
1388 'global options') % (name and " " + name or "")
1388 'global options') % (name and " " + name or "")
1389 else:
1389 else:
1390 msg = _('use "hg -v help %s" to show global options') % name
1390 msg = _('use "hg -v help %s" to show global options') % name
1391 option_lists.append((msg, ()))
1391 option_lists.append((msg, ()))
1392
1392
1393 def helpcmd(name):
1393 def helpcmd(name):
1394 if with_version:
1394 if with_version:
1395 version_(ui)
1395 version_(ui)
1396 ui.write('\n')
1396 ui.write('\n')
1397
1397
1398 try:
1398 try:
1399 aliases, i = cmdutil.findcmd(name, table, False)
1399 aliases, i = cmdutil.findcmd(name, table, False)
1400 except error.AmbiguousCommand, inst:
1400 except error.AmbiguousCommand, inst:
1401 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1401 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1402 helplist(_('list of commands:\n\n'), select)
1402 helplist(_('list of commands:\n\n'), select)
1403 return
1403 return
1404
1404
1405 # synopsis
1405 # synopsis
1406 if len(i) > 2:
1406 if len(i) > 2:
1407 if i[2].startswith('hg'):
1407 if i[2].startswith('hg'):
1408 ui.write("%s\n" % i[2])
1408 ui.write("%s\n" % i[2])
1409 else:
1409 else:
1410 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1410 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1411 else:
1411 else:
1412 ui.write('hg %s\n' % aliases[0])
1412 ui.write('hg %s\n' % aliases[0])
1413
1413
1414 # aliases
1414 # aliases
1415 if not ui.quiet and len(aliases) > 1:
1415 if not ui.quiet and len(aliases) > 1:
1416 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1416 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1417
1417
1418 # description
1418 # description
1419 doc = gettext(i[0].__doc__)
1419 doc = gettext(i[0].__doc__)
1420 if not doc:
1420 if not doc:
1421 doc = _("(no help text available)")
1421 doc = _("(no help text available)")
1422 if ui.quiet:
1422 if ui.quiet:
1423 doc = doc.splitlines(0)[0]
1423 doc = doc.splitlines(0)[0]
1424 ui.write("\n%s\n" % doc.rstrip())
1424 ui.write("\n%s\n" % doc.rstrip())
1425
1425
1426 if not ui.quiet:
1426 if not ui.quiet:
1427 # options
1427 # options
1428 if i[1]:
1428 if i[1]:
1429 option_lists.append((_("options:\n"), i[1]))
1429 option_lists.append((_("options:\n"), i[1]))
1430
1430
1431 addglobalopts(False)
1431 addglobalopts(False)
1432
1432
1433 def helplist(header, select=None):
1433 def helplist(header, select=None):
1434 h = {}
1434 h = {}
1435 cmds = {}
1435 cmds = {}
1436 for c, e in table.iteritems():
1436 for c, e in table.iteritems():
1437 f = c.split("|", 1)[0]
1437 f = c.split("|", 1)[0]
1438 if select and not select(f):
1438 if select and not select(f):
1439 continue
1439 continue
1440 if (not select and name != 'shortlist' and
1440 if (not select and name != 'shortlist' and
1441 e[0].__module__ != __name__):
1441 e[0].__module__ != __name__):
1442 continue
1442 continue
1443 if name == "shortlist" and not f.startswith("^"):
1443 if name == "shortlist" and not f.startswith("^"):
1444 continue
1444 continue
1445 f = f.lstrip("^")
1445 f = f.lstrip("^")
1446 if not ui.debugflag and f.startswith("debug"):
1446 if not ui.debugflag and f.startswith("debug"):
1447 continue
1447 continue
1448 doc = gettext(e[0].__doc__)
1448 doc = gettext(e[0].__doc__)
1449 if not doc:
1449 if not doc:
1450 doc = _("(no help text available)")
1450 doc = _("(no help text available)")
1451 h[f] = doc.splitlines(0)[0].rstrip()
1451 h[f] = doc.splitlines(0)[0].rstrip()
1452 cmds[f] = c.lstrip("^")
1452 cmds[f] = c.lstrip("^")
1453
1453
1454 if not h:
1454 if not h:
1455 ui.status(_('no commands defined\n'))
1455 ui.status(_('no commands defined\n'))
1456 return
1456 return
1457
1457
1458 ui.status(header)
1458 ui.status(header)
1459 fns = sorted(h)
1459 fns = sorted(h)
1460 m = max(map(len, fns))
1460 m = max(map(len, fns))
1461 for f in fns:
1461 for f in fns:
1462 if ui.verbose:
1462 if ui.verbose:
1463 commands = cmds[f].replace("|",", ")
1463 commands = cmds[f].replace("|",", ")
1464 ui.write(" %s:\n %s\n"%(commands, h[f]))
1464 ui.write(" %s:\n %s\n"%(commands, h[f]))
1465 else:
1465 else:
1466 ui.write(' %-*s %s\n' % (m, f, h[f]))
1466 ui.write(' %-*s %s\n' % (m, f, h[f]))
1467
1467
1468 exts = list(extensions.extensions())
1468 exts = list(extensions.extensions())
1469 if exts and name != 'shortlist':
1469 if exts and name != 'shortlist':
1470 ui.write(_('\nenabled extensions:\n\n'))
1470 ui.write(_('\nenabled extensions:\n\n'))
1471 maxlength = 0
1471 maxlength = 0
1472 exthelps = []
1472 exthelps = []
1473 for ename, ext in exts:
1473 for ename, ext in exts:
1474 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1474 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1475 ename = ename.split('.')[-1]
1475 ename = ename.split('.')[-1]
1476 maxlength = max(len(ename), maxlength)
1476 maxlength = max(len(ename), maxlength)
1477 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1477 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1478 for ename, text in exthelps:
1478 for ename, text in exthelps:
1479 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1479 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1480
1480
1481 if not ui.quiet:
1481 if not ui.quiet:
1482 addglobalopts(True)
1482 addglobalopts(True)
1483
1483
1484 def helptopic(name):
1484 def helptopic(name):
1485 for names, header, doc in help.helptable:
1485 for names, header, doc in help.helptable:
1486 if name in names:
1486 if name in names:
1487 break
1487 break
1488 else:
1488 else:
1489 raise error.UnknownCommand(name)
1489 raise error.UnknownCommand(name)
1490
1490
1491 # description
1491 # description
1492 if not doc:
1492 if not doc:
1493 doc = _("(no help text available)")
1493 doc = _("(no help text available)")
1494 if hasattr(doc, '__call__'):
1494 if hasattr(doc, '__call__'):
1495 doc = doc()
1495 doc = doc()
1496
1496
1497 ui.write("%s\n" % header)
1497 ui.write("%s\n" % header)
1498 ui.write("%s\n" % doc.rstrip())
1498 ui.write("%s\n" % doc.rstrip())
1499
1499
1500 def helpext(name):
1500 def helpext(name):
1501 try:
1501 try:
1502 mod = extensions.find(name)
1502 mod = extensions.find(name)
1503 except KeyError:
1503 except KeyError:
1504 raise error.UnknownCommand(name)
1504 raise error.UnknownCommand(name)
1505
1505
1506 doc = gettext(mod.__doc__) or _('no help text available')
1506 doc = gettext(mod.__doc__) or _('no help text available')
1507 doc = doc.splitlines(0)
1507 doc = doc.splitlines(0)
1508 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1508 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1509 for d in doc[1:]:
1509 for d in doc[1:]:
1510 ui.write(d, '\n')
1510 ui.write(d, '\n')
1511
1511
1512 ui.status('\n')
1512 ui.status('\n')
1513
1513
1514 try:
1514 try:
1515 ct = mod.cmdtable
1515 ct = mod.cmdtable
1516 except AttributeError:
1516 except AttributeError:
1517 ct = {}
1517 ct = {}
1518
1518
1519 modcmds = set([c.split('|', 1)[0] for c in ct])
1519 modcmds = set([c.split('|', 1)[0] for c in ct])
1520 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1520 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1521
1521
1522 if name and name != 'shortlist':
1522 if name and name != 'shortlist':
1523 i = None
1523 i = None
1524 for f in (helptopic, helpcmd, helpext):
1524 for f in (helptopic, helpcmd, helpext):
1525 try:
1525 try:
1526 f(name)
1526 f(name)
1527 i = None
1527 i = None
1528 break
1528 break
1529 except error.UnknownCommand, inst:
1529 except error.UnknownCommand, inst:
1530 i = inst
1530 i = inst
1531 if i:
1531 if i:
1532 raise i
1532 raise i
1533
1533
1534 else:
1534 else:
1535 # program name
1535 # program name
1536 if ui.verbose or with_version:
1536 if ui.verbose or with_version:
1537 version_(ui)
1537 version_(ui)
1538 else:
1538 else:
1539 ui.status(_("Mercurial Distributed SCM\n"))
1539 ui.status(_("Mercurial Distributed SCM\n"))
1540 ui.status('\n')
1540 ui.status('\n')
1541
1541
1542 # list of commands
1542 # list of commands
1543 if name == "shortlist":
1543 if name == "shortlist":
1544 header = _('basic commands:\n\n')
1544 header = _('basic commands:\n\n')
1545 else:
1545 else:
1546 header = _('list of commands:\n\n')
1546 header = _('list of commands:\n\n')
1547
1547
1548 helplist(header)
1548 helplist(header)
1549
1549
1550 # list all option lists
1550 # list all option lists
1551 opt_output = []
1551 opt_output = []
1552 for title, options in option_lists:
1552 for title, options in option_lists:
1553 opt_output.append(("\n%s" % title, None))
1553 opt_output.append(("\n%s" % title, None))
1554 for shortopt, longopt, default, desc in options:
1554 for shortopt, longopt, default, desc in options:
1555 if "DEPRECATED" in desc and not ui.verbose: continue
1555 if "DEPRECATED" in desc and not ui.verbose: continue
1556 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1556 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1557 longopt and " --%s" % longopt),
1557 longopt and " --%s" % longopt),
1558 "%s%s" % (desc,
1558 "%s%s" % (desc,
1559 default
1559 default
1560 and _(" (default: %s)") % default
1560 and _(" (default: %s)") % default
1561 or "")))
1561 or "")))
1562
1562
1563 if not name:
1563 if not name:
1564 ui.write(_("\nadditional help topics:\n\n"))
1564 ui.write(_("\nadditional help topics:\n\n"))
1565 topics = []
1565 topics = []
1566 for names, header, doc in help.helptable:
1566 for names, header, doc in help.helptable:
1567 names = [(-len(name), name) for name in names]
1567 names = [(-len(name), name) for name in names]
1568 names.sort()
1568 names.sort()
1569 topics.append((names[0][1], header))
1569 topics.append((names[0][1], header))
1570 topics_len = max([len(s[0]) for s in topics])
1570 topics_len = max([len(s[0]) for s in topics])
1571 for t, desc in topics:
1571 for t, desc in topics:
1572 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1572 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1573
1573
1574 if opt_output:
1574 if opt_output:
1575 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1575 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1576 for first, second in opt_output:
1576 for first, second in opt_output:
1577 if second:
1577 if second:
1578 # wrap descriptions at 70 characters, just like the
1578 # wrap descriptions at 70 characters, just like the
1579 # main help texts
1579 # main help texts
1580 second = textwrap.wrap(second, width=70 - opts_len - 3)
1580 second = textwrap.wrap(second, width=70 - opts_len - 3)
1581 pad = '\n' + ' ' * (opts_len + 3)
1581 pad = '\n' + ' ' * (opts_len + 3)
1582 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1582 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1583 else:
1583 else:
1584 ui.write("%s\n" % first)
1584 ui.write("%s\n" % first)
1585
1585
1586 def identify(ui, repo, source=None,
1586 def identify(ui, repo, source=None,
1587 rev=None, num=None, id=None, branch=None, tags=None):
1587 rev=None, num=None, id=None, branch=None, tags=None):
1588 """identify the working copy or specified revision
1588 """identify the working copy or specified revision
1589
1589
1590 With no revision, print a summary of the current state of the
1590 With no revision, print a summary of the current state of the
1591 repository.
1591 repository.
1592
1592
1593 Specifying a path to a repository root or Mercurial bundle will
1593 Specifying a path to a repository root or Mercurial bundle will
1594 cause lookup to operate on that repository/bundle.
1594 cause lookup to operate on that repository/bundle.
1595
1595
1596 This summary identifies the repository state using one or two
1596 This summary identifies the repository state using one or two
1597 parent hash identifiers, followed by a "+" if there are
1597 parent hash identifiers, followed by a "+" if there are
1598 uncommitted changes in the working directory, a list of tags for
1598 uncommitted changes in the working directory, a list of tags for
1599 this revision and a branch name for non-default branches.
1599 this revision and a branch name for non-default branches.
1600 """
1600 """
1601
1601
1602 if not repo and not source:
1602 if not repo and not source:
1603 raise util.Abort(_("There is no Mercurial repository here "
1603 raise util.Abort(_("There is no Mercurial repository here "
1604 "(.hg not found)"))
1604 "(.hg not found)"))
1605
1605
1606 hexfunc = ui.debugflag and hex or short
1606 hexfunc = ui.debugflag and hex or short
1607 default = not (num or id or branch or tags)
1607 default = not (num or id or branch or tags)
1608 output = []
1608 output = []
1609
1609
1610 revs = []
1610 revs = []
1611 if source:
1611 if source:
1612 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1612 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1613 repo = hg.repository(ui, source)
1613 repo = hg.repository(ui, source)
1614
1614
1615 if not repo.local():
1615 if not repo.local():
1616 if not rev and revs:
1616 if not rev and revs:
1617 rev = revs[0]
1617 rev = revs[0]
1618 if not rev:
1618 if not rev:
1619 rev = "tip"
1619 rev = "tip"
1620 if num or branch or tags:
1620 if num or branch or tags:
1621 raise util.Abort(
1621 raise util.Abort(
1622 "can't query remote revision number, branch, or tags")
1622 "can't query remote revision number, branch, or tags")
1623 output = [hexfunc(repo.lookup(rev))]
1623 output = [hexfunc(repo.lookup(rev))]
1624 elif not rev:
1624 elif not rev:
1625 ctx = repo[None]
1625 ctx = repo[None]
1626 parents = ctx.parents()
1626 parents = ctx.parents()
1627 changed = False
1627 changed = False
1628 if default or id or num:
1628 if default or id or num:
1629 changed = ctx.files() + ctx.deleted()
1629 changed = ctx.files() + ctx.deleted()
1630 if default or id:
1630 if default or id:
1631 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1631 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1632 (changed) and "+" or "")]
1632 (changed) and "+" or "")]
1633 if num:
1633 if num:
1634 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1634 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1635 (changed) and "+" or ""))
1635 (changed) and "+" or ""))
1636 else:
1636 else:
1637 ctx = repo[rev]
1637 ctx = repo[rev]
1638 if default or id:
1638 if default or id:
1639 output = [hexfunc(ctx.node())]
1639 output = [hexfunc(ctx.node())]
1640 if num:
1640 if num:
1641 output.append(str(ctx.rev()))
1641 output.append(str(ctx.rev()))
1642
1642
1643 if repo.local() and default and not ui.quiet:
1643 if repo.local() and default and not ui.quiet:
1644 b = encoding.tolocal(ctx.branch())
1644 b = encoding.tolocal(ctx.branch())
1645 if b != 'default':
1645 if b != 'default':
1646 output.append("(%s)" % b)
1646 output.append("(%s)" % b)
1647
1647
1648 # multiple tags for a single parent separated by '/'
1648 # multiple tags for a single parent separated by '/'
1649 t = "/".join(ctx.tags())
1649 t = "/".join(ctx.tags())
1650 if t:
1650 if t:
1651 output.append(t)
1651 output.append(t)
1652
1652
1653 if branch:
1653 if branch:
1654 output.append(encoding.tolocal(ctx.branch()))
1654 output.append(encoding.tolocal(ctx.branch()))
1655
1655
1656 if tags:
1656 if tags:
1657 output.extend(ctx.tags())
1657 output.extend(ctx.tags())
1658
1658
1659 ui.write("%s\n" % ' '.join(output))
1659 ui.write("%s\n" % ' '.join(output))
1660
1660
1661 def import_(ui, repo, patch1, *patches, **opts):
1661 def import_(ui, repo, patch1, *patches, **opts):
1662 """import an ordered set of patches
1662 """import an ordered set of patches
1663
1663
1664 Import a list of patches and commit them individually.
1664 Import a list of patches and commit them individually.
1665
1665
1666 If there are outstanding changes in the working directory, import
1666 If there are outstanding changes in the working directory, import
1667 will abort unless given the -f/--force flag.
1667 will abort unless given the -f/--force flag.
1668
1668
1669 You can import a patch straight from a mail message. Even patches
1669 You can import a patch straight from a mail message. Even patches
1670 as attachments work (to use the body part, it must have type
1670 as attachments work (to use the body part, it must have type
1671 text/plain or text/x-patch). From and Subject headers of email
1671 text/plain or text/x-patch). From and Subject headers of email
1672 message are used as default committer and commit message. All
1672 message are used as default committer and commit message. All
1673 text/plain body parts before first diff are added to commit
1673 text/plain body parts before first diff are added to commit
1674 message.
1674 message.
1675
1675
1676 If the imported patch was generated by hg export, user and
1676 If the imported patch was generated by hg export, user and
1677 description from patch override values from message headers and
1677 description from patch override values from message headers and
1678 body. Values given on command line with -m/--message and -u/--user
1678 body. Values given on command line with -m/--message and -u/--user
1679 override these.
1679 override these.
1680
1680
1681 If --exact is specified, import will set the working directory to
1681 If --exact is specified, import will set the working directory to
1682 the parent of each patch before applying it, and will abort if the
1682 the parent of each patch before applying it, and will abort if the
1683 resulting changeset has a different ID than the one recorded in
1683 resulting changeset has a different ID than the one recorded in
1684 the patch. This may happen due to character set problems or other
1684 the patch. This may happen due to character set problems or other
1685 deficiencies in the text patch format.
1685 deficiencies in the text patch format.
1686
1686
1687 With -s/--similarity, hg will attempt to discover renames and
1687 With -s/--similarity, hg will attempt to discover renames and
1688 copies in the patch in the same way as 'addremove'.
1688 copies in the patch in the same way as 'addremove'.
1689
1689
1690 To read a patch from standard input, use "-" as the patch name.
1690 To read a patch from standard input, use "-" as the patch name.
1691 See 'hg help dates' for a list of formats valid for -d/--date.
1691 See 'hg help dates' for a list of formats valid for -d/--date.
1692 """
1692 """
1693 patches = (patch1,) + patches
1693 patches = (patch1,) + patches
1694
1694
1695 date = opts.get('date')
1695 date = opts.get('date')
1696 if date:
1696 if date:
1697 opts['date'] = util.parsedate(date)
1697 opts['date'] = util.parsedate(date)
1698
1698
1699 try:
1699 try:
1700 sim = float(opts.get('similarity') or 0)
1700 sim = float(opts.get('similarity') or 0)
1701 except ValueError:
1701 except ValueError:
1702 raise util.Abort(_('similarity must be a number'))
1702 raise util.Abort(_('similarity must be a number'))
1703 if sim < 0 or sim > 100:
1703 if sim < 0 or sim > 100:
1704 raise util.Abort(_('similarity must be between 0 and 100'))
1704 raise util.Abort(_('similarity must be between 0 and 100'))
1705
1705
1706 if opts.get('exact') or not opts.get('force'):
1706 if opts.get('exact') or not opts.get('force'):
1707 cmdutil.bail_if_changed(repo)
1707 cmdutil.bail_if_changed(repo)
1708
1708
1709 d = opts["base"]
1709 d = opts["base"]
1710 strip = opts["strip"]
1710 strip = opts["strip"]
1711 wlock = lock = None
1711 wlock = lock = None
1712 try:
1712 try:
1713 wlock = repo.wlock()
1713 wlock = repo.wlock()
1714 lock = repo.lock()
1714 lock = repo.lock()
1715 for p in patches:
1715 for p in patches:
1716 pf = os.path.join(d, p)
1716 pf = os.path.join(d, p)
1717
1717
1718 if pf == '-':
1718 if pf == '-':
1719 ui.status(_("applying patch from stdin\n"))
1719 ui.status(_("applying patch from stdin\n"))
1720 pf = sys.stdin
1720 pf = sys.stdin
1721 else:
1721 else:
1722 ui.status(_("applying %s\n") % p)
1722 ui.status(_("applying %s\n") % p)
1723 pf = url.open(ui, pf)
1723 pf = url.open(ui, pf)
1724 data = patch.extract(ui, pf)
1724 data = patch.extract(ui, pf)
1725 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1725 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1726
1726
1727 if tmpname is None:
1727 if tmpname is None:
1728 raise util.Abort(_('no diffs found'))
1728 raise util.Abort(_('no diffs found'))
1729
1729
1730 try:
1730 try:
1731 cmdline_message = cmdutil.logmessage(opts)
1731 cmdline_message = cmdutil.logmessage(opts)
1732 if cmdline_message:
1732 if cmdline_message:
1733 # pickup the cmdline msg
1733 # pickup the cmdline msg
1734 message = cmdline_message
1734 message = cmdline_message
1735 elif message:
1735 elif message:
1736 # pickup the patch msg
1736 # pickup the patch msg
1737 message = message.strip()
1737 message = message.strip()
1738 else:
1738 else:
1739 # launch the editor
1739 # launch the editor
1740 message = None
1740 message = None
1741 ui.debug(_('message:\n%s\n') % message)
1741 ui.debug(_('message:\n%s\n') % message)
1742
1742
1743 wp = repo.parents()
1743 wp = repo.parents()
1744 if opts.get('exact'):
1744 if opts.get('exact'):
1745 if not nodeid or not p1:
1745 if not nodeid or not p1:
1746 raise util.Abort(_('not a Mercurial patch'))
1746 raise util.Abort(_('not a Mercurial patch'))
1747 p1 = repo.lookup(p1)
1747 p1 = repo.lookup(p1)
1748 p2 = repo.lookup(p2 or hex(nullid))
1748 p2 = repo.lookup(p2 or hex(nullid))
1749
1749
1750 if p1 != wp[0].node():
1750 if p1 != wp[0].node():
1751 hg.clean(repo, p1)
1751 hg.clean(repo, p1)
1752 repo.dirstate.setparents(p1, p2)
1752 repo.dirstate.setparents(p1, p2)
1753 elif p2:
1753 elif p2:
1754 try:
1754 try:
1755 p1 = repo.lookup(p1)
1755 p1 = repo.lookup(p1)
1756 p2 = repo.lookup(p2)
1756 p2 = repo.lookup(p2)
1757 if p1 == wp[0].node():
1757 if p1 == wp[0].node():
1758 repo.dirstate.setparents(p1, p2)
1758 repo.dirstate.setparents(p1, p2)
1759 except error.RepoError:
1759 except error.RepoError:
1760 pass
1760 pass
1761 if opts.get('exact') or opts.get('import_branch'):
1761 if opts.get('exact') or opts.get('import_branch'):
1762 repo.dirstate.setbranch(branch or 'default')
1762 repo.dirstate.setbranch(branch or 'default')
1763
1763
1764 files = {}
1764 files = {}
1765 try:
1765 try:
1766 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1766 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1767 files=files)
1767 files=files, eolmode=None)
1768 finally:
1768 finally:
1769 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1769 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1770 if not opts.get('no_commit'):
1770 if not opts.get('no_commit'):
1771 m = cmdutil.matchfiles(repo, files or [])
1771 m = cmdutil.matchfiles(repo, files or [])
1772 n = repo.commit(message, opts.get('user') or user,
1772 n = repo.commit(message, opts.get('user') or user,
1773 opts.get('date') or date, match=m,
1773 opts.get('date') or date, match=m,
1774 editor=cmdutil.commiteditor)
1774 editor=cmdutil.commiteditor)
1775 if opts.get('exact'):
1775 if opts.get('exact'):
1776 if hex(n) != nodeid:
1776 if hex(n) != nodeid:
1777 repo.rollback()
1777 repo.rollback()
1778 raise util.Abort(_('patch is damaged'
1778 raise util.Abort(_('patch is damaged'
1779 ' or loses information'))
1779 ' or loses information'))
1780 # Force a dirstate write so that the next transaction
1780 # Force a dirstate write so that the next transaction
1781 # backups an up-do-date file.
1781 # backups an up-do-date file.
1782 repo.dirstate.write()
1782 repo.dirstate.write()
1783 finally:
1783 finally:
1784 os.unlink(tmpname)
1784 os.unlink(tmpname)
1785 finally:
1785 finally:
1786 release(lock, wlock)
1786 release(lock, wlock)
1787
1787
1788 def incoming(ui, repo, source="default", **opts):
1788 def incoming(ui, repo, source="default", **opts):
1789 """show new changesets found in source
1789 """show new changesets found in source
1790
1790
1791 Show new changesets found in the specified path/URL or the default
1791 Show new changesets found in the specified path/URL or the default
1792 pull location. These are the changesets that would have been pulled
1792 pull location. These are the changesets that would have been pulled
1793 if a pull at the time you issued this command.
1793 if a pull at the time you issued this command.
1794
1794
1795 For remote repository, using --bundle avoids downloading the
1795 For remote repository, using --bundle avoids downloading the
1796 changesets twice if the incoming is followed by a pull.
1796 changesets twice if the incoming is followed by a pull.
1797
1797
1798 See pull for valid source format details.
1798 See pull for valid source format details.
1799 """
1799 """
1800 limit = cmdutil.loglimit(opts)
1800 limit = cmdutil.loglimit(opts)
1801 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1801 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1802 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1802 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1803 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1803 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1804 if revs:
1804 if revs:
1805 revs = [other.lookup(rev) for rev in revs]
1805 revs = [other.lookup(rev) for rev in revs]
1806 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1806 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1807 force=opts["force"])
1807 force=opts["force"])
1808 if not incoming:
1808 if not incoming:
1809 try:
1809 try:
1810 os.unlink(opts["bundle"])
1810 os.unlink(opts["bundle"])
1811 except:
1811 except:
1812 pass
1812 pass
1813 ui.status(_("no changes found\n"))
1813 ui.status(_("no changes found\n"))
1814 return 1
1814 return 1
1815
1815
1816 cleanup = None
1816 cleanup = None
1817 try:
1817 try:
1818 fname = opts["bundle"]
1818 fname = opts["bundle"]
1819 if fname or not other.local():
1819 if fname or not other.local():
1820 # create a bundle (uncompressed if other repo is not local)
1820 # create a bundle (uncompressed if other repo is not local)
1821
1821
1822 if revs is None and other.capable('changegroupsubset'):
1822 if revs is None and other.capable('changegroupsubset'):
1823 revs = rheads
1823 revs = rheads
1824
1824
1825 if revs is None:
1825 if revs is None:
1826 cg = other.changegroup(incoming, "incoming")
1826 cg = other.changegroup(incoming, "incoming")
1827 else:
1827 else:
1828 cg = other.changegroupsubset(incoming, revs, 'incoming')
1828 cg = other.changegroupsubset(incoming, revs, 'incoming')
1829 bundletype = other.local() and "HG10BZ" or "HG10UN"
1829 bundletype = other.local() and "HG10BZ" or "HG10UN"
1830 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1830 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1831 # keep written bundle?
1831 # keep written bundle?
1832 if opts["bundle"]:
1832 if opts["bundle"]:
1833 cleanup = None
1833 cleanup = None
1834 if not other.local():
1834 if not other.local():
1835 # use the created uncompressed bundlerepo
1835 # use the created uncompressed bundlerepo
1836 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1836 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1837
1837
1838 o = other.changelog.nodesbetween(incoming, revs)[0]
1838 o = other.changelog.nodesbetween(incoming, revs)[0]
1839 if opts.get('newest_first'):
1839 if opts.get('newest_first'):
1840 o.reverse()
1840 o.reverse()
1841 displayer = cmdutil.show_changeset(ui, other, opts)
1841 displayer = cmdutil.show_changeset(ui, other, opts)
1842 count = 0
1842 count = 0
1843 for n in o:
1843 for n in o:
1844 if count >= limit:
1844 if count >= limit:
1845 break
1845 break
1846 parents = [p for p in other.changelog.parents(n) if p != nullid]
1846 parents = [p for p in other.changelog.parents(n) if p != nullid]
1847 if opts.get('no_merges') and len(parents) == 2:
1847 if opts.get('no_merges') and len(parents) == 2:
1848 continue
1848 continue
1849 count += 1
1849 count += 1
1850 displayer.show(other[n])
1850 displayer.show(other[n])
1851 finally:
1851 finally:
1852 if hasattr(other, 'close'):
1852 if hasattr(other, 'close'):
1853 other.close()
1853 other.close()
1854 if cleanup:
1854 if cleanup:
1855 os.unlink(cleanup)
1855 os.unlink(cleanup)
1856
1856
1857 def init(ui, dest=".", **opts):
1857 def init(ui, dest=".", **opts):
1858 """create a new repository in the given directory
1858 """create a new repository in the given directory
1859
1859
1860 Initialize a new repository in the given directory. If the given
1860 Initialize a new repository in the given directory. If the given
1861 directory does not exist, it will be created.
1861 directory does not exist, it will be created.
1862
1862
1863 If no directory is given, the current directory is used.
1863 If no directory is given, the current directory is used.
1864
1864
1865 It is possible to specify an ssh:// URL as the destination.
1865 It is possible to specify an ssh:// URL as the destination.
1866 See 'hg help urls' for more information.
1866 See 'hg help urls' for more information.
1867 """
1867 """
1868 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1868 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1869
1869
1870 def locate(ui, repo, *pats, **opts):
1870 def locate(ui, repo, *pats, **opts):
1871 """locate files matching specific patterns
1871 """locate files matching specific patterns
1872
1872
1873 Print files under Mercurial control in the working directory whose
1873 Print files under Mercurial control in the working directory whose
1874 names match the given patterns.
1874 names match the given patterns.
1875
1875
1876 By default, this command searches all directories in the working
1876 By default, this command searches all directories in the working
1877 directory. To search just the current directory and its
1877 directory. To search just the current directory and its
1878 subdirectories, use "--include .".
1878 subdirectories, use "--include .".
1879
1879
1880 If no patterns are given to match, this command prints the names
1880 If no patterns are given to match, this command prints the names
1881 of all files under Mercurial control in the working directory.
1881 of all files under Mercurial control in the working directory.
1882
1882
1883 If you want to feed the output of this command into the "xargs"
1883 If you want to feed the output of this command into the "xargs"
1884 command, use the -0 option to both this command and "xargs". This
1884 command, use the -0 option to both this command and "xargs". This
1885 will avoid the problem of "xargs" treating single filenames that
1885 will avoid the problem of "xargs" treating single filenames that
1886 contain whitespace as multiple filenames.
1886 contain whitespace as multiple filenames.
1887 """
1887 """
1888 end = opts.get('print0') and '\0' or '\n'
1888 end = opts.get('print0') and '\0' or '\n'
1889 rev = opts.get('rev') or None
1889 rev = opts.get('rev') or None
1890
1890
1891 ret = 1
1891 ret = 1
1892 m = cmdutil.match(repo, pats, opts, default='relglob')
1892 m = cmdutil.match(repo, pats, opts, default='relglob')
1893 m.bad = lambda x,y: False
1893 m.bad = lambda x,y: False
1894 for abs in repo[rev].walk(m):
1894 for abs in repo[rev].walk(m):
1895 if not rev and abs not in repo.dirstate:
1895 if not rev and abs not in repo.dirstate:
1896 continue
1896 continue
1897 if opts.get('fullpath'):
1897 if opts.get('fullpath'):
1898 ui.write(repo.wjoin(abs), end)
1898 ui.write(repo.wjoin(abs), end)
1899 else:
1899 else:
1900 ui.write(((pats and m.rel(abs)) or abs), end)
1900 ui.write(((pats and m.rel(abs)) or abs), end)
1901 ret = 0
1901 ret = 0
1902
1902
1903 return ret
1903 return ret
1904
1904
1905 def log(ui, repo, *pats, **opts):
1905 def log(ui, repo, *pats, **opts):
1906 """show revision history of entire repository or files
1906 """show revision history of entire repository or files
1907
1907
1908 Print the revision history of the specified files or the entire
1908 Print the revision history of the specified files or the entire
1909 project.
1909 project.
1910
1910
1911 File history is shown without following rename or copy history of
1911 File history is shown without following rename or copy history of
1912 files. Use -f/--follow with a filename to follow history across
1912 files. Use -f/--follow with a filename to follow history across
1913 renames and copies. --follow without a filename will only show
1913 renames and copies. --follow without a filename will only show
1914 ancestors or descendants of the starting revision. --follow-first
1914 ancestors or descendants of the starting revision. --follow-first
1915 only follows the first parent of merge revisions.
1915 only follows the first parent of merge revisions.
1916
1916
1917 If no revision range is specified, the default is tip:0 unless
1917 If no revision range is specified, the default is tip:0 unless
1918 --follow is set, in which case the working directory parent is
1918 --follow is set, in which case the working directory parent is
1919 used as the starting revision.
1919 used as the starting revision.
1920
1920
1921 See 'hg help dates' for a list of formats valid for -d/--date.
1921 See 'hg help dates' for a list of formats valid for -d/--date.
1922
1922
1923 By default this command prints revision number and changeset id,
1923 By default this command prints revision number and changeset id,
1924 tags, non-trivial parents, user, date and time, and a summary for
1924 tags, non-trivial parents, user, date and time, and a summary for
1925 each commit. When the -v/--verbose switch is used, the list of
1925 each commit. When the -v/--verbose switch is used, the list of
1926 changed files and full commit message are shown.
1926 changed files and full commit message are shown.
1927
1927
1928 NOTE: log -p/--patch may generate unexpected diff output for merge
1928 NOTE: log -p/--patch may generate unexpected diff output for merge
1929 changesets, as it will only compare the merge changeset against
1929 changesets, as it will only compare the merge changeset against
1930 its first parent. Also, only files different from BOTH parents
1930 its first parent. Also, only files different from BOTH parents
1931 will appear in files:.
1931 will appear in files:.
1932 """
1932 """
1933
1933
1934 get = util.cachefunc(lambda r: repo[r].changeset())
1934 get = util.cachefunc(lambda r: repo[r].changeset())
1935 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1935 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1936
1936
1937 limit = cmdutil.loglimit(opts)
1937 limit = cmdutil.loglimit(opts)
1938 count = 0
1938 count = 0
1939
1939
1940 if opts.get('copies') and opts.get('rev'):
1940 if opts.get('copies') and opts.get('rev'):
1941 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1941 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1942 else:
1942 else:
1943 endrev = len(repo)
1943 endrev = len(repo)
1944 rcache = {}
1944 rcache = {}
1945 ncache = {}
1945 ncache = {}
1946 def getrenamed(fn, rev):
1946 def getrenamed(fn, rev):
1947 '''looks up all renames for a file (up to endrev) the first
1947 '''looks up all renames for a file (up to endrev) the first
1948 time the file is given. It indexes on the changerev and only
1948 time the file is given. It indexes on the changerev and only
1949 parses the manifest if linkrev != changerev.
1949 parses the manifest if linkrev != changerev.
1950 Returns rename info for fn at changerev rev.'''
1950 Returns rename info for fn at changerev rev.'''
1951 if fn not in rcache:
1951 if fn not in rcache:
1952 rcache[fn] = {}
1952 rcache[fn] = {}
1953 ncache[fn] = {}
1953 ncache[fn] = {}
1954 fl = repo.file(fn)
1954 fl = repo.file(fn)
1955 for i in fl:
1955 for i in fl:
1956 node = fl.node(i)
1956 node = fl.node(i)
1957 lr = fl.linkrev(i)
1957 lr = fl.linkrev(i)
1958 renamed = fl.renamed(node)
1958 renamed = fl.renamed(node)
1959 rcache[fn][lr] = renamed
1959 rcache[fn][lr] = renamed
1960 if renamed:
1960 if renamed:
1961 ncache[fn][node] = renamed
1961 ncache[fn][node] = renamed
1962 if lr >= endrev:
1962 if lr >= endrev:
1963 break
1963 break
1964 if rev in rcache[fn]:
1964 if rev in rcache[fn]:
1965 return rcache[fn][rev]
1965 return rcache[fn][rev]
1966
1966
1967 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1967 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1968 # filectx logic.
1968 # filectx logic.
1969
1969
1970 try:
1970 try:
1971 return repo[rev][fn].renamed()
1971 return repo[rev][fn].renamed()
1972 except error.LookupError:
1972 except error.LookupError:
1973 pass
1973 pass
1974 return None
1974 return None
1975
1975
1976 df = False
1976 df = False
1977 if opts["date"]:
1977 if opts["date"]:
1978 df = util.matchdate(opts["date"])
1978 df = util.matchdate(opts["date"])
1979
1979
1980 only_branches = opts.get('only_branch')
1980 only_branches = opts.get('only_branch')
1981
1981
1982 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1982 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1983 for st, rev, fns in changeiter:
1983 for st, rev, fns in changeiter:
1984 if st == 'add':
1984 if st == 'add':
1985 parents = [p for p in repo.changelog.parentrevs(rev)
1985 parents = [p for p in repo.changelog.parentrevs(rev)
1986 if p != nullrev]
1986 if p != nullrev]
1987 if opts.get('no_merges') and len(parents) == 2:
1987 if opts.get('no_merges') and len(parents) == 2:
1988 continue
1988 continue
1989 if opts.get('only_merges') and len(parents) != 2:
1989 if opts.get('only_merges') and len(parents) != 2:
1990 continue
1990 continue
1991
1991
1992 if only_branches:
1992 if only_branches:
1993 revbranch = get(rev)[5]['branch']
1993 revbranch = get(rev)[5]['branch']
1994 if revbranch not in only_branches:
1994 if revbranch not in only_branches:
1995 continue
1995 continue
1996
1996
1997 if df:
1997 if df:
1998 changes = get(rev)
1998 changes = get(rev)
1999 if not df(changes[2][0]):
1999 if not df(changes[2][0]):
2000 continue
2000 continue
2001
2001
2002 if opts.get('keyword'):
2002 if opts.get('keyword'):
2003 changes = get(rev)
2003 changes = get(rev)
2004 miss = 0
2004 miss = 0
2005 for k in [kw.lower() for kw in opts['keyword']]:
2005 for k in [kw.lower() for kw in opts['keyword']]:
2006 if not (k in changes[1].lower() or
2006 if not (k in changes[1].lower() or
2007 k in changes[4].lower() or
2007 k in changes[4].lower() or
2008 k in " ".join(changes[3]).lower()):
2008 k in " ".join(changes[3]).lower()):
2009 miss = 1
2009 miss = 1
2010 break
2010 break
2011 if miss:
2011 if miss:
2012 continue
2012 continue
2013
2013
2014 if opts['user']:
2014 if opts['user']:
2015 changes = get(rev)
2015 changes = get(rev)
2016 if not [k for k in opts['user'] if k in changes[1]]:
2016 if not [k for k in opts['user'] if k in changes[1]]:
2017 continue
2017 continue
2018
2018
2019 copies = []
2019 copies = []
2020 if opts.get('copies') and rev:
2020 if opts.get('copies') and rev:
2021 for fn in get(rev)[3]:
2021 for fn in get(rev)[3]:
2022 rename = getrenamed(fn, rev)
2022 rename = getrenamed(fn, rev)
2023 if rename:
2023 if rename:
2024 copies.append((fn, rename[0]))
2024 copies.append((fn, rename[0]))
2025 displayer.show(context.changectx(repo, rev), copies=copies)
2025 displayer.show(context.changectx(repo, rev), copies=copies)
2026 elif st == 'iter':
2026 elif st == 'iter':
2027 if count == limit: break
2027 if count == limit: break
2028 if displayer.flush(rev):
2028 if displayer.flush(rev):
2029 count += 1
2029 count += 1
2030
2030
2031 def manifest(ui, repo, node=None, rev=None):
2031 def manifest(ui, repo, node=None, rev=None):
2032 """output the current or given revision of the project manifest
2032 """output the current or given revision of the project manifest
2033
2033
2034 Print a list of version controlled files for the given revision.
2034 Print a list of version controlled files for the given revision.
2035 If no revision is given, the first parent of the working directory
2035 If no revision is given, the first parent of the working directory
2036 is used, or the null revision if no revision is checked out.
2036 is used, or the null revision if no revision is checked out.
2037
2037
2038 With -v, print file permissions, symlink and executable bits.
2038 With -v, print file permissions, symlink and executable bits.
2039 With --debug, print file revision hashes.
2039 With --debug, print file revision hashes.
2040 """
2040 """
2041
2041
2042 if rev and node:
2042 if rev and node:
2043 raise util.Abort(_("please specify just one revision"))
2043 raise util.Abort(_("please specify just one revision"))
2044
2044
2045 if not node:
2045 if not node:
2046 node = rev
2046 node = rev
2047
2047
2048 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2048 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2049 ctx = repo[node]
2049 ctx = repo[node]
2050 for f in ctx:
2050 for f in ctx:
2051 if ui.debugflag:
2051 if ui.debugflag:
2052 ui.write("%40s " % hex(ctx.manifest()[f]))
2052 ui.write("%40s " % hex(ctx.manifest()[f]))
2053 if ui.verbose:
2053 if ui.verbose:
2054 ui.write(decor[ctx.flags(f)])
2054 ui.write(decor[ctx.flags(f)])
2055 ui.write("%s\n" % f)
2055 ui.write("%s\n" % f)
2056
2056
2057 def merge(ui, repo, node=None, **opts):
2057 def merge(ui, repo, node=None, **opts):
2058 """merge working directory with another revision
2058 """merge working directory with another revision
2059
2059
2060 The current working directory is updated with all changes made in
2060 The current working directory is updated with all changes made in
2061 the requested revision since the last common predecessor revision.
2061 the requested revision since the last common predecessor revision.
2062
2062
2063 Files that changed between either parent are marked as changed for
2063 Files that changed between either parent are marked as changed for
2064 the next commit and a commit must be performed before any further
2064 the next commit and a commit must be performed before any further
2065 updates to the repository are allowed. The next commit will have
2065 updates to the repository are allowed. The next commit will have
2066 two parents.
2066 two parents.
2067
2067
2068 If no revision is specified, the working directory's parent is a
2068 If no revision is specified, the working directory's parent is a
2069 head revision, and the current branch contains exactly one other
2069 head revision, and the current branch contains exactly one other
2070 head, the other head is merged with by default. Otherwise, an
2070 head, the other head is merged with by default. Otherwise, an
2071 explicit revision with which to merge with must be provided.
2071 explicit revision with which to merge with must be provided.
2072 """
2072 """
2073
2073
2074 if opts.get('rev') and node:
2074 if opts.get('rev') and node:
2075 raise util.Abort(_("please specify just one revision"))
2075 raise util.Abort(_("please specify just one revision"))
2076 if not node:
2076 if not node:
2077 node = opts.get('rev')
2077 node = opts.get('rev')
2078
2078
2079 if not node:
2079 if not node:
2080 branch = repo.changectx(None).branch()
2080 branch = repo.changectx(None).branch()
2081 bheads = repo.branchheads(branch)
2081 bheads = repo.branchheads(branch)
2082 if len(bheads) > 2:
2082 if len(bheads) > 2:
2083 raise util.Abort(_("branch '%s' has %d heads - "
2083 raise util.Abort(_("branch '%s' has %d heads - "
2084 "please merge with an explicit rev") %
2084 "please merge with an explicit rev") %
2085 (branch, len(bheads)))
2085 (branch, len(bheads)))
2086
2086
2087 parent = repo.dirstate.parents()[0]
2087 parent = repo.dirstate.parents()[0]
2088 if len(bheads) == 1:
2088 if len(bheads) == 1:
2089 if len(repo.heads()) > 1:
2089 if len(repo.heads()) > 1:
2090 raise util.Abort(_("branch '%s' has one head - "
2090 raise util.Abort(_("branch '%s' has one head - "
2091 "please merge with an explicit rev") %
2091 "please merge with an explicit rev") %
2092 branch)
2092 branch)
2093 msg = _('there is nothing to merge')
2093 msg = _('there is nothing to merge')
2094 if parent != repo.lookup(repo[None].branch()):
2094 if parent != repo.lookup(repo[None].branch()):
2095 msg = _('%s - use "hg update" instead') % msg
2095 msg = _('%s - use "hg update" instead') % msg
2096 raise util.Abort(msg)
2096 raise util.Abort(msg)
2097
2097
2098 if parent not in bheads:
2098 if parent not in bheads:
2099 raise util.Abort(_('working dir not at a head rev - '
2099 raise util.Abort(_('working dir not at a head rev - '
2100 'use "hg update" or merge with an explicit rev'))
2100 'use "hg update" or merge with an explicit rev'))
2101 node = parent == bheads[0] and bheads[-1] or bheads[0]
2101 node = parent == bheads[0] and bheads[-1] or bheads[0]
2102
2102
2103 if opts.get('show'):
2103 if opts.get('show'):
2104 p1 = repo['.']
2104 p1 = repo['.']
2105 p2 = repo[node]
2105 p2 = repo[node]
2106 common = p1.ancestor(p2)
2106 common = p1.ancestor(p2)
2107 roots, heads = [common.node()], [p2.node()]
2107 roots, heads = [common.node()], [p2.node()]
2108 displayer = cmdutil.show_changeset(ui, repo, opts)
2108 displayer = cmdutil.show_changeset(ui, repo, opts)
2109 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2109 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2110 displayer.show(repo[node])
2110 displayer.show(repo[node])
2111 return 0
2111 return 0
2112
2112
2113 return hg.merge(repo, node, force=opts.get('force'))
2113 return hg.merge(repo, node, force=opts.get('force'))
2114
2114
2115 def outgoing(ui, repo, dest=None, **opts):
2115 def outgoing(ui, repo, dest=None, **opts):
2116 """show changesets not found in destination
2116 """show changesets not found in destination
2117
2117
2118 Show changesets not found in the specified destination repository
2118 Show changesets not found in the specified destination repository
2119 or the default push location. These are the changesets that would
2119 or the default push location. These are the changesets that would
2120 be pushed if a push was requested.
2120 be pushed if a push was requested.
2121
2121
2122 See pull for valid destination format details.
2122 See pull for valid destination format details.
2123 """
2123 """
2124 limit = cmdutil.loglimit(opts)
2124 limit = cmdutil.loglimit(opts)
2125 dest, revs, checkout = hg.parseurl(
2125 dest, revs, checkout = hg.parseurl(
2126 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2126 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2127 if revs:
2127 if revs:
2128 revs = [repo.lookup(rev) for rev in revs]
2128 revs = [repo.lookup(rev) for rev in revs]
2129
2129
2130 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2130 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2131 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2131 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2132 o = repo.findoutgoing(other, force=opts.get('force'))
2132 o = repo.findoutgoing(other, force=opts.get('force'))
2133 if not o:
2133 if not o:
2134 ui.status(_("no changes found\n"))
2134 ui.status(_("no changes found\n"))
2135 return 1
2135 return 1
2136 o = repo.changelog.nodesbetween(o, revs)[0]
2136 o = repo.changelog.nodesbetween(o, revs)[0]
2137 if opts.get('newest_first'):
2137 if opts.get('newest_first'):
2138 o.reverse()
2138 o.reverse()
2139 displayer = cmdutil.show_changeset(ui, repo, opts)
2139 displayer = cmdutil.show_changeset(ui, repo, opts)
2140 count = 0
2140 count = 0
2141 for n in o:
2141 for n in o:
2142 if count >= limit:
2142 if count >= limit:
2143 break
2143 break
2144 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2144 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2145 if opts.get('no_merges') and len(parents) == 2:
2145 if opts.get('no_merges') and len(parents) == 2:
2146 continue
2146 continue
2147 count += 1
2147 count += 1
2148 displayer.show(repo[n])
2148 displayer.show(repo[n])
2149
2149
2150 def parents(ui, repo, file_=None, **opts):
2150 def parents(ui, repo, file_=None, **opts):
2151 """show the parents of the working directory or revision
2151 """show the parents of the working directory or revision
2152
2152
2153 Print the working directory's parent revisions. If a revision is
2153 Print the working directory's parent revisions. If a revision is
2154 given via -r/--rev, the parent of that revision will be printed.
2154 given via -r/--rev, the parent of that revision will be printed.
2155 If a file argument is given, the revision in which the file was
2155 If a file argument is given, the revision in which the file was
2156 last changed (before the working directory revision or the
2156 last changed (before the working directory revision or the
2157 argument to --rev if given) is printed.
2157 argument to --rev if given) is printed.
2158 """
2158 """
2159 rev = opts.get('rev')
2159 rev = opts.get('rev')
2160 if rev:
2160 if rev:
2161 ctx = repo[rev]
2161 ctx = repo[rev]
2162 else:
2162 else:
2163 ctx = repo[None]
2163 ctx = repo[None]
2164
2164
2165 if file_:
2165 if file_:
2166 m = cmdutil.match(repo, (file_,), opts)
2166 m = cmdutil.match(repo, (file_,), opts)
2167 if m.anypats() or len(m.files()) != 1:
2167 if m.anypats() or len(m.files()) != 1:
2168 raise util.Abort(_('can only specify an explicit filename'))
2168 raise util.Abort(_('can only specify an explicit filename'))
2169 file_ = m.files()[0]
2169 file_ = m.files()[0]
2170 filenodes = []
2170 filenodes = []
2171 for cp in ctx.parents():
2171 for cp in ctx.parents():
2172 if not cp:
2172 if not cp:
2173 continue
2173 continue
2174 try:
2174 try:
2175 filenodes.append(cp.filenode(file_))
2175 filenodes.append(cp.filenode(file_))
2176 except error.LookupError:
2176 except error.LookupError:
2177 pass
2177 pass
2178 if not filenodes:
2178 if not filenodes:
2179 raise util.Abort(_("'%s' not found in manifest!") % file_)
2179 raise util.Abort(_("'%s' not found in manifest!") % file_)
2180 fl = repo.file(file_)
2180 fl = repo.file(file_)
2181 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2181 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2182 else:
2182 else:
2183 p = [cp.node() for cp in ctx.parents()]
2183 p = [cp.node() for cp in ctx.parents()]
2184
2184
2185 displayer = cmdutil.show_changeset(ui, repo, opts)
2185 displayer = cmdutil.show_changeset(ui, repo, opts)
2186 for n in p:
2186 for n in p:
2187 if n != nullid:
2187 if n != nullid:
2188 displayer.show(repo[n])
2188 displayer.show(repo[n])
2189
2189
2190 def paths(ui, repo, search=None):
2190 def paths(ui, repo, search=None):
2191 """show aliases for remote repositories
2191 """show aliases for remote repositories
2192
2192
2193 Show definition of symbolic path name NAME. If no name is given,
2193 Show definition of symbolic path name NAME. If no name is given,
2194 show definition of all available names.
2194 show definition of all available names.
2195
2195
2196 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2196 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2197 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2197 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2198
2198
2199 See 'hg help urls' for more information.
2199 See 'hg help urls' for more information.
2200 """
2200 """
2201 if search:
2201 if search:
2202 for name, path in ui.configitems("paths"):
2202 for name, path in ui.configitems("paths"):
2203 if name == search:
2203 if name == search:
2204 ui.write("%s\n" % url.hidepassword(path))
2204 ui.write("%s\n" % url.hidepassword(path))
2205 return
2205 return
2206 ui.warn(_("not found!\n"))
2206 ui.warn(_("not found!\n"))
2207 return 1
2207 return 1
2208 else:
2208 else:
2209 for name, path in ui.configitems("paths"):
2209 for name, path in ui.configitems("paths"):
2210 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2210 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2211
2211
2212 def postincoming(ui, repo, modheads, optupdate, checkout):
2212 def postincoming(ui, repo, modheads, optupdate, checkout):
2213 if modheads == 0:
2213 if modheads == 0:
2214 return
2214 return
2215 if optupdate:
2215 if optupdate:
2216 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2216 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2217 return hg.update(repo, checkout)
2217 return hg.update(repo, checkout)
2218 else:
2218 else:
2219 ui.status(_("not updating, since new heads added\n"))
2219 ui.status(_("not updating, since new heads added\n"))
2220 if modheads > 1:
2220 if modheads > 1:
2221 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2221 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2222 else:
2222 else:
2223 ui.status(_("(run 'hg update' to get a working copy)\n"))
2223 ui.status(_("(run 'hg update' to get a working copy)\n"))
2224
2224
2225 def pull(ui, repo, source="default", **opts):
2225 def pull(ui, repo, source="default", **opts):
2226 """pull changes from the specified source
2226 """pull changes from the specified source
2227
2227
2228 Pull changes from a remote repository to a local one.
2228 Pull changes from a remote repository to a local one.
2229
2229
2230 This finds all changes from the repository at the specified path
2230 This finds all changes from the repository at the specified path
2231 or URL and adds them to a local repository (the current one unless
2231 or URL and adds them to a local repository (the current one unless
2232 -R is specified). By default, this does not update the copy of the
2232 -R is specified). By default, this does not update the copy of the
2233 project in the working directory.
2233 project in the working directory.
2234
2234
2235 Use hg incoming if you want to see what would have been added by a
2235 Use hg incoming if you want to see what would have been added by a
2236 pull at the time you issued this command. If you then decide to
2236 pull at the time you issued this command. If you then decide to
2237 added those changes to the repository, you should use pull -r X
2237 added those changes to the repository, you should use pull -r X
2238 where X is the last changeset listed by hg incoming.
2238 where X is the last changeset listed by hg incoming.
2239
2239
2240 If SOURCE is omitted, the 'default' path will be used.
2240 If SOURCE is omitted, the 'default' path will be used.
2241 See 'hg help urls' for more information.
2241 See 'hg help urls' for more information.
2242 """
2242 """
2243 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2243 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2244 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2244 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2245 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2245 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2246 if revs:
2246 if revs:
2247 try:
2247 try:
2248 revs = [other.lookup(rev) for rev in revs]
2248 revs = [other.lookup(rev) for rev in revs]
2249 except error.CapabilityError:
2249 except error.CapabilityError:
2250 err = _("Other repository doesn't support revision lookup, "
2250 err = _("Other repository doesn't support revision lookup, "
2251 "so a rev cannot be specified.")
2251 "so a rev cannot be specified.")
2252 raise util.Abort(err)
2252 raise util.Abort(err)
2253
2253
2254 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2254 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2255 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2255 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2256
2256
2257 def push(ui, repo, dest=None, **opts):
2257 def push(ui, repo, dest=None, **opts):
2258 """push changes to the specified destination
2258 """push changes to the specified destination
2259
2259
2260 Push changes from the local repository to the given destination.
2260 Push changes from the local repository to the given destination.
2261
2261
2262 This is the symmetrical operation for pull. It moves changes from
2262 This is the symmetrical operation for pull. It moves changes from
2263 the current repository to a different one. If the destination is
2263 the current repository to a different one. If the destination is
2264 local this is identical to a pull in that directory from the
2264 local this is identical to a pull in that directory from the
2265 current one.
2265 current one.
2266
2266
2267 By default, push will refuse to run if it detects the result would
2267 By default, push will refuse to run if it detects the result would
2268 increase the number of remote heads. This generally indicates the
2268 increase the number of remote heads. This generally indicates the
2269 user forgot to pull and merge before pushing.
2269 user forgot to pull and merge before pushing.
2270
2270
2271 If -r/--rev is used, the named revision and all its ancestors will
2271 If -r/--rev is used, the named revision and all its ancestors will
2272 be pushed to the remote repository.
2272 be pushed to the remote repository.
2273
2273
2274 Please see 'hg help urls' for important details about ssh://
2274 Please see 'hg help urls' for important details about ssh://
2275 URLs. If DESTINATION is omitted, a default path will be used.
2275 URLs. If DESTINATION is omitted, a default path will be used.
2276 See 'hg help urls' for more information.
2276 See 'hg help urls' for more information.
2277 """
2277 """
2278 dest, revs, checkout = hg.parseurl(
2278 dest, revs, checkout = hg.parseurl(
2279 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2279 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2280 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2280 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2281 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2281 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2282 if revs:
2282 if revs:
2283 revs = [repo.lookup(rev) for rev in revs]
2283 revs = [repo.lookup(rev) for rev in revs]
2284 r = repo.push(other, opts.get('force'), revs=revs)
2284 r = repo.push(other, opts.get('force'), revs=revs)
2285 return r == 0
2285 return r == 0
2286
2286
2287 def recover(ui, repo):
2287 def recover(ui, repo):
2288 """roll back an interrupted transaction
2288 """roll back an interrupted transaction
2289
2289
2290 Recover from an interrupted commit or pull.
2290 Recover from an interrupted commit or pull.
2291
2291
2292 This command tries to fix the repository status after an
2292 This command tries to fix the repository status after an
2293 interrupted operation. It should only be necessary when Mercurial
2293 interrupted operation. It should only be necessary when Mercurial
2294 suggests it.
2294 suggests it.
2295 """
2295 """
2296 if repo.recover():
2296 if repo.recover():
2297 return hg.verify(repo)
2297 return hg.verify(repo)
2298 return 1
2298 return 1
2299
2299
2300 def remove(ui, repo, *pats, **opts):
2300 def remove(ui, repo, *pats, **opts):
2301 """remove the specified files on the next commit
2301 """remove the specified files on the next commit
2302
2302
2303 Schedule the indicated files for removal from the repository.
2303 Schedule the indicated files for removal from the repository.
2304
2304
2305 This only removes files from the current branch, not from the
2305 This only removes files from the current branch, not from the
2306 entire project history. -A/--after can be used to remove only
2306 entire project history. -A/--after can be used to remove only
2307 files that have already been deleted, -f/--force can be used to
2307 files that have already been deleted, -f/--force can be used to
2308 force deletion, and -Af can be used to remove files from the next
2308 force deletion, and -Af can be used to remove files from the next
2309 revision without deleting them from the working directory.
2309 revision without deleting them from the working directory.
2310
2310
2311 The following table details the behavior of remove for different
2311 The following table details the behavior of remove for different
2312 file states (columns) and option combinations (rows). The file
2312 file states (columns) and option combinations (rows). The file
2313 states are Added [A], Clean [C], Modified [M] and Missing [!]
2313 states are Added [A], Clean [C], Modified [M] and Missing [!]
2314 (as reported by hg status). The actions are Warn, Remove (from
2314 (as reported by hg status). The actions are Warn, Remove (from
2315 branch) and Delete (from disk).
2315 branch) and Delete (from disk).
2316
2316
2317 A C M !
2317 A C M !
2318 none W RD W R
2318 none W RD W R
2319 -f R RD RD R
2319 -f R RD RD R
2320 -A W W W R
2320 -A W W W R
2321 -Af R R R R
2321 -Af R R R R
2322
2322
2323 This command schedules the files to be removed at the next commit.
2323 This command schedules the files to be removed at the next commit.
2324 To undo a remove before that, see hg revert.
2324 To undo a remove before that, see hg revert.
2325 """
2325 """
2326
2326
2327 after, force = opts.get('after'), opts.get('force')
2327 after, force = opts.get('after'), opts.get('force')
2328 if not pats and not after:
2328 if not pats and not after:
2329 raise util.Abort(_('no files specified'))
2329 raise util.Abort(_('no files specified'))
2330
2330
2331 m = cmdutil.match(repo, pats, opts)
2331 m = cmdutil.match(repo, pats, opts)
2332 s = repo.status(match=m, clean=True)
2332 s = repo.status(match=m, clean=True)
2333 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2333 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2334
2334
2335 for f in m.files():
2335 for f in m.files():
2336 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2336 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2337 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2337 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2338
2338
2339 def warn(files, reason):
2339 def warn(files, reason):
2340 for f in files:
2340 for f in files:
2341 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2341 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2342 % (m.rel(f), reason))
2342 % (m.rel(f), reason))
2343
2343
2344 if force:
2344 if force:
2345 remove, forget = modified + deleted + clean, added
2345 remove, forget = modified + deleted + clean, added
2346 elif after:
2346 elif after:
2347 remove, forget = deleted, []
2347 remove, forget = deleted, []
2348 warn(modified + added + clean, _('still exists'))
2348 warn(modified + added + clean, _('still exists'))
2349 else:
2349 else:
2350 remove, forget = deleted + clean, []
2350 remove, forget = deleted + clean, []
2351 warn(modified, _('is modified'))
2351 warn(modified, _('is modified'))
2352 warn(added, _('has been marked for add'))
2352 warn(added, _('has been marked for add'))
2353
2353
2354 for f in sorted(remove + forget):
2354 for f in sorted(remove + forget):
2355 if ui.verbose or not m.exact(f):
2355 if ui.verbose or not m.exact(f):
2356 ui.status(_('removing %s\n') % m.rel(f))
2356 ui.status(_('removing %s\n') % m.rel(f))
2357
2357
2358 repo.forget(forget)
2358 repo.forget(forget)
2359 repo.remove(remove, unlink=not after)
2359 repo.remove(remove, unlink=not after)
2360
2360
2361 def rename(ui, repo, *pats, **opts):
2361 def rename(ui, repo, *pats, **opts):
2362 """rename files; equivalent of copy + remove
2362 """rename files; equivalent of copy + remove
2363
2363
2364 Mark dest as copies of sources; mark sources for deletion. If dest
2364 Mark dest as copies of sources; mark sources for deletion. If dest
2365 is a directory, copies are put in that directory. If dest is a
2365 is a directory, copies are put in that directory. If dest is a
2366 file, there can only be one source.
2366 file, there can only be one source.
2367
2367
2368 By default, this command copies the contents of files as they
2368 By default, this command copies the contents of files as they
2369 exist in the working directory. If invoked with -A/--after, the
2369 exist in the working directory. If invoked with -A/--after, the
2370 operation is recorded, but no copying is performed.
2370 operation is recorded, but no copying is performed.
2371
2371
2372 This command takes effect at the next commit. To undo a rename
2372 This command takes effect at the next commit. To undo a rename
2373 before that, see hg revert.
2373 before that, see hg revert.
2374 """
2374 """
2375 wlock = repo.wlock(False)
2375 wlock = repo.wlock(False)
2376 try:
2376 try:
2377 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2377 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2378 finally:
2378 finally:
2379 wlock.release()
2379 wlock.release()
2380
2380
2381 def resolve(ui, repo, *pats, **opts):
2381 def resolve(ui, repo, *pats, **opts):
2382 """retry file merges from a merge or update
2382 """retry file merges from a merge or update
2383
2383
2384 This command will cleanly retry unresolved file merges using file
2384 This command will cleanly retry unresolved file merges using file
2385 revisions preserved from the last update or merge. To attempt to
2385 revisions preserved from the last update or merge. To attempt to
2386 resolve all unresolved files, use the -a/--all switch.
2386 resolve all unresolved files, use the -a/--all switch.
2387
2387
2388 If a conflict is resolved manually, please note that the changes
2388 If a conflict is resolved manually, please note that the changes
2389 will be overwritten if the merge is retried with resolve. The
2389 will be overwritten if the merge is retried with resolve. The
2390 -m/--mark switch should be used to mark the file as resolved.
2390 -m/--mark switch should be used to mark the file as resolved.
2391
2391
2392 This command also allows listing resolved files and manually
2392 This command also allows listing resolved files and manually
2393 indicating whether or not files are resolved. All files must be
2393 indicating whether or not files are resolved. All files must be
2394 marked as resolved before a commit is permitted.
2394 marked as resolved before a commit is permitted.
2395
2395
2396 The codes used to show the status of files are:
2396 The codes used to show the status of files are:
2397 U = unresolved
2397 U = unresolved
2398 R = resolved
2398 R = resolved
2399 """
2399 """
2400
2400
2401 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2401 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2402
2402
2403 if (show and (mark or unmark)) or (mark and unmark):
2403 if (show and (mark or unmark)) or (mark and unmark):
2404 raise util.Abort(_("too many options specified"))
2404 raise util.Abort(_("too many options specified"))
2405 if pats and all:
2405 if pats and all:
2406 raise util.Abort(_("can't specify --all and patterns"))
2406 raise util.Abort(_("can't specify --all and patterns"))
2407 if not (all or pats or show or mark or unmark):
2407 if not (all or pats or show or mark or unmark):
2408 raise util.Abort(_('no files or directories specified; '
2408 raise util.Abort(_('no files or directories specified; '
2409 'use --all to remerge all files'))
2409 'use --all to remerge all files'))
2410
2410
2411 ms = merge_.mergestate(repo)
2411 ms = merge_.mergestate(repo)
2412 m = cmdutil.match(repo, pats, opts)
2412 m = cmdutil.match(repo, pats, opts)
2413
2413
2414 for f in ms:
2414 for f in ms:
2415 if m(f):
2415 if m(f):
2416 if show:
2416 if show:
2417 ui.write("%s %s\n" % (ms[f].upper(), f))
2417 ui.write("%s %s\n" % (ms[f].upper(), f))
2418 elif mark:
2418 elif mark:
2419 ms.mark(f, "r")
2419 ms.mark(f, "r")
2420 elif unmark:
2420 elif unmark:
2421 ms.mark(f, "u")
2421 ms.mark(f, "u")
2422 else:
2422 else:
2423 wctx = repo[None]
2423 wctx = repo[None]
2424 mctx = wctx.parents()[-1]
2424 mctx = wctx.parents()[-1]
2425
2425
2426 # backup pre-resolve (merge uses .orig for its own purposes)
2426 # backup pre-resolve (merge uses .orig for its own purposes)
2427 a = repo.wjoin(f)
2427 a = repo.wjoin(f)
2428 util.copyfile(a, a + ".resolve")
2428 util.copyfile(a, a + ".resolve")
2429
2429
2430 # resolve file
2430 # resolve file
2431 ms.resolve(f, wctx, mctx)
2431 ms.resolve(f, wctx, mctx)
2432
2432
2433 # replace filemerge's .orig file with our resolve file
2433 # replace filemerge's .orig file with our resolve file
2434 util.rename(a + ".resolve", a + ".orig")
2434 util.rename(a + ".resolve", a + ".orig")
2435
2435
2436 def revert(ui, repo, *pats, **opts):
2436 def revert(ui, repo, *pats, **opts):
2437 """restore individual files or directories to an earlier state
2437 """restore individual files or directories to an earlier state
2438
2438
2439 (Use update -r to check out earlier revisions, revert does not
2439 (Use update -r to check out earlier revisions, revert does not
2440 change the working directory parents.)
2440 change the working directory parents.)
2441
2441
2442 With no revision specified, revert the named files or directories
2442 With no revision specified, revert the named files or directories
2443 to the contents they had in the parent of the working directory.
2443 to the contents they had in the parent of the working directory.
2444 This restores the contents of the affected files to an unmodified
2444 This restores the contents of the affected files to an unmodified
2445 state and unschedules adds, removes, copies, and renames. If the
2445 state and unschedules adds, removes, copies, and renames. If the
2446 working directory has two parents, you must explicitly specify the
2446 working directory has two parents, you must explicitly specify the
2447 revision to revert to.
2447 revision to revert to.
2448
2448
2449 Using the -r/--rev option, revert the given files or directories
2449 Using the -r/--rev option, revert the given files or directories
2450 to their contents as of a specific revision. This can be helpful
2450 to their contents as of a specific revision. This can be helpful
2451 to "roll back" some or all of an earlier change. See 'hg help
2451 to "roll back" some or all of an earlier change. See 'hg help
2452 dates' for a list of formats valid for -d/--date.
2452 dates' for a list of formats valid for -d/--date.
2453
2453
2454 Revert modifies the working directory. It does not commit any
2454 Revert modifies the working directory. It does not commit any
2455 changes, or change the parent of the working directory. If you
2455 changes, or change the parent of the working directory. If you
2456 revert to a revision other than the parent of the working
2456 revert to a revision other than the parent of the working
2457 directory, the reverted files will thus appear modified
2457 directory, the reverted files will thus appear modified
2458 afterwards.
2458 afterwards.
2459
2459
2460 If a file has been deleted, it is restored. If the executable mode
2460 If a file has been deleted, it is restored. If the executable mode
2461 of a file was changed, it is reset.
2461 of a file was changed, it is reset.
2462
2462
2463 If names are given, all files matching the names are reverted.
2463 If names are given, all files matching the names are reverted.
2464 If no arguments are given, no files are reverted.
2464 If no arguments are given, no files are reverted.
2465
2465
2466 Modified files are saved with a .orig suffix before reverting.
2466 Modified files are saved with a .orig suffix before reverting.
2467 To disable these backups, use --no-backup.
2467 To disable these backups, use --no-backup.
2468 """
2468 """
2469
2469
2470 if opts["date"]:
2470 if opts["date"]:
2471 if opts["rev"]:
2471 if opts["rev"]:
2472 raise util.Abort(_("you can't specify a revision and a date"))
2472 raise util.Abort(_("you can't specify a revision and a date"))
2473 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2473 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2474
2474
2475 if not pats and not opts.get('all'):
2475 if not pats and not opts.get('all'):
2476 raise util.Abort(_('no files or directories specified; '
2476 raise util.Abort(_('no files or directories specified; '
2477 'use --all to revert the whole repo'))
2477 'use --all to revert the whole repo'))
2478
2478
2479 parent, p2 = repo.dirstate.parents()
2479 parent, p2 = repo.dirstate.parents()
2480 if not opts.get('rev') and p2 != nullid:
2480 if not opts.get('rev') and p2 != nullid:
2481 raise util.Abort(_('uncommitted merge - please provide a '
2481 raise util.Abort(_('uncommitted merge - please provide a '
2482 'specific revision'))
2482 'specific revision'))
2483 ctx = repo[opts.get('rev')]
2483 ctx = repo[opts.get('rev')]
2484 node = ctx.node()
2484 node = ctx.node()
2485 mf = ctx.manifest()
2485 mf = ctx.manifest()
2486 if node == parent:
2486 if node == parent:
2487 pmf = mf
2487 pmf = mf
2488 else:
2488 else:
2489 pmf = None
2489 pmf = None
2490
2490
2491 # need all matching names in dirstate and manifest of target rev,
2491 # need all matching names in dirstate and manifest of target rev,
2492 # so have to walk both. do not print errors if files exist in one
2492 # so have to walk both. do not print errors if files exist in one
2493 # but not other.
2493 # but not other.
2494
2494
2495 names = {}
2495 names = {}
2496
2496
2497 wlock = repo.wlock()
2497 wlock = repo.wlock()
2498 try:
2498 try:
2499 # walk dirstate.
2499 # walk dirstate.
2500
2500
2501 m = cmdutil.match(repo, pats, opts)
2501 m = cmdutil.match(repo, pats, opts)
2502 m.bad = lambda x,y: False
2502 m.bad = lambda x,y: False
2503 for abs in repo.walk(m):
2503 for abs in repo.walk(m):
2504 names[abs] = m.rel(abs), m.exact(abs)
2504 names[abs] = m.rel(abs), m.exact(abs)
2505
2505
2506 # walk target manifest.
2506 # walk target manifest.
2507
2507
2508 def badfn(path, msg):
2508 def badfn(path, msg):
2509 if path in names:
2509 if path in names:
2510 return
2510 return
2511 path_ = path + '/'
2511 path_ = path + '/'
2512 for f in names:
2512 for f in names:
2513 if f.startswith(path_):
2513 if f.startswith(path_):
2514 return
2514 return
2515 ui.warn("%s: %s\n" % (m.rel(path), msg))
2515 ui.warn("%s: %s\n" % (m.rel(path), msg))
2516
2516
2517 m = cmdutil.match(repo, pats, opts)
2517 m = cmdutil.match(repo, pats, opts)
2518 m.bad = badfn
2518 m.bad = badfn
2519 for abs in repo[node].walk(m):
2519 for abs in repo[node].walk(m):
2520 if abs not in names:
2520 if abs not in names:
2521 names[abs] = m.rel(abs), m.exact(abs)
2521 names[abs] = m.rel(abs), m.exact(abs)
2522
2522
2523 m = cmdutil.matchfiles(repo, names)
2523 m = cmdutil.matchfiles(repo, names)
2524 changes = repo.status(match=m)[:4]
2524 changes = repo.status(match=m)[:4]
2525 modified, added, removed, deleted = map(set, changes)
2525 modified, added, removed, deleted = map(set, changes)
2526
2526
2527 # if f is a rename, also revert the source
2527 # if f is a rename, also revert the source
2528 cwd = repo.getcwd()
2528 cwd = repo.getcwd()
2529 for f in added:
2529 for f in added:
2530 src = repo.dirstate.copied(f)
2530 src = repo.dirstate.copied(f)
2531 if src and src not in names and repo.dirstate[src] == 'r':
2531 if src and src not in names and repo.dirstate[src] == 'r':
2532 removed.add(src)
2532 removed.add(src)
2533 names[src] = (repo.pathto(src, cwd), True)
2533 names[src] = (repo.pathto(src, cwd), True)
2534
2534
2535 def removeforget(abs):
2535 def removeforget(abs):
2536 if repo.dirstate[abs] == 'a':
2536 if repo.dirstate[abs] == 'a':
2537 return _('forgetting %s\n')
2537 return _('forgetting %s\n')
2538 return _('removing %s\n')
2538 return _('removing %s\n')
2539
2539
2540 revert = ([], _('reverting %s\n'))
2540 revert = ([], _('reverting %s\n'))
2541 add = ([], _('adding %s\n'))
2541 add = ([], _('adding %s\n'))
2542 remove = ([], removeforget)
2542 remove = ([], removeforget)
2543 undelete = ([], _('undeleting %s\n'))
2543 undelete = ([], _('undeleting %s\n'))
2544
2544
2545 disptable = (
2545 disptable = (
2546 # dispatch table:
2546 # dispatch table:
2547 # file state
2547 # file state
2548 # action if in target manifest
2548 # action if in target manifest
2549 # action if not in target manifest
2549 # action if not in target manifest
2550 # make backup if in target manifest
2550 # make backup if in target manifest
2551 # make backup if not in target manifest
2551 # make backup if not in target manifest
2552 (modified, revert, remove, True, True),
2552 (modified, revert, remove, True, True),
2553 (added, revert, remove, True, False),
2553 (added, revert, remove, True, False),
2554 (removed, undelete, None, False, False),
2554 (removed, undelete, None, False, False),
2555 (deleted, revert, remove, False, False),
2555 (deleted, revert, remove, False, False),
2556 )
2556 )
2557
2557
2558 for abs, (rel, exact) in sorted(names.items()):
2558 for abs, (rel, exact) in sorted(names.items()):
2559 mfentry = mf.get(abs)
2559 mfentry = mf.get(abs)
2560 target = repo.wjoin(abs)
2560 target = repo.wjoin(abs)
2561 def handle(xlist, dobackup):
2561 def handle(xlist, dobackup):
2562 xlist[0].append(abs)
2562 xlist[0].append(abs)
2563 if dobackup and not opts.get('no_backup') and util.lexists(target):
2563 if dobackup and not opts.get('no_backup') and util.lexists(target):
2564 bakname = "%s.orig" % rel
2564 bakname = "%s.orig" % rel
2565 ui.note(_('saving current version of %s as %s\n') %
2565 ui.note(_('saving current version of %s as %s\n') %
2566 (rel, bakname))
2566 (rel, bakname))
2567 if not opts.get('dry_run'):
2567 if not opts.get('dry_run'):
2568 util.copyfile(target, bakname)
2568 util.copyfile(target, bakname)
2569 if ui.verbose or not exact:
2569 if ui.verbose or not exact:
2570 msg = xlist[1]
2570 msg = xlist[1]
2571 if not isinstance(msg, basestring):
2571 if not isinstance(msg, basestring):
2572 msg = msg(abs)
2572 msg = msg(abs)
2573 ui.status(msg % rel)
2573 ui.status(msg % rel)
2574 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2574 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2575 if abs not in table: continue
2575 if abs not in table: continue
2576 # file has changed in dirstate
2576 # file has changed in dirstate
2577 if mfentry:
2577 if mfentry:
2578 handle(hitlist, backuphit)
2578 handle(hitlist, backuphit)
2579 elif misslist is not None:
2579 elif misslist is not None:
2580 handle(misslist, backupmiss)
2580 handle(misslist, backupmiss)
2581 break
2581 break
2582 else:
2582 else:
2583 if abs not in repo.dirstate:
2583 if abs not in repo.dirstate:
2584 if mfentry:
2584 if mfentry:
2585 handle(add, True)
2585 handle(add, True)
2586 elif exact:
2586 elif exact:
2587 ui.warn(_('file not managed: %s\n') % rel)
2587 ui.warn(_('file not managed: %s\n') % rel)
2588 continue
2588 continue
2589 # file has not changed in dirstate
2589 # file has not changed in dirstate
2590 if node == parent:
2590 if node == parent:
2591 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2591 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2592 continue
2592 continue
2593 if pmf is None:
2593 if pmf is None:
2594 # only need parent manifest in this unlikely case,
2594 # only need parent manifest in this unlikely case,
2595 # so do not read by default
2595 # so do not read by default
2596 pmf = repo[parent].manifest()
2596 pmf = repo[parent].manifest()
2597 if abs in pmf:
2597 if abs in pmf:
2598 if mfentry:
2598 if mfentry:
2599 # if version of file is same in parent and target
2599 # if version of file is same in parent and target
2600 # manifests, do nothing
2600 # manifests, do nothing
2601 if (pmf[abs] != mfentry or
2601 if (pmf[abs] != mfentry or
2602 pmf.flags(abs) != mf.flags(abs)):
2602 pmf.flags(abs) != mf.flags(abs)):
2603 handle(revert, False)
2603 handle(revert, False)
2604 else:
2604 else:
2605 handle(remove, False)
2605 handle(remove, False)
2606
2606
2607 if not opts.get('dry_run'):
2607 if not opts.get('dry_run'):
2608 def checkout(f):
2608 def checkout(f):
2609 fc = ctx[f]
2609 fc = ctx[f]
2610 repo.wwrite(f, fc.data(), fc.flags())
2610 repo.wwrite(f, fc.data(), fc.flags())
2611
2611
2612 audit_path = util.path_auditor(repo.root)
2612 audit_path = util.path_auditor(repo.root)
2613 for f in remove[0]:
2613 for f in remove[0]:
2614 if repo.dirstate[f] == 'a':
2614 if repo.dirstate[f] == 'a':
2615 repo.dirstate.forget(f)
2615 repo.dirstate.forget(f)
2616 continue
2616 continue
2617 audit_path(f)
2617 audit_path(f)
2618 try:
2618 try:
2619 util.unlink(repo.wjoin(f))
2619 util.unlink(repo.wjoin(f))
2620 except OSError:
2620 except OSError:
2621 pass
2621 pass
2622 repo.dirstate.remove(f)
2622 repo.dirstate.remove(f)
2623
2623
2624 normal = None
2624 normal = None
2625 if node == parent:
2625 if node == parent:
2626 # We're reverting to our parent. If possible, we'd like status
2626 # We're reverting to our parent. If possible, we'd like status
2627 # to report the file as clean. We have to use normallookup for
2627 # to report the file as clean. We have to use normallookup for
2628 # merges to avoid losing information about merged/dirty files.
2628 # merges to avoid losing information about merged/dirty files.
2629 if p2 != nullid:
2629 if p2 != nullid:
2630 normal = repo.dirstate.normallookup
2630 normal = repo.dirstate.normallookup
2631 else:
2631 else:
2632 normal = repo.dirstate.normal
2632 normal = repo.dirstate.normal
2633 for f in revert[0]:
2633 for f in revert[0]:
2634 checkout(f)
2634 checkout(f)
2635 if normal:
2635 if normal:
2636 normal(f)
2636 normal(f)
2637
2637
2638 for f in add[0]:
2638 for f in add[0]:
2639 checkout(f)
2639 checkout(f)
2640 repo.dirstate.add(f)
2640 repo.dirstate.add(f)
2641
2641
2642 normal = repo.dirstate.normallookup
2642 normal = repo.dirstate.normallookup
2643 if node == parent and p2 == nullid:
2643 if node == parent and p2 == nullid:
2644 normal = repo.dirstate.normal
2644 normal = repo.dirstate.normal
2645 for f in undelete[0]:
2645 for f in undelete[0]:
2646 checkout(f)
2646 checkout(f)
2647 normal(f)
2647 normal(f)
2648
2648
2649 finally:
2649 finally:
2650 wlock.release()
2650 wlock.release()
2651
2651
2652 def rollback(ui, repo):
2652 def rollback(ui, repo):
2653 """roll back the last transaction
2653 """roll back the last transaction
2654
2654
2655 This command should be used with care. There is only one level of
2655 This command should be used with care. There is only one level of
2656 rollback, and there is no way to undo a rollback. It will also
2656 rollback, and there is no way to undo a rollback. It will also
2657 restore the dirstate at the time of the last transaction, losing
2657 restore the dirstate at the time of the last transaction, losing
2658 any dirstate changes since that time.
2658 any dirstate changes since that time.
2659
2659
2660 Transactions are used to encapsulate the effects of all commands
2660 Transactions are used to encapsulate the effects of all commands
2661 that create new changesets or propagate existing changesets into a
2661 that create new changesets or propagate existing changesets into a
2662 repository. For example, the following commands are transactional,
2662 repository. For example, the following commands are transactional,
2663 and their effects can be rolled back:
2663 and their effects can be rolled back:
2664
2664
2665 commit
2665 commit
2666 import
2666 import
2667 pull
2667 pull
2668 push (with this repository as destination)
2668 push (with this repository as destination)
2669 unbundle
2669 unbundle
2670
2670
2671 This command is not intended for use on public repositories. Once
2671 This command is not intended for use on public repositories. Once
2672 changes are visible for pull by other users, rolling a transaction
2672 changes are visible for pull by other users, rolling a transaction
2673 back locally is ineffective (someone else may already have pulled
2673 back locally is ineffective (someone else may already have pulled
2674 the changes). Furthermore, a race is possible with readers of the
2674 the changes). Furthermore, a race is possible with readers of the
2675 repository; for example an in-progress pull from the repository
2675 repository; for example an in-progress pull from the repository
2676 may fail if a rollback is performed.
2676 may fail if a rollback is performed.
2677 """
2677 """
2678 repo.rollback()
2678 repo.rollback()
2679
2679
2680 def root(ui, repo):
2680 def root(ui, repo):
2681 """print the root (top) of the current working directory
2681 """print the root (top) of the current working directory
2682
2682
2683 Print the root directory of the current repository.
2683 Print the root directory of the current repository.
2684 """
2684 """
2685 ui.write(repo.root + "\n")
2685 ui.write(repo.root + "\n")
2686
2686
2687 def serve(ui, repo, **opts):
2687 def serve(ui, repo, **opts):
2688 """export the repository via HTTP
2688 """export the repository via HTTP
2689
2689
2690 Start a local HTTP repository browser and pull server.
2690 Start a local HTTP repository browser and pull server.
2691
2691
2692 By default, the server logs accesses to stdout and errors to
2692 By default, the server logs accesses to stdout and errors to
2693 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2693 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2694 files.
2694 files.
2695 """
2695 """
2696
2696
2697 if opts["stdio"]:
2697 if opts["stdio"]:
2698 if repo is None:
2698 if repo is None:
2699 raise error.RepoError(_("There is no Mercurial repository here"
2699 raise error.RepoError(_("There is no Mercurial repository here"
2700 " (.hg not found)"))
2700 " (.hg not found)"))
2701 s = sshserver.sshserver(ui, repo)
2701 s = sshserver.sshserver(ui, repo)
2702 s.serve_forever()
2702 s.serve_forever()
2703
2703
2704 baseui = repo and repo.baseui or ui
2704 baseui = repo and repo.baseui or ui
2705 optlist = ("name templates style address port prefix ipv6"
2705 optlist = ("name templates style address port prefix ipv6"
2706 " accesslog errorlog webdir_conf certificate")
2706 " accesslog errorlog webdir_conf certificate")
2707 for o in optlist.split():
2707 for o in optlist.split():
2708 if opts[o]:
2708 if opts[o]:
2709 baseui.setconfig("web", o, str(opts[o]))
2709 baseui.setconfig("web", o, str(opts[o]))
2710 if (repo is not None) and (repo.ui != baseui):
2710 if (repo is not None) and (repo.ui != baseui):
2711 repo.ui.setconfig("web", o, str(opts[o]))
2711 repo.ui.setconfig("web", o, str(opts[o]))
2712
2712
2713 if repo is None and not ui.config("web", "webdir_conf"):
2713 if repo is None and not ui.config("web", "webdir_conf"):
2714 raise error.RepoError(_("There is no Mercurial repository here"
2714 raise error.RepoError(_("There is no Mercurial repository here"
2715 " (.hg not found)"))
2715 " (.hg not found)"))
2716
2716
2717 class service(object):
2717 class service(object):
2718 def init(self):
2718 def init(self):
2719 util.set_signal_handler()
2719 util.set_signal_handler()
2720 self.httpd = server.create_server(baseui, repo)
2720 self.httpd = server.create_server(baseui, repo)
2721
2721
2722 if not ui.verbose: return
2722 if not ui.verbose: return
2723
2723
2724 if self.httpd.prefix:
2724 if self.httpd.prefix:
2725 prefix = self.httpd.prefix.strip('/') + '/'
2725 prefix = self.httpd.prefix.strip('/') + '/'
2726 else:
2726 else:
2727 prefix = ''
2727 prefix = ''
2728
2728
2729 port = ':%d' % self.httpd.port
2729 port = ':%d' % self.httpd.port
2730 if port == ':80':
2730 if port == ':80':
2731 port = ''
2731 port = ''
2732
2732
2733 bindaddr = self.httpd.addr
2733 bindaddr = self.httpd.addr
2734 if bindaddr == '0.0.0.0':
2734 if bindaddr == '0.0.0.0':
2735 bindaddr = '*'
2735 bindaddr = '*'
2736 elif ':' in bindaddr: # IPv6
2736 elif ':' in bindaddr: # IPv6
2737 bindaddr = '[%s]' % bindaddr
2737 bindaddr = '[%s]' % bindaddr
2738
2738
2739 fqaddr = self.httpd.fqaddr
2739 fqaddr = self.httpd.fqaddr
2740 if ':' in fqaddr:
2740 if ':' in fqaddr:
2741 fqaddr = '[%s]' % fqaddr
2741 fqaddr = '[%s]' % fqaddr
2742 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2742 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2743 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2743 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2744
2744
2745 def run(self):
2745 def run(self):
2746 self.httpd.serve_forever()
2746 self.httpd.serve_forever()
2747
2747
2748 service = service()
2748 service = service()
2749
2749
2750 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2750 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2751
2751
2752 def status(ui, repo, *pats, **opts):
2752 def status(ui, repo, *pats, **opts):
2753 """show changed files in the working directory
2753 """show changed files in the working directory
2754
2754
2755 Show status of files in the repository. If names are given, only
2755 Show status of files in the repository. If names are given, only
2756 files that match are shown. Files that are clean or ignored or
2756 files that match are shown. Files that are clean or ignored or
2757 the source of a copy/move operation, are not listed unless
2757 the source of a copy/move operation, are not listed unless
2758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2759 Unless options described with "show only ..." are given, the
2759 Unless options described with "show only ..." are given, the
2760 options -mardu are used.
2760 options -mardu are used.
2761
2761
2762 Option -q/--quiet hides untracked (unknown and ignored) files
2762 Option -q/--quiet hides untracked (unknown and ignored) files
2763 unless explicitly requested with -u/--unknown or -i/--ignored.
2763 unless explicitly requested with -u/--unknown or -i/--ignored.
2764
2764
2765 NOTE: status may appear to disagree with diff if permissions have
2765 NOTE: status may appear to disagree with diff if permissions have
2766 changed or a merge has occurred. The standard diff format does not
2766 changed or a merge has occurred. The standard diff format does not
2767 report permission changes and diff only reports changes relative
2767 report permission changes and diff only reports changes relative
2768 to one merge parent.
2768 to one merge parent.
2769
2769
2770 If one revision is given, it is used as the base revision.
2770 If one revision is given, it is used as the base revision.
2771 If two revisions are given, the differences between them are
2771 If two revisions are given, the differences between them are
2772 shown.
2772 shown.
2773
2773
2774 The codes used to show the status of files are:
2774 The codes used to show the status of files are:
2775 M = modified
2775 M = modified
2776 A = added
2776 A = added
2777 R = removed
2777 R = removed
2778 C = clean
2778 C = clean
2779 ! = missing (deleted by non-hg command, but still tracked)
2779 ! = missing (deleted by non-hg command, but still tracked)
2780 ? = not tracked
2780 ? = not tracked
2781 I = ignored
2781 I = ignored
2782 = origin of the previous file listed as A (added)
2782 = origin of the previous file listed as A (added)
2783 """
2783 """
2784
2784
2785 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2785 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2786 cwd = (pats and repo.getcwd()) or ''
2786 cwd = (pats and repo.getcwd()) or ''
2787 end = opts.get('print0') and '\0' or '\n'
2787 end = opts.get('print0') and '\0' or '\n'
2788 copy = {}
2788 copy = {}
2789 states = 'modified added removed deleted unknown ignored clean'.split()
2789 states = 'modified added removed deleted unknown ignored clean'.split()
2790 show = [k for k in states if opts.get(k)]
2790 show = [k for k in states if opts.get(k)]
2791 if opts.get('all'):
2791 if opts.get('all'):
2792 show += ui.quiet and (states[:4] + ['clean']) or states
2792 show += ui.quiet and (states[:4] + ['clean']) or states
2793 if not show:
2793 if not show:
2794 show = ui.quiet and states[:4] or states[:5]
2794 show = ui.quiet and states[:4] or states[:5]
2795
2795
2796 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2796 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2797 'ignored' in show, 'clean' in show, 'unknown' in show)
2797 'ignored' in show, 'clean' in show, 'unknown' in show)
2798 changestates = zip(states, 'MAR!?IC', stat)
2798 changestates = zip(states, 'MAR!?IC', stat)
2799
2799
2800 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2800 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2801 ctxn = repo[nullid]
2801 ctxn = repo[nullid]
2802 ctx1 = repo[node1]
2802 ctx1 = repo[node1]
2803 ctx2 = repo[node2]
2803 ctx2 = repo[node2]
2804 added = stat[1]
2804 added = stat[1]
2805 if node2 is None:
2805 if node2 is None:
2806 added = stat[0] + stat[1] # merged?
2806 added = stat[0] + stat[1] # merged?
2807
2807
2808 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2808 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2809 if k in added:
2809 if k in added:
2810 copy[k] = v
2810 copy[k] = v
2811 elif v in added:
2811 elif v in added:
2812 copy[v] = k
2812 copy[v] = k
2813
2813
2814 for state, char, files in changestates:
2814 for state, char, files in changestates:
2815 if state in show:
2815 if state in show:
2816 format = "%s %%s%s" % (char, end)
2816 format = "%s %%s%s" % (char, end)
2817 if opts.get('no_status'):
2817 if opts.get('no_status'):
2818 format = "%%s%s" % end
2818 format = "%%s%s" % end
2819
2819
2820 for f in files:
2820 for f in files:
2821 ui.write(format % repo.pathto(f, cwd))
2821 ui.write(format % repo.pathto(f, cwd))
2822 if f in copy:
2822 if f in copy:
2823 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2823 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2824
2824
2825 def tag(ui, repo, name1, *names, **opts):
2825 def tag(ui, repo, name1, *names, **opts):
2826 """add one or more tags for the current or given revision
2826 """add one or more tags for the current or given revision
2827
2827
2828 Name a particular revision using <name>.
2828 Name a particular revision using <name>.
2829
2829
2830 Tags are used to name particular revisions of the repository and are
2830 Tags are used to name particular revisions of the repository and are
2831 very useful to compare different revisions, to go back to significant
2831 very useful to compare different revisions, to go back to significant
2832 earlier versions or to mark branch points as releases, etc.
2832 earlier versions or to mark branch points as releases, etc.
2833
2833
2834 If no revision is given, the parent of the working directory is
2834 If no revision is given, the parent of the working directory is
2835 used, or tip if no revision is checked out.
2835 used, or tip if no revision is checked out.
2836
2836
2837 To facilitate version control, distribution, and merging of tags,
2837 To facilitate version control, distribution, and merging of tags,
2838 they are stored as a file named ".hgtags" which is managed
2838 they are stored as a file named ".hgtags" which is managed
2839 similarly to other project files and can be hand-edited if
2839 similarly to other project files and can be hand-edited if
2840 necessary. The file '.hg/localtags' is used for local tags (not
2840 necessary. The file '.hg/localtags' is used for local tags (not
2841 shared among repositories).
2841 shared among repositories).
2842
2842
2843 See 'hg help dates' for a list of formats valid for -d/--date.
2843 See 'hg help dates' for a list of formats valid for -d/--date.
2844 """
2844 """
2845
2845
2846 rev_ = "."
2846 rev_ = "."
2847 names = (name1,) + names
2847 names = (name1,) + names
2848 if len(names) != len(set(names)):
2848 if len(names) != len(set(names)):
2849 raise util.Abort(_('tag names must be unique'))
2849 raise util.Abort(_('tag names must be unique'))
2850 for n in names:
2850 for n in names:
2851 if n in ['tip', '.', 'null']:
2851 if n in ['tip', '.', 'null']:
2852 raise util.Abort(_('the name \'%s\' is reserved') % n)
2852 raise util.Abort(_('the name \'%s\' is reserved') % n)
2853 if opts.get('rev') and opts.get('remove'):
2853 if opts.get('rev') and opts.get('remove'):
2854 raise util.Abort(_("--rev and --remove are incompatible"))
2854 raise util.Abort(_("--rev and --remove are incompatible"))
2855 if opts.get('rev'):
2855 if opts.get('rev'):
2856 rev_ = opts['rev']
2856 rev_ = opts['rev']
2857 message = opts.get('message')
2857 message = opts.get('message')
2858 if opts.get('remove'):
2858 if opts.get('remove'):
2859 expectedtype = opts.get('local') and 'local' or 'global'
2859 expectedtype = opts.get('local') and 'local' or 'global'
2860 for n in names:
2860 for n in names:
2861 if not repo.tagtype(n):
2861 if not repo.tagtype(n):
2862 raise util.Abort(_('tag \'%s\' does not exist') % n)
2862 raise util.Abort(_('tag \'%s\' does not exist') % n)
2863 if repo.tagtype(n) != expectedtype:
2863 if repo.tagtype(n) != expectedtype:
2864 if expectedtype == 'global':
2864 if expectedtype == 'global':
2865 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2865 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2866 else:
2866 else:
2867 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2867 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2868 rev_ = nullid
2868 rev_ = nullid
2869 if not message:
2869 if not message:
2870 message = _('Removed tag %s') % ', '.join(names)
2870 message = _('Removed tag %s') % ', '.join(names)
2871 elif not opts.get('force'):
2871 elif not opts.get('force'):
2872 for n in names:
2872 for n in names:
2873 if n in repo.tags():
2873 if n in repo.tags():
2874 raise util.Abort(_('tag \'%s\' already exists '
2874 raise util.Abort(_('tag \'%s\' already exists '
2875 '(use -f to force)') % n)
2875 '(use -f to force)') % n)
2876 if not rev_ and repo.dirstate.parents()[1] != nullid:
2876 if not rev_ and repo.dirstate.parents()[1] != nullid:
2877 raise util.Abort(_('uncommitted merge - please provide a '
2877 raise util.Abort(_('uncommitted merge - please provide a '
2878 'specific revision'))
2878 'specific revision'))
2879 r = repo[rev_].node()
2879 r = repo[rev_].node()
2880
2880
2881 if not message:
2881 if not message:
2882 message = (_('Added tag %s for changeset %s') %
2882 message = (_('Added tag %s for changeset %s') %
2883 (', '.join(names), short(r)))
2883 (', '.join(names), short(r)))
2884
2884
2885 date = opts.get('date')
2885 date = opts.get('date')
2886 if date:
2886 if date:
2887 date = util.parsedate(date)
2887 date = util.parsedate(date)
2888
2888
2889 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2889 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2890
2890
2891 def tags(ui, repo):
2891 def tags(ui, repo):
2892 """list repository tags
2892 """list repository tags
2893
2893
2894 This lists both regular and local tags. When the -v/--verbose
2894 This lists both regular and local tags. When the -v/--verbose
2895 switch is used, a third column "local" is printed for local tags.
2895 switch is used, a third column "local" is printed for local tags.
2896 """
2896 """
2897
2897
2898 hexfunc = ui.debugflag and hex or short
2898 hexfunc = ui.debugflag and hex or short
2899 tagtype = ""
2899 tagtype = ""
2900
2900
2901 for t, n in reversed(repo.tagslist()):
2901 for t, n in reversed(repo.tagslist()):
2902 if ui.quiet:
2902 if ui.quiet:
2903 ui.write("%s\n" % t)
2903 ui.write("%s\n" % t)
2904 continue
2904 continue
2905
2905
2906 try:
2906 try:
2907 hn = hexfunc(n)
2907 hn = hexfunc(n)
2908 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2908 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2909 except error.LookupError:
2909 except error.LookupError:
2910 r = " ?:%s" % hn
2910 r = " ?:%s" % hn
2911 else:
2911 else:
2912 spaces = " " * (30 - encoding.colwidth(t))
2912 spaces = " " * (30 - encoding.colwidth(t))
2913 if ui.verbose:
2913 if ui.verbose:
2914 if repo.tagtype(t) == 'local':
2914 if repo.tagtype(t) == 'local':
2915 tagtype = " local"
2915 tagtype = " local"
2916 else:
2916 else:
2917 tagtype = ""
2917 tagtype = ""
2918 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2918 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2919
2919
2920 def tip(ui, repo, **opts):
2920 def tip(ui, repo, **opts):
2921 """show the tip revision
2921 """show the tip revision
2922
2922
2923 The tip revision (usually just called the tip) is the changeset
2923 The tip revision (usually just called the tip) is the changeset
2924 most recently added to the repository (and therefore the most
2924 most recently added to the repository (and therefore the most
2925 recently changed head).
2925 recently changed head).
2926
2926
2927 If you have just made a commit, that commit will be the tip. If
2927 If you have just made a commit, that commit will be the tip. If
2928 you have just pulled changes from another repository, the tip of
2928 you have just pulled changes from another repository, the tip of
2929 that repository becomes the current tip. The "tip" tag is special
2929 that repository becomes the current tip. The "tip" tag is special
2930 and cannot be renamed or assigned to a different changeset.
2930 and cannot be renamed or assigned to a different changeset.
2931 """
2931 """
2932 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2932 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2933
2933
2934 def unbundle(ui, repo, fname1, *fnames, **opts):
2934 def unbundle(ui, repo, fname1, *fnames, **opts):
2935 """apply one or more changegroup files
2935 """apply one or more changegroup files
2936
2936
2937 Apply one or more compressed changegroup files generated by the
2937 Apply one or more compressed changegroup files generated by the
2938 bundle command.
2938 bundle command.
2939 """
2939 """
2940 fnames = (fname1,) + fnames
2940 fnames = (fname1,) + fnames
2941
2941
2942 lock = repo.lock()
2942 lock = repo.lock()
2943 try:
2943 try:
2944 for fname in fnames:
2944 for fname in fnames:
2945 f = url.open(ui, fname)
2945 f = url.open(ui, fname)
2946 gen = changegroup.readbundle(f, fname)
2946 gen = changegroup.readbundle(f, fname)
2947 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2947 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2948 finally:
2948 finally:
2949 lock.release()
2949 lock.release()
2950
2950
2951 return postincoming(ui, repo, modheads, opts.get('update'), None)
2951 return postincoming(ui, repo, modheads, opts.get('update'), None)
2952
2952
2953 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2953 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2954 """update working directory
2954 """update working directory
2955
2955
2956 Update the repository's working directory to the specified
2956 Update the repository's working directory to the specified
2957 revision, or the tip of the current branch if none is specified.
2957 revision, or the tip of the current branch if none is specified.
2958 Use null as the revision to remove the working copy (like 'hg
2958 Use null as the revision to remove the working copy (like 'hg
2959 clone -U').
2959 clone -U').
2960
2960
2961 When the working directory contains no uncommitted changes, it
2961 When the working directory contains no uncommitted changes, it
2962 will be replaced by the state of the requested revision from the
2962 will be replaced by the state of the requested revision from the
2963 repository. When the requested revision is on a different branch,
2963 repository. When the requested revision is on a different branch,
2964 the working directory will additionally be switched to that
2964 the working directory will additionally be switched to that
2965 branch.
2965 branch.
2966
2966
2967 When there are uncommitted changes, use option -C/--clean to
2967 When there are uncommitted changes, use option -C/--clean to
2968 discard them, forcibly replacing the state of the working
2968 discard them, forcibly replacing the state of the working
2969 directory with the requested revision.
2969 directory with the requested revision.
2970
2970
2971 When there are uncommitted changes and option -C/--clean is not
2971 When there are uncommitted changes and option -C/--clean is not
2972 used, and the parent revision and requested revision are on the
2972 used, and the parent revision and requested revision are on the
2973 same branch, and one of them is an ancestor of the other, then the
2973 same branch, and one of them is an ancestor of the other, then the
2974 new working directory will contain the requested revision merged
2974 new working directory will contain the requested revision merged
2975 with the uncommitted changes. Otherwise, the update will fail with
2975 with the uncommitted changes. Otherwise, the update will fail with
2976 a suggestion to use 'merge' or 'update -C' instead.
2976 a suggestion to use 'merge' or 'update -C' instead.
2977
2977
2978 If you want to update just one file to an older revision, use
2978 If you want to update just one file to an older revision, use
2979 revert.
2979 revert.
2980
2980
2981 See 'hg help dates' for a list of formats valid for -d/--date.
2981 See 'hg help dates' for a list of formats valid for -d/--date.
2982 """
2982 """
2983 if rev and node:
2983 if rev and node:
2984 raise util.Abort(_("please specify just one revision"))
2984 raise util.Abort(_("please specify just one revision"))
2985
2985
2986 if not rev:
2986 if not rev:
2987 rev = node
2987 rev = node
2988
2988
2989 if date:
2989 if date:
2990 if rev:
2990 if rev:
2991 raise util.Abort(_("you can't specify a revision and a date"))
2991 raise util.Abort(_("you can't specify a revision and a date"))
2992 rev = cmdutil.finddate(ui, repo, date)
2992 rev = cmdutil.finddate(ui, repo, date)
2993
2993
2994 if clean:
2994 if clean:
2995 return hg.clean(repo, rev)
2995 return hg.clean(repo, rev)
2996 else:
2996 else:
2997 return hg.update(repo, rev)
2997 return hg.update(repo, rev)
2998
2998
2999 def verify(ui, repo):
2999 def verify(ui, repo):
3000 """verify the integrity of the repository
3000 """verify the integrity of the repository
3001
3001
3002 Verify the integrity of the current repository.
3002 Verify the integrity of the current repository.
3003
3003
3004 This will perform an extensive check of the repository's
3004 This will perform an extensive check of the repository's
3005 integrity, validating the hashes and checksums of each entry in
3005 integrity, validating the hashes and checksums of each entry in
3006 the changelog, manifest, and tracked files, as well as the
3006 the changelog, manifest, and tracked files, as well as the
3007 integrity of their crosslinks and indices.
3007 integrity of their crosslinks and indices.
3008 """
3008 """
3009 return hg.verify(repo)
3009 return hg.verify(repo)
3010
3010
3011 def version_(ui):
3011 def version_(ui):
3012 """output version and copyright information"""
3012 """output version and copyright information"""
3013 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3013 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3014 % util.version())
3014 % util.version())
3015 ui.status(_(
3015 ui.status(_(
3016 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3016 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3017 "This is free software; see the source for copying conditions. "
3017 "This is free software; see the source for copying conditions. "
3018 "There is NO\nwarranty; "
3018 "There is NO\nwarranty; "
3019 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3019 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3020 ))
3020 ))
3021
3021
3022 # Command options and aliases are listed here, alphabetically
3022 # Command options and aliases are listed here, alphabetically
3023
3023
3024 globalopts = [
3024 globalopts = [
3025 ('R', 'repository', '',
3025 ('R', 'repository', '',
3026 _('repository root directory or symbolic path name')),
3026 _('repository root directory or symbolic path name')),
3027 ('', 'cwd', '', _('change working directory')),
3027 ('', 'cwd', '', _('change working directory')),
3028 ('y', 'noninteractive', None,
3028 ('y', 'noninteractive', None,
3029 _('do not prompt, assume \'yes\' for any required answers')),
3029 _('do not prompt, assume \'yes\' for any required answers')),
3030 ('q', 'quiet', None, _('suppress output')),
3030 ('q', 'quiet', None, _('suppress output')),
3031 ('v', 'verbose', None, _('enable additional output')),
3031 ('v', 'verbose', None, _('enable additional output')),
3032 ('', 'config', [], _('set/override config option')),
3032 ('', 'config', [], _('set/override config option')),
3033 ('', 'debug', None, _('enable debugging output')),
3033 ('', 'debug', None, _('enable debugging output')),
3034 ('', 'debugger', None, _('start debugger')),
3034 ('', 'debugger', None, _('start debugger')),
3035 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3035 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3036 ('', 'encodingmode', encoding.encodingmode,
3036 ('', 'encodingmode', encoding.encodingmode,
3037 _('set the charset encoding mode')),
3037 _('set the charset encoding mode')),
3038 ('', 'traceback', None, _('print traceback on exception')),
3038 ('', 'traceback', None, _('print traceback on exception')),
3039 ('', 'time', None, _('time how long the command takes')),
3039 ('', 'time', None, _('time how long the command takes')),
3040 ('', 'profile', None, _('print command execution profile')),
3040 ('', 'profile', None, _('print command execution profile')),
3041 ('', 'version', None, _('output version information and exit')),
3041 ('', 'version', None, _('output version information and exit')),
3042 ('h', 'help', None, _('display help and exit')),
3042 ('h', 'help', None, _('display help and exit')),
3043 ]
3043 ]
3044
3044
3045 dryrunopts = [('n', 'dry-run', None,
3045 dryrunopts = [('n', 'dry-run', None,
3046 _('do not perform actions, just print output'))]
3046 _('do not perform actions, just print output'))]
3047
3047
3048 remoteopts = [
3048 remoteopts = [
3049 ('e', 'ssh', '', _('specify ssh command to use')),
3049 ('e', 'ssh', '', _('specify ssh command to use')),
3050 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3050 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3051 ]
3051 ]
3052
3052
3053 walkopts = [
3053 walkopts = [
3054 ('I', 'include', [], _('include names matching the given patterns')),
3054 ('I', 'include', [], _('include names matching the given patterns')),
3055 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3055 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3056 ]
3056 ]
3057
3057
3058 commitopts = [
3058 commitopts = [
3059 ('m', 'message', '', _('use <text> as commit message')),
3059 ('m', 'message', '', _('use <text> as commit message')),
3060 ('l', 'logfile', '', _('read commit message from <file>')),
3060 ('l', 'logfile', '', _('read commit message from <file>')),
3061 ]
3061 ]
3062
3062
3063 commitopts2 = [
3063 commitopts2 = [
3064 ('d', 'date', '', _('record datecode as commit date')),
3064 ('d', 'date', '', _('record datecode as commit date')),
3065 ('u', 'user', '', _('record the specified user as committer')),
3065 ('u', 'user', '', _('record the specified user as committer')),
3066 ]
3066 ]
3067
3067
3068 templateopts = [
3068 templateopts = [
3069 ('', 'style', '', _('display using template map file')),
3069 ('', 'style', '', _('display using template map file')),
3070 ('', 'template', '', _('display with template')),
3070 ('', 'template', '', _('display with template')),
3071 ]
3071 ]
3072
3072
3073 logopts = [
3073 logopts = [
3074 ('p', 'patch', None, _('show patch')),
3074 ('p', 'patch', None, _('show patch')),
3075 ('g', 'git', None, _('use git extended diff format')),
3075 ('g', 'git', None, _('use git extended diff format')),
3076 ('l', 'limit', '', _('limit number of changes displayed')),
3076 ('l', 'limit', '', _('limit number of changes displayed')),
3077 ('M', 'no-merges', None, _('do not show merges')),
3077 ('M', 'no-merges', None, _('do not show merges')),
3078 ] + templateopts
3078 ] + templateopts
3079
3079
3080 diffopts = [
3080 diffopts = [
3081 ('a', 'text', None, _('treat all files as text')),
3081 ('a', 'text', None, _('treat all files as text')),
3082 ('g', 'git', None, _('use git extended diff format')),
3082 ('g', 'git', None, _('use git extended diff format')),
3083 ('', 'nodates', None, _("don't include dates in diff headers"))
3083 ('', 'nodates', None, _("don't include dates in diff headers"))
3084 ]
3084 ]
3085
3085
3086 diffopts2 = [
3086 diffopts2 = [
3087 ('p', 'show-function', None, _('show which function each change is in')),
3087 ('p', 'show-function', None, _('show which function each change is in')),
3088 ('w', 'ignore-all-space', None,
3088 ('w', 'ignore-all-space', None,
3089 _('ignore white space when comparing lines')),
3089 _('ignore white space when comparing lines')),
3090 ('b', 'ignore-space-change', None,
3090 ('b', 'ignore-space-change', None,
3091 _('ignore changes in the amount of white space')),
3091 _('ignore changes in the amount of white space')),
3092 ('B', 'ignore-blank-lines', None,
3092 ('B', 'ignore-blank-lines', None,
3093 _('ignore changes whose lines are all blank')),
3093 _('ignore changes whose lines are all blank')),
3094 ('U', 'unified', '', _('number of lines of context to show'))
3094 ('U', 'unified', '', _('number of lines of context to show'))
3095 ]
3095 ]
3096
3096
3097 similarityopts = [
3097 similarityopts = [
3098 ('s', 'similarity', '',
3098 ('s', 'similarity', '',
3099 _('guess renamed files by similarity (0<=s<=100)'))
3099 _('guess renamed files by similarity (0<=s<=100)'))
3100 ]
3100 ]
3101
3101
3102 table = {
3102 table = {
3103 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3103 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3104 "addremove":
3104 "addremove":
3105 (addremove, similarityopts + walkopts + dryrunopts,
3105 (addremove, similarityopts + walkopts + dryrunopts,
3106 _('[OPTION]... [FILE]...')),
3106 _('[OPTION]... [FILE]...')),
3107 "^annotate|blame":
3107 "^annotate|blame":
3108 (annotate,
3108 (annotate,
3109 [('r', 'rev', '', _('annotate the specified revision')),
3109 [('r', 'rev', '', _('annotate the specified revision')),
3110 ('f', 'follow', None, _('follow file copies and renames')),
3110 ('f', 'follow', None, _('follow file copies and renames')),
3111 ('a', 'text', None, _('treat all files as text')),
3111 ('a', 'text', None, _('treat all files as text')),
3112 ('u', 'user', None, _('list the author (long with -v)')),
3112 ('u', 'user', None, _('list the author (long with -v)')),
3113 ('d', 'date', None, _('list the date (short with -q)')),
3113 ('d', 'date', None, _('list the date (short with -q)')),
3114 ('n', 'number', None, _('list the revision number (default)')),
3114 ('n', 'number', None, _('list the revision number (default)')),
3115 ('c', 'changeset', None, _('list the changeset')),
3115 ('c', 'changeset', None, _('list the changeset')),
3116 ('l', 'line-number', None,
3116 ('l', 'line-number', None,
3117 _('show line number at the first appearance'))
3117 _('show line number at the first appearance'))
3118 ] + walkopts,
3118 ] + walkopts,
3119 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3119 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3120 "archive":
3120 "archive":
3121 (archive,
3121 (archive,
3122 [('', 'no-decode', None, _('do not pass files through decoders')),
3122 [('', 'no-decode', None, _('do not pass files through decoders')),
3123 ('p', 'prefix', '', _('directory prefix for files in archive')),
3123 ('p', 'prefix', '', _('directory prefix for files in archive')),
3124 ('r', 'rev', '', _('revision to distribute')),
3124 ('r', 'rev', '', _('revision to distribute')),
3125 ('t', 'type', '', _('type of distribution to create')),
3125 ('t', 'type', '', _('type of distribution to create')),
3126 ] + walkopts,
3126 ] + walkopts,
3127 _('[OPTION]... DEST')),
3127 _('[OPTION]... DEST')),
3128 "backout":
3128 "backout":
3129 (backout,
3129 (backout,
3130 [('', 'merge', None,
3130 [('', 'merge', None,
3131 _('merge with old dirstate parent after backout')),
3131 _('merge with old dirstate parent after backout')),
3132 ('', 'parent', '', _('parent to choose when backing out merge')),
3132 ('', 'parent', '', _('parent to choose when backing out merge')),
3133 ('r', 'rev', '', _('revision to backout')),
3133 ('r', 'rev', '', _('revision to backout')),
3134 ] + walkopts + commitopts + commitopts2,
3134 ] + walkopts + commitopts + commitopts2,
3135 _('[OPTION]... [-r] REV')),
3135 _('[OPTION]... [-r] REV')),
3136 "bisect":
3136 "bisect":
3137 (bisect,
3137 (bisect,
3138 [('r', 'reset', False, _('reset bisect state')),
3138 [('r', 'reset', False, _('reset bisect state')),
3139 ('g', 'good', False, _('mark changeset good')),
3139 ('g', 'good', False, _('mark changeset good')),
3140 ('b', 'bad', False, _('mark changeset bad')),
3140 ('b', 'bad', False, _('mark changeset bad')),
3141 ('s', 'skip', False, _('skip testing changeset')),
3141 ('s', 'skip', False, _('skip testing changeset')),
3142 ('c', 'command', '', _('use command to check changeset state')),
3142 ('c', 'command', '', _('use command to check changeset state')),
3143 ('U', 'noupdate', False, _('do not update to target'))],
3143 ('U', 'noupdate', False, _('do not update to target'))],
3144 _("[-gbsr] [-c CMD] [REV]")),
3144 _("[-gbsr] [-c CMD] [REV]")),
3145 "branch":
3145 "branch":
3146 (branch,
3146 (branch,
3147 [('f', 'force', None,
3147 [('f', 'force', None,
3148 _('set branch name even if it shadows an existing branch')),
3148 _('set branch name even if it shadows an existing branch')),
3149 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3149 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3150 _('[-fC] [NAME]')),
3150 _('[-fC] [NAME]')),
3151 "branches":
3151 "branches":
3152 (branches,
3152 (branches,
3153 [('a', 'active', False,
3153 [('a', 'active', False,
3154 _('show only branches that have unmerged heads'))],
3154 _('show only branches that have unmerged heads'))],
3155 _('[-a]')),
3155 _('[-a]')),
3156 "bundle":
3156 "bundle":
3157 (bundle,
3157 (bundle,
3158 [('f', 'force', None,
3158 [('f', 'force', None,
3159 _('run even when remote repository is unrelated')),
3159 _('run even when remote repository is unrelated')),
3160 ('r', 'rev', [],
3160 ('r', 'rev', [],
3161 _('a changeset up to which you would like to bundle')),
3161 _('a changeset up to which you would like to bundle')),
3162 ('', 'base', [],
3162 ('', 'base', [],
3163 _('a base changeset to specify instead of a destination')),
3163 _('a base changeset to specify instead of a destination')),
3164 ('a', 'all', None, _('bundle all changesets in the repository')),
3164 ('a', 'all', None, _('bundle all changesets in the repository')),
3165 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3165 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3166 ] + remoteopts,
3166 ] + remoteopts,
3167 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3167 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3168 "cat":
3168 "cat":
3169 (cat,
3169 (cat,
3170 [('o', 'output', '', _('print output to file with formatted name')),
3170 [('o', 'output', '', _('print output to file with formatted name')),
3171 ('r', 'rev', '', _('print the given revision')),
3171 ('r', 'rev', '', _('print the given revision')),
3172 ('', 'decode', None, _('apply any matching decode filter')),
3172 ('', 'decode', None, _('apply any matching decode filter')),
3173 ] + walkopts,
3173 ] + walkopts,
3174 _('[OPTION]... FILE...')),
3174 _('[OPTION]... FILE...')),
3175 "^clone":
3175 "^clone":
3176 (clone,
3176 (clone,
3177 [('U', 'noupdate', None,
3177 [('U', 'noupdate', None,
3178 _('the clone will only contain a repository (no working copy)')),
3178 _('the clone will only contain a repository (no working copy)')),
3179 ('r', 'rev', [],
3179 ('r', 'rev', [],
3180 _('a changeset you would like to have after cloning')),
3180 _('a changeset you would like to have after cloning')),
3181 ('', 'pull', None, _('use pull protocol to copy metadata')),
3181 ('', 'pull', None, _('use pull protocol to copy metadata')),
3182 ('', 'uncompressed', None,
3182 ('', 'uncompressed', None,
3183 _('use uncompressed transfer (fast over LAN)')),
3183 _('use uncompressed transfer (fast over LAN)')),
3184 ] + remoteopts,
3184 ] + remoteopts,
3185 _('[OPTION]... SOURCE [DEST]')),
3185 _('[OPTION]... SOURCE [DEST]')),
3186 "^commit|ci":
3186 "^commit|ci":
3187 (commit,
3187 (commit,
3188 [('A', 'addremove', None,
3188 [('A', 'addremove', None,
3189 _('mark new/missing files as added/removed before committing')),
3189 _('mark new/missing files as added/removed before committing')),
3190 ('', 'close-branch', None,
3190 ('', 'close-branch', None,
3191 _('mark a branch as closed, hiding it from the branch list')),
3191 _('mark a branch as closed, hiding it from the branch list')),
3192 ] + walkopts + commitopts + commitopts2,
3192 ] + walkopts + commitopts + commitopts2,
3193 _('[OPTION]... [FILE]...')),
3193 _('[OPTION]... [FILE]...')),
3194 "copy|cp":
3194 "copy|cp":
3195 (copy,
3195 (copy,
3196 [('A', 'after', None, _('record a copy that has already occurred')),
3196 [('A', 'after', None, _('record a copy that has already occurred')),
3197 ('f', 'force', None,
3197 ('f', 'force', None,
3198 _('forcibly copy over an existing managed file')),
3198 _('forcibly copy over an existing managed file')),
3199 ] + walkopts + dryrunopts,
3199 ] + walkopts + dryrunopts,
3200 _('[OPTION]... [SOURCE]... DEST')),
3200 _('[OPTION]... [SOURCE]... DEST')),
3201 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3201 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3202 "debugcheckstate": (debugcheckstate, []),
3202 "debugcheckstate": (debugcheckstate, []),
3203 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3203 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3204 "debugcomplete":
3204 "debugcomplete":
3205 (debugcomplete,
3205 (debugcomplete,
3206 [('o', 'options', None, _('show the command options'))],
3206 [('o', 'options', None, _('show the command options'))],
3207 _('[-o] CMD')),
3207 _('[-o] CMD')),
3208 "debugdate":
3208 "debugdate":
3209 (debugdate,
3209 (debugdate,
3210 [('e', 'extended', None, _('try extended date formats'))],
3210 [('e', 'extended', None, _('try extended date formats'))],
3211 _('[-e] DATE [RANGE]')),
3211 _('[-e] DATE [RANGE]')),
3212 "debugdata": (debugdata, [], _('FILE REV')),
3212 "debugdata": (debugdata, [], _('FILE REV')),
3213 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3213 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3214 "debugindex": (debugindex, [], _('FILE')),
3214 "debugindex": (debugindex, [], _('FILE')),
3215 "debugindexdot": (debugindexdot, [], _('FILE')),
3215 "debugindexdot": (debugindexdot, [], _('FILE')),
3216 "debuginstall": (debuginstall, []),
3216 "debuginstall": (debuginstall, []),
3217 "debugrebuildstate":
3217 "debugrebuildstate":
3218 (debugrebuildstate,
3218 (debugrebuildstate,
3219 [('r', 'rev', '', _('revision to rebuild to'))],
3219 [('r', 'rev', '', _('revision to rebuild to'))],
3220 _('[-r REV] [REV]')),
3220 _('[-r REV] [REV]')),
3221 "debugrename":
3221 "debugrename":
3222 (debugrename,
3222 (debugrename,
3223 [('r', 'rev', '', _('revision to debug'))],
3223 [('r', 'rev', '', _('revision to debug'))],
3224 _('[-r REV] FILE')),
3224 _('[-r REV] FILE')),
3225 "debugsetparents":
3225 "debugsetparents":
3226 (debugsetparents, [], _('REV1 [REV2]')),
3226 (debugsetparents, [], _('REV1 [REV2]')),
3227 "debugstate":
3227 "debugstate":
3228 (debugstate,
3228 (debugstate,
3229 [('', 'nodates', None, _('do not display the saved mtime'))],
3229 [('', 'nodates', None, _('do not display the saved mtime'))],
3230 _('[OPTION]...')),
3230 _('[OPTION]...')),
3231 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3231 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3232 "^diff":
3232 "^diff":
3233 (diff,
3233 (diff,
3234 [('r', 'rev', [], _('revision')),
3234 [('r', 'rev', [], _('revision')),
3235 ('c', 'change', '', _('change made by revision'))
3235 ('c', 'change', '', _('change made by revision'))
3236 ] + diffopts + diffopts2 + walkopts,
3236 ] + diffopts + diffopts2 + walkopts,
3237 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3237 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3238 "^export":
3238 "^export":
3239 (export,
3239 (export,
3240 [('o', 'output', '', _('print output to file with formatted name')),
3240 [('o', 'output', '', _('print output to file with formatted name')),
3241 ('', 'switch-parent', None, _('diff against the second parent'))
3241 ('', 'switch-parent', None, _('diff against the second parent'))
3242 ] + diffopts,
3242 ] + diffopts,
3243 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3243 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3244 "grep":
3244 "grep":
3245 (grep,
3245 (grep,
3246 [('0', 'print0', None, _('end fields with NUL')),
3246 [('0', 'print0', None, _('end fields with NUL')),
3247 ('', 'all', None, _('print all revisions that match')),
3247 ('', 'all', None, _('print all revisions that match')),
3248 ('f', 'follow', None,
3248 ('f', 'follow', None,
3249 _('follow changeset history, or file history across copies and renames')),
3249 _('follow changeset history, or file history across copies and renames')),
3250 ('i', 'ignore-case', None, _('ignore case when matching')),
3250 ('i', 'ignore-case', None, _('ignore case when matching')),
3251 ('l', 'files-with-matches', None,
3251 ('l', 'files-with-matches', None,
3252 _('print only filenames and revisions that match')),
3252 _('print only filenames and revisions that match')),
3253 ('n', 'line-number', None, _('print matching line numbers')),
3253 ('n', 'line-number', None, _('print matching line numbers')),
3254 ('r', 'rev', [], _('search in given revision range')),
3254 ('r', 'rev', [], _('search in given revision range')),
3255 ('u', 'user', None, _('list the author (long with -v)')),
3255 ('u', 'user', None, _('list the author (long with -v)')),
3256 ('d', 'date', None, _('list the date (short with -q)')),
3256 ('d', 'date', None, _('list the date (short with -q)')),
3257 ] + walkopts,
3257 ] + walkopts,
3258 _('[OPTION]... PATTERN [FILE]...')),
3258 _('[OPTION]... PATTERN [FILE]...')),
3259 "heads":
3259 "heads":
3260 (heads,
3260 (heads,
3261 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3261 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3262 ('a', 'active', False,
3262 ('a', 'active', False,
3263 _('show only the active heads from open branches')),
3263 _('show only the active heads from open branches')),
3264 ('c', 'closed', False,
3264 ('c', 'closed', False,
3265 _('show normal and closed heads')),
3265 _('show normal and closed heads')),
3266 ] + templateopts,
3266 ] + templateopts,
3267 _('[-r STARTREV] [REV]...')),
3267 _('[-r STARTREV] [REV]...')),
3268 "help": (help_, [], _('[TOPIC]')),
3268 "help": (help_, [], _('[TOPIC]')),
3269 "identify|id":
3269 "identify|id":
3270 (identify,
3270 (identify,
3271 [('r', 'rev', '', _('identify the specified revision')),
3271 [('r', 'rev', '', _('identify the specified revision')),
3272 ('n', 'num', None, _('show local revision number')),
3272 ('n', 'num', None, _('show local revision number')),
3273 ('i', 'id', None, _('show global revision id')),
3273 ('i', 'id', None, _('show global revision id')),
3274 ('b', 'branch', None, _('show branch')),
3274 ('b', 'branch', None, _('show branch')),
3275 ('t', 'tags', None, _('show tags'))],
3275 ('t', 'tags', None, _('show tags'))],
3276 _('[-nibt] [-r REV] [SOURCE]')),
3276 _('[-nibt] [-r REV] [SOURCE]')),
3277 "import|patch":
3277 "import|patch":
3278 (import_,
3278 (import_,
3279 [('p', 'strip', 1,
3279 [('p', 'strip', 1,
3280 _('directory strip option for patch. This has the same '
3280 _('directory strip option for patch. This has the same '
3281 'meaning as the corresponding patch option')),
3281 'meaning as the corresponding patch option')),
3282 ('b', 'base', '', _('base path')),
3282 ('b', 'base', '', _('base path')),
3283 ('f', 'force', None,
3283 ('f', 'force', None,
3284 _('skip check for outstanding uncommitted changes')),
3284 _('skip check for outstanding uncommitted changes')),
3285 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3285 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3286 ('', 'exact', None,
3286 ('', 'exact', None,
3287 _('apply patch to the nodes from which it was generated')),
3287 _('apply patch to the nodes from which it was generated')),
3288 ('', 'import-branch', None,
3288 ('', 'import-branch', None,
3289 _('use any branch information in patch (implied by --exact)'))] +
3289 _('use any branch information in patch (implied by --exact)'))] +
3290 commitopts + commitopts2 + similarityopts,
3290 commitopts + commitopts2 + similarityopts,
3291 _('[OPTION]... PATCH...')),
3291 _('[OPTION]... PATCH...')),
3292 "incoming|in":
3292 "incoming|in":
3293 (incoming,
3293 (incoming,
3294 [('f', 'force', None,
3294 [('f', 'force', None,
3295 _('run even when remote repository is unrelated')),
3295 _('run even when remote repository is unrelated')),
3296 ('n', 'newest-first', None, _('show newest record first')),
3296 ('n', 'newest-first', None, _('show newest record first')),
3297 ('', 'bundle', '', _('file to store the bundles into')),
3297 ('', 'bundle', '', _('file to store the bundles into')),
3298 ('r', 'rev', [],
3298 ('r', 'rev', [],
3299 _('a specific revision up to which you would like to pull')),
3299 _('a specific revision up to which you would like to pull')),
3300 ] + logopts + remoteopts,
3300 ] + logopts + remoteopts,
3301 _('[-p] [-n] [-M] [-f] [-r REV]...'
3301 _('[-p] [-n] [-M] [-f] [-r REV]...'
3302 ' [--bundle FILENAME] [SOURCE]')),
3302 ' [--bundle FILENAME] [SOURCE]')),
3303 "^init":
3303 "^init":
3304 (init,
3304 (init,
3305 remoteopts,
3305 remoteopts,
3306 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3306 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3307 "locate":
3307 "locate":
3308 (locate,
3308 (locate,
3309 [('r', 'rev', '', _('search the repository as it stood at REV')),
3309 [('r', 'rev', '', _('search the repository as it stood at REV')),
3310 ('0', 'print0', None,
3310 ('0', 'print0', None,
3311 _('end filenames with NUL, for use with xargs')),
3311 _('end filenames with NUL, for use with xargs')),
3312 ('f', 'fullpath', None,
3312 ('f', 'fullpath', None,
3313 _('print complete paths from the filesystem root')),
3313 _('print complete paths from the filesystem root')),
3314 ] + walkopts,
3314 ] + walkopts,
3315 _('[OPTION]... [PATTERN]...')),
3315 _('[OPTION]... [PATTERN]...')),
3316 "^log|history":
3316 "^log|history":
3317 (log,
3317 (log,
3318 [('f', 'follow', None,
3318 [('f', 'follow', None,
3319 _('follow changeset history, or file history across copies and renames')),
3319 _('follow changeset history, or file history across copies and renames')),
3320 ('', 'follow-first', None,
3320 ('', 'follow-first', None,
3321 _('only follow the first parent of merge changesets')),
3321 _('only follow the first parent of merge changesets')),
3322 ('d', 'date', '', _('show revisions matching date spec')),
3322 ('d', 'date', '', _('show revisions matching date spec')),
3323 ('C', 'copies', None, _('show copied files')),
3323 ('C', 'copies', None, _('show copied files')),
3324 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3324 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3325 ('r', 'rev', [], _('show the specified revision or range')),
3325 ('r', 'rev', [], _('show the specified revision or range')),
3326 ('', 'removed', None, _('include revisions where files were removed')),
3326 ('', 'removed', None, _('include revisions where files were removed')),
3327 ('m', 'only-merges', None, _('show only merges')),
3327 ('m', 'only-merges', None, _('show only merges')),
3328 ('u', 'user', [], _('revisions committed by user')),
3328 ('u', 'user', [], _('revisions committed by user')),
3329 ('b', 'only-branch', [],
3329 ('b', 'only-branch', [],
3330 _('show only changesets within the given named branch')),
3330 _('show only changesets within the given named branch')),
3331 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3331 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3332 ] + logopts + walkopts,
3332 ] + logopts + walkopts,
3333 _('[OPTION]... [FILE]')),
3333 _('[OPTION]... [FILE]')),
3334 "manifest":
3334 "manifest":
3335 (manifest,
3335 (manifest,
3336 [('r', 'rev', '', _('revision to display'))],
3336 [('r', 'rev', '', _('revision to display'))],
3337 _('[-r REV]')),
3337 _('[-r REV]')),
3338 "^merge":
3338 "^merge":
3339 (merge,
3339 (merge,
3340 [('f', 'force', None, _('force a merge with outstanding changes')),
3340 [('f', 'force', None, _('force a merge with outstanding changes')),
3341 ('r', 'rev', '', _('revision to merge')),
3341 ('r', 'rev', '', _('revision to merge')),
3342 ('S', 'show', None,
3342 ('S', 'show', None,
3343 _('review revisions to merge (no merge is performed)'))],
3343 _('review revisions to merge (no merge is performed)'))],
3344 _('[-f] [[-r] REV]')),
3344 _('[-f] [[-r] REV]')),
3345 "outgoing|out":
3345 "outgoing|out":
3346 (outgoing,
3346 (outgoing,
3347 [('f', 'force', None,
3347 [('f', 'force', None,
3348 _('run even when remote repository is unrelated')),
3348 _('run even when remote repository is unrelated')),
3349 ('r', 'rev', [],
3349 ('r', 'rev', [],
3350 _('a specific revision up to which you would like to push')),
3350 _('a specific revision up to which you would like to push')),
3351 ('n', 'newest-first', None, _('show newest record first')),
3351 ('n', 'newest-first', None, _('show newest record first')),
3352 ] + logopts + remoteopts,
3352 ] + logopts + remoteopts,
3353 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3353 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3354 "^parents":
3354 "^parents":
3355 (parents,
3355 (parents,
3356 [('r', 'rev', '', _('show parents from the specified revision')),
3356 [('r', 'rev', '', _('show parents from the specified revision')),
3357 ] + templateopts,
3357 ] + templateopts,
3358 _('[-r REV] [FILE]')),
3358 _('[-r REV] [FILE]')),
3359 "paths": (paths, [], _('[NAME]')),
3359 "paths": (paths, [], _('[NAME]')),
3360 "^pull":
3360 "^pull":
3361 (pull,
3361 (pull,
3362 [('u', 'update', None,
3362 [('u', 'update', None,
3363 _('update to new tip if changesets were pulled')),
3363 _('update to new tip if changesets were pulled')),
3364 ('f', 'force', None,
3364 ('f', 'force', None,
3365 _('run even when remote repository is unrelated')),
3365 _('run even when remote repository is unrelated')),
3366 ('r', 'rev', [],
3366 ('r', 'rev', [],
3367 _('a specific revision up to which you would like to pull')),
3367 _('a specific revision up to which you would like to pull')),
3368 ] + remoteopts,
3368 ] + remoteopts,
3369 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3369 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3370 "^push":
3370 "^push":
3371 (push,
3371 (push,
3372 [('f', 'force', None, _('force push')),
3372 [('f', 'force', None, _('force push')),
3373 ('r', 'rev', [],
3373 ('r', 'rev', [],
3374 _('a specific revision up to which you would like to push')),
3374 _('a specific revision up to which you would like to push')),
3375 ] + remoteopts,
3375 ] + remoteopts,
3376 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3376 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3377 "recover": (recover, []),
3377 "recover": (recover, []),
3378 "^remove|rm":
3378 "^remove|rm":
3379 (remove,
3379 (remove,
3380 [('A', 'after', None, _('record delete for missing files')),
3380 [('A', 'after', None, _('record delete for missing files')),
3381 ('f', 'force', None,
3381 ('f', 'force', None,
3382 _('remove (and delete) file even if added or modified')),
3382 _('remove (and delete) file even if added or modified')),
3383 ] + walkopts,
3383 ] + walkopts,
3384 _('[OPTION]... FILE...')),
3384 _('[OPTION]... FILE...')),
3385 "rename|mv":
3385 "rename|mv":
3386 (rename,
3386 (rename,
3387 [('A', 'after', None, _('record a rename that has already occurred')),
3387 [('A', 'after', None, _('record a rename that has already occurred')),
3388 ('f', 'force', None,
3388 ('f', 'force', None,
3389 _('forcibly copy over an existing managed file')),
3389 _('forcibly copy over an existing managed file')),
3390 ] + walkopts + dryrunopts,
3390 ] + walkopts + dryrunopts,
3391 _('[OPTION]... SOURCE... DEST')),
3391 _('[OPTION]... SOURCE... DEST')),
3392 "resolve":
3392 "resolve":
3393 (resolve,
3393 (resolve,
3394 [('a', 'all', None, _('remerge all unresolved files')),
3394 [('a', 'all', None, _('remerge all unresolved files')),
3395 ('l', 'list', None, _('list state of files needing merge')),
3395 ('l', 'list', None, _('list state of files needing merge')),
3396 ('m', 'mark', None, _('mark files as resolved')),
3396 ('m', 'mark', None, _('mark files as resolved')),
3397 ('u', 'unmark', None, _('unmark files as resolved'))]
3397 ('u', 'unmark', None, _('unmark files as resolved'))]
3398 + walkopts,
3398 + walkopts,
3399 _('[OPTION]... [FILE]...')),
3399 _('[OPTION]... [FILE]...')),
3400 "revert":
3400 "revert":
3401 (revert,
3401 (revert,
3402 [('a', 'all', None, _('revert all changes when no arguments given')),
3402 [('a', 'all', None, _('revert all changes when no arguments given')),
3403 ('d', 'date', '', _('tipmost revision matching date')),
3403 ('d', 'date', '', _('tipmost revision matching date')),
3404 ('r', 'rev', '', _('revision to revert to')),
3404 ('r', 'rev', '', _('revision to revert to')),
3405 ('', 'no-backup', None, _('do not save backup copies of files')),
3405 ('', 'no-backup', None, _('do not save backup copies of files')),
3406 ] + walkopts + dryrunopts,
3406 ] + walkopts + dryrunopts,
3407 _('[OPTION]... [-r REV] [NAME]...')),
3407 _('[OPTION]... [-r REV] [NAME]...')),
3408 "rollback": (rollback, []),
3408 "rollback": (rollback, []),
3409 "root": (root, []),
3409 "root": (root, []),
3410 "^serve":
3410 "^serve":
3411 (serve,
3411 (serve,
3412 [('A', 'accesslog', '', _('name of access log file to write to')),
3412 [('A', 'accesslog', '', _('name of access log file to write to')),
3413 ('d', 'daemon', None, _('run server in background')),
3413 ('d', 'daemon', None, _('run server in background')),
3414 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3414 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3415 ('E', 'errorlog', '', _('name of error log file to write to')),
3415 ('E', 'errorlog', '', _('name of error log file to write to')),
3416 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3416 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3417 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3417 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3418 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3418 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3419 ('n', 'name', '',
3419 ('n', 'name', '',
3420 _('name to show in web pages (default: working directory)')),
3420 _('name to show in web pages (default: working directory)')),
3421 ('', 'webdir-conf', '', _('name of the webdir config file'
3421 ('', 'webdir-conf', '', _('name of the webdir config file'
3422 ' (serve more than one repository)')),
3422 ' (serve more than one repository)')),
3423 ('', 'pid-file', '', _('name of file to write process ID to')),
3423 ('', 'pid-file', '', _('name of file to write process ID to')),
3424 ('', 'stdio', None, _('for remote clients')),
3424 ('', 'stdio', None, _('for remote clients')),
3425 ('t', 'templates', '', _('web templates to use')),
3425 ('t', 'templates', '', _('web templates to use')),
3426 ('', 'style', '', _('template style to use')),
3426 ('', 'style', '', _('template style to use')),
3427 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3427 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3428 ('', 'certificate', '', _('SSL certificate file'))],
3428 ('', 'certificate', '', _('SSL certificate file'))],
3429 _('[OPTION]...')),
3429 _('[OPTION]...')),
3430 "showconfig|debugconfig":
3430 "showconfig|debugconfig":
3431 (showconfig,
3431 (showconfig,
3432 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3432 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3433 _('[-u] [NAME]...')),
3433 _('[-u] [NAME]...')),
3434 "^status|st":
3434 "^status|st":
3435 (status,
3435 (status,
3436 [('A', 'all', None, _('show status of all files')),
3436 [('A', 'all', None, _('show status of all files')),
3437 ('m', 'modified', None, _('show only modified files')),
3437 ('m', 'modified', None, _('show only modified files')),
3438 ('a', 'added', None, _('show only added files')),
3438 ('a', 'added', None, _('show only added files')),
3439 ('r', 'removed', None, _('show only removed files')),
3439 ('r', 'removed', None, _('show only removed files')),
3440 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3440 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3441 ('c', 'clean', None, _('show only files without changes')),
3441 ('c', 'clean', None, _('show only files without changes')),
3442 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3442 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3443 ('i', 'ignored', None, _('show only ignored files')),
3443 ('i', 'ignored', None, _('show only ignored files')),
3444 ('n', 'no-status', None, _('hide status prefix')),
3444 ('n', 'no-status', None, _('hide status prefix')),
3445 ('C', 'copies', None, _('show source of copied files')),
3445 ('C', 'copies', None, _('show source of copied files')),
3446 ('0', 'print0', None,
3446 ('0', 'print0', None,
3447 _('end filenames with NUL, for use with xargs')),
3447 _('end filenames with NUL, for use with xargs')),
3448 ('', 'rev', [], _('show difference from revision')),
3448 ('', 'rev', [], _('show difference from revision')),
3449 ] + walkopts,
3449 ] + walkopts,
3450 _('[OPTION]... [FILE]...')),
3450 _('[OPTION]... [FILE]...')),
3451 "tag":
3451 "tag":
3452 (tag,
3452 (tag,
3453 [('f', 'force', None, _('replace existing tag')),
3453 [('f', 'force', None, _('replace existing tag')),
3454 ('l', 'local', None, _('make the tag local')),
3454 ('l', 'local', None, _('make the tag local')),
3455 ('r', 'rev', '', _('revision to tag')),
3455 ('r', 'rev', '', _('revision to tag')),
3456 ('', 'remove', None, _('remove a tag')),
3456 ('', 'remove', None, _('remove a tag')),
3457 # -l/--local is already there, commitopts cannot be used
3457 # -l/--local is already there, commitopts cannot be used
3458 ('m', 'message', '', _('use <text> as commit message')),
3458 ('m', 'message', '', _('use <text> as commit message')),
3459 ] + commitopts2,
3459 ] + commitopts2,
3460 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3460 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3461 "tags": (tags, []),
3461 "tags": (tags, []),
3462 "tip":
3462 "tip":
3463 (tip,
3463 (tip,
3464 [('p', 'patch', None, _('show patch')),
3464 [('p', 'patch', None, _('show patch')),
3465 ('g', 'git', None, _('use git extended diff format')),
3465 ('g', 'git', None, _('use git extended diff format')),
3466 ] + templateopts,
3466 ] + templateopts,
3467 _('[-p]')),
3467 _('[-p]')),
3468 "unbundle":
3468 "unbundle":
3469 (unbundle,
3469 (unbundle,
3470 [('u', 'update', None,
3470 [('u', 'update', None,
3471 _('update to new tip if changesets were unbundled'))],
3471 _('update to new tip if changesets were unbundled'))],
3472 _('[-u] FILE...')),
3472 _('[-u] FILE...')),
3473 "^update|up|checkout|co":
3473 "^update|up|checkout|co":
3474 (update,
3474 (update,
3475 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3475 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3476 ('d', 'date', '', _('tipmost revision matching date')),
3476 ('d', 'date', '', _('tipmost revision matching date')),
3477 ('r', 'rev', '', _('revision'))],
3477 ('r', 'rev', '', _('revision'))],
3478 _('[-C] [-d DATE] [[-r] REV]')),
3478 _('[-C] [-d DATE] [[-r] REV]')),
3479 "verify": (verify, []),
3479 "verify": (verify, []),
3480 "version": (version_, []),
3480 "version": (version_, []),
3481 }
3481 }
3482
3482
3483 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3483 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3484 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3484 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3485 optionalrepo = ("identify paths serve showconfig debugancestor")
3485 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1394 +1,1434 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 from i18n import _
9 from i18n import _
10 from node import hex, nullid, short
10 from node import hex, nullid, short
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 import cStringIO, email.Parser, os, re, math
12 import cStringIO, email.Parser, os, re, math
13 import sys, tempfile, zlib
13 import sys, tempfile, zlib
14
14
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16
16
17 class PatchError(Exception):
17 class PatchError(Exception):
18 pass
18 pass
19
19
20 class NoHunks(PatchError):
20 class NoHunks(PatchError):
21 pass
21 pass
22
22
23 # helper functions
23 # helper functions
24
24
25 def copyfile(src, dst, basedir):
25 def copyfile(src, dst, basedir):
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 if os.path.exists(absdst):
27 if os.path.exists(absdst):
28 raise util.Abort(_("cannot create %s: destination already exists") %
28 raise util.Abort(_("cannot create %s: destination already exists") %
29 dst)
29 dst)
30
30
31 dstdir = os.path.dirname(absdst)
31 dstdir = os.path.dirname(absdst)
32 if dstdir and not os.path.isdir(dstdir):
32 if dstdir and not os.path.isdir(dstdir):
33 try:
33 try:
34 os.makedirs(dstdir)
34 os.makedirs(dstdir)
35 except IOError:
35 except IOError:
36 raise util.Abort(
36 raise util.Abort(
37 _("cannot create %s: unable to create destination directory")
37 _("cannot create %s: unable to create destination directory")
38 % dst)
38 % dst)
39
39
40 util.copyfile(abssrc, absdst)
40 util.copyfile(abssrc, absdst)
41
41
42 # public functions
42 # public functions
43
43
44 def extract(ui, fileobj):
44 def extract(ui, fileobj):
45 '''extract patch from data read from fileobj.
45 '''extract patch from data read from fileobj.
46
46
47 patch can be a normal patch or contained in an email message.
47 patch can be a normal patch or contained in an email message.
48
48
49 return tuple (filename, message, user, date, node, p1, p2).
49 return tuple (filename, message, user, date, node, p1, p2).
50 Any item in the returned tuple can be None. If filename is None,
50 Any item in the returned tuple can be None. If filename is None,
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
51 fileobj did not contain a patch. Caller must unlink filename when done.'''
52
52
53 # attempt to detect the start of a patch
53 # attempt to detect the start of a patch
54 # (this heuristic is borrowed from quilt)
54 # (this heuristic is borrowed from quilt)
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58
58
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 tmpfp = os.fdopen(fd, 'w')
60 tmpfp = os.fdopen(fd, 'w')
61 try:
61 try:
62 msg = email.Parser.Parser().parse(fileobj)
62 msg = email.Parser.Parser().parse(fileobj)
63
63
64 subject = msg['Subject']
64 subject = msg['Subject']
65 user = msg['From']
65 user = msg['From']
66 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
66 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
67 # should try to parse msg['Date']
67 # should try to parse msg['Date']
68 date = None
68 date = None
69 nodeid = None
69 nodeid = None
70 branch = None
70 branch = None
71 parents = []
71 parents = []
72
72
73 if subject:
73 if subject:
74 if subject.startswith('[PATCH'):
74 if subject.startswith('[PATCH'):
75 pend = subject.find(']')
75 pend = subject.find(']')
76 if pend >= 0:
76 if pend >= 0:
77 subject = subject[pend+1:].lstrip()
77 subject = subject[pend+1:].lstrip()
78 subject = subject.replace('\n\t', ' ')
78 subject = subject.replace('\n\t', ' ')
79 ui.debug('Subject: %s\n' % subject)
79 ui.debug('Subject: %s\n' % subject)
80 if user:
80 if user:
81 ui.debug('From: %s\n' % user)
81 ui.debug('From: %s\n' % user)
82 diffs_seen = 0
82 diffs_seen = 0
83 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
83 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
84 message = ''
84 message = ''
85 for part in msg.walk():
85 for part in msg.walk():
86 content_type = part.get_content_type()
86 content_type = part.get_content_type()
87 ui.debug('Content-Type: %s\n' % content_type)
87 ui.debug('Content-Type: %s\n' % content_type)
88 if content_type not in ok_types:
88 if content_type not in ok_types:
89 continue
89 continue
90 payload = part.get_payload(decode=True)
90 payload = part.get_payload(decode=True)
91 m = diffre.search(payload)
91 m = diffre.search(payload)
92 if m:
92 if m:
93 hgpatch = False
93 hgpatch = False
94 ignoretext = False
94 ignoretext = False
95
95
96 ui.debug(_('found patch at byte %d\n') % m.start(0))
96 ui.debug(_('found patch at byte %d\n') % m.start(0))
97 diffs_seen += 1
97 diffs_seen += 1
98 cfp = cStringIO.StringIO()
98 cfp = cStringIO.StringIO()
99 for line in payload[:m.start(0)].splitlines():
99 for line in payload[:m.start(0)].splitlines():
100 if line.startswith('# HG changeset patch'):
100 if line.startswith('# HG changeset patch'):
101 ui.debug(_('patch generated by hg export\n'))
101 ui.debug(_('patch generated by hg export\n'))
102 hgpatch = True
102 hgpatch = True
103 # drop earlier commit message content
103 # drop earlier commit message content
104 cfp.seek(0)
104 cfp.seek(0)
105 cfp.truncate()
105 cfp.truncate()
106 subject = None
106 subject = None
107 elif hgpatch:
107 elif hgpatch:
108 if line.startswith('# User '):
108 if line.startswith('# User '):
109 user = line[7:]
109 user = line[7:]
110 ui.debug('From: %s\n' % user)
110 ui.debug('From: %s\n' % user)
111 elif line.startswith("# Date "):
111 elif line.startswith("# Date "):
112 date = line[7:]
112 date = line[7:]
113 elif line.startswith("# Branch "):
113 elif line.startswith("# Branch "):
114 branch = line[9:]
114 branch = line[9:]
115 elif line.startswith("# Node ID "):
115 elif line.startswith("# Node ID "):
116 nodeid = line[10:]
116 nodeid = line[10:]
117 elif line.startswith("# Parent "):
117 elif line.startswith("# Parent "):
118 parents.append(line[10:])
118 parents.append(line[10:])
119 elif line == '---' and gitsendmail:
119 elif line == '---' and gitsendmail:
120 ignoretext = True
120 ignoretext = True
121 if not line.startswith('# ') and not ignoretext:
121 if not line.startswith('# ') and not ignoretext:
122 cfp.write(line)
122 cfp.write(line)
123 cfp.write('\n')
123 cfp.write('\n')
124 message = cfp.getvalue()
124 message = cfp.getvalue()
125 if tmpfp:
125 if tmpfp:
126 tmpfp.write(payload)
126 tmpfp.write(payload)
127 if not payload.endswith('\n'):
127 if not payload.endswith('\n'):
128 tmpfp.write('\n')
128 tmpfp.write('\n')
129 elif not diffs_seen and message and content_type == 'text/plain':
129 elif not diffs_seen and message and content_type == 'text/plain':
130 message += '\n' + payload
130 message += '\n' + payload
131 except:
131 except:
132 tmpfp.close()
132 tmpfp.close()
133 os.unlink(tmpname)
133 os.unlink(tmpname)
134 raise
134 raise
135
135
136 if subject and not message.startswith(subject):
136 if subject and not message.startswith(subject):
137 message = '%s\n%s' % (subject, message)
137 message = '%s\n%s' % (subject, message)
138 tmpfp.close()
138 tmpfp.close()
139 if not diffs_seen:
139 if not diffs_seen:
140 os.unlink(tmpname)
140 os.unlink(tmpname)
141 return None, message, user, date, branch, None, None, None
141 return None, message, user, date, branch, None, None, None
142 p1 = parents and parents.pop(0) or None
142 p1 = parents and parents.pop(0) or None
143 p2 = parents and parents.pop(0) or None
143 p2 = parents and parents.pop(0) or None
144 return tmpname, message, user, date, branch, nodeid, p1, p2
144 return tmpname, message, user, date, branch, nodeid, p1, p2
145
145
146 GP_PATCH = 1 << 0 # we have to run patch
146 GP_PATCH = 1 << 0 # we have to run patch
147 GP_FILTER = 1 << 1 # there's some copy/rename operation
147 GP_FILTER = 1 << 1 # there's some copy/rename operation
148 GP_BINARY = 1 << 2 # there's a binary patch
148 GP_BINARY = 1 << 2 # there's a binary patch
149
149
150 class patchmeta(object):
150 class patchmeta(object):
151 """Patched file metadata
151 """Patched file metadata
152
152
153 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
153 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
154 or COPY. 'path' is patched file path. 'oldpath' is set to the
154 or COPY. 'path' is patched file path. 'oldpath' is set to the
155 origin file when 'op' is either COPY or RENAME, None otherwise. If
155 origin file when 'op' is either COPY or RENAME, None otherwise. If
156 file mode is changed, 'mode' is a tuple (islink, isexec) where
156 file mode is changed, 'mode' is a tuple (islink, isexec) where
157 'islink' is True if the file is a symlink and 'isexec' is True if
157 'islink' is True if the file is a symlink and 'isexec' is True if
158 the file is executable. Otherwise, 'mode' is None.
158 the file is executable. Otherwise, 'mode' is None.
159 """
159 """
160 def __init__(self, path):
160 def __init__(self, path):
161 self.path = path
161 self.path = path
162 self.oldpath = None
162 self.oldpath = None
163 self.mode = None
163 self.mode = None
164 self.op = 'MODIFY'
164 self.op = 'MODIFY'
165 self.lineno = 0
165 self.lineno = 0
166 self.binary = False
166 self.binary = False
167
167
168 def setmode(self, mode):
168 def setmode(self, mode):
169 islink = mode & 020000
169 islink = mode & 020000
170 isexec = mode & 0100
170 isexec = mode & 0100
171 self.mode = (islink, isexec)
171 self.mode = (islink, isexec)
172
172
173 def readgitpatch(lr):
173 def readgitpatch(lr):
174 """extract git-style metadata about patches from <patchname>"""
174 """extract git-style metadata about patches from <patchname>"""
175
175
176 # Filter patch for git information
176 # Filter patch for git information
177 gp = None
177 gp = None
178 gitpatches = []
178 gitpatches = []
179 # Can have a git patch with only metadata, causing patch to complain
179 # Can have a git patch with only metadata, causing patch to complain
180 dopatch = 0
180 dopatch = 0
181
181
182 lineno = 0
182 lineno = 0
183 for line in lr:
183 for line in lr:
184 lineno += 1
184 lineno += 1
185 if line.startswith('diff --git'):
185 if line.startswith('diff --git'):
186 m = gitre.match(line)
186 m = gitre.match(line)
187 if m:
187 if m:
188 if gp:
188 if gp:
189 gitpatches.append(gp)
189 gitpatches.append(gp)
190 src, dst = m.group(1, 2)
190 src, dst = m.group(1, 2)
191 gp = patchmeta(dst)
191 gp = patchmeta(dst)
192 gp.lineno = lineno
192 gp.lineno = lineno
193 elif gp:
193 elif gp:
194 if line.startswith('--- '):
194 if line.startswith('--- '):
195 if gp.op in ('COPY', 'RENAME'):
195 if gp.op in ('COPY', 'RENAME'):
196 dopatch |= GP_FILTER
196 dopatch |= GP_FILTER
197 gitpatches.append(gp)
197 gitpatches.append(gp)
198 gp = None
198 gp = None
199 dopatch |= GP_PATCH
199 dopatch |= GP_PATCH
200 continue
200 continue
201 if line.startswith('rename from '):
201 if line.startswith('rename from '):
202 gp.op = 'RENAME'
202 gp.op = 'RENAME'
203 gp.oldpath = line[12:].rstrip()
203 gp.oldpath = line[12:].rstrip()
204 elif line.startswith('rename to '):
204 elif line.startswith('rename to '):
205 gp.path = line[10:].rstrip()
205 gp.path = line[10:].rstrip()
206 elif line.startswith('copy from '):
206 elif line.startswith('copy from '):
207 gp.op = 'COPY'
207 gp.op = 'COPY'
208 gp.oldpath = line[10:].rstrip()
208 gp.oldpath = line[10:].rstrip()
209 elif line.startswith('copy to '):
209 elif line.startswith('copy to '):
210 gp.path = line[8:].rstrip()
210 gp.path = line[8:].rstrip()
211 elif line.startswith('deleted file'):
211 elif line.startswith('deleted file'):
212 gp.op = 'DELETE'
212 gp.op = 'DELETE'
213 # is the deleted file a symlink?
213 # is the deleted file a symlink?
214 gp.setmode(int(line.rstrip()[-6:], 8))
214 gp.setmode(int(line.rstrip()[-6:], 8))
215 elif line.startswith('new file mode '):
215 elif line.startswith('new file mode '):
216 gp.op = 'ADD'
216 gp.op = 'ADD'
217 gp.setmode(int(line.rstrip()[-6:], 8))
217 gp.setmode(int(line.rstrip()[-6:], 8))
218 elif line.startswith('new mode '):
218 elif line.startswith('new mode '):
219 gp.setmode(int(line.rstrip()[-6:], 8))
219 gp.setmode(int(line.rstrip()[-6:], 8))
220 elif line.startswith('GIT binary patch'):
220 elif line.startswith('GIT binary patch'):
221 dopatch |= GP_BINARY
221 dopatch |= GP_BINARY
222 gp.binary = True
222 gp.binary = True
223 if gp:
223 if gp:
224 gitpatches.append(gp)
224 gitpatches.append(gp)
225
225
226 if not gitpatches:
226 if not gitpatches:
227 dopatch = GP_PATCH
227 dopatch = GP_PATCH
228
228
229 return (dopatch, gitpatches)
229 return (dopatch, gitpatches)
230
230
231 class linereader:
232 # simple class to allow pushing lines back into the input stream
233 def __init__(self, fp, textmode=False):
234 self.fp = fp
235 self.buf = []
236 self.textmode = textmode
237
238 def push(self, line):
239 if line is not None:
240 self.buf.append(line)
241
242 def readline(self):
243 if self.buf:
244 l = self.buf[0]
245 del self.buf[0]
246 return l
247 l = self.fp.readline()
248 if self.textmode and l.endswith('\r\n'):
249 l = l[:-2] + '\n'
250 return l
251
252 def __iter__(self):
253 while 1:
254 l = self.readline()
255 if not l:
256 break
257 yield l
258
231 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
259 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
232 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
260 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
233 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
261 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
234
262
235 class patchfile(object):
263 class patchfile(object):
236 def __init__(self, ui, fname, opener, missing=False):
264 def __init__(self, ui, fname, opener, missing=False, eol=None):
237 self.fname = fname
265 self.fname = fname
266 self.eol = eol
238 self.opener = opener
267 self.opener = opener
239 self.ui = ui
268 self.ui = ui
240 self.lines = []
269 self.lines = []
241 self.exists = False
270 self.exists = False
242 self.missing = missing
271 self.missing = missing
243 if not missing:
272 if not missing:
244 try:
273 try:
245 self.lines = self.readlines(fname)
274 self.lines = self.readlines(fname)
246 self.exists = True
275 self.exists = True
247 except IOError:
276 except IOError:
248 pass
277 pass
249 else:
278 else:
250 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
279 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
251
280
252 self.hash = {}
281 self.hash = {}
253 self.dirty = 0
282 self.dirty = 0
254 self.offset = 0
283 self.offset = 0
255 self.rej = []
284 self.rej = []
256 self.fileprinted = False
285 self.fileprinted = False
257 self.printfile(False)
286 self.printfile(False)
258 self.hunks = 0
287 self.hunks = 0
259
288
260 def readlines(self, fname):
289 def readlines(self, fname):
261 fp = self.opener(fname, 'r')
290 fp = self.opener(fname, 'r')
262 try:
291 try:
263 return fp.readlines()
292 return list(linereader(fp, self.eol is not None))
264 finally:
293 finally:
265 fp.close()
294 fp.close()
266
295
267 def writelines(self, fname, lines):
296 def writelines(self, fname, lines):
268 fp = self.opener(fname, 'w')
297 fp = self.opener(fname, 'w')
269 try:
298 try:
299 if self.eol and self.eol != '\n':
300 for l in lines:
301 if l and l[-1] == '\n':
302 l = l[:1] + self.eol
303 fp.write(l)
304 else:
270 fp.writelines(lines)
305 fp.writelines(lines)
271 finally:
306 finally:
272 fp.close()
307 fp.close()
273
308
274 def unlink(self, fname):
309 def unlink(self, fname):
275 os.unlink(fname)
310 os.unlink(fname)
276
311
277 def printfile(self, warn):
312 def printfile(self, warn):
278 if self.fileprinted:
313 if self.fileprinted:
279 return
314 return
280 if warn or self.ui.verbose:
315 if warn or self.ui.verbose:
281 self.fileprinted = True
316 self.fileprinted = True
282 s = _("patching file %s\n") % self.fname
317 s = _("patching file %s\n") % self.fname
283 if warn:
318 if warn:
284 self.ui.warn(s)
319 self.ui.warn(s)
285 else:
320 else:
286 self.ui.note(s)
321 self.ui.note(s)
287
322
288
323
289 def findlines(self, l, linenum):
324 def findlines(self, l, linenum):
290 # looks through the hash and finds candidate lines. The
325 # looks through the hash and finds candidate lines. The
291 # result is a list of line numbers sorted based on distance
326 # result is a list of line numbers sorted based on distance
292 # from linenum
327 # from linenum
293 def sorter(a, b):
328 def sorter(a, b):
294 vala = abs(a - linenum)
329 vala = abs(a - linenum)
295 valb = abs(b - linenum)
330 valb = abs(b - linenum)
296 return cmp(vala, valb)
331 return cmp(vala, valb)
297
332
298 try:
333 try:
299 cand = self.hash[l]
334 cand = self.hash[l]
300 except:
335 except:
301 return []
336 return []
302
337
303 if len(cand) > 1:
338 if len(cand) > 1:
304 # resort our list of potentials forward then back.
339 # resort our list of potentials forward then back.
305 cand.sort(sorter)
340 cand.sort(sorter)
306 return cand
341 return cand
307
342
308 def hashlines(self):
343 def hashlines(self):
309 self.hash = {}
344 self.hash = {}
310 for x, s in enumerate(self.lines):
345 for x, s in enumerate(self.lines):
311 self.hash.setdefault(s, []).append(x)
346 self.hash.setdefault(s, []).append(x)
312
347
313 def write_rej(self):
348 def write_rej(self):
314 # our rejects are a little different from patch(1). This always
349 # our rejects are a little different from patch(1). This always
315 # creates rejects in the same form as the original patch. A file
350 # creates rejects in the same form as the original patch. A file
316 # header is inserted so that you can run the reject through patch again
351 # header is inserted so that you can run the reject through patch again
317 # without having to type the filename.
352 # without having to type the filename.
318
353
319 if not self.rej:
354 if not self.rej:
320 return
355 return
321
356
322 fname = self.fname + ".rej"
357 fname = self.fname + ".rej"
323 self.ui.warn(
358 self.ui.warn(
324 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
359 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
325 (len(self.rej), self.hunks, fname))
360 (len(self.rej), self.hunks, fname))
326
361
327 def rejlines():
362 def rejlines():
328 base = os.path.basename(self.fname)
363 base = os.path.basename(self.fname)
329 yield "--- %s\n+++ %s\n" % (base, base)
364 yield "--- %s\n+++ %s\n" % (base, base)
330 for x in self.rej:
365 for x in self.rej:
331 for l in x.hunk:
366 for l in x.hunk:
332 yield l
367 yield l
333 if l[-1] != '\n':
368 if l[-1] != '\n':
334 yield "\n\ No newline at end of file\n"
369 yield "\n\ No newline at end of file\n"
335
370
336 self.writelines(fname, rejlines())
371 self.writelines(fname, rejlines())
337
372
338 def write(self, dest=None):
373 def write(self, dest=None):
339 if not self.dirty:
374 if not self.dirty:
340 return
375 return
341 if not dest:
376 if not dest:
342 dest = self.fname
377 dest = self.fname
343 self.writelines(dest, self.lines)
378 self.writelines(dest, self.lines)
344
379
345 def close(self):
380 def close(self):
346 self.write()
381 self.write()
347 self.write_rej()
382 self.write_rej()
348
383
349 def apply(self, h, reverse):
384 def apply(self, h, reverse):
350 if not h.complete():
385 if not h.complete():
351 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
386 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
352 (h.number, h.desc, len(h.a), h.lena, len(h.b),
387 (h.number, h.desc, len(h.a), h.lena, len(h.b),
353 h.lenb))
388 h.lenb))
354
389
355 self.hunks += 1
390 self.hunks += 1
356 if reverse:
391 if reverse:
357 h.reverse()
392 h.reverse()
358
393
359 if self.missing:
394 if self.missing:
360 self.rej.append(h)
395 self.rej.append(h)
361 return -1
396 return -1
362
397
363 if self.exists and h.createfile():
398 if self.exists and h.createfile():
364 self.ui.warn(_("file %s already exists\n") % self.fname)
399 self.ui.warn(_("file %s already exists\n") % self.fname)
365 self.rej.append(h)
400 self.rej.append(h)
366 return -1
401 return -1
367
402
368 if isinstance(h, githunk):
403 if isinstance(h, githunk):
369 if h.rmfile():
404 if h.rmfile():
370 self.unlink(self.fname)
405 self.unlink(self.fname)
371 else:
406 else:
372 self.lines[:] = h.new()
407 self.lines[:] = h.new()
373 self.offset += len(h.new())
408 self.offset += len(h.new())
374 self.dirty = 1
409 self.dirty = 1
375 return 0
410 return 0
376
411
377 # fast case first, no offsets, no fuzz
412 # fast case first, no offsets, no fuzz
378 old = h.old()
413 old = h.old()
379 # patch starts counting at 1 unless we are adding the file
414 # patch starts counting at 1 unless we are adding the file
380 if h.starta == 0:
415 if h.starta == 0:
381 start = 0
416 start = 0
382 else:
417 else:
383 start = h.starta + self.offset - 1
418 start = h.starta + self.offset - 1
384 orig_start = start
419 orig_start = start
385 if diffhelpers.testhunk(old, self.lines, start) == 0:
420 if diffhelpers.testhunk(old, self.lines, start) == 0:
386 if h.rmfile():
421 if h.rmfile():
387 self.unlink(self.fname)
422 self.unlink(self.fname)
388 else:
423 else:
389 self.lines[start : start + h.lena] = h.new()
424 self.lines[start : start + h.lena] = h.new()
390 self.offset += h.lenb - h.lena
425 self.offset += h.lenb - h.lena
391 self.dirty = 1
426 self.dirty = 1
392 return 0
427 return 0
393
428
394 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
429 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
395 self.hashlines()
430 self.hashlines()
396 if h.hunk[-1][0] != ' ':
431 if h.hunk[-1][0] != ' ':
397 # if the hunk tried to put something at the bottom of the file
432 # if the hunk tried to put something at the bottom of the file
398 # override the start line and use eof here
433 # override the start line and use eof here
399 search_start = len(self.lines)
434 search_start = len(self.lines)
400 else:
435 else:
401 search_start = orig_start
436 search_start = orig_start
402
437
403 for fuzzlen in xrange(3):
438 for fuzzlen in xrange(3):
404 for toponly in [ True, False ]:
439 for toponly in [ True, False ]:
405 old = h.old(fuzzlen, toponly)
440 old = h.old(fuzzlen, toponly)
406
441
407 cand = self.findlines(old[0][1:], search_start)
442 cand = self.findlines(old[0][1:], search_start)
408 for l in cand:
443 for l in cand:
409 if diffhelpers.testhunk(old, self.lines, l) == 0:
444 if diffhelpers.testhunk(old, self.lines, l) == 0:
410 newlines = h.new(fuzzlen, toponly)
445 newlines = h.new(fuzzlen, toponly)
411 self.lines[l : l + len(old)] = newlines
446 self.lines[l : l + len(old)] = newlines
412 self.offset += len(newlines) - len(old)
447 self.offset += len(newlines) - len(old)
413 self.dirty = 1
448 self.dirty = 1
414 if fuzzlen:
449 if fuzzlen:
415 fuzzstr = "with fuzz %d " % fuzzlen
450 fuzzstr = "with fuzz %d " % fuzzlen
416 f = self.ui.warn
451 f = self.ui.warn
417 self.printfile(True)
452 self.printfile(True)
418 else:
453 else:
419 fuzzstr = ""
454 fuzzstr = ""
420 f = self.ui.note
455 f = self.ui.note
421 offset = l - orig_start - fuzzlen
456 offset = l - orig_start - fuzzlen
422 if offset == 1:
457 if offset == 1:
423 msg = _("Hunk #%d succeeded at %d %s"
458 msg = _("Hunk #%d succeeded at %d %s"
424 "(offset %d line).\n")
459 "(offset %d line).\n")
425 else:
460 else:
426 msg = _("Hunk #%d succeeded at %d %s"
461 msg = _("Hunk #%d succeeded at %d %s"
427 "(offset %d lines).\n")
462 "(offset %d lines).\n")
428 f(msg % (h.number, l+1, fuzzstr, offset))
463 f(msg % (h.number, l+1, fuzzstr, offset))
429 return fuzzlen
464 return fuzzlen
430 self.printfile(True)
465 self.printfile(True)
431 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
466 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
432 self.rej.append(h)
467 self.rej.append(h)
433 return -1
468 return -1
434
469
435 class hunk(object):
470 class hunk(object):
436 def __init__(self, desc, num, lr, context, create=False, remove=False):
471 def __init__(self, desc, num, lr, context, create=False, remove=False):
437 self.number = num
472 self.number = num
438 self.desc = desc
473 self.desc = desc
439 self.hunk = [ desc ]
474 self.hunk = [ desc ]
440 self.a = []
475 self.a = []
441 self.b = []
476 self.b = []
442 if context:
477 if context:
443 self.read_context_hunk(lr)
478 self.read_context_hunk(lr)
444 else:
479 else:
445 self.read_unified_hunk(lr)
480 self.read_unified_hunk(lr)
446 self.create = create
481 self.create = create
447 self.remove = remove and not create
482 self.remove = remove and not create
448
483
449 def read_unified_hunk(self, lr):
484 def read_unified_hunk(self, lr):
450 m = unidesc.match(self.desc)
485 m = unidesc.match(self.desc)
451 if not m:
486 if not m:
452 raise PatchError(_("bad hunk #%d") % self.number)
487 raise PatchError(_("bad hunk #%d") % self.number)
453 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
488 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
454 if self.lena is None:
489 if self.lena is None:
455 self.lena = 1
490 self.lena = 1
456 else:
491 else:
457 self.lena = int(self.lena)
492 self.lena = int(self.lena)
458 if self.lenb is None:
493 if self.lenb is None:
459 self.lenb = 1
494 self.lenb = 1
460 else:
495 else:
461 self.lenb = int(self.lenb)
496 self.lenb = int(self.lenb)
462 self.starta = int(self.starta)
497 self.starta = int(self.starta)
463 self.startb = int(self.startb)
498 self.startb = int(self.startb)
464 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
499 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
465 # if we hit eof before finishing out the hunk, the last line will
500 # if we hit eof before finishing out the hunk, the last line will
466 # be zero length. Lets try to fix it up.
501 # be zero length. Lets try to fix it up.
467 while len(self.hunk[-1]) == 0:
502 while len(self.hunk[-1]) == 0:
468 del self.hunk[-1]
503 del self.hunk[-1]
469 del self.a[-1]
504 del self.a[-1]
470 del self.b[-1]
505 del self.b[-1]
471 self.lena -= 1
506 self.lena -= 1
472 self.lenb -= 1
507 self.lenb -= 1
473
508
474 def read_context_hunk(self, lr):
509 def read_context_hunk(self, lr):
475 self.desc = lr.readline()
510 self.desc = lr.readline()
476 m = contextdesc.match(self.desc)
511 m = contextdesc.match(self.desc)
477 if not m:
512 if not m:
478 raise PatchError(_("bad hunk #%d") % self.number)
513 raise PatchError(_("bad hunk #%d") % self.number)
479 foo, self.starta, foo2, aend, foo3 = m.groups()
514 foo, self.starta, foo2, aend, foo3 = m.groups()
480 self.starta = int(self.starta)
515 self.starta = int(self.starta)
481 if aend is None:
516 if aend is None:
482 aend = self.starta
517 aend = self.starta
483 self.lena = int(aend) - self.starta
518 self.lena = int(aend) - self.starta
484 if self.starta:
519 if self.starta:
485 self.lena += 1
520 self.lena += 1
486 for x in xrange(self.lena):
521 for x in xrange(self.lena):
487 l = lr.readline()
522 l = lr.readline()
488 if l.startswith('---'):
523 if l.startswith('---'):
489 lr.push(l)
524 lr.push(l)
490 break
525 break
491 s = l[2:]
526 s = l[2:]
492 if l.startswith('- ') or l.startswith('! '):
527 if l.startswith('- ') or l.startswith('! '):
493 u = '-' + s
528 u = '-' + s
494 elif l.startswith(' '):
529 elif l.startswith(' '):
495 u = ' ' + s
530 u = ' ' + s
496 else:
531 else:
497 raise PatchError(_("bad hunk #%d old text line %d") %
532 raise PatchError(_("bad hunk #%d old text line %d") %
498 (self.number, x))
533 (self.number, x))
499 self.a.append(u)
534 self.a.append(u)
500 self.hunk.append(u)
535 self.hunk.append(u)
501
536
502 l = lr.readline()
537 l = lr.readline()
503 if l.startswith('\ '):
538 if l.startswith('\ '):
504 s = self.a[-1][:-1]
539 s = self.a[-1][:-1]
505 self.a[-1] = s
540 self.a[-1] = s
506 self.hunk[-1] = s
541 self.hunk[-1] = s
507 l = lr.readline()
542 l = lr.readline()
508 m = contextdesc.match(l)
543 m = contextdesc.match(l)
509 if not m:
544 if not m:
510 raise PatchError(_("bad hunk #%d") % self.number)
545 raise PatchError(_("bad hunk #%d") % self.number)
511 foo, self.startb, foo2, bend, foo3 = m.groups()
546 foo, self.startb, foo2, bend, foo3 = m.groups()
512 self.startb = int(self.startb)
547 self.startb = int(self.startb)
513 if bend is None:
548 if bend is None:
514 bend = self.startb
549 bend = self.startb
515 self.lenb = int(bend) - self.startb
550 self.lenb = int(bend) - self.startb
516 if self.startb:
551 if self.startb:
517 self.lenb += 1
552 self.lenb += 1
518 hunki = 1
553 hunki = 1
519 for x in xrange(self.lenb):
554 for x in xrange(self.lenb):
520 l = lr.readline()
555 l = lr.readline()
521 if l.startswith('\ '):
556 if l.startswith('\ '):
522 s = self.b[-1][:-1]
557 s = self.b[-1][:-1]
523 self.b[-1] = s
558 self.b[-1] = s
524 self.hunk[hunki-1] = s
559 self.hunk[hunki-1] = s
525 continue
560 continue
526 if not l:
561 if not l:
527 lr.push(l)
562 lr.push(l)
528 break
563 break
529 s = l[2:]
564 s = l[2:]
530 if l.startswith('+ ') or l.startswith('! '):
565 if l.startswith('+ ') or l.startswith('! '):
531 u = '+' + s
566 u = '+' + s
532 elif l.startswith(' '):
567 elif l.startswith(' '):
533 u = ' ' + s
568 u = ' ' + s
534 elif len(self.b) == 0:
569 elif len(self.b) == 0:
535 # this can happen when the hunk does not add any lines
570 # this can happen when the hunk does not add any lines
536 lr.push(l)
571 lr.push(l)
537 break
572 break
538 else:
573 else:
539 raise PatchError(_("bad hunk #%d old text line %d") %
574 raise PatchError(_("bad hunk #%d old text line %d") %
540 (self.number, x))
575 (self.number, x))
541 self.b.append(s)
576 self.b.append(s)
542 while True:
577 while True:
543 if hunki >= len(self.hunk):
578 if hunki >= len(self.hunk):
544 h = ""
579 h = ""
545 else:
580 else:
546 h = self.hunk[hunki]
581 h = self.hunk[hunki]
547 hunki += 1
582 hunki += 1
548 if h == u:
583 if h == u:
549 break
584 break
550 elif h.startswith('-'):
585 elif h.startswith('-'):
551 continue
586 continue
552 else:
587 else:
553 self.hunk.insert(hunki-1, u)
588 self.hunk.insert(hunki-1, u)
554 break
589 break
555
590
556 if not self.a:
591 if not self.a:
557 # this happens when lines were only added to the hunk
592 # this happens when lines were only added to the hunk
558 for x in self.hunk:
593 for x in self.hunk:
559 if x.startswith('-') or x.startswith(' '):
594 if x.startswith('-') or x.startswith(' '):
560 self.a.append(x)
595 self.a.append(x)
561 if not self.b:
596 if not self.b:
562 # this happens when lines were only deleted from the hunk
597 # this happens when lines were only deleted from the hunk
563 for x in self.hunk:
598 for x in self.hunk:
564 if x.startswith('+') or x.startswith(' '):
599 if x.startswith('+') or x.startswith(' '):
565 self.b.append(x[1:])
600 self.b.append(x[1:])
566 # @@ -start,len +start,len @@
601 # @@ -start,len +start,len @@
567 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
602 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
568 self.startb, self.lenb)
603 self.startb, self.lenb)
569 self.hunk[0] = self.desc
604 self.hunk[0] = self.desc
570
605
571 def reverse(self):
606 def reverse(self):
572 self.create, self.remove = self.remove, self.create
607 self.create, self.remove = self.remove, self.create
573 origlena = self.lena
608 origlena = self.lena
574 origstarta = self.starta
609 origstarta = self.starta
575 self.lena = self.lenb
610 self.lena = self.lenb
576 self.starta = self.startb
611 self.starta = self.startb
577 self.lenb = origlena
612 self.lenb = origlena
578 self.startb = origstarta
613 self.startb = origstarta
579 self.a = []
614 self.a = []
580 self.b = []
615 self.b = []
581 # self.hunk[0] is the @@ description
616 # self.hunk[0] is the @@ description
582 for x in xrange(1, len(self.hunk)):
617 for x in xrange(1, len(self.hunk)):
583 o = self.hunk[x]
618 o = self.hunk[x]
584 if o.startswith('-'):
619 if o.startswith('-'):
585 n = '+' + o[1:]
620 n = '+' + o[1:]
586 self.b.append(o[1:])
621 self.b.append(o[1:])
587 elif o.startswith('+'):
622 elif o.startswith('+'):
588 n = '-' + o[1:]
623 n = '-' + o[1:]
589 self.a.append(n)
624 self.a.append(n)
590 else:
625 else:
591 n = o
626 n = o
592 self.b.append(o[1:])
627 self.b.append(o[1:])
593 self.a.append(o)
628 self.a.append(o)
594 self.hunk[x] = o
629 self.hunk[x] = o
595
630
596 def fix_newline(self):
631 def fix_newline(self):
597 diffhelpers.fix_newline(self.hunk, self.a, self.b)
632 diffhelpers.fix_newline(self.hunk, self.a, self.b)
598
633
599 def complete(self):
634 def complete(self):
600 return len(self.a) == self.lena and len(self.b) == self.lenb
635 return len(self.a) == self.lena and len(self.b) == self.lenb
601
636
602 def createfile(self):
637 def createfile(self):
603 return self.starta == 0 and self.lena == 0 and self.create
638 return self.starta == 0 and self.lena == 0 and self.create
604
639
605 def rmfile(self):
640 def rmfile(self):
606 return self.startb == 0 and self.lenb == 0 and self.remove
641 return self.startb == 0 and self.lenb == 0 and self.remove
607
642
608 def fuzzit(self, l, fuzz, toponly):
643 def fuzzit(self, l, fuzz, toponly):
609 # this removes context lines from the top and bottom of list 'l'. It
644 # this removes context lines from the top and bottom of list 'l'. It
610 # checks the hunk to make sure only context lines are removed, and then
645 # checks the hunk to make sure only context lines are removed, and then
611 # returns a new shortened list of lines.
646 # returns a new shortened list of lines.
612 fuzz = min(fuzz, len(l)-1)
647 fuzz = min(fuzz, len(l)-1)
613 if fuzz:
648 if fuzz:
614 top = 0
649 top = 0
615 bot = 0
650 bot = 0
616 hlen = len(self.hunk)
651 hlen = len(self.hunk)
617 for x in xrange(hlen-1):
652 for x in xrange(hlen-1):
618 # the hunk starts with the @@ line, so use x+1
653 # the hunk starts with the @@ line, so use x+1
619 if self.hunk[x+1][0] == ' ':
654 if self.hunk[x+1][0] == ' ':
620 top += 1
655 top += 1
621 else:
656 else:
622 break
657 break
623 if not toponly:
658 if not toponly:
624 for x in xrange(hlen-1):
659 for x in xrange(hlen-1):
625 if self.hunk[hlen-bot-1][0] == ' ':
660 if self.hunk[hlen-bot-1][0] == ' ':
626 bot += 1
661 bot += 1
627 else:
662 else:
628 break
663 break
629
664
630 # top and bot now count context in the hunk
665 # top and bot now count context in the hunk
631 # adjust them if either one is short
666 # adjust them if either one is short
632 context = max(top, bot, 3)
667 context = max(top, bot, 3)
633 if bot < context:
668 if bot < context:
634 bot = max(0, fuzz - (context - bot))
669 bot = max(0, fuzz - (context - bot))
635 else:
670 else:
636 bot = min(fuzz, bot)
671 bot = min(fuzz, bot)
637 if top < context:
672 if top < context:
638 top = max(0, fuzz - (context - top))
673 top = max(0, fuzz - (context - top))
639 else:
674 else:
640 top = min(fuzz, top)
675 top = min(fuzz, top)
641
676
642 return l[top:len(l)-bot]
677 return l[top:len(l)-bot]
643 return l
678 return l
644
679
645 def old(self, fuzz=0, toponly=False):
680 def old(self, fuzz=0, toponly=False):
646 return self.fuzzit(self.a, fuzz, toponly)
681 return self.fuzzit(self.a, fuzz, toponly)
647
682
648 def newctrl(self):
683 def newctrl(self):
649 res = []
684 res = []
650 for x in self.hunk:
685 for x in self.hunk:
651 c = x[0]
686 c = x[0]
652 if c == ' ' or c == '+':
687 if c == ' ' or c == '+':
653 res.append(x)
688 res.append(x)
654 return res
689 return res
655
690
656 def new(self, fuzz=0, toponly=False):
691 def new(self, fuzz=0, toponly=False):
657 return self.fuzzit(self.b, fuzz, toponly)
692 return self.fuzzit(self.b, fuzz, toponly)
658
693
659 class githunk(object):
694 class githunk(object):
660 """A git hunk"""
695 """A git hunk"""
661 def __init__(self, gitpatch):
696 def __init__(self, gitpatch):
662 self.gitpatch = gitpatch
697 self.gitpatch = gitpatch
663 self.text = None
698 self.text = None
664 self.hunk = []
699 self.hunk = []
665
700
666 def createfile(self):
701 def createfile(self):
667 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
702 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
668
703
669 def rmfile(self):
704 def rmfile(self):
670 return self.gitpatch.op == 'DELETE'
705 return self.gitpatch.op == 'DELETE'
671
706
672 def complete(self):
707 def complete(self):
673 return self.text is not None
708 return self.text is not None
674
709
675 def new(self):
710 def new(self):
676 return [self.text]
711 return [self.text]
677
712
678 class binhunk(githunk):
713 class binhunk(githunk):
679 'A binary patch file. Only understands literals so far.'
714 'A binary patch file. Only understands literals so far.'
680 def __init__(self, gitpatch):
715 def __init__(self, gitpatch):
681 super(binhunk, self).__init__(gitpatch)
716 super(binhunk, self).__init__(gitpatch)
682 self.hunk = ['GIT binary patch\n']
717 self.hunk = ['GIT binary patch\n']
683
718
684 def extract(self, lr):
719 def extract(self, lr):
685 line = lr.readline()
720 line = lr.readline()
686 self.hunk.append(line)
721 self.hunk.append(line)
687 while line and not line.startswith('literal '):
722 while line and not line.startswith('literal '):
688 line = lr.readline()
723 line = lr.readline()
689 self.hunk.append(line)
724 self.hunk.append(line)
690 if not line:
725 if not line:
691 raise PatchError(_('could not extract binary patch'))
726 raise PatchError(_('could not extract binary patch'))
692 size = int(line[8:].rstrip())
727 size = int(line[8:].rstrip())
693 dec = []
728 dec = []
694 line = lr.readline()
729 line = lr.readline()
695 self.hunk.append(line)
730 self.hunk.append(line)
696 while len(line) > 1:
731 while len(line) > 1:
697 l = line[0]
732 l = line[0]
698 if l <= 'Z' and l >= 'A':
733 if l <= 'Z' and l >= 'A':
699 l = ord(l) - ord('A') + 1
734 l = ord(l) - ord('A') + 1
700 else:
735 else:
701 l = ord(l) - ord('a') + 27
736 l = ord(l) - ord('a') + 27
702 dec.append(base85.b85decode(line[1:-1])[:l])
737 dec.append(base85.b85decode(line[1:-1])[:l])
703 line = lr.readline()
738 line = lr.readline()
704 self.hunk.append(line)
739 self.hunk.append(line)
705 text = zlib.decompress(''.join(dec))
740 text = zlib.decompress(''.join(dec))
706 if len(text) != size:
741 if len(text) != size:
707 raise PatchError(_('binary patch is %d bytes, not %d') %
742 raise PatchError(_('binary patch is %d bytes, not %d') %
708 len(text), size)
743 len(text), size)
709 self.text = text
744 self.text = text
710
745
711 class symlinkhunk(githunk):
746 class symlinkhunk(githunk):
712 """A git symlink hunk"""
747 """A git symlink hunk"""
713 def __init__(self, gitpatch, hunk):
748 def __init__(self, gitpatch, hunk):
714 super(symlinkhunk, self).__init__(gitpatch)
749 super(symlinkhunk, self).__init__(gitpatch)
715 self.hunk = hunk
750 self.hunk = hunk
716
751
717 def complete(self):
752 def complete(self):
718 return True
753 return True
719
754
720 def fix_newline(self):
755 def fix_newline(self):
721 return
756 return
722
757
723 def parsefilename(str):
758 def parsefilename(str):
724 # --- filename \t|space stuff
759 # --- filename \t|space stuff
725 s = str[4:].rstrip('\r\n')
760 s = str[4:].rstrip('\r\n')
726 i = s.find('\t')
761 i = s.find('\t')
727 if i < 0:
762 if i < 0:
728 i = s.find(' ')
763 i = s.find(' ')
729 if i < 0:
764 if i < 0:
730 return s
765 return s
731 return s[:i]
766 return s[:i]
732
767
733 def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
768 def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
734 def pathstrip(path, count=1):
769 def pathstrip(path, count=1):
735 pathlen = len(path)
770 pathlen = len(path)
736 i = 0
771 i = 0
737 if count == 0:
772 if count == 0:
738 return '', path.rstrip()
773 return '', path.rstrip()
739 while count > 0:
774 while count > 0:
740 i = path.find('/', i)
775 i = path.find('/', i)
741 if i == -1:
776 if i == -1:
742 raise PatchError(_("unable to strip away %d dirs from %s") %
777 raise PatchError(_("unable to strip away %d dirs from %s") %
743 (count, path))
778 (count, path))
744 i += 1
779 i += 1
745 # consume '//' in the path
780 # consume '//' in the path
746 while i < pathlen - 1 and path[i] == '/':
781 while i < pathlen - 1 and path[i] == '/':
747 i += 1
782 i += 1
748 count -= 1
783 count -= 1
749 return path[:i].lstrip(), path[i:].rstrip()
784 return path[:i].lstrip(), path[i:].rstrip()
750
785
751 nulla = afile_orig == "/dev/null"
786 nulla = afile_orig == "/dev/null"
752 nullb = bfile_orig == "/dev/null"
787 nullb = bfile_orig == "/dev/null"
753 abase, afile = pathstrip(afile_orig, strip)
788 abase, afile = pathstrip(afile_orig, strip)
754 gooda = not nulla and util.lexists(afile)
789 gooda = not nulla and util.lexists(afile)
755 bbase, bfile = pathstrip(bfile_orig, strip)
790 bbase, bfile = pathstrip(bfile_orig, strip)
756 if afile == bfile:
791 if afile == bfile:
757 goodb = gooda
792 goodb = gooda
758 else:
793 else:
759 goodb = not nullb and os.path.exists(bfile)
794 goodb = not nullb and os.path.exists(bfile)
760 createfunc = hunk.createfile
795 createfunc = hunk.createfile
761 if reverse:
796 if reverse:
762 createfunc = hunk.rmfile
797 createfunc = hunk.rmfile
763 missing = not goodb and not gooda and not createfunc()
798 missing = not goodb and not gooda and not createfunc()
764 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
799 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
765 # diff is between a file and its backup. In this case, the original
800 # diff is between a file and its backup. In this case, the original
766 # file should be patched (see original mpatch code).
801 # file should be patched (see original mpatch code).
767 isbackup = (abase == bbase and bfile.startswith(afile))
802 isbackup = (abase == bbase and bfile.startswith(afile))
768 fname = None
803 fname = None
769 if not missing:
804 if not missing:
770 if gooda and goodb:
805 if gooda and goodb:
771 fname = isbackup and afile or bfile
806 fname = isbackup and afile or bfile
772 elif gooda:
807 elif gooda:
773 fname = afile
808 fname = afile
774
809
775 if not fname:
810 if not fname:
776 if not nullb:
811 if not nullb:
777 fname = isbackup and afile or bfile
812 fname = isbackup and afile or bfile
778 elif not nulla:
813 elif not nulla:
779 fname = afile
814 fname = afile
780 else:
815 else:
781 raise PatchError(_("undefined source and destination files"))
816 raise PatchError(_("undefined source and destination files"))
782
817
783 return fname, missing
818 return fname, missing
784
819
785 class linereader(object):
786 # simple class to allow pushing lines back into the input stream
787 def __init__(self, fp):
788 self.fp = fp
789 self.buf = []
790
791 def push(self, line):
792 if line is not None:
793 self.buf.append(line)
794
795 def readline(self):
796 if self.buf:
797 return self.buf.pop(0)
798 return self.fp.readline()
799
800 def __iter__(self):
801 while 1:
802 l = self.readline()
803 if not l:
804 break
805 yield l
806
807 def scangitpatch(lr, firstline):
820 def scangitpatch(lr, firstline):
808 """
821 """
809 Git patches can emit:
822 Git patches can emit:
810 - rename a to b
823 - rename a to b
811 - change b
824 - change b
812 - copy a to c
825 - copy a to c
813 - change c
826 - change c
814
827
815 We cannot apply this sequence as-is, the renamed 'a' could not be
828 We cannot apply this sequence as-is, the renamed 'a' could not be
816 found for it would have been renamed already. And we cannot copy
829 found for it would have been renamed already. And we cannot copy
817 from 'b' instead because 'b' would have been changed already. So
830 from 'b' instead because 'b' would have been changed already. So
818 we scan the git patch for copy and rename commands so we can
831 we scan the git patch for copy and rename commands so we can
819 perform the copies ahead of time.
832 perform the copies ahead of time.
820 """
833 """
821 pos = 0
834 pos = 0
822 try:
835 try:
823 pos = lr.fp.tell()
836 pos = lr.fp.tell()
824 fp = lr.fp
837 fp = lr.fp
825 except IOError:
838 except IOError:
826 fp = cStringIO.StringIO(lr.fp.read())
839 fp = cStringIO.StringIO(lr.fp.read())
827 gitlr = linereader(fp)
840 gitlr = linereader(fp, lr.textmode)
828 gitlr.push(firstline)
841 gitlr.push(firstline)
829 (dopatch, gitpatches) = readgitpatch(gitlr)
842 (dopatch, gitpatches) = readgitpatch(gitlr)
830 fp.seek(pos)
843 fp.seek(pos)
831 return dopatch, gitpatches
844 return dopatch, gitpatches
832
845
833 def iterhunks(ui, fp, sourcefile=None):
846 def iterhunks(ui, fp, sourcefile=None, textmode=False):
834 """Read a patch and yield the following events:
847 """Read a patch and yield the following events:
835 - ("file", afile, bfile, firsthunk): select a new target file.
848 - ("file", afile, bfile, firsthunk): select a new target file.
836 - ("hunk", hunk): a new hunk is ready to be applied, follows a
849 - ("hunk", hunk): a new hunk is ready to be applied, follows a
837 "file" event.
850 "file" event.
838 - ("git", gitchanges): current diff is in git format, gitchanges
851 - ("git", gitchanges): current diff is in git format, gitchanges
839 maps filenames to gitpatch records. Unique event.
852 maps filenames to gitpatch records. Unique event.
853
854 If textmode is True, input line-endings are normalized to LF.
840 """
855 """
841 changed = {}
856 changed = {}
842 current_hunk = None
857 current_hunk = None
843 afile = ""
858 afile = ""
844 bfile = ""
859 bfile = ""
845 state = None
860 state = None
846 hunknum = 0
861 hunknum = 0
847 emitfile = False
862 emitfile = False
848 git = False
863 git = False
849
864
850 # our states
865 # our states
851 BFILE = 1
866 BFILE = 1
852 context = None
867 context = None
853 lr = linereader(fp)
868 lr = linereader(fp, textmode)
854 dopatch = True
869 dopatch = True
855 # gitworkdone is True if a git operation (copy, rename, ...) was
870 # gitworkdone is True if a git operation (copy, rename, ...) was
856 # performed already for the current file. Useful when the file
871 # performed already for the current file. Useful when the file
857 # section may have no hunk.
872 # section may have no hunk.
858 gitworkdone = False
873 gitworkdone = False
859
874
860 while True:
875 while True:
861 newfile = False
876 newfile = False
862 x = lr.readline()
877 x = lr.readline()
863 if not x:
878 if not x:
864 break
879 break
865 if current_hunk:
880 if current_hunk:
866 if x.startswith('\ '):
881 if x.startswith('\ '):
867 current_hunk.fix_newline()
882 current_hunk.fix_newline()
868 yield 'hunk', current_hunk
883 yield 'hunk', current_hunk
869 current_hunk = None
884 current_hunk = None
870 gitworkdone = False
885 gitworkdone = False
871 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
886 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
872 ((context is not False) and x.startswith('***************')))):
887 ((context is not False) and x.startswith('***************')))):
873 try:
888 try:
874 if context is None and x.startswith('***************'):
889 if context is None and x.startswith('***************'):
875 context = True
890 context = True
876 gpatch = changed.get(bfile)
891 gpatch = changed.get(bfile)
877 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
892 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
878 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
893 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
879 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
894 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
880 if remove:
895 if remove:
881 gpatch = changed.get(afile[2:])
896 gpatch = changed.get(afile[2:])
882 if gpatch and gpatch.mode[0]:
897 if gpatch and gpatch.mode[0]:
883 current_hunk = symlinkhunk(gpatch, current_hunk)
898 current_hunk = symlinkhunk(gpatch, current_hunk)
884 except PatchError, err:
899 except PatchError, err:
885 ui.debug(err)
900 ui.debug(err)
886 current_hunk = None
901 current_hunk = None
887 continue
902 continue
888 hunknum += 1
903 hunknum += 1
889 if emitfile:
904 if emitfile:
890 emitfile = False
905 emitfile = False
891 yield 'file', (afile, bfile, current_hunk)
906 yield 'file', (afile, bfile, current_hunk)
892 elif state == BFILE and x.startswith('GIT binary patch'):
907 elif state == BFILE and x.startswith('GIT binary patch'):
893 current_hunk = binhunk(changed[bfile])
908 current_hunk = binhunk(changed[bfile])
894 hunknum += 1
909 hunknum += 1
895 if emitfile:
910 if emitfile:
896 emitfile = False
911 emitfile = False
897 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
912 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
898 current_hunk.extract(lr)
913 current_hunk.extract(lr)
899 elif x.startswith('diff --git'):
914 elif x.startswith('diff --git'):
900 # check for git diff, scanning the whole patch file if needed
915 # check for git diff, scanning the whole patch file if needed
901 m = gitre.match(x)
916 m = gitre.match(x)
902 if m:
917 if m:
903 afile, bfile = m.group(1, 2)
918 afile, bfile = m.group(1, 2)
904 if not git:
919 if not git:
905 git = True
920 git = True
906 dopatch, gitpatches = scangitpatch(lr, x)
921 dopatch, gitpatches = scangitpatch(lr, x)
907 yield 'git', gitpatches
922 yield 'git', gitpatches
908 for gp in gitpatches:
923 for gp in gitpatches:
909 changed[gp.path] = gp
924 changed[gp.path] = gp
910 # else error?
925 # else error?
911 # copy/rename + modify should modify target, not source
926 # copy/rename + modify should modify target, not source
912 gp = changed.get(bfile)
927 gp = changed.get(bfile)
913 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
928 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
914 afile = bfile
929 afile = bfile
915 gitworkdone = True
930 gitworkdone = True
916 newfile = True
931 newfile = True
917 elif x.startswith('---'):
932 elif x.startswith('---'):
918 # check for a unified diff
933 # check for a unified diff
919 l2 = lr.readline()
934 l2 = lr.readline()
920 if not l2.startswith('+++'):
935 if not l2.startswith('+++'):
921 lr.push(l2)
936 lr.push(l2)
922 continue
937 continue
923 newfile = True
938 newfile = True
924 context = False
939 context = False
925 afile = parsefilename(x)
940 afile = parsefilename(x)
926 bfile = parsefilename(l2)
941 bfile = parsefilename(l2)
927 elif x.startswith('***'):
942 elif x.startswith('***'):
928 # check for a context diff
943 # check for a context diff
929 l2 = lr.readline()
944 l2 = lr.readline()
930 if not l2.startswith('---'):
945 if not l2.startswith('---'):
931 lr.push(l2)
946 lr.push(l2)
932 continue
947 continue
933 l3 = lr.readline()
948 l3 = lr.readline()
934 lr.push(l3)
949 lr.push(l3)
935 if not l3.startswith("***************"):
950 if not l3.startswith("***************"):
936 lr.push(l2)
951 lr.push(l2)
937 continue
952 continue
938 newfile = True
953 newfile = True
939 context = True
954 context = True
940 afile = parsefilename(x)
955 afile = parsefilename(x)
941 bfile = parsefilename(l2)
956 bfile = parsefilename(l2)
942
957
943 if newfile:
958 if newfile:
944 emitfile = True
959 emitfile = True
945 state = BFILE
960 state = BFILE
946 hunknum = 0
961 hunknum = 0
947 if current_hunk:
962 if current_hunk:
948 if current_hunk.complete():
963 if current_hunk.complete():
949 yield 'hunk', current_hunk
964 yield 'hunk', current_hunk
950 else:
965 else:
951 raise PatchError(_("malformed patch %s %s") % (afile,
966 raise PatchError(_("malformed patch %s %s") % (afile,
952 current_hunk.desc))
967 current_hunk.desc))
953
968
954 if hunknum == 0 and dopatch and not gitworkdone:
969 if hunknum == 0 and dopatch and not gitworkdone:
955 raise NoHunks
970 raise NoHunks
956
971
957 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False):
972 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
958 """reads a patch from fp and tries to apply it. The dict 'changed' is
973 eol=None):
959 filled in with all of the filenames changed by the patch. Returns 0
974 """
960 for a clean patch, -1 if any rejects were found and 1 if there was
975 Reads a patch from fp and tries to apply it.
961 any fuzz."""
962
976
977 The dict 'changed' is filled in with all of the filenames changed
978 by the patch. Returns 0 for a clean patch, -1 if any rejects were
979 found and 1 if there was any fuzz.
980
981 If 'eol' is None, the patch content and patched file are read in
982 binary mode. Otherwise, line endings are ignored when patching then
983 normalized to 'eol' (usually '\n' or \r\n').
984 """
963 rejects = 0
985 rejects = 0
964 err = 0
986 err = 0
965 current_file = None
987 current_file = None
966 gitpatches = None
988 gitpatches = None
967 opener = util.opener(os.getcwd())
989 opener = util.opener(os.getcwd())
990 textmode = eol is not None
968
991
969 def closefile():
992 def closefile():
970 if not current_file:
993 if not current_file:
971 return 0
994 return 0
972 current_file.close()
995 current_file.close()
973 return len(current_file.rej)
996 return len(current_file.rej)
974
997
975 for state, values in iterhunks(ui, fp, sourcefile):
998 for state, values in iterhunks(ui, fp, sourcefile, textmode):
976 if state == 'hunk':
999 if state == 'hunk':
977 if not current_file:
1000 if not current_file:
978 continue
1001 continue
979 current_hunk = values
1002 current_hunk = values
980 ret = current_file.apply(current_hunk, reverse)
1003 ret = current_file.apply(current_hunk, reverse)
981 if ret >= 0:
1004 if ret >= 0:
982 changed.setdefault(current_file.fname, None)
1005 changed.setdefault(current_file.fname, None)
983 if ret > 0:
1006 if ret > 0:
984 err = 1
1007 err = 1
985 elif state == 'file':
1008 elif state == 'file':
986 rejects += closefile()
1009 rejects += closefile()
987 afile, bfile, first_hunk = values
1010 afile, bfile, first_hunk = values
988 try:
1011 try:
989 if sourcefile:
1012 if sourcefile:
990 current_file = patchfile(ui, sourcefile, opener)
1013 current_file = patchfile(ui, sourcefile, opener, eol=eol)
991 else:
1014 else:
992 current_file, missing = selectfile(afile, bfile, first_hunk,
1015 current_file, missing = selectfile(afile, bfile, first_hunk,
993 strip, reverse)
1016 strip, reverse)
994 current_file = patchfile(ui, current_file, opener, missing)
1017 current_file = patchfile(ui, current_file, opener, missing, eol)
995 except PatchError, err:
1018 except PatchError, err:
996 ui.warn(str(err) + '\n')
1019 ui.warn(str(err) + '\n')
997 current_file, current_hunk = None, None
1020 current_file, current_hunk = None, None
998 rejects += 1
1021 rejects += 1
999 continue
1022 continue
1000 elif state == 'git':
1023 elif state == 'git':
1001 gitpatches = values
1024 gitpatches = values
1002 cwd = os.getcwd()
1025 cwd = os.getcwd()
1003 for gp in gitpatches:
1026 for gp in gitpatches:
1004 if gp.op in ('COPY', 'RENAME'):
1027 if gp.op in ('COPY', 'RENAME'):
1005 copyfile(gp.oldpath, gp.path, cwd)
1028 copyfile(gp.oldpath, gp.path, cwd)
1006 changed[gp.path] = gp
1029 changed[gp.path] = gp
1007 else:
1030 else:
1008 raise util.Abort(_('unsupported parser state: %s') % state)
1031 raise util.Abort(_('unsupported parser state: %s') % state)
1009
1032
1010 rejects += closefile()
1033 rejects += closefile()
1011
1034
1012 if rejects:
1035 if rejects:
1013 return -1
1036 return -1
1014 return err
1037 return err
1015
1038
1016 def diffopts(ui, opts={}, untrusted=False):
1039 def diffopts(ui, opts={}, untrusted=False):
1017 def get(key, name=None, getter=ui.configbool):
1040 def get(key, name=None, getter=ui.configbool):
1018 return (opts.get(key) or
1041 return (opts.get(key) or
1019 getter('diff', name or key, None, untrusted=untrusted))
1042 getter('diff', name or key, None, untrusted=untrusted))
1020 return mdiff.diffopts(
1043 return mdiff.diffopts(
1021 text=opts.get('text'),
1044 text=opts.get('text'),
1022 git=get('git'),
1045 git=get('git'),
1023 nodates=get('nodates'),
1046 nodates=get('nodates'),
1024 showfunc=get('show_function', 'showfunc'),
1047 showfunc=get('show_function', 'showfunc'),
1025 ignorews=get('ignore_all_space', 'ignorews'),
1048 ignorews=get('ignore_all_space', 'ignorews'),
1026 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1049 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1027 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1050 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1028 context=get('unified', getter=ui.config))
1051 context=get('unified', getter=ui.config))
1029
1052
1030 def updatedir(ui, repo, patches, similarity=0):
1053 def updatedir(ui, repo, patches, similarity=0):
1031 '''Update dirstate after patch application according to metadata'''
1054 '''Update dirstate after patch application according to metadata'''
1032 if not patches:
1055 if not patches:
1033 return
1056 return
1034 copies = []
1057 copies = []
1035 removes = set()
1058 removes = set()
1036 cfiles = patches.keys()
1059 cfiles = patches.keys()
1037 cwd = repo.getcwd()
1060 cwd = repo.getcwd()
1038 if cwd:
1061 if cwd:
1039 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1062 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1040 for f in patches:
1063 for f in patches:
1041 gp = patches[f]
1064 gp = patches[f]
1042 if not gp:
1065 if not gp:
1043 continue
1066 continue
1044 if gp.op == 'RENAME':
1067 if gp.op == 'RENAME':
1045 copies.append((gp.oldpath, gp.path))
1068 copies.append((gp.oldpath, gp.path))
1046 removes.add(gp.oldpath)
1069 removes.add(gp.oldpath)
1047 elif gp.op == 'COPY':
1070 elif gp.op == 'COPY':
1048 copies.append((gp.oldpath, gp.path))
1071 copies.append((gp.oldpath, gp.path))
1049 elif gp.op == 'DELETE':
1072 elif gp.op == 'DELETE':
1050 removes.add(gp.path)
1073 removes.add(gp.path)
1051 for src, dst in copies:
1074 for src, dst in copies:
1052 repo.copy(src, dst)
1075 repo.copy(src, dst)
1053 if (not similarity) and removes:
1076 if (not similarity) and removes:
1054 repo.remove(sorted(removes), True)
1077 repo.remove(sorted(removes), True)
1055 for f in patches:
1078 for f in patches:
1056 gp = patches[f]
1079 gp = patches[f]
1057 if gp and gp.mode:
1080 if gp and gp.mode:
1058 islink, isexec = gp.mode
1081 islink, isexec = gp.mode
1059 dst = repo.wjoin(gp.path)
1082 dst = repo.wjoin(gp.path)
1060 # patch won't create empty files
1083 # patch won't create empty files
1061 if gp.op == 'ADD' and not os.path.exists(dst):
1084 if gp.op == 'ADD' and not os.path.exists(dst):
1062 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1085 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1063 repo.wwrite(gp.path, '', flags)
1086 repo.wwrite(gp.path, '', flags)
1064 elif gp.op != 'DELETE':
1087 elif gp.op != 'DELETE':
1065 util.set_flags(dst, islink, isexec)
1088 util.set_flags(dst, islink, isexec)
1066 cmdutil.addremove(repo, cfiles, similarity=similarity)
1089 cmdutil.addremove(repo, cfiles, similarity=similarity)
1067 files = patches.keys()
1090 files = patches.keys()
1068 files.extend([r for r in removes if r not in files])
1091 files.extend([r for r in removes if r not in files])
1069 return sorted(files)
1092 return sorted(files)
1070
1093
1071 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1094 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1072 """use <patcher> to apply <patchname> to the working directory.
1095 """use <patcher> to apply <patchname> to the working directory.
1073 returns whether patch was applied with fuzz factor."""
1096 returns whether patch was applied with fuzz factor."""
1074
1097
1075 fuzz = False
1098 fuzz = False
1076 if cwd:
1099 if cwd:
1077 args.append('-d %s' % util.shellquote(cwd))
1100 args.append('-d %s' % util.shellquote(cwd))
1078 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1101 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1079 util.shellquote(patchname)))
1102 util.shellquote(patchname)))
1080
1103
1081 for line in fp:
1104 for line in fp:
1082 line = line.rstrip()
1105 line = line.rstrip()
1083 ui.note(line + '\n')
1106 ui.note(line + '\n')
1084 if line.startswith('patching file '):
1107 if line.startswith('patching file '):
1085 pf = util.parse_patch_output(line)
1108 pf = util.parse_patch_output(line)
1086 printed_file = False
1109 printed_file = False
1087 files.setdefault(pf, None)
1110 files.setdefault(pf, None)
1088 elif line.find('with fuzz') >= 0:
1111 elif line.find('with fuzz') >= 0:
1089 fuzz = True
1112 fuzz = True
1090 if not printed_file:
1113 if not printed_file:
1091 ui.warn(pf + '\n')
1114 ui.warn(pf + '\n')
1092 printed_file = True
1115 printed_file = True
1093 ui.warn(line + '\n')
1116 ui.warn(line + '\n')
1094 elif line.find('saving rejects to file') >= 0:
1117 elif line.find('saving rejects to file') >= 0:
1095 ui.warn(line + '\n')
1118 ui.warn(line + '\n')
1096 elif line.find('FAILED') >= 0:
1119 elif line.find('FAILED') >= 0:
1097 if not printed_file:
1120 if not printed_file:
1098 ui.warn(pf + '\n')
1121 ui.warn(pf + '\n')
1099 printed_file = True
1122 printed_file = True
1100 ui.warn(line + '\n')
1123 ui.warn(line + '\n')
1101 code = fp.close()
1124 code = fp.close()
1102 if code:
1125 if code:
1103 raise PatchError(_("patch command failed: %s") %
1126 raise PatchError(_("patch command failed: %s") %
1104 util.explain_exit(code)[0])
1127 util.explain_exit(code)[0])
1105 return fuzz
1128 return fuzz
1106
1129
1107 def internalpatch(patchobj, ui, strip, cwd, files={}):
1130 def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'):
1108 """use builtin patch to apply <patchobj> to the working directory.
1131 """use builtin patch to apply <patchobj> to the working directory.
1109 returns whether patch was applied with fuzz factor."""
1132 returns whether patch was applied with fuzz factor."""
1133
1134 if eolmode is None:
1135 eolmode = ui.config('patch', 'eol', 'strict')
1136 try:
1137 eol = {'strict': None, 'crlf': '\r\n', 'lf': '\n'}[eolmode.lower()]
1138 except KeyError:
1139 raise util.Abort(_('Unsupported line endings type: %s') % eolmode)
1140
1110 try:
1141 try:
1111 fp = file(patchobj, 'rb')
1142 fp = file(patchobj, 'rb')
1112 except TypeError:
1143 except TypeError:
1113 fp = patchobj
1144 fp = patchobj
1114 if cwd:
1145 if cwd:
1115 curdir = os.getcwd()
1146 curdir = os.getcwd()
1116 os.chdir(cwd)
1147 os.chdir(cwd)
1117 try:
1148 try:
1118 ret = applydiff(ui, fp, files, strip=strip)
1149 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1119 finally:
1150 finally:
1120 if cwd:
1151 if cwd:
1121 os.chdir(curdir)
1152 os.chdir(curdir)
1122 if ret < 0:
1153 if ret < 0:
1123 raise PatchError
1154 raise PatchError
1124 return ret > 0
1155 return ret > 0
1125
1156
1126 def patch(patchname, ui, strip=1, cwd=None, files={}):
1157 def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'):
1127 """apply <patchname> to the working directory.
1158 """Apply <patchname> to the working directory.
1128 returns whether patch was applied with fuzz factor."""
1159
1160 'eolmode' specifies how end of lines should be handled. It can be:
1161 - 'strict': inputs are read in binary mode, EOLs are preserved
1162 - 'crlf': EOLs are ignored when patching and reset to CRLF
1163 - 'lf': EOLs are ignored when patching and reset to LF
1164 - None: get it from user settings, default to 'strict'
1165 'eolmode' is ignored when using an external patcher program.
1166
1167 Returns whether patch was applied with fuzz factor.
1168 """
1129 patcher = ui.config('ui', 'patch')
1169 patcher = ui.config('ui', 'patch')
1130 args = []
1170 args = []
1131 try:
1171 try:
1132 if patcher:
1172 if patcher:
1133 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1173 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1134 files)
1174 files)
1135 else:
1175 else:
1136 try:
1176 try:
1137 return internalpatch(patchname, ui, strip, cwd, files)
1177 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1138 except NoHunks:
1178 except NoHunks:
1139 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1179 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1140 ui.debug(_('no valid hunks found; trying with %r instead\n') %
1180 ui.debug(_('no valid hunks found; trying with %r instead\n') %
1141 patcher)
1181 patcher)
1142 if util.needbinarypatch():
1182 if util.needbinarypatch():
1143 args.append('--binary')
1183 args.append('--binary')
1144 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1184 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1145 files)
1185 files)
1146 except PatchError, err:
1186 except PatchError, err:
1147 s = str(err)
1187 s = str(err)
1148 if s:
1188 if s:
1149 raise util.Abort(s)
1189 raise util.Abort(s)
1150 else:
1190 else:
1151 raise util.Abort(_('patch failed to apply'))
1191 raise util.Abort(_('patch failed to apply'))
1152
1192
1153 def b85diff(to, tn):
1193 def b85diff(to, tn):
1154 '''print base85-encoded binary diff'''
1194 '''print base85-encoded binary diff'''
1155 def gitindex(text):
1195 def gitindex(text):
1156 if not text:
1196 if not text:
1157 return '0' * 40
1197 return '0' * 40
1158 l = len(text)
1198 l = len(text)
1159 s = util.sha1('blob %d\0' % l)
1199 s = util.sha1('blob %d\0' % l)
1160 s.update(text)
1200 s.update(text)
1161 return s.hexdigest()
1201 return s.hexdigest()
1162
1202
1163 def fmtline(line):
1203 def fmtline(line):
1164 l = len(line)
1204 l = len(line)
1165 if l <= 26:
1205 if l <= 26:
1166 l = chr(ord('A') + l - 1)
1206 l = chr(ord('A') + l - 1)
1167 else:
1207 else:
1168 l = chr(l - 26 + ord('a') - 1)
1208 l = chr(l - 26 + ord('a') - 1)
1169 return '%c%s\n' % (l, base85.b85encode(line, True))
1209 return '%c%s\n' % (l, base85.b85encode(line, True))
1170
1210
1171 def chunk(text, csize=52):
1211 def chunk(text, csize=52):
1172 l = len(text)
1212 l = len(text)
1173 i = 0
1213 i = 0
1174 while i < l:
1214 while i < l:
1175 yield text[i:i+csize]
1215 yield text[i:i+csize]
1176 i += csize
1216 i += csize
1177
1217
1178 tohash = gitindex(to)
1218 tohash = gitindex(to)
1179 tnhash = gitindex(tn)
1219 tnhash = gitindex(tn)
1180 if tohash == tnhash:
1220 if tohash == tnhash:
1181 return ""
1221 return ""
1182
1222
1183 # TODO: deltas
1223 # TODO: deltas
1184 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1224 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1185 (tohash, tnhash, len(tn))]
1225 (tohash, tnhash, len(tn))]
1186 for l in chunk(zlib.compress(tn)):
1226 for l in chunk(zlib.compress(tn)):
1187 ret.append(fmtline(l))
1227 ret.append(fmtline(l))
1188 ret.append('\n')
1228 ret.append('\n')
1189 return ''.join(ret)
1229 return ''.join(ret)
1190
1230
1191 def _addmodehdr(header, omode, nmode):
1231 def _addmodehdr(header, omode, nmode):
1192 if omode != nmode:
1232 if omode != nmode:
1193 header.append('old mode %s\n' % omode)
1233 header.append('old mode %s\n' % omode)
1194 header.append('new mode %s\n' % nmode)
1234 header.append('new mode %s\n' % nmode)
1195
1235
1196 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1236 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1197 '''yields diff of changes to files between two nodes, or node and
1237 '''yields diff of changes to files between two nodes, or node and
1198 working directory.
1238 working directory.
1199
1239
1200 if node1 is None, use first dirstate parent instead.
1240 if node1 is None, use first dirstate parent instead.
1201 if node2 is None, compare node1 with working directory.'''
1241 if node2 is None, compare node1 with working directory.'''
1202
1242
1203 if opts is None:
1243 if opts is None:
1204 opts = mdiff.defaultopts
1244 opts = mdiff.defaultopts
1205
1245
1206 if not node1:
1246 if not node1:
1207 node1 = repo.dirstate.parents()[0]
1247 node1 = repo.dirstate.parents()[0]
1208
1248
1209 flcache = {}
1249 flcache = {}
1210 def getfilectx(f, ctx):
1250 def getfilectx(f, ctx):
1211 flctx = ctx.filectx(f, filelog=flcache.get(f))
1251 flctx = ctx.filectx(f, filelog=flcache.get(f))
1212 if f not in flcache:
1252 if f not in flcache:
1213 flcache[f] = flctx._filelog
1253 flcache[f] = flctx._filelog
1214 return flctx
1254 return flctx
1215
1255
1216 ctx1 = repo[node1]
1256 ctx1 = repo[node1]
1217 ctx2 = repo[node2]
1257 ctx2 = repo[node2]
1218
1258
1219 if not changes:
1259 if not changes:
1220 changes = repo.status(ctx1, ctx2, match=match)
1260 changes = repo.status(ctx1, ctx2, match=match)
1221 modified, added, removed = changes[:3]
1261 modified, added, removed = changes[:3]
1222
1262
1223 if not modified and not added and not removed:
1263 if not modified and not added and not removed:
1224 return
1264 return
1225
1265
1226 date1 = util.datestr(ctx1.date())
1266 date1 = util.datestr(ctx1.date())
1227 man1 = ctx1.manifest()
1267 man1 = ctx1.manifest()
1228
1268
1229 if repo.ui.quiet:
1269 if repo.ui.quiet:
1230 r = None
1270 r = None
1231 else:
1271 else:
1232 hexfunc = repo.ui.debugflag and hex or short
1272 hexfunc = repo.ui.debugflag and hex or short
1233 r = [hexfunc(node) for node in [node1, node2] if node]
1273 r = [hexfunc(node) for node in [node1, node2] if node]
1234
1274
1235 if opts.git:
1275 if opts.git:
1236 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1276 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1237 copy = copy.copy()
1277 copy = copy.copy()
1238 for k, v in copy.items():
1278 for k, v in copy.items():
1239 copy[v] = k
1279 copy[v] = k
1240
1280
1241 gone = set()
1281 gone = set()
1242 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1282 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1243
1283
1244 for f in sorted(modified + added + removed):
1284 for f in sorted(modified + added + removed):
1245 to = None
1285 to = None
1246 tn = None
1286 tn = None
1247 dodiff = True
1287 dodiff = True
1248 header = []
1288 header = []
1249 if f in man1:
1289 if f in man1:
1250 to = getfilectx(f, ctx1).data()
1290 to = getfilectx(f, ctx1).data()
1251 if f not in removed:
1291 if f not in removed:
1252 tn = getfilectx(f, ctx2).data()
1292 tn = getfilectx(f, ctx2).data()
1253 a, b = f, f
1293 a, b = f, f
1254 if opts.git:
1294 if opts.git:
1255 if f in added:
1295 if f in added:
1256 mode = gitmode[ctx2.flags(f)]
1296 mode = gitmode[ctx2.flags(f)]
1257 if f in copy:
1297 if f in copy:
1258 a = copy[f]
1298 a = copy[f]
1259 omode = gitmode[man1.flags(a)]
1299 omode = gitmode[man1.flags(a)]
1260 _addmodehdr(header, omode, mode)
1300 _addmodehdr(header, omode, mode)
1261 if a in removed and a not in gone:
1301 if a in removed and a not in gone:
1262 op = 'rename'
1302 op = 'rename'
1263 gone.add(a)
1303 gone.add(a)
1264 else:
1304 else:
1265 op = 'copy'
1305 op = 'copy'
1266 header.append('%s from %s\n' % (op, a))
1306 header.append('%s from %s\n' % (op, a))
1267 header.append('%s to %s\n' % (op, f))
1307 header.append('%s to %s\n' % (op, f))
1268 to = getfilectx(a, ctx1).data()
1308 to = getfilectx(a, ctx1).data()
1269 else:
1309 else:
1270 header.append('new file mode %s\n' % mode)
1310 header.append('new file mode %s\n' % mode)
1271 if util.binary(tn):
1311 if util.binary(tn):
1272 dodiff = 'binary'
1312 dodiff = 'binary'
1273 elif f in removed:
1313 elif f in removed:
1274 # have we already reported a copy above?
1314 # have we already reported a copy above?
1275 if f in copy and copy[f] in added and copy[copy[f]] == f:
1315 if f in copy and copy[f] in added and copy[copy[f]] == f:
1276 dodiff = False
1316 dodiff = False
1277 else:
1317 else:
1278 header.append('deleted file mode %s\n' %
1318 header.append('deleted file mode %s\n' %
1279 gitmode[man1.flags(f)])
1319 gitmode[man1.flags(f)])
1280 else:
1320 else:
1281 omode = gitmode[man1.flags(f)]
1321 omode = gitmode[man1.flags(f)]
1282 nmode = gitmode[ctx2.flags(f)]
1322 nmode = gitmode[ctx2.flags(f)]
1283 _addmodehdr(header, omode, nmode)
1323 _addmodehdr(header, omode, nmode)
1284 if util.binary(to) or util.binary(tn):
1324 if util.binary(to) or util.binary(tn):
1285 dodiff = 'binary'
1325 dodiff = 'binary'
1286 r = None
1326 r = None
1287 header.insert(0, mdiff.diffline(r, a, b, opts))
1327 header.insert(0, mdiff.diffline(r, a, b, opts))
1288 if dodiff:
1328 if dodiff:
1289 if dodiff == 'binary':
1329 if dodiff == 'binary':
1290 text = b85diff(to, tn)
1330 text = b85diff(to, tn)
1291 else:
1331 else:
1292 text = mdiff.unidiff(to, date1,
1332 text = mdiff.unidiff(to, date1,
1293 # ctx2 date may be dynamic
1333 # ctx2 date may be dynamic
1294 tn, util.datestr(ctx2.date()),
1334 tn, util.datestr(ctx2.date()),
1295 a, b, r, opts=opts)
1335 a, b, r, opts=opts)
1296 if header and (text or len(header) > 1):
1336 if header and (text or len(header) > 1):
1297 yield ''.join(header)
1337 yield ''.join(header)
1298 if text:
1338 if text:
1299 yield text
1339 yield text
1300
1340
1301 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1341 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1302 opts=None):
1342 opts=None):
1303 '''export changesets as hg patches.'''
1343 '''export changesets as hg patches.'''
1304
1344
1305 total = len(revs)
1345 total = len(revs)
1306 revwidth = max([len(str(rev)) for rev in revs])
1346 revwidth = max([len(str(rev)) for rev in revs])
1307
1347
1308 def single(rev, seqno, fp):
1348 def single(rev, seqno, fp):
1309 ctx = repo[rev]
1349 ctx = repo[rev]
1310 node = ctx.node()
1350 node = ctx.node()
1311 parents = [p.node() for p in ctx.parents() if p]
1351 parents = [p.node() for p in ctx.parents() if p]
1312 branch = ctx.branch()
1352 branch = ctx.branch()
1313 if switch_parent:
1353 if switch_parent:
1314 parents.reverse()
1354 parents.reverse()
1315 prev = (parents and parents[0]) or nullid
1355 prev = (parents and parents[0]) or nullid
1316
1356
1317 if not fp:
1357 if not fp:
1318 fp = cmdutil.make_file(repo, template, node, total=total,
1358 fp = cmdutil.make_file(repo, template, node, total=total,
1319 seqno=seqno, revwidth=revwidth,
1359 seqno=seqno, revwidth=revwidth,
1320 mode='ab')
1360 mode='ab')
1321 if fp != sys.stdout and hasattr(fp, 'name'):
1361 if fp != sys.stdout and hasattr(fp, 'name'):
1322 repo.ui.note("%s\n" % fp.name)
1362 repo.ui.note("%s\n" % fp.name)
1323
1363
1324 fp.write("# HG changeset patch\n")
1364 fp.write("# HG changeset patch\n")
1325 fp.write("# User %s\n" % ctx.user())
1365 fp.write("# User %s\n" % ctx.user())
1326 fp.write("# Date %d %d\n" % ctx.date())
1366 fp.write("# Date %d %d\n" % ctx.date())
1327 if branch and (branch != 'default'):
1367 if branch and (branch != 'default'):
1328 fp.write("# Branch %s\n" % branch)
1368 fp.write("# Branch %s\n" % branch)
1329 fp.write("# Node ID %s\n" % hex(node))
1369 fp.write("# Node ID %s\n" % hex(node))
1330 fp.write("# Parent %s\n" % hex(prev))
1370 fp.write("# Parent %s\n" % hex(prev))
1331 if len(parents) > 1:
1371 if len(parents) > 1:
1332 fp.write("# Parent %s\n" % hex(parents[1]))
1372 fp.write("# Parent %s\n" % hex(parents[1]))
1333 fp.write(ctx.description().rstrip())
1373 fp.write(ctx.description().rstrip())
1334 fp.write("\n\n")
1374 fp.write("\n\n")
1335
1375
1336 for chunk in diff(repo, prev, node, opts=opts):
1376 for chunk in diff(repo, prev, node, opts=opts):
1337 fp.write(chunk)
1377 fp.write(chunk)
1338
1378
1339 for seqno, rev in enumerate(revs):
1379 for seqno, rev in enumerate(revs):
1340 single(rev, seqno+1, fp)
1380 single(rev, seqno+1, fp)
1341
1381
1342 def diffstatdata(lines):
1382 def diffstatdata(lines):
1343 filename, adds, removes = None, 0, 0
1383 filename, adds, removes = None, 0, 0
1344 for line in lines:
1384 for line in lines:
1345 if line.startswith('diff'):
1385 if line.startswith('diff'):
1346 if filename:
1386 if filename:
1347 yield (filename, adds, removes)
1387 yield (filename, adds, removes)
1348 # set numbers to 0 anyway when starting new file
1388 # set numbers to 0 anyway when starting new file
1349 adds, removes = 0, 0
1389 adds, removes = 0, 0
1350 if line.startswith('diff --git'):
1390 if line.startswith('diff --git'):
1351 filename = gitre.search(line).group(1)
1391 filename = gitre.search(line).group(1)
1352 else:
1392 else:
1353 # format: "diff -r ... -r ... filename"
1393 # format: "diff -r ... -r ... filename"
1354 filename = line.split(None, 5)[-1]
1394 filename = line.split(None, 5)[-1]
1355 elif line.startswith('+') and not line.startswith('+++'):
1395 elif line.startswith('+') and not line.startswith('+++'):
1356 adds += 1
1396 adds += 1
1357 elif line.startswith('-') and not line.startswith('---'):
1397 elif line.startswith('-') and not line.startswith('---'):
1358 removes += 1
1398 removes += 1
1359 if filename:
1399 if filename:
1360 yield (filename, adds, removes)
1400 yield (filename, adds, removes)
1361
1401
1362 def diffstat(lines, width=80):
1402 def diffstat(lines, width=80):
1363 output = []
1403 output = []
1364 stats = list(diffstatdata(lines))
1404 stats = list(diffstatdata(lines))
1365
1405
1366 maxtotal, maxname = 0, 0
1406 maxtotal, maxname = 0, 0
1367 totaladds, totalremoves = 0, 0
1407 totaladds, totalremoves = 0, 0
1368 for filename, adds, removes in stats:
1408 for filename, adds, removes in stats:
1369 totaladds += adds
1409 totaladds += adds
1370 totalremoves += removes
1410 totalremoves += removes
1371 maxname = max(maxname, len(filename))
1411 maxname = max(maxname, len(filename))
1372 maxtotal = max(maxtotal, adds+removes)
1412 maxtotal = max(maxtotal, adds+removes)
1373
1413
1374 countwidth = len(str(maxtotal))
1414 countwidth = len(str(maxtotal))
1375 graphwidth = width - countwidth - maxname
1415 graphwidth = width - countwidth - maxname
1376 if graphwidth < 10:
1416 if graphwidth < 10:
1377 graphwidth = 10
1417 graphwidth = 10
1378
1418
1379 factor = max(int(math.ceil(float(maxtotal) / graphwidth)), 1)
1419 factor = max(int(math.ceil(float(maxtotal) / graphwidth)), 1)
1380
1420
1381 for filename, adds, removes in stats:
1421 for filename, adds, removes in stats:
1382 # If diffstat runs out of room it doesn't print anything, which
1422 # If diffstat runs out of room it doesn't print anything, which
1383 # isn't very useful, so always print at least one + or - if there
1423 # isn't very useful, so always print at least one + or - if there
1384 # were at least some changes
1424 # were at least some changes
1385 pluses = '+' * max(adds/factor, int(bool(adds)))
1425 pluses = '+' * max(adds/factor, int(bool(adds)))
1386 minuses = '-' * max(removes/factor, int(bool(removes)))
1426 minuses = '-' * max(removes/factor, int(bool(removes)))
1387 output.append(' %-*s | %*.d %s%s\n' % (maxname, filename, countwidth,
1427 output.append(' %-*s | %*.d %s%s\n' % (maxname, filename, countwidth,
1388 adds+removes, pluses, minuses))
1428 adds+removes, pluses, minuses))
1389
1429
1390 if stats:
1430 if stats:
1391 output.append(' %d files changed, %d insertions(+), %d deletions(-)\n'
1431 output.append(' %d files changed, %d insertions(+), %d deletions(-)\n'
1392 % (len(stats), totaladds, totalremoves))
1432 % (len(stats), totaladds, totalremoves))
1393
1433
1394 return ''.join(output)
1434 return ''.join(output)
General Comments 0
You need to be logged in to leave comments. Login now