##// 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 1 HGRC(5)
2 2 =======
3 3 Bryan O'Sullivan <bos@serpentine.com>
4 4 :man source: Mercurial
5 5 :man manual: Mercurial Manual
6 6
7 7 NAME
8 8 ----
9 9 hgrc - configuration files for Mercurial
10 10
11 11 SYNOPSIS
12 12 --------
13 13
14 14 The Mercurial system uses a set of configuration files to control
15 15 aspects of its behavior.
16 16
17 17 FILES
18 18 -----
19 19
20 20 Mercurial reads configuration data from several files, if they exist.
21 21 The names of these files depend on the system on which Mercurial is
22 22 installed. `*.rc` files from a single directory are read in
23 23 alphabetical order, later ones overriding earlier ones. Where multiple
24 24 paths are given below, settings from later paths override earlier
25 25 ones.
26 26
27 27 (Unix) `<install-root>/etc/mercurial/hgrc.d/*.rc`::
28 28 (Unix) `<install-root>/etc/mercurial/hgrc`::
29 29 Per-installation configuration files, searched for in the
30 30 directory where Mercurial is installed. `<install-root>` is the
31 31 parent directory of the hg executable (or symlink) being run. For
32 32 example, if installed in `/shared/tools/bin/hg`, Mercurial will look
33 33 in `/shared/tools/etc/mercurial/hgrc`. Options in these files apply
34 34 to all Mercurial commands executed by any user in any directory.
35 35
36 36 (Unix) `/etc/mercurial/hgrc.d/*.rc`::
37 37 (Unix) `/etc/mercurial/hgrc`::
38 38 Per-system configuration files, for the system on which Mercurial
39 39 is running. Options in these files apply to all Mercurial commands
40 40 executed by any user in any directory. Options in these files
41 41 override per-installation options.
42 42
43 43 (Windows) `<install-dir>\Mercurial.ini`::
44 44 or else::
45 45 (Windows) `HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`::
46 46 or else::
47 47 (Windows) `C:\Mercurial\Mercurial.ini`::
48 48 Per-installation/system configuration files, for the system on
49 49 which Mercurial is running. Options in these files apply to all
50 50 Mercurial commands executed by any user in any directory. Registry
51 51 keys contain PATH-like strings, every part of which must reference
52 52 a `Mercurial.ini` file or be a directory where `*.rc` files will
53 53 be read.
54 54
55 55 (Unix) `$HOME/.hgrc`::
56 56 (Windows) `%HOME%\Mercurial.ini`::
57 57 (Windows) `%HOME%\.hgrc`::
58 58 (Windows) `%USERPROFILE%\Mercurial.ini`::
59 59 (Windows) `%USERPROFILE%\.hgrc`::
60 60 Per-user configuration file(s), for the user running Mercurial. On
61 61 Windows 9x, `%HOME%` is replaced by `%APPDATA%`. Options in these
62 62 files apply to all Mercurial commands executed by this user in any
63 63 directory. Options in these files override per-installation and
64 64 per-system options.
65 65
66 66 (Unix, Windows) `<repo>/.hg/hgrc`::
67 67 Per-repository configuration options that only apply in a
68 68 particular repository. This file is not version-controlled, and
69 69 will not get transferred during a "clone" operation. Options in
70 70 this file override options in all other configuration files. On
71 71 Unix, most of this file will be ignored if it doesn't belong to a
72 72 trusted user or to a trusted group. See the documentation for the
73 73 trusted section below for more details.
74 74
75 75 SYNTAX
76 76 ------
77 77
78 78 A configuration file consists of sections, led by a "[section]" header
79 79 and followed by "name: value" entries; "name=value" is also accepted.
80 80
81 81 [spam]
82 82 eggs=ham
83 83 green=
84 84 eggs
85 85
86 86 Each line contains one entry. If the lines that follow are indented,
87 87 they are treated as continuations of that entry.
88 88
89 89 Leading whitespace is removed from values. Empty lines are skipped.
90 90
91 91 The optional values can contain format strings which refer to other
92 92 values in the same section, or values in a special DEFAULT section.
93 93
94 94 Lines beginning with "#" or ";" are ignored and may be used to provide
95 95 comments.
96 96
97 97 SECTIONS
98 98 --------
99 99
100 100 This section describes the different sections that may appear in a
101 101 Mercurial "hgrc" file, the purpose of each section, its possible keys,
102 102 and their possible values.
103 103
104 104 [[alias]]
105 105 alias::
106 106 Defines command aliases.
107 107 Aliases allow you to define your own commands in terms of other
108 108 commands (or aliases), optionally including arguments.
109 109 +
110 110 --
111 111 Alias definitions consist of lines of the form:
112 112
113 113 <alias> = <command> [<argument]...
114 114
115 115 For example, this definition:
116 116
117 117 latest = log --limit 5
118 118
119 119 creates a new command `latest` that shows only the five most recent
120 120 changesets. You can define subsequent aliases using earlier ones:
121 121
122 122 stable5 = latest -b stable
123 123
124 124 NOTE: It is possible to create aliases with the same names as existing
125 125 commands, which will then override the original definitions. This is
126 126 almost always a bad idea!
127 127 --
128 128
129 129 [[auth]]
130 130 auth::
131 131 Authentication credentials for HTTP authentication. Each line has
132 132 the following format:
133 133
134 134 <name>.<argument> = <value>
135 135 +
136 136 --
137 137 where <name> is used to group arguments into authentication entries.
138 138 Example:
139 139
140 140 foo.prefix = hg.intevation.org/mercurial
141 141 foo.username = foo
142 142 foo.password = bar
143 143 foo.schemes = http https
144 144
145 145 Supported arguments:
146 146
147 147 prefix;;
148 148 Either "++\*++" or a URI prefix with or without the scheme part.
149 149 The authentication entry with the longest matching prefix is used
150 150 (where "++*++" matches everything and counts as a match of length
151 151 1). If the prefix doesn't include a scheme, the match is performed
152 152 against the URI with its scheme stripped as well, and the schemes
153 153 argument, q.v., is then subsequently consulted.
154 154 username;;
155 155 Username to authenticate with.
156 156 password;;
157 157 Optional. Password to authenticate with. If not given the user
158 158 will be prompted for it.
159 159 schemes;;
160 160 Optional. Space separated list of URI schemes to use this
161 161 authentication entry with. Only used if the prefix doesn't include
162 162 a scheme. Supported schemes are http and https. They will match
163 163 static-http and static-https respectively, as well.
164 164 Default: https.
165 165
166 166 If no suitable authentication entry is found, the user is prompted
167 167 for credentials as usual if required by the remote.
168 168 --
169 169
170 170 [[decode]]
171 171 decode/encode::
172 172 Filters for transforming files on checkout/checkin. This would
173 173 typically be used for newline processing or other
174 174 localization/canonicalization of files.
175 175 +
176 176 --
177 177 Filters consist of a filter pattern followed by a filter command.
178 178 Filter patterns are globs by default, rooted at the repository root.
179 179 For example, to match any file ending in "`.txt`" in the root
180 180 directory only, use the pattern "++\*.txt++". To match any file ending
181 181 in "`.c`" anywhere in the repository, use the pattern "++**.c++".
182 182
183 183 The filter command can start with a specifier, either "pipe:" or
184 184 "tempfile:". If no specifier is given, "pipe:" is used by default.
185 185
186 186 A "pipe:" command must accept data on stdin and return the transformed
187 187 data on stdout.
188 188
189 189 Pipe example:
190 190
191 191 [encode]
192 192 # uncompress gzip files on checkin to improve delta compression
193 193 # note: not necessarily a good idea, just an example
194 194 *.gz = pipe: gunzip
195 195
196 196 [decode]
197 197 # recompress gzip files when writing them to the working dir (we
198 198 # can safely omit "pipe:", because it's the default)
199 199 *.gz = gzip
200 200
201 201 A "tempfile:" command is a template. The string INFILE is replaced
202 202 with the name of a temporary file that contains the data to be
203 203 filtered by the command. The string OUTFILE is replaced with the name
204 204 of an empty temporary file, where the filtered data must be written by
205 205 the command.
206 206
207 207 NOTE: the tempfile mechanism is recommended for Windows systems, where
208 208 the standard shell I/O redirection operators often have strange
209 209 effects and may corrupt the contents of your files.
210 210
211 211 The most common usage is for LF <-> CRLF translation on Windows. For
212 212 this, use the "smart" converters which check for binary files:
213 213
214 214 [extensions]
215 215 hgext.win32text =
216 216 [encode]
217 217 ** = cleverencode:
218 218 [decode]
219 219 ** = cleverdecode:
220 220
221 221 or if you only want to translate certain files:
222 222
223 223 [extensions]
224 224 hgext.win32text =
225 225 [encode]
226 226 **.txt = dumbencode:
227 227 [decode]
228 228 **.txt = dumbdecode:
229 229 --
230 230
231 231 [[defaults]]
232 232 defaults::
233 233 Use the [defaults] section to define command defaults, i.e. the
234 234 default options/arguments to pass to the specified commands.
235 235 +
236 236 --
237 237 The following example makes 'hg log' run in verbose mode, and 'hg
238 238 status' show only the modified files, by default.
239 239
240 240 [defaults]
241 241 log = -v
242 242 status = -m
243 243
244 244 The actual commands, instead of their aliases, must be used when
245 245 defining command defaults. The command defaults will also be applied
246 246 to the aliases of the commands defined.
247 247 --
248 248
249 249 [[diff]]
250 250 diff::
251 251 Settings used when displaying diffs. They are all Boolean and
252 252 defaults to False.
253 253 git;;
254 254 Use git extended diff format.
255 255 nodates;;
256 256 Don't include dates in diff headers.
257 257 showfunc;;
258 258 Show which function each change is in.
259 259 ignorews;;
260 260 Ignore white space when comparing lines.
261 261 ignorewsamount;;
262 262 Ignore changes in the amount of white space.
263 263 ignoreblanklines;;
264 264 Ignore changes whose lines are all blank.
265 265
266 266 [[email]]
267 267 email::
268 268 Settings for extensions that send email messages.
269 269 from;;
270 270 Optional. Email address to use in "From" header and SMTP envelope
271 271 of outgoing messages.
272 272 to;;
273 273 Optional. Comma-separated list of recipients' email addresses.
274 274 cc;;
275 275 Optional. Comma-separated list of carbon copy recipients'
276 276 email addresses.
277 277 bcc;;
278 278 Optional. Comma-separated list of blind carbon copy recipients'
279 279 email addresses. Cannot be set interactively.
280 280 method;;
281 281 Optional. Method to use to send email messages. If value is "smtp"
282 282 (default), use SMTP (see section "[smtp]" for configuration).
283 283 Otherwise, use as name of program to run that acts like sendmail
284 284 (takes "-f" option for sender, list of recipients on command line,
285 285 message on stdin). Normally, setting this to "sendmail" or
286 286 "/usr/sbin/sendmail" is enough to use sendmail to send messages.
287 287 charsets;;
288 288 Optional. Comma-separated list of character sets considered
289 289 convenient for recipients. Addresses, headers, and parts not
290 290 containing patches of outgoing messages will be encoded in the
291 291 first character set to which conversion from local encoding
292 292 (`$HGENCODING`, `ui.fallbackencoding`) succeeds. If correct
293 293 conversion fails, the text in question is sent as is. Defaults to
294 294 empty (explicit) list.
295 295 +
296 296 --
297 297 Order of outgoing email character sets:
298 298
299 299 us-ascii always first, regardless of settings
300 300 email.charsets in order given by user
301 301 ui.fallbackencoding if not in email.charsets
302 302 $HGENCODING if not in email.charsets
303 303 utf-8 always last, regardless of settings
304 304
305 305 Email example:
306 306
307 307 [email]
308 308 from = Joseph User <joe.user@example.com>
309 309 method = /usr/sbin/sendmail
310 310 # charsets for western Europeans
311 311 # us-ascii, utf-8 omitted, as they are tried first and last
312 312 charsets = iso-8859-1, iso-8859-15, windows-1252
313 313 --
314 314
315 315 [[extensions]]
316 316 extensions::
317 317 Mercurial has an extension mechanism for adding new features. To
318 318 enable an extension, create an entry for it in this section.
319 319 +
320 320 --
321 321 If you know that the extension is already in Python's search path,
322 322 you can give the name of the module, followed by "=", with nothing
323 323 after the "=".
324 324
325 325 Otherwise, give a name that you choose, followed by "=", followed by
326 326 the path to the ".py" file (including the file name extension) that
327 327 defines the extension.
328 328
329 329 To explicitly disable an extension that is enabled in an hgrc of
330 330 broader scope, prepend its path with '!', as in
331 331 'hgext.foo = !/ext/path' or 'hgext.foo = !' when path is not
332 332 supplied.
333 333
334 334 Example for `~/.hgrc`:
335 335
336 336 [extensions]
337 337 # (the mq extension will get loaded from Mercurial's path)
338 338 hgext.mq =
339 339 # (this extension will get loaded from the file specified)
340 340 myfeature = ~/.hgext/myfeature.py
341 341 --
342 342
343 343 [[format]]
344 344 format::
345 345
346 346 usestore;;
347 347 Enable or disable the "store" repository format which improves
348 348 compatibility with systems that fold case or otherwise mangle
349 349 filenames. Enabled by default. Disabling this option will allow
350 350 you to store longer filenames in some situations at the expense of
351 351 compatibility and ensures that the on-disk format of newly created
352 352 repositories will be compatible with Mercurial before version 0.9.4.
353 353
354 354 usefncache;;
355 355 Enable or disable the "fncache" repository format which enhances
356 356 the "store" repository format (which has to be enabled to use
357 357 fncache) to allow longer filenames and avoids using Windows
358 358 reserved names, e.g. "nul". Enabled by default. Disabling this
359 359 option ensures that the on-disk format of newly created
360 360 repositories will be compatible with Mercurial before version 1.1.
361 361
362 362 [[merge-patterns]]
363 363 merge-patterns::
364 364 This section specifies merge tools to associate with particular file
365 365 patterns. Tools matched here will take precedence over the default
366 366 merge tool. Patterns are globs by default, rooted at the repository
367 367 root.
368 368 +
369 369 Example:
370 370 +
371 371 [merge-patterns]
372 372 **.c = kdiff3
373 373 **.jpg = myimgmerge
374 374
375 375 [[merge-tools]]
376 376 merge-tools::
377 377 This section configures external merge tools to use for file-level
378 378 merges.
379 379 +
380 380 --
381 381 Example `~/.hgrc`:
382 382
383 383 [merge-tools]
384 384 # Override stock tool location
385 385 kdiff3.executable = ~/bin/kdiff3
386 386 # Specify command line
387 387 kdiff3.args = $base $local $other -o $output
388 388 # Give higher priority
389 389 kdiff3.priority = 1
390 390
391 391 # Define new tool
392 392 myHtmlTool.args = -m $local $other $base $output
393 393 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
394 394 myHtmlTool.priority = 1
395 395
396 396 Supported arguments:
397 397
398 398 priority;;
399 399 The priority in which to evaluate this tool.
400 400 Default: 0.
401 401 executable;;
402 402 Either just the name of the executable or its pathname.
403 403 Default: the tool name.
404 404 args;;
405 405 The arguments to pass to the tool executable. You can refer to the
406 406 files being merged as well as the output file through these
407 407 variables: `$base`, `$local`, `$other`, `$output`.
408 408 Default: `$local $base $other`
409 409 premerge;;
410 410 Attempt to run internal non-interactive 3-way merge tool before
411 411 launching external tool.
412 412 Default: True
413 413 binary;;
414 414 This tool can merge binary files. Defaults to False, unless tool
415 415 was selected by file pattern match.
416 416 symlink;;
417 417 This tool can merge symlinks. Defaults to False, even if tool was
418 418 selected by file pattern match.
419 419 checkconflicts;;
420 420 Check whether there are conflicts even though the tool reported
421 421 success.
422 422 Default: False
423 423 checkchanged;;
424 424 Check whether outputs were written even though the tool reported
425 425 success.
426 426 Default: False
427 427 fixeol;;
428 428 Attempt to fix up EOL changes caused by the merge tool.
429 429 Default: False
430 430 gui;;
431 431 This tool requires a graphical interface to run. Default: False
432 432 regkey;;
433 433 Windows registry key which describes install location of this
434 434 tool. Mercurial will search for this key first under
435 435 `HKEY_CURRENT_USER` and then under `HKEY_LOCAL_MACHINE`.
436 436 Default: None
437 437 regname;;
438 438 Name of value to read from specified registry key. Defaults to the
439 439 unnamed (default) value.
440 440 regappend;;
441 441 String to append to the value read from the registry, typically
442 442 the executable name of the tool.
443 443 Default: None
444 444 --
445 445
446 446 [[hooks]]
447 447 hooks::
448 448 Commands or Python functions that get automatically executed by
449 449 various actions such as starting or finishing a commit. Multiple
450 450 hooks can be run for the same action by appending a suffix to the
451 451 action. Overriding a site-wide hook can be done by changing its
452 452 value or setting it to an empty string.
453 453 +
454 454 --
455 455 Example `.hg/hgrc`:
456 456
457 457 [hooks]
458 458 # do not use the site-wide hook
459 459 incoming =
460 460 incoming.email = /my/email/hook
461 461 incoming.autobuild = /my/build/hook
462 462
463 463 Most hooks are run with environment variables set that give useful
464 464 additional information. For each hook below, the environment
465 465 variables it is passed are listed with names of the form "$HG_foo".
466 466
467 467 changegroup;;
468 468 Run after a changegroup has been added via push, pull or unbundle.
469 469 ID of the first new changeset is in `$HG_NODE`. URL from which
470 470 changes came is in `$HG_URL`.
471 471 commit;;
472 472 Run after a changeset has been created in the local repository. ID
473 473 of the newly created changeset is in `$HG_NODE`. Parent changeset
474 474 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
475 475 incoming;;
476 476 Run after a changeset has been pulled, pushed, or unbundled into
477 477 the local repository. The ID of the newly arrived changeset is in
478 478 `$HG_NODE`. URL that was source of changes came is in `$HG_URL`.
479 479 outgoing;;
480 480 Run after sending changes from local repository to another. ID of
481 481 first changeset sent is in `$HG_NODE`. Source of operation is in
482 482 `$HG_SOURCE`; see "preoutgoing" hook for description.
483 483 post-<command>;;
484 484 Run after successful invocations of the associated command. The
485 485 contents of the command line are passed as `$HG_ARGS` and the result
486 486 code in `$HG_RESULT`. Hook failure is ignored.
487 487 pre-<command>;;
488 488 Run before executing the associated command. The contents of the
489 489 command line are passed as `$HG_ARGS`. If the hook returns failure,
490 490 the command doesn't execute and Mercurial returns the failure
491 491 code.
492 492 prechangegroup;;
493 493 Run before a changegroup is added via push, pull or unbundle. Exit
494 494 status 0 allows the changegroup to proceed. Non-zero status will
495 495 cause the push, pull or unbundle to fail. URL from which changes
496 496 will come is in `$HG_URL`.
497 497 precommit;;
498 498 Run before starting a local commit. Exit status 0 allows the
499 499 commit to proceed. Non-zero status will cause the commit to fail.
500 500 Parent changeset IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
501 501 preoutgoing;;
502 502 Run before collecting changes to send from the local repository to
503 503 another. Non-zero status will cause failure. This lets you prevent
504 504 pull over HTTP or SSH. Also prevents against local pull, push
505 505 (outbound) or bundle commands, but not effective, since you can
506 506 just copy files instead then. Source of operation is in
507 507 `$HG_SOURCE`. If "serve", operation is happening on behalf of remote
508 508 SSH or HTTP repository. If "push", "pull" or "bundle", operation
509 509 is happening on behalf of repository on same system.
510 510 pretag;;
511 511 Run before creating a tag. Exit status 0 allows the tag to be
512 512 created. Non-zero status will cause the tag to fail. ID of
513 513 changeset to tag is in `$HG_NODE`. Name of tag is in `$HG_TAG`. Tag is
514 514 local if `$HG_LOCAL=1`, in repository if `$HG_LOCAL=0`.
515 515 pretxnchangegroup;;
516 516 Run after a changegroup has been added via push, pull or unbundle,
517 517 but before the transaction has been committed. Changegroup is
518 518 visible to hook program. This lets you validate incoming changes
519 519 before accepting them. Passed the ID of the first new changeset in
520 520 `$HG_NODE`. Exit status 0 allows the transaction to commit. Non-zero
521 521 status will cause the transaction to be rolled back and the push,
522 522 pull or unbundle will fail. URL that was source of changes is in
523 523 `$HG_URL`.
524 524 pretxncommit;;
525 525 Run after a changeset has been created but the transaction not yet
526 526 committed. Changeset is visible to hook program. This lets you
527 527 validate commit message and changes. Exit status 0 allows the
528 528 commit to proceed. Non-zero status will cause the transaction to
529 529 be rolled back. ID of changeset is in `$HG_NODE`. Parent changeset
530 530 IDs are in `$HG_PARENT1` and `$HG_PARENT2`.
531 531 preupdate;;
532 532 Run before updating the working directory. Exit status 0 allows
533 533 the update to proceed. Non-zero status will prevent the update.
534 534 Changeset ID of first new parent is in `$HG_PARENT1`. If merge, ID
535 535 of second new parent is in `$HG_PARENT2`.
536 536 tag;;
537 537 Run after a tag is created. ID of tagged changeset is in `$HG_NODE`.
538 538 Name of tag is in `$HG_TAG`. Tag is local if `$HG_LOCAL=1`, in
539 539 repository if `$HG_LOCAL=0`.
540 540 update;;
541 541 Run after updating the working directory. Changeset ID of first
542 542 new parent is in `$HG_PARENT1`. If merge, ID of second new parent is
543 543 in `$HG_PARENT2`. If the update succeeded, `$HG_ERROR=0`. If the
544 544 update failed (e.g. because conflicts not resolved), `$HG_ERROR=1`.
545 545
546 546 NOTE: it is generally better to use standard hooks rather than the
547 547 generic pre- and post- command hooks as they are guaranteed to be
548 548 called in the appropriate contexts for influencing transactions.
549 549 Also, hooks like "commit" will be called in all contexts that
550 550 generate a commit (e.g. tag) and not just the commit command.
551 551
552 552 NOTE: Environment variables with empty values may not be passed to
553 553 hooks on platforms such as Windows. As an example, `$HG_PARENT2` will
554 554 have an empty value under Unix-like platforms for non-merge
555 555 changesets, while it will not be available at all under Windows.
556 556
557 557 The syntax for Python hooks is as follows:
558 558
559 559 hookname = python:modulename.submodule.callable
560 560 hookname = python:/path/to/python/module.py:callable
561 561
562 562 Python hooks are run within the Mercurial process. Each hook is
563 563 called with at least three keyword arguments: a ui object (keyword
564 564 "ui"), a repository object (keyword "repo"), and a "hooktype"
565 565 keyword that tells what kind of hook is used. Arguments listed as
566 566 environment variables above are passed as keyword arguments, with no
567 567 "HG_" prefix, and names in lower case.
568 568
569 569 If a Python hook returns a "true" value or raises an exception, this
570 570 is treated as a failure.
571 571 --
572 572
573 573 [[http_proxy]]
574 574 http_proxy::
575 575 Used to access web-based Mercurial repositories through a HTTP
576 576 proxy.
577 577 host;;
578 578 Host name and (optional) port of the proxy server, for example
579 579 "myproxy:8000".
580 580 no;;
581 581 Optional. Comma-separated list of host names that should bypass
582 582 the proxy.
583 583 passwd;;
584 584 Optional. Password to authenticate with at the proxy server.
585 585 user;;
586 586 Optional. User name to authenticate with at the proxy server.
587 587
588 588 [[smtp]]
589 589 smtp::
590 590 Configuration for extensions that need to send email messages.
591 591 host;;
592 592 Host name of mail server, e.g. "mail.example.com".
593 593 port;;
594 594 Optional. Port to connect to on mail server. Default: 25.
595 595 tls;;
596 596 Optional. Whether to connect to mail server using TLS. True or
597 597 False. Default: False.
598 598 username;;
599 599 Optional. User name to authenticate to SMTP server with. If
600 600 username is specified, password must also be specified.
601 601 Default: none.
602 602 password;;
603 603 Optional. Password to authenticate to SMTP server with. If
604 604 username is specified, password must also be specified.
605 605 Default: none.
606 606 local_hostname;;
607 607 Optional. It's the hostname that the sender can use to identify
608 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 621 [[paths]]
611 622 paths::
612 623 Assigns symbolic names to repositories. The left side is the
613 624 symbolic name, and the right gives the directory or URL that is the
614 625 location of the repository. Default paths can be declared by setting
615 626 the following entries.
616 627 default;;
617 628 Directory or URL to use when pulling if no source is specified.
618 629 Default is set to repository from which the current repository was
619 630 cloned.
620 631 default-push;;
621 632 Optional. Directory or URL to use when pushing if no destination
622 633 is specified.
623 634
624 635 [[profiling]]
625 636 profiling::
626 637 Specifies profiling format and file output. In this section
627 638 description, 'profiling data' stands for the raw data collected
628 639 during profiling, while 'profiling report' stands for a statistical
629 640 text report generated from the profiling data. The profiling is done
630 641 using lsprof.
631 642 format;;
632 643 Profiling format.
633 644 Default: text.
634 645 text;;
635 646 Generate a profiling report. When saving to a file, it should be
636 647 noted that only the report is saved, and the profiling data is
637 648 not kept.
638 649 kcachegrind;;
639 650 Format profiling data for kcachegrind use: when saving to a
640 651 file, the generated file can directly be loaded into
641 652 kcachegrind.
642 653 output;;
643 654 File path where profiling data or report should be saved. If the
644 655 file exists, it is replaced. Default: None, data is printed on
645 656 stderr
646 657
647 658 [[server]]
648 659 server::
649 660 Controls generic server settings.
650 661 uncompressed;;
651 662 Whether to allow clients to clone a repository using the
652 663 uncompressed streaming protocol. This transfers about 40% more
653 664 data than a regular clone, but uses less memory and CPU on both
654 665 server and client. Over a LAN (100 Mbps or better) or a very fast
655 666 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
656 667 regular clone. Over most WAN connections (anything slower than
657 668 about 6 Mbps), uncompressed streaming is slower, because of the
658 669 extra data transfer overhead. Default is False.
659 670
660 671 [[trusted]]
661 672 trusted::
662 673 For security reasons, Mercurial will not use the settings in the
663 674 `.hg/hgrc` file from a repository if it doesn't belong to a trusted
664 675 user or to a trusted group. The main exception is the web interface,
665 676 which automatically uses some safe settings, since it's common to
666 677 serve repositories from different users.
667 678 +
668 679 --
669 680 This section specifies what users and groups are trusted. The
670 681 current user is always trusted. To trust everybody, list a user or a
671 682 group with name "`*`".
672 683
673 684 users;;
674 685 Comma-separated list of trusted users.
675 686 groups;;
676 687 Comma-separated list of trusted groups.
677 688 --
678 689
679 690 [[ui]]
680 691 ui::
681 692 User interface controls.
682 693 +
683 694 --
684 695 archivemeta;;
685 696 Whether to include the .hg_archival.txt file containing meta data
686 697 (hashes for the repository base and for tip) in archives created
687 698 by the hg archive command or downloaded via hgweb.
688 699 Default is true.
689 700 askusername;;
690 701 Whether to prompt for a username when committing. If True, and
691 702 neither `$HGUSER` nor `$EMAIL` has been specified, then the user will
692 703 be prompted to enter a username. If no username is entered, the
693 704 default USER@HOST is used instead.
694 705 Default is False.
695 706 debug;;
696 707 Print debugging information. True or False. Default is False.
697 708 editor;;
698 709 The editor to use during a commit. Default is `$EDITOR` or "vi".
699 710 fallbackencoding;;
700 711 Encoding to try if it's not possible to decode the changelog using
701 712 UTF-8. Default is ISO-8859-1.
702 713 ignore;;
703 714 A file to read per-user ignore patterns from. This file should be
704 715 in the same format as a repository-wide .hgignore file. This
705 716 option supports hook syntax, so if you want to specify multiple
706 717 ignore files, you can do so by setting something like
707 718 "ignore.other = ~/.hgignore2". For details of the ignore file
708 719 format, see the hgignore(5) man page.
709 720 interactive;;
710 721 Allow to prompt the user. True or False. Default is True.
711 722 logtemplate;;
712 723 Template string for commands that print changesets.
713 724 merge;;
714 725 The conflict resolution program to use during a manual merge.
715 726 There are some internal tools available:
716 727 +
717 728 internal:local;;
718 729 keep the local version
719 730 internal:other;;
720 731 use the other version
721 732 internal:merge;;
722 733 use the internal non-interactive merge tool
723 734 internal:fail;;
724 735 fail to merge
725 736 +
726 737 For more information on configuring merge tools see the
727 738 merge-tools section.
728 739
729 740 patch;;
730 741 command to use to apply patches. Look for 'gpatch' or 'patch' in
731 742 PATH if unset.
732 743 quiet;;
733 744 Reduce the amount of output printed. True or False. Default is False.
734 745 remotecmd;;
735 746 remote command to use for clone/push/pull operations. Default is 'hg'.
736 747 report_untrusted;;
737 748 Warn if a `.hg/hgrc` file is ignored due to not being owned by a
738 749 trusted user or group. True or False. Default is True.
739 750 slash;;
740 751 Display paths using a slash ("++/++") as the path separator. This
741 752 only makes a difference on systems where the default path
742 753 separator is not the slash character (e.g. Windows uses the
743 754 backslash character ("++\++")).
744 755 Default is False.
745 756 ssh;;
746 757 command to use for SSH connections. Default is 'ssh'.
747 758 strict;;
748 759 Require exact command names, instead of allowing unambiguous
749 760 abbreviations. True or False. Default is False.
750 761 style;;
751 762 Name of style to use for command output.
752 763 timeout;;
753 764 The timeout used when a lock is held (in seconds), a negative value
754 765 means no timeout. Default is 600.
755 766 username;;
756 767 The committer of a changeset created when running "commit".
757 768 Typically a person's name and email address, e.g. "Fred Widget
758 769 <fred@example.com>". Default is `$EMAIL` or username@hostname. If
759 770 the username in hgrc is empty, it has to be specified manually or
760 771 in a different hgrc file (e.g. `$HOME/.hgrc`, if the admin set
761 772 "username =" in the system hgrc).
762 773 verbose;;
763 774 Increase the amount of output printed. True or False. Default is False.
764 775 --
765 776
766 777 [[web]]
767 778 web::
768 779 Web interface configuration.
769 780 accesslog;;
770 781 Where to output the access log. Default is stdout.
771 782 address;;
772 783 Interface address to bind to. Default is all.
773 784 allow_archive;;
774 785 List of archive format (bz2, gz, zip) allowed for downloading.
775 786 Default is empty.
776 787 allowbz2;;
777 788 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
778 789 revisions.
779 790 Default is false.
780 791 allowgz;;
781 792 (DEPRECATED) Whether to allow .tar.gz downloading of repository
782 793 revisions.
783 794 Default is false.
784 795 allowpull;;
785 796 Whether to allow pulling from the repository. Default is true.
786 797 allow_push;;
787 798 Whether to allow pushing to the repository. If empty or not set,
788 799 push is not allowed. If the special value "`*`", any remote user can
789 800 push, including unauthenticated users. Otherwise, the remote user
790 801 must have been authenticated, and the authenticated user name must
791 802 be present in this list (separated by whitespace or ","). The
792 803 contents of the allow_push list are examined after the deny_push
793 804 list.
794 805 allow_read;;
795 806 If the user has not already been denied repository access due to
796 807 the contents of deny_read, this list determines whether to grant
797 808 repository access to the user. If this list is not empty, and the
798 809 user is unauthenticated or not present in the list (separated by
799 810 whitespace or ","), then access is denied for the user. If the
800 811 list is empty or not set, then access is permitted to all users by
801 812 default. Setting allow_read to the special value "`*`" is equivalent
802 813 to it not being set (i.e. access is permitted to all users). The
803 814 contents of the allow_read list are examined after the deny_read
804 815 list.
805 816 allowzip;;
806 817 (DEPRECATED) Whether to allow .zip downloading of repository
807 818 revisions. Default is false. This feature creates temporary files.
808 819 baseurl;;
809 820 Base URL to use when publishing URLs in other locations, so
810 821 third-party tools like email notification hooks can construct
811 822 URLs. Example: "http://hgserver/repos/"
812 823 contact;;
813 824 Name or email address of the person in charge of the repository.
814 825 Defaults to ui.username or `$EMAIL` or "unknown" if unset or empty.
815 826 deny_push;;
816 827 Whether to deny pushing to the repository. If empty or not set,
817 828 push is not denied. If the special value "`*`", all remote users are
818 829 denied push. Otherwise, unauthenticated users are all denied, and
819 830 any authenticated user name present in this list (separated by
820 831 whitespace or ",") is also denied. The contents of the deny_push
821 832 list are examined before the allow_push list.
822 833 deny_read;;
823 834 Whether to deny reading/viewing of the repository. If this list is
824 835 not empty, unauthenticated users are all denied, and any
825 836 authenticated user name present in this list (separated by
826 837 whitespace or ",") is also denied access to the repository. If set
827 838 to the special value "`*`", all remote users are denied access
828 839 (rarely needed ;). If deny_read is empty or not set, the
829 840 determination of repository access depends on the presence and
830 841 content of the allow_read list (see description). If both
831 842 deny_read and allow_read are empty or not set, then access is
832 843 permitted to all users by default. If the repository is being
833 844 served via hgwebdir, denied users will not be able to see it in
834 845 the list of repositories. The contents of the deny_read list have
835 846 priority over (are examined before) the contents of the allow_read
836 847 list.
837 848 description;;
838 849 Textual description of the repository's purpose or contents.
839 850 Default is "unknown".
840 851 encoding;;
841 852 Character encoding name.
842 853 Example: "UTF-8"
843 854 errorlog;;
844 855 Where to output the error log. Default is stderr.
845 856 hidden;;
846 857 Whether to hide the repository in the hgwebdir index.
847 858 Default is false.
848 859 ipv6;;
849 860 Whether to use IPv6. Default is false.
850 861 name;;
851 862 Repository name to use in the web interface. Default is current
852 863 working directory.
853 864 maxchanges;;
854 865 Maximum number of changes to list on the changelog. Default is 10.
855 866 maxfiles;;
856 867 Maximum number of files to list per changeset. Default is 10.
857 868 port;;
858 869 Port to listen on. Default is 8000.
859 870 prefix;;
860 871 Prefix path to serve from. Default is '' (server root).
861 872 push_ssl;;
862 873 Whether to require that inbound pushes be transported over SSL to
863 874 prevent password sniffing. Default is true.
864 875 staticurl;;
865 876 Base URL to use for static files. If unset, static files (e.g. the
866 877 hgicon.png favicon) will be served by the CGI script itself. Use
867 878 this setting to serve them directly with the HTTP server.
868 879 Example: "http://hgserver/static/"
869 880 stripes;;
870 881 How many lines a "zebra stripe" should span in multiline output.
871 882 Default is 1; set to 0 to disable.
872 883 style;;
873 884 Which template map style to use.
874 885 templates;;
875 886 Where to find the HTML templates. Default is install path.
876 887
877 888
878 889 AUTHOR
879 890 ------
880 891 Bryan O'Sullivan <bos@serpentine.com>.
881 892
882 893 Mercurial was written by Matt Mackall <mpm@selenic.com>.
883 894
884 895 SEE ALSO
885 896 --------
886 897 hg(1), hgignore(5)
887 898
888 899 COPYING
889 900 -------
890 901 This manual page is copyright 2005 Bryan O'Sullivan.
891 902 Mercurial is copyright 2005-2009 Matt Mackall.
892 903 Free use of this software is granted under the terms of the GNU General
893 904 Public License (GPL).
@@ -1,534 +1,534 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an
15 15 # audience not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$ in
38 38 tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in the
41 41 change history. The mechanism can be regarded as a convenience for the
42 42 current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections of
45 45 hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repositories.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be
63 63 replaced with customized keywords and templates. Again, run "hg
64 64 kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 67 the risk of inadvertently storing expanded keywords in the change
68 68 history.
69 69
70 70 To force expansion after enabling it, or a configuration change, run
71 71 "hg kwexpand".
72 72
73 73 Also, when committing with the record extension or using mq's qrecord,
74 74 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
75 75 the files in question to update keyword expansions after all changes
76 76 have been checked in.
77 77
78 78 Expansions spanning more than one line and incremental expansions,
79 79 like CVS' $Log$, are not supported. A keyword template map
80 80 "Log = {desc}" expands to the first line of the changeset description.
81 81 '''
82 82
83 83 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
84 84 from mercurial import patch, localrepo, templater, templatefilters, util, match
85 85 from mercurial.hgweb import webcommands
86 86 from mercurial.lock import release
87 87 from mercurial.node import nullid, hex
88 88 from mercurial.i18n import _
89 89 import re, shutil, tempfile, time
90 90
91 91 commands.optionalrepo += ' kwdemo'
92 92
93 93 # hg commands that do not act on keywords
94 94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
95 95 ' log outgoing push rename rollback tip verify'
96 96 ' convert email glog')
97 97
98 98 # hg commands that trigger expansion only when writing to working dir,
99 99 # not when reading filelog, and unexpand when reading from working dir
100 100 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
101 101
102 102 def utcdate(date):
103 103 '''Returns hgdate in cvs-like UTC format.'''
104 104 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
105 105
106 106 # make keyword tools accessible
107 107 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
108 108
109 109
110 110 class kwtemplater(object):
111 111 '''
112 112 Sets up keyword templates, corresponding keyword regex, and
113 113 provides keyword substitution functions.
114 114 '''
115 115 templates = {
116 116 'Revision': '{node|short}',
117 117 'Author': '{author|user}',
118 118 'Date': '{date|utcdate}',
119 119 'RCSFile': '{file|basename},v',
120 120 'Source': '{root}/{file},v',
121 121 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
122 122 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
123 123 }
124 124
125 125 def __init__(self, ui, repo):
126 126 self.ui = ui
127 127 self.repo = repo
128 128 self.match = match.match(repo.root, '', [],
129 129 kwtools['inc'], kwtools['exc'])
130 130 self.restrict = kwtools['hgcmd'] in restricted.split()
131 131
132 132 kwmaps = self.ui.configitems('keywordmaps')
133 133 if kwmaps: # override default templates
134 134 kwmaps = [(k, templater.parsestring(v, False))
135 135 for (k, v) in kwmaps]
136 136 self.templates = dict(kwmaps)
137 137 escaped = map(re.escape, self.templates.keys())
138 138 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
139 139 self.re_kw = re.compile(kwpat)
140 140
141 141 templatefilters.filters['utcdate'] = utcdate
142 142 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
143 143 False, None, '', False)
144 144
145 145 def substitute(self, data, path, ctx, subfunc):
146 146 '''Replaces keywords in data with expanded template.'''
147 147 def kwsub(mobj):
148 148 kw = mobj.group(1)
149 149 self.ct.use_template(self.templates[kw])
150 150 self.ui.pushbuffer()
151 151 self.ct.show(ctx, root=self.repo.root, file=path)
152 152 ekw = templatefilters.firstline(self.ui.popbuffer())
153 153 return '$%s: %s $' % (kw, ekw)
154 154 return subfunc(kwsub, data)
155 155
156 156 def expand(self, path, node, data):
157 157 '''Returns data with keywords expanded.'''
158 158 if not self.restrict and self.match(path) and not util.binary(data):
159 159 ctx = self.repo.filectx(path, fileid=node).changectx()
160 160 return self.substitute(data, path, ctx, self.re_kw.sub)
161 161 return data
162 162
163 163 def iskwfile(self, path, flagfunc):
164 164 '''Returns true if path matches [keyword] pattern
165 165 and is not a symbolic link.
166 166 Caveat: localrepository._link fails on Windows.'''
167 167 return self.match(path) and not 'l' in flagfunc(path)
168 168
169 169 def overwrite(self, node, expand, files):
170 170 '''Overwrites selected files expanding/shrinking keywords.'''
171 171 ctx = self.repo[node]
172 172 mf = ctx.manifest()
173 173 if node is not None: # commit
174 174 files = [f for f in ctx.files() if f in mf]
175 175 notify = self.ui.debug
176 176 else: # kwexpand/kwshrink
177 177 notify = self.ui.note
178 178 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
179 179 if candidates:
180 180 self.restrict = True # do not expand when reading
181 181 msg = (expand and _('overwriting %s expanding keywords\n')
182 182 or _('overwriting %s shrinking keywords\n'))
183 183 for f in candidates:
184 184 fp = self.repo.file(f)
185 185 data = fp.read(mf[f])
186 186 if util.binary(data):
187 187 continue
188 188 if expand:
189 189 if node is None:
190 190 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
191 191 data, found = self.substitute(data, f, ctx,
192 192 self.re_kw.subn)
193 193 else:
194 194 found = self.re_kw.search(data)
195 195 if found:
196 196 notify(msg % f)
197 197 self.repo.wwrite(f, data, mf.flags(f))
198 198 self.repo.dirstate.normal(f)
199 199 self.restrict = False
200 200
201 201 def shrinktext(self, text):
202 202 '''Unconditionally removes all keyword substitutions from text.'''
203 203 return self.re_kw.sub(r'$\1$', text)
204 204
205 205 def shrink(self, fname, text):
206 206 '''Returns text with all keyword substitutions removed.'''
207 207 if self.match(fname) and not util.binary(text):
208 208 return self.shrinktext(text)
209 209 return text
210 210
211 211 def shrinklines(self, fname, lines):
212 212 '''Returns lines with keyword substitutions removed.'''
213 213 if self.match(fname):
214 214 text = ''.join(lines)
215 215 if not util.binary(text):
216 216 return self.shrinktext(text).splitlines(True)
217 217 return lines
218 218
219 219 def wread(self, fname, data):
220 220 '''If in restricted mode returns data read from wdir with
221 221 keyword substitutions removed.'''
222 222 return self.restrict and self.shrink(fname, data) or data
223 223
224 224 class kwfilelog(filelog.filelog):
225 225 '''
226 226 Subclass of filelog to hook into its read, add, cmp methods.
227 227 Keywords are "stored" unexpanded, and processed on reading.
228 228 '''
229 229 def __init__(self, opener, kwt, path):
230 230 super(kwfilelog, self).__init__(opener, path)
231 231 self.kwt = kwt
232 232 self.path = path
233 233
234 234 def read(self, node):
235 235 '''Expands keywords when reading filelog.'''
236 236 data = super(kwfilelog, self).read(node)
237 237 return self.kwt.expand(self.path, node, data)
238 238
239 239 def add(self, text, meta, tr, link, p1=None, p2=None):
240 240 '''Removes keyword substitutions when adding to filelog.'''
241 241 text = self.kwt.shrink(self.path, text)
242 242 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
243 243
244 244 def cmp(self, node, text):
245 245 '''Removes keyword substitutions for comparison.'''
246 246 text = self.kwt.shrink(self.path, text)
247 247 if self.renamed(node):
248 248 t2 = super(kwfilelog, self).read(node)
249 249 return t2 != text
250 250 return revlog.revlog.cmp(self, node, text)
251 251
252 252 def _status(ui, repo, kwt, unknown, *pats, **opts):
253 253 '''Bails out if [keyword] configuration is not active.
254 254 Returns status of working directory.'''
255 255 if kwt:
256 256 match = cmdutil.match(repo, pats, opts)
257 257 return repo.status(match=match, unknown=unknown, clean=True)
258 258 if ui.configitems('keyword'):
259 259 raise util.Abort(_('[keyword] patterns cannot match'))
260 260 raise util.Abort(_('no [keyword] patterns configured'))
261 261
262 262 def _kwfwrite(ui, repo, expand, *pats, **opts):
263 263 '''Selects files and passes them to kwtemplater.overwrite.'''
264 264 if repo.dirstate.parents()[1] != nullid:
265 265 raise util.Abort(_('outstanding uncommitted merge'))
266 266 kwt = kwtools['templater']
267 267 status = _status(ui, repo, kwt, False, *pats, **opts)
268 268 modified, added, removed, deleted = status[:4]
269 269 if modified or added or removed or deleted:
270 270 raise util.Abort(_('outstanding uncommitted changes'))
271 271 wlock = lock = None
272 272 try:
273 273 wlock = repo.wlock()
274 274 lock = repo.lock()
275 275 kwt.overwrite(None, expand, status[6])
276 276 finally:
277 277 release(lock, wlock)
278 278
279 279 def demo(ui, repo, *args, **opts):
280 280 '''print [keywordmaps] configuration and an expansion example
281 281
282 282 Show current, custom, or default keyword template maps and their
283 283 expansions.
284 284
285 285 Extend current configuration by specifying maps as arguments and
286 286 optionally by reading from an additional hgrc file.
287 287
288 288 Override current keyword template maps with "default" option.
289 289 '''
290 290 def demostatus(stat):
291 291 ui.status(_('\n\t%s\n') % stat)
292 292
293 293 def demoitems(section, items):
294 294 ui.write('[%s]\n' % section)
295 295 for k, v in items:
296 296 ui.write('%s = %s\n' % (k, v))
297 297
298 298 msg = 'hg keyword config and expansion example'
299 299 kwstatus = 'current'
300 300 fn = 'demo.txt'
301 301 branchname = 'demobranch'
302 302 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
303 303 ui.note(_('creating temporary repository at %s\n') % tmpdir)
304 304 repo = localrepo.localrepository(ui, tmpdir, True)
305 305 ui.setconfig('keyword', fn, '')
306 306 if args or opts.get('rcfile'):
307 307 kwstatus = 'custom'
308 308 if opts.get('rcfile'):
309 309 ui.readconfig(opts.get('rcfile'))
310 310 if opts.get('default'):
311 311 kwstatus = 'default'
312 312 kwmaps = kwtemplater.templates
313 313 if ui.configitems('keywordmaps'):
314 314 # override maps from optional rcfile
315 315 for k, v in kwmaps.iteritems():
316 316 ui.setconfig('keywordmaps', k, v)
317 317 elif args:
318 318 # simulate hgrc parsing
319 319 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
320 320 fp = repo.opener('hgrc', 'w')
321 321 fp.writelines(rcmaps)
322 322 fp.close()
323 323 ui.readconfig(repo.join('hgrc'))
324 324 if not opts.get('default'):
325 325 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
326 326 uisetup(ui)
327 327 reposetup(ui, repo)
328 328 for k, v in ui.configitems('extensions'):
329 329 if k.endswith('keyword'):
330 330 extension = '%s = %s' % (k, v)
331 331 break
332 332 demostatus('config using %s keyword template maps' % kwstatus)
333 333 ui.write('[extensions]\n%s\n' % extension)
334 334 demoitems('keyword', ui.configitems('keyword'))
335 335 demoitems('keywordmaps', kwmaps.iteritems())
336 336 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
337 337 repo.wopener(fn, 'w').write(keywords)
338 338 repo.add([fn])
339 339 path = repo.wjoin(fn)
340 340 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
341 341 ui.note(keywords)
342 342 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
343 343 # silence branch command if not verbose
344 344 quiet = ui.quiet
345 345 ui.quiet = not ui.verbose
346 346 commands.branch(ui, repo, branchname)
347 347 ui.quiet = quiet
348 348 for name, cmd in ui.configitems('hooks'):
349 349 if name.split('.', 1)[0].find('commit') > -1:
350 350 repo.ui.setconfig('hooks', name, '')
351 351 ui.note(_('unhooked all commit hooks\n'))
352 352 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
353 353 repo.commit(text=msg)
354 354 fmt = ui.verbose and ' in %s' % path or ''
355 355 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
356 356 ui.write(repo.wread(fn))
357 357 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
358 358 shutil.rmtree(tmpdir, ignore_errors=True)
359 359
360 360 def expand(ui, repo, *pats, **opts):
361 361 '''expand keywords in the working directory
362 362
363 363 Run after (re)enabling keyword expansion.
364 364
365 365 kwexpand refuses to run if given files contain local changes.
366 366 '''
367 367 # 3rd argument sets expansion to True
368 368 _kwfwrite(ui, repo, True, *pats, **opts)
369 369
370 370 def files(ui, repo, *pats, **opts):
371 371 '''print files currently configured for keyword expansion
372 372
373 373 Crosscheck which files in working directory are potential targets
374 374 for keyword expansion. That is, files matched by [keyword] config
375 375 patterns but not symlinks.
376 376 '''
377 377 kwt = kwtools['templater']
378 378 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
379 379 modified, added, removed, deleted, unknown, ignored, clean = status
380 380 files = sorted(modified + added + clean + unknown)
381 381 wctx = repo[None]
382 382 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
383 383 cwd = pats and repo.getcwd() or ''
384 384 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
385 385 if opts.get('all') or opts.get('ignore'):
386 386 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
387 387 for char, filenames in kwfstats:
388 388 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
389 389 for f in filenames:
390 390 ui.write(fmt % repo.pathto(f, cwd))
391 391
392 392 def shrink(ui, repo, *pats, **opts):
393 393 '''revert expanded keywords in the working directory
394 394
395 395 Run before changing/disabling active keywords or if you experience
396 396 problems with "hg import" or "hg merge".
397 397
398 398 kwshrink refuses to run if given files contain local changes.
399 399 '''
400 400 # 3rd argument sets expansion to False
401 401 _kwfwrite(ui, repo, False, *pats, **opts)
402 402
403 403
404 404 def uisetup(ui):
405 405 '''Collects [keyword] config in kwtools.
406 406 Monkeypatches dispatch._parse if needed.'''
407 407
408 408 for pat, opt in ui.configitems('keyword'):
409 409 if opt != 'ignore':
410 410 kwtools['inc'].append(pat)
411 411 else:
412 412 kwtools['exc'].append(pat)
413 413
414 414 if kwtools['inc']:
415 415 def kwdispatch_parse(orig, ui, args):
416 416 '''Monkeypatch dispatch._parse to obtain running hg command.'''
417 417 cmd, func, args, options, cmdoptions = orig(ui, args)
418 418 kwtools['hgcmd'] = cmd
419 419 return cmd, func, args, options, cmdoptions
420 420
421 421 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
422 422
423 423 def reposetup(ui, repo):
424 424 '''Sets up repo as kwrepo for keyword substitution.
425 425 Overrides file method to return kwfilelog instead of filelog
426 426 if file matches user configuration.
427 427 Wraps commit to overwrite configured files with updated
428 428 keyword substitutions.
429 429 Monkeypatches patch and webcommands.'''
430 430
431 431 try:
432 432 if (not repo.local() or not kwtools['inc']
433 433 or kwtools['hgcmd'] in nokwcommands.split()
434 434 or '.hg' in util.splitpath(repo.root)
435 435 or repo._url.startswith('bundle:')):
436 436 return
437 437 except AttributeError:
438 438 pass
439 439
440 440 kwtools['templater'] = kwt = kwtemplater(ui, repo)
441 441
442 442 class kwrepo(repo.__class__):
443 443 def file(self, f):
444 444 if f[0] == '/':
445 445 f = f[1:]
446 446 return kwfilelog(self.sopener, kwt, f)
447 447
448 448 def wread(self, filename):
449 449 data = super(kwrepo, self).wread(filename)
450 450 return kwt.wread(filename, data)
451 451
452 452 def commit(self, text='', user=None, date=None, match=None,
453 453 force=False, editor=None, extra={}):
454 454 wlock = lock = None
455 455 _p1 = _p2 = None
456 456 try:
457 457 wlock = self.wlock()
458 458 lock = self.lock()
459 459 # store and postpone commit hooks
460 460 commithooks = {}
461 461 for name, cmd in ui.configitems('hooks'):
462 462 if name.split('.', 1)[0] == 'commit':
463 463 commithooks[name] = cmd
464 464 ui.setconfig('hooks', name, None)
465 465 if commithooks:
466 466 # store parents for commit hook environment
467 467 _p1, _p2 = repo.dirstate.parents()
468 468 _p1 = hex(_p1)
469 469 if _p2 == nullid:
470 470 _p2 = ''
471 471 else:
472 472 _p2 = hex(_p2)
473 473
474 474 n = super(kwrepo, self).commit(text, user, date, match, force,
475 475 editor, extra)
476 476
477 477 # restore commit hooks
478 478 for name, cmd in commithooks.iteritems():
479 479 ui.setconfig('hooks', name, cmd)
480 480 if n is not None:
481 481 kwt.overwrite(n, True, None)
482 482 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
483 483 return n
484 484 finally:
485 485 release(lock, wlock)
486 486
487 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 489 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
490 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 492 # shrink keywords read from working dir
493 493 self.lines = kwt.shrinklines(self.fname, self.lines)
494 494
495 495 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
496 496 opts=None):
497 497 '''Monkeypatch patch.diff to avoid expansion except when
498 498 comparing against working dir.'''
499 499 if node2 is not None:
500 500 kwt.match = util.never
501 501 elif node1 is not None and node1 != repo['.'].node():
502 502 kwt.restrict = True
503 503 return orig(repo, node1, node2, match, changes, opts)
504 504
505 505 def kwweb_skip(orig, web, req, tmpl):
506 506 '''Wraps webcommands.x turning off keyword expansion.'''
507 507 kwt.match = util.never
508 508 return orig(web, req, tmpl)
509 509
510 510 repo.__class__ = kwrepo
511 511
512 512 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
513 513 extensions.wrapfunction(patch, 'diff', kw_diff)
514 514 for c in 'annotate changeset rev filediff diff'.split():
515 515 extensions.wrapfunction(webcommands, c, kwweb_skip)
516 516
517 517 cmdtable = {
518 518 'kwdemo':
519 519 (demo,
520 520 [('d', 'default', None, _('show default keyword template maps')),
521 521 ('f', 'rcfile', [], _('read maps from rcfile'))],
522 522 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
523 523 'kwexpand': (expand, commands.walkopts,
524 524 _('hg kwexpand [OPTION]... [FILE]...')),
525 525 'kwfiles':
526 526 (files,
527 527 [('a', 'all', None, _('show keyword status flags of all files')),
528 528 ('i', 'ignore', None, _('show files excluded from expansion')),
529 529 ('u', 'untracked', None, _('additionally show untracked files')),
530 530 ] + commands.walkopts,
531 531 _('hg kwfiles [OPTION]... [FILE]...')),
532 532 'kwshrink': (shrink, commands.walkopts,
533 533 _('hg kwshrink [OPTION]... [FILE]...')),
534 534 }
@@ -1,3485 +1,3485 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, textwrap, subprocess, difflib, time
12 12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 13 import patch, help, mdiff, tempfile, url, encoding
14 14 import archival, changegroup, cmdutil, sshserver, hbisect
15 15 from hgweb import server
16 16 import merge as merge_
17 17
18 18 # Commands start here, listed alphabetically
19 19
20 20 def add(ui, repo, *pats, **opts):
21 21 """add the specified files on the next commit
22 22
23 23 Schedule files to be version controlled and added to the
24 24 repository.
25 25
26 26 The files will be added to the repository at the next commit. To
27 27 undo an add before that, see hg revert.
28 28
29 29 If no names are given, add all files to the repository.
30 30 """
31 31
32 32 bad = []
33 33 exacts = {}
34 34 names = []
35 35 m = cmdutil.match(repo, pats, opts)
36 36 oldbad = m.bad
37 37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38 38
39 39 for f in repo.walk(m):
40 40 exact = m.exact(f)
41 41 if exact or f not in repo.dirstate:
42 42 names.append(f)
43 43 if ui.verbose or not exact:
44 44 ui.status(_('adding %s\n') % m.rel(f))
45 45 if not opts.get('dry_run'):
46 46 bad += [f for f in repo.add(names) if f in m.files()]
47 47 return bad and 1 or 0
48 48
49 49 def addremove(ui, repo, *pats, **opts):
50 50 """add all new files, delete all missing files
51 51
52 52 Add all new files and remove all missing files from the
53 53 repository.
54 54
55 55 New files are ignored if they match any of the patterns in
56 56 .hgignore. As with add, these changes take effect at the next
57 57 commit.
58 58
59 59 Use the -s/--similarity option to detect renamed files. With a
60 60 parameter > 0, this compares every removed file with every added
61 61 file and records those similar enough as renames. This option
62 62 takes a percentage between 0 (disabled) and 100 (files must be
63 63 identical) as its parameter. Detecting renamed files this way can
64 64 be expensive.
65 65 """
66 66 try:
67 67 sim = float(opts.get('similarity') or 0)
68 68 except ValueError:
69 69 raise util.Abort(_('similarity must be a number'))
70 70 if sim < 0 or sim > 100:
71 71 raise util.Abort(_('similarity must be between 0 and 100'))
72 72 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
73 73
74 74 def annotate(ui, repo, *pats, **opts):
75 75 """show changeset information by line for each file
76 76
77 77 List changes in files, showing the revision id responsible for
78 78 each line
79 79
80 80 This command is useful for discovering when a change was made and
81 81 by whom.
82 82
83 83 Without the -a/--text option, annotate will avoid processing files
84 84 it detects as binary. With -a, annotate will annotate the file
85 85 anyway, although the results will probably be neither useful
86 86 nor desirable.
87 87 """
88 88 datefunc = ui.quiet and util.shortdate or util.datestr
89 89 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
90 90
91 91 if not pats:
92 92 raise util.Abort(_('at least one filename or pattern is required'))
93 93
94 94 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
95 95 ('number', lambda x: str(x[0].rev())),
96 96 ('changeset', lambda x: short(x[0].node())),
97 97 ('date', getdate),
98 98 ('follow', lambda x: x[0].path()),
99 99 ]
100 100
101 101 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
102 102 and not opts.get('follow')):
103 103 opts['number'] = 1
104 104
105 105 linenumber = opts.get('line_number') is not None
106 106 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
107 107 raise util.Abort(_('at least one of -n/-c is required for -l'))
108 108
109 109 funcmap = [func for op, func in opmap if opts.get(op)]
110 110 if linenumber:
111 111 lastfunc = funcmap[-1]
112 112 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
113 113
114 114 ctx = repo[opts.get('rev')]
115 115
116 116 m = cmdutil.match(repo, pats, opts)
117 117 for abs in ctx.walk(m):
118 118 fctx = ctx[abs]
119 119 if not opts.get('text') and util.binary(fctx.data()):
120 120 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
121 121 continue
122 122
123 123 lines = fctx.annotate(follow=opts.get('follow'),
124 124 linenumber=linenumber)
125 125 pieces = []
126 126
127 127 for f in funcmap:
128 128 l = [f(n) for n, dummy in lines]
129 129 if l:
130 130 ml = max(map(len, l))
131 131 pieces.append(["%*s" % (ml, x) for x in l])
132 132
133 133 if pieces:
134 134 for p, l in zip(zip(*pieces), lines):
135 135 ui.write("%s: %s" % (" ".join(p), l[1]))
136 136
137 137 def archive(ui, repo, dest, **opts):
138 138 '''create an unversioned archive of a repository revision
139 139
140 140 By default, the revision used is the parent of the working
141 141 directory; use -r/--rev to specify a different revision.
142 142
143 143 To specify the type of archive to create, use -t/--type. Valid
144 144 types are:
145 145
146 146 "files" (default): a directory full of files
147 147 "tar": tar archive, uncompressed
148 148 "tbz2": tar archive, compressed using bzip2
149 149 "tgz": tar archive, compressed using gzip
150 150 "uzip": zip archive, uncompressed
151 151 "zip": zip archive, compressed using deflate
152 152
153 153 The exact name of the destination archive or directory is given
154 154 using a format string; see 'hg help export' for details.
155 155
156 156 Each member added to an archive file has a directory prefix
157 157 prepended. Use -p/--prefix to specify a format string for the
158 158 prefix. The default is the basename of the archive, with suffixes
159 159 removed.
160 160 '''
161 161
162 162 ctx = repo[opts.get('rev')]
163 163 if not ctx:
164 164 raise util.Abort(_('no working directory: please specify a revision'))
165 165 node = ctx.node()
166 166 dest = cmdutil.make_filename(repo, dest, node)
167 167 if os.path.realpath(dest) == repo.root:
168 168 raise util.Abort(_('repository root cannot be destination'))
169 169 matchfn = cmdutil.match(repo, [], opts)
170 170 kind = opts.get('type') or 'files'
171 171 prefix = opts.get('prefix')
172 172 if dest == '-':
173 173 if kind == 'files':
174 174 raise util.Abort(_('cannot archive plain files to stdout'))
175 175 dest = sys.stdout
176 176 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
177 177 prefix = cmdutil.make_filename(repo, prefix, node)
178 178 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
179 179 matchfn, prefix)
180 180
181 181 def backout(ui, repo, node=None, rev=None, **opts):
182 182 '''reverse effect of earlier changeset
183 183
184 184 Commit the backed out changes as a new changeset. The new
185 185 changeset is a child of the backed out changeset.
186 186
187 187 If you backout a changeset other than the tip, a new head is
188 188 created. This head will be the new tip and you should merge this
189 189 backout changeset with another head.
190 190
191 191 The --merge option remembers the parent of the working directory
192 192 before starting the backout, then merges the new head with that
193 193 changeset afterwards. This saves you from doing the merge by hand.
194 194 The result of this merge is not committed, as with a normal merge.
195 195
196 196 See 'hg help dates' for a list of formats valid for -d/--date.
197 197 '''
198 198 if rev and node:
199 199 raise util.Abort(_("please specify just one revision"))
200 200
201 201 if not rev:
202 202 rev = node
203 203
204 204 if not rev:
205 205 raise util.Abort(_("please specify a revision to backout"))
206 206
207 207 date = opts.get('date')
208 208 if date:
209 209 opts['date'] = util.parsedate(date)
210 210
211 211 cmdutil.bail_if_changed(repo)
212 212 node = repo.lookup(rev)
213 213
214 214 op1, op2 = repo.dirstate.parents()
215 215 a = repo.changelog.ancestor(op1, node)
216 216 if a != node:
217 217 raise util.Abort(_('cannot backout change on a different branch'))
218 218
219 219 p1, p2 = repo.changelog.parents(node)
220 220 if p1 == nullid:
221 221 raise util.Abort(_('cannot backout a change with no parents'))
222 222 if p2 != nullid:
223 223 if not opts.get('parent'):
224 224 raise util.Abort(_('cannot backout a merge changeset without '
225 225 '--parent'))
226 226 p = repo.lookup(opts['parent'])
227 227 if p not in (p1, p2):
228 228 raise util.Abort(_('%s is not a parent of %s') %
229 229 (short(p), short(node)))
230 230 parent = p
231 231 else:
232 232 if opts.get('parent'):
233 233 raise util.Abort(_('cannot use --parent on non-merge changeset'))
234 234 parent = p1
235 235
236 236 # the backout should appear on the same branch
237 237 branch = repo.dirstate.branch()
238 238 hg.clean(repo, node, show_stats=False)
239 239 repo.dirstate.setbranch(branch)
240 240 revert_opts = opts.copy()
241 241 revert_opts['date'] = None
242 242 revert_opts['all'] = True
243 243 revert_opts['rev'] = hex(parent)
244 244 revert_opts['no_backup'] = None
245 245 revert(ui, repo, **revert_opts)
246 246 commit_opts = opts.copy()
247 247 commit_opts['addremove'] = False
248 248 if not commit_opts['message'] and not commit_opts['logfile']:
249 249 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
250 250 commit_opts['force_editor'] = True
251 251 commit(ui, repo, **commit_opts)
252 252 def nice(node):
253 253 return '%d:%s' % (repo.changelog.rev(node), short(node))
254 254 ui.status(_('changeset %s backs out changeset %s\n') %
255 255 (nice(repo.changelog.tip()), nice(node)))
256 256 if op1 != node:
257 257 hg.clean(repo, op1, show_stats=False)
258 258 if opts.get('merge'):
259 259 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
260 260 hg.merge(repo, hex(repo.changelog.tip()))
261 261 else:
262 262 ui.status(_('the backout changeset is a new head - '
263 263 'do not forget to merge\n'))
264 264 ui.status(_('(use "backout --merge" '
265 265 'if you want to auto-merge)\n'))
266 266
267 267 def bisect(ui, repo, rev=None, extra=None, command=None,
268 268 reset=None, good=None, bad=None, skip=None, noupdate=None):
269 269 """subdivision search of changesets
270 270
271 271 This command helps to find changesets which introduce problems. To
272 272 use, mark the earliest changeset you know exhibits the problem as
273 273 bad, then mark the latest changeset which is free from the problem
274 274 as good. Bisect will update your working directory to a revision
275 275 for testing (unless the -U/--noupdate option is specified). Once
276 276 you have performed tests, mark the working directory as good or
277 277 bad, and bisect will either update to another candidate changeset
278 278 or announce that it has found the bad revision.
279 279
280 280 As a shortcut, you can also use the revision argument to mark a
281 281 revision as good or bad without checking it out first.
282 282
283 283 If you supply a command, it will be used for automatic bisection.
284 284 Its exit status will be used to mark revisions as good or bad:
285 285 status 0 means good, 125 means to skip the revision, 127
286 286 (command not found) will abort the bisection, and any other
287 287 non-zero exit status means the revision is bad.
288 288 """
289 289 def print_result(nodes, good):
290 290 displayer = cmdutil.show_changeset(ui, repo, {})
291 291 if len(nodes) == 1:
292 292 # narrowed it down to a single revision
293 293 if good:
294 294 ui.write(_("The first good revision is:\n"))
295 295 else:
296 296 ui.write(_("The first bad revision is:\n"))
297 297 displayer.show(repo[nodes[0]])
298 298 else:
299 299 # multiple possible revisions
300 300 if good:
301 301 ui.write(_("Due to skipped revisions, the first "
302 302 "good revision could be any of:\n"))
303 303 else:
304 304 ui.write(_("Due to skipped revisions, the first "
305 305 "bad revision could be any of:\n"))
306 306 for n in nodes:
307 307 displayer.show(repo[n])
308 308
309 309 def check_state(state, interactive=True):
310 310 if not state['good'] or not state['bad']:
311 311 if (good or bad or skip or reset) and interactive:
312 312 return
313 313 if not state['good']:
314 314 raise util.Abort(_('cannot bisect (no known good revisions)'))
315 315 else:
316 316 raise util.Abort(_('cannot bisect (no known bad revisions)'))
317 317 return True
318 318
319 319 # backward compatibility
320 320 if rev in "good bad reset init".split():
321 321 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
322 322 cmd, rev, extra = rev, extra, None
323 323 if cmd == "good":
324 324 good = True
325 325 elif cmd == "bad":
326 326 bad = True
327 327 else:
328 328 reset = True
329 329 elif extra or good + bad + skip + reset + bool(command) > 1:
330 330 raise util.Abort(_('incompatible arguments'))
331 331
332 332 if reset:
333 333 p = repo.join("bisect.state")
334 334 if os.path.exists(p):
335 335 os.unlink(p)
336 336 return
337 337
338 338 state = hbisect.load_state(repo)
339 339
340 340 if command:
341 341 commandpath = util.find_exe(command)
342 342 if commandpath is None:
343 343 raise util.Abort(_("cannot find executable: %s") % command)
344 344 changesets = 1
345 345 try:
346 346 while changesets:
347 347 # update state
348 348 status = subprocess.call([commandpath])
349 349 if status == 125:
350 350 transition = "skip"
351 351 elif status == 0:
352 352 transition = "good"
353 353 # status < 0 means process was killed
354 354 elif status == 127:
355 355 raise util.Abort(_("failed to execute %s") % command)
356 356 elif status < 0:
357 357 raise util.Abort(_("%s killed") % command)
358 358 else:
359 359 transition = "bad"
360 360 ctx = repo[rev or '.']
361 361 state[transition].append(ctx.node())
362 362 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
363 363 check_state(state, interactive=False)
364 364 # bisect
365 365 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
366 366 # update to next check
367 367 cmdutil.bail_if_changed(repo)
368 368 hg.clean(repo, nodes[0], show_stats=False)
369 369 finally:
370 370 hbisect.save_state(repo, state)
371 371 return print_result(nodes, not status)
372 372
373 373 # update state
374 374 node = repo.lookup(rev or '.')
375 375 if good:
376 376 state['good'].append(node)
377 377 elif bad:
378 378 state['bad'].append(node)
379 379 elif skip:
380 380 state['skip'].append(node)
381 381
382 382 hbisect.save_state(repo, state)
383 383
384 384 if not check_state(state):
385 385 return
386 386
387 387 # actually bisect
388 388 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
389 389 if changesets == 0:
390 390 print_result(nodes, good)
391 391 else:
392 392 assert len(nodes) == 1 # only a single node can be tested next
393 393 node = nodes[0]
394 394 # compute the approximate number of remaining tests
395 395 tests, size = 0, 2
396 396 while size <= changesets:
397 397 tests, size = tests + 1, size * 2
398 398 rev = repo.changelog.rev(node)
399 399 ui.write(_("Testing changeset %s:%s "
400 400 "(%s changesets remaining, ~%s tests)\n")
401 401 % (rev, short(node), changesets, tests))
402 402 if not noupdate:
403 403 cmdutil.bail_if_changed(repo)
404 404 return hg.clean(repo, node)
405 405
406 406 def branch(ui, repo, label=None, **opts):
407 407 """set or show the current branch name
408 408
409 409 With no argument, show the current branch name. With one argument,
410 410 set the working directory branch name (the branch will not exist
411 411 in the repository until the next commit). Standard practice
412 412 recommends that primary development take place on the 'default'
413 413 branch.
414 414
415 415 Unless -f/--force is specified, branch will not let you set a
416 416 branch name that already exists, even if it's inactive.
417 417
418 418 Use -C/--clean to reset the working directory branch to that of
419 419 the parent of the working directory, negating a previous branch
420 420 change.
421 421
422 422 Use the command 'hg update' to switch to an existing branch.
423 423 """
424 424
425 425 if opts.get('clean'):
426 426 label = repo[None].parents()[0].branch()
427 427 repo.dirstate.setbranch(label)
428 428 ui.status(_('reset working directory to branch %s\n') % label)
429 429 elif label:
430 430 if not opts.get('force') and label in repo.branchtags():
431 431 if label not in [p.branch() for p in repo.parents()]:
432 432 raise util.Abort(_('a branch of the same name already exists'
433 433 ' (use --force to override)'))
434 434 repo.dirstate.setbranch(encoding.fromlocal(label))
435 435 ui.status(_('marked working directory as branch %s\n') % label)
436 436 else:
437 437 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
438 438
439 439 def branches(ui, repo, active=False):
440 440 """list repository named branches
441 441
442 442 List the repository's named branches, indicating which ones are
443 443 inactive. If active is specified, only show active branches.
444 444
445 445 A branch is considered active if it contains repository heads.
446 446
447 447 Use the command 'hg update' to switch to an existing branch.
448 448 """
449 449 hexfunc = ui.debugflag and hex or short
450 450 activebranches = [encoding.tolocal(repo[n].branch())
451 451 for n in repo.heads()]
452 452 def testactive(tag, node):
453 453 realhead = tag in activebranches
454 454 open = node in repo.branchheads(tag, closed=False)
455 455 return realhead and open
456 456 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
457 457 for tag, node in repo.branchtags().items()],
458 458 reverse=True)
459 459
460 460 for isactive, node, tag in branches:
461 461 if (not active) or isactive:
462 462 if ui.quiet:
463 463 ui.write("%s\n" % tag)
464 464 else:
465 465 hn = repo.lookup(node)
466 466 if isactive:
467 467 notice = ''
468 468 elif hn not in repo.branchheads(tag, closed=False):
469 469 notice = ' (closed)'
470 470 else:
471 471 notice = ' (inactive)'
472 472 rev = str(node).rjust(31 - encoding.colwidth(tag))
473 473 data = tag, rev, hexfunc(hn), notice
474 474 ui.write("%s %s:%s%s\n" % data)
475 475
476 476 def bundle(ui, repo, fname, dest=None, **opts):
477 477 """create a changegroup file
478 478
479 479 Generate a compressed changegroup file collecting changesets not
480 480 known to be in another repository.
481 481
482 482 If no destination repository is specified the destination is
483 483 assumed to have all the nodes specified by one or more --base
484 484 parameters. To create a bundle containing all changesets, use
485 485 -a/--all (or --base null). To change the compression method
486 486 applied, use the -t/--type option (by default, bundles are
487 487 compressed using bz2).
488 488
489 489 The bundle file can then be transferred using conventional means
490 490 and applied to another repository with the unbundle or pull
491 491 command. This is useful when direct push and pull are not
492 492 available or when exporting an entire repository is undesirable.
493 493
494 494 Applying bundles preserves all changeset contents including
495 495 permissions, copy/rename information, and revision history.
496 496 """
497 497 revs = opts.get('rev') or None
498 498 if revs:
499 499 revs = [repo.lookup(rev) for rev in revs]
500 500 if opts.get('all'):
501 501 base = ['null']
502 502 else:
503 503 base = opts.get('base')
504 504 if base:
505 505 if dest:
506 506 raise util.Abort(_("--base is incompatible with specifying "
507 507 "a destination"))
508 508 base = [repo.lookup(rev) for rev in base]
509 509 # create the right base
510 510 # XXX: nodesbetween / changegroup* should be "fixed" instead
511 511 o = []
512 512 has = set((nullid,))
513 513 for n in base:
514 514 has.update(repo.changelog.reachable(n))
515 515 if revs:
516 516 visit = list(revs)
517 517 else:
518 518 visit = repo.changelog.heads()
519 519 seen = {}
520 520 while visit:
521 521 n = visit.pop(0)
522 522 parents = [p for p in repo.changelog.parents(n) if p not in has]
523 523 if len(parents) == 0:
524 524 o.insert(0, n)
525 525 else:
526 526 for p in parents:
527 527 if p not in seen:
528 528 seen[p] = 1
529 529 visit.append(p)
530 530 else:
531 531 dest, revs, checkout = hg.parseurl(
532 532 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
533 533 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
534 534 o = repo.findoutgoing(other, force=opts.get('force'))
535 535
536 536 if revs:
537 537 cg = repo.changegroupsubset(o, revs, 'bundle')
538 538 else:
539 539 cg = repo.changegroup(o, 'bundle')
540 540
541 541 bundletype = opts.get('type', 'bzip2').lower()
542 542 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
543 543 bundletype = btypes.get(bundletype)
544 544 if bundletype not in changegroup.bundletypes:
545 545 raise util.Abort(_('unknown bundle type specified with --type'))
546 546
547 547 changegroup.writebundle(cg, fname, bundletype)
548 548
549 549 def cat(ui, repo, file1, *pats, **opts):
550 550 """output the current or given revision of files
551 551
552 552 Print the specified files as they were at the given revision. If
553 553 no revision is given, the parent of the working directory is used,
554 554 or tip if no revision is checked out.
555 555
556 556 Output may be to a file, in which case the name of the file is
557 557 given using a format string. The formatting rules are the same as
558 558 for the export command, with the following additions:
559 559
560 560 %s basename of file being printed
561 561 %d dirname of file being printed, or '.' if in repository root
562 562 %p root-relative path name of file being printed
563 563 """
564 564 ctx = repo[opts.get('rev')]
565 565 err = 1
566 566 m = cmdutil.match(repo, (file1,) + pats, opts)
567 567 for abs in ctx.walk(m):
568 568 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
569 569 data = ctx[abs].data()
570 570 if opts.get('decode'):
571 571 data = repo.wwritedata(abs, data)
572 572 fp.write(data)
573 573 err = 0
574 574 return err
575 575
576 576 def clone(ui, source, dest=None, **opts):
577 577 """make a copy of an existing repository
578 578
579 579 Create a copy of an existing repository in a new directory.
580 580
581 581 If no destination directory name is specified, it defaults to the
582 582 basename of the source.
583 583
584 584 The location of the source is added to the new repository's
585 585 .hg/hgrc file, as the default to be used for future pulls.
586 586
587 587 If you use the -r/--rev option to clone up to a specific revision,
588 588 no subsequent revisions (including subsequent tags) will be
589 589 present in the cloned repository. This option implies --pull, even
590 590 on local repositories.
591 591
592 592 By default, clone will check out the head of the 'default' branch.
593 593 If the -U/--noupdate option is used, the new clone will contain
594 594 only a repository (.hg) and no working copy (the working copy
595 595 parent is the null revision).
596 596
597 597 See 'hg help urls' for valid source format details.
598 598
599 599 It is possible to specify an ssh:// URL as the destination, but no
600 600 .hg/hgrc and working directory will be created on the remote side.
601 601 Please see 'hg help urls' for important details about ssh:// URLs.
602 602
603 603 For efficiency, hardlinks are used for cloning whenever the source
604 604 and destination are on the same filesystem (note this applies only
605 605 to the repository data, not to the checked out files). Some
606 606 filesystems, such as AFS, implement hardlinking incorrectly, but
607 607 do not report errors. In these cases, use the --pull option to
608 608 avoid hardlinking.
609 609
610 610 In some cases, you can clone repositories and checked out files
611 611 using full hardlinks with
612 612
613 613 $ cp -al REPO REPOCLONE
614 614
615 615 This is the fastest way to clone, but it is not always safe. The
616 616 operation is not atomic (making sure REPO is not modified during
617 617 the operation is up to you) and you have to make sure your editor
618 618 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
619 619 this is not compatible with certain extensions that place their
620 620 metadata under the .hg directory, such as mq.
621 621
622 622 """
623 623 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
624 624 pull=opts.get('pull'),
625 625 stream=opts.get('uncompressed'),
626 626 rev=opts.get('rev'),
627 627 update=not opts.get('noupdate'))
628 628
629 629 def commit(ui, repo, *pats, **opts):
630 630 """commit the specified files or all outstanding changes
631 631
632 632 Commit changes to the given files into the repository. Unlike a
633 633 centralized RCS, this operation is a local operation. See hg push
634 634 for a way to actively distribute your changes.
635 635
636 636 If a list of files is omitted, all changes reported by "hg status"
637 637 will be committed.
638 638
639 639 If you are committing the result of a merge, do not provide any
640 640 filenames or -I/-X filters.
641 641
642 642 If no commit message is specified, the configured editor is
643 643 started to prompt you for a message.
644 644
645 645 See 'hg help dates' for a list of formats valid for -d/--date.
646 646 """
647 647 extra = {}
648 648 if opts.get('close_branch'):
649 649 extra['close'] = 1
650 650 e = cmdutil.commiteditor
651 651 if opts.get('force_editor'):
652 652 e = cmdutil.commitforceeditor
653 653
654 654 def commitfunc(ui, repo, message, match, opts):
655 655 return repo.commit(message, opts.get('user'), opts.get('date'), match,
656 656 editor=e, extra=extra)
657 657
658 658 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
659 659 if not node:
660 660 return
661 661 cl = repo.changelog
662 662 rev = cl.rev(node)
663 663 parents = cl.parentrevs(rev)
664 664 if rev - 1 in parents:
665 665 # one of the parents was the old tip
666 666 pass
667 667 elif (parents == (nullrev, nullrev) or
668 668 len(cl.heads(cl.node(parents[0]))) > 1 and
669 669 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
670 670 ui.status(_('created new head\n'))
671 671
672 672 if ui.debugflag:
673 673 ui.write(_('committed changeset %d:%s\n') % (rev,hex(node)))
674 674 elif ui.verbose:
675 675 ui.write(_('committed changeset %d:%s\n') % (rev,short(node)))
676 676
677 677 def copy(ui, repo, *pats, **opts):
678 678 """mark files as copied for the next commit
679 679
680 680 Mark dest as having copies of source files. If dest is a
681 681 directory, copies are put in that directory. If dest is a file,
682 682 the source must be a single file.
683 683
684 684 By default, this command copies the contents of files as they
685 685 exist in the working directory. If invoked with -A/--after, the
686 686 operation is recorded, but no copying is performed.
687 687
688 688 This command takes effect with the next commit. To undo a copy
689 689 before that, see hg revert.
690 690 """
691 691 wlock = repo.wlock(False)
692 692 try:
693 693 return cmdutil.copy(ui, repo, pats, opts)
694 694 finally:
695 695 wlock.release()
696 696
697 697 def debugancestor(ui, repo, *args):
698 698 """find the ancestor revision of two revisions in a given index"""
699 699 if len(args) == 3:
700 700 index, rev1, rev2 = args
701 701 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
702 702 lookup = r.lookup
703 703 elif len(args) == 2:
704 704 if not repo:
705 705 raise util.Abort(_("There is no Mercurial repository here "
706 706 "(.hg not found)"))
707 707 rev1, rev2 = args
708 708 r = repo.changelog
709 709 lookup = repo.lookup
710 710 else:
711 711 raise util.Abort(_('either two or three arguments required'))
712 712 a = r.ancestor(lookup(rev1), lookup(rev2))
713 713 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
714 714
715 715 def debugcommands(ui, cmd='', *args):
716 716 for cmd, vals in sorted(table.iteritems()):
717 717 cmd = cmd.split('|')[0].strip('^')
718 718 opts = ', '.join([i[1] for i in vals[1]])
719 719 ui.write('%s: %s\n' % (cmd, opts))
720 720
721 721 def debugcomplete(ui, cmd='', **opts):
722 722 """returns the completion list associated with the given command"""
723 723
724 724 if opts.get('options'):
725 725 options = []
726 726 otables = [globalopts]
727 727 if cmd:
728 728 aliases, entry = cmdutil.findcmd(cmd, table, False)
729 729 otables.append(entry[1])
730 730 for t in otables:
731 731 for o in t:
732 732 if o[0]:
733 733 options.append('-%s' % o[0])
734 734 options.append('--%s' % o[1])
735 735 ui.write("%s\n" % "\n".join(options))
736 736 return
737 737
738 738 cmdlist = cmdutil.findpossible(cmd, table)
739 739 if ui.verbose:
740 740 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
741 741 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
742 742
743 743 def debugfsinfo(ui, path = "."):
744 744 file('.debugfsinfo', 'w').write('')
745 745 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
746 746 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
747 747 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
748 748 and 'yes' or 'no'))
749 749 os.unlink('.debugfsinfo')
750 750
751 751 def debugrebuildstate(ui, repo, rev="tip"):
752 752 """rebuild the dirstate as it would look like for the given revision"""
753 753 ctx = repo[rev]
754 754 wlock = repo.wlock()
755 755 try:
756 756 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
757 757 finally:
758 758 wlock.release()
759 759
760 760 def debugcheckstate(ui, repo):
761 761 """validate the correctness of the current dirstate"""
762 762 parent1, parent2 = repo.dirstate.parents()
763 763 m1 = repo[parent1].manifest()
764 764 m2 = repo[parent2].manifest()
765 765 errors = 0
766 766 for f in repo.dirstate:
767 767 state = repo.dirstate[f]
768 768 if state in "nr" and f not in m1:
769 769 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
770 770 errors += 1
771 771 if state in "a" and f in m1:
772 772 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
773 773 errors += 1
774 774 if state in "m" and f not in m1 and f not in m2:
775 775 ui.warn(_("%s in state %s, but not in either manifest\n") %
776 776 (f, state))
777 777 errors += 1
778 778 for f in m1:
779 779 state = repo.dirstate[f]
780 780 if state not in "nrm":
781 781 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
782 782 errors += 1
783 783 if errors:
784 784 error = _(".hg/dirstate inconsistent with current parent's manifest")
785 785 raise util.Abort(error)
786 786
787 787 def showconfig(ui, repo, *values, **opts):
788 788 """show combined config settings from all hgrc files
789 789
790 790 With no arguments, print names and values of all config items.
791 791
792 792 With one argument of the form section.name, print just the value
793 793 of that config item.
794 794
795 795 With multiple arguments, print names and values of all config
796 796 items with matching section names.
797 797
798 798 With --debug, the source (filename and line number) is printed
799 799 for each config item.
800 800 """
801 801
802 802 untrusted = bool(opts.get('untrusted'))
803 803 if values:
804 804 if len([v for v in values if '.' in v]) > 1:
805 805 raise util.Abort(_('only one config item permitted'))
806 806 for section, name, value in ui.walkconfig(untrusted=untrusted):
807 807 sectname = section + '.' + name
808 808 if values:
809 809 for v in values:
810 810 if v == section:
811 811 ui.debug('%s: ' %
812 812 ui.configsource(section, name, untrusted))
813 813 ui.write('%s=%s\n' % (sectname, value))
814 814 elif v == sectname:
815 815 ui.debug('%s: ' %
816 816 ui.configsource(section, name, untrusted))
817 817 ui.write(value, '\n')
818 818 else:
819 819 ui.debug('%s: ' %
820 820 ui.configsource(section, name, untrusted))
821 821 ui.write('%s=%s\n' % (sectname, value))
822 822
823 823 def debugsetparents(ui, repo, rev1, rev2=None):
824 824 """manually set the parents of the current working directory
825 825
826 826 This is useful for writing repository conversion tools, but should
827 827 be used with care.
828 828 """
829 829
830 830 if not rev2:
831 831 rev2 = hex(nullid)
832 832
833 833 wlock = repo.wlock()
834 834 try:
835 835 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
836 836 finally:
837 837 wlock.release()
838 838
839 839 def debugstate(ui, repo, nodates=None):
840 840 """show the contents of the current dirstate"""
841 841 timestr = ""
842 842 showdate = not nodates
843 843 for file_, ent in sorted(repo.dirstate._map.iteritems()):
844 844 if showdate:
845 845 if ent[3] == -1:
846 846 # Pad or slice to locale representation
847 847 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
848 848 timestr = 'unset'
849 849 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
850 850 else:
851 851 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
852 852 if ent[1] & 020000:
853 853 mode = 'lnk'
854 854 else:
855 855 mode = '%3o' % (ent[1] & 0777)
856 856 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
857 857 for f in repo.dirstate.copies():
858 858 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
859 859
860 860 def debugdata(ui, file_, rev):
861 861 """dump the contents of a data file revision"""
862 862 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
863 863 try:
864 864 ui.write(r.revision(r.lookup(rev)))
865 865 except KeyError:
866 866 raise util.Abort(_('invalid revision identifier %s') % rev)
867 867
868 868 def debugdate(ui, date, range=None, **opts):
869 869 """parse and display a date"""
870 870 if opts["extended"]:
871 871 d = util.parsedate(date, util.extendeddateformats)
872 872 else:
873 873 d = util.parsedate(date)
874 874 ui.write("internal: %s %s\n" % d)
875 875 ui.write("standard: %s\n" % util.datestr(d))
876 876 if range:
877 877 m = util.matchdate(range)
878 878 ui.write("match: %s\n" % m(d[0]))
879 879
880 880 def debugindex(ui, file_):
881 881 """dump the contents of an index file"""
882 882 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
883 883 ui.write(" rev offset length base linkrev"
884 884 " nodeid p1 p2\n")
885 885 for i in r:
886 886 node = r.node(i)
887 887 try:
888 888 pp = r.parents(node)
889 889 except:
890 890 pp = [nullid, nullid]
891 891 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
892 892 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
893 893 short(node), short(pp[0]), short(pp[1])))
894 894
895 895 def debugindexdot(ui, file_):
896 896 """dump an index DAG as a graphviz dot file"""
897 897 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
898 898 ui.write("digraph G {\n")
899 899 for i in r:
900 900 node = r.node(i)
901 901 pp = r.parents(node)
902 902 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
903 903 if pp[1] != nullid:
904 904 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
905 905 ui.write("}\n")
906 906
907 907 def debuginstall(ui):
908 908 '''test Mercurial installation'''
909 909
910 910 def writetemp(contents):
911 911 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
912 912 f = os.fdopen(fd, "wb")
913 913 f.write(contents)
914 914 f.close()
915 915 return name
916 916
917 917 problems = 0
918 918
919 919 # encoding
920 920 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
921 921 try:
922 922 encoding.fromlocal("test")
923 923 except util.Abort, inst:
924 924 ui.write(" %s\n" % inst)
925 925 ui.write(_(" (check that your locale is properly set)\n"))
926 926 problems += 1
927 927
928 928 # compiled modules
929 929 ui.status(_("Checking extensions...\n"))
930 930 try:
931 931 import bdiff, mpatch, base85
932 932 except Exception, inst:
933 933 ui.write(" %s\n" % inst)
934 934 ui.write(_(" One or more extensions could not be found"))
935 935 ui.write(_(" (check that you compiled the extensions)\n"))
936 936 problems += 1
937 937
938 938 # templates
939 939 ui.status(_("Checking templates...\n"))
940 940 try:
941 941 import templater
942 942 templater.templater(templater.templatepath("map-cmdline.default"))
943 943 except Exception, inst:
944 944 ui.write(" %s\n" % inst)
945 945 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
946 946 problems += 1
947 947
948 948 # patch
949 949 ui.status(_("Checking patch...\n"))
950 950 patchproblems = 0
951 951 a = "1\n2\n3\n4\n"
952 952 b = "1\n2\n3\ninsert\n4\n"
953 953 fa = writetemp(a)
954 954 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
955 955 os.path.basename(fa))
956 956 fd = writetemp(d)
957 957
958 958 files = {}
959 959 try:
960 960 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
961 961 except util.Abort, e:
962 962 ui.write(_(" patch call failed:\n"))
963 963 ui.write(" " + str(e) + "\n")
964 964 patchproblems += 1
965 965 else:
966 966 if list(files) != [os.path.basename(fa)]:
967 967 ui.write(_(" unexpected patch output!\n"))
968 968 patchproblems += 1
969 969 a = file(fa).read()
970 970 if a != b:
971 971 ui.write(_(" patch test failed!\n"))
972 972 patchproblems += 1
973 973
974 974 if patchproblems:
975 975 if ui.config('ui', 'patch'):
976 976 ui.write(_(" (Current patch tool may be incompatible with patch,"
977 977 " or misconfigured. Please check your .hgrc file)\n"))
978 978 else:
979 979 ui.write(_(" Internal patcher failure, please report this error"
980 980 " to http://www.selenic.com/mercurial/bts\n"))
981 981 problems += patchproblems
982 982
983 983 os.unlink(fa)
984 984 os.unlink(fd)
985 985
986 986 # editor
987 987 ui.status(_("Checking commit editor...\n"))
988 988 editor = ui.geteditor()
989 989 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
990 990 if not cmdpath:
991 991 if editor == 'vi':
992 992 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
993 993 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
994 994 else:
995 995 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
996 996 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
997 997 problems += 1
998 998
999 999 # check username
1000 1000 ui.status(_("Checking username...\n"))
1001 1001 user = os.environ.get("HGUSER")
1002 1002 if user is None:
1003 1003 user = ui.config("ui", "username")
1004 1004 if user is None:
1005 1005 user = os.environ.get("EMAIL")
1006 1006 if not user:
1007 1007 ui.warn(" ")
1008 1008 ui.username()
1009 1009 ui.write(_(" (specify a username in your .hgrc file)\n"))
1010 1010
1011 1011 if not problems:
1012 1012 ui.status(_("No problems detected\n"))
1013 1013 else:
1014 1014 ui.write(_("%s problems detected,"
1015 1015 " please check your install!\n") % problems)
1016 1016
1017 1017 return problems
1018 1018
1019 1019 def debugrename(ui, repo, file1, *pats, **opts):
1020 1020 """dump rename information"""
1021 1021
1022 1022 ctx = repo[opts.get('rev')]
1023 1023 m = cmdutil.match(repo, (file1,) + pats, opts)
1024 1024 for abs in ctx.walk(m):
1025 1025 fctx = ctx[abs]
1026 1026 o = fctx.filelog().renamed(fctx.filenode())
1027 1027 rel = m.rel(abs)
1028 1028 if o:
1029 1029 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1030 1030 else:
1031 1031 ui.write(_("%s not renamed\n") % rel)
1032 1032
1033 1033 def debugwalk(ui, repo, *pats, **opts):
1034 1034 """show how files match on given patterns"""
1035 1035 m = cmdutil.match(repo, pats, opts)
1036 1036 items = list(repo.walk(m))
1037 1037 if not items:
1038 1038 return
1039 1039 fmt = 'f %%-%ds %%-%ds %%s' % (
1040 1040 max([len(abs) for abs in items]),
1041 1041 max([len(m.rel(abs)) for abs in items]))
1042 1042 for abs in items:
1043 1043 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1044 1044 ui.write("%s\n" % line.rstrip())
1045 1045
1046 1046 def diff(ui, repo, *pats, **opts):
1047 1047 """diff repository (or selected files)
1048 1048
1049 1049 Show differences between revisions for the specified files.
1050 1050
1051 1051 Differences between files are shown using the unified diff format.
1052 1052
1053 1053 NOTE: diff may generate unexpected results for merges, as it will
1054 1054 default to comparing against the working directory's first parent
1055 1055 changeset if no revisions are specified.
1056 1056
1057 1057 When two revision arguments are given, then changes are shown
1058 1058 between those revisions. If only one revision is specified then
1059 1059 that revision is compared to the working directory, and, when no
1060 1060 revisions are specified, the working directory files are compared
1061 1061 to its parent.
1062 1062
1063 1063 Without the -a/--text option, diff will avoid generating diffs of
1064 1064 files it detects as binary. With -a, diff will generate a diff
1065 1065 anyway, probably with undesirable results.
1066 1066
1067 1067 Use the -g/--git option to generate diffs in the git extended diff
1068 1068 format. For more information, read 'hg help diffs'.
1069 1069 """
1070 1070
1071 1071 revs = opts.get('rev')
1072 1072 change = opts.get('change')
1073 1073
1074 1074 if revs and change:
1075 1075 msg = _('cannot specify --rev and --change at the same time')
1076 1076 raise util.Abort(msg)
1077 1077 elif change:
1078 1078 node2 = repo.lookup(change)
1079 1079 node1 = repo[node2].parents()[0].node()
1080 1080 else:
1081 1081 node1, node2 = cmdutil.revpair(repo, revs)
1082 1082
1083 1083 m = cmdutil.match(repo, pats, opts)
1084 1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1085 1085 for chunk in it:
1086 1086 ui.write(chunk)
1087 1087
1088 1088 def export(ui, repo, *changesets, **opts):
1089 1089 """dump the header and diffs for one or more changesets
1090 1090
1091 1091 Print the changeset header and diffs for one or more revisions.
1092 1092
1093 1093 The information shown in the changeset header is: author,
1094 1094 changeset hash, parent(s) and commit comment.
1095 1095
1096 1096 NOTE: export may generate unexpected diff output for merge
1097 1097 changesets, as it will compare the merge changeset against its
1098 1098 first parent only.
1099 1099
1100 1100 Output may be to a file, in which case the name of the file is
1101 1101 given using a format string. The formatting rules are as follows:
1102 1102
1103 1103 %% literal "%" character
1104 1104 %H changeset hash (40 bytes of hexadecimal)
1105 1105 %N number of patches being generated
1106 1106 %R changeset revision number
1107 1107 %b basename of the exporting repository
1108 1108 %h short-form changeset hash (12 bytes of hexadecimal)
1109 1109 %n zero-padded sequence number, starting at 1
1110 1110 %r zero-padded changeset revision number
1111 1111
1112 1112 Without the -a/--text option, export will avoid generating diffs
1113 1113 of files it detects as binary. With -a, export will generate a
1114 1114 diff anyway, probably with undesirable results.
1115 1115
1116 1116 Use the -g/--git option to generate diffs in the git extended diff
1117 1117 format. See 'hg help diffs' for more information.
1118 1118
1119 1119 With the --switch-parent option, the diff will be against the
1120 1120 second parent. It can be useful to review a merge.
1121 1121 """
1122 1122 if not changesets:
1123 1123 raise util.Abort(_("export requires at least one changeset"))
1124 1124 revs = cmdutil.revrange(repo, changesets)
1125 1125 if len(revs) > 1:
1126 1126 ui.note(_('exporting patches:\n'))
1127 1127 else:
1128 1128 ui.note(_('exporting patch:\n'))
1129 1129 patch.export(repo, revs, template=opts.get('output'),
1130 1130 switch_parent=opts.get('switch_parent'),
1131 1131 opts=patch.diffopts(ui, opts))
1132 1132
1133 1133 def grep(ui, repo, pattern, *pats, **opts):
1134 1134 """search for a pattern in specified files and revisions
1135 1135
1136 1136 Search revisions of files for a regular expression.
1137 1137
1138 1138 This command behaves differently than Unix grep. It only accepts
1139 1139 Python/Perl regexps. It searches repository history, not the
1140 1140 working directory. It always prints the revision number in which a
1141 1141 match appears.
1142 1142
1143 1143 By default, grep only prints output for the first revision of a
1144 1144 file in which it finds a match. To get it to print every revision
1145 1145 that contains a change in match status ("-" for a match that
1146 1146 becomes a non-match, or "+" for a non-match that becomes a match),
1147 1147 use the --all flag.
1148 1148 """
1149 1149 reflags = 0
1150 1150 if opts.get('ignore_case'):
1151 1151 reflags |= re.I
1152 1152 try:
1153 1153 regexp = re.compile(pattern, reflags)
1154 1154 except Exception, inst:
1155 1155 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1156 1156 return None
1157 1157 sep, eol = ':', '\n'
1158 1158 if opts.get('print0'):
1159 1159 sep = eol = '\0'
1160 1160
1161 1161 fcache = {}
1162 1162 forder = []
1163 1163 def getfile(fn):
1164 1164 if fn not in fcache:
1165 1165 if len(fcache) > 20:
1166 1166 del fcache[forder.pop(0)]
1167 1167 fcache[fn] = repo.file(fn)
1168 1168 else:
1169 1169 forder.remove(fn)
1170 1170
1171 1171 forder.append(fn)
1172 1172 return fcache[fn]
1173 1173
1174 1174 def matchlines(body):
1175 1175 begin = 0
1176 1176 linenum = 0
1177 1177 while True:
1178 1178 match = regexp.search(body, begin)
1179 1179 if not match:
1180 1180 break
1181 1181 mstart, mend = match.span()
1182 1182 linenum += body.count('\n', begin, mstart) + 1
1183 1183 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1184 1184 begin = body.find('\n', mend) + 1 or len(body)
1185 1185 lend = begin - 1
1186 1186 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1187 1187
1188 1188 class linestate(object):
1189 1189 def __init__(self, line, linenum, colstart, colend):
1190 1190 self.line = line
1191 1191 self.linenum = linenum
1192 1192 self.colstart = colstart
1193 1193 self.colend = colend
1194 1194
1195 1195 def __hash__(self):
1196 1196 return hash((self.linenum, self.line))
1197 1197
1198 1198 def __eq__(self, other):
1199 1199 return self.line == other.line
1200 1200
1201 1201 matches = {}
1202 1202 copies = {}
1203 1203 def grepbody(fn, rev, body):
1204 1204 matches[rev].setdefault(fn, [])
1205 1205 m = matches[rev][fn]
1206 1206 for lnum, cstart, cend, line in matchlines(body):
1207 1207 s = linestate(line, lnum, cstart, cend)
1208 1208 m.append(s)
1209 1209
1210 1210 def difflinestates(a, b):
1211 1211 sm = difflib.SequenceMatcher(None, a, b)
1212 1212 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1213 1213 if tag == 'insert':
1214 1214 for i in xrange(blo, bhi):
1215 1215 yield ('+', b[i])
1216 1216 elif tag == 'delete':
1217 1217 for i in xrange(alo, ahi):
1218 1218 yield ('-', a[i])
1219 1219 elif tag == 'replace':
1220 1220 for i in xrange(alo, ahi):
1221 1221 yield ('-', a[i])
1222 1222 for i in xrange(blo, bhi):
1223 1223 yield ('+', b[i])
1224 1224
1225 1225 prev = {}
1226 1226 def display(fn, rev, states, prevstates):
1227 1227 datefunc = ui.quiet and util.shortdate or util.datestr
1228 1228 found = False
1229 1229 filerevmatches = {}
1230 1230 r = prev.get(fn, -1)
1231 1231 if opts.get('all'):
1232 1232 iter = difflinestates(states, prevstates)
1233 1233 else:
1234 1234 iter = [('', l) for l in prevstates]
1235 1235 for change, l in iter:
1236 1236 cols = [fn, str(r)]
1237 1237 if opts.get('line_number'):
1238 1238 cols.append(str(l.linenum))
1239 1239 if opts.get('all'):
1240 1240 cols.append(change)
1241 1241 if opts.get('user'):
1242 1242 cols.append(ui.shortuser(get(r)[1]))
1243 1243 if opts.get('date'):
1244 1244 cols.append(datefunc(get(r)[2]))
1245 1245 if opts.get('files_with_matches'):
1246 1246 c = (fn, r)
1247 1247 if c in filerevmatches:
1248 1248 continue
1249 1249 filerevmatches[c] = 1
1250 1250 else:
1251 1251 cols.append(l.line)
1252 1252 ui.write(sep.join(cols), eol)
1253 1253 found = True
1254 1254 return found
1255 1255
1256 1256 fstate = {}
1257 1257 skip = {}
1258 1258 get = util.cachefunc(lambda r: repo[r].changeset())
1259 1259 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1260 1260 found = False
1261 1261 follow = opts.get('follow')
1262 1262 for st, rev, fns in changeiter:
1263 1263 if st == 'window':
1264 1264 matches.clear()
1265 1265 elif st == 'add':
1266 1266 ctx = repo[rev]
1267 1267 matches[rev] = {}
1268 1268 for fn in fns:
1269 1269 if fn in skip:
1270 1270 continue
1271 1271 try:
1272 1272 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1273 1273 fstate.setdefault(fn, [])
1274 1274 if follow:
1275 1275 copied = getfile(fn).renamed(ctx.filenode(fn))
1276 1276 if copied:
1277 1277 copies.setdefault(rev, {})[fn] = copied[0]
1278 1278 except error.LookupError:
1279 1279 pass
1280 1280 elif st == 'iter':
1281 1281 for fn, m in sorted(matches[rev].items()):
1282 1282 copy = copies.get(rev, {}).get(fn)
1283 1283 if fn in skip:
1284 1284 if copy:
1285 1285 skip[copy] = True
1286 1286 continue
1287 1287 if fn in prev or fstate[fn]:
1288 1288 r = display(fn, rev, m, fstate[fn])
1289 1289 found = found or r
1290 1290 if r and not opts.get('all'):
1291 1291 skip[fn] = True
1292 1292 if copy:
1293 1293 skip[copy] = True
1294 1294 fstate[fn] = m
1295 1295 if copy:
1296 1296 fstate[copy] = m
1297 1297 prev[fn] = rev
1298 1298
1299 1299 for fn, state in sorted(fstate.items()):
1300 1300 if fn in skip:
1301 1301 continue
1302 1302 if fn not in copies.get(prev[fn], {}):
1303 1303 found = display(fn, rev, {}, state) or found
1304 1304 return (not found and 1) or 0
1305 1305
1306 1306 def heads(ui, repo, *branchrevs, **opts):
1307 1307 """show current repository heads or show branch heads
1308 1308
1309 1309 With no arguments, show all repository head changesets.
1310 1310
1311 1311 Repository "heads" are changesets that don't have child
1312 1312 changesets. They are where development generally takes place and
1313 1313 are the usual targets for update and merge operations.
1314 1314
1315 1315 If one or more REV is given, the "branch heads" will be shown for
1316 1316 the named branch associated with that revision. The name of the
1317 1317 branch is called the revision's branch tag.
1318 1318
1319 1319 Branch heads are revisions on a given named branch that do not have
1320 1320 any children on the same branch. A branch head could be a true head
1321 1321 or it could be the last changeset on a branch before a new branch
1322 1322 was created. If none of the branch heads are true heads, the branch
1323 1323 is considered inactive.
1324 1324
1325 1325 If STARTREV is specified only those heads (or branch heads) that
1326 1326 are descendants of STARTREV will be displayed.
1327 1327 """
1328 1328 if opts.get('rev'):
1329 1329 start = repo.lookup(opts['rev'])
1330 1330 else:
1331 1331 start = None
1332 1332 closed = opts.get('closed')
1333 1333 hideinactive, _heads = opts.get('active'), None
1334 1334 if not branchrevs:
1335 1335 # Assume we're looking repo-wide heads if no revs were specified.
1336 1336 heads = repo.heads(start)
1337 1337 else:
1338 1338 if hideinactive:
1339 1339 _heads = repo.heads(start)
1340 1340 heads = []
1341 1341 visitedset = set()
1342 1342 for branchrev in branchrevs:
1343 1343 branch = repo[branchrev].branch()
1344 1344 if branch in visitedset:
1345 1345 continue
1346 1346 visitedset.add(branch)
1347 1347 bheads = repo.branchheads(branch, start, closed=closed)
1348 1348 if not bheads:
1349 1349 if not opts.get('rev'):
1350 1350 ui.warn(_("no open branch heads on branch %s\n") % branch)
1351 1351 elif branch != branchrev:
1352 1352 ui.warn(_("no changes on branch %s containing %s are "
1353 1353 "reachable from %s\n")
1354 1354 % (branch, branchrev, opts.get('rev')))
1355 1355 else:
1356 1356 ui.warn(_("no changes on branch %s are reachable from %s\n")
1357 1357 % (branch, opts.get('rev')))
1358 1358 if hideinactive:
1359 1359 bheads = [bhead for bhead in bheads if bhead in _heads]
1360 1360 heads.extend(bheads)
1361 1361 if not heads:
1362 1362 return 1
1363 1363 displayer = cmdutil.show_changeset(ui, repo, opts)
1364 1364 for n in heads:
1365 1365 displayer.show(repo[n])
1366 1366
1367 1367 def help_(ui, name=None, with_version=False):
1368 1368 """show help for a given topic or a help overview
1369 1369
1370 1370 With no arguments, print a list of commands with short help messages.
1371 1371
1372 1372 Given a topic, extension, or command name, print help for that
1373 1373 topic."""
1374 1374 option_lists = []
1375 1375
1376 1376 def addglobalopts(aliases):
1377 1377 if ui.verbose:
1378 1378 option_lists.append((_("global options:"), globalopts))
1379 1379 if name == 'shortlist':
1380 1380 option_lists.append((_('use "hg help" for the full list '
1381 1381 'of commands'), ()))
1382 1382 else:
1383 1383 if name == 'shortlist':
1384 1384 msg = _('use "hg help" for the full list of commands '
1385 1385 'or "hg -v" for details')
1386 1386 elif aliases:
1387 1387 msg = _('use "hg -v help%s" to show aliases and '
1388 1388 'global options') % (name and " " + name or "")
1389 1389 else:
1390 1390 msg = _('use "hg -v help %s" to show global options') % name
1391 1391 option_lists.append((msg, ()))
1392 1392
1393 1393 def helpcmd(name):
1394 1394 if with_version:
1395 1395 version_(ui)
1396 1396 ui.write('\n')
1397 1397
1398 1398 try:
1399 1399 aliases, i = cmdutil.findcmd(name, table, False)
1400 1400 except error.AmbiguousCommand, inst:
1401 1401 select = lambda c: c.lstrip('^').startswith(inst.args[0])
1402 1402 helplist(_('list of commands:\n\n'), select)
1403 1403 return
1404 1404
1405 1405 # synopsis
1406 1406 if len(i) > 2:
1407 1407 if i[2].startswith('hg'):
1408 1408 ui.write("%s\n" % i[2])
1409 1409 else:
1410 1410 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1411 1411 else:
1412 1412 ui.write('hg %s\n' % aliases[0])
1413 1413
1414 1414 # aliases
1415 1415 if not ui.quiet and len(aliases) > 1:
1416 1416 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1417 1417
1418 1418 # description
1419 1419 doc = gettext(i[0].__doc__)
1420 1420 if not doc:
1421 1421 doc = _("(no help text available)")
1422 1422 if ui.quiet:
1423 1423 doc = doc.splitlines(0)[0]
1424 1424 ui.write("\n%s\n" % doc.rstrip())
1425 1425
1426 1426 if not ui.quiet:
1427 1427 # options
1428 1428 if i[1]:
1429 1429 option_lists.append((_("options:\n"), i[1]))
1430 1430
1431 1431 addglobalopts(False)
1432 1432
1433 1433 def helplist(header, select=None):
1434 1434 h = {}
1435 1435 cmds = {}
1436 1436 for c, e in table.iteritems():
1437 1437 f = c.split("|", 1)[0]
1438 1438 if select and not select(f):
1439 1439 continue
1440 1440 if (not select and name != 'shortlist' and
1441 1441 e[0].__module__ != __name__):
1442 1442 continue
1443 1443 if name == "shortlist" and not f.startswith("^"):
1444 1444 continue
1445 1445 f = f.lstrip("^")
1446 1446 if not ui.debugflag and f.startswith("debug"):
1447 1447 continue
1448 1448 doc = gettext(e[0].__doc__)
1449 1449 if not doc:
1450 1450 doc = _("(no help text available)")
1451 1451 h[f] = doc.splitlines(0)[0].rstrip()
1452 1452 cmds[f] = c.lstrip("^")
1453 1453
1454 1454 if not h:
1455 1455 ui.status(_('no commands defined\n'))
1456 1456 return
1457 1457
1458 1458 ui.status(header)
1459 1459 fns = sorted(h)
1460 1460 m = max(map(len, fns))
1461 1461 for f in fns:
1462 1462 if ui.verbose:
1463 1463 commands = cmds[f].replace("|",", ")
1464 1464 ui.write(" %s:\n %s\n"%(commands, h[f]))
1465 1465 else:
1466 1466 ui.write(' %-*s %s\n' % (m, f, h[f]))
1467 1467
1468 1468 exts = list(extensions.extensions())
1469 1469 if exts and name != 'shortlist':
1470 1470 ui.write(_('\nenabled extensions:\n\n'))
1471 1471 maxlength = 0
1472 1472 exthelps = []
1473 1473 for ename, ext in exts:
1474 1474 doc = (gettext(ext.__doc__) or _('(no help text available)'))
1475 1475 ename = ename.split('.')[-1]
1476 1476 maxlength = max(len(ename), maxlength)
1477 1477 exthelps.append((ename, doc.splitlines(0)[0].strip()))
1478 1478 for ename, text in exthelps:
1479 1479 ui.write(_(' %s %s\n') % (ename.ljust(maxlength), text))
1480 1480
1481 1481 if not ui.quiet:
1482 1482 addglobalopts(True)
1483 1483
1484 1484 def helptopic(name):
1485 1485 for names, header, doc in help.helptable:
1486 1486 if name in names:
1487 1487 break
1488 1488 else:
1489 1489 raise error.UnknownCommand(name)
1490 1490
1491 1491 # description
1492 1492 if not doc:
1493 1493 doc = _("(no help text available)")
1494 1494 if hasattr(doc, '__call__'):
1495 1495 doc = doc()
1496 1496
1497 1497 ui.write("%s\n" % header)
1498 1498 ui.write("%s\n" % doc.rstrip())
1499 1499
1500 1500 def helpext(name):
1501 1501 try:
1502 1502 mod = extensions.find(name)
1503 1503 except KeyError:
1504 1504 raise error.UnknownCommand(name)
1505 1505
1506 1506 doc = gettext(mod.__doc__) or _('no help text available')
1507 1507 doc = doc.splitlines(0)
1508 1508 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1509 1509 for d in doc[1:]:
1510 1510 ui.write(d, '\n')
1511 1511
1512 1512 ui.status('\n')
1513 1513
1514 1514 try:
1515 1515 ct = mod.cmdtable
1516 1516 except AttributeError:
1517 1517 ct = {}
1518 1518
1519 1519 modcmds = set([c.split('|', 1)[0] for c in ct])
1520 1520 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1521 1521
1522 1522 if name and name != 'shortlist':
1523 1523 i = None
1524 1524 for f in (helptopic, helpcmd, helpext):
1525 1525 try:
1526 1526 f(name)
1527 1527 i = None
1528 1528 break
1529 1529 except error.UnknownCommand, inst:
1530 1530 i = inst
1531 1531 if i:
1532 1532 raise i
1533 1533
1534 1534 else:
1535 1535 # program name
1536 1536 if ui.verbose or with_version:
1537 1537 version_(ui)
1538 1538 else:
1539 1539 ui.status(_("Mercurial Distributed SCM\n"))
1540 1540 ui.status('\n')
1541 1541
1542 1542 # list of commands
1543 1543 if name == "shortlist":
1544 1544 header = _('basic commands:\n\n')
1545 1545 else:
1546 1546 header = _('list of commands:\n\n')
1547 1547
1548 1548 helplist(header)
1549 1549
1550 1550 # list all option lists
1551 1551 opt_output = []
1552 1552 for title, options in option_lists:
1553 1553 opt_output.append(("\n%s" % title, None))
1554 1554 for shortopt, longopt, default, desc in options:
1555 1555 if "DEPRECATED" in desc and not ui.verbose: continue
1556 1556 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1557 1557 longopt and " --%s" % longopt),
1558 1558 "%s%s" % (desc,
1559 1559 default
1560 1560 and _(" (default: %s)") % default
1561 1561 or "")))
1562 1562
1563 1563 if not name:
1564 1564 ui.write(_("\nadditional help topics:\n\n"))
1565 1565 topics = []
1566 1566 for names, header, doc in help.helptable:
1567 1567 names = [(-len(name), name) for name in names]
1568 1568 names.sort()
1569 1569 topics.append((names[0][1], header))
1570 1570 topics_len = max([len(s[0]) for s in topics])
1571 1571 for t, desc in topics:
1572 1572 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1573 1573
1574 1574 if opt_output:
1575 1575 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1576 1576 for first, second in opt_output:
1577 1577 if second:
1578 1578 # wrap descriptions at 70 characters, just like the
1579 1579 # main help texts
1580 1580 second = textwrap.wrap(second, width=70 - opts_len - 3)
1581 1581 pad = '\n' + ' ' * (opts_len + 3)
1582 1582 ui.write(" %-*s %s\n" % (opts_len, first, pad.join(second)))
1583 1583 else:
1584 1584 ui.write("%s\n" % first)
1585 1585
1586 1586 def identify(ui, repo, source=None,
1587 1587 rev=None, num=None, id=None, branch=None, tags=None):
1588 1588 """identify the working copy or specified revision
1589 1589
1590 1590 With no revision, print a summary of the current state of the
1591 1591 repository.
1592 1592
1593 1593 Specifying a path to a repository root or Mercurial bundle will
1594 1594 cause lookup to operate on that repository/bundle.
1595 1595
1596 1596 This summary identifies the repository state using one or two
1597 1597 parent hash identifiers, followed by a "+" if there are
1598 1598 uncommitted changes in the working directory, a list of tags for
1599 1599 this revision and a branch name for non-default branches.
1600 1600 """
1601 1601
1602 1602 if not repo and not source:
1603 1603 raise util.Abort(_("There is no Mercurial repository here "
1604 1604 "(.hg not found)"))
1605 1605
1606 1606 hexfunc = ui.debugflag and hex or short
1607 1607 default = not (num or id or branch or tags)
1608 1608 output = []
1609 1609
1610 1610 revs = []
1611 1611 if source:
1612 1612 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1613 1613 repo = hg.repository(ui, source)
1614 1614
1615 1615 if not repo.local():
1616 1616 if not rev and revs:
1617 1617 rev = revs[0]
1618 1618 if not rev:
1619 1619 rev = "tip"
1620 1620 if num or branch or tags:
1621 1621 raise util.Abort(
1622 1622 "can't query remote revision number, branch, or tags")
1623 1623 output = [hexfunc(repo.lookup(rev))]
1624 1624 elif not rev:
1625 1625 ctx = repo[None]
1626 1626 parents = ctx.parents()
1627 1627 changed = False
1628 1628 if default or id or num:
1629 1629 changed = ctx.files() + ctx.deleted()
1630 1630 if default or id:
1631 1631 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1632 1632 (changed) and "+" or "")]
1633 1633 if num:
1634 1634 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1635 1635 (changed) and "+" or ""))
1636 1636 else:
1637 1637 ctx = repo[rev]
1638 1638 if default or id:
1639 1639 output = [hexfunc(ctx.node())]
1640 1640 if num:
1641 1641 output.append(str(ctx.rev()))
1642 1642
1643 1643 if repo.local() and default and not ui.quiet:
1644 1644 b = encoding.tolocal(ctx.branch())
1645 1645 if b != 'default':
1646 1646 output.append("(%s)" % b)
1647 1647
1648 1648 # multiple tags for a single parent separated by '/'
1649 1649 t = "/".join(ctx.tags())
1650 1650 if t:
1651 1651 output.append(t)
1652 1652
1653 1653 if branch:
1654 1654 output.append(encoding.tolocal(ctx.branch()))
1655 1655
1656 1656 if tags:
1657 1657 output.extend(ctx.tags())
1658 1658
1659 1659 ui.write("%s\n" % ' '.join(output))
1660 1660
1661 1661 def import_(ui, repo, patch1, *patches, **opts):
1662 1662 """import an ordered set of patches
1663 1663
1664 1664 Import a list of patches and commit them individually.
1665 1665
1666 1666 If there are outstanding changes in the working directory, import
1667 1667 will abort unless given the -f/--force flag.
1668 1668
1669 1669 You can import a patch straight from a mail message. Even patches
1670 1670 as attachments work (to use the body part, it must have type
1671 1671 text/plain or text/x-patch). From and Subject headers of email
1672 1672 message are used as default committer and commit message. All
1673 1673 text/plain body parts before first diff are added to commit
1674 1674 message.
1675 1675
1676 1676 If the imported patch was generated by hg export, user and
1677 1677 description from patch override values from message headers and
1678 1678 body. Values given on command line with -m/--message and -u/--user
1679 1679 override these.
1680 1680
1681 1681 If --exact is specified, import will set the working directory to
1682 1682 the parent of each patch before applying it, and will abort if the
1683 1683 resulting changeset has a different ID than the one recorded in
1684 1684 the patch. This may happen due to character set problems or other
1685 1685 deficiencies in the text patch format.
1686 1686
1687 1687 With -s/--similarity, hg will attempt to discover renames and
1688 1688 copies in the patch in the same way as 'addremove'.
1689 1689
1690 1690 To read a patch from standard input, use "-" as the patch name.
1691 1691 See 'hg help dates' for a list of formats valid for -d/--date.
1692 1692 """
1693 1693 patches = (patch1,) + patches
1694 1694
1695 1695 date = opts.get('date')
1696 1696 if date:
1697 1697 opts['date'] = util.parsedate(date)
1698 1698
1699 1699 try:
1700 1700 sim = float(opts.get('similarity') or 0)
1701 1701 except ValueError:
1702 1702 raise util.Abort(_('similarity must be a number'))
1703 1703 if sim < 0 or sim > 100:
1704 1704 raise util.Abort(_('similarity must be between 0 and 100'))
1705 1705
1706 1706 if opts.get('exact') or not opts.get('force'):
1707 1707 cmdutil.bail_if_changed(repo)
1708 1708
1709 1709 d = opts["base"]
1710 1710 strip = opts["strip"]
1711 1711 wlock = lock = None
1712 1712 try:
1713 1713 wlock = repo.wlock()
1714 1714 lock = repo.lock()
1715 1715 for p in patches:
1716 1716 pf = os.path.join(d, p)
1717 1717
1718 1718 if pf == '-':
1719 1719 ui.status(_("applying patch from stdin\n"))
1720 1720 pf = sys.stdin
1721 1721 else:
1722 1722 ui.status(_("applying %s\n") % p)
1723 1723 pf = url.open(ui, pf)
1724 1724 data = patch.extract(ui, pf)
1725 1725 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1726 1726
1727 1727 if tmpname is None:
1728 1728 raise util.Abort(_('no diffs found'))
1729 1729
1730 1730 try:
1731 1731 cmdline_message = cmdutil.logmessage(opts)
1732 1732 if cmdline_message:
1733 1733 # pickup the cmdline msg
1734 1734 message = cmdline_message
1735 1735 elif message:
1736 1736 # pickup the patch msg
1737 1737 message = message.strip()
1738 1738 else:
1739 1739 # launch the editor
1740 1740 message = None
1741 1741 ui.debug(_('message:\n%s\n') % message)
1742 1742
1743 1743 wp = repo.parents()
1744 1744 if opts.get('exact'):
1745 1745 if not nodeid or not p1:
1746 1746 raise util.Abort(_('not a Mercurial patch'))
1747 1747 p1 = repo.lookup(p1)
1748 1748 p2 = repo.lookup(p2 or hex(nullid))
1749 1749
1750 1750 if p1 != wp[0].node():
1751 1751 hg.clean(repo, p1)
1752 1752 repo.dirstate.setparents(p1, p2)
1753 1753 elif p2:
1754 1754 try:
1755 1755 p1 = repo.lookup(p1)
1756 1756 p2 = repo.lookup(p2)
1757 1757 if p1 == wp[0].node():
1758 1758 repo.dirstate.setparents(p1, p2)
1759 1759 except error.RepoError:
1760 1760 pass
1761 1761 if opts.get('exact') or opts.get('import_branch'):
1762 1762 repo.dirstate.setbranch(branch or 'default')
1763 1763
1764 1764 files = {}
1765 1765 try:
1766 1766 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1767 files=files)
1767 files=files, eolmode=None)
1768 1768 finally:
1769 1769 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1770 1770 if not opts.get('no_commit'):
1771 1771 m = cmdutil.matchfiles(repo, files or [])
1772 1772 n = repo.commit(message, opts.get('user') or user,
1773 1773 opts.get('date') or date, match=m,
1774 1774 editor=cmdutil.commiteditor)
1775 1775 if opts.get('exact'):
1776 1776 if hex(n) != nodeid:
1777 1777 repo.rollback()
1778 1778 raise util.Abort(_('patch is damaged'
1779 1779 ' or loses information'))
1780 1780 # Force a dirstate write so that the next transaction
1781 1781 # backups an up-do-date file.
1782 1782 repo.dirstate.write()
1783 1783 finally:
1784 1784 os.unlink(tmpname)
1785 1785 finally:
1786 1786 release(lock, wlock)
1787 1787
1788 1788 def incoming(ui, repo, source="default", **opts):
1789 1789 """show new changesets found in source
1790 1790
1791 1791 Show new changesets found in the specified path/URL or the default
1792 1792 pull location. These are the changesets that would have been pulled
1793 1793 if a pull at the time you issued this command.
1794 1794
1795 1795 For remote repository, using --bundle avoids downloading the
1796 1796 changesets twice if the incoming is followed by a pull.
1797 1797
1798 1798 See pull for valid source format details.
1799 1799 """
1800 1800 limit = cmdutil.loglimit(opts)
1801 1801 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1802 1802 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1803 1803 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1804 1804 if revs:
1805 1805 revs = [other.lookup(rev) for rev in revs]
1806 1806 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1807 1807 force=opts["force"])
1808 1808 if not incoming:
1809 1809 try:
1810 1810 os.unlink(opts["bundle"])
1811 1811 except:
1812 1812 pass
1813 1813 ui.status(_("no changes found\n"))
1814 1814 return 1
1815 1815
1816 1816 cleanup = None
1817 1817 try:
1818 1818 fname = opts["bundle"]
1819 1819 if fname or not other.local():
1820 1820 # create a bundle (uncompressed if other repo is not local)
1821 1821
1822 1822 if revs is None and other.capable('changegroupsubset'):
1823 1823 revs = rheads
1824 1824
1825 1825 if revs is None:
1826 1826 cg = other.changegroup(incoming, "incoming")
1827 1827 else:
1828 1828 cg = other.changegroupsubset(incoming, revs, 'incoming')
1829 1829 bundletype = other.local() and "HG10BZ" or "HG10UN"
1830 1830 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1831 1831 # keep written bundle?
1832 1832 if opts["bundle"]:
1833 1833 cleanup = None
1834 1834 if not other.local():
1835 1835 # use the created uncompressed bundlerepo
1836 1836 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1837 1837
1838 1838 o = other.changelog.nodesbetween(incoming, revs)[0]
1839 1839 if opts.get('newest_first'):
1840 1840 o.reverse()
1841 1841 displayer = cmdutil.show_changeset(ui, other, opts)
1842 1842 count = 0
1843 1843 for n in o:
1844 1844 if count >= limit:
1845 1845 break
1846 1846 parents = [p for p in other.changelog.parents(n) if p != nullid]
1847 1847 if opts.get('no_merges') and len(parents) == 2:
1848 1848 continue
1849 1849 count += 1
1850 1850 displayer.show(other[n])
1851 1851 finally:
1852 1852 if hasattr(other, 'close'):
1853 1853 other.close()
1854 1854 if cleanup:
1855 1855 os.unlink(cleanup)
1856 1856
1857 1857 def init(ui, dest=".", **opts):
1858 1858 """create a new repository in the given directory
1859 1859
1860 1860 Initialize a new repository in the given directory. If the given
1861 1861 directory does not exist, it will be created.
1862 1862
1863 1863 If no directory is given, the current directory is used.
1864 1864
1865 1865 It is possible to specify an ssh:// URL as the destination.
1866 1866 See 'hg help urls' for more information.
1867 1867 """
1868 1868 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1869 1869
1870 1870 def locate(ui, repo, *pats, **opts):
1871 1871 """locate files matching specific patterns
1872 1872
1873 1873 Print files under Mercurial control in the working directory whose
1874 1874 names match the given patterns.
1875 1875
1876 1876 By default, this command searches all directories in the working
1877 1877 directory. To search just the current directory and its
1878 1878 subdirectories, use "--include .".
1879 1879
1880 1880 If no patterns are given to match, this command prints the names
1881 1881 of all files under Mercurial control in the working directory.
1882 1882
1883 1883 If you want to feed the output of this command into the "xargs"
1884 1884 command, use the -0 option to both this command and "xargs". This
1885 1885 will avoid the problem of "xargs" treating single filenames that
1886 1886 contain whitespace as multiple filenames.
1887 1887 """
1888 1888 end = opts.get('print0') and '\0' or '\n'
1889 1889 rev = opts.get('rev') or None
1890 1890
1891 1891 ret = 1
1892 1892 m = cmdutil.match(repo, pats, opts, default='relglob')
1893 1893 m.bad = lambda x,y: False
1894 1894 for abs in repo[rev].walk(m):
1895 1895 if not rev and abs not in repo.dirstate:
1896 1896 continue
1897 1897 if opts.get('fullpath'):
1898 1898 ui.write(repo.wjoin(abs), end)
1899 1899 else:
1900 1900 ui.write(((pats and m.rel(abs)) or abs), end)
1901 1901 ret = 0
1902 1902
1903 1903 return ret
1904 1904
1905 1905 def log(ui, repo, *pats, **opts):
1906 1906 """show revision history of entire repository or files
1907 1907
1908 1908 Print the revision history of the specified files or the entire
1909 1909 project.
1910 1910
1911 1911 File history is shown without following rename or copy history of
1912 1912 files. Use -f/--follow with a filename to follow history across
1913 1913 renames and copies. --follow without a filename will only show
1914 1914 ancestors or descendants of the starting revision. --follow-first
1915 1915 only follows the first parent of merge revisions.
1916 1916
1917 1917 If no revision range is specified, the default is tip:0 unless
1918 1918 --follow is set, in which case the working directory parent is
1919 1919 used as the starting revision.
1920 1920
1921 1921 See 'hg help dates' for a list of formats valid for -d/--date.
1922 1922
1923 1923 By default this command prints revision number and changeset id,
1924 1924 tags, non-trivial parents, user, date and time, and a summary for
1925 1925 each commit. When the -v/--verbose switch is used, the list of
1926 1926 changed files and full commit message are shown.
1927 1927
1928 1928 NOTE: log -p/--patch may generate unexpected diff output for merge
1929 1929 changesets, as it will only compare the merge changeset against
1930 1930 its first parent. Also, only files different from BOTH parents
1931 1931 will appear in files:.
1932 1932 """
1933 1933
1934 1934 get = util.cachefunc(lambda r: repo[r].changeset())
1935 1935 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1936 1936
1937 1937 limit = cmdutil.loglimit(opts)
1938 1938 count = 0
1939 1939
1940 1940 if opts.get('copies') and opts.get('rev'):
1941 1941 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1942 1942 else:
1943 1943 endrev = len(repo)
1944 1944 rcache = {}
1945 1945 ncache = {}
1946 1946 def getrenamed(fn, rev):
1947 1947 '''looks up all renames for a file (up to endrev) the first
1948 1948 time the file is given. It indexes on the changerev and only
1949 1949 parses the manifest if linkrev != changerev.
1950 1950 Returns rename info for fn at changerev rev.'''
1951 1951 if fn not in rcache:
1952 1952 rcache[fn] = {}
1953 1953 ncache[fn] = {}
1954 1954 fl = repo.file(fn)
1955 1955 for i in fl:
1956 1956 node = fl.node(i)
1957 1957 lr = fl.linkrev(i)
1958 1958 renamed = fl.renamed(node)
1959 1959 rcache[fn][lr] = renamed
1960 1960 if renamed:
1961 1961 ncache[fn][node] = renamed
1962 1962 if lr >= endrev:
1963 1963 break
1964 1964 if rev in rcache[fn]:
1965 1965 return rcache[fn][rev]
1966 1966
1967 1967 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1968 1968 # filectx logic.
1969 1969
1970 1970 try:
1971 1971 return repo[rev][fn].renamed()
1972 1972 except error.LookupError:
1973 1973 pass
1974 1974 return None
1975 1975
1976 1976 df = False
1977 1977 if opts["date"]:
1978 1978 df = util.matchdate(opts["date"])
1979 1979
1980 1980 only_branches = opts.get('only_branch')
1981 1981
1982 1982 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1983 1983 for st, rev, fns in changeiter:
1984 1984 if st == 'add':
1985 1985 parents = [p for p in repo.changelog.parentrevs(rev)
1986 1986 if p != nullrev]
1987 1987 if opts.get('no_merges') and len(parents) == 2:
1988 1988 continue
1989 1989 if opts.get('only_merges') and len(parents) != 2:
1990 1990 continue
1991 1991
1992 1992 if only_branches:
1993 1993 revbranch = get(rev)[5]['branch']
1994 1994 if revbranch not in only_branches:
1995 1995 continue
1996 1996
1997 1997 if df:
1998 1998 changes = get(rev)
1999 1999 if not df(changes[2][0]):
2000 2000 continue
2001 2001
2002 2002 if opts.get('keyword'):
2003 2003 changes = get(rev)
2004 2004 miss = 0
2005 2005 for k in [kw.lower() for kw in opts['keyword']]:
2006 2006 if not (k in changes[1].lower() or
2007 2007 k in changes[4].lower() or
2008 2008 k in " ".join(changes[3]).lower()):
2009 2009 miss = 1
2010 2010 break
2011 2011 if miss:
2012 2012 continue
2013 2013
2014 2014 if opts['user']:
2015 2015 changes = get(rev)
2016 2016 if not [k for k in opts['user'] if k in changes[1]]:
2017 2017 continue
2018 2018
2019 2019 copies = []
2020 2020 if opts.get('copies') and rev:
2021 2021 for fn in get(rev)[3]:
2022 2022 rename = getrenamed(fn, rev)
2023 2023 if rename:
2024 2024 copies.append((fn, rename[0]))
2025 2025 displayer.show(context.changectx(repo, rev), copies=copies)
2026 2026 elif st == 'iter':
2027 2027 if count == limit: break
2028 2028 if displayer.flush(rev):
2029 2029 count += 1
2030 2030
2031 2031 def manifest(ui, repo, node=None, rev=None):
2032 2032 """output the current or given revision of the project manifest
2033 2033
2034 2034 Print a list of version controlled files for the given revision.
2035 2035 If no revision is given, the first parent of the working directory
2036 2036 is used, or the null revision if no revision is checked out.
2037 2037
2038 2038 With -v, print file permissions, symlink and executable bits.
2039 2039 With --debug, print file revision hashes.
2040 2040 """
2041 2041
2042 2042 if rev and node:
2043 2043 raise util.Abort(_("please specify just one revision"))
2044 2044
2045 2045 if not node:
2046 2046 node = rev
2047 2047
2048 2048 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2049 2049 ctx = repo[node]
2050 2050 for f in ctx:
2051 2051 if ui.debugflag:
2052 2052 ui.write("%40s " % hex(ctx.manifest()[f]))
2053 2053 if ui.verbose:
2054 2054 ui.write(decor[ctx.flags(f)])
2055 2055 ui.write("%s\n" % f)
2056 2056
2057 2057 def merge(ui, repo, node=None, **opts):
2058 2058 """merge working directory with another revision
2059 2059
2060 2060 The current working directory is updated with all changes made in
2061 2061 the requested revision since the last common predecessor revision.
2062 2062
2063 2063 Files that changed between either parent are marked as changed for
2064 2064 the next commit and a commit must be performed before any further
2065 2065 updates to the repository are allowed. The next commit will have
2066 2066 two parents.
2067 2067
2068 2068 If no revision is specified, the working directory's parent is a
2069 2069 head revision, and the current branch contains exactly one other
2070 2070 head, the other head is merged with by default. Otherwise, an
2071 2071 explicit revision with which to merge with must be provided.
2072 2072 """
2073 2073
2074 2074 if opts.get('rev') and node:
2075 2075 raise util.Abort(_("please specify just one revision"))
2076 2076 if not node:
2077 2077 node = opts.get('rev')
2078 2078
2079 2079 if not node:
2080 2080 branch = repo.changectx(None).branch()
2081 2081 bheads = repo.branchheads(branch)
2082 2082 if len(bheads) > 2:
2083 2083 raise util.Abort(_("branch '%s' has %d heads - "
2084 2084 "please merge with an explicit rev") %
2085 2085 (branch, len(bheads)))
2086 2086
2087 2087 parent = repo.dirstate.parents()[0]
2088 2088 if len(bheads) == 1:
2089 2089 if len(repo.heads()) > 1:
2090 2090 raise util.Abort(_("branch '%s' has one head - "
2091 2091 "please merge with an explicit rev") %
2092 2092 branch)
2093 2093 msg = _('there is nothing to merge')
2094 2094 if parent != repo.lookup(repo[None].branch()):
2095 2095 msg = _('%s - use "hg update" instead') % msg
2096 2096 raise util.Abort(msg)
2097 2097
2098 2098 if parent not in bheads:
2099 2099 raise util.Abort(_('working dir not at a head rev - '
2100 2100 'use "hg update" or merge with an explicit rev'))
2101 2101 node = parent == bheads[0] and bheads[-1] or bheads[0]
2102 2102
2103 2103 if opts.get('show'):
2104 2104 p1 = repo['.']
2105 2105 p2 = repo[node]
2106 2106 common = p1.ancestor(p2)
2107 2107 roots, heads = [common.node()], [p2.node()]
2108 2108 displayer = cmdutil.show_changeset(ui, repo, opts)
2109 2109 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2110 2110 displayer.show(repo[node])
2111 2111 return 0
2112 2112
2113 2113 return hg.merge(repo, node, force=opts.get('force'))
2114 2114
2115 2115 def outgoing(ui, repo, dest=None, **opts):
2116 2116 """show changesets not found in destination
2117 2117
2118 2118 Show changesets not found in the specified destination repository
2119 2119 or the default push location. These are the changesets that would
2120 2120 be pushed if a push was requested.
2121 2121
2122 2122 See pull for valid destination format details.
2123 2123 """
2124 2124 limit = cmdutil.loglimit(opts)
2125 2125 dest, revs, checkout = hg.parseurl(
2126 2126 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2127 2127 if revs:
2128 2128 revs = [repo.lookup(rev) for rev in revs]
2129 2129
2130 2130 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2131 2131 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2132 2132 o = repo.findoutgoing(other, force=opts.get('force'))
2133 2133 if not o:
2134 2134 ui.status(_("no changes found\n"))
2135 2135 return 1
2136 2136 o = repo.changelog.nodesbetween(o, revs)[0]
2137 2137 if opts.get('newest_first'):
2138 2138 o.reverse()
2139 2139 displayer = cmdutil.show_changeset(ui, repo, opts)
2140 2140 count = 0
2141 2141 for n in o:
2142 2142 if count >= limit:
2143 2143 break
2144 2144 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2145 2145 if opts.get('no_merges') and len(parents) == 2:
2146 2146 continue
2147 2147 count += 1
2148 2148 displayer.show(repo[n])
2149 2149
2150 2150 def parents(ui, repo, file_=None, **opts):
2151 2151 """show the parents of the working directory or revision
2152 2152
2153 2153 Print the working directory's parent revisions. If a revision is
2154 2154 given via -r/--rev, the parent of that revision will be printed.
2155 2155 If a file argument is given, the revision in which the file was
2156 2156 last changed (before the working directory revision or the
2157 2157 argument to --rev if given) is printed.
2158 2158 """
2159 2159 rev = opts.get('rev')
2160 2160 if rev:
2161 2161 ctx = repo[rev]
2162 2162 else:
2163 2163 ctx = repo[None]
2164 2164
2165 2165 if file_:
2166 2166 m = cmdutil.match(repo, (file_,), opts)
2167 2167 if m.anypats() or len(m.files()) != 1:
2168 2168 raise util.Abort(_('can only specify an explicit filename'))
2169 2169 file_ = m.files()[0]
2170 2170 filenodes = []
2171 2171 for cp in ctx.parents():
2172 2172 if not cp:
2173 2173 continue
2174 2174 try:
2175 2175 filenodes.append(cp.filenode(file_))
2176 2176 except error.LookupError:
2177 2177 pass
2178 2178 if not filenodes:
2179 2179 raise util.Abort(_("'%s' not found in manifest!") % file_)
2180 2180 fl = repo.file(file_)
2181 2181 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2182 2182 else:
2183 2183 p = [cp.node() for cp in ctx.parents()]
2184 2184
2185 2185 displayer = cmdutil.show_changeset(ui, repo, opts)
2186 2186 for n in p:
2187 2187 if n != nullid:
2188 2188 displayer.show(repo[n])
2189 2189
2190 2190 def paths(ui, repo, search=None):
2191 2191 """show aliases for remote repositories
2192 2192
2193 2193 Show definition of symbolic path name NAME. If no name is given,
2194 2194 show definition of all available names.
2195 2195
2196 2196 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2197 2197 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2198 2198
2199 2199 See 'hg help urls' for more information.
2200 2200 """
2201 2201 if search:
2202 2202 for name, path in ui.configitems("paths"):
2203 2203 if name == search:
2204 2204 ui.write("%s\n" % url.hidepassword(path))
2205 2205 return
2206 2206 ui.warn(_("not found!\n"))
2207 2207 return 1
2208 2208 else:
2209 2209 for name, path in ui.configitems("paths"):
2210 2210 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2211 2211
2212 2212 def postincoming(ui, repo, modheads, optupdate, checkout):
2213 2213 if modheads == 0:
2214 2214 return
2215 2215 if optupdate:
2216 2216 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2217 2217 return hg.update(repo, checkout)
2218 2218 else:
2219 2219 ui.status(_("not updating, since new heads added\n"))
2220 2220 if modheads > 1:
2221 2221 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2222 2222 else:
2223 2223 ui.status(_("(run 'hg update' to get a working copy)\n"))
2224 2224
2225 2225 def pull(ui, repo, source="default", **opts):
2226 2226 """pull changes from the specified source
2227 2227
2228 2228 Pull changes from a remote repository to a local one.
2229 2229
2230 2230 This finds all changes from the repository at the specified path
2231 2231 or URL and adds them to a local repository (the current one unless
2232 2232 -R is specified). By default, this does not update the copy of the
2233 2233 project in the working directory.
2234 2234
2235 2235 Use hg incoming if you want to see what would have been added by a
2236 2236 pull at the time you issued this command. If you then decide to
2237 2237 added those changes to the repository, you should use pull -r X
2238 2238 where X is the last changeset listed by hg incoming.
2239 2239
2240 2240 If SOURCE is omitted, the 'default' path will be used.
2241 2241 See 'hg help urls' for more information.
2242 2242 """
2243 2243 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2244 2244 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2245 2245 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2246 2246 if revs:
2247 2247 try:
2248 2248 revs = [other.lookup(rev) for rev in revs]
2249 2249 except error.CapabilityError:
2250 2250 err = _("Other repository doesn't support revision lookup, "
2251 2251 "so a rev cannot be specified.")
2252 2252 raise util.Abort(err)
2253 2253
2254 2254 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2255 2255 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2256 2256
2257 2257 def push(ui, repo, dest=None, **opts):
2258 2258 """push changes to the specified destination
2259 2259
2260 2260 Push changes from the local repository to the given destination.
2261 2261
2262 2262 This is the symmetrical operation for pull. It moves changes from
2263 2263 the current repository to a different one. If the destination is
2264 2264 local this is identical to a pull in that directory from the
2265 2265 current one.
2266 2266
2267 2267 By default, push will refuse to run if it detects the result would
2268 2268 increase the number of remote heads. This generally indicates the
2269 2269 user forgot to pull and merge before pushing.
2270 2270
2271 2271 If -r/--rev is used, the named revision and all its ancestors will
2272 2272 be pushed to the remote repository.
2273 2273
2274 2274 Please see 'hg help urls' for important details about ssh://
2275 2275 URLs. If DESTINATION is omitted, a default path will be used.
2276 2276 See 'hg help urls' for more information.
2277 2277 """
2278 2278 dest, revs, checkout = hg.parseurl(
2279 2279 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2280 2280 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2281 2281 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2282 2282 if revs:
2283 2283 revs = [repo.lookup(rev) for rev in revs]
2284 2284 r = repo.push(other, opts.get('force'), revs=revs)
2285 2285 return r == 0
2286 2286
2287 2287 def recover(ui, repo):
2288 2288 """roll back an interrupted transaction
2289 2289
2290 2290 Recover from an interrupted commit or pull.
2291 2291
2292 2292 This command tries to fix the repository status after an
2293 2293 interrupted operation. It should only be necessary when Mercurial
2294 2294 suggests it.
2295 2295 """
2296 2296 if repo.recover():
2297 2297 return hg.verify(repo)
2298 2298 return 1
2299 2299
2300 2300 def remove(ui, repo, *pats, **opts):
2301 2301 """remove the specified files on the next commit
2302 2302
2303 2303 Schedule the indicated files for removal from the repository.
2304 2304
2305 2305 This only removes files from the current branch, not from the
2306 2306 entire project history. -A/--after can be used to remove only
2307 2307 files that have already been deleted, -f/--force can be used to
2308 2308 force deletion, and -Af can be used to remove files from the next
2309 2309 revision without deleting them from the working directory.
2310 2310
2311 2311 The following table details the behavior of remove for different
2312 2312 file states (columns) and option combinations (rows). The file
2313 2313 states are Added [A], Clean [C], Modified [M] and Missing [!]
2314 2314 (as reported by hg status). The actions are Warn, Remove (from
2315 2315 branch) and Delete (from disk).
2316 2316
2317 2317 A C M !
2318 2318 none W RD W R
2319 2319 -f R RD RD R
2320 2320 -A W W W R
2321 2321 -Af R R R R
2322 2322
2323 2323 This command schedules the files to be removed at the next commit.
2324 2324 To undo a remove before that, see hg revert.
2325 2325 """
2326 2326
2327 2327 after, force = opts.get('after'), opts.get('force')
2328 2328 if not pats and not after:
2329 2329 raise util.Abort(_('no files specified'))
2330 2330
2331 2331 m = cmdutil.match(repo, pats, opts)
2332 2332 s = repo.status(match=m, clean=True)
2333 2333 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2334 2334
2335 2335 for f in m.files():
2336 2336 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2337 2337 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2338 2338
2339 2339 def warn(files, reason):
2340 2340 for f in files:
2341 2341 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2342 2342 % (m.rel(f), reason))
2343 2343
2344 2344 if force:
2345 2345 remove, forget = modified + deleted + clean, added
2346 2346 elif after:
2347 2347 remove, forget = deleted, []
2348 2348 warn(modified + added + clean, _('still exists'))
2349 2349 else:
2350 2350 remove, forget = deleted + clean, []
2351 2351 warn(modified, _('is modified'))
2352 2352 warn(added, _('has been marked for add'))
2353 2353
2354 2354 for f in sorted(remove + forget):
2355 2355 if ui.verbose or not m.exact(f):
2356 2356 ui.status(_('removing %s\n') % m.rel(f))
2357 2357
2358 2358 repo.forget(forget)
2359 2359 repo.remove(remove, unlink=not after)
2360 2360
2361 2361 def rename(ui, repo, *pats, **opts):
2362 2362 """rename files; equivalent of copy + remove
2363 2363
2364 2364 Mark dest as copies of sources; mark sources for deletion. If dest
2365 2365 is a directory, copies are put in that directory. If dest is a
2366 2366 file, there can only be one source.
2367 2367
2368 2368 By default, this command copies the contents of files as they
2369 2369 exist in the working directory. If invoked with -A/--after, the
2370 2370 operation is recorded, but no copying is performed.
2371 2371
2372 2372 This command takes effect at the next commit. To undo a rename
2373 2373 before that, see hg revert.
2374 2374 """
2375 2375 wlock = repo.wlock(False)
2376 2376 try:
2377 2377 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2378 2378 finally:
2379 2379 wlock.release()
2380 2380
2381 2381 def resolve(ui, repo, *pats, **opts):
2382 2382 """retry file merges from a merge or update
2383 2383
2384 2384 This command will cleanly retry unresolved file merges using file
2385 2385 revisions preserved from the last update or merge. To attempt to
2386 2386 resolve all unresolved files, use the -a/--all switch.
2387 2387
2388 2388 If a conflict is resolved manually, please note that the changes
2389 2389 will be overwritten if the merge is retried with resolve. The
2390 2390 -m/--mark switch should be used to mark the file as resolved.
2391 2391
2392 2392 This command also allows listing resolved files and manually
2393 2393 indicating whether or not files are resolved. All files must be
2394 2394 marked as resolved before a commit is permitted.
2395 2395
2396 2396 The codes used to show the status of files are:
2397 2397 U = unresolved
2398 2398 R = resolved
2399 2399 """
2400 2400
2401 2401 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2402 2402
2403 2403 if (show and (mark or unmark)) or (mark and unmark):
2404 2404 raise util.Abort(_("too many options specified"))
2405 2405 if pats and all:
2406 2406 raise util.Abort(_("can't specify --all and patterns"))
2407 2407 if not (all or pats or show or mark or unmark):
2408 2408 raise util.Abort(_('no files or directories specified; '
2409 2409 'use --all to remerge all files'))
2410 2410
2411 2411 ms = merge_.mergestate(repo)
2412 2412 m = cmdutil.match(repo, pats, opts)
2413 2413
2414 2414 for f in ms:
2415 2415 if m(f):
2416 2416 if show:
2417 2417 ui.write("%s %s\n" % (ms[f].upper(), f))
2418 2418 elif mark:
2419 2419 ms.mark(f, "r")
2420 2420 elif unmark:
2421 2421 ms.mark(f, "u")
2422 2422 else:
2423 2423 wctx = repo[None]
2424 2424 mctx = wctx.parents()[-1]
2425 2425
2426 2426 # backup pre-resolve (merge uses .orig for its own purposes)
2427 2427 a = repo.wjoin(f)
2428 2428 util.copyfile(a, a + ".resolve")
2429 2429
2430 2430 # resolve file
2431 2431 ms.resolve(f, wctx, mctx)
2432 2432
2433 2433 # replace filemerge's .orig file with our resolve file
2434 2434 util.rename(a + ".resolve", a + ".orig")
2435 2435
2436 2436 def revert(ui, repo, *pats, **opts):
2437 2437 """restore individual files or directories to an earlier state
2438 2438
2439 2439 (Use update -r to check out earlier revisions, revert does not
2440 2440 change the working directory parents.)
2441 2441
2442 2442 With no revision specified, revert the named files or directories
2443 2443 to the contents they had in the parent of the working directory.
2444 2444 This restores the contents of the affected files to an unmodified
2445 2445 state and unschedules adds, removes, copies, and renames. If the
2446 2446 working directory has two parents, you must explicitly specify the
2447 2447 revision to revert to.
2448 2448
2449 2449 Using the -r/--rev option, revert the given files or directories
2450 2450 to their contents as of a specific revision. This can be helpful
2451 2451 to "roll back" some or all of an earlier change. See 'hg help
2452 2452 dates' for a list of formats valid for -d/--date.
2453 2453
2454 2454 Revert modifies the working directory. It does not commit any
2455 2455 changes, or change the parent of the working directory. If you
2456 2456 revert to a revision other than the parent of the working
2457 2457 directory, the reverted files will thus appear modified
2458 2458 afterwards.
2459 2459
2460 2460 If a file has been deleted, it is restored. If the executable mode
2461 2461 of a file was changed, it is reset.
2462 2462
2463 2463 If names are given, all files matching the names are reverted.
2464 2464 If no arguments are given, no files are reverted.
2465 2465
2466 2466 Modified files are saved with a .orig suffix before reverting.
2467 2467 To disable these backups, use --no-backup.
2468 2468 """
2469 2469
2470 2470 if opts["date"]:
2471 2471 if opts["rev"]:
2472 2472 raise util.Abort(_("you can't specify a revision and a date"))
2473 2473 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2474 2474
2475 2475 if not pats and not opts.get('all'):
2476 2476 raise util.Abort(_('no files or directories specified; '
2477 2477 'use --all to revert the whole repo'))
2478 2478
2479 2479 parent, p2 = repo.dirstate.parents()
2480 2480 if not opts.get('rev') and p2 != nullid:
2481 2481 raise util.Abort(_('uncommitted merge - please provide a '
2482 2482 'specific revision'))
2483 2483 ctx = repo[opts.get('rev')]
2484 2484 node = ctx.node()
2485 2485 mf = ctx.manifest()
2486 2486 if node == parent:
2487 2487 pmf = mf
2488 2488 else:
2489 2489 pmf = None
2490 2490
2491 2491 # need all matching names in dirstate and manifest of target rev,
2492 2492 # so have to walk both. do not print errors if files exist in one
2493 2493 # but not other.
2494 2494
2495 2495 names = {}
2496 2496
2497 2497 wlock = repo.wlock()
2498 2498 try:
2499 2499 # walk dirstate.
2500 2500
2501 2501 m = cmdutil.match(repo, pats, opts)
2502 2502 m.bad = lambda x,y: False
2503 2503 for abs in repo.walk(m):
2504 2504 names[abs] = m.rel(abs), m.exact(abs)
2505 2505
2506 2506 # walk target manifest.
2507 2507
2508 2508 def badfn(path, msg):
2509 2509 if path in names:
2510 2510 return
2511 2511 path_ = path + '/'
2512 2512 for f in names:
2513 2513 if f.startswith(path_):
2514 2514 return
2515 2515 ui.warn("%s: %s\n" % (m.rel(path), msg))
2516 2516
2517 2517 m = cmdutil.match(repo, pats, opts)
2518 2518 m.bad = badfn
2519 2519 for abs in repo[node].walk(m):
2520 2520 if abs not in names:
2521 2521 names[abs] = m.rel(abs), m.exact(abs)
2522 2522
2523 2523 m = cmdutil.matchfiles(repo, names)
2524 2524 changes = repo.status(match=m)[:4]
2525 2525 modified, added, removed, deleted = map(set, changes)
2526 2526
2527 2527 # if f is a rename, also revert the source
2528 2528 cwd = repo.getcwd()
2529 2529 for f in added:
2530 2530 src = repo.dirstate.copied(f)
2531 2531 if src and src not in names and repo.dirstate[src] == 'r':
2532 2532 removed.add(src)
2533 2533 names[src] = (repo.pathto(src, cwd), True)
2534 2534
2535 2535 def removeforget(abs):
2536 2536 if repo.dirstate[abs] == 'a':
2537 2537 return _('forgetting %s\n')
2538 2538 return _('removing %s\n')
2539 2539
2540 2540 revert = ([], _('reverting %s\n'))
2541 2541 add = ([], _('adding %s\n'))
2542 2542 remove = ([], removeforget)
2543 2543 undelete = ([], _('undeleting %s\n'))
2544 2544
2545 2545 disptable = (
2546 2546 # dispatch table:
2547 2547 # file state
2548 2548 # action if in target manifest
2549 2549 # action if not in target manifest
2550 2550 # make backup if in target manifest
2551 2551 # make backup if not in target manifest
2552 2552 (modified, revert, remove, True, True),
2553 2553 (added, revert, remove, True, False),
2554 2554 (removed, undelete, None, False, False),
2555 2555 (deleted, revert, remove, False, False),
2556 2556 )
2557 2557
2558 2558 for abs, (rel, exact) in sorted(names.items()):
2559 2559 mfentry = mf.get(abs)
2560 2560 target = repo.wjoin(abs)
2561 2561 def handle(xlist, dobackup):
2562 2562 xlist[0].append(abs)
2563 2563 if dobackup and not opts.get('no_backup') and util.lexists(target):
2564 2564 bakname = "%s.orig" % rel
2565 2565 ui.note(_('saving current version of %s as %s\n') %
2566 2566 (rel, bakname))
2567 2567 if not opts.get('dry_run'):
2568 2568 util.copyfile(target, bakname)
2569 2569 if ui.verbose or not exact:
2570 2570 msg = xlist[1]
2571 2571 if not isinstance(msg, basestring):
2572 2572 msg = msg(abs)
2573 2573 ui.status(msg % rel)
2574 2574 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2575 2575 if abs not in table: continue
2576 2576 # file has changed in dirstate
2577 2577 if mfentry:
2578 2578 handle(hitlist, backuphit)
2579 2579 elif misslist is not None:
2580 2580 handle(misslist, backupmiss)
2581 2581 break
2582 2582 else:
2583 2583 if abs not in repo.dirstate:
2584 2584 if mfentry:
2585 2585 handle(add, True)
2586 2586 elif exact:
2587 2587 ui.warn(_('file not managed: %s\n') % rel)
2588 2588 continue
2589 2589 # file has not changed in dirstate
2590 2590 if node == parent:
2591 2591 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2592 2592 continue
2593 2593 if pmf is None:
2594 2594 # only need parent manifest in this unlikely case,
2595 2595 # so do not read by default
2596 2596 pmf = repo[parent].manifest()
2597 2597 if abs in pmf:
2598 2598 if mfentry:
2599 2599 # if version of file is same in parent and target
2600 2600 # manifests, do nothing
2601 2601 if (pmf[abs] != mfentry or
2602 2602 pmf.flags(abs) != mf.flags(abs)):
2603 2603 handle(revert, False)
2604 2604 else:
2605 2605 handle(remove, False)
2606 2606
2607 2607 if not opts.get('dry_run'):
2608 2608 def checkout(f):
2609 2609 fc = ctx[f]
2610 2610 repo.wwrite(f, fc.data(), fc.flags())
2611 2611
2612 2612 audit_path = util.path_auditor(repo.root)
2613 2613 for f in remove[0]:
2614 2614 if repo.dirstate[f] == 'a':
2615 2615 repo.dirstate.forget(f)
2616 2616 continue
2617 2617 audit_path(f)
2618 2618 try:
2619 2619 util.unlink(repo.wjoin(f))
2620 2620 except OSError:
2621 2621 pass
2622 2622 repo.dirstate.remove(f)
2623 2623
2624 2624 normal = None
2625 2625 if node == parent:
2626 2626 # We're reverting to our parent. If possible, we'd like status
2627 2627 # to report the file as clean. We have to use normallookup for
2628 2628 # merges to avoid losing information about merged/dirty files.
2629 2629 if p2 != nullid:
2630 2630 normal = repo.dirstate.normallookup
2631 2631 else:
2632 2632 normal = repo.dirstate.normal
2633 2633 for f in revert[0]:
2634 2634 checkout(f)
2635 2635 if normal:
2636 2636 normal(f)
2637 2637
2638 2638 for f in add[0]:
2639 2639 checkout(f)
2640 2640 repo.dirstate.add(f)
2641 2641
2642 2642 normal = repo.dirstate.normallookup
2643 2643 if node == parent and p2 == nullid:
2644 2644 normal = repo.dirstate.normal
2645 2645 for f in undelete[0]:
2646 2646 checkout(f)
2647 2647 normal(f)
2648 2648
2649 2649 finally:
2650 2650 wlock.release()
2651 2651
2652 2652 def rollback(ui, repo):
2653 2653 """roll back the last transaction
2654 2654
2655 2655 This command should be used with care. There is only one level of
2656 2656 rollback, and there is no way to undo a rollback. It will also
2657 2657 restore the dirstate at the time of the last transaction, losing
2658 2658 any dirstate changes since that time.
2659 2659
2660 2660 Transactions are used to encapsulate the effects of all commands
2661 2661 that create new changesets or propagate existing changesets into a
2662 2662 repository. For example, the following commands are transactional,
2663 2663 and their effects can be rolled back:
2664 2664
2665 2665 commit
2666 2666 import
2667 2667 pull
2668 2668 push (with this repository as destination)
2669 2669 unbundle
2670 2670
2671 2671 This command is not intended for use on public repositories. Once
2672 2672 changes are visible for pull by other users, rolling a transaction
2673 2673 back locally is ineffective (someone else may already have pulled
2674 2674 the changes). Furthermore, a race is possible with readers of the
2675 2675 repository; for example an in-progress pull from the repository
2676 2676 may fail if a rollback is performed.
2677 2677 """
2678 2678 repo.rollback()
2679 2679
2680 2680 def root(ui, repo):
2681 2681 """print the root (top) of the current working directory
2682 2682
2683 2683 Print the root directory of the current repository.
2684 2684 """
2685 2685 ui.write(repo.root + "\n")
2686 2686
2687 2687 def serve(ui, repo, **opts):
2688 2688 """export the repository via HTTP
2689 2689
2690 2690 Start a local HTTP repository browser and pull server.
2691 2691
2692 2692 By default, the server logs accesses to stdout and errors to
2693 2693 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
2694 2694 files.
2695 2695 """
2696 2696
2697 2697 if opts["stdio"]:
2698 2698 if repo is None:
2699 2699 raise error.RepoError(_("There is no Mercurial repository here"
2700 2700 " (.hg not found)"))
2701 2701 s = sshserver.sshserver(ui, repo)
2702 2702 s.serve_forever()
2703 2703
2704 2704 baseui = repo and repo.baseui or ui
2705 2705 optlist = ("name templates style address port prefix ipv6"
2706 2706 " accesslog errorlog webdir_conf certificate")
2707 2707 for o in optlist.split():
2708 2708 if opts[o]:
2709 2709 baseui.setconfig("web", o, str(opts[o]))
2710 2710 if (repo is not None) and (repo.ui != baseui):
2711 2711 repo.ui.setconfig("web", o, str(opts[o]))
2712 2712
2713 2713 if repo is None and not ui.config("web", "webdir_conf"):
2714 2714 raise error.RepoError(_("There is no Mercurial repository here"
2715 2715 " (.hg not found)"))
2716 2716
2717 2717 class service(object):
2718 2718 def init(self):
2719 2719 util.set_signal_handler()
2720 2720 self.httpd = server.create_server(baseui, repo)
2721 2721
2722 2722 if not ui.verbose: return
2723 2723
2724 2724 if self.httpd.prefix:
2725 2725 prefix = self.httpd.prefix.strip('/') + '/'
2726 2726 else:
2727 2727 prefix = ''
2728 2728
2729 2729 port = ':%d' % self.httpd.port
2730 2730 if port == ':80':
2731 2731 port = ''
2732 2732
2733 2733 bindaddr = self.httpd.addr
2734 2734 if bindaddr == '0.0.0.0':
2735 2735 bindaddr = '*'
2736 2736 elif ':' in bindaddr: # IPv6
2737 2737 bindaddr = '[%s]' % bindaddr
2738 2738
2739 2739 fqaddr = self.httpd.fqaddr
2740 2740 if ':' in fqaddr:
2741 2741 fqaddr = '[%s]' % fqaddr
2742 2742 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2743 2743 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2744 2744
2745 2745 def run(self):
2746 2746 self.httpd.serve_forever()
2747 2747
2748 2748 service = service()
2749 2749
2750 2750 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2751 2751
2752 2752 def status(ui, repo, *pats, **opts):
2753 2753 """show changed files in the working directory
2754 2754
2755 2755 Show status of files in the repository. If names are given, only
2756 2756 files that match are shown. Files that are clean or ignored or
2757 2757 the source of a copy/move operation, are not listed unless
2758 2758 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
2759 2759 Unless options described with "show only ..." are given, the
2760 2760 options -mardu are used.
2761 2761
2762 2762 Option -q/--quiet hides untracked (unknown and ignored) files
2763 2763 unless explicitly requested with -u/--unknown or -i/--ignored.
2764 2764
2765 2765 NOTE: status may appear to disagree with diff if permissions have
2766 2766 changed or a merge has occurred. The standard diff format does not
2767 2767 report permission changes and diff only reports changes relative
2768 2768 to one merge parent.
2769 2769
2770 2770 If one revision is given, it is used as the base revision.
2771 2771 If two revisions are given, the differences between them are
2772 2772 shown.
2773 2773
2774 2774 The codes used to show the status of files are:
2775 2775 M = modified
2776 2776 A = added
2777 2777 R = removed
2778 2778 C = clean
2779 2779 ! = missing (deleted by non-hg command, but still tracked)
2780 2780 ? = not tracked
2781 2781 I = ignored
2782 2782 = origin of the previous file listed as A (added)
2783 2783 """
2784 2784
2785 2785 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2786 2786 cwd = (pats and repo.getcwd()) or ''
2787 2787 end = opts.get('print0') and '\0' or '\n'
2788 2788 copy = {}
2789 2789 states = 'modified added removed deleted unknown ignored clean'.split()
2790 2790 show = [k for k in states if opts.get(k)]
2791 2791 if opts.get('all'):
2792 2792 show += ui.quiet and (states[:4] + ['clean']) or states
2793 2793 if not show:
2794 2794 show = ui.quiet and states[:4] or states[:5]
2795 2795
2796 2796 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2797 2797 'ignored' in show, 'clean' in show, 'unknown' in show)
2798 2798 changestates = zip(states, 'MAR!?IC', stat)
2799 2799
2800 2800 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2801 2801 ctxn = repo[nullid]
2802 2802 ctx1 = repo[node1]
2803 2803 ctx2 = repo[node2]
2804 2804 added = stat[1]
2805 2805 if node2 is None:
2806 2806 added = stat[0] + stat[1] # merged?
2807 2807
2808 2808 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2809 2809 if k in added:
2810 2810 copy[k] = v
2811 2811 elif v in added:
2812 2812 copy[v] = k
2813 2813
2814 2814 for state, char, files in changestates:
2815 2815 if state in show:
2816 2816 format = "%s %%s%s" % (char, end)
2817 2817 if opts.get('no_status'):
2818 2818 format = "%%s%s" % end
2819 2819
2820 2820 for f in files:
2821 2821 ui.write(format % repo.pathto(f, cwd))
2822 2822 if f in copy:
2823 2823 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2824 2824
2825 2825 def tag(ui, repo, name1, *names, **opts):
2826 2826 """add one or more tags for the current or given revision
2827 2827
2828 2828 Name a particular revision using <name>.
2829 2829
2830 2830 Tags are used to name particular revisions of the repository and are
2831 2831 very useful to compare different revisions, to go back to significant
2832 2832 earlier versions or to mark branch points as releases, etc.
2833 2833
2834 2834 If no revision is given, the parent of the working directory is
2835 2835 used, or tip if no revision is checked out.
2836 2836
2837 2837 To facilitate version control, distribution, and merging of tags,
2838 2838 they are stored as a file named ".hgtags" which is managed
2839 2839 similarly to other project files and can be hand-edited if
2840 2840 necessary. The file '.hg/localtags' is used for local tags (not
2841 2841 shared among repositories).
2842 2842
2843 2843 See 'hg help dates' for a list of formats valid for -d/--date.
2844 2844 """
2845 2845
2846 2846 rev_ = "."
2847 2847 names = (name1,) + names
2848 2848 if len(names) != len(set(names)):
2849 2849 raise util.Abort(_('tag names must be unique'))
2850 2850 for n in names:
2851 2851 if n in ['tip', '.', 'null']:
2852 2852 raise util.Abort(_('the name \'%s\' is reserved') % n)
2853 2853 if opts.get('rev') and opts.get('remove'):
2854 2854 raise util.Abort(_("--rev and --remove are incompatible"))
2855 2855 if opts.get('rev'):
2856 2856 rev_ = opts['rev']
2857 2857 message = opts.get('message')
2858 2858 if opts.get('remove'):
2859 2859 expectedtype = opts.get('local') and 'local' or 'global'
2860 2860 for n in names:
2861 2861 if not repo.tagtype(n):
2862 2862 raise util.Abort(_('tag \'%s\' does not exist') % n)
2863 2863 if repo.tagtype(n) != expectedtype:
2864 2864 if expectedtype == 'global':
2865 2865 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2866 2866 else:
2867 2867 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2868 2868 rev_ = nullid
2869 2869 if not message:
2870 2870 message = _('Removed tag %s') % ', '.join(names)
2871 2871 elif not opts.get('force'):
2872 2872 for n in names:
2873 2873 if n in repo.tags():
2874 2874 raise util.Abort(_('tag \'%s\' already exists '
2875 2875 '(use -f to force)') % n)
2876 2876 if not rev_ and repo.dirstate.parents()[1] != nullid:
2877 2877 raise util.Abort(_('uncommitted merge - please provide a '
2878 2878 'specific revision'))
2879 2879 r = repo[rev_].node()
2880 2880
2881 2881 if not message:
2882 2882 message = (_('Added tag %s for changeset %s') %
2883 2883 (', '.join(names), short(r)))
2884 2884
2885 2885 date = opts.get('date')
2886 2886 if date:
2887 2887 date = util.parsedate(date)
2888 2888
2889 2889 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2890 2890
2891 2891 def tags(ui, repo):
2892 2892 """list repository tags
2893 2893
2894 2894 This lists both regular and local tags. When the -v/--verbose
2895 2895 switch is used, a third column "local" is printed for local tags.
2896 2896 """
2897 2897
2898 2898 hexfunc = ui.debugflag and hex or short
2899 2899 tagtype = ""
2900 2900
2901 2901 for t, n in reversed(repo.tagslist()):
2902 2902 if ui.quiet:
2903 2903 ui.write("%s\n" % t)
2904 2904 continue
2905 2905
2906 2906 try:
2907 2907 hn = hexfunc(n)
2908 2908 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2909 2909 except error.LookupError:
2910 2910 r = " ?:%s" % hn
2911 2911 else:
2912 2912 spaces = " " * (30 - encoding.colwidth(t))
2913 2913 if ui.verbose:
2914 2914 if repo.tagtype(t) == 'local':
2915 2915 tagtype = " local"
2916 2916 else:
2917 2917 tagtype = ""
2918 2918 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2919 2919
2920 2920 def tip(ui, repo, **opts):
2921 2921 """show the tip revision
2922 2922
2923 2923 The tip revision (usually just called the tip) is the changeset
2924 2924 most recently added to the repository (and therefore the most
2925 2925 recently changed head).
2926 2926
2927 2927 If you have just made a commit, that commit will be the tip. If
2928 2928 you have just pulled changes from another repository, the tip of
2929 2929 that repository becomes the current tip. The "tip" tag is special
2930 2930 and cannot be renamed or assigned to a different changeset.
2931 2931 """
2932 2932 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2933 2933
2934 2934 def unbundle(ui, repo, fname1, *fnames, **opts):
2935 2935 """apply one or more changegroup files
2936 2936
2937 2937 Apply one or more compressed changegroup files generated by the
2938 2938 bundle command.
2939 2939 """
2940 2940 fnames = (fname1,) + fnames
2941 2941
2942 2942 lock = repo.lock()
2943 2943 try:
2944 2944 for fname in fnames:
2945 2945 f = url.open(ui, fname)
2946 2946 gen = changegroup.readbundle(f, fname)
2947 2947 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2948 2948 finally:
2949 2949 lock.release()
2950 2950
2951 2951 return postincoming(ui, repo, modheads, opts.get('update'), None)
2952 2952
2953 2953 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2954 2954 """update working directory
2955 2955
2956 2956 Update the repository's working directory to the specified
2957 2957 revision, or the tip of the current branch if none is specified.
2958 2958 Use null as the revision to remove the working copy (like 'hg
2959 2959 clone -U').
2960 2960
2961 2961 When the working directory contains no uncommitted changes, it
2962 2962 will be replaced by the state of the requested revision from the
2963 2963 repository. When the requested revision is on a different branch,
2964 2964 the working directory will additionally be switched to that
2965 2965 branch.
2966 2966
2967 2967 When there are uncommitted changes, use option -C/--clean to
2968 2968 discard them, forcibly replacing the state of the working
2969 2969 directory with the requested revision.
2970 2970
2971 2971 When there are uncommitted changes and option -C/--clean is not
2972 2972 used, and the parent revision and requested revision are on the
2973 2973 same branch, and one of them is an ancestor of the other, then the
2974 2974 new working directory will contain the requested revision merged
2975 2975 with the uncommitted changes. Otherwise, the update will fail with
2976 2976 a suggestion to use 'merge' or 'update -C' instead.
2977 2977
2978 2978 If you want to update just one file to an older revision, use
2979 2979 revert.
2980 2980
2981 2981 See 'hg help dates' for a list of formats valid for -d/--date.
2982 2982 """
2983 2983 if rev and node:
2984 2984 raise util.Abort(_("please specify just one revision"))
2985 2985
2986 2986 if not rev:
2987 2987 rev = node
2988 2988
2989 2989 if date:
2990 2990 if rev:
2991 2991 raise util.Abort(_("you can't specify a revision and a date"))
2992 2992 rev = cmdutil.finddate(ui, repo, date)
2993 2993
2994 2994 if clean:
2995 2995 return hg.clean(repo, rev)
2996 2996 else:
2997 2997 return hg.update(repo, rev)
2998 2998
2999 2999 def verify(ui, repo):
3000 3000 """verify the integrity of the repository
3001 3001
3002 3002 Verify the integrity of the current repository.
3003 3003
3004 3004 This will perform an extensive check of the repository's
3005 3005 integrity, validating the hashes and checksums of each entry in
3006 3006 the changelog, manifest, and tracked files, as well as the
3007 3007 integrity of their crosslinks and indices.
3008 3008 """
3009 3009 return hg.verify(repo)
3010 3010
3011 3011 def version_(ui):
3012 3012 """output version and copyright information"""
3013 3013 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3014 3014 % util.version())
3015 3015 ui.status(_(
3016 3016 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3017 3017 "This is free software; see the source for copying conditions. "
3018 3018 "There is NO\nwarranty; "
3019 3019 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3020 3020 ))
3021 3021
3022 3022 # Command options and aliases are listed here, alphabetically
3023 3023
3024 3024 globalopts = [
3025 3025 ('R', 'repository', '',
3026 3026 _('repository root directory or symbolic path name')),
3027 3027 ('', 'cwd', '', _('change working directory')),
3028 3028 ('y', 'noninteractive', None,
3029 3029 _('do not prompt, assume \'yes\' for any required answers')),
3030 3030 ('q', 'quiet', None, _('suppress output')),
3031 3031 ('v', 'verbose', None, _('enable additional output')),
3032 3032 ('', 'config', [], _('set/override config option')),
3033 3033 ('', 'debug', None, _('enable debugging output')),
3034 3034 ('', 'debugger', None, _('start debugger')),
3035 3035 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3036 3036 ('', 'encodingmode', encoding.encodingmode,
3037 3037 _('set the charset encoding mode')),
3038 3038 ('', 'traceback', None, _('print traceback on exception')),
3039 3039 ('', 'time', None, _('time how long the command takes')),
3040 3040 ('', 'profile', None, _('print command execution profile')),
3041 3041 ('', 'version', None, _('output version information and exit')),
3042 3042 ('h', 'help', None, _('display help and exit')),
3043 3043 ]
3044 3044
3045 3045 dryrunopts = [('n', 'dry-run', None,
3046 3046 _('do not perform actions, just print output'))]
3047 3047
3048 3048 remoteopts = [
3049 3049 ('e', 'ssh', '', _('specify ssh command to use')),
3050 3050 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3051 3051 ]
3052 3052
3053 3053 walkopts = [
3054 3054 ('I', 'include', [], _('include names matching the given patterns')),
3055 3055 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3056 3056 ]
3057 3057
3058 3058 commitopts = [
3059 3059 ('m', 'message', '', _('use <text> as commit message')),
3060 3060 ('l', 'logfile', '', _('read commit message from <file>')),
3061 3061 ]
3062 3062
3063 3063 commitopts2 = [
3064 3064 ('d', 'date', '', _('record datecode as commit date')),
3065 3065 ('u', 'user', '', _('record the specified user as committer')),
3066 3066 ]
3067 3067
3068 3068 templateopts = [
3069 3069 ('', 'style', '', _('display using template map file')),
3070 3070 ('', 'template', '', _('display with template')),
3071 3071 ]
3072 3072
3073 3073 logopts = [
3074 3074 ('p', 'patch', None, _('show patch')),
3075 3075 ('g', 'git', None, _('use git extended diff format')),
3076 3076 ('l', 'limit', '', _('limit number of changes displayed')),
3077 3077 ('M', 'no-merges', None, _('do not show merges')),
3078 3078 ] + templateopts
3079 3079
3080 3080 diffopts = [
3081 3081 ('a', 'text', None, _('treat all files as text')),
3082 3082 ('g', 'git', None, _('use git extended diff format')),
3083 3083 ('', 'nodates', None, _("don't include dates in diff headers"))
3084 3084 ]
3085 3085
3086 3086 diffopts2 = [
3087 3087 ('p', 'show-function', None, _('show which function each change is in')),
3088 3088 ('w', 'ignore-all-space', None,
3089 3089 _('ignore white space when comparing lines')),
3090 3090 ('b', 'ignore-space-change', None,
3091 3091 _('ignore changes in the amount of white space')),
3092 3092 ('B', 'ignore-blank-lines', None,
3093 3093 _('ignore changes whose lines are all blank')),
3094 3094 ('U', 'unified', '', _('number of lines of context to show'))
3095 3095 ]
3096 3096
3097 3097 similarityopts = [
3098 3098 ('s', 'similarity', '',
3099 3099 _('guess renamed files by similarity (0<=s<=100)'))
3100 3100 ]
3101 3101
3102 3102 table = {
3103 3103 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3104 3104 "addremove":
3105 3105 (addremove, similarityopts + walkopts + dryrunopts,
3106 3106 _('[OPTION]... [FILE]...')),
3107 3107 "^annotate|blame":
3108 3108 (annotate,
3109 3109 [('r', 'rev', '', _('annotate the specified revision')),
3110 3110 ('f', 'follow', None, _('follow file copies and renames')),
3111 3111 ('a', 'text', None, _('treat all files as text')),
3112 3112 ('u', 'user', None, _('list the author (long with -v)')),
3113 3113 ('d', 'date', None, _('list the date (short with -q)')),
3114 3114 ('n', 'number', None, _('list the revision number (default)')),
3115 3115 ('c', 'changeset', None, _('list the changeset')),
3116 3116 ('l', 'line-number', None,
3117 3117 _('show line number at the first appearance'))
3118 3118 ] + walkopts,
3119 3119 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3120 3120 "archive":
3121 3121 (archive,
3122 3122 [('', 'no-decode', None, _('do not pass files through decoders')),
3123 3123 ('p', 'prefix', '', _('directory prefix for files in archive')),
3124 3124 ('r', 'rev', '', _('revision to distribute')),
3125 3125 ('t', 'type', '', _('type of distribution to create')),
3126 3126 ] + walkopts,
3127 3127 _('[OPTION]... DEST')),
3128 3128 "backout":
3129 3129 (backout,
3130 3130 [('', 'merge', None,
3131 3131 _('merge with old dirstate parent after backout')),
3132 3132 ('', 'parent', '', _('parent to choose when backing out merge')),
3133 3133 ('r', 'rev', '', _('revision to backout')),
3134 3134 ] + walkopts + commitopts + commitopts2,
3135 3135 _('[OPTION]... [-r] REV')),
3136 3136 "bisect":
3137 3137 (bisect,
3138 3138 [('r', 'reset', False, _('reset bisect state')),
3139 3139 ('g', 'good', False, _('mark changeset good')),
3140 3140 ('b', 'bad', False, _('mark changeset bad')),
3141 3141 ('s', 'skip', False, _('skip testing changeset')),
3142 3142 ('c', 'command', '', _('use command to check changeset state')),
3143 3143 ('U', 'noupdate', False, _('do not update to target'))],
3144 3144 _("[-gbsr] [-c CMD] [REV]")),
3145 3145 "branch":
3146 3146 (branch,
3147 3147 [('f', 'force', None,
3148 3148 _('set branch name even if it shadows an existing branch')),
3149 3149 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3150 3150 _('[-fC] [NAME]')),
3151 3151 "branches":
3152 3152 (branches,
3153 3153 [('a', 'active', False,
3154 3154 _('show only branches that have unmerged heads'))],
3155 3155 _('[-a]')),
3156 3156 "bundle":
3157 3157 (bundle,
3158 3158 [('f', 'force', None,
3159 3159 _('run even when remote repository is unrelated')),
3160 3160 ('r', 'rev', [],
3161 3161 _('a changeset up to which you would like to bundle')),
3162 3162 ('', 'base', [],
3163 3163 _('a base changeset to specify instead of a destination')),
3164 3164 ('a', 'all', None, _('bundle all changesets in the repository')),
3165 3165 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3166 3166 ] + remoteopts,
3167 3167 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3168 3168 "cat":
3169 3169 (cat,
3170 3170 [('o', 'output', '', _('print output to file with formatted name')),
3171 3171 ('r', 'rev', '', _('print the given revision')),
3172 3172 ('', 'decode', None, _('apply any matching decode filter')),
3173 3173 ] + walkopts,
3174 3174 _('[OPTION]... FILE...')),
3175 3175 "^clone":
3176 3176 (clone,
3177 3177 [('U', 'noupdate', None,
3178 3178 _('the clone will only contain a repository (no working copy)')),
3179 3179 ('r', 'rev', [],
3180 3180 _('a changeset you would like to have after cloning')),
3181 3181 ('', 'pull', None, _('use pull protocol to copy metadata')),
3182 3182 ('', 'uncompressed', None,
3183 3183 _('use uncompressed transfer (fast over LAN)')),
3184 3184 ] + remoteopts,
3185 3185 _('[OPTION]... SOURCE [DEST]')),
3186 3186 "^commit|ci":
3187 3187 (commit,
3188 3188 [('A', 'addremove', None,
3189 3189 _('mark new/missing files as added/removed before committing')),
3190 3190 ('', 'close-branch', None,
3191 3191 _('mark a branch as closed, hiding it from the branch list')),
3192 3192 ] + walkopts + commitopts + commitopts2,
3193 3193 _('[OPTION]... [FILE]...')),
3194 3194 "copy|cp":
3195 3195 (copy,
3196 3196 [('A', 'after', None, _('record a copy that has already occurred')),
3197 3197 ('f', 'force', None,
3198 3198 _('forcibly copy over an existing managed file')),
3199 3199 ] + walkopts + dryrunopts,
3200 3200 _('[OPTION]... [SOURCE]... DEST')),
3201 3201 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3202 3202 "debugcheckstate": (debugcheckstate, []),
3203 3203 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3204 3204 "debugcomplete":
3205 3205 (debugcomplete,
3206 3206 [('o', 'options', None, _('show the command options'))],
3207 3207 _('[-o] CMD')),
3208 3208 "debugdate":
3209 3209 (debugdate,
3210 3210 [('e', 'extended', None, _('try extended date formats'))],
3211 3211 _('[-e] DATE [RANGE]')),
3212 3212 "debugdata": (debugdata, [], _('FILE REV')),
3213 3213 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3214 3214 "debugindex": (debugindex, [], _('FILE')),
3215 3215 "debugindexdot": (debugindexdot, [], _('FILE')),
3216 3216 "debuginstall": (debuginstall, []),
3217 3217 "debugrebuildstate":
3218 3218 (debugrebuildstate,
3219 3219 [('r', 'rev', '', _('revision to rebuild to'))],
3220 3220 _('[-r REV] [REV]')),
3221 3221 "debugrename":
3222 3222 (debugrename,
3223 3223 [('r', 'rev', '', _('revision to debug'))],
3224 3224 _('[-r REV] FILE')),
3225 3225 "debugsetparents":
3226 3226 (debugsetparents, [], _('REV1 [REV2]')),
3227 3227 "debugstate":
3228 3228 (debugstate,
3229 3229 [('', 'nodates', None, _('do not display the saved mtime'))],
3230 3230 _('[OPTION]...')),
3231 3231 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3232 3232 "^diff":
3233 3233 (diff,
3234 3234 [('r', 'rev', [], _('revision')),
3235 3235 ('c', 'change', '', _('change made by revision'))
3236 3236 ] + diffopts + diffopts2 + walkopts,
3237 3237 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3238 3238 "^export":
3239 3239 (export,
3240 3240 [('o', 'output', '', _('print output to file with formatted name')),
3241 3241 ('', 'switch-parent', None, _('diff against the second parent'))
3242 3242 ] + diffopts,
3243 3243 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3244 3244 "grep":
3245 3245 (grep,
3246 3246 [('0', 'print0', None, _('end fields with NUL')),
3247 3247 ('', 'all', None, _('print all revisions that match')),
3248 3248 ('f', 'follow', None,
3249 3249 _('follow changeset history, or file history across copies and renames')),
3250 3250 ('i', 'ignore-case', None, _('ignore case when matching')),
3251 3251 ('l', 'files-with-matches', None,
3252 3252 _('print only filenames and revisions that match')),
3253 3253 ('n', 'line-number', None, _('print matching line numbers')),
3254 3254 ('r', 'rev', [], _('search in given revision range')),
3255 3255 ('u', 'user', None, _('list the author (long with -v)')),
3256 3256 ('d', 'date', None, _('list the date (short with -q)')),
3257 3257 ] + walkopts,
3258 3258 _('[OPTION]... PATTERN [FILE]...')),
3259 3259 "heads":
3260 3260 (heads,
3261 3261 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3262 3262 ('a', 'active', False,
3263 3263 _('show only the active heads from open branches')),
3264 3264 ('c', 'closed', False,
3265 3265 _('show normal and closed heads')),
3266 3266 ] + templateopts,
3267 3267 _('[-r STARTREV] [REV]...')),
3268 3268 "help": (help_, [], _('[TOPIC]')),
3269 3269 "identify|id":
3270 3270 (identify,
3271 3271 [('r', 'rev', '', _('identify the specified revision')),
3272 3272 ('n', 'num', None, _('show local revision number')),
3273 3273 ('i', 'id', None, _('show global revision id')),
3274 3274 ('b', 'branch', None, _('show branch')),
3275 3275 ('t', 'tags', None, _('show tags'))],
3276 3276 _('[-nibt] [-r REV] [SOURCE]')),
3277 3277 "import|patch":
3278 3278 (import_,
3279 3279 [('p', 'strip', 1,
3280 3280 _('directory strip option for patch. This has the same '
3281 3281 'meaning as the corresponding patch option')),
3282 3282 ('b', 'base', '', _('base path')),
3283 3283 ('f', 'force', None,
3284 3284 _('skip check for outstanding uncommitted changes')),
3285 3285 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3286 3286 ('', 'exact', None,
3287 3287 _('apply patch to the nodes from which it was generated')),
3288 3288 ('', 'import-branch', None,
3289 3289 _('use any branch information in patch (implied by --exact)'))] +
3290 3290 commitopts + commitopts2 + similarityopts,
3291 3291 _('[OPTION]... PATCH...')),
3292 3292 "incoming|in":
3293 3293 (incoming,
3294 3294 [('f', 'force', None,
3295 3295 _('run even when remote repository is unrelated')),
3296 3296 ('n', 'newest-first', None, _('show newest record first')),
3297 3297 ('', 'bundle', '', _('file to store the bundles into')),
3298 3298 ('r', 'rev', [],
3299 3299 _('a specific revision up to which you would like to pull')),
3300 3300 ] + logopts + remoteopts,
3301 3301 _('[-p] [-n] [-M] [-f] [-r REV]...'
3302 3302 ' [--bundle FILENAME] [SOURCE]')),
3303 3303 "^init":
3304 3304 (init,
3305 3305 remoteopts,
3306 3306 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3307 3307 "locate":
3308 3308 (locate,
3309 3309 [('r', 'rev', '', _('search the repository as it stood at REV')),
3310 3310 ('0', 'print0', None,
3311 3311 _('end filenames with NUL, for use with xargs')),
3312 3312 ('f', 'fullpath', None,
3313 3313 _('print complete paths from the filesystem root')),
3314 3314 ] + walkopts,
3315 3315 _('[OPTION]... [PATTERN]...')),
3316 3316 "^log|history":
3317 3317 (log,
3318 3318 [('f', 'follow', None,
3319 3319 _('follow changeset history, or file history across copies and renames')),
3320 3320 ('', 'follow-first', None,
3321 3321 _('only follow the first parent of merge changesets')),
3322 3322 ('d', 'date', '', _('show revisions matching date spec')),
3323 3323 ('C', 'copies', None, _('show copied files')),
3324 3324 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3325 3325 ('r', 'rev', [], _('show the specified revision or range')),
3326 3326 ('', 'removed', None, _('include revisions where files were removed')),
3327 3327 ('m', 'only-merges', None, _('show only merges')),
3328 3328 ('u', 'user', [], _('revisions committed by user')),
3329 3329 ('b', 'only-branch', [],
3330 3330 _('show only changesets within the given named branch')),
3331 3331 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3332 3332 ] + logopts + walkopts,
3333 3333 _('[OPTION]... [FILE]')),
3334 3334 "manifest":
3335 3335 (manifest,
3336 3336 [('r', 'rev', '', _('revision to display'))],
3337 3337 _('[-r REV]')),
3338 3338 "^merge":
3339 3339 (merge,
3340 3340 [('f', 'force', None, _('force a merge with outstanding changes')),
3341 3341 ('r', 'rev', '', _('revision to merge')),
3342 3342 ('S', 'show', None,
3343 3343 _('review revisions to merge (no merge is performed)'))],
3344 3344 _('[-f] [[-r] REV]')),
3345 3345 "outgoing|out":
3346 3346 (outgoing,
3347 3347 [('f', 'force', None,
3348 3348 _('run even when remote repository is unrelated')),
3349 3349 ('r', 'rev', [],
3350 3350 _('a specific revision up to which you would like to push')),
3351 3351 ('n', 'newest-first', None, _('show newest record first')),
3352 3352 ] + logopts + remoteopts,
3353 3353 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3354 3354 "^parents":
3355 3355 (parents,
3356 3356 [('r', 'rev', '', _('show parents from the specified revision')),
3357 3357 ] + templateopts,
3358 3358 _('[-r REV] [FILE]')),
3359 3359 "paths": (paths, [], _('[NAME]')),
3360 3360 "^pull":
3361 3361 (pull,
3362 3362 [('u', 'update', None,
3363 3363 _('update to new tip if changesets were pulled')),
3364 3364 ('f', 'force', None,
3365 3365 _('run even when remote repository is unrelated')),
3366 3366 ('r', 'rev', [],
3367 3367 _('a specific revision up to which you would like to pull')),
3368 3368 ] + remoteopts,
3369 3369 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3370 3370 "^push":
3371 3371 (push,
3372 3372 [('f', 'force', None, _('force push')),
3373 3373 ('r', 'rev', [],
3374 3374 _('a specific revision up to which you would like to push')),
3375 3375 ] + remoteopts,
3376 3376 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3377 3377 "recover": (recover, []),
3378 3378 "^remove|rm":
3379 3379 (remove,
3380 3380 [('A', 'after', None, _('record delete for missing files')),
3381 3381 ('f', 'force', None,
3382 3382 _('remove (and delete) file even if added or modified')),
3383 3383 ] + walkopts,
3384 3384 _('[OPTION]... FILE...')),
3385 3385 "rename|mv":
3386 3386 (rename,
3387 3387 [('A', 'after', None, _('record a rename that has already occurred')),
3388 3388 ('f', 'force', None,
3389 3389 _('forcibly copy over an existing managed file')),
3390 3390 ] + walkopts + dryrunopts,
3391 3391 _('[OPTION]... SOURCE... DEST')),
3392 3392 "resolve":
3393 3393 (resolve,
3394 3394 [('a', 'all', None, _('remerge all unresolved files')),
3395 3395 ('l', 'list', None, _('list state of files needing merge')),
3396 3396 ('m', 'mark', None, _('mark files as resolved')),
3397 3397 ('u', 'unmark', None, _('unmark files as resolved'))]
3398 3398 + walkopts,
3399 3399 _('[OPTION]... [FILE]...')),
3400 3400 "revert":
3401 3401 (revert,
3402 3402 [('a', 'all', None, _('revert all changes when no arguments given')),
3403 3403 ('d', 'date', '', _('tipmost revision matching date')),
3404 3404 ('r', 'rev', '', _('revision to revert to')),
3405 3405 ('', 'no-backup', None, _('do not save backup copies of files')),
3406 3406 ] + walkopts + dryrunopts,
3407 3407 _('[OPTION]... [-r REV] [NAME]...')),
3408 3408 "rollback": (rollback, []),
3409 3409 "root": (root, []),
3410 3410 "^serve":
3411 3411 (serve,
3412 3412 [('A', 'accesslog', '', _('name of access log file to write to')),
3413 3413 ('d', 'daemon', None, _('run server in background')),
3414 3414 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3415 3415 ('E', 'errorlog', '', _('name of error log file to write to')),
3416 3416 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3417 3417 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3418 3418 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3419 3419 ('n', 'name', '',
3420 3420 _('name to show in web pages (default: working directory)')),
3421 3421 ('', 'webdir-conf', '', _('name of the webdir config file'
3422 3422 ' (serve more than one repository)')),
3423 3423 ('', 'pid-file', '', _('name of file to write process ID to')),
3424 3424 ('', 'stdio', None, _('for remote clients')),
3425 3425 ('t', 'templates', '', _('web templates to use')),
3426 3426 ('', 'style', '', _('template style to use')),
3427 3427 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3428 3428 ('', 'certificate', '', _('SSL certificate file'))],
3429 3429 _('[OPTION]...')),
3430 3430 "showconfig|debugconfig":
3431 3431 (showconfig,
3432 3432 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3433 3433 _('[-u] [NAME]...')),
3434 3434 "^status|st":
3435 3435 (status,
3436 3436 [('A', 'all', None, _('show status of all files')),
3437 3437 ('m', 'modified', None, _('show only modified files')),
3438 3438 ('a', 'added', None, _('show only added files')),
3439 3439 ('r', 'removed', None, _('show only removed files')),
3440 3440 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3441 3441 ('c', 'clean', None, _('show only files without changes')),
3442 3442 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3443 3443 ('i', 'ignored', None, _('show only ignored files')),
3444 3444 ('n', 'no-status', None, _('hide status prefix')),
3445 3445 ('C', 'copies', None, _('show source of copied files')),
3446 3446 ('0', 'print0', None,
3447 3447 _('end filenames with NUL, for use with xargs')),
3448 3448 ('', 'rev', [], _('show difference from revision')),
3449 3449 ] + walkopts,
3450 3450 _('[OPTION]... [FILE]...')),
3451 3451 "tag":
3452 3452 (tag,
3453 3453 [('f', 'force', None, _('replace existing tag')),
3454 3454 ('l', 'local', None, _('make the tag local')),
3455 3455 ('r', 'rev', '', _('revision to tag')),
3456 3456 ('', 'remove', None, _('remove a tag')),
3457 3457 # -l/--local is already there, commitopts cannot be used
3458 3458 ('m', 'message', '', _('use <text> as commit message')),
3459 3459 ] + commitopts2,
3460 3460 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3461 3461 "tags": (tags, []),
3462 3462 "tip":
3463 3463 (tip,
3464 3464 [('p', 'patch', None, _('show patch')),
3465 3465 ('g', 'git', None, _('use git extended diff format')),
3466 3466 ] + templateopts,
3467 3467 _('[-p]')),
3468 3468 "unbundle":
3469 3469 (unbundle,
3470 3470 [('u', 'update', None,
3471 3471 _('update to new tip if changesets were unbundled'))],
3472 3472 _('[-u] FILE...')),
3473 3473 "^update|up|checkout|co":
3474 3474 (update,
3475 3475 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3476 3476 ('d', 'date', '', _('tipmost revision matching date')),
3477 3477 ('r', 'rev', '', _('revision'))],
3478 3478 _('[-C] [-d DATE] [[-r] REV]')),
3479 3479 "verify": (verify, []),
3480 3480 "version": (version_, []),
3481 3481 }
3482 3482
3483 3483 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3484 3484 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3485 3485 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,1394 +1,1434 b''
1 1 # patch.py - patch file parsing routines
2 2 #
3 3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
4 4 # Copyright 2007 Chris Mason <chris.mason@oracle.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 from i18n import _
10 10 from node import hex, nullid, short
11 11 import base85, cmdutil, mdiff, util, diffhelpers, copies
12 12 import cStringIO, email.Parser, os, re, math
13 13 import sys, tempfile, zlib
14 14
15 15 gitre = re.compile('diff --git a/(.*) b/(.*)')
16 16
17 17 class PatchError(Exception):
18 18 pass
19 19
20 20 class NoHunks(PatchError):
21 21 pass
22 22
23 23 # helper functions
24 24
25 25 def copyfile(src, dst, basedir):
26 26 abssrc, absdst = [util.canonpath(basedir, basedir, x) for x in [src, dst]]
27 27 if os.path.exists(absdst):
28 28 raise util.Abort(_("cannot create %s: destination already exists") %
29 29 dst)
30 30
31 31 dstdir = os.path.dirname(absdst)
32 32 if dstdir and not os.path.isdir(dstdir):
33 33 try:
34 34 os.makedirs(dstdir)
35 35 except IOError:
36 36 raise util.Abort(
37 37 _("cannot create %s: unable to create destination directory")
38 38 % dst)
39 39
40 40 util.copyfile(abssrc, absdst)
41 41
42 42 # public functions
43 43
44 44 def extract(ui, fileobj):
45 45 '''extract patch from data read from fileobj.
46 46
47 47 patch can be a normal patch or contained in an email message.
48 48
49 49 return tuple (filename, message, user, date, node, p1, p2).
50 50 Any item in the returned tuple can be None. If filename is None,
51 51 fileobj did not contain a patch. Caller must unlink filename when done.'''
52 52
53 53 # attempt to detect the start of a patch
54 54 # (this heuristic is borrowed from quilt)
55 55 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
56 56 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
57 57 r'(---|\*\*\*)[ \t])', re.MULTILINE)
58 58
59 59 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
60 60 tmpfp = os.fdopen(fd, 'w')
61 61 try:
62 62 msg = email.Parser.Parser().parse(fileobj)
63 63
64 64 subject = msg['Subject']
65 65 user = msg['From']
66 66 gitsendmail = 'git-send-email' in msg.get('X-Mailer', '')
67 67 # should try to parse msg['Date']
68 68 date = None
69 69 nodeid = None
70 70 branch = None
71 71 parents = []
72 72
73 73 if subject:
74 74 if subject.startswith('[PATCH'):
75 75 pend = subject.find(']')
76 76 if pend >= 0:
77 77 subject = subject[pend+1:].lstrip()
78 78 subject = subject.replace('\n\t', ' ')
79 79 ui.debug('Subject: %s\n' % subject)
80 80 if user:
81 81 ui.debug('From: %s\n' % user)
82 82 diffs_seen = 0
83 83 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
84 84 message = ''
85 85 for part in msg.walk():
86 86 content_type = part.get_content_type()
87 87 ui.debug('Content-Type: %s\n' % content_type)
88 88 if content_type not in ok_types:
89 89 continue
90 90 payload = part.get_payload(decode=True)
91 91 m = diffre.search(payload)
92 92 if m:
93 93 hgpatch = False
94 94 ignoretext = False
95 95
96 96 ui.debug(_('found patch at byte %d\n') % m.start(0))
97 97 diffs_seen += 1
98 98 cfp = cStringIO.StringIO()
99 99 for line in payload[:m.start(0)].splitlines():
100 100 if line.startswith('# HG changeset patch'):
101 101 ui.debug(_('patch generated by hg export\n'))
102 102 hgpatch = True
103 103 # drop earlier commit message content
104 104 cfp.seek(0)
105 105 cfp.truncate()
106 106 subject = None
107 107 elif hgpatch:
108 108 if line.startswith('# User '):
109 109 user = line[7:]
110 110 ui.debug('From: %s\n' % user)
111 111 elif line.startswith("# Date "):
112 112 date = line[7:]
113 113 elif line.startswith("# Branch "):
114 114 branch = line[9:]
115 115 elif line.startswith("# Node ID "):
116 116 nodeid = line[10:]
117 117 elif line.startswith("# Parent "):
118 118 parents.append(line[10:])
119 119 elif line == '---' and gitsendmail:
120 120 ignoretext = True
121 121 if not line.startswith('# ') and not ignoretext:
122 122 cfp.write(line)
123 123 cfp.write('\n')
124 124 message = cfp.getvalue()
125 125 if tmpfp:
126 126 tmpfp.write(payload)
127 127 if not payload.endswith('\n'):
128 128 tmpfp.write('\n')
129 129 elif not diffs_seen and message and content_type == 'text/plain':
130 130 message += '\n' + payload
131 131 except:
132 132 tmpfp.close()
133 133 os.unlink(tmpname)
134 134 raise
135 135
136 136 if subject and not message.startswith(subject):
137 137 message = '%s\n%s' % (subject, message)
138 138 tmpfp.close()
139 139 if not diffs_seen:
140 140 os.unlink(tmpname)
141 141 return None, message, user, date, branch, None, None, None
142 142 p1 = parents and parents.pop(0) or None
143 143 p2 = parents and parents.pop(0) or None
144 144 return tmpname, message, user, date, branch, nodeid, p1, p2
145 145
146 146 GP_PATCH = 1 << 0 # we have to run patch
147 147 GP_FILTER = 1 << 1 # there's some copy/rename operation
148 148 GP_BINARY = 1 << 2 # there's a binary patch
149 149
150 150 class patchmeta(object):
151 151 """Patched file metadata
152 152
153 153 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
154 154 or COPY. 'path' is patched file path. 'oldpath' is set to the
155 155 origin file when 'op' is either COPY or RENAME, None otherwise. If
156 156 file mode is changed, 'mode' is a tuple (islink, isexec) where
157 157 'islink' is True if the file is a symlink and 'isexec' is True if
158 158 the file is executable. Otherwise, 'mode' is None.
159 159 """
160 160 def __init__(self, path):
161 161 self.path = path
162 162 self.oldpath = None
163 163 self.mode = None
164 164 self.op = 'MODIFY'
165 165 self.lineno = 0
166 166 self.binary = False
167 167
168 168 def setmode(self, mode):
169 169 islink = mode & 020000
170 170 isexec = mode & 0100
171 171 self.mode = (islink, isexec)
172 172
173 173 def readgitpatch(lr):
174 174 """extract git-style metadata about patches from <patchname>"""
175 175
176 176 # Filter patch for git information
177 177 gp = None
178 178 gitpatches = []
179 179 # Can have a git patch with only metadata, causing patch to complain
180 180 dopatch = 0
181 181
182 182 lineno = 0
183 183 for line in lr:
184 184 lineno += 1
185 185 if line.startswith('diff --git'):
186 186 m = gitre.match(line)
187 187 if m:
188 188 if gp:
189 189 gitpatches.append(gp)
190 190 src, dst = m.group(1, 2)
191 191 gp = patchmeta(dst)
192 192 gp.lineno = lineno
193 193 elif gp:
194 194 if line.startswith('--- '):
195 195 if gp.op in ('COPY', 'RENAME'):
196 196 dopatch |= GP_FILTER
197 197 gitpatches.append(gp)
198 198 gp = None
199 199 dopatch |= GP_PATCH
200 200 continue
201 201 if line.startswith('rename from '):
202 202 gp.op = 'RENAME'
203 203 gp.oldpath = line[12:].rstrip()
204 204 elif line.startswith('rename to '):
205 205 gp.path = line[10:].rstrip()
206 206 elif line.startswith('copy from '):
207 207 gp.op = 'COPY'
208 208 gp.oldpath = line[10:].rstrip()
209 209 elif line.startswith('copy to '):
210 210 gp.path = line[8:].rstrip()
211 211 elif line.startswith('deleted file'):
212 212 gp.op = 'DELETE'
213 213 # is the deleted file a symlink?
214 214 gp.setmode(int(line.rstrip()[-6:], 8))
215 215 elif line.startswith('new file mode '):
216 216 gp.op = 'ADD'
217 217 gp.setmode(int(line.rstrip()[-6:], 8))
218 218 elif line.startswith('new mode '):
219 219 gp.setmode(int(line.rstrip()[-6:], 8))
220 220 elif line.startswith('GIT binary patch'):
221 221 dopatch |= GP_BINARY
222 222 gp.binary = True
223 223 if gp:
224 224 gitpatches.append(gp)
225 225
226 226 if not gitpatches:
227 227 dopatch = GP_PATCH
228 228
229 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 259 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
232 260 unidesc = re.compile('@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@')
233 261 contextdesc = re.compile('(---|\*\*\*) (\d+)(,(\d+))? (---|\*\*\*)')
234 262
235 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 265 self.fname = fname
266 self.eol = eol
238 267 self.opener = opener
239 268 self.ui = ui
240 269 self.lines = []
241 270 self.exists = False
242 271 self.missing = missing
243 272 if not missing:
244 273 try:
245 274 self.lines = self.readlines(fname)
246 275 self.exists = True
247 276 except IOError:
248 277 pass
249 278 else:
250 279 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
251 280
252 281 self.hash = {}
253 282 self.dirty = 0
254 283 self.offset = 0
255 284 self.rej = []
256 285 self.fileprinted = False
257 286 self.printfile(False)
258 287 self.hunks = 0
259 288
260 289 def readlines(self, fname):
261 290 fp = self.opener(fname, 'r')
262 291 try:
263 return fp.readlines()
292 return list(linereader(fp, self.eol is not None))
264 293 finally:
265 294 fp.close()
266 295
267 296 def writelines(self, fname, lines):
268 297 fp = self.opener(fname, 'w')
269 298 try:
270 fp.writelines(lines)
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:
305 fp.writelines(lines)
271 306 finally:
272 307 fp.close()
273 308
274 309 def unlink(self, fname):
275 310 os.unlink(fname)
276 311
277 312 def printfile(self, warn):
278 313 if self.fileprinted:
279 314 return
280 315 if warn or self.ui.verbose:
281 316 self.fileprinted = True
282 317 s = _("patching file %s\n") % self.fname
283 318 if warn:
284 319 self.ui.warn(s)
285 320 else:
286 321 self.ui.note(s)
287 322
288 323
289 324 def findlines(self, l, linenum):
290 325 # looks through the hash and finds candidate lines. The
291 326 # result is a list of line numbers sorted based on distance
292 327 # from linenum
293 328 def sorter(a, b):
294 329 vala = abs(a - linenum)
295 330 valb = abs(b - linenum)
296 331 return cmp(vala, valb)
297 332
298 333 try:
299 334 cand = self.hash[l]
300 335 except:
301 336 return []
302 337
303 338 if len(cand) > 1:
304 339 # resort our list of potentials forward then back.
305 340 cand.sort(sorter)
306 341 return cand
307 342
308 343 def hashlines(self):
309 344 self.hash = {}
310 345 for x, s in enumerate(self.lines):
311 346 self.hash.setdefault(s, []).append(x)
312 347
313 348 def write_rej(self):
314 349 # our rejects are a little different from patch(1). This always
315 350 # creates rejects in the same form as the original patch. A file
316 351 # header is inserted so that you can run the reject through patch again
317 352 # without having to type the filename.
318 353
319 354 if not self.rej:
320 355 return
321 356
322 357 fname = self.fname + ".rej"
323 358 self.ui.warn(
324 359 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
325 360 (len(self.rej), self.hunks, fname))
326 361
327 362 def rejlines():
328 363 base = os.path.basename(self.fname)
329 364 yield "--- %s\n+++ %s\n" % (base, base)
330 365 for x in self.rej:
331 366 for l in x.hunk:
332 367 yield l
333 368 if l[-1] != '\n':
334 369 yield "\n\ No newline at end of file\n"
335 370
336 371 self.writelines(fname, rejlines())
337 372
338 373 def write(self, dest=None):
339 374 if not self.dirty:
340 375 return
341 376 if not dest:
342 377 dest = self.fname
343 378 self.writelines(dest, self.lines)
344 379
345 380 def close(self):
346 381 self.write()
347 382 self.write_rej()
348 383
349 384 def apply(self, h, reverse):
350 385 if not h.complete():
351 386 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
352 387 (h.number, h.desc, len(h.a), h.lena, len(h.b),
353 388 h.lenb))
354 389
355 390 self.hunks += 1
356 391 if reverse:
357 392 h.reverse()
358 393
359 394 if self.missing:
360 395 self.rej.append(h)
361 396 return -1
362 397
363 398 if self.exists and h.createfile():
364 399 self.ui.warn(_("file %s already exists\n") % self.fname)
365 400 self.rej.append(h)
366 401 return -1
367 402
368 403 if isinstance(h, githunk):
369 404 if h.rmfile():
370 405 self.unlink(self.fname)
371 406 else:
372 407 self.lines[:] = h.new()
373 408 self.offset += len(h.new())
374 409 self.dirty = 1
375 410 return 0
376 411
377 412 # fast case first, no offsets, no fuzz
378 413 old = h.old()
379 414 # patch starts counting at 1 unless we are adding the file
380 415 if h.starta == 0:
381 416 start = 0
382 417 else:
383 418 start = h.starta + self.offset - 1
384 419 orig_start = start
385 420 if diffhelpers.testhunk(old, self.lines, start) == 0:
386 421 if h.rmfile():
387 422 self.unlink(self.fname)
388 423 else:
389 424 self.lines[start : start + h.lena] = h.new()
390 425 self.offset += h.lenb - h.lena
391 426 self.dirty = 1
392 427 return 0
393 428
394 429 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
395 430 self.hashlines()
396 431 if h.hunk[-1][0] != ' ':
397 432 # if the hunk tried to put something at the bottom of the file
398 433 # override the start line and use eof here
399 434 search_start = len(self.lines)
400 435 else:
401 436 search_start = orig_start
402 437
403 438 for fuzzlen in xrange(3):
404 439 for toponly in [ True, False ]:
405 440 old = h.old(fuzzlen, toponly)
406 441
407 442 cand = self.findlines(old[0][1:], search_start)
408 443 for l in cand:
409 444 if diffhelpers.testhunk(old, self.lines, l) == 0:
410 445 newlines = h.new(fuzzlen, toponly)
411 446 self.lines[l : l + len(old)] = newlines
412 447 self.offset += len(newlines) - len(old)
413 448 self.dirty = 1
414 449 if fuzzlen:
415 450 fuzzstr = "with fuzz %d " % fuzzlen
416 451 f = self.ui.warn
417 452 self.printfile(True)
418 453 else:
419 454 fuzzstr = ""
420 455 f = self.ui.note
421 456 offset = l - orig_start - fuzzlen
422 457 if offset == 1:
423 458 msg = _("Hunk #%d succeeded at %d %s"
424 459 "(offset %d line).\n")
425 460 else:
426 461 msg = _("Hunk #%d succeeded at %d %s"
427 462 "(offset %d lines).\n")
428 463 f(msg % (h.number, l+1, fuzzstr, offset))
429 464 return fuzzlen
430 465 self.printfile(True)
431 466 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
432 467 self.rej.append(h)
433 468 return -1
434 469
435 470 class hunk(object):
436 471 def __init__(self, desc, num, lr, context, create=False, remove=False):
437 472 self.number = num
438 473 self.desc = desc
439 474 self.hunk = [ desc ]
440 475 self.a = []
441 476 self.b = []
442 477 if context:
443 478 self.read_context_hunk(lr)
444 479 else:
445 480 self.read_unified_hunk(lr)
446 481 self.create = create
447 482 self.remove = remove and not create
448 483
449 484 def read_unified_hunk(self, lr):
450 485 m = unidesc.match(self.desc)
451 486 if not m:
452 487 raise PatchError(_("bad hunk #%d") % self.number)
453 488 self.starta, foo, self.lena, self.startb, foo2, self.lenb = m.groups()
454 489 if self.lena is None:
455 490 self.lena = 1
456 491 else:
457 492 self.lena = int(self.lena)
458 493 if self.lenb is None:
459 494 self.lenb = 1
460 495 else:
461 496 self.lenb = int(self.lenb)
462 497 self.starta = int(self.starta)
463 498 self.startb = int(self.startb)
464 499 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a, self.b)
465 500 # if we hit eof before finishing out the hunk, the last line will
466 501 # be zero length. Lets try to fix it up.
467 502 while len(self.hunk[-1]) == 0:
468 503 del self.hunk[-1]
469 504 del self.a[-1]
470 505 del self.b[-1]
471 506 self.lena -= 1
472 507 self.lenb -= 1
473 508
474 509 def read_context_hunk(self, lr):
475 510 self.desc = lr.readline()
476 511 m = contextdesc.match(self.desc)
477 512 if not m:
478 513 raise PatchError(_("bad hunk #%d") % self.number)
479 514 foo, self.starta, foo2, aend, foo3 = m.groups()
480 515 self.starta = int(self.starta)
481 516 if aend is None:
482 517 aend = self.starta
483 518 self.lena = int(aend) - self.starta
484 519 if self.starta:
485 520 self.lena += 1
486 521 for x in xrange(self.lena):
487 522 l = lr.readline()
488 523 if l.startswith('---'):
489 524 lr.push(l)
490 525 break
491 526 s = l[2:]
492 527 if l.startswith('- ') or l.startswith('! '):
493 528 u = '-' + s
494 529 elif l.startswith(' '):
495 530 u = ' ' + s
496 531 else:
497 532 raise PatchError(_("bad hunk #%d old text line %d") %
498 533 (self.number, x))
499 534 self.a.append(u)
500 535 self.hunk.append(u)
501 536
502 537 l = lr.readline()
503 538 if l.startswith('\ '):
504 539 s = self.a[-1][:-1]
505 540 self.a[-1] = s
506 541 self.hunk[-1] = s
507 542 l = lr.readline()
508 543 m = contextdesc.match(l)
509 544 if not m:
510 545 raise PatchError(_("bad hunk #%d") % self.number)
511 546 foo, self.startb, foo2, bend, foo3 = m.groups()
512 547 self.startb = int(self.startb)
513 548 if bend is None:
514 549 bend = self.startb
515 550 self.lenb = int(bend) - self.startb
516 551 if self.startb:
517 552 self.lenb += 1
518 553 hunki = 1
519 554 for x in xrange(self.lenb):
520 555 l = lr.readline()
521 556 if l.startswith('\ '):
522 557 s = self.b[-1][:-1]
523 558 self.b[-1] = s
524 559 self.hunk[hunki-1] = s
525 560 continue
526 561 if not l:
527 562 lr.push(l)
528 563 break
529 564 s = l[2:]
530 565 if l.startswith('+ ') or l.startswith('! '):
531 566 u = '+' + s
532 567 elif l.startswith(' '):
533 568 u = ' ' + s
534 569 elif len(self.b) == 0:
535 570 # this can happen when the hunk does not add any lines
536 571 lr.push(l)
537 572 break
538 573 else:
539 574 raise PatchError(_("bad hunk #%d old text line %d") %
540 575 (self.number, x))
541 576 self.b.append(s)
542 577 while True:
543 578 if hunki >= len(self.hunk):
544 579 h = ""
545 580 else:
546 581 h = self.hunk[hunki]
547 582 hunki += 1
548 583 if h == u:
549 584 break
550 585 elif h.startswith('-'):
551 586 continue
552 587 else:
553 588 self.hunk.insert(hunki-1, u)
554 589 break
555 590
556 591 if not self.a:
557 592 # this happens when lines were only added to the hunk
558 593 for x in self.hunk:
559 594 if x.startswith('-') or x.startswith(' '):
560 595 self.a.append(x)
561 596 if not self.b:
562 597 # this happens when lines were only deleted from the hunk
563 598 for x in self.hunk:
564 599 if x.startswith('+') or x.startswith(' '):
565 600 self.b.append(x[1:])
566 601 # @@ -start,len +start,len @@
567 602 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
568 603 self.startb, self.lenb)
569 604 self.hunk[0] = self.desc
570 605
571 606 def reverse(self):
572 607 self.create, self.remove = self.remove, self.create
573 608 origlena = self.lena
574 609 origstarta = self.starta
575 610 self.lena = self.lenb
576 611 self.starta = self.startb
577 612 self.lenb = origlena
578 613 self.startb = origstarta
579 614 self.a = []
580 615 self.b = []
581 616 # self.hunk[0] is the @@ description
582 617 for x in xrange(1, len(self.hunk)):
583 618 o = self.hunk[x]
584 619 if o.startswith('-'):
585 620 n = '+' + o[1:]
586 621 self.b.append(o[1:])
587 622 elif o.startswith('+'):
588 623 n = '-' + o[1:]
589 624 self.a.append(n)
590 625 else:
591 626 n = o
592 627 self.b.append(o[1:])
593 628 self.a.append(o)
594 629 self.hunk[x] = o
595 630
596 631 def fix_newline(self):
597 632 diffhelpers.fix_newline(self.hunk, self.a, self.b)
598 633
599 634 def complete(self):
600 635 return len(self.a) == self.lena and len(self.b) == self.lenb
601 636
602 637 def createfile(self):
603 638 return self.starta == 0 and self.lena == 0 and self.create
604 639
605 640 def rmfile(self):
606 641 return self.startb == 0 and self.lenb == 0 and self.remove
607 642
608 643 def fuzzit(self, l, fuzz, toponly):
609 644 # this removes context lines from the top and bottom of list 'l'. It
610 645 # checks the hunk to make sure only context lines are removed, and then
611 646 # returns a new shortened list of lines.
612 647 fuzz = min(fuzz, len(l)-1)
613 648 if fuzz:
614 649 top = 0
615 650 bot = 0
616 651 hlen = len(self.hunk)
617 652 for x in xrange(hlen-1):
618 653 # the hunk starts with the @@ line, so use x+1
619 654 if self.hunk[x+1][0] == ' ':
620 655 top += 1
621 656 else:
622 657 break
623 658 if not toponly:
624 659 for x in xrange(hlen-1):
625 660 if self.hunk[hlen-bot-1][0] == ' ':
626 661 bot += 1
627 662 else:
628 663 break
629 664
630 665 # top and bot now count context in the hunk
631 666 # adjust them if either one is short
632 667 context = max(top, bot, 3)
633 668 if bot < context:
634 669 bot = max(0, fuzz - (context - bot))
635 670 else:
636 671 bot = min(fuzz, bot)
637 672 if top < context:
638 673 top = max(0, fuzz - (context - top))
639 674 else:
640 675 top = min(fuzz, top)
641 676
642 677 return l[top:len(l)-bot]
643 678 return l
644 679
645 680 def old(self, fuzz=0, toponly=False):
646 681 return self.fuzzit(self.a, fuzz, toponly)
647 682
648 683 def newctrl(self):
649 684 res = []
650 685 for x in self.hunk:
651 686 c = x[0]
652 687 if c == ' ' or c == '+':
653 688 res.append(x)
654 689 return res
655 690
656 691 def new(self, fuzz=0, toponly=False):
657 692 return self.fuzzit(self.b, fuzz, toponly)
658 693
659 694 class githunk(object):
660 695 """A git hunk"""
661 696 def __init__(self, gitpatch):
662 697 self.gitpatch = gitpatch
663 698 self.text = None
664 699 self.hunk = []
665 700
666 701 def createfile(self):
667 702 return self.gitpatch.op in ('ADD', 'RENAME', 'COPY')
668 703
669 704 def rmfile(self):
670 705 return self.gitpatch.op == 'DELETE'
671 706
672 707 def complete(self):
673 708 return self.text is not None
674 709
675 710 def new(self):
676 711 return [self.text]
677 712
678 713 class binhunk(githunk):
679 714 'A binary patch file. Only understands literals so far.'
680 715 def __init__(self, gitpatch):
681 716 super(binhunk, self).__init__(gitpatch)
682 717 self.hunk = ['GIT binary patch\n']
683 718
684 719 def extract(self, lr):
685 720 line = lr.readline()
686 721 self.hunk.append(line)
687 722 while line and not line.startswith('literal '):
688 723 line = lr.readline()
689 724 self.hunk.append(line)
690 725 if not line:
691 726 raise PatchError(_('could not extract binary patch'))
692 727 size = int(line[8:].rstrip())
693 728 dec = []
694 729 line = lr.readline()
695 730 self.hunk.append(line)
696 731 while len(line) > 1:
697 732 l = line[0]
698 733 if l <= 'Z' and l >= 'A':
699 734 l = ord(l) - ord('A') + 1
700 735 else:
701 736 l = ord(l) - ord('a') + 27
702 737 dec.append(base85.b85decode(line[1:-1])[:l])
703 738 line = lr.readline()
704 739 self.hunk.append(line)
705 740 text = zlib.decompress(''.join(dec))
706 741 if len(text) != size:
707 742 raise PatchError(_('binary patch is %d bytes, not %d') %
708 743 len(text), size)
709 744 self.text = text
710 745
711 746 class symlinkhunk(githunk):
712 747 """A git symlink hunk"""
713 748 def __init__(self, gitpatch, hunk):
714 749 super(symlinkhunk, self).__init__(gitpatch)
715 750 self.hunk = hunk
716 751
717 752 def complete(self):
718 753 return True
719 754
720 755 def fix_newline(self):
721 756 return
722 757
723 758 def parsefilename(str):
724 759 # --- filename \t|space stuff
725 760 s = str[4:].rstrip('\r\n')
726 761 i = s.find('\t')
727 762 if i < 0:
728 763 i = s.find(' ')
729 764 if i < 0:
730 765 return s
731 766 return s[:i]
732 767
733 768 def selectfile(afile_orig, bfile_orig, hunk, strip, reverse):
734 769 def pathstrip(path, count=1):
735 770 pathlen = len(path)
736 771 i = 0
737 772 if count == 0:
738 773 return '', path.rstrip()
739 774 while count > 0:
740 775 i = path.find('/', i)
741 776 if i == -1:
742 777 raise PatchError(_("unable to strip away %d dirs from %s") %
743 778 (count, path))
744 779 i += 1
745 780 # consume '//' in the path
746 781 while i < pathlen - 1 and path[i] == '/':
747 782 i += 1
748 783 count -= 1
749 784 return path[:i].lstrip(), path[i:].rstrip()
750 785
751 786 nulla = afile_orig == "/dev/null"
752 787 nullb = bfile_orig == "/dev/null"
753 788 abase, afile = pathstrip(afile_orig, strip)
754 789 gooda = not nulla and util.lexists(afile)
755 790 bbase, bfile = pathstrip(bfile_orig, strip)
756 791 if afile == bfile:
757 792 goodb = gooda
758 793 else:
759 794 goodb = not nullb and os.path.exists(bfile)
760 795 createfunc = hunk.createfile
761 796 if reverse:
762 797 createfunc = hunk.rmfile
763 798 missing = not goodb and not gooda and not createfunc()
764 799 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
765 800 # diff is between a file and its backup. In this case, the original
766 801 # file should be patched (see original mpatch code).
767 802 isbackup = (abase == bbase and bfile.startswith(afile))
768 803 fname = None
769 804 if not missing:
770 805 if gooda and goodb:
771 806 fname = isbackup and afile or bfile
772 807 elif gooda:
773 808 fname = afile
774 809
775 810 if not fname:
776 811 if not nullb:
777 812 fname = isbackup and afile or bfile
778 813 elif not nulla:
779 814 fname = afile
780 815 else:
781 816 raise PatchError(_("undefined source and destination files"))
782 817
783 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 820 def scangitpatch(lr, firstline):
808 821 """
809 822 Git patches can emit:
810 823 - rename a to b
811 824 - change b
812 825 - copy a to c
813 826 - change c
814 827
815 828 We cannot apply this sequence as-is, the renamed 'a' could not be
816 829 found for it would have been renamed already. And we cannot copy
817 830 from 'b' instead because 'b' would have been changed already. So
818 831 we scan the git patch for copy and rename commands so we can
819 832 perform the copies ahead of time.
820 833 """
821 834 pos = 0
822 835 try:
823 836 pos = lr.fp.tell()
824 837 fp = lr.fp
825 838 except IOError:
826 839 fp = cStringIO.StringIO(lr.fp.read())
827 gitlr = linereader(fp)
840 gitlr = linereader(fp, lr.textmode)
828 841 gitlr.push(firstline)
829 842 (dopatch, gitpatches) = readgitpatch(gitlr)
830 843 fp.seek(pos)
831 844 return dopatch, gitpatches
832 845
833 def iterhunks(ui, fp, sourcefile=None):
846 def iterhunks(ui, fp, sourcefile=None, textmode=False):
834 847 """Read a patch and yield the following events:
835 848 - ("file", afile, bfile, firsthunk): select a new target file.
836 849 - ("hunk", hunk): a new hunk is ready to be applied, follows a
837 850 "file" event.
838 851 - ("git", gitchanges): current diff is in git format, gitchanges
839 852 maps filenames to gitpatch records. Unique event.
853
854 If textmode is True, input line-endings are normalized to LF.
840 855 """
841 856 changed = {}
842 857 current_hunk = None
843 858 afile = ""
844 859 bfile = ""
845 860 state = None
846 861 hunknum = 0
847 862 emitfile = False
848 863 git = False
849 864
850 865 # our states
851 866 BFILE = 1
852 867 context = None
853 lr = linereader(fp)
868 lr = linereader(fp, textmode)
854 869 dopatch = True
855 870 # gitworkdone is True if a git operation (copy, rename, ...) was
856 871 # performed already for the current file. Useful when the file
857 872 # section may have no hunk.
858 873 gitworkdone = False
859 874
860 875 while True:
861 876 newfile = False
862 877 x = lr.readline()
863 878 if not x:
864 879 break
865 880 if current_hunk:
866 881 if x.startswith('\ '):
867 882 current_hunk.fix_newline()
868 883 yield 'hunk', current_hunk
869 884 current_hunk = None
870 885 gitworkdone = False
871 886 if ((sourcefile or state == BFILE) and ((not context and x[0] == '@') or
872 887 ((context is not False) and x.startswith('***************')))):
873 888 try:
874 889 if context is None and x.startswith('***************'):
875 890 context = True
876 891 gpatch = changed.get(bfile)
877 892 create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
878 893 remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
879 894 current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
880 895 if remove:
881 896 gpatch = changed.get(afile[2:])
882 897 if gpatch and gpatch.mode[0]:
883 898 current_hunk = symlinkhunk(gpatch, current_hunk)
884 899 except PatchError, err:
885 900 ui.debug(err)
886 901 current_hunk = None
887 902 continue
888 903 hunknum += 1
889 904 if emitfile:
890 905 emitfile = False
891 906 yield 'file', (afile, bfile, current_hunk)
892 907 elif state == BFILE and x.startswith('GIT binary patch'):
893 908 current_hunk = binhunk(changed[bfile])
894 909 hunknum += 1
895 910 if emitfile:
896 911 emitfile = False
897 912 yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
898 913 current_hunk.extract(lr)
899 914 elif x.startswith('diff --git'):
900 915 # check for git diff, scanning the whole patch file if needed
901 916 m = gitre.match(x)
902 917 if m:
903 918 afile, bfile = m.group(1, 2)
904 919 if not git:
905 920 git = True
906 921 dopatch, gitpatches = scangitpatch(lr, x)
907 922 yield 'git', gitpatches
908 923 for gp in gitpatches:
909 924 changed[gp.path] = gp
910 925 # else error?
911 926 # copy/rename + modify should modify target, not source
912 927 gp = changed.get(bfile)
913 928 if gp and gp.op in ('COPY', 'DELETE', 'RENAME', 'ADD'):
914 929 afile = bfile
915 930 gitworkdone = True
916 931 newfile = True
917 932 elif x.startswith('---'):
918 933 # check for a unified diff
919 934 l2 = lr.readline()
920 935 if not l2.startswith('+++'):
921 936 lr.push(l2)
922 937 continue
923 938 newfile = True
924 939 context = False
925 940 afile = parsefilename(x)
926 941 bfile = parsefilename(l2)
927 942 elif x.startswith('***'):
928 943 # check for a context diff
929 944 l2 = lr.readline()
930 945 if not l2.startswith('---'):
931 946 lr.push(l2)
932 947 continue
933 948 l3 = lr.readline()
934 949 lr.push(l3)
935 950 if not l3.startswith("***************"):
936 951 lr.push(l2)
937 952 continue
938 953 newfile = True
939 954 context = True
940 955 afile = parsefilename(x)
941 956 bfile = parsefilename(l2)
942 957
943 958 if newfile:
944 959 emitfile = True
945 960 state = BFILE
946 961 hunknum = 0
947 962 if current_hunk:
948 963 if current_hunk.complete():
949 964 yield 'hunk', current_hunk
950 965 else:
951 966 raise PatchError(_("malformed patch %s %s") % (afile,
952 967 current_hunk.desc))
953 968
954 969 if hunknum == 0 and dopatch and not gitworkdone:
955 970 raise NoHunks
956 971
957 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
959 filled in with all of the filenames changed by the patch. Returns 0
960 for a clean patch, -1 if any rejects were found and 1 if there was
961 any fuzz."""
972 def applydiff(ui, fp, changed, strip=1, sourcefile=None, reverse=False,
973 eol=None):
974 """
975 Reads a patch from fp and tries to apply it.
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 985 rejects = 0
964 986 err = 0
965 987 current_file = None
966 988 gitpatches = None
967 989 opener = util.opener(os.getcwd())
990 textmode = eol is not None
968 991
969 992 def closefile():
970 993 if not current_file:
971 994 return 0
972 995 current_file.close()
973 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 999 if state == 'hunk':
977 1000 if not current_file:
978 1001 continue
979 1002 current_hunk = values
980 1003 ret = current_file.apply(current_hunk, reverse)
981 1004 if ret >= 0:
982 1005 changed.setdefault(current_file.fname, None)
983 1006 if ret > 0:
984 1007 err = 1
985 1008 elif state == 'file':
986 1009 rejects += closefile()
987 1010 afile, bfile, first_hunk = values
988 1011 try:
989 1012 if sourcefile:
990 current_file = patchfile(ui, sourcefile, opener)
1013 current_file = patchfile(ui, sourcefile, opener, eol=eol)
991 1014 else:
992 1015 current_file, missing = selectfile(afile, bfile, first_hunk,
993 1016 strip, reverse)
994 current_file = patchfile(ui, current_file, opener, missing)
1017 current_file = patchfile(ui, current_file, opener, missing, eol)
995 1018 except PatchError, err:
996 1019 ui.warn(str(err) + '\n')
997 1020 current_file, current_hunk = None, None
998 1021 rejects += 1
999 1022 continue
1000 1023 elif state == 'git':
1001 1024 gitpatches = values
1002 1025 cwd = os.getcwd()
1003 1026 for gp in gitpatches:
1004 1027 if gp.op in ('COPY', 'RENAME'):
1005 1028 copyfile(gp.oldpath, gp.path, cwd)
1006 1029 changed[gp.path] = gp
1007 1030 else:
1008 1031 raise util.Abort(_('unsupported parser state: %s') % state)
1009 1032
1010 1033 rejects += closefile()
1011 1034
1012 1035 if rejects:
1013 1036 return -1
1014 1037 return err
1015 1038
1016 1039 def diffopts(ui, opts={}, untrusted=False):
1017 1040 def get(key, name=None, getter=ui.configbool):
1018 1041 return (opts.get(key) or
1019 1042 getter('diff', name or key, None, untrusted=untrusted))
1020 1043 return mdiff.diffopts(
1021 1044 text=opts.get('text'),
1022 1045 git=get('git'),
1023 1046 nodates=get('nodates'),
1024 1047 showfunc=get('show_function', 'showfunc'),
1025 1048 ignorews=get('ignore_all_space', 'ignorews'),
1026 1049 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
1027 1050 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'),
1028 1051 context=get('unified', getter=ui.config))
1029 1052
1030 1053 def updatedir(ui, repo, patches, similarity=0):
1031 1054 '''Update dirstate after patch application according to metadata'''
1032 1055 if not patches:
1033 1056 return
1034 1057 copies = []
1035 1058 removes = set()
1036 1059 cfiles = patches.keys()
1037 1060 cwd = repo.getcwd()
1038 1061 if cwd:
1039 1062 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1040 1063 for f in patches:
1041 1064 gp = patches[f]
1042 1065 if not gp:
1043 1066 continue
1044 1067 if gp.op == 'RENAME':
1045 1068 copies.append((gp.oldpath, gp.path))
1046 1069 removes.add(gp.oldpath)
1047 1070 elif gp.op == 'COPY':
1048 1071 copies.append((gp.oldpath, gp.path))
1049 1072 elif gp.op == 'DELETE':
1050 1073 removes.add(gp.path)
1051 1074 for src, dst in copies:
1052 1075 repo.copy(src, dst)
1053 1076 if (not similarity) and removes:
1054 1077 repo.remove(sorted(removes), True)
1055 1078 for f in patches:
1056 1079 gp = patches[f]
1057 1080 if gp and gp.mode:
1058 1081 islink, isexec = gp.mode
1059 1082 dst = repo.wjoin(gp.path)
1060 1083 # patch won't create empty files
1061 1084 if gp.op == 'ADD' and not os.path.exists(dst):
1062 1085 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1063 1086 repo.wwrite(gp.path, '', flags)
1064 1087 elif gp.op != 'DELETE':
1065 1088 util.set_flags(dst, islink, isexec)
1066 1089 cmdutil.addremove(repo, cfiles, similarity=similarity)
1067 1090 files = patches.keys()
1068 1091 files.extend([r for r in removes if r not in files])
1069 1092 return sorted(files)
1070 1093
1071 1094 def externalpatch(patcher, args, patchname, ui, strip, cwd, files):
1072 1095 """use <patcher> to apply <patchname> to the working directory.
1073 1096 returns whether patch was applied with fuzz factor."""
1074 1097
1075 1098 fuzz = False
1076 1099 if cwd:
1077 1100 args.append('-d %s' % util.shellquote(cwd))
1078 1101 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1079 1102 util.shellquote(patchname)))
1080 1103
1081 1104 for line in fp:
1082 1105 line = line.rstrip()
1083 1106 ui.note(line + '\n')
1084 1107 if line.startswith('patching file '):
1085 1108 pf = util.parse_patch_output(line)
1086 1109 printed_file = False
1087 1110 files.setdefault(pf, None)
1088 1111 elif line.find('with fuzz') >= 0:
1089 1112 fuzz = True
1090 1113 if not printed_file:
1091 1114 ui.warn(pf + '\n')
1092 1115 printed_file = True
1093 1116 ui.warn(line + '\n')
1094 1117 elif line.find('saving rejects to file') >= 0:
1095 1118 ui.warn(line + '\n')
1096 1119 elif line.find('FAILED') >= 0:
1097 1120 if not printed_file:
1098 1121 ui.warn(pf + '\n')
1099 1122 printed_file = True
1100 1123 ui.warn(line + '\n')
1101 1124 code = fp.close()
1102 1125 if code:
1103 1126 raise PatchError(_("patch command failed: %s") %
1104 1127 util.explain_exit(code)[0])
1105 1128 return fuzz
1106 1129
1107 def internalpatch(patchobj, ui, strip, cwd, files={}):
1130 def internalpatch(patchobj, ui, strip, cwd, files={}, eolmode='strict'):
1108 1131 """use builtin patch to apply <patchobj> to the working directory.
1109 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 1141 try:
1111 1142 fp = file(patchobj, 'rb')
1112 1143 except TypeError:
1113 1144 fp = patchobj
1114 1145 if cwd:
1115 1146 curdir = os.getcwd()
1116 1147 os.chdir(cwd)
1117 1148 try:
1118 ret = applydiff(ui, fp, files, strip=strip)
1149 ret = applydiff(ui, fp, files, strip=strip, eol=eol)
1119 1150 finally:
1120 1151 if cwd:
1121 1152 os.chdir(curdir)
1122 1153 if ret < 0:
1123 1154 raise PatchError
1124 1155 return ret > 0
1125 1156
1126 def patch(patchname, ui, strip=1, cwd=None, files={}):
1127 """apply <patchname> to the working directory.
1128 returns whether patch was applied with fuzz factor."""
1157 def patch(patchname, ui, strip=1, cwd=None, files={}, eolmode='strict'):
1158 """Apply <patchname> to the working directory.
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 1169 patcher = ui.config('ui', 'patch')
1130 1170 args = []
1131 1171 try:
1132 1172 if patcher:
1133 1173 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1134 1174 files)
1135 1175 else:
1136 1176 try:
1137 return internalpatch(patchname, ui, strip, cwd, files)
1177 return internalpatch(patchname, ui, strip, cwd, files, eolmode)
1138 1178 except NoHunks:
1139 1179 patcher = util.find_exe('gpatch') or util.find_exe('patch') or 'patch'
1140 1180 ui.debug(_('no valid hunks found; trying with %r instead\n') %
1141 1181 patcher)
1142 1182 if util.needbinarypatch():
1143 1183 args.append('--binary')
1144 1184 return externalpatch(patcher, args, patchname, ui, strip, cwd,
1145 1185 files)
1146 1186 except PatchError, err:
1147 1187 s = str(err)
1148 1188 if s:
1149 1189 raise util.Abort(s)
1150 1190 else:
1151 1191 raise util.Abort(_('patch failed to apply'))
1152 1192
1153 1193 def b85diff(to, tn):
1154 1194 '''print base85-encoded binary diff'''
1155 1195 def gitindex(text):
1156 1196 if not text:
1157 1197 return '0' * 40
1158 1198 l = len(text)
1159 1199 s = util.sha1('blob %d\0' % l)
1160 1200 s.update(text)
1161 1201 return s.hexdigest()
1162 1202
1163 1203 def fmtline(line):
1164 1204 l = len(line)
1165 1205 if l <= 26:
1166 1206 l = chr(ord('A') + l - 1)
1167 1207 else:
1168 1208 l = chr(l - 26 + ord('a') - 1)
1169 1209 return '%c%s\n' % (l, base85.b85encode(line, True))
1170 1210
1171 1211 def chunk(text, csize=52):
1172 1212 l = len(text)
1173 1213 i = 0
1174 1214 while i < l:
1175 1215 yield text[i:i+csize]
1176 1216 i += csize
1177 1217
1178 1218 tohash = gitindex(to)
1179 1219 tnhash = gitindex(tn)
1180 1220 if tohash == tnhash:
1181 1221 return ""
1182 1222
1183 1223 # TODO: deltas
1184 1224 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
1185 1225 (tohash, tnhash, len(tn))]
1186 1226 for l in chunk(zlib.compress(tn)):
1187 1227 ret.append(fmtline(l))
1188 1228 ret.append('\n')
1189 1229 return ''.join(ret)
1190 1230
1191 1231 def _addmodehdr(header, omode, nmode):
1192 1232 if omode != nmode:
1193 1233 header.append('old mode %s\n' % omode)
1194 1234 header.append('new mode %s\n' % nmode)
1195 1235
1196 1236 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None):
1197 1237 '''yields diff of changes to files between two nodes, or node and
1198 1238 working directory.
1199 1239
1200 1240 if node1 is None, use first dirstate parent instead.
1201 1241 if node2 is None, compare node1 with working directory.'''
1202 1242
1203 1243 if opts is None:
1204 1244 opts = mdiff.defaultopts
1205 1245
1206 1246 if not node1:
1207 1247 node1 = repo.dirstate.parents()[0]
1208 1248
1209 1249 flcache = {}
1210 1250 def getfilectx(f, ctx):
1211 1251 flctx = ctx.filectx(f, filelog=flcache.get(f))
1212 1252 if f not in flcache:
1213 1253 flcache[f] = flctx._filelog
1214 1254 return flctx
1215 1255
1216 1256 ctx1 = repo[node1]
1217 1257 ctx2 = repo[node2]
1218 1258
1219 1259 if not changes:
1220 1260 changes = repo.status(ctx1, ctx2, match=match)
1221 1261 modified, added, removed = changes[:3]
1222 1262
1223 1263 if not modified and not added and not removed:
1224 1264 return
1225 1265
1226 1266 date1 = util.datestr(ctx1.date())
1227 1267 man1 = ctx1.manifest()
1228 1268
1229 1269 if repo.ui.quiet:
1230 1270 r = None
1231 1271 else:
1232 1272 hexfunc = repo.ui.debugflag and hex or short
1233 1273 r = [hexfunc(node) for node in [node1, node2] if node]
1234 1274
1235 1275 if opts.git:
1236 1276 copy, diverge = copies.copies(repo, ctx1, ctx2, repo[nullid])
1237 1277 copy = copy.copy()
1238 1278 for k, v in copy.items():
1239 1279 copy[v] = k
1240 1280
1241 1281 gone = set()
1242 1282 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
1243 1283
1244 1284 for f in sorted(modified + added + removed):
1245 1285 to = None
1246 1286 tn = None
1247 1287 dodiff = True
1248 1288 header = []
1249 1289 if f in man1:
1250 1290 to = getfilectx(f, ctx1).data()
1251 1291 if f not in removed:
1252 1292 tn = getfilectx(f, ctx2).data()
1253 1293 a, b = f, f
1254 1294 if opts.git:
1255 1295 if f in added:
1256 1296 mode = gitmode[ctx2.flags(f)]
1257 1297 if f in copy:
1258 1298 a = copy[f]
1259 1299 omode = gitmode[man1.flags(a)]
1260 1300 _addmodehdr(header, omode, mode)
1261 1301 if a in removed and a not in gone:
1262 1302 op = 'rename'
1263 1303 gone.add(a)
1264 1304 else:
1265 1305 op = 'copy'
1266 1306 header.append('%s from %s\n' % (op, a))
1267 1307 header.append('%s to %s\n' % (op, f))
1268 1308 to = getfilectx(a, ctx1).data()
1269 1309 else:
1270 1310 header.append('new file mode %s\n' % mode)
1271 1311 if util.binary(tn):
1272 1312 dodiff = 'binary'
1273 1313 elif f in removed:
1274 1314 # have we already reported a copy above?
1275 1315 if f in copy and copy[f] in added and copy[copy[f]] == f:
1276 1316 dodiff = False
1277 1317 else:
1278 1318 header.append('deleted file mode %s\n' %
1279 1319 gitmode[man1.flags(f)])
1280 1320 else:
1281 1321 omode = gitmode[man1.flags(f)]
1282 1322 nmode = gitmode[ctx2.flags(f)]
1283 1323 _addmodehdr(header, omode, nmode)
1284 1324 if util.binary(to) or util.binary(tn):
1285 1325 dodiff = 'binary'
1286 1326 r = None
1287 1327 header.insert(0, mdiff.diffline(r, a, b, opts))
1288 1328 if dodiff:
1289 1329 if dodiff == 'binary':
1290 1330 text = b85diff(to, tn)
1291 1331 else:
1292 1332 text = mdiff.unidiff(to, date1,
1293 1333 # ctx2 date may be dynamic
1294 1334 tn, util.datestr(ctx2.date()),
1295 1335 a, b, r, opts=opts)
1296 1336 if header and (text or len(header) > 1):
1297 1337 yield ''.join(header)
1298 1338 if text:
1299 1339 yield text
1300 1340
1301 1341 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1302 1342 opts=None):
1303 1343 '''export changesets as hg patches.'''
1304 1344
1305 1345 total = len(revs)
1306 1346 revwidth = max([len(str(rev)) for rev in revs])
1307 1347
1308 1348 def single(rev, seqno, fp):
1309 1349 ctx = repo[rev]
1310 1350 node = ctx.node()
1311 1351 parents = [p.node() for p in ctx.parents() if p]
1312 1352 branch = ctx.branch()
1313 1353 if switch_parent:
1314 1354 parents.reverse()
1315 1355 prev = (parents and parents[0]) or nullid
1316 1356
1317 1357 if not fp:
1318 1358 fp = cmdutil.make_file(repo, template, node, total=total,
1319 1359 seqno=seqno, revwidth=revwidth,
1320 1360 mode='ab')
1321 1361 if fp != sys.stdout and hasattr(fp, 'name'):
1322 1362 repo.ui.note("%s\n" % fp.name)
1323 1363
1324 1364 fp.write("# HG changeset patch\n")
1325 1365 fp.write("# User %s\n" % ctx.user())
1326 1366 fp.write("# Date %d %d\n" % ctx.date())
1327 1367 if branch and (branch != 'default'):
1328 1368 fp.write("# Branch %s\n" % branch)
1329 1369 fp.write("# Node ID %s\n" % hex(node))
1330 1370 fp.write("# Parent %s\n" % hex(prev))
1331 1371 if len(parents) > 1:
1332 1372 fp.write("# Parent %s\n" % hex(parents[1]))
1333 1373 fp.write(ctx.description().rstrip())
1334 1374 fp.write("\n\n")
1335 1375
1336 1376 for chunk in diff(repo, prev, node, opts=opts):
1337 1377 fp.write(chunk)
1338 1378
1339 1379 for seqno, rev in enumerate(revs):
1340 1380 single(rev, seqno+1, fp)
1341 1381
1342 1382 def diffstatdata(lines):
1343 1383 filename, adds, removes = None, 0, 0
1344 1384 for line in lines:
1345 1385 if line.startswith('diff'):
1346 1386 if filename:
1347 1387 yield (filename, adds, removes)
1348 1388 # set numbers to 0 anyway when starting new file
1349 1389 adds, removes = 0, 0
1350 1390 if line.startswith('diff --git'):
1351 1391 filename = gitre.search(line).group(1)
1352 1392 else:
1353 1393 # format: "diff -r ... -r ... filename"
1354 1394 filename = line.split(None, 5)[-1]
1355 1395 elif line.startswith('+') and not line.startswith('+++'):
1356 1396 adds += 1
1357 1397 elif line.startswith('-') and not line.startswith('---'):
1358 1398 removes += 1
1359 1399 if filename:
1360 1400 yield (filename, adds, removes)
1361 1401
1362 1402 def diffstat(lines, width=80):
1363 1403 output = []
1364 1404 stats = list(diffstatdata(lines))
1365 1405
1366 1406 maxtotal, maxname = 0, 0
1367 1407 totaladds, totalremoves = 0, 0
1368 1408 for filename, adds, removes in stats:
1369 1409 totaladds += adds
1370 1410 totalremoves += removes
1371 1411 maxname = max(maxname, len(filename))
1372 1412 maxtotal = max(maxtotal, adds+removes)
1373 1413
1374 1414 countwidth = len(str(maxtotal))
1375 1415 graphwidth = width - countwidth - maxname
1376 1416 if graphwidth < 10:
1377 1417 graphwidth = 10
1378 1418
1379 1419 factor = max(int(math.ceil(float(maxtotal) / graphwidth)), 1)
1380 1420
1381 1421 for filename, adds, removes in stats:
1382 1422 # If diffstat runs out of room it doesn't print anything, which
1383 1423 # isn't very useful, so always print at least one + or - if there
1384 1424 # were at least some changes
1385 1425 pluses = '+' * max(adds/factor, int(bool(adds)))
1386 1426 minuses = '-' * max(removes/factor, int(bool(removes)))
1387 1427 output.append(' %-*s | %*.d %s%s\n' % (maxname, filename, countwidth,
1388 1428 adds+removes, pluses, minuses))
1389 1429
1390 1430 if stats:
1391 1431 output.append(' %d files changed, %d insertions(+), %d deletions(-)\n'
1392 1432 % (len(stats), totaladds, totalremoves))
1393 1433
1394 1434 return ''.join(output)
General Comments 0
You need to be logged in to leave comments. Login now