##// END OF EJS Templates
patch: add fuzz config flag (issue4697)
Matt Mackall -
r25631:2748bf78 default
parent child Browse files
Show More
@@ -1,1792 +1,1797
1 1 The Mercurial system uses a set of configuration files to control
2 2 aspects of its behavior.
3 3
4 4 The configuration files use a simple ini-file format. A configuration
5 5 file consists of sections, led by a ``[section]`` header and followed
6 6 by ``name = value`` entries::
7 7
8 8 [ui]
9 9 username = Firstname Lastname <firstname.lastname@example.net>
10 10 verbose = True
11 11
12 12 The above entries will be referred to as ``ui.username`` and
13 13 ``ui.verbose``, respectively. See the Syntax section below.
14 14
15 15 Files
16 16 =====
17 17
18 18 Mercurial reads configuration data from several files, if they exist.
19 19 These files do not exist by default and you will have to create the
20 20 appropriate configuration files yourself: global configuration like
21 21 the username setting is typically put into
22 22 ``%USERPROFILE%\mercurial.ini`` or ``$HOME/.hgrc`` and local
23 23 configuration is put into the per-repository ``<repo>/.hg/hgrc`` file.
24 24
25 25 The names of these files depend on the system on which Mercurial is
26 26 installed. ``*.rc`` files from a single directory are read in
27 27 alphabetical order, later ones overriding earlier ones. Where multiple
28 28 paths are given below, settings from earlier paths override later
29 29 ones.
30 30
31 31 .. container:: verbose.unix
32 32
33 33 On Unix, the following files are consulted:
34 34
35 35 - ``<repo>/.hg/hgrc`` (per-repository)
36 36 - ``$HOME/.hgrc`` (per-user)
37 37 - ``<install-root>/etc/mercurial/hgrc`` (per-installation)
38 38 - ``<install-root>/etc/mercurial/hgrc.d/*.rc`` (per-installation)
39 39 - ``/etc/mercurial/hgrc`` (per-system)
40 40 - ``/etc/mercurial/hgrc.d/*.rc`` (per-system)
41 41 - ``<internal>/default.d/*.rc`` (defaults)
42 42
43 43 .. container:: verbose.windows
44 44
45 45 On Windows, the following files are consulted:
46 46
47 47 - ``<repo>/.hg/hgrc`` (per-repository)
48 48 - ``%USERPROFILE%\.hgrc`` (per-user)
49 49 - ``%USERPROFILE%\Mercurial.ini`` (per-user)
50 50 - ``%HOME%\.hgrc`` (per-user)
51 51 - ``%HOME%\Mercurial.ini`` (per-user)
52 52 - ``<install-dir>\Mercurial.ini`` (per-installation)
53 53 - ``<install-dir>\hgrc.d\*.rc`` (per-installation)
54 54 - ``HKEY_LOCAL_MACHINE\SOFTWARE\Mercurial`` (per-installation)
55 55 - ``<internal>/default.d/*.rc`` (defaults)
56 56
57 57 .. note::
58 58
59 59 The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
60 60 is used when running 32-bit Python on 64-bit Windows.
61 61
62 62 .. container:: verbose.plan9
63 63
64 64 On Plan9, the following files are consulted:
65 65
66 66 - ``<repo>/.hg/hgrc`` (per-repository)
67 67 - ``$home/lib/hgrc`` (per-user)
68 68 - ``<install-root>/lib/mercurial/hgrc`` (per-installation)
69 69 - ``<install-root>/lib/mercurial/hgrc.d/*.rc`` (per-installation)
70 70 - ``/lib/mercurial/hgrc`` (per-system)
71 71 - ``/lib/mercurial/hgrc.d/*.rc`` (per-system)
72 72 - ``<internal>/default.d/*.rc`` (defaults)
73 73
74 74 Per-repository configuration options only apply in a
75 75 particular repository. This file is not version-controlled, and
76 76 will not get transferred during a "clone" operation. Options in
77 77 this file override options in all other configuration files. On
78 78 Plan 9 and Unix, most of this file will be ignored if it doesn't
79 79 belong to a trusted user or to a trusted group. See the documentation
80 80 for the ``[trusted]`` section below for more details.
81 81
82 82 Per-user configuration file(s) are for the user running Mercurial. On
83 83 Windows 9x, ``%HOME%`` is replaced by ``%APPDATA%``. Options in these
84 84 files apply to all Mercurial commands executed by this user in any
85 85 directory. Options in these files override per-system and per-installation
86 86 options.
87 87
88 88 Per-installation configuration files are searched for in the
89 89 directory where Mercurial is installed. ``<install-root>`` is the
90 90 parent directory of the **hg** executable (or symlink) being run. For
91 91 example, if installed in ``/shared/tools/bin/hg``, Mercurial will look
92 92 in ``/shared/tools/etc/mercurial/hgrc``. Options in these files apply
93 93 to all Mercurial commands executed by any user in any directory.
94 94
95 95 Per-installation configuration files are for the system on
96 96 which Mercurial is running. Options in these files apply to all
97 97 Mercurial commands executed by any user in any directory. Registry
98 98 keys contain PATH-like strings, every part of which must reference
99 99 a ``Mercurial.ini`` file or be a directory where ``*.rc`` files will
100 100 be read. Mercurial checks each of these locations in the specified
101 101 order until one or more configuration files are detected.
102 102
103 103 Per-system configuration files are for the system on which Mercurial
104 104 is running. Options in these files apply to all Mercurial commands
105 105 executed by any user in any directory. Options in these files
106 106 override per-installation options.
107 107
108 108 Mercurial comes with some default configuration. The default configuration
109 109 files are installed with Mercurial and will be overwritten on upgrades. Default
110 110 configuration files should never be edited by users or administrators but can
111 111 be overridden in other configuration files. So far the directory only contains
112 112 merge tool configuration but packagers can also put other default configuration
113 113 there.
114 114
115 115 Syntax
116 116 ======
117 117
118 118 A configuration file consists of sections, led by a ``[section]`` header
119 119 and followed by ``name = value`` entries (sometimes called
120 120 ``configuration keys``)::
121 121
122 122 [spam]
123 123 eggs=ham
124 124 green=
125 125 eggs
126 126
127 127 Each line contains one entry. If the lines that follow are indented,
128 128 they are treated as continuations of that entry. Leading whitespace is
129 129 removed from values. Empty lines are skipped. Lines beginning with
130 130 ``#`` or ``;`` are ignored and may be used to provide comments.
131 131
132 132 Configuration keys can be set multiple times, in which case Mercurial
133 133 will use the value that was configured last. As an example::
134 134
135 135 [spam]
136 136 eggs=large
137 137 ham=serrano
138 138 eggs=small
139 139
140 140 This would set the configuration key named ``eggs`` to ``small``.
141 141
142 142 It is also possible to define a section multiple times. A section can
143 143 be redefined on the same and/or on different configuration files. For
144 144 example::
145 145
146 146 [foo]
147 147 eggs=large
148 148 ham=serrano
149 149 eggs=small
150 150
151 151 [bar]
152 152 eggs=ham
153 153 green=
154 154 eggs
155 155
156 156 [foo]
157 157 ham=prosciutto
158 158 eggs=medium
159 159 bread=toasted
160 160
161 161 This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
162 162 of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
163 163 respectively. As you can see there only thing that matters is the last
164 164 value that was set for each of the configuration keys.
165 165
166 166 If a configuration key is set multiple times in different
167 167 configuration files the final value will depend on the order in which
168 168 the different configuration files are read, with settings from earlier
169 169 paths overriding later ones as described on the ``Files`` section
170 170 above.
171 171
172 172 A line of the form ``%include file`` will include ``file`` into the
173 173 current configuration file. The inclusion is recursive, which means
174 174 that included files can include other files. Filenames are relative to
175 175 the configuration file in which the ``%include`` directive is found.
176 176 Environment variables and ``~user`` constructs are expanded in
177 177 ``file``. This lets you do something like::
178 178
179 179 %include ~/.hgrc.d/$HOST.rc
180 180
181 181 to include a different configuration file on each computer you use.
182 182
183 183 A line with ``%unset name`` will remove ``name`` from the current
184 184 section, if it has been set previously.
185 185
186 186 The values are either free-form text strings, lists of text strings,
187 187 or Boolean values. Boolean values can be set to true using any of "1",
188 188 "yes", "true", or "on" and to false using "0", "no", "false", or "off"
189 189 (all case insensitive).
190 190
191 191 List values are separated by whitespace or comma, except when values are
192 192 placed in double quotation marks::
193 193
194 194 allow_read = "John Doe, PhD", brian, betty
195 195
196 196 Quotation marks can be escaped by prefixing them with a backslash. Only
197 197 quotation marks at the beginning of a word is counted as a quotation
198 198 (e.g., ``foo"bar baz`` is the list of ``foo"bar`` and ``baz``).
199 199
200 200 Sections
201 201 ========
202 202
203 203 This section describes the different sections that may appear in a
204 204 Mercurial configuration file, the purpose of each section, its possible
205 205 keys, and their possible values.
206 206
207 207 ``alias``
208 208 ---------
209 209
210 210 Defines command aliases.
211 211 Aliases allow you to define your own commands in terms of other
212 212 commands (or aliases), optionally including arguments. Positional
213 213 arguments in the form of ``$1``, ``$2``, etc in the alias definition
214 214 are expanded by Mercurial before execution. Positional arguments not
215 215 already used by ``$N`` in the definition are put at the end of the
216 216 command to be executed.
217 217
218 218 Alias definitions consist of lines of the form::
219 219
220 220 <alias> = <command> [<argument>]...
221 221
222 222 For example, this definition::
223 223
224 224 latest = log --limit 5
225 225
226 226 creates a new command ``latest`` that shows only the five most recent
227 227 changesets. You can define subsequent aliases using earlier ones::
228 228
229 229 stable5 = latest -b stable
230 230
231 231 .. note::
232 232
233 233 It is possible to create aliases with the same names as
234 234 existing commands, which will then override the original
235 235 definitions. This is almost always a bad idea!
236 236
237 237 An alias can start with an exclamation point (``!``) to make it a
238 238 shell alias. A shell alias is executed with the shell and will let you
239 239 run arbitrary commands. As an example, ::
240 240
241 241 echo = !echo $@
242 242
243 243 will let you do ``hg echo foo`` to have ``foo`` printed in your
244 244 terminal. A better example might be::
245 245
246 246 purge = !$HG status --no-status --unknown -0 | xargs -0 rm
247 247
248 248 which will make ``hg purge`` delete all unknown files in the
249 249 repository in the same manner as the purge extension.
250 250
251 251 Positional arguments like ``$1``, ``$2``, etc. in the alias definition
252 252 expand to the command arguments. Unmatched arguments are
253 253 removed. ``$0`` expands to the alias name and ``$@`` expands to all
254 254 arguments separated by a space. ``"$@"`` (with quotes) expands to all
255 255 arguments quoted individually and separated by a space. These expansions
256 256 happen before the command is passed to the shell.
257 257
258 258 Shell aliases are executed in an environment where ``$HG`` expands to
259 259 the path of the Mercurial that was used to execute the alias. This is
260 260 useful when you want to call further Mercurial commands in a shell
261 261 alias, as was done above for the purge alias. In addition,
262 262 ``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
263 263 echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
264 264
265 265 .. note::
266 266
267 267 Some global configuration options such as ``-R`` are
268 268 processed before shell aliases and will thus not be passed to
269 269 aliases.
270 270
271 271
272 272 ``annotate``
273 273 ------------
274 274
275 275 Settings used when displaying file annotations. All values are
276 276 Booleans and default to False. See ``diff`` section for related
277 277 options for the diff command.
278 278
279 279 ``ignorews``
280 280 Ignore white space when comparing lines.
281 281
282 282 ``ignorewsamount``
283 283 Ignore changes in the amount of white space.
284 284
285 285 ``ignoreblanklines``
286 286 Ignore changes whose lines are all blank.
287 287
288 288
289 289 ``auth``
290 290 --------
291 291
292 292 Authentication credentials for HTTP authentication. This section
293 293 allows you to store usernames and passwords for use when logging
294 294 *into* HTTP servers. See the ``[web]`` configuration section if
295 295 you want to configure *who* can login to your HTTP server.
296 296
297 297 Each line has the following format::
298 298
299 299 <name>.<argument> = <value>
300 300
301 301 where ``<name>`` is used to group arguments into authentication
302 302 entries. Example::
303 303
304 304 foo.prefix = hg.intevation.org/mercurial
305 305 foo.username = foo
306 306 foo.password = bar
307 307 foo.schemes = http https
308 308
309 309 bar.prefix = secure.example.org
310 310 bar.key = path/to/file.key
311 311 bar.cert = path/to/file.cert
312 312 bar.schemes = https
313 313
314 314 Supported arguments:
315 315
316 316 ``prefix``
317 317 Either ``*`` or a URI prefix with or without the scheme part.
318 318 The authentication entry with the longest matching prefix is used
319 319 (where ``*`` matches everything and counts as a match of length
320 320 1). If the prefix doesn't include a scheme, the match is performed
321 321 against the URI with its scheme stripped as well, and the schemes
322 322 argument, q.v., is then subsequently consulted.
323 323
324 324 ``username``
325 325 Optional. Username to authenticate with. If not given, and the
326 326 remote site requires basic or digest authentication, the user will
327 327 be prompted for it. Environment variables are expanded in the
328 328 username letting you do ``foo.username = $USER``. If the URI
329 329 includes a username, only ``[auth]`` entries with a matching
330 330 username or without a username will be considered.
331 331
332 332 ``password``
333 333 Optional. Password to authenticate with. If not given, and the
334 334 remote site requires basic or digest authentication, the user
335 335 will be prompted for it.
336 336
337 337 ``key``
338 338 Optional. PEM encoded client certificate key file. Environment
339 339 variables are expanded in the filename.
340 340
341 341 ``cert``
342 342 Optional. PEM encoded client certificate chain file. Environment
343 343 variables are expanded in the filename.
344 344
345 345 ``schemes``
346 346 Optional. Space separated list of URI schemes to use this
347 347 authentication entry with. Only used if the prefix doesn't include
348 348 a scheme. Supported schemes are http and https. They will match
349 349 static-http and static-https respectively, as well.
350 350 Default: https.
351 351
352 352 If no suitable authentication entry is found, the user is prompted
353 353 for credentials as usual if required by the remote.
354 354
355 355
356 356 ``committemplate``
357 357 ------------------
358 358
359 359 ``changeset`` configuration in this section is used as the template to
360 360 customize the text shown in the editor when committing.
361 361
362 362 In addition to pre-defined template keywords, commit log specific one
363 363 below can be used for customization:
364 364
365 365 ``extramsg``
366 366 String: Extra message (typically 'Leave message empty to abort
367 367 commit.'). This may be changed by some commands or extensions.
368 368
369 369 For example, the template configuration below shows as same text as
370 370 one shown by default::
371 371
372 372 [committemplate]
373 373 changeset = {desc}\n\n
374 374 HG: Enter commit message. Lines beginning with 'HG:' are removed.
375 375 HG: {extramsg}
376 376 HG: --
377 377 HG: user: {author}\n{ifeq(p2rev, "-1", "",
378 378 "HG: branch merge\n")
379 379 }HG: branch '{branch}'\n{if(activebookmark,
380 380 "HG: bookmark '{activebookmark}'\n") }{subrepos %
381 381 "HG: subrepo {subrepo}\n" }{file_adds %
382 382 "HG: added {file}\n" }{file_mods %
383 383 "HG: changed {file}\n" }{file_dels %
384 384 "HG: removed {file}\n" }{if(files, "",
385 385 "HG: no files changed\n")}
386 386
387 387 .. note::
388 388
389 389 For some problematic encodings (see :hg:`help win32mbcs` for
390 390 detail), this customization should be configured carefully, to
391 391 avoid showing broken characters.
392 392
393 393 For example, if multibyte character ending with backslash (0x5c) is
394 394 followed by ASCII character 'n' in the customized template,
395 395 sequence of backslash and 'n' is treated as line-feed unexpectedly
396 396 (and multibyte character is broken, too).
397 397
398 398 Customized template is used for commands below (``--edit`` may be
399 399 required):
400 400
401 401 - :hg:`backout`
402 402 - :hg:`commit`
403 403 - :hg:`fetch` (for merge commit only)
404 404 - :hg:`graft`
405 405 - :hg:`histedit`
406 406 - :hg:`import`
407 407 - :hg:`qfold`, :hg:`qnew` and :hg:`qrefresh`
408 408 - :hg:`rebase`
409 409 - :hg:`shelve`
410 410 - :hg:`sign`
411 411 - :hg:`tag`
412 412 - :hg:`transplant`
413 413
414 414 Configuring items below instead of ``changeset`` allows showing
415 415 customized message only for specific actions, or showing different
416 416 messages for each action.
417 417
418 418 - ``changeset.backout`` for :hg:`backout`
419 419 - ``changeset.commit.amend.merge`` for :hg:`commit --amend` on merges
420 420 - ``changeset.commit.amend.normal`` for :hg:`commit --amend` on other
421 421 - ``changeset.commit.normal.merge`` for :hg:`commit` on merges
422 422 - ``changeset.commit.normal.normal`` for :hg:`commit` on other
423 423 - ``changeset.fetch`` for :hg:`fetch` (impling merge commit)
424 424 - ``changeset.gpg.sign`` for :hg:`sign`
425 425 - ``changeset.graft`` for :hg:`graft`
426 426 - ``changeset.histedit.edit`` for ``edit`` of :hg:`histedit`
427 427 - ``changeset.histedit.fold`` for ``fold`` of :hg:`histedit`
428 428 - ``changeset.histedit.mess`` for ``mess`` of :hg:`histedit`
429 429 - ``changeset.histedit.pick`` for ``pick`` of :hg:`histedit`
430 430 - ``changeset.import.bypass`` for :hg:`import --bypass`
431 431 - ``changeset.import.normal.merge`` for :hg:`import` on merges
432 432 - ``changeset.import.normal.normal`` for :hg:`import` on other
433 433 - ``changeset.mq.qnew`` for :hg:`qnew`
434 434 - ``changeset.mq.qfold`` for :hg:`qfold`
435 435 - ``changeset.mq.qrefresh`` for :hg:`qrefresh`
436 436 - ``changeset.rebase.collapse`` for :hg:`rebase --collapse`
437 437 - ``changeset.rebase.merge`` for :hg:`rebase` on merges
438 438 - ``changeset.rebase.normal`` for :hg:`rebase` on other
439 439 - ``changeset.shelve.shelve`` for :hg:`shelve`
440 440 - ``changeset.tag.add`` for :hg:`tag` without ``--remove``
441 441 - ``changeset.tag.remove`` for :hg:`tag --remove`
442 442 - ``changeset.transplant.merge`` for :hg:`transplant` on merges
443 443 - ``changeset.transplant.normal`` for :hg:`transplant` on other
444 444
445 445 These dot-separated lists of names are treated as hierarchical ones.
446 446 For example, ``changeset.tag.remove`` customizes the commit message
447 447 only for :hg:`tag --remove`, but ``changeset.tag`` customizes the
448 448 commit message for :hg:`tag` regardless of ``--remove`` option.
449 449
450 450 At the external editor invocation for committing, corresponding
451 451 dot-separated list of names without ``changeset.`` prefix
452 452 (e.g. ``commit.normal.normal``) is in ``HGEDITFORM`` environment variable.
453 453
454 454 In this section, items other than ``changeset`` can be referred from
455 455 others. For example, the configuration to list committed files up
456 456 below can be referred as ``{listupfiles}``::
457 457
458 458 [committemplate]
459 459 listupfiles = {file_adds %
460 460 "HG: added {file}\n" }{file_mods %
461 461 "HG: changed {file}\n" }{file_dels %
462 462 "HG: removed {file}\n" }{if(files, "",
463 463 "HG: no files changed\n")}
464 464
465 465 ``decode/encode``
466 466 -----------------
467 467
468 468 Filters for transforming files on checkout/checkin. This would
469 469 typically be used for newline processing or other
470 470 localization/canonicalization of files.
471 471
472 472 Filters consist of a filter pattern followed by a filter command.
473 473 Filter patterns are globs by default, rooted at the repository root.
474 474 For example, to match any file ending in ``.txt`` in the root
475 475 directory only, use the pattern ``*.txt``. To match any file ending
476 476 in ``.c`` anywhere in the repository, use the pattern ``**.c``.
477 477 For each file only the first matching filter applies.
478 478
479 479 The filter command can start with a specifier, either ``pipe:`` or
480 480 ``tempfile:``. If no specifier is given, ``pipe:`` is used by default.
481 481
482 482 A ``pipe:`` command must accept data on stdin and return the transformed
483 483 data on stdout.
484 484
485 485 Pipe example::
486 486
487 487 [encode]
488 488 # uncompress gzip files on checkin to improve delta compression
489 489 # note: not necessarily a good idea, just an example
490 490 *.gz = pipe: gunzip
491 491
492 492 [decode]
493 493 # recompress gzip files when writing them to the working dir (we
494 494 # can safely omit "pipe:", because it's the default)
495 495 *.gz = gzip
496 496
497 497 A ``tempfile:`` command is a template. The string ``INFILE`` is replaced
498 498 with the name of a temporary file that contains the data to be
499 499 filtered by the command. The string ``OUTFILE`` is replaced with the name
500 500 of an empty temporary file, where the filtered data must be written by
501 501 the command.
502 502
503 503 .. note::
504 504
505 505 The tempfile mechanism is recommended for Windows systems,
506 506 where the standard shell I/O redirection operators often have
507 507 strange effects and may corrupt the contents of your files.
508 508
509 509 This filter mechanism is used internally by the ``eol`` extension to
510 510 translate line ending characters between Windows (CRLF) and Unix (LF)
511 511 format. We suggest you use the ``eol`` extension for convenience.
512 512
513 513
514 514 ``defaults``
515 515 ------------
516 516
517 517 (defaults are deprecated. Don't use them. Use aliases instead)
518 518
519 519 Use the ``[defaults]`` section to define command defaults, i.e. the
520 520 default options/arguments to pass to the specified commands.
521 521
522 522 The following example makes :hg:`log` run in verbose mode, and
523 523 :hg:`status` show only the modified files, by default::
524 524
525 525 [defaults]
526 526 log = -v
527 527 status = -m
528 528
529 529 The actual commands, instead of their aliases, must be used when
530 530 defining command defaults. The command defaults will also be applied
531 531 to the aliases of the commands defined.
532 532
533 533
534 534 ``diff``
535 535 --------
536 536
537 537 Settings used when displaying diffs. Everything except for ``unified``
538 538 is a Boolean and defaults to False. See ``annotate`` section for
539 539 related options for the annotate command.
540 540
541 541 ``git``
542 542 Use git extended diff format.
543 543
544 544 ``nobinary``
545 545 Omit git binary patches.
546 546
547 547 ``nodates``
548 548 Don't include dates in diff headers.
549 549
550 550 ``noprefix``
551 551 Omit 'a/' and 'b/' prefixes from filenames. Ignored in plain mode.
552 552
553 553 ``showfunc``
554 554 Show which function each change is in.
555 555
556 556 ``ignorews``
557 557 Ignore white space when comparing lines.
558 558
559 559 ``ignorewsamount``
560 560 Ignore changes in the amount of white space.
561 561
562 562 ``ignoreblanklines``
563 563 Ignore changes whose lines are all blank.
564 564
565 565 ``unified``
566 566 Number of lines of context to show.
567 567
568 568 ``email``
569 569 ---------
570 570
571 571 Settings for extensions that send email messages.
572 572
573 573 ``from``
574 574 Optional. Email address to use in "From" header and SMTP envelope
575 575 of outgoing messages.
576 576
577 577 ``to``
578 578 Optional. Comma-separated list of recipients' email addresses.
579 579
580 580 ``cc``
581 581 Optional. Comma-separated list of carbon copy recipients'
582 582 email addresses.
583 583
584 584 ``bcc``
585 585 Optional. Comma-separated list of blind carbon copy recipients'
586 586 email addresses.
587 587
588 588 ``method``
589 589 Optional. Method to use to send email messages. If value is ``smtp``
590 590 (default), use SMTP (see the ``[smtp]`` section for configuration).
591 591 Otherwise, use as name of program to run that acts like sendmail
592 592 (takes ``-f`` option for sender, list of recipients on command line,
593 593 message on stdin). Normally, setting this to ``sendmail`` or
594 594 ``/usr/sbin/sendmail`` is enough to use sendmail to send messages.
595 595
596 596 ``charsets``
597 597 Optional. Comma-separated list of character sets considered
598 598 convenient for recipients. Addresses, headers, and parts not
599 599 containing patches of outgoing messages will be encoded in the
600 600 first character set to which conversion from local encoding
601 601 (``$HGENCODING``, ``ui.fallbackencoding``) succeeds. If correct
602 602 conversion fails, the text in question is sent as is. Defaults to
603 603 empty (explicit) list.
604 604
605 605 Order of outgoing email character sets:
606 606
607 607 1. ``us-ascii``: always first, regardless of settings
608 608 2. ``email.charsets``: in order given by user
609 609 3. ``ui.fallbackencoding``: if not in email.charsets
610 610 4. ``$HGENCODING``: if not in email.charsets
611 611 5. ``utf-8``: always last, regardless of settings
612 612
613 613 Email example::
614 614
615 615 [email]
616 616 from = Joseph User <joe.user@example.com>
617 617 method = /usr/sbin/sendmail
618 618 # charsets for western Europeans
619 619 # us-ascii, utf-8 omitted, as they are tried first and last
620 620 charsets = iso-8859-1, iso-8859-15, windows-1252
621 621
622 622
623 623 ``extensions``
624 624 --------------
625 625
626 626 Mercurial has an extension mechanism for adding new features. To
627 627 enable an extension, create an entry for it in this section.
628 628
629 629 If you know that the extension is already in Python's search path,
630 630 you can give the name of the module, followed by ``=``, with nothing
631 631 after the ``=``.
632 632
633 633 Otherwise, give a name that you choose, followed by ``=``, followed by
634 634 the path to the ``.py`` file (including the file name extension) that
635 635 defines the extension.
636 636
637 637 To explicitly disable an extension that is enabled in an hgrc of
638 638 broader scope, prepend its path with ``!``, as in ``foo = !/ext/path``
639 639 or ``foo = !`` when path is not supplied.
640 640
641 641 Example for ``~/.hgrc``::
642 642
643 643 [extensions]
644 644 # (the color extension will get loaded from Mercurial's path)
645 645 color =
646 646 # (this extension will get loaded from the file specified)
647 647 myfeature = ~/.hgext/myfeature.py
648 648
649 649
650 650 ``format``
651 651 ----------
652 652
653 653 ``usestore``
654 654 Enable or disable the "store" repository format which improves
655 655 compatibility with systems that fold case or otherwise mangle
656 656 filenames. Enabled by default. Disabling this option will allow
657 657 you to store longer filenames in some situations at the expense of
658 658 compatibility and ensures that the on-disk format of newly created
659 659 repositories will be compatible with Mercurial before version 0.9.4.
660 660
661 661 ``usefncache``
662 662 Enable or disable the "fncache" repository format which enhances
663 663 the "store" repository format (which has to be enabled to use
664 664 fncache) to allow longer filenames and avoids using Windows
665 665 reserved names, e.g. "nul". Enabled by default. Disabling this
666 666 option ensures that the on-disk format of newly created
667 667 repositories will be compatible with Mercurial before version 1.1.
668 668
669 669 ``dotencode``
670 670 Enable or disable the "dotencode" repository format which enhances
671 671 the "fncache" repository format (which has to be enabled to use
672 672 dotencode) to avoid issues with filenames starting with ._ on
673 673 Mac OS X and spaces on Windows. Enabled by default. Disabling this
674 674 option ensures that the on-disk format of newly created
675 675 repositories will be compatible with Mercurial before version 1.7.
676 676
677 677 ``graph``
678 678 ---------
679 679
680 680 Web graph view configuration. This section let you change graph
681 681 elements display properties by branches, for instance to make the
682 682 ``default`` branch stand out.
683 683
684 684 Each line has the following format::
685 685
686 686 <branch>.<argument> = <value>
687 687
688 688 where ``<branch>`` is the name of the branch being
689 689 customized. Example::
690 690
691 691 [graph]
692 692 # 2px width
693 693 default.width = 2
694 694 # red color
695 695 default.color = FF0000
696 696
697 697 Supported arguments:
698 698
699 699 ``width``
700 700 Set branch edges width in pixels.
701 701
702 702 ``color``
703 703 Set branch edges color in hexadecimal RGB notation.
704 704
705 705 ``hooks``
706 706 ---------
707 707
708 708 Commands or Python functions that get automatically executed by
709 709 various actions such as starting or finishing a commit. Multiple
710 710 hooks can be run for the same action by appending a suffix to the
711 711 action. Overriding a site-wide hook can be done by changing its
712 712 value or setting it to an empty string. Hooks can be prioritized
713 713 by adding a prefix of ``priority`` to the hook name on a new line
714 714 and setting the priority. The default priority is 0 if
715 715 not specified.
716 716
717 717 Example ``.hg/hgrc``::
718 718
719 719 [hooks]
720 720 # update working directory after adding changesets
721 721 changegroup.update = hg update
722 722 # do not use the site-wide hook
723 723 incoming =
724 724 incoming.email = /my/email/hook
725 725 incoming.autobuild = /my/build/hook
726 726 # force autobuild hook to run before other incoming hooks
727 727 priority.incoming.autobuild = 1
728 728
729 729 Most hooks are run with environment variables set that give useful
730 730 additional information. For each hook below, the environment
731 731 variables it is passed are listed with names of the form ``$HG_foo``.
732 732
733 733 ``changegroup``
734 734 Run after a changegroup has been added via push, pull or unbundle.
735 735 ID of the first new changeset is in ``$HG_NODE``. URL from which
736 736 changes came is in ``$HG_URL``.
737 737
738 738 ``commit``
739 739 Run after a changeset has been created in the local repository. ID
740 740 of the newly created changeset is in ``$HG_NODE``. Parent changeset
741 741 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
742 742
743 743 ``incoming``
744 744 Run after a changeset has been pulled, pushed, or unbundled into
745 745 the local repository. The ID of the newly arrived changeset is in
746 746 ``$HG_NODE``. URL that was source of changes came is in ``$HG_URL``.
747 747
748 748 ``outgoing``
749 749 Run after sending changes from local repository to another. ID of
750 750 first changeset sent is in ``$HG_NODE``. Source of operation is in
751 751 ``$HG_SOURCE``; see "preoutgoing" hook for description.
752 752
753 753 ``post-<command>``
754 754 Run after successful invocations of the associated command. The
755 755 contents of the command line are passed as ``$HG_ARGS`` and the result
756 756 code in ``$HG_RESULT``. Parsed command line arguments are passed as
757 757 ``$HG_PATS`` and ``$HG_OPTS``. These contain string representations of
758 758 the python data internally passed to <command>. ``$HG_OPTS`` is a
759 759 dictionary of options (with unspecified options set to their defaults).
760 760 ``$HG_PATS`` is a list of arguments. Hook failure is ignored.
761 761
762 762 ``pre-<command>``
763 763 Run before executing the associated command. The contents of the
764 764 command line are passed as ``$HG_ARGS``. Parsed command line arguments
765 765 are passed as ``$HG_PATS`` and ``$HG_OPTS``. These contain string
766 766 representations of the data internally passed to <command>. ``$HG_OPTS``
767 767 is a dictionary of options (with unspecified options set to their
768 768 defaults). ``$HG_PATS`` is a list of arguments. If the hook returns
769 769 failure, the command doesn't execute and Mercurial returns the failure
770 770 code.
771 771
772 772 ``prechangegroup``
773 773 Run before a changegroup is added via push, pull or unbundle. Exit
774 774 status 0 allows the changegroup to proceed. Non-zero status will
775 775 cause the push, pull or unbundle to fail. URL from which changes
776 776 will come is in ``$HG_URL``.
777 777
778 778 ``precommit``
779 779 Run before starting a local commit. Exit status 0 allows the
780 780 commit to proceed. Non-zero status will cause the commit to fail.
781 781 Parent changeset IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
782 782
783 783 ``prelistkeys``
784 784 Run before listing pushkeys (like bookmarks) in the
785 785 repository. Non-zero status will cause failure. The key namespace is
786 786 in ``$HG_NAMESPACE``.
787 787
788 788 ``preoutgoing``
789 789 Run before collecting changes to send from the local repository to
790 790 another. Non-zero status will cause failure. This lets you prevent
791 791 pull over HTTP or SSH. Also prevents against local pull, push
792 792 (outbound) or bundle commands, but not effective, since you can
793 793 just copy files instead then. Source of operation is in
794 794 ``$HG_SOURCE``. If "serve", operation is happening on behalf of remote
795 795 SSH or HTTP repository. If "push", "pull" or "bundle", operation
796 796 is happening on behalf of repository on same system.
797 797
798 798 ``prepushkey``
799 799 Run before a pushkey (like a bookmark) is added to the
800 800 repository. Non-zero status will cause the key to be rejected. The
801 801 key namespace is in ``$HG_NAMESPACE``, the key is in ``$HG_KEY``,
802 802 the old value (if any) is in ``$HG_OLD``, and the new value is in
803 803 ``$HG_NEW``.
804 804
805 805 ``pretag``
806 806 Run before creating a tag. Exit status 0 allows the tag to be
807 807 created. Non-zero status will cause the tag to fail. ID of
808 808 changeset to tag is in ``$HG_NODE``. Name of tag is in ``$HG_TAG``. Tag is
809 809 local if ``$HG_LOCAL=1``, in repository if ``$HG_LOCAL=0``.
810 810
811 811 ``pretxnopen``
812 812 Run before any new repository transaction is open. The reason for the
813 813 transaction will be in ``$HG_TXNNAME`` and a unique identifier for the
814 814 transaction will be in ``HG_TXNID``. A non-zero status will prevent the
815 815 transaction from being opened.
816 816
817 817 ``pretxnclose``
818 818 Run right before the transaction is actually finalized. Any
819 819 repository change will be visible to the hook program. This lets you
820 820 validate the transaction content or change it. Exit status 0 allows
821 821 the commit to proceed. Non-zero status will cause the transaction to
822 822 be rolled back. The reason for the transaction opening will be in
823 823 ``$HG_TXNNAME`` and a unique identifier for the transaction will be in
824 824 ``HG_TXNID``. The rest of the available data will vary according the
825 825 transaction type. New changesets will add ``$HG_NODE`` (id of the
826 826 first added changeset), ``$HG_URL`` and ``$HG_SOURCE`` variables,
827 827 bookmarks and phases changes will set ``HG_BOOKMARK_MOVED`` and
828 828 ``HG_PHASES_MOVED`` to ``1``, etc.
829 829
830 830 ``txnclose``
831 831 Run after any repository transaction has been committed. At this
832 832 point, the transaction can no longer be rolled back. The hook will run
833 833 after the lock is released. See ``pretxnclose`` docs for details about
834 834 available variables.
835 835
836 836 ``txnabort``
837 837 Run when a transaction is aborted. See ``pretxnclose`` docs for details about
838 838 available variables.
839 839
840 840 ``pretxnchangegroup``
841 841 Run after a changegroup has been added via push, pull or unbundle,
842 842 but before the transaction has been committed. Changegroup is
843 843 visible to hook program. This lets you validate incoming changes
844 844 before accepting them. Passed the ID of the first new changeset in
845 845 ``$HG_NODE``. Exit status 0 allows the transaction to commit. Non-zero
846 846 status will cause the transaction to be rolled back and the push,
847 847 pull or unbundle will fail. URL that was source of changes is in
848 848 ``$HG_URL``.
849 849
850 850 ``pretxncommit``
851 851 Run after a changeset has been created but the transaction not yet
852 852 committed. Changeset is visible to hook program. This lets you
853 853 validate commit message and changes. Exit status 0 allows the
854 854 commit to proceed. Non-zero status will cause the transaction to
855 855 be rolled back. ID of changeset is in ``$HG_NODE``. Parent changeset
856 856 IDs are in ``$HG_PARENT1`` and ``$HG_PARENT2``.
857 857
858 858 ``preupdate``
859 859 Run before updating the working directory. Exit status 0 allows
860 860 the update to proceed. Non-zero status will prevent the update.
861 861 Changeset ID of first new parent is in ``$HG_PARENT1``. If merge, ID
862 862 of second new parent is in ``$HG_PARENT2``.
863 863
864 864 ``listkeys``
865 865 Run after listing pushkeys (like bookmarks) in the repository. The
866 866 key namespace is in ``$HG_NAMESPACE``. ``$HG_VALUES`` is a
867 867 dictionary containing the keys and values.
868 868
869 869 ``pushkey``
870 870 Run after a pushkey (like a bookmark) is added to the
871 871 repository. The key namespace is in ``$HG_NAMESPACE``, the key is in
872 872 ``$HG_KEY``, the old value (if any) is in ``$HG_OLD``, and the new
873 873 value is in ``$HG_NEW``.
874 874
875 875 ``tag``
876 876 Run after a tag is created. ID of tagged changeset is in ``$HG_NODE``.
877 877 Name of tag is in ``$HG_TAG``. Tag is local if ``$HG_LOCAL=1``, in
878 878 repository if ``$HG_LOCAL=0``.
879 879
880 880 ``update``
881 881 Run after updating the working directory. Changeset ID of first
882 882 new parent is in ``$HG_PARENT1``. If merge, ID of second new parent is
883 883 in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
884 884 update failed (e.g. because conflicts not resolved), ``$HG_ERROR=1``.
885 885
886 886 .. note::
887 887
888 888 It is generally better to use standard hooks rather than the
889 889 generic pre- and post- command hooks as they are guaranteed to be
890 890 called in the appropriate contexts for influencing transactions.
891 891 Also, hooks like "commit" will be called in all contexts that
892 892 generate a commit (e.g. tag) and not just the commit command.
893 893
894 894 .. note::
895 895
896 896 Environment variables with empty values may not be passed to
897 897 hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
898 898 will have an empty value under Unix-like platforms for non-merge
899 899 changesets, while it will not be available at all under Windows.
900 900
901 901 The syntax for Python hooks is as follows::
902 902
903 903 hookname = python:modulename.submodule.callable
904 904 hookname = python:/path/to/python/module.py:callable
905 905
906 906 Python hooks are run within the Mercurial process. Each hook is
907 907 called with at least three keyword arguments: a ui object (keyword
908 908 ``ui``), a repository object (keyword ``repo``), and a ``hooktype``
909 909 keyword that tells what kind of hook is used. Arguments listed as
910 910 environment variables above are passed as keyword arguments, with no
911 911 ``HG_`` prefix, and names in lower case.
912 912
913 913 If a Python hook returns a "true" value or raises an exception, this
914 914 is treated as a failure.
915 915
916 916
917 917 ``hostfingerprints``
918 918 --------------------
919 919
920 920 Fingerprints of the certificates of known HTTPS servers.
921 921 A HTTPS connection to a server with a fingerprint configured here will
922 922 only succeed if the servers certificate matches the fingerprint.
923 923 This is very similar to how ssh known hosts works.
924 924 The fingerprint is the SHA-1 hash value of the DER encoded certificate.
925 925 The CA chain and web.cacerts is not used for servers with a fingerprint.
926 926
927 927 For example::
928 928
929 929 [hostfingerprints]
930 930 hg.intevation.org = fa:1f:d9:48:f1:e7:74:30:38:8d:d8:58:b6:94:b8:58:28:7d:8b:d0
931 931
932 932 This feature is only supported when using Python 2.6 or later.
933 933
934 934
935 935 ``http_proxy``
936 936 --------------
937 937
938 938 Used to access web-based Mercurial repositories through a HTTP
939 939 proxy.
940 940
941 941 ``host``
942 942 Host name and (optional) port of the proxy server, for example
943 943 "myproxy:8000".
944 944
945 945 ``no``
946 946 Optional. Comma-separated list of host names that should bypass
947 947 the proxy.
948 948
949 949 ``passwd``
950 950 Optional. Password to authenticate with at the proxy server.
951 951
952 952 ``user``
953 953 Optional. User name to authenticate with at the proxy server.
954 954
955 955 ``always``
956 956 Optional. Always use the proxy, even for localhost and any entries
957 957 in ``http_proxy.no``. True or False. Default: False.
958 958
959 959 ``merge-patterns``
960 960 ------------------
961 961
962 962 This section specifies merge tools to associate with particular file
963 963 patterns. Tools matched here will take precedence over the default
964 964 merge tool. Patterns are globs by default, rooted at the repository
965 965 root.
966 966
967 967 Example::
968 968
969 969 [merge-patterns]
970 970 **.c = kdiff3
971 971 **.jpg = myimgmerge
972 972
973 973 ``merge-tools``
974 974 ---------------
975 975
976 976 This section configures external merge tools to use for file-level
977 977 merges. This section has likely been preconfigured at install time.
978 978 Use :hg:`config merge-tools` to check the existing configuration.
979 979 Also see :hg:`help merge-tools` for more details.
980 980
981 981 Example ``~/.hgrc``::
982 982
983 983 [merge-tools]
984 984 # Override stock tool location
985 985 kdiff3.executable = ~/bin/kdiff3
986 986 # Specify command line
987 987 kdiff3.args = $base $local $other -o $output
988 988 # Give higher priority
989 989 kdiff3.priority = 1
990 990
991 991 # Changing the priority of preconfigured tool
992 992 vimdiff.priority = 0
993 993
994 994 # Define new tool
995 995 myHtmlTool.args = -m $local $other $base $output
996 996 myHtmlTool.regkey = Software\FooSoftware\HtmlMerge
997 997 myHtmlTool.priority = 1
998 998
999 999 Supported arguments:
1000 1000
1001 1001 ``priority``
1002 1002 The priority in which to evaluate this tool.
1003 1003 Default: 0.
1004 1004
1005 1005 ``executable``
1006 1006 Either just the name of the executable or its pathname. On Windows,
1007 1007 the path can use environment variables with ${ProgramFiles} syntax.
1008 1008 Default: the tool name.
1009 1009
1010 1010 ``args``
1011 1011 The arguments to pass to the tool executable. You can refer to the
1012 1012 files being merged as well as the output file through these
1013 1013 variables: ``$base``, ``$local``, ``$other``, ``$output``. The meaning
1014 1014 of ``$local`` and ``$other`` can vary depending on which action is being
1015 1015 performed. During and update or merge, ``$local`` represents the original
1016 1016 state of the file, while ``$other`` represents the commit you are updating
1017 1017 to or the commit you are merging with. During a rebase ``$local``
1018 1018 represents the destination of the rebase, and ``$other`` represents the
1019 1019 commit being rebased.
1020 1020 Default: ``$local $base $other``
1021 1021
1022 1022 ``premerge``
1023 1023 Attempt to run internal non-interactive 3-way merge tool before
1024 1024 launching external tool. Options are ``true``, ``false``, ``keep`` or
1025 1025 ``keep-merge3``. The ``keep`` option will leave markers in the file if the
1026 1026 premerge fails. The ``keep-merge3`` will do the same but include information
1027 1027 about the base of the merge in the marker (see internal :merge3 in
1028 1028 :hg:`help merge-tools`).
1029 1029 Default: True
1030 1030
1031 1031 ``binary``
1032 1032 This tool can merge binary files. Defaults to False, unless tool
1033 1033 was selected by file pattern match.
1034 1034
1035 1035 ``symlink``
1036 1036 This tool can merge symlinks. Defaults to False, even if tool was
1037 1037 selected by file pattern match.
1038 1038
1039 1039 ``check``
1040 1040 A list of merge success-checking options:
1041 1041
1042 1042 ``changed``
1043 1043 Ask whether merge was successful when the merged file shows no changes.
1044 1044 ``conflicts``
1045 1045 Check whether there are conflicts even though the tool reported success.
1046 1046 ``prompt``
1047 1047 Always prompt for merge success, regardless of success reported by tool.
1048 1048
1049 1049 ``fixeol``
1050 1050 Attempt to fix up EOL changes caused by the merge tool.
1051 1051 Default: False
1052 1052
1053 1053 ``gui``
1054 1054 This tool requires a graphical interface to run. Default: False
1055 1055
1056 1056 ``regkey``
1057 1057 Windows registry key which describes install location of this
1058 1058 tool. Mercurial will search for this key first under
1059 1059 ``HKEY_CURRENT_USER`` and then under ``HKEY_LOCAL_MACHINE``.
1060 1060 Default: None
1061 1061
1062 1062 ``regkeyalt``
1063 1063 An alternate Windows registry key to try if the first key is not
1064 1064 found. The alternate key uses the same ``regname`` and ``regappend``
1065 1065 semantics of the primary key. The most common use for this key
1066 1066 is to search for 32bit applications on 64bit operating systems.
1067 1067 Default: None
1068 1068
1069 1069 ``regname``
1070 1070 Name of value to read from specified registry key. Defaults to the
1071 1071 unnamed (default) value.
1072 1072
1073 1073 ``regappend``
1074 1074 String to append to the value read from the registry, typically
1075 1075 the executable name of the tool.
1076 1076 Default: None
1077 1077
1078 1078
1079 1079 ``patch``
1080 1080 ---------
1081 1081
1082 1082 Settings used when applying patches, for instance through the 'import'
1083 1083 command or with Mercurial Queues extension.
1084 1084
1085 1085 ``eol``
1086 1086 When set to 'strict' patch content and patched files end of lines
1087 1087 are preserved. When set to ``lf`` or ``crlf``, both files end of
1088 1088 lines are ignored when patching and the result line endings are
1089 1089 normalized to either LF (Unix) or CRLF (Windows). When set to
1090 1090 ``auto``, end of lines are again ignored while patching but line
1091 1091 endings in patched files are normalized to their original setting
1092 1092 on a per-file basis. If target file does not exist or has no end
1093 1093 of line, patch line endings are preserved.
1094 1094 Default: strict.
1095 1095
1096 ``fuzz``
1097 The number of lines of 'fuzz' to allow when applying patches. This
1098 controls how much context the patcher is allowed to ignore when
1099 trying to apply a patch.
1100 Default: 2
1096 1101
1097 1102 ``paths``
1098 1103 ---------
1099 1104
1100 1105 Assigns symbolic names to repositories. The left side is the
1101 1106 symbolic name, and the right gives the directory or URL that is the
1102 1107 location of the repository. Default paths can be declared by setting
1103 1108 the following entries.
1104 1109
1105 1110 ``default``
1106 1111 Directory or URL to use when pulling if no source is specified.
1107 1112 Default is set to repository from which the current repository was
1108 1113 cloned.
1109 1114
1110 1115 ``default-push``
1111 1116 Optional. Directory or URL to use when pushing if no destination
1112 1117 is specified.
1113 1118
1114 1119 Custom paths can be defined by assigning the path to a name that later can be
1115 1120 used from the command line. Example::
1116 1121
1117 1122 [paths]
1118 1123 my_path = http://example.com/path
1119 1124
1120 1125 To push to the path defined in ``my_path`` run the command::
1121 1126
1122 1127 hg push my_path
1123 1128
1124 1129
1125 1130 ``phases``
1126 1131 ----------
1127 1132
1128 1133 Specifies default handling of phases. See :hg:`help phases` for more
1129 1134 information about working with phases.
1130 1135
1131 1136 ``publish``
1132 1137 Controls draft phase behavior when working as a server. When true,
1133 1138 pushed changesets are set to public in both client and server and
1134 1139 pulled or cloned changesets are set to public in the client.
1135 1140 Default: True
1136 1141
1137 1142 ``new-commit``
1138 1143 Phase of newly-created commits.
1139 1144 Default: draft
1140 1145
1141 1146 ``checksubrepos``
1142 1147 Check the phase of the current revision of each subrepository. Allowed
1143 1148 values are "ignore", "follow" and "abort". For settings other than
1144 1149 "ignore", the phase of the current revision of each subrepository is
1145 1150 checked before committing the parent repository. If any of those phases is
1146 1151 greater than the phase of the parent repository (e.g. if a subrepo is in a
1147 1152 "secret" phase while the parent repo is in "draft" phase), the commit is
1148 1153 either aborted (if checksubrepos is set to "abort") or the higher phase is
1149 1154 used for the parent repository commit (if set to "follow").
1150 1155 Default: "follow"
1151 1156
1152 1157
1153 1158 ``profiling``
1154 1159 -------------
1155 1160
1156 1161 Specifies profiling type, format, and file output. Two profilers are
1157 1162 supported: an instrumenting profiler (named ``ls``), and a sampling
1158 1163 profiler (named ``stat``).
1159 1164
1160 1165 In this section description, 'profiling data' stands for the raw data
1161 1166 collected during profiling, while 'profiling report' stands for a
1162 1167 statistical text report generated from the profiling data. The
1163 1168 profiling is done using lsprof.
1164 1169
1165 1170 ``type``
1166 1171 The type of profiler to use.
1167 1172 Default: ls.
1168 1173
1169 1174 ``ls``
1170 1175 Use Python's built-in instrumenting profiler. This profiler
1171 1176 works on all platforms, but each line number it reports is the
1172 1177 first line of a function. This restriction makes it difficult to
1173 1178 identify the expensive parts of a non-trivial function.
1174 1179 ``stat``
1175 1180 Use a third-party statistical profiler, statprof. This profiler
1176 1181 currently runs only on Unix systems, and is most useful for
1177 1182 profiling commands that run for longer than about 0.1 seconds.
1178 1183
1179 1184 ``format``
1180 1185 Profiling format. Specific to the ``ls`` instrumenting profiler.
1181 1186 Default: text.
1182 1187
1183 1188 ``text``
1184 1189 Generate a profiling report. When saving to a file, it should be
1185 1190 noted that only the report is saved, and the profiling data is
1186 1191 not kept.
1187 1192 ``kcachegrind``
1188 1193 Format profiling data for kcachegrind use: when saving to a
1189 1194 file, the generated file can directly be loaded into
1190 1195 kcachegrind.
1191 1196
1192 1197 ``frequency``
1193 1198 Sampling frequency. Specific to the ``stat`` sampling profiler.
1194 1199 Default: 1000.
1195 1200
1196 1201 ``output``
1197 1202 File path where profiling data or report should be saved. If the
1198 1203 file exists, it is replaced. Default: None, data is printed on
1199 1204 stderr
1200 1205
1201 1206 ``sort``
1202 1207 Sort field. Specific to the ``ls`` instrumenting profiler.
1203 1208 One of ``callcount``, ``reccallcount``, ``totaltime`` and
1204 1209 ``inlinetime``.
1205 1210 Default: inlinetime.
1206 1211
1207 1212 ``limit``
1208 1213 Number of lines to show. Specific to the ``ls`` instrumenting profiler.
1209 1214 Default: 30.
1210 1215
1211 1216 ``nested``
1212 1217 Show at most this number of lines of drill-down info after each main entry.
1213 1218 This can help explain the difference between Total and Inline.
1214 1219 Specific to the ``ls`` instrumenting profiler.
1215 1220 Default: 5.
1216 1221
1217 1222 ``progress``
1218 1223 ------------
1219 1224
1220 1225 Mercurial commands can draw progress bars that are as informative as
1221 1226 possible. Some progress bars only offer indeterminate information, while others
1222 1227 have a definite end point.
1223 1228
1224 1229 ``delay``
1225 1230 Number of seconds (float) before showing the progress bar. (default: 3)
1226 1231
1227 1232 ``changedelay``
1228 1233 Minimum delay before showing a new topic. When set to less than 3 * refresh,
1229 1234 that value will be used instead. (default: 1)
1230 1235
1231 1236 ``refresh``
1232 1237 Time in seconds between refreshes of the progress bar. (default: 0.1)
1233 1238
1234 1239 ``format``
1235 1240 Format of the progress bar.
1236 1241
1237 1242 Valid entries for the format field are ``topic``, ``bar``, ``number``,
1238 1243 ``unit``, ``estimate``, speed, and item. item defaults to the last 20
1239 1244 characters of the item, but this can be changed by adding either ``-<num>``
1240 1245 which would take the last num characters, or ``+<num>`` for the first num
1241 1246 characters.
1242 1247
1243 1248 (default: Topic bar number estimate)
1244 1249
1245 1250 ``width``
1246 1251 If set, the maximum width of the progress information (that is, min(width,
1247 1252 term width) will be used)
1248 1253
1249 1254 ``clear-complete``
1250 1255 clear the progress bar after it's done (default to True)
1251 1256
1252 1257 ``disable``
1253 1258 If true, don't show a progress bar
1254 1259
1255 1260 ``assume-tty``
1256 1261 If true, ALWAYS show a progress bar, unless disable is given
1257 1262
1258 1263 ``revsetalias``
1259 1264 ---------------
1260 1265
1261 1266 Alias definitions for revsets. See :hg:`help revsets` for details.
1262 1267
1263 1268 ``server``
1264 1269 ----------
1265 1270
1266 1271 Controls generic server settings.
1267 1272
1268 1273 ``uncompressed``
1269 1274 Whether to allow clients to clone a repository using the
1270 1275 uncompressed streaming protocol. This transfers about 40% more
1271 1276 data than a regular clone, but uses less memory and CPU on both
1272 1277 server and client. Over a LAN (100 Mbps or better) or a very fast
1273 1278 WAN, an uncompressed streaming clone is a lot faster (~10x) than a
1274 1279 regular clone. Over most WAN connections (anything slower than
1275 1280 about 6 Mbps), uncompressed streaming is slower, because of the
1276 1281 extra data transfer overhead. This mode will also temporarily hold
1277 1282 the write lock while determining what data to transfer.
1278 1283 Default is True.
1279 1284
1280 1285 ``preferuncompressed``
1281 1286 When set, clients will try to use the uncompressed streaming
1282 1287 protocol. Default is False.
1283 1288
1284 1289 ``validate``
1285 1290 Whether to validate the completeness of pushed changesets by
1286 1291 checking that all new file revisions specified in manifests are
1287 1292 present. Default is False.
1288 1293
1289 1294 ``smtp``
1290 1295 --------
1291 1296
1292 1297 Configuration for extensions that need to send email messages.
1293 1298
1294 1299 ``host``
1295 1300 Host name of mail server, e.g. "mail.example.com".
1296 1301
1297 1302 ``port``
1298 1303 Optional. Port to connect to on mail server. Default: 465 (if
1299 1304 ``tls`` is smtps) or 25 (otherwise).
1300 1305
1301 1306 ``tls``
1302 1307 Optional. Method to enable TLS when connecting to mail server: starttls,
1303 1308 smtps or none. Default: none.
1304 1309
1305 1310 ``verifycert``
1306 1311 Optional. Verification for the certificate of mail server, when
1307 1312 ``tls`` is starttls or smtps. "strict", "loose" or False. For
1308 1313 "strict" or "loose", the certificate is verified as same as the
1309 1314 verification for HTTPS connections (see ``[hostfingerprints]`` and
1310 1315 ``[web] cacerts`` also). For "strict", sending email is also
1311 1316 aborted, if there is no configuration for mail server in
1312 1317 ``[hostfingerprints]`` and ``[web] cacerts``. --insecure for
1313 1318 :hg:`email` overwrites this as "loose". Default: "strict".
1314 1319
1315 1320 ``username``
1316 1321 Optional. User name for authenticating with the SMTP server.
1317 1322 Default: none.
1318 1323
1319 1324 ``password``
1320 1325 Optional. Password for authenticating with the SMTP server. If not
1321 1326 specified, interactive sessions will prompt the user for a
1322 1327 password; non-interactive sessions will fail. Default: none.
1323 1328
1324 1329 ``local_hostname``
1325 1330 Optional. It's the hostname that the sender can use to identify
1326 1331 itself to the MTA.
1327 1332
1328 1333
1329 1334 ``subpaths``
1330 1335 ------------
1331 1336
1332 1337 Subrepository source URLs can go stale if a remote server changes name
1333 1338 or becomes temporarily unavailable. This section lets you define
1334 1339 rewrite rules of the form::
1335 1340
1336 1341 <pattern> = <replacement>
1337 1342
1338 1343 where ``pattern`` is a regular expression matching a subrepository
1339 1344 source URL and ``replacement`` is the replacement string used to
1340 1345 rewrite it. Groups can be matched in ``pattern`` and referenced in
1341 1346 ``replacements``. For instance::
1342 1347
1343 1348 http://server/(.*)-hg/ = http://hg.server/\1/
1344 1349
1345 1350 rewrites ``http://server/foo-hg/`` into ``http://hg.server/foo/``.
1346 1351
1347 1352 Relative subrepository paths are first made absolute, and the
1348 1353 rewrite rules are then applied on the full (absolute) path. The rules
1349 1354 are applied in definition order.
1350 1355
1351 1356 ``trusted``
1352 1357 -----------
1353 1358
1354 1359 Mercurial will not use the settings in the
1355 1360 ``.hg/hgrc`` file from a repository if it doesn't belong to a trusted
1356 1361 user or to a trusted group, as various hgrc features allow arbitrary
1357 1362 commands to be run. This issue is often encountered when configuring
1358 1363 hooks or extensions for shared repositories or servers. However,
1359 1364 the web interface will use some safe settings from the ``[web]``
1360 1365 section.
1361 1366
1362 1367 This section specifies what users and groups are trusted. The
1363 1368 current user is always trusted. To trust everybody, list a user or a
1364 1369 group with name ``*``. These settings must be placed in an
1365 1370 *already-trusted file* to take effect, such as ``$HOME/.hgrc`` of the
1366 1371 user or service running Mercurial.
1367 1372
1368 1373 ``users``
1369 1374 Comma-separated list of trusted users.
1370 1375
1371 1376 ``groups``
1372 1377 Comma-separated list of trusted groups.
1373 1378
1374 1379
1375 1380 ``ui``
1376 1381 ------
1377 1382
1378 1383 User interface controls.
1379 1384
1380 1385 ``archivemeta``
1381 1386 Whether to include the .hg_archival.txt file containing meta data
1382 1387 (hashes for the repository base and for tip) in archives created
1383 1388 by the :hg:`archive` command or downloaded via hgweb.
1384 1389 Default is True.
1385 1390
1386 1391 ``askusername``
1387 1392 Whether to prompt for a username when committing. If True, and
1388 1393 neither ``$HGUSER`` nor ``$EMAIL`` has been specified, then the user will
1389 1394 be prompted to enter a username. If no username is entered, the
1390 1395 default ``USER@HOST`` is used instead.
1391 1396 Default is False.
1392 1397
1393 1398 ``commitsubrepos``
1394 1399 Whether to commit modified subrepositories when committing the
1395 1400 parent repository. If False and one subrepository has uncommitted
1396 1401 changes, abort the commit.
1397 1402 Default is False.
1398 1403
1399 1404 ``debug``
1400 1405 Print debugging information. True or False. Default is False.
1401 1406
1402 1407 ``editor``
1403 1408 The editor to use during a commit. Default is ``$EDITOR`` or ``vi``.
1404 1409
1405 1410 ``fallbackencoding``
1406 1411 Encoding to try if it's not possible to decode the changelog using
1407 1412 UTF-8. Default is ISO-8859-1.
1408 1413
1409 1414 ``ignore``
1410 1415 A file to read per-user ignore patterns from. This file should be
1411 1416 in the same format as a repository-wide .hgignore file. Filenames
1412 1417 are relative to the repository root. This option supports hook syntax,
1413 1418 so if you want to specify multiple ignore files, you can do so by
1414 1419 setting something like ``ignore.other = ~/.hgignore2``. For details
1415 1420 of the ignore file format, see the ``hgignore(5)`` man page.
1416 1421
1417 1422 ``interactive``
1418 1423 Allow to prompt the user. True or False. Default is True.
1419 1424
1420 1425 ``logtemplate``
1421 1426 Template string for commands that print changesets.
1422 1427
1423 1428 ``merge``
1424 1429 The conflict resolution program to use during a manual merge.
1425 1430 For more information on merge tools see :hg:`help merge-tools`.
1426 1431 For configuring merge tools see the ``[merge-tools]`` section.
1427 1432
1428 1433 ``mergemarkers``
1429 1434 Sets the merge conflict marker label styling. The ``detailed``
1430 1435 style uses the ``mergemarkertemplate`` setting to style the labels.
1431 1436 The ``basic`` style just uses 'local' and 'other' as the marker label.
1432 1437 One of ``basic`` or ``detailed``.
1433 1438 Default is ``basic``.
1434 1439
1435 1440 ``mergemarkertemplate``
1436 1441 The template used to print the commit description next to each conflict
1437 1442 marker during merge conflicts. See :hg:`help templates` for the template
1438 1443 format.
1439 1444 Defaults to showing the hash, tags, branches, bookmarks, author, and
1440 1445 the first line of the commit description.
1441 1446 If you use non-ASCII characters in names for tags, branches, bookmarks,
1442 1447 authors, and/or commit descriptions, you must pay attention to encodings of
1443 1448 managed files. At template expansion, non-ASCII characters use the encoding
1444 1449 specified by the ``--encoding`` global option, ``HGENCODING`` or other
1445 1450 environment variables that govern your locale. If the encoding of the merge
1446 1451 markers is different from the encoding of the merged files,
1447 1452 serious problems may occur.
1448 1453
1449 1454 ``portablefilenames``
1450 1455 Check for portable filenames. Can be ``warn``, ``ignore`` or ``abort``.
1451 1456 Default is ``warn``.
1452 1457 If set to ``warn`` (or ``true``), a warning message is printed on POSIX
1453 1458 platforms, if a file with a non-portable filename is added (e.g. a file
1454 1459 with a name that can't be created on Windows because it contains reserved
1455 1460 parts like ``AUX``, reserved characters like ``:``, or would cause a case
1456 1461 collision with an existing file).
1457 1462 If set to ``ignore`` (or ``false``), no warning is printed.
1458 1463 If set to ``abort``, the command is aborted.
1459 1464 On Windows, this configuration option is ignored and the command aborted.
1460 1465
1461 1466 ``quiet``
1462 1467 Reduce the amount of output printed. True or False. Default is False.
1463 1468
1464 1469 ``remotecmd``
1465 1470 remote command to use for clone/push/pull operations. Default is ``hg``.
1466 1471
1467 1472 ``report_untrusted``
1468 1473 Warn if a ``.hg/hgrc`` file is ignored due to not being owned by a
1469 1474 trusted user or group. True or False. Default is True.
1470 1475
1471 1476 ``slash``
1472 1477 Display paths using a slash (``/``) as the path separator. This
1473 1478 only makes a difference on systems where the default path
1474 1479 separator is not the slash character (e.g. Windows uses the
1475 1480 backslash character (``\``)).
1476 1481 Default is False.
1477 1482
1478 1483 ``statuscopies``
1479 1484 Display copies in the status command.
1480 1485
1481 1486 ``ssh``
1482 1487 command to use for SSH connections. Default is ``ssh``.
1483 1488
1484 1489 ``strict``
1485 1490 Require exact command names, instead of allowing unambiguous
1486 1491 abbreviations. True or False. Default is False.
1487 1492
1488 1493 ``style``
1489 1494 Name of style to use for command output.
1490 1495
1491 1496 ``timeout``
1492 1497 The timeout used when a lock is held (in seconds), a negative value
1493 1498 means no timeout. Default is 600.
1494 1499
1495 1500 ``traceback``
1496 1501 Mercurial always prints a traceback when an unknown exception
1497 1502 occurs. Setting this to True will make Mercurial print a traceback
1498 1503 on all exceptions, even those recognized by Mercurial (such as
1499 1504 IOError or MemoryError). Default is False.
1500 1505
1501 1506 ``username``
1502 1507 The committer of a changeset created when running "commit".
1503 1508 Typically a person's name and email address, e.g. ``Fred Widget
1504 1509 <fred@example.com>``. Default is ``$EMAIL`` or ``username@hostname``. If
1505 1510 the username in hgrc is empty, it has to be specified manually or
1506 1511 in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set
1507 1512 ``username =`` in the system hgrc). Environment variables in the
1508 1513 username are expanded.
1509 1514
1510 1515 ``verbose``
1511 1516 Increase the amount of output printed. True or False. Default is False.
1512 1517
1513 1518
1514 1519 ``web``
1515 1520 -------
1516 1521
1517 1522 Web interface configuration. The settings in this section apply to
1518 1523 both the builtin webserver (started by :hg:`serve`) and the script you
1519 1524 run through a webserver (``hgweb.cgi`` and the derivatives for FastCGI
1520 1525 and WSGI).
1521 1526
1522 1527 The Mercurial webserver does no authentication (it does not prompt for
1523 1528 usernames and passwords to validate *who* users are), but it does do
1524 1529 authorization (it grants or denies access for *authenticated users*
1525 1530 based on settings in this section). You must either configure your
1526 1531 webserver to do authentication for you, or disable the authorization
1527 1532 checks.
1528 1533
1529 1534 For a quick setup in a trusted environment, e.g., a private LAN, where
1530 1535 you want it to accept pushes from anybody, you can use the following
1531 1536 command line::
1532 1537
1533 1538 $ hg --config web.allow_push=* --config web.push_ssl=False serve
1534 1539
1535 1540 Note that this will allow anybody to push anything to the server and
1536 1541 that this should not be used for public servers.
1537 1542
1538 1543 The full set of options is:
1539 1544
1540 1545 ``accesslog``
1541 1546 Where to output the access log. Default is stdout.
1542 1547
1543 1548 ``address``
1544 1549 Interface address to bind to. Default is all.
1545 1550
1546 1551 ``allow_archive``
1547 1552 List of archive format (bz2, gz, zip) allowed for downloading.
1548 1553 Default is empty.
1549 1554
1550 1555 ``allowbz2``
1551 1556 (DEPRECATED) Whether to allow .tar.bz2 downloading of repository
1552 1557 revisions.
1553 1558 Default is False.
1554 1559
1555 1560 ``allowgz``
1556 1561 (DEPRECATED) Whether to allow .tar.gz downloading of repository
1557 1562 revisions.
1558 1563 Default is False.
1559 1564
1560 1565 ``allowpull``
1561 1566 Whether to allow pulling from the repository. Default is True.
1562 1567
1563 1568 ``allow_push``
1564 1569 Whether to allow pushing to the repository. If empty or not set,
1565 1570 push is not allowed. If the special value ``*``, any remote user can
1566 1571 push, including unauthenticated users. Otherwise, the remote user
1567 1572 must have been authenticated, and the authenticated user name must
1568 1573 be present in this list. The contents of the allow_push list are
1569 1574 examined after the deny_push list.
1570 1575
1571 1576 ``allow_read``
1572 1577 If the user has not already been denied repository access due to
1573 1578 the contents of deny_read, this list determines whether to grant
1574 1579 repository access to the user. If this list is not empty, and the
1575 1580 user is unauthenticated or not present in the list, then access is
1576 1581 denied for the user. If the list is empty or not set, then access
1577 1582 is permitted to all users by default. Setting allow_read to the
1578 1583 special value ``*`` is equivalent to it not being set (i.e. access
1579 1584 is permitted to all users). The contents of the allow_read list are
1580 1585 examined after the deny_read list.
1581 1586
1582 1587 ``allowzip``
1583 1588 (DEPRECATED) Whether to allow .zip downloading of repository
1584 1589 revisions. Default is False. This feature creates temporary files.
1585 1590
1586 1591 ``archivesubrepos``
1587 1592 Whether to recurse into subrepositories when archiving. Default is
1588 1593 False.
1589 1594
1590 1595 ``baseurl``
1591 1596 Base URL to use when publishing URLs in other locations, so
1592 1597 third-party tools like email notification hooks can construct
1593 1598 URLs. Example: ``http://hgserver/repos/``.
1594 1599
1595 1600 ``cacerts``
1596 1601 Path to file containing a list of PEM encoded certificate
1597 1602 authority certificates. Environment variables and ``~user``
1598 1603 constructs are expanded in the filename. If specified on the
1599 1604 client, then it will verify the identity of remote HTTPS servers
1600 1605 with these certificates.
1601 1606
1602 1607 This feature is only supported when using Python 2.6 or later. If you wish
1603 1608 to use it with earlier versions of Python, install the backported
1604 1609 version of the ssl library that is available from
1605 1610 ``http://pypi.python.org``.
1606 1611
1607 1612 To disable SSL verification temporarily, specify ``--insecure`` from
1608 1613 command line.
1609 1614
1610 1615 You can use OpenSSL's CA certificate file if your platform has
1611 1616 one. On most Linux systems this will be
1612 1617 ``/etc/ssl/certs/ca-certificates.crt``. Otherwise you will have to
1613 1618 generate this file manually. The form must be as follows::
1614 1619
1615 1620 -----BEGIN CERTIFICATE-----
1616 1621 ... (certificate in base64 PEM encoding) ...
1617 1622 -----END CERTIFICATE-----
1618 1623 -----BEGIN CERTIFICATE-----
1619 1624 ... (certificate in base64 PEM encoding) ...
1620 1625 -----END CERTIFICATE-----
1621 1626
1622 1627 ``cache``
1623 1628 Whether to support caching in hgweb. Defaults to True.
1624 1629
1625 1630 ``collapse``
1626 1631 With ``descend`` enabled, repositories in subdirectories are shown at
1627 1632 a single level alongside repositories in the current path. With
1628 1633 ``collapse`` also enabled, repositories residing at a deeper level than
1629 1634 the current path are grouped behind navigable directory entries that
1630 1635 lead to the locations of these repositories. In effect, this setting
1631 1636 collapses each collection of repositories found within a subdirectory
1632 1637 into a single entry for that subdirectory. Default is False.
1633 1638
1634 1639 ``comparisoncontext``
1635 1640 Number of lines of context to show in side-by-side file comparison. If
1636 1641 negative or the value ``full``, whole files are shown. Default is 5.
1637 1642 This setting can be overridden by a ``context`` request parameter to the
1638 1643 ``comparison`` command, taking the same values.
1639 1644
1640 1645 ``contact``
1641 1646 Name or email address of the person in charge of the repository.
1642 1647 Defaults to ui.username or ``$EMAIL`` or "unknown" if unset or empty.
1643 1648
1644 1649 ``deny_push``
1645 1650 Whether to deny pushing to the repository. If empty or not set,
1646 1651 push is not denied. If the special value ``*``, all remote users are
1647 1652 denied push. Otherwise, unauthenticated users are all denied, and
1648 1653 any authenticated user name present in this list is also denied. The
1649 1654 contents of the deny_push list are examined before the allow_push list.
1650 1655
1651 1656 ``deny_read``
1652 1657 Whether to deny reading/viewing of the repository. If this list is
1653 1658 not empty, unauthenticated users are all denied, and any
1654 1659 authenticated user name present in this list is also denied access to
1655 1660 the repository. If set to the special value ``*``, all remote users
1656 1661 are denied access (rarely needed ;). If deny_read is empty or not set,
1657 1662 the determination of repository access depends on the presence and
1658 1663 content of the allow_read list (see description). If both
1659 1664 deny_read and allow_read are empty or not set, then access is
1660 1665 permitted to all users by default. If the repository is being
1661 1666 served via hgwebdir, denied users will not be able to see it in
1662 1667 the list of repositories. The contents of the deny_read list have
1663 1668 priority over (are examined before) the contents of the allow_read
1664 1669 list.
1665 1670
1666 1671 ``descend``
1667 1672 hgwebdir indexes will not descend into subdirectories. Only repositories
1668 1673 directly in the current path will be shown (other repositories are still
1669 1674 available from the index corresponding to their containing path).
1670 1675
1671 1676 ``description``
1672 1677 Textual description of the repository's purpose or contents.
1673 1678 Default is "unknown".
1674 1679
1675 1680 ``encoding``
1676 1681 Character encoding name. Default is the current locale charset.
1677 1682 Example: "UTF-8"
1678 1683
1679 1684 ``errorlog``
1680 1685 Where to output the error log. Default is stderr.
1681 1686
1682 1687 ``guessmime``
1683 1688 Control MIME types for raw download of file content.
1684 1689 Set to True to let hgweb guess the content type from the file
1685 1690 extension. This will serve HTML files as ``text/html`` and might
1686 1691 allow cross-site scripting attacks when serving untrusted
1687 1692 repositories. Default is False.
1688 1693
1689 1694 ``hidden``
1690 1695 Whether to hide the repository in the hgwebdir index.
1691 1696 Default is False.
1692 1697
1693 1698 ``ipv6``
1694 1699 Whether to use IPv6. Default is False.
1695 1700
1696 1701 ``logoimg``
1697 1702 File name of the logo image that some templates display on each page.
1698 1703 The file name is relative to ``staticurl``. That is, the full path to
1699 1704 the logo image is "staticurl/logoimg".
1700 1705 If unset, ``hglogo.png`` will be used.
1701 1706
1702 1707 ``logourl``
1703 1708 Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
1704 1709 will be used.
1705 1710
1706 1711 ``maxchanges``
1707 1712 Maximum number of changes to list on the changelog. Default is 10.
1708 1713
1709 1714 ``maxfiles``
1710 1715 Maximum number of files to list per changeset. Default is 10.
1711 1716
1712 1717 ``maxshortchanges``
1713 1718 Maximum number of changes to list on the shortlog, graph or filelog
1714 1719 pages. Default is 60.
1715 1720
1716 1721 ``name``
1717 1722 Repository name to use in the web interface. Default is current
1718 1723 working directory.
1719 1724
1720 1725 ``port``
1721 1726 Port to listen on. Default is 8000.
1722 1727
1723 1728 ``prefix``
1724 1729 Prefix path to serve from. Default is '' (server root).
1725 1730
1726 1731 ``push_ssl``
1727 1732 Whether to require that inbound pushes be transported over SSL to
1728 1733 prevent password sniffing. Default is True.
1729 1734
1730 1735 ``staticurl``
1731 1736 Base URL to use for static files. If unset, static files (e.g. the
1732 1737 hgicon.png favicon) will be served by the CGI script itself. Use
1733 1738 this setting to serve them directly with the HTTP server.
1734 1739 Example: ``http://hgserver/static/``.
1735 1740
1736 1741 ``stripes``
1737 1742 How many lines a "zebra stripe" should span in multi-line output.
1738 1743 Default is 1; set to 0 to disable.
1739 1744
1740 1745 ``style``
1741 1746 Which template map style to use. The available options are the names of
1742 1747 subdirectories in the HTML templates path. Default is ``paper``.
1743 1748 Example: ``monoblue``
1744 1749
1745 1750 ``templates``
1746 1751 Where to find the HTML templates. The default path to the HTML templates
1747 1752 can be obtained from ``hg debuginstall``.
1748 1753
1749 1754 ``websub``
1750 1755 ----------
1751 1756
1752 1757 Web substitution filter definition. You can use this section to
1753 1758 define a set of regular expression substitution patterns which
1754 1759 let you automatically modify the hgweb server output.
1755 1760
1756 1761 The default hgweb templates only apply these substitution patterns
1757 1762 on the revision description fields. You can apply them anywhere
1758 1763 you want when you create your own templates by adding calls to the
1759 1764 "websub" filter (usually after calling the "escape" filter).
1760 1765
1761 1766 This can be used, for example, to convert issue references to links
1762 1767 to your issue tracker, or to convert "markdown-like" syntax into
1763 1768 HTML (see the examples below).
1764 1769
1765 1770 Each entry in this section names a substitution filter.
1766 1771 The value of each entry defines the substitution expression itself.
1767 1772 The websub expressions follow the old interhg extension syntax,
1768 1773 which in turn imitates the Unix sed replacement syntax::
1769 1774
1770 1775 patternname = s/SEARCH_REGEX/REPLACE_EXPRESSION/[i]
1771 1776
1772 1777 You can use any separator other than "/". The final "i" is optional
1773 1778 and indicates that the search must be case insensitive.
1774 1779
1775 1780 Examples::
1776 1781
1777 1782 [websub]
1778 1783 issues = s|issue(\d+)|<a href="http://bts.example.org/issue\1">issue\1</a>|i
1779 1784 italic = s/\b_(\S+)_\b/<i>\1<\/i>/
1780 1785 bold = s/\*\b(\S+)\b\*/<b>\1<\/b>/
1781 1786
1782 1787 ``worker``
1783 1788 ----------
1784 1789
1785 1790 Parallel master/worker configuration. We currently perform working
1786 1791 directory updates in parallel on Unix-like systems, which greatly
1787 1792 helps performance.
1788 1793
1789 1794 ``numcpus``
1790 1795 Number of CPUs to use for parallel operations. Default is 4 or the
1791 1796 number of CPUs on the system, whichever is larger. A zero or
1792 1797 negative value is treated as ``use the default``.
@@ -1,2553 +1,2553
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 or any later version.
8 8
9 9 import collections
10 10 import cStringIO, email, os, errno, re, posixpath, copy
11 11 import tempfile, zlib, shutil
12 12 # On python2.4 you have to import these by name or they fail to
13 13 # load. This was not a problem on Python 2.7.
14 14 import email.Generator
15 15 import email.Parser
16 16
17 17 from i18n import _
18 18 from node import hex, short
19 19 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, error
20 20 import pathutil
21 21
22 22 gitre = re.compile('diff --git a/(.*) b/(.*)')
23 23 tabsplitter = re.compile(r'(\t+|[^\t]+)')
24 24
25 25 class PatchError(Exception):
26 26 pass
27 27
28 28
29 29 # public functions
30 30
31 31 def split(stream):
32 32 '''return an iterator of individual patches from a stream'''
33 33 def isheader(line, inheader):
34 34 if inheader and line[0] in (' ', '\t'):
35 35 # continuation
36 36 return True
37 37 if line[0] in (' ', '-', '+'):
38 38 # diff line - don't check for header pattern in there
39 39 return False
40 40 l = line.split(': ', 1)
41 41 return len(l) == 2 and ' ' not in l[0]
42 42
43 43 def chunk(lines):
44 44 return cStringIO.StringIO(''.join(lines))
45 45
46 46 def hgsplit(stream, cur):
47 47 inheader = True
48 48
49 49 for line in stream:
50 50 if not line.strip():
51 51 inheader = False
52 52 if not inheader and line.startswith('# HG changeset patch'):
53 53 yield chunk(cur)
54 54 cur = []
55 55 inheader = True
56 56
57 57 cur.append(line)
58 58
59 59 if cur:
60 60 yield chunk(cur)
61 61
62 62 def mboxsplit(stream, cur):
63 63 for line in stream:
64 64 if line.startswith('From '):
65 65 for c in split(chunk(cur[1:])):
66 66 yield c
67 67 cur = []
68 68
69 69 cur.append(line)
70 70
71 71 if cur:
72 72 for c in split(chunk(cur[1:])):
73 73 yield c
74 74
75 75 def mimesplit(stream, cur):
76 76 def msgfp(m):
77 77 fp = cStringIO.StringIO()
78 78 g = email.Generator.Generator(fp, mangle_from_=False)
79 79 g.flatten(m)
80 80 fp.seek(0)
81 81 return fp
82 82
83 83 for line in stream:
84 84 cur.append(line)
85 85 c = chunk(cur)
86 86
87 87 m = email.Parser.Parser().parse(c)
88 88 if not m.is_multipart():
89 89 yield msgfp(m)
90 90 else:
91 91 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
92 92 for part in m.walk():
93 93 ct = part.get_content_type()
94 94 if ct not in ok_types:
95 95 continue
96 96 yield msgfp(part)
97 97
98 98 def headersplit(stream, cur):
99 99 inheader = False
100 100
101 101 for line in stream:
102 102 if not inheader and isheader(line, inheader):
103 103 yield chunk(cur)
104 104 cur = []
105 105 inheader = True
106 106 if inheader and not isheader(line, inheader):
107 107 inheader = False
108 108
109 109 cur.append(line)
110 110
111 111 if cur:
112 112 yield chunk(cur)
113 113
114 114 def remainder(cur):
115 115 yield chunk(cur)
116 116
117 117 class fiter(object):
118 118 def __init__(self, fp):
119 119 self.fp = fp
120 120
121 121 def __iter__(self):
122 122 return self
123 123
124 124 def next(self):
125 125 l = self.fp.readline()
126 126 if not l:
127 127 raise StopIteration
128 128 return l
129 129
130 130 inheader = False
131 131 cur = []
132 132
133 133 mimeheaders = ['content-type']
134 134
135 135 if not util.safehasattr(stream, 'next'):
136 136 # http responses, for example, have readline but not next
137 137 stream = fiter(stream)
138 138
139 139 for line in stream:
140 140 cur.append(line)
141 141 if line.startswith('# HG changeset patch'):
142 142 return hgsplit(stream, cur)
143 143 elif line.startswith('From '):
144 144 return mboxsplit(stream, cur)
145 145 elif isheader(line, inheader):
146 146 inheader = True
147 147 if line.split(':', 1)[0].lower() in mimeheaders:
148 148 # let email parser handle this
149 149 return mimesplit(stream, cur)
150 150 elif line.startswith('--- ') and inheader:
151 151 # No evil headers seen by diff start, split by hand
152 152 return headersplit(stream, cur)
153 153 # Not enough info, keep reading
154 154
155 155 # if we are here, we have a very plain patch
156 156 return remainder(cur)
157 157
158 158 def extract(ui, fileobj):
159 159 '''extract patch from data read from fileobj.
160 160
161 161 patch can be a normal patch or contained in an email message.
162 162
163 163 return tuple (filename, message, user, date, branch, node, p1, p2).
164 164 Any item in the returned tuple can be None. If filename is None,
165 165 fileobj did not contain a patch. Caller must unlink filename when done.'''
166 166
167 167 # attempt to detect the start of a patch
168 168 # (this heuristic is borrowed from quilt)
169 169 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |'
170 170 r'retrieving revision [0-9]+(\.[0-9]+)*$|'
171 171 r'---[ \t].*?^\+\+\+[ \t]|'
172 172 r'\*\*\*[ \t].*?^---[ \t])', re.MULTILINE|re.DOTALL)
173 173
174 174 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
175 175 tmpfp = os.fdopen(fd, 'w')
176 176 try:
177 177 msg = email.Parser.Parser().parse(fileobj)
178 178
179 179 subject = msg['Subject']
180 180 user = msg['From']
181 181 if not subject and not user:
182 182 # Not an email, restore parsed headers if any
183 183 subject = '\n'.join(': '.join(h) for h in msg.items()) + '\n'
184 184
185 185 # should try to parse msg['Date']
186 186 date = None
187 187 nodeid = None
188 188 branch = None
189 189 parents = []
190 190
191 191 if subject:
192 192 if subject.startswith('[PATCH'):
193 193 pend = subject.find(']')
194 194 if pend >= 0:
195 195 subject = subject[pend + 1:].lstrip()
196 196 subject = re.sub(r'\n[ \t]+', ' ', subject)
197 197 ui.debug('Subject: %s\n' % subject)
198 198 if user:
199 199 ui.debug('From: %s\n' % user)
200 200 diffs_seen = 0
201 201 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
202 202 message = ''
203 203 for part in msg.walk():
204 204 content_type = part.get_content_type()
205 205 ui.debug('Content-Type: %s\n' % content_type)
206 206 if content_type not in ok_types:
207 207 continue
208 208 payload = part.get_payload(decode=True)
209 209 m = diffre.search(payload)
210 210 if m:
211 211 hgpatch = False
212 212 hgpatchheader = False
213 213 ignoretext = False
214 214
215 215 ui.debug('found patch at byte %d\n' % m.start(0))
216 216 diffs_seen += 1
217 217 cfp = cStringIO.StringIO()
218 218 for line in payload[:m.start(0)].splitlines():
219 219 if line.startswith('# HG changeset patch') and not hgpatch:
220 220 ui.debug('patch generated by hg export\n')
221 221 hgpatch = True
222 222 hgpatchheader = True
223 223 # drop earlier commit message content
224 224 cfp.seek(0)
225 225 cfp.truncate()
226 226 subject = None
227 227 elif hgpatchheader:
228 228 if line.startswith('# User '):
229 229 user = line[7:]
230 230 ui.debug('From: %s\n' % user)
231 231 elif line.startswith("# Date "):
232 232 date = line[7:]
233 233 elif line.startswith("# Branch "):
234 234 branch = line[9:]
235 235 elif line.startswith("# Node ID "):
236 236 nodeid = line[10:]
237 237 elif line.startswith("# Parent "):
238 238 parents.append(line[9:].lstrip())
239 239 elif not line.startswith("# "):
240 240 hgpatchheader = False
241 241 elif line == '---':
242 242 ignoretext = True
243 243 if not hgpatchheader and not ignoretext:
244 244 cfp.write(line)
245 245 cfp.write('\n')
246 246 message = cfp.getvalue()
247 247 if tmpfp:
248 248 tmpfp.write(payload)
249 249 if not payload.endswith('\n'):
250 250 tmpfp.write('\n')
251 251 elif not diffs_seen and message and content_type == 'text/plain':
252 252 message += '\n' + payload
253 253 except: # re-raises
254 254 tmpfp.close()
255 255 os.unlink(tmpname)
256 256 raise
257 257
258 258 if subject and not message.startswith(subject):
259 259 message = '%s\n%s' % (subject, message)
260 260 tmpfp.close()
261 261 if not diffs_seen:
262 262 os.unlink(tmpname)
263 263 return None, message, user, date, branch, None, None, None
264 264
265 265 if parents:
266 266 p1 = parents.pop(0)
267 267 else:
268 268 p1 = None
269 269
270 270 if parents:
271 271 p2 = parents.pop(0)
272 272 else:
273 273 p2 = None
274 274
275 275 return tmpname, message, user, date, branch, nodeid, p1, p2
276 276
277 277 class patchmeta(object):
278 278 """Patched file metadata
279 279
280 280 'op' is the performed operation within ADD, DELETE, RENAME, MODIFY
281 281 or COPY. 'path' is patched file path. 'oldpath' is set to the
282 282 origin file when 'op' is either COPY or RENAME, None otherwise. If
283 283 file mode is changed, 'mode' is a tuple (islink, isexec) where
284 284 'islink' is True if the file is a symlink and 'isexec' is True if
285 285 the file is executable. Otherwise, 'mode' is None.
286 286 """
287 287 def __init__(self, path):
288 288 self.path = path
289 289 self.oldpath = None
290 290 self.mode = None
291 291 self.op = 'MODIFY'
292 292 self.binary = False
293 293
294 294 def setmode(self, mode):
295 295 islink = mode & 020000
296 296 isexec = mode & 0100
297 297 self.mode = (islink, isexec)
298 298
299 299 def copy(self):
300 300 other = patchmeta(self.path)
301 301 other.oldpath = self.oldpath
302 302 other.mode = self.mode
303 303 other.op = self.op
304 304 other.binary = self.binary
305 305 return other
306 306
307 307 def _ispatchinga(self, afile):
308 308 if afile == '/dev/null':
309 309 return self.op == 'ADD'
310 310 return afile == 'a/' + (self.oldpath or self.path)
311 311
312 312 def _ispatchingb(self, bfile):
313 313 if bfile == '/dev/null':
314 314 return self.op == 'DELETE'
315 315 return bfile == 'b/' + self.path
316 316
317 317 def ispatching(self, afile, bfile):
318 318 return self._ispatchinga(afile) and self._ispatchingb(bfile)
319 319
320 320 def __repr__(self):
321 321 return "<patchmeta %s %r>" % (self.op, self.path)
322 322
323 323 def readgitpatch(lr):
324 324 """extract git-style metadata about patches from <patchname>"""
325 325
326 326 # Filter patch for git information
327 327 gp = None
328 328 gitpatches = []
329 329 for line in lr:
330 330 line = line.rstrip(' \r\n')
331 331 if line.startswith('diff --git a/'):
332 332 m = gitre.match(line)
333 333 if m:
334 334 if gp:
335 335 gitpatches.append(gp)
336 336 dst = m.group(2)
337 337 gp = patchmeta(dst)
338 338 elif gp:
339 339 if line.startswith('--- '):
340 340 gitpatches.append(gp)
341 341 gp = None
342 342 continue
343 343 if line.startswith('rename from '):
344 344 gp.op = 'RENAME'
345 345 gp.oldpath = line[12:]
346 346 elif line.startswith('rename to '):
347 347 gp.path = line[10:]
348 348 elif line.startswith('copy from '):
349 349 gp.op = 'COPY'
350 350 gp.oldpath = line[10:]
351 351 elif line.startswith('copy to '):
352 352 gp.path = line[8:]
353 353 elif line.startswith('deleted file'):
354 354 gp.op = 'DELETE'
355 355 elif line.startswith('new file mode '):
356 356 gp.op = 'ADD'
357 357 gp.setmode(int(line[-6:], 8))
358 358 elif line.startswith('new mode '):
359 359 gp.setmode(int(line[-6:], 8))
360 360 elif line.startswith('GIT binary patch'):
361 361 gp.binary = True
362 362 if gp:
363 363 gitpatches.append(gp)
364 364
365 365 return gitpatches
366 366
367 367 class linereader(object):
368 368 # simple class to allow pushing lines back into the input stream
369 369 def __init__(self, fp):
370 370 self.fp = fp
371 371 self.buf = []
372 372
373 373 def push(self, line):
374 374 if line is not None:
375 375 self.buf.append(line)
376 376
377 377 def readline(self):
378 378 if self.buf:
379 379 l = self.buf[0]
380 380 del self.buf[0]
381 381 return l
382 382 return self.fp.readline()
383 383
384 384 def __iter__(self):
385 385 while True:
386 386 l = self.readline()
387 387 if not l:
388 388 break
389 389 yield l
390 390
391 391 class abstractbackend(object):
392 392 def __init__(self, ui):
393 393 self.ui = ui
394 394
395 395 def getfile(self, fname):
396 396 """Return target file data and flags as a (data, (islink,
397 397 isexec)) tuple. Data is None if file is missing/deleted.
398 398 """
399 399 raise NotImplementedError
400 400
401 401 def setfile(self, fname, data, mode, copysource):
402 402 """Write data to target file fname and set its mode. mode is a
403 403 (islink, isexec) tuple. If data is None, the file content should
404 404 be left unchanged. If the file is modified after being copied,
405 405 copysource is set to the original file name.
406 406 """
407 407 raise NotImplementedError
408 408
409 409 def unlink(self, fname):
410 410 """Unlink target file."""
411 411 raise NotImplementedError
412 412
413 413 def writerej(self, fname, failed, total, lines):
414 414 """Write rejected lines for fname. total is the number of hunks
415 415 which failed to apply and total the total number of hunks for this
416 416 files.
417 417 """
418 418 pass
419 419
420 420 def exists(self, fname):
421 421 raise NotImplementedError
422 422
423 423 class fsbackend(abstractbackend):
424 424 def __init__(self, ui, basedir):
425 425 super(fsbackend, self).__init__(ui)
426 426 self.opener = scmutil.opener(basedir)
427 427
428 428 def _join(self, f):
429 429 return os.path.join(self.opener.base, f)
430 430
431 431 def getfile(self, fname):
432 432 if self.opener.islink(fname):
433 433 return (self.opener.readlink(fname), (True, False))
434 434
435 435 isexec = False
436 436 try:
437 437 isexec = self.opener.lstat(fname).st_mode & 0100 != 0
438 438 except OSError, e:
439 439 if e.errno != errno.ENOENT:
440 440 raise
441 441 try:
442 442 return (self.opener.read(fname), (False, isexec))
443 443 except IOError, e:
444 444 if e.errno != errno.ENOENT:
445 445 raise
446 446 return None, None
447 447
448 448 def setfile(self, fname, data, mode, copysource):
449 449 islink, isexec = mode
450 450 if data is None:
451 451 self.opener.setflags(fname, islink, isexec)
452 452 return
453 453 if islink:
454 454 self.opener.symlink(data, fname)
455 455 else:
456 456 self.opener.write(fname, data)
457 457 if isexec:
458 458 self.opener.setflags(fname, False, True)
459 459
460 460 def unlink(self, fname):
461 461 self.opener.unlinkpath(fname, ignoremissing=True)
462 462
463 463 def writerej(self, fname, failed, total, lines):
464 464 fname = fname + ".rej"
465 465 self.ui.warn(
466 466 _("%d out of %d hunks FAILED -- saving rejects to file %s\n") %
467 467 (failed, total, fname))
468 468 fp = self.opener(fname, 'w')
469 469 fp.writelines(lines)
470 470 fp.close()
471 471
472 472 def exists(self, fname):
473 473 return self.opener.lexists(fname)
474 474
475 475 class workingbackend(fsbackend):
476 476 def __init__(self, ui, repo, similarity):
477 477 super(workingbackend, self).__init__(ui, repo.root)
478 478 self.repo = repo
479 479 self.similarity = similarity
480 480 self.removed = set()
481 481 self.changed = set()
482 482 self.copied = []
483 483
484 484 def _checkknown(self, fname):
485 485 if self.repo.dirstate[fname] == '?' and self.exists(fname):
486 486 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
487 487
488 488 def setfile(self, fname, data, mode, copysource):
489 489 self._checkknown(fname)
490 490 super(workingbackend, self).setfile(fname, data, mode, copysource)
491 491 if copysource is not None:
492 492 self.copied.append((copysource, fname))
493 493 self.changed.add(fname)
494 494
495 495 def unlink(self, fname):
496 496 self._checkknown(fname)
497 497 super(workingbackend, self).unlink(fname)
498 498 self.removed.add(fname)
499 499 self.changed.add(fname)
500 500
501 501 def close(self):
502 502 wctx = self.repo[None]
503 503 changed = set(self.changed)
504 504 for src, dst in self.copied:
505 505 scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst)
506 506 if self.removed:
507 507 wctx.forget(sorted(self.removed))
508 508 for f in self.removed:
509 509 if f not in self.repo.dirstate:
510 510 # File was deleted and no longer belongs to the
511 511 # dirstate, it was probably marked added then
512 512 # deleted, and should not be considered by
513 513 # marktouched().
514 514 changed.discard(f)
515 515 if changed:
516 516 scmutil.marktouched(self.repo, changed, self.similarity)
517 517 return sorted(self.changed)
518 518
519 519 class filestore(object):
520 520 def __init__(self, maxsize=None):
521 521 self.opener = None
522 522 self.files = {}
523 523 self.created = 0
524 524 self.maxsize = maxsize
525 525 if self.maxsize is None:
526 526 self.maxsize = 4*(2**20)
527 527 self.size = 0
528 528 self.data = {}
529 529
530 530 def setfile(self, fname, data, mode, copied=None):
531 531 if self.maxsize < 0 or (len(data) + self.size) <= self.maxsize:
532 532 self.data[fname] = (data, mode, copied)
533 533 self.size += len(data)
534 534 else:
535 535 if self.opener is None:
536 536 root = tempfile.mkdtemp(prefix='hg-patch-')
537 537 self.opener = scmutil.opener(root)
538 538 # Avoid filename issues with these simple names
539 539 fn = str(self.created)
540 540 self.opener.write(fn, data)
541 541 self.created += 1
542 542 self.files[fname] = (fn, mode, copied)
543 543
544 544 def getfile(self, fname):
545 545 if fname in self.data:
546 546 return self.data[fname]
547 547 if not self.opener or fname not in self.files:
548 548 return None, None, None
549 549 fn, mode, copied = self.files[fname]
550 550 return self.opener.read(fn), mode, copied
551 551
552 552 def close(self):
553 553 if self.opener:
554 554 shutil.rmtree(self.opener.base)
555 555
556 556 class repobackend(abstractbackend):
557 557 def __init__(self, ui, repo, ctx, store):
558 558 super(repobackend, self).__init__(ui)
559 559 self.repo = repo
560 560 self.ctx = ctx
561 561 self.store = store
562 562 self.changed = set()
563 563 self.removed = set()
564 564 self.copied = {}
565 565
566 566 def _checkknown(self, fname):
567 567 if fname not in self.ctx:
568 568 raise PatchError(_('cannot patch %s: file is not tracked') % fname)
569 569
570 570 def getfile(self, fname):
571 571 try:
572 572 fctx = self.ctx[fname]
573 573 except error.LookupError:
574 574 return None, None
575 575 flags = fctx.flags()
576 576 return fctx.data(), ('l' in flags, 'x' in flags)
577 577
578 578 def setfile(self, fname, data, mode, copysource):
579 579 if copysource:
580 580 self._checkknown(copysource)
581 581 if data is None:
582 582 data = self.ctx[fname].data()
583 583 self.store.setfile(fname, data, mode, copysource)
584 584 self.changed.add(fname)
585 585 if copysource:
586 586 self.copied[fname] = copysource
587 587
588 588 def unlink(self, fname):
589 589 self._checkknown(fname)
590 590 self.removed.add(fname)
591 591
592 592 def exists(self, fname):
593 593 return fname in self.ctx
594 594
595 595 def close(self):
596 596 return self.changed | self.removed
597 597
598 598 # @@ -start,len +start,len @@ or @@ -start +start @@ if len is 1
599 599 unidesc = re.compile('@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@')
600 600 contextdesc = re.compile('(?:---|\*\*\*) (\d+)(?:,(\d+))? (?:---|\*\*\*)')
601 601 eolmodes = ['strict', 'crlf', 'lf', 'auto']
602 602
603 603 class patchfile(object):
604 604 def __init__(self, ui, gp, backend, store, eolmode='strict'):
605 605 self.fname = gp.path
606 606 self.eolmode = eolmode
607 607 self.eol = None
608 608 self.backend = backend
609 609 self.ui = ui
610 610 self.lines = []
611 611 self.exists = False
612 612 self.missing = True
613 613 self.mode = gp.mode
614 614 self.copysource = gp.oldpath
615 615 self.create = gp.op in ('ADD', 'COPY', 'RENAME')
616 616 self.remove = gp.op == 'DELETE'
617 617 if self.copysource is None:
618 618 data, mode = backend.getfile(self.fname)
619 619 else:
620 620 data, mode = store.getfile(self.copysource)[:2]
621 621 if data is not None:
622 622 self.exists = self.copysource is None or backend.exists(self.fname)
623 623 self.missing = False
624 624 if data:
625 625 self.lines = mdiff.splitnewlines(data)
626 626 if self.mode is None:
627 627 self.mode = mode
628 628 if self.lines:
629 629 # Normalize line endings
630 630 if self.lines[0].endswith('\r\n'):
631 631 self.eol = '\r\n'
632 632 elif self.lines[0].endswith('\n'):
633 633 self.eol = '\n'
634 634 if eolmode != 'strict':
635 635 nlines = []
636 636 for l in self.lines:
637 637 if l.endswith('\r\n'):
638 638 l = l[:-2] + '\n'
639 639 nlines.append(l)
640 640 self.lines = nlines
641 641 else:
642 642 if self.create:
643 643 self.missing = False
644 644 if self.mode is None:
645 645 self.mode = (False, False)
646 646 if self.missing:
647 647 self.ui.warn(_("unable to find '%s' for patching\n") % self.fname)
648 648
649 649 self.hash = {}
650 650 self.dirty = 0
651 651 self.offset = 0
652 652 self.skew = 0
653 653 self.rej = []
654 654 self.fileprinted = False
655 655 self.printfile(False)
656 656 self.hunks = 0
657 657
658 658 def writelines(self, fname, lines, mode):
659 659 if self.eolmode == 'auto':
660 660 eol = self.eol
661 661 elif self.eolmode == 'crlf':
662 662 eol = '\r\n'
663 663 else:
664 664 eol = '\n'
665 665
666 666 if self.eolmode != 'strict' and eol and eol != '\n':
667 667 rawlines = []
668 668 for l in lines:
669 669 if l and l[-1] == '\n':
670 670 l = l[:-1] + eol
671 671 rawlines.append(l)
672 672 lines = rawlines
673 673
674 674 self.backend.setfile(fname, ''.join(lines), mode, self.copysource)
675 675
676 676 def printfile(self, warn):
677 677 if self.fileprinted:
678 678 return
679 679 if warn or self.ui.verbose:
680 680 self.fileprinted = True
681 681 s = _("patching file %s\n") % self.fname
682 682 if warn:
683 683 self.ui.warn(s)
684 684 else:
685 685 self.ui.note(s)
686 686
687 687
688 688 def findlines(self, l, linenum):
689 689 # looks through the hash and finds candidate lines. The
690 690 # result is a list of line numbers sorted based on distance
691 691 # from linenum
692 692
693 693 cand = self.hash.get(l, [])
694 694 if len(cand) > 1:
695 695 # resort our list of potentials forward then back.
696 696 cand.sort(key=lambda x: abs(x - linenum))
697 697 return cand
698 698
699 699 def write_rej(self):
700 700 # our rejects are a little different from patch(1). This always
701 701 # creates rejects in the same form as the original patch. A file
702 702 # header is inserted so that you can run the reject through patch again
703 703 # without having to type the filename.
704 704 if not self.rej:
705 705 return
706 706 base = os.path.basename(self.fname)
707 707 lines = ["--- %s\n+++ %s\n" % (base, base)]
708 708 for x in self.rej:
709 709 for l in x.hunk:
710 710 lines.append(l)
711 711 if l[-1] != '\n':
712 712 lines.append("\n\ No newline at end of file\n")
713 713 self.backend.writerej(self.fname, len(self.rej), self.hunks, lines)
714 714
715 715 def apply(self, h):
716 716 if not h.complete():
717 717 raise PatchError(_("bad hunk #%d %s (%d %d %d %d)") %
718 718 (h.number, h.desc, len(h.a), h.lena, len(h.b),
719 719 h.lenb))
720 720
721 721 self.hunks += 1
722 722
723 723 if self.missing:
724 724 self.rej.append(h)
725 725 return -1
726 726
727 727 if self.exists and self.create:
728 728 if self.copysource:
729 729 self.ui.warn(_("cannot create %s: destination already "
730 730 "exists\n") % self.fname)
731 731 else:
732 732 self.ui.warn(_("file %s already exists\n") % self.fname)
733 733 self.rej.append(h)
734 734 return -1
735 735
736 736 if isinstance(h, binhunk):
737 737 if self.remove:
738 738 self.backend.unlink(self.fname)
739 739 else:
740 740 l = h.new(self.lines)
741 741 self.lines[:] = l
742 742 self.offset += len(l)
743 743 self.dirty = True
744 744 return 0
745 745
746 746 horig = h
747 747 if (self.eolmode in ('crlf', 'lf')
748 748 or self.eolmode == 'auto' and self.eol):
749 749 # If new eols are going to be normalized, then normalize
750 750 # hunk data before patching. Otherwise, preserve input
751 751 # line-endings.
752 752 h = h.getnormalized()
753 753
754 754 # fast case first, no offsets, no fuzz
755 755 old, oldstart, new, newstart = h.fuzzit(0, False)
756 756 oldstart += self.offset
757 757 orig_start = oldstart
758 758 # if there's skew we want to emit the "(offset %d lines)" even
759 759 # when the hunk cleanly applies at start + skew, so skip the
760 760 # fast case code
761 761 if (self.skew == 0 and
762 762 diffhelpers.testhunk(old, self.lines, oldstart) == 0):
763 763 if self.remove:
764 764 self.backend.unlink(self.fname)
765 765 else:
766 766 self.lines[oldstart:oldstart + len(old)] = new
767 767 self.offset += len(new) - len(old)
768 768 self.dirty = True
769 769 return 0
770 770
771 771 # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
772 772 self.hash = {}
773 773 for x, s in enumerate(self.lines):
774 774 self.hash.setdefault(s, []).append(x)
775 775
776 for fuzzlen in xrange(3):
776 for fuzzlen in xrange(self.ui.configint("patch", "fuzz", 2) + 1):
777 777 for toponly in [True, False]:
778 778 old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly)
779 779 oldstart = oldstart + self.offset + self.skew
780 780 oldstart = min(oldstart, len(self.lines))
781 781 if old:
782 782 cand = self.findlines(old[0][1:], oldstart)
783 783 else:
784 784 # Only adding lines with no or fuzzed context, just
785 785 # take the skew in account
786 786 cand = [oldstart]
787 787
788 788 for l in cand:
789 789 if not old or diffhelpers.testhunk(old, self.lines, l) == 0:
790 790 self.lines[l : l + len(old)] = new
791 791 self.offset += len(new) - len(old)
792 792 self.skew = l - orig_start
793 793 self.dirty = True
794 794 offset = l - orig_start - fuzzlen
795 795 if fuzzlen:
796 796 msg = _("Hunk #%d succeeded at %d "
797 797 "with fuzz %d "
798 798 "(offset %d lines).\n")
799 799 self.printfile(True)
800 800 self.ui.warn(msg %
801 801 (h.number, l + 1, fuzzlen, offset))
802 802 else:
803 803 msg = _("Hunk #%d succeeded at %d "
804 804 "(offset %d lines).\n")
805 805 self.ui.note(msg % (h.number, l + 1, offset))
806 806 return fuzzlen
807 807 self.printfile(True)
808 808 self.ui.warn(_("Hunk #%d FAILED at %d\n") % (h.number, orig_start))
809 809 self.rej.append(horig)
810 810 return -1
811 811
812 812 def close(self):
813 813 if self.dirty:
814 814 self.writelines(self.fname, self.lines, self.mode)
815 815 self.write_rej()
816 816 return len(self.rej)
817 817
818 818 class header(object):
819 819 """patch header
820 820 """
821 821 diffgit_re = re.compile('diff --git a/(.*) b/(.*)$')
822 822 diff_re = re.compile('diff -r .* (.*)$')
823 823 allhunks_re = re.compile('(?:index|deleted file) ')
824 824 pretty_re = re.compile('(?:new file|deleted file) ')
825 825 special_re = re.compile('(?:index|deleted|copy|rename) ')
826 826 newfile_re = re.compile('(?:new file)')
827 827
828 828 def __init__(self, header):
829 829 self.header = header
830 830 self.hunks = []
831 831
832 832 def binary(self):
833 833 return any(h.startswith('index ') for h in self.header)
834 834
835 835 def pretty(self, fp):
836 836 for h in self.header:
837 837 if h.startswith('index '):
838 838 fp.write(_('this modifies a binary file (all or nothing)\n'))
839 839 break
840 840 if self.pretty_re.match(h):
841 841 fp.write(h)
842 842 if self.binary():
843 843 fp.write(_('this is a binary file\n'))
844 844 break
845 845 if h.startswith('---'):
846 846 fp.write(_('%d hunks, %d lines changed\n') %
847 847 (len(self.hunks),
848 848 sum([max(h.added, h.removed) for h in self.hunks])))
849 849 break
850 850 fp.write(h)
851 851
852 852 def write(self, fp):
853 853 fp.write(''.join(self.header))
854 854
855 855 def allhunks(self):
856 856 return any(self.allhunks_re.match(h) for h in self.header)
857 857
858 858 def files(self):
859 859 match = self.diffgit_re.match(self.header[0])
860 860 if match:
861 861 fromfile, tofile = match.groups()
862 862 if fromfile == tofile:
863 863 return [fromfile]
864 864 return [fromfile, tofile]
865 865 else:
866 866 return self.diff_re.match(self.header[0]).groups()
867 867
868 868 def filename(self):
869 869 return self.files()[-1]
870 870
871 871 def __repr__(self):
872 872 return '<header %s>' % (' '.join(map(repr, self.files())))
873 873
874 874 def isnewfile(self):
875 875 return any(self.newfile_re.match(h) for h in self.header)
876 876
877 877 def special(self):
878 878 # Special files are shown only at the header level and not at the hunk
879 879 # level for example a file that has been deleted is a special file.
880 880 # The user cannot change the content of the operation, in the case of
881 881 # the deleted file he has to take the deletion or not take it, he
882 882 # cannot take some of it.
883 883 # Newly added files are special if they are empty, they are not special
884 884 # if they have some content as we want to be able to change it
885 885 nocontent = len(self.header) == 2
886 886 emptynewfile = self.isnewfile() and nocontent
887 887 return emptynewfile or \
888 888 any(self.special_re.match(h) for h in self.header)
889 889
890 890 class recordhunk(object):
891 891 """patch hunk
892 892
893 893 XXX shouldn't we merge this with the other hunk class?
894 894 """
895 895 maxcontext = 3
896 896
897 897 def __init__(self, header, fromline, toline, proc, before, hunk, after):
898 898 def trimcontext(number, lines):
899 899 delta = len(lines) - self.maxcontext
900 900 if False and delta > 0:
901 901 return number + delta, lines[:self.maxcontext]
902 902 return number, lines
903 903
904 904 self.header = header
905 905 self.fromline, self.before = trimcontext(fromline, before)
906 906 self.toline, self.after = trimcontext(toline, after)
907 907 self.proc = proc
908 908 self.hunk = hunk
909 909 self.added, self.removed = self.countchanges(self.hunk)
910 910
911 911 def __eq__(self, v):
912 912 if not isinstance(v, recordhunk):
913 913 return False
914 914
915 915 return ((v.hunk == self.hunk) and
916 916 (v.proc == self.proc) and
917 917 (self.fromline == v.fromline) and
918 918 (self.header.files() == v.header.files()))
919 919
920 920 def __hash__(self):
921 921 return hash((tuple(self.hunk),
922 922 tuple(self.header.files()),
923 923 self.fromline,
924 924 self.proc))
925 925
926 926 def countchanges(self, hunk):
927 927 """hunk -> (n+,n-)"""
928 928 add = len([h for h in hunk if h[0] == '+'])
929 929 rem = len([h for h in hunk if h[0] == '-'])
930 930 return add, rem
931 931
932 932 def write(self, fp):
933 933 delta = len(self.before) + len(self.after)
934 934 if self.after and self.after[-1] == '\\ No newline at end of file\n':
935 935 delta -= 1
936 936 fromlen = delta + self.removed
937 937 tolen = delta + self.added
938 938 fp.write('@@ -%d,%d +%d,%d @@%s\n' %
939 939 (self.fromline, fromlen, self.toline, tolen,
940 940 self.proc and (' ' + self.proc)))
941 941 fp.write(''.join(self.before + self.hunk + self.after))
942 942
943 943 pretty = write
944 944
945 945 def filename(self):
946 946 return self.header.filename()
947 947
948 948 def __repr__(self):
949 949 return '<hunk %r@%d>' % (self.filename(), self.fromline)
950 950
951 951 def filterpatch(ui, headers, operation=None):
952 952 """Interactively filter patch chunks into applied-only chunks"""
953 953 if operation is None:
954 954 operation = _('record')
955 955
956 956 def prompt(skipfile, skipall, query, chunk):
957 957 """prompt query, and process base inputs
958 958
959 959 - y/n for the rest of file
960 960 - y/n for the rest
961 961 - ? (help)
962 962 - q (quit)
963 963
964 964 Return True/False and possibly updated skipfile and skipall.
965 965 """
966 966 newpatches = None
967 967 if skipall is not None:
968 968 return skipall, skipfile, skipall, newpatches
969 969 if skipfile is not None:
970 970 return skipfile, skipfile, skipall, newpatches
971 971 while True:
972 972 resps = _('[Ynesfdaq?]'
973 973 '$$ &Yes, record this change'
974 974 '$$ &No, skip this change'
975 975 '$$ &Edit this change manually'
976 976 '$$ &Skip remaining changes to this file'
977 977 '$$ Record remaining changes to this &file'
978 978 '$$ &Done, skip remaining changes and files'
979 979 '$$ Record &all changes to all remaining files'
980 980 '$$ &Quit, recording no changes'
981 981 '$$ &? (display help)')
982 982 r = ui.promptchoice("%s %s" % (query, resps))
983 983 ui.write("\n")
984 984 if r == 8: # ?
985 985 for c, t in ui.extractchoices(resps)[1]:
986 986 ui.write('%s - %s\n' % (c, t.lower()))
987 987 continue
988 988 elif r == 0: # yes
989 989 ret = True
990 990 elif r == 1: # no
991 991 ret = False
992 992 elif r == 2: # Edit patch
993 993 if chunk is None:
994 994 ui.write(_('cannot edit patch for whole file'))
995 995 ui.write("\n")
996 996 continue
997 997 if chunk.header.binary():
998 998 ui.write(_('cannot edit patch for binary file'))
999 999 ui.write("\n")
1000 1000 continue
1001 1001 # Patch comment based on the Git one (based on comment at end of
1002 1002 # http://mercurial.selenic.com/wiki/RecordExtension)
1003 1003 phelp = '---' + _("""
1004 1004 To remove '-' lines, make them ' ' lines (context).
1005 1005 To remove '+' lines, delete them.
1006 1006 Lines starting with # will be removed from the patch.
1007 1007
1008 1008 If the patch applies cleanly, the edited hunk will immediately be
1009 1009 added to the record list. If it does not apply cleanly, a rejects
1010 1010 file will be generated: you can use that when you try again. If
1011 1011 all lines of the hunk are removed, then the edit is aborted and
1012 1012 the hunk is left unchanged.
1013 1013 """)
1014 1014 (patchfd, patchfn) = tempfile.mkstemp(prefix="hg-editor-",
1015 1015 suffix=".diff", text=True)
1016 1016 ncpatchfp = None
1017 1017 try:
1018 1018 # Write the initial patch
1019 1019 f = os.fdopen(patchfd, "w")
1020 1020 chunk.header.write(f)
1021 1021 chunk.write(f)
1022 1022 f.write('\n'.join(['# ' + i for i in phelp.splitlines()]))
1023 1023 f.close()
1024 1024 # Start the editor and wait for it to complete
1025 1025 editor = ui.geteditor()
1026 1026 ret = ui.system("%s \"%s\"" % (editor, patchfn),
1027 1027 environ={'HGUSER': ui.username()})
1028 1028 if ret != 0:
1029 1029 ui.warn(_("editor exited with exit code %d\n") % ret)
1030 1030 continue
1031 1031 # Remove comment lines
1032 1032 patchfp = open(patchfn)
1033 1033 ncpatchfp = cStringIO.StringIO()
1034 1034 for line in patchfp:
1035 1035 if not line.startswith('#'):
1036 1036 ncpatchfp.write(line)
1037 1037 patchfp.close()
1038 1038 ncpatchfp.seek(0)
1039 1039 newpatches = parsepatch(ncpatchfp)
1040 1040 finally:
1041 1041 os.unlink(patchfn)
1042 1042 del ncpatchfp
1043 1043 # Signal that the chunk shouldn't be applied as-is, but
1044 1044 # provide the new patch to be used instead.
1045 1045 ret = False
1046 1046 elif r == 3: # Skip
1047 1047 ret = skipfile = False
1048 1048 elif r == 4: # file (Record remaining)
1049 1049 ret = skipfile = True
1050 1050 elif r == 5: # done, skip remaining
1051 1051 ret = skipall = False
1052 1052 elif r == 6: # all
1053 1053 ret = skipall = True
1054 1054 elif r == 7: # quit
1055 1055 raise util.Abort(_('user quit'))
1056 1056 return ret, skipfile, skipall, newpatches
1057 1057
1058 1058 seen = set()
1059 1059 applied = {} # 'filename' -> [] of chunks
1060 1060 skipfile, skipall = None, None
1061 1061 pos, total = 1, sum(len(h.hunks) for h in headers)
1062 1062 for h in headers:
1063 1063 pos += len(h.hunks)
1064 1064 skipfile = None
1065 1065 fixoffset = 0
1066 1066 hdr = ''.join(h.header)
1067 1067 if hdr in seen:
1068 1068 continue
1069 1069 seen.add(hdr)
1070 1070 if skipall is None:
1071 1071 h.pretty(ui)
1072 1072 msg = (_('examine changes to %s?') %
1073 1073 _(' and ').join("'%s'" % f for f in h.files()))
1074 1074 r, skipfile, skipall, np = prompt(skipfile, skipall, msg, None)
1075 1075 if not r:
1076 1076 continue
1077 1077 applied[h.filename()] = [h]
1078 1078 if h.allhunks():
1079 1079 applied[h.filename()] += h.hunks
1080 1080 continue
1081 1081 for i, chunk in enumerate(h.hunks):
1082 1082 if skipfile is None and skipall is None:
1083 1083 chunk.pretty(ui)
1084 1084 if total == 1:
1085 1085 msg = _("record this change to '%s'?") % chunk.filename()
1086 1086 else:
1087 1087 idx = pos - len(h.hunks) + i
1088 1088 msg = _("record change %d/%d to '%s'?") % (idx, total,
1089 1089 chunk.filename())
1090 1090 r, skipfile, skipall, newpatches = prompt(skipfile,
1091 1091 skipall, msg, chunk)
1092 1092 if r:
1093 1093 if fixoffset:
1094 1094 chunk = copy.copy(chunk)
1095 1095 chunk.toline += fixoffset
1096 1096 applied[chunk.filename()].append(chunk)
1097 1097 elif newpatches is not None:
1098 1098 for newpatch in newpatches:
1099 1099 for newhunk in newpatch.hunks:
1100 1100 if fixoffset:
1101 1101 newhunk.toline += fixoffset
1102 1102 applied[newhunk.filename()].append(newhunk)
1103 1103 else:
1104 1104 fixoffset += chunk.removed - chunk.added
1105 1105 return sum([h for h in applied.itervalues()
1106 1106 if h[0].special() or len(h) > 1], [])
1107 1107 class hunk(object):
1108 1108 def __init__(self, desc, num, lr, context):
1109 1109 self.number = num
1110 1110 self.desc = desc
1111 1111 self.hunk = [desc]
1112 1112 self.a = []
1113 1113 self.b = []
1114 1114 self.starta = self.lena = None
1115 1115 self.startb = self.lenb = None
1116 1116 if lr is not None:
1117 1117 if context:
1118 1118 self.read_context_hunk(lr)
1119 1119 else:
1120 1120 self.read_unified_hunk(lr)
1121 1121
1122 1122 def getnormalized(self):
1123 1123 """Return a copy with line endings normalized to LF."""
1124 1124
1125 1125 def normalize(lines):
1126 1126 nlines = []
1127 1127 for line in lines:
1128 1128 if line.endswith('\r\n'):
1129 1129 line = line[:-2] + '\n'
1130 1130 nlines.append(line)
1131 1131 return nlines
1132 1132
1133 1133 # Dummy object, it is rebuilt manually
1134 1134 nh = hunk(self.desc, self.number, None, None)
1135 1135 nh.number = self.number
1136 1136 nh.desc = self.desc
1137 1137 nh.hunk = self.hunk
1138 1138 nh.a = normalize(self.a)
1139 1139 nh.b = normalize(self.b)
1140 1140 nh.starta = self.starta
1141 1141 nh.startb = self.startb
1142 1142 nh.lena = self.lena
1143 1143 nh.lenb = self.lenb
1144 1144 return nh
1145 1145
1146 1146 def read_unified_hunk(self, lr):
1147 1147 m = unidesc.match(self.desc)
1148 1148 if not m:
1149 1149 raise PatchError(_("bad hunk #%d") % self.number)
1150 1150 self.starta, self.lena, self.startb, self.lenb = m.groups()
1151 1151 if self.lena is None:
1152 1152 self.lena = 1
1153 1153 else:
1154 1154 self.lena = int(self.lena)
1155 1155 if self.lenb is None:
1156 1156 self.lenb = 1
1157 1157 else:
1158 1158 self.lenb = int(self.lenb)
1159 1159 self.starta = int(self.starta)
1160 1160 self.startb = int(self.startb)
1161 1161 diffhelpers.addlines(lr, self.hunk, self.lena, self.lenb, self.a,
1162 1162 self.b)
1163 1163 # if we hit eof before finishing out the hunk, the last line will
1164 1164 # be zero length. Lets try to fix it up.
1165 1165 while len(self.hunk[-1]) == 0:
1166 1166 del self.hunk[-1]
1167 1167 del self.a[-1]
1168 1168 del self.b[-1]
1169 1169 self.lena -= 1
1170 1170 self.lenb -= 1
1171 1171 self._fixnewline(lr)
1172 1172
1173 1173 def read_context_hunk(self, lr):
1174 1174 self.desc = lr.readline()
1175 1175 m = contextdesc.match(self.desc)
1176 1176 if not m:
1177 1177 raise PatchError(_("bad hunk #%d") % self.number)
1178 1178 self.starta, aend = m.groups()
1179 1179 self.starta = int(self.starta)
1180 1180 if aend is None:
1181 1181 aend = self.starta
1182 1182 self.lena = int(aend) - self.starta
1183 1183 if self.starta:
1184 1184 self.lena += 1
1185 1185 for x in xrange(self.lena):
1186 1186 l = lr.readline()
1187 1187 if l.startswith('---'):
1188 1188 # lines addition, old block is empty
1189 1189 lr.push(l)
1190 1190 break
1191 1191 s = l[2:]
1192 1192 if l.startswith('- ') or l.startswith('! '):
1193 1193 u = '-' + s
1194 1194 elif l.startswith(' '):
1195 1195 u = ' ' + s
1196 1196 else:
1197 1197 raise PatchError(_("bad hunk #%d old text line %d") %
1198 1198 (self.number, x))
1199 1199 self.a.append(u)
1200 1200 self.hunk.append(u)
1201 1201
1202 1202 l = lr.readline()
1203 1203 if l.startswith('\ '):
1204 1204 s = self.a[-1][:-1]
1205 1205 self.a[-1] = s
1206 1206 self.hunk[-1] = s
1207 1207 l = lr.readline()
1208 1208 m = contextdesc.match(l)
1209 1209 if not m:
1210 1210 raise PatchError(_("bad hunk #%d") % self.number)
1211 1211 self.startb, bend = m.groups()
1212 1212 self.startb = int(self.startb)
1213 1213 if bend is None:
1214 1214 bend = self.startb
1215 1215 self.lenb = int(bend) - self.startb
1216 1216 if self.startb:
1217 1217 self.lenb += 1
1218 1218 hunki = 1
1219 1219 for x in xrange(self.lenb):
1220 1220 l = lr.readline()
1221 1221 if l.startswith('\ '):
1222 1222 # XXX: the only way to hit this is with an invalid line range.
1223 1223 # The no-eol marker is not counted in the line range, but I
1224 1224 # guess there are diff(1) out there which behave differently.
1225 1225 s = self.b[-1][:-1]
1226 1226 self.b[-1] = s
1227 1227 self.hunk[hunki - 1] = s
1228 1228 continue
1229 1229 if not l:
1230 1230 # line deletions, new block is empty and we hit EOF
1231 1231 lr.push(l)
1232 1232 break
1233 1233 s = l[2:]
1234 1234 if l.startswith('+ ') or l.startswith('! '):
1235 1235 u = '+' + s
1236 1236 elif l.startswith(' '):
1237 1237 u = ' ' + s
1238 1238 elif len(self.b) == 0:
1239 1239 # line deletions, new block is empty
1240 1240 lr.push(l)
1241 1241 break
1242 1242 else:
1243 1243 raise PatchError(_("bad hunk #%d old text line %d") %
1244 1244 (self.number, x))
1245 1245 self.b.append(s)
1246 1246 while True:
1247 1247 if hunki >= len(self.hunk):
1248 1248 h = ""
1249 1249 else:
1250 1250 h = self.hunk[hunki]
1251 1251 hunki += 1
1252 1252 if h == u:
1253 1253 break
1254 1254 elif h.startswith('-'):
1255 1255 continue
1256 1256 else:
1257 1257 self.hunk.insert(hunki - 1, u)
1258 1258 break
1259 1259
1260 1260 if not self.a:
1261 1261 # this happens when lines were only added to the hunk
1262 1262 for x in self.hunk:
1263 1263 if x.startswith('-') or x.startswith(' '):
1264 1264 self.a.append(x)
1265 1265 if not self.b:
1266 1266 # this happens when lines were only deleted from the hunk
1267 1267 for x in self.hunk:
1268 1268 if x.startswith('+') or x.startswith(' '):
1269 1269 self.b.append(x[1:])
1270 1270 # @@ -start,len +start,len @@
1271 1271 self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
1272 1272 self.startb, self.lenb)
1273 1273 self.hunk[0] = self.desc
1274 1274 self._fixnewline(lr)
1275 1275
1276 1276 def _fixnewline(self, lr):
1277 1277 l = lr.readline()
1278 1278 if l.startswith('\ '):
1279 1279 diffhelpers.fix_newline(self.hunk, self.a, self.b)
1280 1280 else:
1281 1281 lr.push(l)
1282 1282
1283 1283 def complete(self):
1284 1284 return len(self.a) == self.lena and len(self.b) == self.lenb
1285 1285
1286 1286 def _fuzzit(self, old, new, fuzz, toponly):
1287 1287 # this removes context lines from the top and bottom of list 'l'. It
1288 1288 # checks the hunk to make sure only context lines are removed, and then
1289 1289 # returns a new shortened list of lines.
1290 1290 fuzz = min(fuzz, len(old))
1291 1291 if fuzz:
1292 1292 top = 0
1293 1293 bot = 0
1294 1294 hlen = len(self.hunk)
1295 1295 for x in xrange(hlen - 1):
1296 1296 # the hunk starts with the @@ line, so use x+1
1297 1297 if self.hunk[x + 1][0] == ' ':
1298 1298 top += 1
1299 1299 else:
1300 1300 break
1301 1301 if not toponly:
1302 1302 for x in xrange(hlen - 1):
1303 1303 if self.hunk[hlen - bot - 1][0] == ' ':
1304 1304 bot += 1
1305 1305 else:
1306 1306 break
1307 1307
1308 1308 bot = min(fuzz, bot)
1309 1309 top = min(fuzz, top)
1310 1310 return old[top:len(old) - bot], new[top:len(new) - bot], top
1311 1311 return old, new, 0
1312 1312
1313 1313 def fuzzit(self, fuzz, toponly):
1314 1314 old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly)
1315 1315 oldstart = self.starta + top
1316 1316 newstart = self.startb + top
1317 1317 # zero length hunk ranges already have their start decremented
1318 1318 if self.lena and oldstart > 0:
1319 1319 oldstart -= 1
1320 1320 if self.lenb and newstart > 0:
1321 1321 newstart -= 1
1322 1322 return old, oldstart, new, newstart
1323 1323
1324 1324 class binhunk(object):
1325 1325 'A binary patch file.'
1326 1326 def __init__(self, lr, fname):
1327 1327 self.text = None
1328 1328 self.delta = False
1329 1329 self.hunk = ['GIT binary patch\n']
1330 1330 self._fname = fname
1331 1331 self._read(lr)
1332 1332
1333 1333 def complete(self):
1334 1334 return self.text is not None
1335 1335
1336 1336 def new(self, lines):
1337 1337 if self.delta:
1338 1338 return [applybindelta(self.text, ''.join(lines))]
1339 1339 return [self.text]
1340 1340
1341 1341 def _read(self, lr):
1342 1342 def getline(lr, hunk):
1343 1343 l = lr.readline()
1344 1344 hunk.append(l)
1345 1345 return l.rstrip('\r\n')
1346 1346
1347 1347 size = 0
1348 1348 while True:
1349 1349 line = getline(lr, self.hunk)
1350 1350 if not line:
1351 1351 raise PatchError(_('could not extract "%s" binary data')
1352 1352 % self._fname)
1353 1353 if line.startswith('literal '):
1354 1354 size = int(line[8:].rstrip())
1355 1355 break
1356 1356 if line.startswith('delta '):
1357 1357 size = int(line[6:].rstrip())
1358 1358 self.delta = True
1359 1359 break
1360 1360 dec = []
1361 1361 line = getline(lr, self.hunk)
1362 1362 while len(line) > 1:
1363 1363 l = line[0]
1364 1364 if l <= 'Z' and l >= 'A':
1365 1365 l = ord(l) - ord('A') + 1
1366 1366 else:
1367 1367 l = ord(l) - ord('a') + 27
1368 1368 try:
1369 1369 dec.append(base85.b85decode(line[1:])[:l])
1370 1370 except ValueError, e:
1371 1371 raise PatchError(_('could not decode "%s" binary patch: %s')
1372 1372 % (self._fname, str(e)))
1373 1373 line = getline(lr, self.hunk)
1374 1374 text = zlib.decompress(''.join(dec))
1375 1375 if len(text) != size:
1376 1376 raise PatchError(_('"%s" length is %d bytes, should be %d')
1377 1377 % (self._fname, len(text), size))
1378 1378 self.text = text
1379 1379
1380 1380 def parsefilename(str):
1381 1381 # --- filename \t|space stuff
1382 1382 s = str[4:].rstrip('\r\n')
1383 1383 i = s.find('\t')
1384 1384 if i < 0:
1385 1385 i = s.find(' ')
1386 1386 if i < 0:
1387 1387 return s
1388 1388 return s[:i]
1389 1389
1390 1390 def reversehunks(hunks):
1391 1391 '''reverse the signs in the hunks given as argument
1392 1392
1393 1393 This function operates on hunks coming out of patch.filterpatch, that is
1394 1394 a list of the form: [header1, hunk1, hunk2, header2...]. Example usage:
1395 1395
1396 1396 >>> rawpatch = """diff --git a/folder1/g b/folder1/g
1397 1397 ... --- a/folder1/g
1398 1398 ... +++ b/folder1/g
1399 1399 ... @@ -1,7 +1,7 @@
1400 1400 ... +firstline
1401 1401 ... c
1402 1402 ... 1
1403 1403 ... 2
1404 1404 ... + 3
1405 1405 ... -4
1406 1406 ... 5
1407 1407 ... d
1408 1408 ... +lastline"""
1409 1409 >>> hunks = parsepatch(rawpatch)
1410 1410 >>> hunkscomingfromfilterpatch = []
1411 1411 >>> for h in hunks:
1412 1412 ... hunkscomingfromfilterpatch.append(h)
1413 1413 ... hunkscomingfromfilterpatch.extend(h.hunks)
1414 1414
1415 1415 >>> reversedhunks = reversehunks(hunkscomingfromfilterpatch)
1416 1416 >>> fp = cStringIO.StringIO()
1417 1417 >>> for c in reversedhunks:
1418 1418 ... c.write(fp)
1419 1419 >>> fp.seek(0)
1420 1420 >>> reversedpatch = fp.read()
1421 1421 >>> print reversedpatch
1422 1422 diff --git a/folder1/g b/folder1/g
1423 1423 --- a/folder1/g
1424 1424 +++ b/folder1/g
1425 1425 @@ -1,4 +1,3 @@
1426 1426 -firstline
1427 1427 c
1428 1428 1
1429 1429 2
1430 1430 @@ -1,6 +2,6 @@
1431 1431 c
1432 1432 1
1433 1433 2
1434 1434 - 3
1435 1435 +4
1436 1436 5
1437 1437 d
1438 1438 @@ -5,3 +6,2 @@
1439 1439 5
1440 1440 d
1441 1441 -lastline
1442 1442
1443 1443 '''
1444 1444
1445 1445 import crecord as crecordmod
1446 1446 newhunks = []
1447 1447 for c in hunks:
1448 1448 if isinstance(c, crecordmod.uihunk):
1449 1449 # curses hunks encapsulate the record hunk in _hunk
1450 1450 c = c._hunk
1451 1451 if isinstance(c, recordhunk):
1452 1452 for j, line in enumerate(c.hunk):
1453 1453 if line.startswith("-"):
1454 1454 c.hunk[j] = "+" + c.hunk[j][1:]
1455 1455 elif line.startswith("+"):
1456 1456 c.hunk[j] = "-" + c.hunk[j][1:]
1457 1457 c.added, c.removed = c.removed, c.added
1458 1458 newhunks.append(c)
1459 1459 return newhunks
1460 1460
1461 1461 def parsepatch(originalchunks):
1462 1462 """patch -> [] of headers -> [] of hunks """
1463 1463 class parser(object):
1464 1464 """patch parsing state machine"""
1465 1465 def __init__(self):
1466 1466 self.fromline = 0
1467 1467 self.toline = 0
1468 1468 self.proc = ''
1469 1469 self.header = None
1470 1470 self.context = []
1471 1471 self.before = []
1472 1472 self.hunk = []
1473 1473 self.headers = []
1474 1474
1475 1475 def addrange(self, limits):
1476 1476 fromstart, fromend, tostart, toend, proc = limits
1477 1477 self.fromline = int(fromstart)
1478 1478 self.toline = int(tostart)
1479 1479 self.proc = proc
1480 1480
1481 1481 def addcontext(self, context):
1482 1482 if self.hunk:
1483 1483 h = recordhunk(self.header, self.fromline, self.toline,
1484 1484 self.proc, self.before, self.hunk, context)
1485 1485 self.header.hunks.append(h)
1486 1486 self.fromline += len(self.before) + h.removed
1487 1487 self.toline += len(self.before) + h.added
1488 1488 self.before = []
1489 1489 self.hunk = []
1490 1490 self.proc = ''
1491 1491 self.context = context
1492 1492
1493 1493 def addhunk(self, hunk):
1494 1494 if self.context:
1495 1495 self.before = self.context
1496 1496 self.context = []
1497 1497 self.hunk = hunk
1498 1498
1499 1499 def newfile(self, hdr):
1500 1500 self.addcontext([])
1501 1501 h = header(hdr)
1502 1502 self.headers.append(h)
1503 1503 self.header = h
1504 1504
1505 1505 def addother(self, line):
1506 1506 pass # 'other' lines are ignored
1507 1507
1508 1508 def finished(self):
1509 1509 self.addcontext([])
1510 1510 return self.headers
1511 1511
1512 1512 transitions = {
1513 1513 'file': {'context': addcontext,
1514 1514 'file': newfile,
1515 1515 'hunk': addhunk,
1516 1516 'range': addrange},
1517 1517 'context': {'file': newfile,
1518 1518 'hunk': addhunk,
1519 1519 'range': addrange,
1520 1520 'other': addother},
1521 1521 'hunk': {'context': addcontext,
1522 1522 'file': newfile,
1523 1523 'range': addrange},
1524 1524 'range': {'context': addcontext,
1525 1525 'hunk': addhunk},
1526 1526 'other': {'other': addother},
1527 1527 }
1528 1528
1529 1529 p = parser()
1530 1530 fp = cStringIO.StringIO()
1531 1531 fp.write(''.join(originalchunks))
1532 1532 fp.seek(0)
1533 1533
1534 1534 state = 'context'
1535 1535 for newstate, data in scanpatch(fp):
1536 1536 try:
1537 1537 p.transitions[state][newstate](p, data)
1538 1538 except KeyError:
1539 1539 raise PatchError('unhandled transition: %s -> %s' %
1540 1540 (state, newstate))
1541 1541 state = newstate
1542 1542 del fp
1543 1543 return p.finished()
1544 1544
1545 1545 def pathtransform(path, strip, prefix):
1546 1546 '''turn a path from a patch into a path suitable for the repository
1547 1547
1548 1548 prefix, if not empty, is expected to be normalized with a / at the end.
1549 1549
1550 1550 Returns (stripped components, path in repository).
1551 1551
1552 1552 >>> pathtransform('a/b/c', 0, '')
1553 1553 ('', 'a/b/c')
1554 1554 >>> pathtransform(' a/b/c ', 0, '')
1555 1555 ('', ' a/b/c')
1556 1556 >>> pathtransform(' a/b/c ', 2, '')
1557 1557 ('a/b/', 'c')
1558 1558 >>> pathtransform('a/b/c', 0, 'd/e/')
1559 1559 ('', 'd/e/a/b/c')
1560 1560 >>> pathtransform(' a//b/c ', 2, 'd/e/')
1561 1561 ('a//b/', 'd/e/c')
1562 1562 >>> pathtransform('a/b/c', 3, '')
1563 1563 Traceback (most recent call last):
1564 1564 PatchError: unable to strip away 1 of 3 dirs from a/b/c
1565 1565 '''
1566 1566 pathlen = len(path)
1567 1567 i = 0
1568 1568 if strip == 0:
1569 1569 return '', prefix + path.rstrip()
1570 1570 count = strip
1571 1571 while count > 0:
1572 1572 i = path.find('/', i)
1573 1573 if i == -1:
1574 1574 raise PatchError(_("unable to strip away %d of %d dirs from %s") %
1575 1575 (count, strip, path))
1576 1576 i += 1
1577 1577 # consume '//' in the path
1578 1578 while i < pathlen - 1 and path[i] == '/':
1579 1579 i += 1
1580 1580 count -= 1
1581 1581 return path[:i].lstrip(), prefix + path[i:].rstrip()
1582 1582
1583 1583 def makepatchmeta(backend, afile_orig, bfile_orig, hunk, strip, prefix):
1584 1584 nulla = afile_orig == "/dev/null"
1585 1585 nullb = bfile_orig == "/dev/null"
1586 1586 create = nulla and hunk.starta == 0 and hunk.lena == 0
1587 1587 remove = nullb and hunk.startb == 0 and hunk.lenb == 0
1588 1588 abase, afile = pathtransform(afile_orig, strip, prefix)
1589 1589 gooda = not nulla and backend.exists(afile)
1590 1590 bbase, bfile = pathtransform(bfile_orig, strip, prefix)
1591 1591 if afile == bfile:
1592 1592 goodb = gooda
1593 1593 else:
1594 1594 goodb = not nullb and backend.exists(bfile)
1595 1595 missing = not goodb and not gooda and not create
1596 1596
1597 1597 # some diff programs apparently produce patches where the afile is
1598 1598 # not /dev/null, but afile starts with bfile
1599 1599 abasedir = afile[:afile.rfind('/') + 1]
1600 1600 bbasedir = bfile[:bfile.rfind('/') + 1]
1601 1601 if (missing and abasedir == bbasedir and afile.startswith(bfile)
1602 1602 and hunk.starta == 0 and hunk.lena == 0):
1603 1603 create = True
1604 1604 missing = False
1605 1605
1606 1606 # If afile is "a/b/foo" and bfile is "a/b/foo.orig" we assume the
1607 1607 # diff is between a file and its backup. In this case, the original
1608 1608 # file should be patched (see original mpatch code).
1609 1609 isbackup = (abase == bbase and bfile.startswith(afile))
1610 1610 fname = None
1611 1611 if not missing:
1612 1612 if gooda and goodb:
1613 1613 if isbackup:
1614 1614 fname = afile
1615 1615 else:
1616 1616 fname = bfile
1617 1617 elif gooda:
1618 1618 fname = afile
1619 1619
1620 1620 if not fname:
1621 1621 if not nullb:
1622 1622 if isbackup:
1623 1623 fname = afile
1624 1624 else:
1625 1625 fname = bfile
1626 1626 elif not nulla:
1627 1627 fname = afile
1628 1628 else:
1629 1629 raise PatchError(_("undefined source and destination files"))
1630 1630
1631 1631 gp = patchmeta(fname)
1632 1632 if create:
1633 1633 gp.op = 'ADD'
1634 1634 elif remove:
1635 1635 gp.op = 'DELETE'
1636 1636 return gp
1637 1637
1638 1638 def scanpatch(fp):
1639 1639 """like patch.iterhunks, but yield different events
1640 1640
1641 1641 - ('file', [header_lines + fromfile + tofile])
1642 1642 - ('context', [context_lines])
1643 1643 - ('hunk', [hunk_lines])
1644 1644 - ('range', (-start,len, +start,len, proc))
1645 1645 """
1646 1646 lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)')
1647 1647 lr = linereader(fp)
1648 1648
1649 1649 def scanwhile(first, p):
1650 1650 """scan lr while predicate holds"""
1651 1651 lines = [first]
1652 1652 while True:
1653 1653 line = lr.readline()
1654 1654 if not line:
1655 1655 break
1656 1656 if p(line):
1657 1657 lines.append(line)
1658 1658 else:
1659 1659 lr.push(line)
1660 1660 break
1661 1661 return lines
1662 1662
1663 1663 while True:
1664 1664 line = lr.readline()
1665 1665 if not line:
1666 1666 break
1667 1667 if line.startswith('diff --git a/') or line.startswith('diff -r '):
1668 1668 def notheader(line):
1669 1669 s = line.split(None, 1)
1670 1670 return not s or s[0] not in ('---', 'diff')
1671 1671 header = scanwhile(line, notheader)
1672 1672 fromfile = lr.readline()
1673 1673 if fromfile.startswith('---'):
1674 1674 tofile = lr.readline()
1675 1675 header += [fromfile, tofile]
1676 1676 else:
1677 1677 lr.push(fromfile)
1678 1678 yield 'file', header
1679 1679 elif line[0] == ' ':
1680 1680 yield 'context', scanwhile(line, lambda l: l[0] in ' \\')
1681 1681 elif line[0] in '-+':
1682 1682 yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\')
1683 1683 else:
1684 1684 m = lines_re.match(line)
1685 1685 if m:
1686 1686 yield 'range', m.groups()
1687 1687 else:
1688 1688 yield 'other', line
1689 1689
1690 1690 def scangitpatch(lr, firstline):
1691 1691 """
1692 1692 Git patches can emit:
1693 1693 - rename a to b
1694 1694 - change b
1695 1695 - copy a to c
1696 1696 - change c
1697 1697
1698 1698 We cannot apply this sequence as-is, the renamed 'a' could not be
1699 1699 found for it would have been renamed already. And we cannot copy
1700 1700 from 'b' instead because 'b' would have been changed already. So
1701 1701 we scan the git patch for copy and rename commands so we can
1702 1702 perform the copies ahead of time.
1703 1703 """
1704 1704 pos = 0
1705 1705 try:
1706 1706 pos = lr.fp.tell()
1707 1707 fp = lr.fp
1708 1708 except IOError:
1709 1709 fp = cStringIO.StringIO(lr.fp.read())
1710 1710 gitlr = linereader(fp)
1711 1711 gitlr.push(firstline)
1712 1712 gitpatches = readgitpatch(gitlr)
1713 1713 fp.seek(pos)
1714 1714 return gitpatches
1715 1715
1716 1716 def iterhunks(fp):
1717 1717 """Read a patch and yield the following events:
1718 1718 - ("file", afile, bfile, firsthunk): select a new target file.
1719 1719 - ("hunk", hunk): a new hunk is ready to be applied, follows a
1720 1720 "file" event.
1721 1721 - ("git", gitchanges): current diff is in git format, gitchanges
1722 1722 maps filenames to gitpatch records. Unique event.
1723 1723 """
1724 1724 afile = ""
1725 1725 bfile = ""
1726 1726 state = None
1727 1727 hunknum = 0
1728 1728 emitfile = newfile = False
1729 1729 gitpatches = None
1730 1730
1731 1731 # our states
1732 1732 BFILE = 1
1733 1733 context = None
1734 1734 lr = linereader(fp)
1735 1735
1736 1736 while True:
1737 1737 x = lr.readline()
1738 1738 if not x:
1739 1739 break
1740 1740 if state == BFILE and (
1741 1741 (not context and x[0] == '@')
1742 1742 or (context is not False and x.startswith('***************'))
1743 1743 or x.startswith('GIT binary patch')):
1744 1744 gp = None
1745 1745 if (gitpatches and
1746 1746 gitpatches[-1].ispatching(afile, bfile)):
1747 1747 gp = gitpatches.pop()
1748 1748 if x.startswith('GIT binary patch'):
1749 1749 h = binhunk(lr, gp.path)
1750 1750 else:
1751 1751 if context is None and x.startswith('***************'):
1752 1752 context = True
1753 1753 h = hunk(x, hunknum + 1, lr, context)
1754 1754 hunknum += 1
1755 1755 if emitfile:
1756 1756 emitfile = False
1757 1757 yield 'file', (afile, bfile, h, gp and gp.copy() or None)
1758 1758 yield 'hunk', h
1759 1759 elif x.startswith('diff --git a/'):
1760 1760 m = gitre.match(x.rstrip(' \r\n'))
1761 1761 if not m:
1762 1762 continue
1763 1763 if gitpatches is None:
1764 1764 # scan whole input for git metadata
1765 1765 gitpatches = scangitpatch(lr, x)
1766 1766 yield 'git', [g.copy() for g in gitpatches
1767 1767 if g.op in ('COPY', 'RENAME')]
1768 1768 gitpatches.reverse()
1769 1769 afile = 'a/' + m.group(1)
1770 1770 bfile = 'b/' + m.group(2)
1771 1771 while gitpatches and not gitpatches[-1].ispatching(afile, bfile):
1772 1772 gp = gitpatches.pop()
1773 1773 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1774 1774 if not gitpatches:
1775 1775 raise PatchError(_('failed to synchronize metadata for "%s"')
1776 1776 % afile[2:])
1777 1777 gp = gitpatches[-1]
1778 1778 newfile = True
1779 1779 elif x.startswith('---'):
1780 1780 # check for a unified diff
1781 1781 l2 = lr.readline()
1782 1782 if not l2.startswith('+++'):
1783 1783 lr.push(l2)
1784 1784 continue
1785 1785 newfile = True
1786 1786 context = False
1787 1787 afile = parsefilename(x)
1788 1788 bfile = parsefilename(l2)
1789 1789 elif x.startswith('***'):
1790 1790 # check for a context diff
1791 1791 l2 = lr.readline()
1792 1792 if not l2.startswith('---'):
1793 1793 lr.push(l2)
1794 1794 continue
1795 1795 l3 = lr.readline()
1796 1796 lr.push(l3)
1797 1797 if not l3.startswith("***************"):
1798 1798 lr.push(l2)
1799 1799 continue
1800 1800 newfile = True
1801 1801 context = True
1802 1802 afile = parsefilename(x)
1803 1803 bfile = parsefilename(l2)
1804 1804
1805 1805 if newfile:
1806 1806 newfile = False
1807 1807 emitfile = True
1808 1808 state = BFILE
1809 1809 hunknum = 0
1810 1810
1811 1811 while gitpatches:
1812 1812 gp = gitpatches.pop()
1813 1813 yield 'file', ('a/' + gp.path, 'b/' + gp.path, None, gp.copy())
1814 1814
1815 1815 def applybindelta(binchunk, data):
1816 1816 """Apply a binary delta hunk
1817 1817 The algorithm used is the algorithm from git's patch-delta.c
1818 1818 """
1819 1819 def deltahead(binchunk):
1820 1820 i = 0
1821 1821 for c in binchunk:
1822 1822 i += 1
1823 1823 if not (ord(c) & 0x80):
1824 1824 return i
1825 1825 return i
1826 1826 out = ""
1827 1827 s = deltahead(binchunk)
1828 1828 binchunk = binchunk[s:]
1829 1829 s = deltahead(binchunk)
1830 1830 binchunk = binchunk[s:]
1831 1831 i = 0
1832 1832 while i < len(binchunk):
1833 1833 cmd = ord(binchunk[i])
1834 1834 i += 1
1835 1835 if (cmd & 0x80):
1836 1836 offset = 0
1837 1837 size = 0
1838 1838 if (cmd & 0x01):
1839 1839 offset = ord(binchunk[i])
1840 1840 i += 1
1841 1841 if (cmd & 0x02):
1842 1842 offset |= ord(binchunk[i]) << 8
1843 1843 i += 1
1844 1844 if (cmd & 0x04):
1845 1845 offset |= ord(binchunk[i]) << 16
1846 1846 i += 1
1847 1847 if (cmd & 0x08):
1848 1848 offset |= ord(binchunk[i]) << 24
1849 1849 i += 1
1850 1850 if (cmd & 0x10):
1851 1851 size = ord(binchunk[i])
1852 1852 i += 1
1853 1853 if (cmd & 0x20):
1854 1854 size |= ord(binchunk[i]) << 8
1855 1855 i += 1
1856 1856 if (cmd & 0x40):
1857 1857 size |= ord(binchunk[i]) << 16
1858 1858 i += 1
1859 1859 if size == 0:
1860 1860 size = 0x10000
1861 1861 offset_end = offset + size
1862 1862 out += data[offset:offset_end]
1863 1863 elif cmd != 0:
1864 1864 offset_end = i + cmd
1865 1865 out += binchunk[i:offset_end]
1866 1866 i += cmd
1867 1867 else:
1868 1868 raise PatchError(_('unexpected delta opcode 0'))
1869 1869 return out
1870 1870
1871 1871 def applydiff(ui, fp, backend, store, strip=1, prefix='', eolmode='strict'):
1872 1872 """Reads a patch from fp and tries to apply it.
1873 1873
1874 1874 Returns 0 for a clean patch, -1 if any rejects were found and 1 if
1875 1875 there was any fuzz.
1876 1876
1877 1877 If 'eolmode' is 'strict', the patch content and patched file are
1878 1878 read in binary mode. Otherwise, line endings are ignored when
1879 1879 patching then normalized according to 'eolmode'.
1880 1880 """
1881 1881 return _applydiff(ui, fp, patchfile, backend, store, strip=strip,
1882 1882 prefix=prefix, eolmode=eolmode)
1883 1883
1884 1884 def _applydiff(ui, fp, patcher, backend, store, strip=1, prefix='',
1885 1885 eolmode='strict'):
1886 1886
1887 1887 if prefix:
1888 1888 prefix = pathutil.canonpath(backend.repo.root, backend.repo.getcwd(),
1889 1889 prefix)
1890 1890 if prefix != '':
1891 1891 prefix += '/'
1892 1892 def pstrip(p):
1893 1893 return pathtransform(p, strip - 1, prefix)[1]
1894 1894
1895 1895 rejects = 0
1896 1896 err = 0
1897 1897 current_file = None
1898 1898
1899 1899 for state, values in iterhunks(fp):
1900 1900 if state == 'hunk':
1901 1901 if not current_file:
1902 1902 continue
1903 1903 ret = current_file.apply(values)
1904 1904 if ret > 0:
1905 1905 err = 1
1906 1906 elif state == 'file':
1907 1907 if current_file:
1908 1908 rejects += current_file.close()
1909 1909 current_file = None
1910 1910 afile, bfile, first_hunk, gp = values
1911 1911 if gp:
1912 1912 gp.path = pstrip(gp.path)
1913 1913 if gp.oldpath:
1914 1914 gp.oldpath = pstrip(gp.oldpath)
1915 1915 else:
1916 1916 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
1917 1917 prefix)
1918 1918 if gp.op == 'RENAME':
1919 1919 backend.unlink(gp.oldpath)
1920 1920 if not first_hunk:
1921 1921 if gp.op == 'DELETE':
1922 1922 backend.unlink(gp.path)
1923 1923 continue
1924 1924 data, mode = None, None
1925 1925 if gp.op in ('RENAME', 'COPY'):
1926 1926 data, mode = store.getfile(gp.oldpath)[:2]
1927 1927 # FIXME: failing getfile has never been handled here
1928 1928 assert data is not None
1929 1929 if gp.mode:
1930 1930 mode = gp.mode
1931 1931 if gp.op == 'ADD':
1932 1932 # Added files without content have no hunk and
1933 1933 # must be created
1934 1934 data = ''
1935 1935 if data or mode:
1936 1936 if (gp.op in ('ADD', 'RENAME', 'COPY')
1937 1937 and backend.exists(gp.path)):
1938 1938 raise PatchError(_("cannot create %s: destination "
1939 1939 "already exists") % gp.path)
1940 1940 backend.setfile(gp.path, data, mode, gp.oldpath)
1941 1941 continue
1942 1942 try:
1943 1943 current_file = patcher(ui, gp, backend, store,
1944 1944 eolmode=eolmode)
1945 1945 except PatchError, inst:
1946 1946 ui.warn(str(inst) + '\n')
1947 1947 current_file = None
1948 1948 rejects += 1
1949 1949 continue
1950 1950 elif state == 'git':
1951 1951 for gp in values:
1952 1952 path = pstrip(gp.oldpath)
1953 1953 data, mode = backend.getfile(path)
1954 1954 if data is None:
1955 1955 # The error ignored here will trigger a getfile()
1956 1956 # error in a place more appropriate for error
1957 1957 # handling, and will not interrupt the patching
1958 1958 # process.
1959 1959 pass
1960 1960 else:
1961 1961 store.setfile(path, data, mode)
1962 1962 else:
1963 1963 raise util.Abort(_('unsupported parser state: %s') % state)
1964 1964
1965 1965 if current_file:
1966 1966 rejects += current_file.close()
1967 1967
1968 1968 if rejects:
1969 1969 return -1
1970 1970 return err
1971 1971
1972 1972 def _externalpatch(ui, repo, patcher, patchname, strip, files,
1973 1973 similarity):
1974 1974 """use <patcher> to apply <patchname> to the working directory.
1975 1975 returns whether patch was applied with fuzz factor."""
1976 1976
1977 1977 fuzz = False
1978 1978 args = []
1979 1979 cwd = repo.root
1980 1980 if cwd:
1981 1981 args.append('-d %s' % util.shellquote(cwd))
1982 1982 fp = util.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
1983 1983 util.shellquote(patchname)))
1984 1984 try:
1985 1985 for line in fp:
1986 1986 line = line.rstrip()
1987 1987 ui.note(line + '\n')
1988 1988 if line.startswith('patching file '):
1989 1989 pf = util.parsepatchoutput(line)
1990 1990 printed_file = False
1991 1991 files.add(pf)
1992 1992 elif line.find('with fuzz') >= 0:
1993 1993 fuzz = True
1994 1994 if not printed_file:
1995 1995 ui.warn(pf + '\n')
1996 1996 printed_file = True
1997 1997 ui.warn(line + '\n')
1998 1998 elif line.find('saving rejects to file') >= 0:
1999 1999 ui.warn(line + '\n')
2000 2000 elif line.find('FAILED') >= 0:
2001 2001 if not printed_file:
2002 2002 ui.warn(pf + '\n')
2003 2003 printed_file = True
2004 2004 ui.warn(line + '\n')
2005 2005 finally:
2006 2006 if files:
2007 2007 scmutil.marktouched(repo, files, similarity)
2008 2008 code = fp.close()
2009 2009 if code:
2010 2010 raise PatchError(_("patch command failed: %s") %
2011 2011 util.explainexit(code)[0])
2012 2012 return fuzz
2013 2013
2014 2014 def patchbackend(ui, backend, patchobj, strip, prefix, files=None,
2015 2015 eolmode='strict'):
2016 2016 if files is None:
2017 2017 files = set()
2018 2018 if eolmode is None:
2019 2019 eolmode = ui.config('patch', 'eol', 'strict')
2020 2020 if eolmode.lower() not in eolmodes:
2021 2021 raise util.Abort(_('unsupported line endings type: %s') % eolmode)
2022 2022 eolmode = eolmode.lower()
2023 2023
2024 2024 store = filestore()
2025 2025 try:
2026 2026 fp = open(patchobj, 'rb')
2027 2027 except TypeError:
2028 2028 fp = patchobj
2029 2029 try:
2030 2030 ret = applydiff(ui, fp, backend, store, strip=strip, prefix=prefix,
2031 2031 eolmode=eolmode)
2032 2032 finally:
2033 2033 if fp != patchobj:
2034 2034 fp.close()
2035 2035 files.update(backend.close())
2036 2036 store.close()
2037 2037 if ret < 0:
2038 2038 raise PatchError(_('patch failed to apply'))
2039 2039 return ret > 0
2040 2040
2041 2041 def internalpatch(ui, repo, patchobj, strip, prefix='', files=None,
2042 2042 eolmode='strict', similarity=0):
2043 2043 """use builtin patch to apply <patchobj> to the working directory.
2044 2044 returns whether patch was applied with fuzz factor."""
2045 2045 backend = workingbackend(ui, repo, similarity)
2046 2046 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2047 2047
2048 2048 def patchrepo(ui, repo, ctx, store, patchobj, strip, prefix, files=None,
2049 2049 eolmode='strict'):
2050 2050 backend = repobackend(ui, repo, ctx, store)
2051 2051 return patchbackend(ui, backend, patchobj, strip, prefix, files, eolmode)
2052 2052
2053 2053 def patch(ui, repo, patchname, strip=1, prefix='', files=None, eolmode='strict',
2054 2054 similarity=0):
2055 2055 """Apply <patchname> to the working directory.
2056 2056
2057 2057 'eolmode' specifies how end of lines should be handled. It can be:
2058 2058 - 'strict': inputs are read in binary mode, EOLs are preserved
2059 2059 - 'crlf': EOLs are ignored when patching and reset to CRLF
2060 2060 - 'lf': EOLs are ignored when patching and reset to LF
2061 2061 - None: get it from user settings, default to 'strict'
2062 2062 'eolmode' is ignored when using an external patcher program.
2063 2063
2064 2064 Returns whether patch was applied with fuzz factor.
2065 2065 """
2066 2066 patcher = ui.config('ui', 'patch')
2067 2067 if files is None:
2068 2068 files = set()
2069 2069 if patcher:
2070 2070 return _externalpatch(ui, repo, patcher, patchname, strip,
2071 2071 files, similarity)
2072 2072 return internalpatch(ui, repo, patchname, strip, prefix, files, eolmode,
2073 2073 similarity)
2074 2074
2075 2075 def changedfiles(ui, repo, patchpath, strip=1):
2076 2076 backend = fsbackend(ui, repo.root)
2077 2077 fp = open(patchpath, 'rb')
2078 2078 try:
2079 2079 changed = set()
2080 2080 for state, values in iterhunks(fp):
2081 2081 if state == 'file':
2082 2082 afile, bfile, first_hunk, gp = values
2083 2083 if gp:
2084 2084 gp.path = pathtransform(gp.path, strip - 1, '')[1]
2085 2085 if gp.oldpath:
2086 2086 gp.oldpath = pathtransform(gp.oldpath, strip - 1, '')[1]
2087 2087 else:
2088 2088 gp = makepatchmeta(backend, afile, bfile, first_hunk, strip,
2089 2089 '')
2090 2090 changed.add(gp.path)
2091 2091 if gp.op == 'RENAME':
2092 2092 changed.add(gp.oldpath)
2093 2093 elif state not in ('hunk', 'git'):
2094 2094 raise util.Abort(_('unsupported parser state: %s') % state)
2095 2095 return changed
2096 2096 finally:
2097 2097 fp.close()
2098 2098
2099 2099 class GitDiffRequired(Exception):
2100 2100 pass
2101 2101
2102 2102 def diffallopts(ui, opts=None, untrusted=False, section='diff'):
2103 2103 '''return diffopts with all features supported and parsed'''
2104 2104 return difffeatureopts(ui, opts=opts, untrusted=untrusted, section=section,
2105 2105 git=True, whitespace=True, formatchanging=True)
2106 2106
2107 2107 diffopts = diffallopts
2108 2108
2109 2109 def difffeatureopts(ui, opts=None, untrusted=False, section='diff', git=False,
2110 2110 whitespace=False, formatchanging=False):
2111 2111 '''return diffopts with only opted-in features parsed
2112 2112
2113 2113 Features:
2114 2114 - git: git-style diffs
2115 2115 - whitespace: whitespace options like ignoreblanklines and ignorews
2116 2116 - formatchanging: options that will likely break or cause correctness issues
2117 2117 with most diff parsers
2118 2118 '''
2119 2119 def get(key, name=None, getter=ui.configbool, forceplain=None):
2120 2120 if opts:
2121 2121 v = opts.get(key)
2122 2122 if v:
2123 2123 return v
2124 2124 if forceplain is not None and ui.plain():
2125 2125 return forceplain
2126 2126 return getter(section, name or key, None, untrusted=untrusted)
2127 2127
2128 2128 # core options, expected to be understood by every diff parser
2129 2129 buildopts = {
2130 2130 'nodates': get('nodates'),
2131 2131 'showfunc': get('show_function', 'showfunc'),
2132 2132 'context': get('unified', getter=ui.config),
2133 2133 }
2134 2134
2135 2135 if git:
2136 2136 buildopts['git'] = get('git')
2137 2137 if whitespace:
2138 2138 buildopts['ignorews'] = get('ignore_all_space', 'ignorews')
2139 2139 buildopts['ignorewsamount'] = get('ignore_space_change',
2140 2140 'ignorewsamount')
2141 2141 buildopts['ignoreblanklines'] = get('ignore_blank_lines',
2142 2142 'ignoreblanklines')
2143 2143 if formatchanging:
2144 2144 buildopts['text'] = opts and opts.get('text')
2145 2145 buildopts['nobinary'] = get('nobinary')
2146 2146 buildopts['noprefix'] = get('noprefix', forceplain=False)
2147 2147
2148 2148 return mdiff.diffopts(**buildopts)
2149 2149
2150 2150 def diff(repo, node1=None, node2=None, match=None, changes=None, opts=None,
2151 2151 losedatafn=None, prefix='', relroot=''):
2152 2152 '''yields diff of changes to files between two nodes, or node and
2153 2153 working directory.
2154 2154
2155 2155 if node1 is None, use first dirstate parent instead.
2156 2156 if node2 is None, compare node1 with working directory.
2157 2157
2158 2158 losedatafn(**kwarg) is a callable run when opts.upgrade=True and
2159 2159 every time some change cannot be represented with the current
2160 2160 patch format. Return False to upgrade to git patch format, True to
2161 2161 accept the loss or raise an exception to abort the diff. It is
2162 2162 called with the name of current file being diffed as 'fn'. If set
2163 2163 to None, patches will always be upgraded to git format when
2164 2164 necessary.
2165 2165
2166 2166 prefix is a filename prefix that is prepended to all filenames on
2167 2167 display (used for subrepos).
2168 2168
2169 2169 relroot, if not empty, must be normalized with a trailing /. Any match
2170 2170 patterns that fall outside it will be ignored.'''
2171 2171
2172 2172 if opts is None:
2173 2173 opts = mdiff.defaultopts
2174 2174
2175 2175 if not node1 and not node2:
2176 2176 node1 = repo.dirstate.p1()
2177 2177
2178 2178 def lrugetfilectx():
2179 2179 cache = {}
2180 2180 order = collections.deque()
2181 2181 def getfilectx(f, ctx):
2182 2182 fctx = ctx.filectx(f, filelog=cache.get(f))
2183 2183 if f not in cache:
2184 2184 if len(cache) > 20:
2185 2185 del cache[order.popleft()]
2186 2186 cache[f] = fctx.filelog()
2187 2187 else:
2188 2188 order.remove(f)
2189 2189 order.append(f)
2190 2190 return fctx
2191 2191 return getfilectx
2192 2192 getfilectx = lrugetfilectx()
2193 2193
2194 2194 ctx1 = repo[node1]
2195 2195 ctx2 = repo[node2]
2196 2196
2197 2197 relfiltered = False
2198 2198 if relroot != '' and match.always():
2199 2199 # as a special case, create a new matcher with just the relroot
2200 2200 pats = [relroot]
2201 2201 match = scmutil.match(ctx2, pats, default='path')
2202 2202 relfiltered = True
2203 2203
2204 2204 if not changes:
2205 2205 changes = repo.status(ctx1, ctx2, match=match)
2206 2206 modified, added, removed = changes[:3]
2207 2207
2208 2208 if not modified and not added and not removed:
2209 2209 return []
2210 2210
2211 2211 if repo.ui.debugflag:
2212 2212 hexfunc = hex
2213 2213 else:
2214 2214 hexfunc = short
2215 2215 revs = [hexfunc(node) for node in [ctx1.node(), ctx2.node()] if node]
2216 2216
2217 2217 copy = {}
2218 2218 if opts.git or opts.upgrade:
2219 2219 copy = copies.pathcopies(ctx1, ctx2, match=match)
2220 2220
2221 2221 if relroot is not None:
2222 2222 if not relfiltered:
2223 2223 # XXX this would ideally be done in the matcher, but that is
2224 2224 # generally meant to 'or' patterns, not 'and' them. In this case we
2225 2225 # need to 'and' all the patterns from the matcher with relroot.
2226 2226 def filterrel(l):
2227 2227 return [f for f in l if f.startswith(relroot)]
2228 2228 modified = filterrel(modified)
2229 2229 added = filterrel(added)
2230 2230 removed = filterrel(removed)
2231 2231 relfiltered = True
2232 2232 # filter out copies where either side isn't inside the relative root
2233 2233 copy = dict(((dst, src) for (dst, src) in copy.iteritems()
2234 2234 if dst.startswith(relroot)
2235 2235 and src.startswith(relroot)))
2236 2236
2237 2237 def difffn(opts, losedata):
2238 2238 return trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2239 2239 copy, getfilectx, opts, losedata, prefix, relroot)
2240 2240 if opts.upgrade and not opts.git:
2241 2241 try:
2242 2242 def losedata(fn):
2243 2243 if not losedatafn or not losedatafn(fn=fn):
2244 2244 raise GitDiffRequired
2245 2245 # Buffer the whole output until we are sure it can be generated
2246 2246 return list(difffn(opts.copy(git=False), losedata))
2247 2247 except GitDiffRequired:
2248 2248 return difffn(opts.copy(git=True), None)
2249 2249 else:
2250 2250 return difffn(opts, None)
2251 2251
2252 2252 def difflabel(func, *args, **kw):
2253 2253 '''yields 2-tuples of (output, label) based on the output of func()'''
2254 2254 headprefixes = [('diff', 'diff.diffline'),
2255 2255 ('copy', 'diff.extended'),
2256 2256 ('rename', 'diff.extended'),
2257 2257 ('old', 'diff.extended'),
2258 2258 ('new', 'diff.extended'),
2259 2259 ('deleted', 'diff.extended'),
2260 2260 ('---', 'diff.file_a'),
2261 2261 ('+++', 'diff.file_b')]
2262 2262 textprefixes = [('@', 'diff.hunk'),
2263 2263 ('-', 'diff.deleted'),
2264 2264 ('+', 'diff.inserted')]
2265 2265 head = False
2266 2266 for chunk in func(*args, **kw):
2267 2267 lines = chunk.split('\n')
2268 2268 for i, line in enumerate(lines):
2269 2269 if i != 0:
2270 2270 yield ('\n', '')
2271 2271 if head:
2272 2272 if line.startswith('@'):
2273 2273 head = False
2274 2274 else:
2275 2275 if line and line[0] not in ' +-@\\':
2276 2276 head = True
2277 2277 stripline = line
2278 2278 diffline = False
2279 2279 if not head and line and line[0] in '+-':
2280 2280 # highlight tabs and trailing whitespace, but only in
2281 2281 # changed lines
2282 2282 stripline = line.rstrip()
2283 2283 diffline = True
2284 2284
2285 2285 prefixes = textprefixes
2286 2286 if head:
2287 2287 prefixes = headprefixes
2288 2288 for prefix, label in prefixes:
2289 2289 if stripline.startswith(prefix):
2290 2290 if diffline:
2291 2291 for token in tabsplitter.findall(stripline):
2292 2292 if '\t' == token[0]:
2293 2293 yield (token, 'diff.tab')
2294 2294 else:
2295 2295 yield (token, label)
2296 2296 else:
2297 2297 yield (stripline, label)
2298 2298 break
2299 2299 else:
2300 2300 yield (line, '')
2301 2301 if line != stripline:
2302 2302 yield (line[len(stripline):], 'diff.trailingwhitespace')
2303 2303
2304 2304 def diffui(*args, **kw):
2305 2305 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2306 2306 return difflabel(diff, *args, **kw)
2307 2307
2308 2308 def _filepairs(ctx1, modified, added, removed, copy, opts):
2309 2309 '''generates tuples (f1, f2, copyop), where f1 is the name of the file
2310 2310 before and f2 is the the name after. For added files, f1 will be None,
2311 2311 and for removed files, f2 will be None. copyop may be set to None, 'copy'
2312 2312 or 'rename' (the latter two only if opts.git is set).'''
2313 2313 gone = set()
2314 2314
2315 2315 copyto = dict([(v, k) for k, v in copy.items()])
2316 2316
2317 2317 addedset, removedset = set(added), set(removed)
2318 2318 # Fix up added, since merged-in additions appear as
2319 2319 # modifications during merges
2320 2320 for f in modified:
2321 2321 if f not in ctx1:
2322 2322 addedset.add(f)
2323 2323
2324 2324 for f in sorted(modified + added + removed):
2325 2325 copyop = None
2326 2326 f1, f2 = f, f
2327 2327 if f in addedset:
2328 2328 f1 = None
2329 2329 if f in copy:
2330 2330 if opts.git:
2331 2331 f1 = copy[f]
2332 2332 if f1 in removedset and f1 not in gone:
2333 2333 copyop = 'rename'
2334 2334 gone.add(f1)
2335 2335 else:
2336 2336 copyop = 'copy'
2337 2337 elif f in removedset:
2338 2338 f2 = None
2339 2339 if opts.git:
2340 2340 # have we already reported a copy above?
2341 2341 if (f in copyto and copyto[f] in addedset
2342 2342 and copy[copyto[f]] == f):
2343 2343 continue
2344 2344 yield f1, f2, copyop
2345 2345
2346 2346 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
2347 2347 copy, getfilectx, opts, losedatafn, prefix, relroot):
2348 2348 '''given input data, generate a diff and yield it in blocks
2349 2349
2350 2350 If generating a diff would lose data like flags or binary data and
2351 2351 losedatafn is not None, it will be called.
2352 2352
2353 2353 relroot is removed and prefix is added to every path in the diff output.
2354 2354
2355 2355 If relroot is not empty, this function expects every path in modified,
2356 2356 added, removed and copy to start with it.'''
2357 2357
2358 2358 def gitindex(text):
2359 2359 if not text:
2360 2360 text = ""
2361 2361 l = len(text)
2362 2362 s = util.sha1('blob %d\0' % l)
2363 2363 s.update(text)
2364 2364 return s.hexdigest()
2365 2365
2366 2366 if opts.noprefix:
2367 2367 aprefix = bprefix = ''
2368 2368 else:
2369 2369 aprefix = 'a/'
2370 2370 bprefix = 'b/'
2371 2371
2372 2372 def diffline(f, revs):
2373 2373 revinfo = ' '.join(["-r %s" % rev for rev in revs])
2374 2374 return 'diff %s %s' % (revinfo, f)
2375 2375
2376 2376 date1 = util.datestr(ctx1.date())
2377 2377 date2 = util.datestr(ctx2.date())
2378 2378
2379 2379 gitmode = {'l': '120000', 'x': '100755', '': '100644'}
2380 2380
2381 2381 if relroot != '' and (repo.ui.configbool('devel', 'all')
2382 2382 or repo.ui.configbool('devel', 'check-relroot')):
2383 2383 for f in modified + added + removed + copy.keys() + copy.values():
2384 2384 if f is not None and not f.startswith(relroot):
2385 2385 raise AssertionError(
2386 2386 "file %s doesn't start with relroot %s" % (f, relroot))
2387 2387
2388 2388 for f1, f2, copyop in _filepairs(
2389 2389 ctx1, modified, added, removed, copy, opts):
2390 2390 content1 = None
2391 2391 content2 = None
2392 2392 flag1 = None
2393 2393 flag2 = None
2394 2394 if f1:
2395 2395 content1 = getfilectx(f1, ctx1).data()
2396 2396 if opts.git or losedatafn:
2397 2397 flag1 = ctx1.flags(f1)
2398 2398 if f2:
2399 2399 content2 = getfilectx(f2, ctx2).data()
2400 2400 if opts.git or losedatafn:
2401 2401 flag2 = ctx2.flags(f2)
2402 2402 binary = False
2403 2403 if opts.git or losedatafn:
2404 2404 binary = util.binary(content1) or util.binary(content2)
2405 2405
2406 2406 if losedatafn and not opts.git:
2407 2407 if (binary or
2408 2408 # copy/rename
2409 2409 f2 in copy or
2410 2410 # empty file creation
2411 2411 (not f1 and not content2) or
2412 2412 # empty file deletion
2413 2413 (not content1 and not f2) or
2414 2414 # create with flags
2415 2415 (not f1 and flag2) or
2416 2416 # change flags
2417 2417 (f1 and f2 and flag1 != flag2)):
2418 2418 losedatafn(f2 or f1)
2419 2419
2420 2420 path1 = f1 or f2
2421 2421 path2 = f2 or f1
2422 2422 path1 = posixpath.join(prefix, path1[len(relroot):])
2423 2423 path2 = posixpath.join(prefix, path2[len(relroot):])
2424 2424 header = []
2425 2425 if opts.git:
2426 2426 header.append('diff --git %s%s %s%s' %
2427 2427 (aprefix, path1, bprefix, path2))
2428 2428 if not f1: # added
2429 2429 header.append('new file mode %s' % gitmode[flag2])
2430 2430 elif not f2: # removed
2431 2431 header.append('deleted file mode %s' % gitmode[flag1])
2432 2432 else: # modified/copied/renamed
2433 2433 mode1, mode2 = gitmode[flag1], gitmode[flag2]
2434 2434 if mode1 != mode2:
2435 2435 header.append('old mode %s' % mode1)
2436 2436 header.append('new mode %s' % mode2)
2437 2437 if copyop is not None:
2438 2438 header.append('%s from %s' % (copyop, path1))
2439 2439 header.append('%s to %s' % (copyop, path2))
2440 2440 elif revs and not repo.ui.quiet:
2441 2441 header.append(diffline(path1, revs))
2442 2442
2443 2443 if binary and opts.git and not opts.nobinary:
2444 2444 text = mdiff.b85diff(content1, content2)
2445 2445 if text:
2446 2446 header.append('index %s..%s' %
2447 2447 (gitindex(content1), gitindex(content2)))
2448 2448 else:
2449 2449 text = mdiff.unidiff(content1, date1,
2450 2450 content2, date2,
2451 2451 path1, path2, opts=opts)
2452 2452 if header and (text or len(header) > 1):
2453 2453 yield '\n'.join(header) + '\n'
2454 2454 if text:
2455 2455 yield text
2456 2456
2457 2457 def diffstatsum(stats):
2458 2458 maxfile, maxtotal, addtotal, removetotal, binary = 0, 0, 0, 0, False
2459 2459 for f, a, r, b in stats:
2460 2460 maxfile = max(maxfile, encoding.colwidth(f))
2461 2461 maxtotal = max(maxtotal, a + r)
2462 2462 addtotal += a
2463 2463 removetotal += r
2464 2464 binary = binary or b
2465 2465
2466 2466 return maxfile, maxtotal, addtotal, removetotal, binary
2467 2467
2468 2468 def diffstatdata(lines):
2469 2469 diffre = re.compile('^diff .*-r [a-z0-9]+\s(.*)$')
2470 2470
2471 2471 results = []
2472 2472 filename, adds, removes, isbinary = None, 0, 0, False
2473 2473
2474 2474 def addresult():
2475 2475 if filename:
2476 2476 results.append((filename, adds, removes, isbinary))
2477 2477
2478 2478 for line in lines:
2479 2479 if line.startswith('diff'):
2480 2480 addresult()
2481 2481 # set numbers to 0 anyway when starting new file
2482 2482 adds, removes, isbinary = 0, 0, False
2483 2483 if line.startswith('diff --git a/'):
2484 2484 filename = gitre.search(line).group(2)
2485 2485 elif line.startswith('diff -r'):
2486 2486 # format: "diff -r ... -r ... filename"
2487 2487 filename = diffre.search(line).group(1)
2488 2488 elif line.startswith('+') and not line.startswith('+++ '):
2489 2489 adds += 1
2490 2490 elif line.startswith('-') and not line.startswith('--- '):
2491 2491 removes += 1
2492 2492 elif (line.startswith('GIT binary patch') or
2493 2493 line.startswith('Binary file')):
2494 2494 isbinary = True
2495 2495 addresult()
2496 2496 return results
2497 2497
2498 2498 def diffstat(lines, width=80, git=False):
2499 2499 output = []
2500 2500 stats = diffstatdata(lines)
2501 2501 maxname, maxtotal, totaladds, totalremoves, hasbinary = diffstatsum(stats)
2502 2502
2503 2503 countwidth = len(str(maxtotal))
2504 2504 if hasbinary and countwidth < 3:
2505 2505 countwidth = 3
2506 2506 graphwidth = width - countwidth - maxname - 6
2507 2507 if graphwidth < 10:
2508 2508 graphwidth = 10
2509 2509
2510 2510 def scale(i):
2511 2511 if maxtotal <= graphwidth:
2512 2512 return i
2513 2513 # If diffstat runs out of room it doesn't print anything,
2514 2514 # which isn't very useful, so always print at least one + or -
2515 2515 # if there were at least some changes.
2516 2516 return max(i * graphwidth // maxtotal, int(bool(i)))
2517 2517
2518 2518 for filename, adds, removes, isbinary in stats:
2519 2519 if isbinary:
2520 2520 count = 'Bin'
2521 2521 else:
2522 2522 count = adds + removes
2523 2523 pluses = '+' * scale(adds)
2524 2524 minuses = '-' * scale(removes)
2525 2525 output.append(' %s%s | %*s %s%s\n' %
2526 2526 (filename, ' ' * (maxname - encoding.colwidth(filename)),
2527 2527 countwidth, count, pluses, minuses))
2528 2528
2529 2529 if stats:
2530 2530 output.append(_(' %d files changed, %d insertions(+), '
2531 2531 '%d deletions(-)\n')
2532 2532 % (len(stats), totaladds, totalremoves))
2533 2533
2534 2534 return ''.join(output)
2535 2535
2536 2536 def diffstatui(*args, **kw):
2537 2537 '''like diffstat(), but yields 2-tuples of (output, label) for
2538 2538 ui.write()
2539 2539 '''
2540 2540
2541 2541 for line in diffstat(*args, **kw).splitlines():
2542 2542 if line and line[-1] in '+-':
2543 2543 name, graph = line.rsplit(' ', 1)
2544 2544 yield (name + ' ', '')
2545 2545 m = re.search(r'\++', graph)
2546 2546 if m:
2547 2547 yield (m.group(0), 'diffstat.inserted')
2548 2548 m = re.search(r'-+', graph)
2549 2549 if m:
2550 2550 yield (m.group(0), 'diffstat.deleted')
2551 2551 else:
2552 2552 yield (line, '')
2553 2553 yield ('\n', '')
@@ -1,1500 +1,1507
1 1 $ hg init a
2 2 $ mkdir a/d1
3 3 $ mkdir a/d1/d2
4 4 $ echo line 1 > a/a
5 5 $ echo line 1 > a/d1/d2/a
6 6 $ hg --cwd a ci -Ama
7 7 adding a
8 8 adding d1/d2/a
9 9
10 10 $ echo line 2 >> a/a
11 11 $ hg --cwd a ci -u someone -d '1 0' -m'second change'
12 12
13 13 import with no args:
14 14
15 15 $ hg --cwd a import
16 16 abort: need at least one patch to import
17 17 [255]
18 18
19 19 generate patches for the test
20 20
21 21 $ hg --cwd a export tip > exported-tip.patch
22 22 $ hg --cwd a diff -r0:1 > diffed-tip.patch
23 23
24 24
25 25 import exported patch
26 26 (this also tests that editor is not invoked, if the patch contains the
27 27 commit message and '--edit' is not specified)
28 28
29 29 $ hg clone -r0 a b
30 30 adding changesets
31 31 adding manifests
32 32 adding file changes
33 33 added 1 changesets with 2 changes to 2 files
34 34 updating to branch default
35 35 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 36 $ HGEDITOR=cat hg --cwd b import ../exported-tip.patch
37 37 applying ../exported-tip.patch
38 38
39 39 message and committer and date should be same
40 40
41 41 $ hg --cwd b tip
42 42 changeset: 1:1d4bd90af0e4
43 43 tag: tip
44 44 user: someone
45 45 date: Thu Jan 01 00:00:01 1970 +0000
46 46 summary: second change
47 47
48 48 $ rm -r b
49 49
50 50
51 51 import exported patch with external patcher
52 52 (this also tests that editor is invoked, if the '--edit' is specified,
53 53 regardless of the commit message in the patch)
54 54
55 55 $ cat > dummypatch.py <<EOF
56 56 > print 'patching file a'
57 57 > file('a', 'wb').write('line2\n')
58 58 > EOF
59 59 $ hg clone -r0 a b
60 60 adding changesets
61 61 adding manifests
62 62 adding file changes
63 63 added 1 changesets with 2 changes to 2 files
64 64 updating to branch default
65 65 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 66 $ HGEDITOR=cat hg --config ui.patch='python ../dummypatch.py' --cwd b import --edit ../exported-tip.patch
67 67 applying ../exported-tip.patch
68 68 second change
69 69
70 70
71 71 HG: Enter commit message. Lines beginning with 'HG:' are removed.
72 72 HG: Leave message empty to abort commit.
73 73 HG: --
74 74 HG: user: someone
75 75 HG: branch 'default'
76 76 HG: changed a
77 77 $ cat b/a
78 78 line2
79 79 $ rm -r b
80 80
81 81
82 82 import of plain diff should fail without message
83 83 (this also tests that editor is invoked, if the patch doesn't contain
84 84 the commit message, regardless of '--edit')
85 85
86 86 $ hg clone -r0 a b
87 87 adding changesets
88 88 adding manifests
89 89 adding file changes
90 90 added 1 changesets with 2 changes to 2 files
91 91 updating to branch default
92 92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 93 $ cat > $TESTTMP/editor.sh <<EOF
94 94 > env | grep HGEDITFORM
95 95 > cat \$1
96 96 > EOF
97 97 $ HGEDITOR="sh $TESTTMP/editor.sh" hg --cwd b import ../diffed-tip.patch
98 98 applying ../diffed-tip.patch
99 99 HGEDITFORM=import.normal.normal
100 100
101 101
102 102 HG: Enter commit message. Lines beginning with 'HG:' are removed.
103 103 HG: Leave message empty to abort commit.
104 104 HG: --
105 105 HG: user: test
106 106 HG: branch 'default'
107 107 HG: changed a
108 108 abort: empty commit message
109 109 [255]
110 110
111 111 Test avoiding editor invocation at applying the patch with --exact,
112 112 even if commit message is empty
113 113
114 114 $ echo a >> b/a
115 115 $ hg --cwd b commit -m ' '
116 116 $ hg --cwd b tip -T "{node}\n"
117 117 d8804f3f5396d800812f579c8452796a5993bdb2
118 118 $ hg --cwd b export -o ../empty-log.diff .
119 119 $ hg --cwd b update -q -C ".^1"
120 120 $ hg --cwd b --config extensions.strip= strip -q tip
121 121 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
122 122 applying ../empty-log.diff
123 123 $ hg --cwd b tip -T "{node}\n"
124 124 d8804f3f5396d800812f579c8452796a5993bdb2
125 125
126 126 $ rm -r b
127 127
128 128
129 129 import of plain diff should be ok with message
130 130
131 131 $ hg clone -r0 a b
132 132 adding changesets
133 133 adding manifests
134 134 adding file changes
135 135 added 1 changesets with 2 changes to 2 files
136 136 updating to branch default
137 137 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 138 $ hg --cwd b import -mpatch ../diffed-tip.patch
139 139 applying ../diffed-tip.patch
140 140 $ rm -r b
141 141
142 142
143 143 import of plain diff with specific date and user
144 144 (this also tests that editor is not invoked, if
145 145 '--message'/'--logfile' is specified and '--edit' is not)
146 146
147 147 $ hg clone -r0 a b
148 148 adding changesets
149 149 adding manifests
150 150 adding file changes
151 151 added 1 changesets with 2 changes to 2 files
152 152 updating to branch default
153 153 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 154 $ hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../diffed-tip.patch
155 155 applying ../diffed-tip.patch
156 156 $ hg -R b tip -pv
157 157 changeset: 1:ca68f19f3a40
158 158 tag: tip
159 159 user: user@nowhere.net
160 160 date: Thu Jan 01 00:00:01 1970 +0000
161 161 files: a
162 162 description:
163 163 patch
164 164
165 165
166 166 diff -r 80971e65b431 -r ca68f19f3a40 a
167 167 --- a/a Thu Jan 01 00:00:00 1970 +0000
168 168 +++ b/a Thu Jan 01 00:00:01 1970 +0000
169 169 @@ -1,1 +1,2 @@
170 170 line 1
171 171 +line 2
172 172
173 173 $ rm -r b
174 174
175 175
176 176 import of plain diff should be ok with --no-commit
177 177 (this also tests that editor is not invoked, if '--no-commit' is
178 178 specified, regardless of '--edit')
179 179
180 180 $ hg clone -r0 a b
181 181 adding changesets
182 182 adding manifests
183 183 adding file changes
184 184 added 1 changesets with 2 changes to 2 files
185 185 updating to branch default
186 186 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 187 $ HGEDITOR=cat hg --cwd b import --no-commit --edit ../diffed-tip.patch
188 188 applying ../diffed-tip.patch
189 189 $ hg --cwd b diff --nodates
190 190 diff -r 80971e65b431 a
191 191 --- a/a
192 192 +++ b/a
193 193 @@ -1,1 +1,2 @@
194 194 line 1
195 195 +line 2
196 196 $ rm -r b
197 197
198 198
199 199 import of malformed plain diff should fail
200 200
201 201 $ hg clone -r0 a b
202 202 adding changesets
203 203 adding manifests
204 204 adding file changes
205 205 added 1 changesets with 2 changes to 2 files
206 206 updating to branch default
207 207 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
208 208 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
209 209 $ hg --cwd b import -mpatch ../broken.patch
210 210 applying ../broken.patch
211 211 abort: bad hunk #1
212 212 [255]
213 213 $ rm -r b
214 214
215 215
216 216 hg -R repo import
217 217 put the clone in a subdir - having a directory named "a"
218 218 used to hide a bug.
219 219
220 220 $ mkdir dir
221 221 $ hg clone -r0 a dir/b
222 222 adding changesets
223 223 adding manifests
224 224 adding file changes
225 225 added 1 changesets with 2 changes to 2 files
226 226 updating to branch default
227 227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 228 $ cd dir
229 229 $ hg -R b import ../exported-tip.patch
230 230 applying ../exported-tip.patch
231 231 $ cd ..
232 232 $ rm -r dir
233 233
234 234
235 235 import from stdin
236 236
237 237 $ hg clone -r0 a b
238 238 adding changesets
239 239 adding manifests
240 240 adding file changes
241 241 added 1 changesets with 2 changes to 2 files
242 242 updating to branch default
243 243 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
244 244 $ hg --cwd b import - < exported-tip.patch
245 245 applying patch from stdin
246 246 $ rm -r b
247 247
248 248
249 249 import two patches in one stream
250 250
251 251 $ hg init b
252 252 $ hg --cwd a export 0:tip | hg --cwd b import -
253 253 applying patch from stdin
254 254 $ hg --cwd a id
255 255 1d4bd90af0e4 tip
256 256 $ hg --cwd b id
257 257 1d4bd90af0e4 tip
258 258 $ rm -r b
259 259
260 260
261 261 override commit message
262 262
263 263 $ hg clone -r0 a b
264 264 adding changesets
265 265 adding manifests
266 266 adding file changes
267 267 added 1 changesets with 2 changes to 2 files
268 268 updating to branch default
269 269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
270 270 $ hg --cwd b import -m 'override' - < exported-tip.patch
271 271 applying patch from stdin
272 272 $ hg --cwd b tip | grep override
273 273 summary: override
274 274 $ rm -r b
275 275
276 276 $ cat > mkmsg.py <<EOF
277 277 > import email.Message, sys
278 278 > msg = email.Message.Message()
279 279 > patch = open(sys.argv[1], 'rb').read()
280 280 > msg.set_payload('email commit message\n' + patch)
281 281 > msg['Subject'] = 'email patch'
282 282 > msg['From'] = 'email patcher'
283 283 > file(sys.argv[2], 'wb').write(msg.as_string())
284 284 > EOF
285 285
286 286
287 287 plain diff in email, subject, message body
288 288
289 289 $ hg clone -r0 a b
290 290 adding changesets
291 291 adding manifests
292 292 adding file changes
293 293 added 1 changesets with 2 changes to 2 files
294 294 updating to branch default
295 295 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 296 $ python mkmsg.py diffed-tip.patch msg.patch
297 297 $ hg --cwd b import ../msg.patch
298 298 applying ../msg.patch
299 299 $ hg --cwd b tip | grep email
300 300 user: email patcher
301 301 summary: email patch
302 302 $ rm -r b
303 303
304 304
305 305 plain diff in email, no subject, message body
306 306
307 307 $ hg clone -r0 a b
308 308 adding changesets
309 309 adding manifests
310 310 adding file changes
311 311 added 1 changesets with 2 changes to 2 files
312 312 updating to branch default
313 313 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
314 314 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
315 315 applying patch from stdin
316 316 $ rm -r b
317 317
318 318
319 319 plain diff in email, subject, no message body
320 320
321 321 $ hg clone -r0 a b
322 322 adding changesets
323 323 adding manifests
324 324 adding file changes
325 325 added 1 changesets with 2 changes to 2 files
326 326 updating to branch default
327 327 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
328 328 $ grep -v '^email ' msg.patch | hg --cwd b import -
329 329 applying patch from stdin
330 330 $ rm -r b
331 331
332 332
333 333 plain diff in email, no subject, no message body, should fail
334 334
335 335 $ hg clone -r0 a b
336 336 adding changesets
337 337 adding manifests
338 338 adding file changes
339 339 added 1 changesets with 2 changes to 2 files
340 340 updating to branch default
341 341 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 342 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
343 343 applying patch from stdin
344 344 abort: empty commit message
345 345 [255]
346 346 $ rm -r b
347 347
348 348
349 349 hg export in email, should use patch header
350 350
351 351 $ hg clone -r0 a b
352 352 adding changesets
353 353 adding manifests
354 354 adding file changes
355 355 added 1 changesets with 2 changes to 2 files
356 356 updating to branch default
357 357 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
358 358 $ python mkmsg.py exported-tip.patch msg.patch
359 359 $ cat msg.patch | hg --cwd b import -
360 360 applying patch from stdin
361 361 $ hg --cwd b tip | grep second
362 362 summary: second change
363 363 $ rm -r b
364 364
365 365
366 366 subject: duplicate detection, removal of [PATCH]
367 367 The '---' tests the gitsendmail handling without proper mail headers
368 368
369 369 $ cat > mkmsg2.py <<EOF
370 370 > import email.Message, sys
371 371 > msg = email.Message.Message()
372 372 > patch = open(sys.argv[1], 'rb').read()
373 373 > msg.set_payload('email patch\n\nnext line\n---\n' + patch)
374 374 > msg['Subject'] = '[PATCH] email patch'
375 375 > msg['From'] = 'email patcher'
376 376 > file(sys.argv[2], 'wb').write(msg.as_string())
377 377 > EOF
378 378
379 379
380 380 plain diff in email, [PATCH] subject, message body with subject
381 381
382 382 $ hg clone -r0 a b
383 383 adding changesets
384 384 adding manifests
385 385 adding file changes
386 386 added 1 changesets with 2 changes to 2 files
387 387 updating to branch default
388 388 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
389 389 $ python mkmsg2.py diffed-tip.patch msg.patch
390 390 $ cat msg.patch | hg --cwd b import -
391 391 applying patch from stdin
392 392 $ hg --cwd b tip --template '{desc}\n'
393 393 email patch
394 394
395 395 next line
396 396 $ rm -r b
397 397
398 398
399 399 Issue963: Parent of working dir incorrect after import of multiple
400 400 patches and rollback
401 401
402 402 We weren't backing up the correct dirstate file when importing many
403 403 patches: import patch1 patch2; rollback
404 404
405 405 $ echo line 3 >> a/a
406 406 $ hg --cwd a ci -m'third change'
407 407 $ hg --cwd a export -o '../patch%R' 1 2
408 408 $ hg clone -qr0 a b
409 409 $ hg --cwd b parents --template 'parent: {rev}\n'
410 410 parent: 0
411 411 $ hg --cwd b import -v ../patch1 ../patch2
412 412 applying ../patch1
413 413 patching file a
414 414 committing files:
415 415 a
416 416 committing manifest
417 417 committing changelog
418 418 created 1d4bd90af0e4
419 419 applying ../patch2
420 420 patching file a
421 421 committing files:
422 422 a
423 423 committing manifest
424 424 committing changelog
425 425 created 6d019af21222
426 426 $ hg --cwd b rollback
427 427 repository tip rolled back to revision 0 (undo import)
428 428 working directory now based on revision 0
429 429 $ hg --cwd b parents --template 'parent: {rev}\n'
430 430 parent: 0
431 431 $ rm -r b
432 432
433 433
434 434 importing a patch in a subdirectory failed at the commit stage
435 435
436 436 $ echo line 2 >> a/d1/d2/a
437 437 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
438 438
439 439 hg import in a subdirectory
440 440
441 441 $ hg clone -r0 a b
442 442 adding changesets
443 443 adding manifests
444 444 adding file changes
445 445 added 1 changesets with 2 changes to 2 files
446 446 updating to branch default
447 447 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 448 $ hg --cwd a export tip > tmp
449 449 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
450 450 $ dir=`pwd`
451 451 $ cd b/d1/d2 2>&1 > /dev/null
452 452 $ hg import ../../../subdir-tip.patch
453 453 applying ../../../subdir-tip.patch
454 454 $ cd "$dir"
455 455
456 456 message should be 'subdir change'
457 457 committer should be 'someoneelse'
458 458
459 459 $ hg --cwd b tip
460 460 changeset: 1:3577f5aea227
461 461 tag: tip
462 462 user: someoneelse
463 463 date: Thu Jan 01 00:00:01 1970 +0000
464 464 summary: subdir change
465 465
466 466
467 467 should be empty
468 468
469 469 $ hg --cwd b status
470 470
471 471
472 472 Test fuzziness (ambiguous patch location, fuzz=2)
473 473
474 474 $ hg init fuzzy
475 475 $ cd fuzzy
476 476 $ echo line1 > a
477 477 $ echo line0 >> a
478 478 $ echo line3 >> a
479 479 $ hg ci -Am adda
480 480 adding a
481 481 $ echo line1 > a
482 482 $ echo line2 >> a
483 483 $ echo line0 >> a
484 484 $ echo line3 >> a
485 485 $ hg ci -m change a
486 486 $ hg export tip > fuzzy-tip.patch
487 487 $ hg up -C 0
488 488 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 489 $ echo line1 > a
490 490 $ echo line0 >> a
491 491 $ echo line1 >> a
492 492 $ echo line0 >> a
493 493 $ hg ci -m brancha
494 494 created new head
495 $ hg import --config patch.fuzz=0 -v fuzzy-tip.patch
496 applying fuzzy-tip.patch
497 patching file a
498 Hunk #1 FAILED at 0
499 1 out of 1 hunks FAILED -- saving rejects to file a.rej
500 abort: patch failed to apply
501 [255]
495 502 $ hg import --no-commit -v fuzzy-tip.patch
496 503 applying fuzzy-tip.patch
497 504 patching file a
498 505 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
499 506 applied to working directory
500 507 $ hg revert -a
501 508 reverting a
502 509
503 510
504 511 import with --no-commit should have written .hg/last-message.txt
505 512
506 513 $ cat .hg/last-message.txt
507 514 change (no-eol)
508 515
509 516
510 517 test fuzziness with eol=auto
511 518
512 519 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
513 520 applying fuzzy-tip.patch
514 521 patching file a
515 522 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
516 523 applied to working directory
517 524 $ cd ..
518 525
519 526
520 527 Test hunk touching empty files (issue906)
521 528
522 529 $ hg init empty
523 530 $ cd empty
524 531 $ touch a
525 532 $ touch b1
526 533 $ touch c1
527 534 $ echo d > d
528 535 $ hg ci -Am init
529 536 adding a
530 537 adding b1
531 538 adding c1
532 539 adding d
533 540 $ echo a > a
534 541 $ echo b > b1
535 542 $ hg mv b1 b2
536 543 $ echo c > c1
537 544 $ hg copy c1 c2
538 545 $ rm d
539 546 $ touch d
540 547 $ hg diff --git
541 548 diff --git a/a b/a
542 549 --- a/a
543 550 +++ b/a
544 551 @@ -0,0 +1,1 @@
545 552 +a
546 553 diff --git a/b1 b/b2
547 554 rename from b1
548 555 rename to b2
549 556 --- a/b1
550 557 +++ b/b2
551 558 @@ -0,0 +1,1 @@
552 559 +b
553 560 diff --git a/c1 b/c1
554 561 --- a/c1
555 562 +++ b/c1
556 563 @@ -0,0 +1,1 @@
557 564 +c
558 565 diff --git a/c1 b/c2
559 566 copy from c1
560 567 copy to c2
561 568 --- a/c1
562 569 +++ b/c2
563 570 @@ -0,0 +1,1 @@
564 571 +c
565 572 diff --git a/d b/d
566 573 --- a/d
567 574 +++ b/d
568 575 @@ -1,1 +0,0 @@
569 576 -d
570 577 $ hg ci -m empty
571 578 $ hg export --git tip > empty.diff
572 579 $ hg up -C 0
573 580 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
574 581 $ hg import empty.diff
575 582 applying empty.diff
576 583 $ for name in a b1 b2 c1 c2 d; do
577 584 > echo % $name file
578 585 > test -f $name && cat $name
579 586 > done
580 587 % a file
581 588 a
582 589 % b1 file
583 590 % b2 file
584 591 b
585 592 % c1 file
586 593 c
587 594 % c2 file
588 595 c
589 596 % d file
590 597 $ cd ..
591 598
592 599
593 600 Test importing a patch ending with a binary file removal
594 601
595 602 $ hg init binaryremoval
596 603 $ cd binaryremoval
597 604 $ echo a > a
598 605 $ $PYTHON -c "file('b', 'wb').write('a\x00b')"
599 606 $ hg ci -Am addall
600 607 adding a
601 608 adding b
602 609 $ hg rm a
603 610 $ hg rm b
604 611 $ hg st
605 612 R a
606 613 R b
607 614 $ hg ci -m remove
608 615 $ hg export --git . > remove.diff
609 616 $ cat remove.diff | grep git
610 617 diff --git a/a b/a
611 618 diff --git a/b b/b
612 619 $ hg up -C 0
613 620 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
614 621 $ hg import remove.diff
615 622 applying remove.diff
616 623 $ hg manifest
617 624 $ cd ..
618 625
619 626
620 627 Issue927: test update+rename with common name
621 628
622 629 $ hg init t
623 630 $ cd t
624 631 $ touch a
625 632 $ hg ci -Am t
626 633 adding a
627 634 $ echo a > a
628 635
629 636 Here, bfile.startswith(afile)
630 637
631 638 $ hg copy a a2
632 639 $ hg ci -m copya
633 640 $ hg export --git tip > copy.diff
634 641 $ hg up -C 0
635 642 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
636 643 $ hg import copy.diff
637 644 applying copy.diff
638 645
639 646 a should contain an 'a'
640 647
641 648 $ cat a
642 649 a
643 650
644 651 and a2 should have duplicated it
645 652
646 653 $ cat a2
647 654 a
648 655 $ cd ..
649 656
650 657
651 658 test -p0
652 659
653 660 $ hg init p0
654 661 $ cd p0
655 662 $ echo a > a
656 663 $ hg ci -Am t
657 664 adding a
658 665 $ hg import -p foo
659 666 abort: invalid value 'foo' for option -p, expected int
660 667 [255]
661 668 $ hg import -p0 - << EOF
662 669 > foobar
663 670 > --- a Sat Apr 12 22:43:58 2008 -0400
664 671 > +++ a Sat Apr 12 22:44:05 2008 -0400
665 672 > @@ -1,1 +1,1 @@
666 673 > -a
667 674 > +bb
668 675 > EOF
669 676 applying patch from stdin
670 677 $ hg status
671 678 $ cat a
672 679 bb
673 680
674 681 test --prefix
675 682
676 683 $ mkdir -p dir/dir2
677 684 $ echo b > dir/dir2/b
678 685 $ hg ci -Am b
679 686 adding dir/dir2/b
680 687 $ hg import -p2 --prefix dir - << EOF
681 688 > foobar
682 689 > --- drop1/drop2/dir2/b
683 690 > +++ drop1/drop2/dir2/b
684 691 > @@ -1,1 +1,1 @@
685 692 > -b
686 693 > +cc
687 694 > EOF
688 695 applying patch from stdin
689 696 $ hg status
690 697 $ cat dir/dir2/b
691 698 cc
692 699 $ cd ..
693 700
694 701
695 702 test paths outside repo root
696 703
697 704 $ mkdir outside
698 705 $ touch outside/foo
699 706 $ hg init inside
700 707 $ cd inside
701 708 $ hg import - <<EOF
702 709 > diff --git a/a b/b
703 710 > rename from ../outside/foo
704 711 > rename to bar
705 712 > EOF
706 713 applying patch from stdin
707 714 abort: path contains illegal component: ../outside/foo (glob)
708 715 [255]
709 716 $ cd ..
710 717
711 718
712 719 test import with similarity and git and strip (issue295 et al.)
713 720
714 721 $ hg init sim
715 722 $ cd sim
716 723 $ echo 'this is a test' > a
717 724 $ hg ci -Ama
718 725 adding a
719 726 $ cat > ../rename.diff <<EOF
720 727 > diff --git a/foo/a b/foo/a
721 728 > deleted file mode 100644
722 729 > --- a/foo/a
723 730 > +++ /dev/null
724 731 > @@ -1,1 +0,0 @@
725 732 > -this is a test
726 733 > diff --git a/foo/b b/foo/b
727 734 > new file mode 100644
728 735 > --- /dev/null
729 736 > +++ b/foo/b
730 737 > @@ -0,0 +1,2 @@
731 738 > +this is a test
732 739 > +foo
733 740 > EOF
734 741 $ hg import --no-commit -v -s 1 ../rename.diff -p2
735 742 applying ../rename.diff
736 743 patching file a
737 744 patching file b
738 745 adding b
739 746 recording removal of a as rename to b (88% similar)
740 747 applied to working directory
741 748 $ hg st -C
742 749 A b
743 750 a
744 751 R a
745 752 $ hg revert -a
746 753 undeleting a
747 754 forgetting b
748 755 $ rm b
749 756 $ hg import --no-commit -v -s 100 ../rename.diff -p2
750 757 applying ../rename.diff
751 758 patching file a
752 759 patching file b
753 760 adding b
754 761 applied to working directory
755 762 $ hg st -C
756 763 A b
757 764 R a
758 765 $ cd ..
759 766
760 767
761 768 Issue1495: add empty file from the end of patch
762 769
763 770 $ hg init addemptyend
764 771 $ cd addemptyend
765 772 $ touch a
766 773 $ hg addremove
767 774 adding a
768 775 $ hg ci -m "commit"
769 776 $ cat > a.patch <<EOF
770 777 > add a, b
771 778 > diff --git a/a b/a
772 779 > --- a/a
773 780 > +++ b/a
774 781 > @@ -0,0 +1,1 @@
775 782 > +a
776 783 > diff --git a/b b/b
777 784 > new file mode 100644
778 785 > EOF
779 786 $ hg import --no-commit a.patch
780 787 applying a.patch
781 788
782 789 apply a good patch followed by an empty patch (mainly to ensure
783 790 that dirstate is *not* updated when import crashes)
784 791 $ hg update -q -C .
785 792 $ rm b
786 793 $ touch empty.patch
787 794 $ hg import a.patch empty.patch
788 795 applying a.patch
789 796 applying empty.patch
790 797 transaction abort!
791 798 rollback completed
792 799 abort: empty.patch: no diffs found
793 800 [255]
794 801 $ hg tip --template '{rev} {desc|firstline}\n'
795 802 0 commit
796 803 $ hg -q status
797 804 M a
798 805 $ cd ..
799 806
800 807 create file when source is not /dev/null
801 808
802 809 $ cat > create.patch <<EOF
803 810 > diff -Naur proj-orig/foo proj-new/foo
804 811 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
805 812 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
806 813 > @@ -0,0 +1,1 @@
807 814 > +a
808 815 > EOF
809 816
810 817 some people have patches like the following too
811 818
812 819 $ cat > create2.patch <<EOF
813 820 > diff -Naur proj-orig/foo proj-new/foo
814 821 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
815 822 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
816 823 > @@ -0,0 +1,1 @@
817 824 > +a
818 825 > EOF
819 826 $ hg init oddcreate
820 827 $ cd oddcreate
821 828 $ hg import --no-commit ../create.patch
822 829 applying ../create.patch
823 830 $ cat foo
824 831 a
825 832 $ rm foo
826 833 $ hg revert foo
827 834 $ hg import --no-commit ../create2.patch
828 835 applying ../create2.patch
829 836 $ cat foo
830 837 a
831 838
832 839 $ cd ..
833 840
834 841 Issue1859: first line mistaken for email headers
835 842
836 843 $ hg init emailconfusion
837 844 $ cd emailconfusion
838 845 $ cat > a.patch <<EOF
839 846 > module: summary
840 847 >
841 848 > description
842 849 >
843 850 >
844 851 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
845 852 > --- /dev/null
846 853 > +++ b/a
847 854 > @@ -0,0 +1,1 @@
848 855 > +a
849 856 > EOF
850 857 $ hg import -d '0 0' a.patch
851 858 applying a.patch
852 859 $ hg parents -v
853 860 changeset: 0:5a681217c0ad
854 861 tag: tip
855 862 user: test
856 863 date: Thu Jan 01 00:00:00 1970 +0000
857 864 files: a
858 865 description:
859 866 module: summary
860 867
861 868 description
862 869
863 870
864 871 $ cd ..
865 872
866 873
867 874 in commit message
868 875
869 876 $ hg init commitconfusion
870 877 $ cd commitconfusion
871 878 $ cat > a.patch <<EOF
872 879 > module: summary
873 880 >
874 881 > --- description
875 882 >
876 883 > diff --git a/a b/a
877 884 > new file mode 100644
878 885 > --- /dev/null
879 886 > +++ b/a
880 887 > @@ -0,0 +1,1 @@
881 888 > +a
882 889 > EOF
883 890 > hg import -d '0 0' a.patch
884 891 > hg parents -v
885 892 > cd ..
886 893 >
887 894 > echo '% tricky header splitting'
888 895 > cat > trickyheaders.patch <<EOF
889 896 > From: User A <user@a>
890 897 > Subject: [PATCH] from: tricky!
891 898 >
892 899 > # HG changeset patch
893 900 > # User User B
894 901 > # Date 1266264441 18000
895 902 > # Branch stable
896 903 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
897 904 > # Parent 0000000000000000000000000000000000000000
898 905 > from: tricky!
899 906 >
900 907 > That is not a header.
901 908 >
902 909 > diff -r 000000000000 -r f2be6a1170ac foo
903 910 > --- /dev/null
904 911 > +++ b/foo
905 912 > @@ -0,0 +1,1 @@
906 913 > +foo
907 914 > EOF
908 915 applying a.patch
909 916 changeset: 0:f34d9187897d
910 917 tag: tip
911 918 user: test
912 919 date: Thu Jan 01 00:00:00 1970 +0000
913 920 files: a
914 921 description:
915 922 module: summary
916 923
917 924
918 925 % tricky header splitting
919 926
920 927 $ hg init trickyheaders
921 928 $ cd trickyheaders
922 929 $ hg import -d '0 0' ../trickyheaders.patch
923 930 applying ../trickyheaders.patch
924 931 $ hg export --git tip
925 932 # HG changeset patch
926 933 # User User B
927 934 # Date 0 0
928 935 # Thu Jan 01 00:00:00 1970 +0000
929 936 # Node ID eb56ab91903632294ac504838508cb370c0901d2
930 937 # Parent 0000000000000000000000000000000000000000
931 938 from: tricky!
932 939
933 940 That is not a header.
934 941
935 942 diff --git a/foo b/foo
936 943 new file mode 100644
937 944 --- /dev/null
938 945 +++ b/foo
939 946 @@ -0,0 +1,1 @@
940 947 +foo
941 948 $ cd ..
942 949
943 950
944 951 Issue2102: hg export and hg import speak different languages
945 952
946 953 $ hg init issue2102
947 954 $ cd issue2102
948 955 $ mkdir -p src/cmd/gc
949 956 $ touch src/cmd/gc/mksys.bash
950 957 $ hg ci -Am init
951 958 adding src/cmd/gc/mksys.bash
952 959 $ hg import - <<EOF
953 960 > # HG changeset patch
954 961 > # User Rob Pike
955 962 > # Date 1216685449 25200
956 963 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
957 964 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
958 965 > help management of empty pkg and lib directories in perforce
959 966 >
960 967 > R=gri
961 968 > DELTA=4 (4 added, 0 deleted, 0 changed)
962 969 > OCL=13328
963 970 > CL=13328
964 971 >
965 972 > diff --git a/lib/place-holder b/lib/place-holder
966 973 > new file mode 100644
967 974 > --- /dev/null
968 975 > +++ b/lib/place-holder
969 976 > @@ -0,0 +1,2 @@
970 977 > +perforce does not maintain empty directories.
971 978 > +this file helps.
972 979 > diff --git a/pkg/place-holder b/pkg/place-holder
973 980 > new file mode 100644
974 981 > --- /dev/null
975 982 > +++ b/pkg/place-holder
976 983 > @@ -0,0 +1,2 @@
977 984 > +perforce does not maintain empty directories.
978 985 > +this file helps.
979 986 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
980 987 > old mode 100644
981 988 > new mode 100755
982 989 > EOF
983 990 applying patch from stdin
984 991
985 992 #if execbit
986 993
987 994 $ hg sum
988 995 parent: 1:d59915696727 tip
989 996 help management of empty pkg and lib directories in perforce
990 997 branch: default
991 998 commit: (clean)
992 999 update: (current)
993 1000 phases: 2 draft
994 1001
995 1002 $ hg diff --git -c tip
996 1003 diff --git a/lib/place-holder b/lib/place-holder
997 1004 new file mode 100644
998 1005 --- /dev/null
999 1006 +++ b/lib/place-holder
1000 1007 @@ -0,0 +1,2 @@
1001 1008 +perforce does not maintain empty directories.
1002 1009 +this file helps.
1003 1010 diff --git a/pkg/place-holder b/pkg/place-holder
1004 1011 new file mode 100644
1005 1012 --- /dev/null
1006 1013 +++ b/pkg/place-holder
1007 1014 @@ -0,0 +1,2 @@
1008 1015 +perforce does not maintain empty directories.
1009 1016 +this file helps.
1010 1017 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1011 1018 old mode 100644
1012 1019 new mode 100755
1013 1020
1014 1021 #else
1015 1022
1016 1023 $ hg sum
1017 1024 parent: 1:28f089cc9ccc tip
1018 1025 help management of empty pkg and lib directories in perforce
1019 1026 branch: default
1020 1027 commit: (clean)
1021 1028 update: (current)
1022 1029 phases: 2 draft
1023 1030
1024 1031 $ hg diff --git -c tip
1025 1032 diff --git a/lib/place-holder b/lib/place-holder
1026 1033 new file mode 100644
1027 1034 --- /dev/null
1028 1035 +++ b/lib/place-holder
1029 1036 @@ -0,0 +1,2 @@
1030 1037 +perforce does not maintain empty directories.
1031 1038 +this file helps.
1032 1039 diff --git a/pkg/place-holder b/pkg/place-holder
1033 1040 new file mode 100644
1034 1041 --- /dev/null
1035 1042 +++ b/pkg/place-holder
1036 1043 @@ -0,0 +1,2 @@
1037 1044 +perforce does not maintain empty directories.
1038 1045 +this file helps.
1039 1046
1040 1047 /* The mode change for mksys.bash is missing here, because on platforms */
1041 1048 /* that don't support execbits, mode changes in patches are ignored when */
1042 1049 /* they are imported. This is obviously also the reason for why the hash */
1043 1050 /* in the created changeset is different to the one you see above the */
1044 1051 /* #else clause */
1045 1052
1046 1053 #endif
1047 1054 $ cd ..
1048 1055
1049 1056
1050 1057 diff lines looking like headers
1051 1058
1052 1059 $ hg init difflineslikeheaders
1053 1060 $ cd difflineslikeheaders
1054 1061 $ echo a >a
1055 1062 $ echo b >b
1056 1063 $ echo c >c
1057 1064 $ hg ci -Am1
1058 1065 adding a
1059 1066 adding b
1060 1067 adding c
1061 1068
1062 1069 $ echo "key: value" >>a
1063 1070 $ echo "key: value" >>b
1064 1071 $ echo "foo" >>c
1065 1072 $ hg ci -m2
1066 1073
1067 1074 $ hg up -C 0
1068 1075 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1069 1076 $ hg diff --git -c1 >want
1070 1077 $ hg diff -c1 | hg import --no-commit -
1071 1078 applying patch from stdin
1072 1079 $ hg diff --git >have
1073 1080 $ diff want have
1074 1081 $ cd ..
1075 1082
1076 1083 import a unified diff with no lines of context (diff -U0)
1077 1084
1078 1085 $ hg init diffzero
1079 1086 $ cd diffzero
1080 1087 $ cat > f << EOF
1081 1088 > c2
1082 1089 > c4
1083 1090 > c5
1084 1091 > EOF
1085 1092 $ hg commit -Am0
1086 1093 adding f
1087 1094
1088 1095 $ hg import --no-commit - << EOF
1089 1096 > # HG changeset patch
1090 1097 > # User test
1091 1098 > # Date 0 0
1092 1099 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1093 1100 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1094 1101 > 1
1095 1102 > diff -r 8679a12a975b -r f4974ab632f3 f
1096 1103 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1097 1104 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1098 1105 > @@ -0,0 +1,1 @@
1099 1106 > +c1
1100 1107 > @@ -1,0 +3,1 @@
1101 1108 > +c3
1102 1109 > @@ -3,1 +4,0 @@
1103 1110 > -c5
1104 1111 > EOF
1105 1112 applying patch from stdin
1106 1113
1107 1114 $ cat f
1108 1115 c1
1109 1116 c2
1110 1117 c3
1111 1118 c4
1112 1119
1113 1120 $ cd ..
1114 1121
1115 1122 no segfault while importing a unified diff which start line is zero but chunk
1116 1123 size is non-zero
1117 1124
1118 1125 $ hg init startlinezero
1119 1126 $ cd startlinezero
1120 1127 $ echo foo > foo
1121 1128 $ hg commit -Amfoo
1122 1129 adding foo
1123 1130
1124 1131 $ hg import --no-commit - << EOF
1125 1132 > diff a/foo b/foo
1126 1133 > --- a/foo
1127 1134 > +++ b/foo
1128 1135 > @@ -0,1 +0,1 @@
1129 1136 > foo
1130 1137 > EOF
1131 1138 applying patch from stdin
1132 1139
1133 1140 $ cd ..
1134 1141
1135 1142 Test corner case involving fuzz and skew
1136 1143
1137 1144 $ hg init morecornercases
1138 1145 $ cd morecornercases
1139 1146
1140 1147 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1141 1148 > diff --git a/a b/a
1142 1149 > --- a/a
1143 1150 > +++ b/a
1144 1151 > @@ -1,0 +1,1 @@
1145 1152 > +line
1146 1153 > EOF
1147 1154
1148 1155 $ cat > 02-no-context-middle-of-file.diff <<EOF
1149 1156 > diff --git a/a b/a
1150 1157 > --- a/a
1151 1158 > +++ b/a
1152 1159 > @@ -1,1 +1,1 @@
1153 1160 > -2
1154 1161 > +add some skew
1155 1162 > @@ -2,0 +2,1 @@
1156 1163 > +line
1157 1164 > EOF
1158 1165
1159 1166 $ cat > 03-no-context-end-of-file.diff <<EOF
1160 1167 > diff --git a/a b/a
1161 1168 > --- a/a
1162 1169 > +++ b/a
1163 1170 > @@ -10,0 +10,1 @@
1164 1171 > +line
1165 1172 > EOF
1166 1173
1167 1174 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1168 1175 > diff --git a/a b/a
1169 1176 > --- a/a
1170 1177 > +++ b/a
1171 1178 > @@ -1,1 +1,1 @@
1172 1179 > -2
1173 1180 > +add some skew
1174 1181 > @@ -2,2 +2,3 @@
1175 1182 > not matching, should fuzz
1176 1183 > ... a bit
1177 1184 > +line
1178 1185 > EOF
1179 1186
1180 1187 $ cat > a <<EOF
1181 1188 > 1
1182 1189 > 2
1183 1190 > 3
1184 1191 > 4
1185 1192 > EOF
1186 1193 $ hg ci -Am adda a
1187 1194 $ for p in *.diff; do
1188 1195 > hg import -v --no-commit $p
1189 1196 > cat a
1190 1197 > hg revert -aqC a
1191 1198 > # patch -p1 < $p
1192 1199 > # cat a
1193 1200 > # hg revert -aC a
1194 1201 > done
1195 1202 applying 01-no-context-beginning-of-file.diff
1196 1203 patching file a
1197 1204 applied to working directory
1198 1205 1
1199 1206 line
1200 1207 2
1201 1208 3
1202 1209 4
1203 1210 applying 02-no-context-middle-of-file.diff
1204 1211 patching file a
1205 1212 Hunk #1 succeeded at 2 (offset 1 lines).
1206 1213 Hunk #2 succeeded at 4 (offset 1 lines).
1207 1214 applied to working directory
1208 1215 1
1209 1216 add some skew
1210 1217 3
1211 1218 line
1212 1219 4
1213 1220 applying 03-no-context-end-of-file.diff
1214 1221 patching file a
1215 1222 Hunk #1 succeeded at 5 (offset -6 lines).
1216 1223 applied to working directory
1217 1224 1
1218 1225 2
1219 1226 3
1220 1227 4
1221 1228 line
1222 1229 applying 04-middle-of-file-completely-fuzzed.diff
1223 1230 patching file a
1224 1231 Hunk #1 succeeded at 2 (offset 1 lines).
1225 1232 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1226 1233 applied to working directory
1227 1234 1
1228 1235 add some skew
1229 1236 3
1230 1237 4
1231 1238 line
1232 1239 $ cd ..
1233 1240
1234 1241 Test partial application
1235 1242 ------------------------
1236 1243
1237 1244 prepare a stack of patches depending on each other
1238 1245
1239 1246 $ hg init partial
1240 1247 $ cd partial
1241 1248 $ cat << EOF > a
1242 1249 > one
1243 1250 > two
1244 1251 > three
1245 1252 > four
1246 1253 > five
1247 1254 > six
1248 1255 > seven
1249 1256 > EOF
1250 1257 $ hg add a
1251 1258 $ echo 'b' > b
1252 1259 $ hg add b
1253 1260 $ hg commit -m 'initial' -u Babar
1254 1261 $ cat << EOF > a
1255 1262 > one
1256 1263 > two
1257 1264 > 3
1258 1265 > four
1259 1266 > five
1260 1267 > six
1261 1268 > seven
1262 1269 > EOF
1263 1270 $ hg commit -m 'three' -u Celeste
1264 1271 $ cat << EOF > a
1265 1272 > one
1266 1273 > two
1267 1274 > 3
1268 1275 > 4
1269 1276 > five
1270 1277 > six
1271 1278 > seven
1272 1279 > EOF
1273 1280 $ hg commit -m 'four' -u Rataxes
1274 1281 $ cat << EOF > a
1275 1282 > one
1276 1283 > two
1277 1284 > 3
1278 1285 > 4
1279 1286 > 5
1280 1287 > six
1281 1288 > seven
1282 1289 > EOF
1283 1290 $ echo bb >> b
1284 1291 $ hg commit -m 'five' -u Arthur
1285 1292 $ echo 'Babar' > jungle
1286 1293 $ hg add jungle
1287 1294 $ hg ci -m 'jungle' -u Zephir
1288 1295 $ echo 'Celeste' >> jungle
1289 1296 $ hg ci -m 'extended jungle' -u Cornelius
1290 1297 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1291 1298 @ extended jungle [Cornelius] 1: +1/-0
1292 1299 |
1293 1300 o jungle [Zephir] 1: +1/-0
1294 1301 |
1295 1302 o five [Arthur] 2: +2/-1
1296 1303 |
1297 1304 o four [Rataxes] 1: +1/-1
1298 1305 |
1299 1306 o three [Celeste] 1: +1/-1
1300 1307 |
1301 1308 o initial [Babar] 2: +8/-0
1302 1309
1303 1310
1304 1311 Importing with some success and some errors:
1305 1312
1306 1313 $ hg update --rev 'desc(initial)'
1307 1314 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1308 1315 $ hg export --rev 'desc(five)' | hg import --partial -
1309 1316 applying patch from stdin
1310 1317 patching file a
1311 1318 Hunk #1 FAILED at 1
1312 1319 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1313 1320 patch applied partially
1314 1321 (fix the .rej files and run `hg commit --amend`)
1315 1322 [1]
1316 1323
1317 1324 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1318 1325 @ five [Arthur] 1: +1/-0
1319 1326 |
1320 1327 | o extended jungle [Cornelius] 1: +1/-0
1321 1328 | |
1322 1329 | o jungle [Zephir] 1: +1/-0
1323 1330 | |
1324 1331 | o five [Arthur] 2: +2/-1
1325 1332 | |
1326 1333 | o four [Rataxes] 1: +1/-1
1327 1334 | |
1328 1335 | o three [Celeste] 1: +1/-1
1329 1336 |/
1330 1337 o initial [Babar] 2: +8/-0
1331 1338
1332 1339 $ hg export
1333 1340 # HG changeset patch
1334 1341 # User Arthur
1335 1342 # Date 0 0
1336 1343 # Thu Jan 01 00:00:00 1970 +0000
1337 1344 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1338 1345 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1339 1346 five
1340 1347
1341 1348 diff -r 8e4f0351909e -r 26e6446bb252 b
1342 1349 --- a/b Thu Jan 01 00:00:00 1970 +0000
1343 1350 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1344 1351 @@ -1,1 +1,2 @@
1345 1352 b
1346 1353 +bb
1347 1354 $ hg status -c .
1348 1355 C a
1349 1356 C b
1350 1357 $ ls
1351 1358 a
1352 1359 a.rej
1353 1360 b
1354 1361
1355 1362 Importing with zero success:
1356 1363
1357 1364 $ hg update --rev 'desc(initial)'
1358 1365 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1359 1366 $ hg export --rev 'desc(four)' | hg import --partial -
1360 1367 applying patch from stdin
1361 1368 patching file a
1362 1369 Hunk #1 FAILED at 0
1363 1370 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1364 1371 patch applied partially
1365 1372 (fix the .rej files and run `hg commit --amend`)
1366 1373 [1]
1367 1374
1368 1375 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1369 1376 @ four [Rataxes] 0: +0/-0
1370 1377 |
1371 1378 | o five [Arthur] 1: +1/-0
1372 1379 |/
1373 1380 | o extended jungle [Cornelius] 1: +1/-0
1374 1381 | |
1375 1382 | o jungle [Zephir] 1: +1/-0
1376 1383 | |
1377 1384 | o five [Arthur] 2: +2/-1
1378 1385 | |
1379 1386 | o four [Rataxes] 1: +1/-1
1380 1387 | |
1381 1388 | o three [Celeste] 1: +1/-1
1382 1389 |/
1383 1390 o initial [Babar] 2: +8/-0
1384 1391
1385 1392 $ hg export
1386 1393 # HG changeset patch
1387 1394 # User Rataxes
1388 1395 # Date 0 0
1389 1396 # Thu Jan 01 00:00:00 1970 +0000
1390 1397 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1391 1398 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1392 1399 four
1393 1400
1394 1401 $ hg status -c .
1395 1402 C a
1396 1403 C b
1397 1404 $ ls
1398 1405 a
1399 1406 a.rej
1400 1407 b
1401 1408
1402 1409 Importing with unknown file:
1403 1410
1404 1411 $ hg update --rev 'desc(initial)'
1405 1412 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1406 1413 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1407 1414 applying patch from stdin
1408 1415 unable to find 'jungle' for patching
1409 1416 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1410 1417 patch applied partially
1411 1418 (fix the .rej files and run `hg commit --amend`)
1412 1419 [1]
1413 1420
1414 1421 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1415 1422 @ extended jungle [Cornelius] 0: +0/-0
1416 1423 |
1417 1424 | o four [Rataxes] 0: +0/-0
1418 1425 |/
1419 1426 | o five [Arthur] 1: +1/-0
1420 1427 |/
1421 1428 | o extended jungle [Cornelius] 1: +1/-0
1422 1429 | |
1423 1430 | o jungle [Zephir] 1: +1/-0
1424 1431 | |
1425 1432 | o five [Arthur] 2: +2/-1
1426 1433 | |
1427 1434 | o four [Rataxes] 1: +1/-1
1428 1435 | |
1429 1436 | o three [Celeste] 1: +1/-1
1430 1437 |/
1431 1438 o initial [Babar] 2: +8/-0
1432 1439
1433 1440 $ hg export
1434 1441 # HG changeset patch
1435 1442 # User Cornelius
1436 1443 # Date 0 0
1437 1444 # Thu Jan 01 00:00:00 1970 +0000
1438 1445 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1439 1446 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1440 1447 extended jungle
1441 1448
1442 1449 $ hg status -c .
1443 1450 C a
1444 1451 C b
1445 1452 $ ls
1446 1453 a
1447 1454 a.rej
1448 1455 b
1449 1456 jungle.rej
1450 1457
1451 1458 Importing multiple failing patches:
1452 1459
1453 1460 $ hg update --rev 'desc(initial)'
1454 1461 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1455 1462 $ echo 'B' > b # just to make another commit
1456 1463 $ hg commit -m "a new base"
1457 1464 created new head
1458 1465 $ hg export --rev 'desc("four") + desc("extended jungle")' | hg import --partial -
1459 1466 applying patch from stdin
1460 1467 patching file a
1461 1468 Hunk #1 FAILED at 0
1462 1469 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1463 1470 patch applied partially
1464 1471 (fix the .rej files and run `hg commit --amend`)
1465 1472 [1]
1466 1473 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1467 1474 @ four [Rataxes] 0: +0/-0
1468 1475 |
1469 1476 o a new base [test] 1: +1/-1
1470 1477 |
1471 1478 | o extended jungle [Cornelius] 0: +0/-0
1472 1479 |/
1473 1480 | o four [Rataxes] 0: +0/-0
1474 1481 |/
1475 1482 | o five [Arthur] 1: +1/-0
1476 1483 |/
1477 1484 | o extended jungle [Cornelius] 1: +1/-0
1478 1485 | |
1479 1486 | o jungle [Zephir] 1: +1/-0
1480 1487 | |
1481 1488 | o five [Arthur] 2: +2/-1
1482 1489 | |
1483 1490 | o four [Rataxes] 1: +1/-1
1484 1491 | |
1485 1492 | o three [Celeste] 1: +1/-1
1486 1493 |/
1487 1494 o initial [Babar] 2: +8/-0
1488 1495
1489 1496 $ hg export
1490 1497 # HG changeset patch
1491 1498 # User Rataxes
1492 1499 # Date 0 0
1493 1500 # Thu Jan 01 00:00:00 1970 +0000
1494 1501 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1495 1502 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1496 1503 four
1497 1504
1498 1505 $ hg status -c .
1499 1506 C a
1500 1507 C b
General Comments 0
You need to be logged in to leave comments. Login now