##// END OF EJS Templates
windows: use raw string in test log paths...
Raphaël Gomès -
r49114:7a4d1874 default
parent child Browse files
Show More
@@ -1,1852 +1,1852 b''
1 1 A script that implements uppercasing of specific lines in a file. This
2 2 approximates the behavior of code formatters well enough for our tests.
3 3
4 4 $ UPPERCASEPY="$TESTTMP/uppercase.py"
5 5 $ cat > $UPPERCASEPY <<EOF
6 6 > import re
7 7 > import sys
8 8 > from mercurial.utils.procutil import setbinary
9 9 > setbinary(sys.stdin)
10 10 > setbinary(sys.stdout)
11 11 > stdin = getattr(sys.stdin, 'buffer', sys.stdin)
12 12 > stdout = getattr(sys.stdout, 'buffer', sys.stdout)
13 13 > lines = set()
14 14 > def format(text):
15 15 > return re.sub(b' +', b' ', text.upper())
16 16 > for arg in sys.argv[1:]:
17 17 > if arg == 'all':
18 18 > stdout.write(format(stdin.read()))
19 19 > sys.exit(0)
20 20 > else:
21 21 > first, last = arg.split('-')
22 22 > lines.update(range(int(first), int(last) + 1))
23 23 > for i, line in enumerate(stdin.readlines()):
24 24 > if i + 1 in lines:
25 25 > stdout.write(format(line))
26 26 > else:
27 27 > stdout.write(line)
28 28 > EOF
29 29 $ TESTLINES="foo\nbar\nbaz\nqux\n"
30 30 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY
31 31 foo
32 32 bar
33 33 baz
34 34 qux
35 35 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY all
36 36 FOO
37 37 BAR
38 38 BAZ
39 39 QUX
40 40 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-1
41 41 FOO
42 42 bar
43 43 baz
44 44 qux
45 45 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-2
46 46 FOO
47 47 BAR
48 48 baz
49 49 qux
50 50 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-3
51 51 foo
52 52 BAR
53 53 BAZ
54 54 qux
55 55 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-2 4-4
56 56 foo
57 57 BAR
58 58 baz
59 59 QUX
60 60
61 61 Set up the config with two simple fixers: one that fixes specific line ranges,
62 62 and one that always fixes the whole file. They both "fix" files by converting
63 63 letters to uppercase. They use different file extensions, so each test case can
64 64 choose which behavior to use by naming files.
65 65
66 66 $ cat >> $HGRCPATH <<EOF
67 67 > [extensions]
68 68 > fix =
69 69 > [experimental]
70 70 > evolution.createmarkers=True
71 71 > evolution.allowunstable=True
72 72 > [fix]
73 73 > uppercase-whole-file:command="$PYTHON" $UPPERCASEPY all
74 74 > uppercase-whole-file:pattern=set:**.whole
75 75 > uppercase-changed-lines:command="$PYTHON" $UPPERCASEPY
76 76 > uppercase-changed-lines:linerange={first}-{last}
77 77 > uppercase-changed-lines:pattern=set:**.changed
78 78 > EOF
79 79
80 80 Help text for fix.
81 81
82 82 $ hg help fix
83 83 hg fix [OPTION]... [FILE]...
84 84
85 85 rewrite file content in changesets or working directory
86 86
87 87 Runs any configured tools to fix the content of files. Only affects files
88 88 with changes, unless file arguments are provided. Only affects changed
89 89 lines of files, unless the --whole flag is used. Some tools may always
90 90 affect the whole file regardless of --whole.
91 91
92 92 If --working-dir is used, files with uncommitted changes in the working
93 93 copy will be fixed. Note that no backup are made.
94 94
95 95 If revisions are specified with --source, those revisions and their
96 96 descendants will be checked, and they may be replaced with new revisions
97 97 that have fixed file content. By automatically including the descendants,
98 98 no merging, rebasing, or evolution will be required. If an ancestor of the
99 99 working copy is included, then the working copy itself will also be fixed,
100 100 and the working copy will be updated to the fixed parent.
101 101
102 102 When determining what lines of each file to fix at each revision, the
103 103 whole set of revisions being fixed is considered, so that fixes to earlier
104 104 revisions are not forgotten in later ones. The --base flag can be used to
105 105 override this default behavior, though it is not usually desirable to do
106 106 so.
107 107
108 108 (use 'hg help -e fix' to show help for the fix extension)
109 109
110 110 options ([+] can be repeated):
111 111
112 112 --all fix all non-public non-obsolete revisions
113 113 --base REV [+] revisions to diff against (overrides automatic selection,
114 114 and applies to every revision being fixed)
115 115 -s --source REV [+] fix the specified revisions and their descendants
116 116 -w --working-dir fix the working directory
117 117 --whole always fix every line of a file
118 118
119 119 (some details hidden, use --verbose to show complete help)
120 120
121 121 $ hg help -e fix
122 122 fix extension - rewrite file content in changesets or working copy
123 123 (EXPERIMENTAL)
124 124
125 125 Provides a command that runs configured tools on the contents of modified
126 126 files, writing back any fixes to the working copy or replacing changesets.
127 127
128 128 Here is an example configuration that causes 'hg fix' to apply automatic
129 129 formatting fixes to modified lines in C++ code:
130 130
131 131 [fix]
132 132 clang-format:command=clang-format --assume-filename={rootpath}
133 133 clang-format:linerange=--lines={first}:{last}
134 134 clang-format:pattern=set:**.cpp or **.hpp
135 135
136 136 The :command suboption forms the first part of the shell command that will be
137 137 used to fix a file. The content of the file is passed on standard input, and
138 138 the fixed file content is expected on standard output. Any output on standard
139 139 error will be displayed as a warning. If the exit status is not zero, the file
140 140 will not be affected. A placeholder warning is displayed if there is a non-
141 141 zero exit status but no standard error output. Some values may be substituted
142 142 into the command:
143 143
144 144 {rootpath} The path of the file being fixed, relative to the repo root
145 145 {basename} The name of the file being fixed, without the directory path
146 146
147 147 If the :linerange suboption is set, the tool will only be run if there are
148 148 changed lines in a file. The value of this suboption is appended to the shell
149 149 command once for every range of changed lines in the file. Some values may be
150 150 substituted into the command:
151 151
152 152 {first} The 1-based line number of the first line in the modified range
153 153 {last} The 1-based line number of the last line in the modified range
154 154
155 155 Deleted sections of a file will be ignored by :linerange, because there is no
156 156 corresponding line range in the version being fixed.
157 157
158 158 By default, tools that set :linerange will only be executed if there is at
159 159 least one changed line range. This is meant to prevent accidents like running
160 160 a code formatter in such a way that it unexpectedly reformats the whole file.
161 161 If such a tool needs to operate on unchanged files, it should set the
162 162 :skipclean suboption to false.
163 163
164 164 The :pattern suboption determines which files will be passed through each
165 165 configured tool. See 'hg help patterns' for possible values. However, all
166 166 patterns are relative to the repo root, even if that text says they are
167 167 relative to the current working directory. If there are file arguments to 'hg
168 168 fix', the intersection of these patterns is used.
169 169
170 170 There is also a configurable limit for the maximum size of file that will be
171 171 processed by 'hg fix':
172 172
173 173 [fix]
174 174 maxfilesize = 2MB
175 175
176 176 Normally, execution of configured tools will continue after a failure
177 177 (indicated by a non-zero exit status). It can also be configured to abort
178 178 after the first such failure, so that no files will be affected if any tool
179 179 fails. This abort will also cause 'hg fix' to exit with a non-zero status:
180 180
181 181 [fix]
182 182 failure = abort
183 183
184 184 When multiple tools are configured to affect a file, they execute in an order
185 185 defined by the :priority suboption. The priority suboption has a default value
186 186 of zero for each tool. Tools are executed in order of descending priority. The
187 187 execution order of tools with equal priority is unspecified. For example, you
188 188 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
189 189 in a text file by ensuring that 'sort' runs before 'head':
190 190
191 191 [fix]
192 192 sort:command = sort -n
193 193 head:command = head -n 10
194 194 sort:pattern = numbers.txt
195 195 head:pattern = numbers.txt
196 196 sort:priority = 2
197 197 head:priority = 1
198 198
199 199 To account for changes made by each tool, the line numbers used for
200 200 incremental formatting are recomputed before executing the next tool. So, each
201 201 tool may see different values for the arguments added by the :linerange
202 202 suboption.
203 203
204 204 Each fixer tool is allowed to return some metadata in addition to the fixed
205 205 file content. The metadata must be placed before the file content on stdout,
206 206 separated from the file content by a zero byte. The metadata is parsed as a
207 207 JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
208 208 tool is expected to produce this metadata encoding if and only if the
209 209 :metadata suboption is true:
210 210
211 211 [fix]
212 212 tool:command = tool --prepend-json-metadata
213 213 tool:metadata = true
214 214
215 215 The metadata values are passed to hooks, which can be used to print summaries
216 216 or perform other post-fixing work. The supported hooks are:
217 217
218 218 "postfixfile"
219 219 Run once for each file in each revision where any fixer tools made changes
220 220 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
221 221 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
222 222 tools that affected the file. Fixer tools that didn't affect the file have a
223 223 value of None. Only fixer tools that executed are present in the metadata.
224 224
225 225 "postfix"
226 226 Run once after all files and revisions have been handled. Provides
227 227 "$HG_REPLACEMENTS" with information about what revisions were created and
228 228 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
229 229 files in the working copy were updated. Provides a list "$HG_METADATA"
230 230 mapping fixer tool names to lists of metadata values returned from
231 231 executions that modified a file. This aggregates the same metadata
232 232 previously passed to the "postfixfile" hook.
233 233
234 234 Fixer tools are run in the repository's root directory. This allows them to
235 235 read configuration files from the working copy, or even write to the working
236 236 copy. The working copy is not updated to match the revision being fixed. In
237 237 fact, several revisions may be fixed in parallel. Writes to the working copy
238 238 are not amended into the revision being fixed; fixer tools should always write
239 239 fixed file content back to stdout as documented above.
240 240
241 241 list of commands:
242 242
243 243 fix rewrite file content in changesets or working directory
244 244
245 245 (use 'hg help -v -e fix' to show built-in aliases and global options)
246 246
247 247 There is no default behavior in the absence of --rev and --working-dir.
248 248
249 249 $ hg init badusage
250 250 $ cd badusage
251 251
252 252 $ hg fix
253 253 abort: no changesets specified
254 254 (use --source or --working-dir)
255 255 [255]
256 256 $ hg fix --whole
257 257 abort: no changesets specified
258 258 (use --source or --working-dir)
259 259 [255]
260 260 $ hg fix --base 0
261 261 abort: no changesets specified
262 262 (use --source or --working-dir)
263 263 [255]
264 264
265 265 Fixing a public revision isn't allowed. It should abort early enough that
266 266 nothing happens, even to the working directory.
267 267
268 268 $ printf "hello\n" > hello.whole
269 269 $ hg commit -Aqm "hello"
270 270 $ hg phase -r 0 --public
271 271 $ hg fix -r 0
272 272 abort: cannot fix public changesets: 6470986d2e7b
273 273 (see 'hg help phases' for details)
274 274 [10]
275 275 $ hg fix -r 0 --working-dir
276 276 abort: cannot fix public changesets: 6470986d2e7b
277 277 (see 'hg help phases' for details)
278 278 [10]
279 279 $ hg cat -r tip hello.whole
280 280 hello
281 281 $ cat hello.whole
282 282 hello
283 283
284 284 $ cd ..
285 285
286 286 Fixing a clean working directory should do nothing. Even the --whole flag
287 287 shouldn't cause any clean files to be fixed. Specifying a clean file explicitly
288 288 should only fix it if the fixer always fixes the whole file. The combination of
289 289 an explicit filename and --whole should format the entire file regardless.
290 290
291 291 $ hg init fixcleanwdir
292 292 $ cd fixcleanwdir
293 293
294 294 $ printf "hello\n" > hello.changed
295 295 $ printf "world\n" > hello.whole
296 296 $ hg commit -Aqm "foo"
297 297 $ hg fix --working-dir
298 298 $ hg diff
299 299 $ hg fix --working-dir --whole
300 300 $ hg diff
301 301 $ hg fix --working-dir *
302 302 $ cat *
303 303 hello
304 304 WORLD
305 305 $ hg revert --all --no-backup
306 306 reverting hello.whole
307 307 $ hg fix --working-dir * --whole
308 308 $ cat *
309 309 HELLO
310 310 WORLD
311 311
312 312 The same ideas apply to fixing a revision, so we create a revision that doesn't
313 313 modify either of the files in question and try fixing it. This also tests that
314 314 we ignore a file that doesn't match any configured fixer.
315 315
316 316 $ hg revert --all --no-backup
317 317 reverting hello.changed
318 318 reverting hello.whole
319 319 $ printf "unimportant\n" > some.file
320 320 $ hg commit -Aqm "some other file"
321 321
322 322 $ hg fix -r .
323 323 $ hg cat -r tip *
324 324 hello
325 325 world
326 326 unimportant
327 327 $ hg fix -r . --whole
328 328 $ hg cat -r tip *
329 329 hello
330 330 world
331 331 unimportant
332 332 $ hg fix -r . *
333 333 $ hg cat -r tip *
334 334 hello
335 335 WORLD
336 336 unimportant
337 337 $ hg fix -r . * --whole --config experimental.evolution.allowdivergence=true
338 338 2 new content-divergent changesets
339 339 $ hg cat -r tip *
340 340 HELLO
341 341 WORLD
342 342 unimportant
343 343
344 344 $ cd ..
345 345
346 346 Fixing the working directory should still work if there are no revisions.
347 347
348 348 $ hg init norevisions
349 349 $ cd norevisions
350 350
351 351 $ printf "something\n" > something.whole
352 352 $ hg add
353 353 adding something.whole
354 354 $ hg fix --working-dir
355 355 $ cat something.whole
356 356 SOMETHING
357 357
358 358 $ cd ..
359 359
360 360 Test that the working copy is reported clean if formatting of the parent makes
361 361 it clean.
362 362 $ hg init wc-already-formatted
363 363 $ cd wc-already-formatted
364 364
365 365 $ printf "hello world\n" > hello.whole
366 366 $ hg commit -Am initial
367 367 adding hello.whole
368 368 $ hg fix -w *
369 369 $ hg st
370 370 M hello.whole
371 371 $ hg fix -s . *
372 372 $ hg st
373 373 $ hg diff
374 374
375 375 $ cd ..
376 376
377 377 Test the effect of fixing the working directory for each possible status, with
378 378 and without providing explicit file arguments.
379 379
380 380 $ hg init implicitlyfixstatus
381 381 $ cd implicitlyfixstatus
382 382
383 383 $ printf "modified\n" > modified.whole
384 384 $ printf "removed\n" > removed.whole
385 385 $ printf "deleted\n" > deleted.whole
386 386 $ printf "clean\n" > clean.whole
387 387 $ printf "ignored.whole" > .hgignore
388 388 $ hg commit -Aqm "stuff"
389 389
390 390 $ printf "modified!!!\n" > modified.whole
391 391 $ printf "unknown\n" > unknown.whole
392 392 $ printf "ignored\n" > ignored.whole
393 393 $ printf "added\n" > added.whole
394 394 $ hg add added.whole
395 395 $ hg remove removed.whole
396 396 $ rm deleted.whole
397 397
398 398 $ hg status --all
399 399 M modified.whole
400 400 A added.whole
401 401 R removed.whole
402 402 ! deleted.whole
403 403 ? unknown.whole
404 404 I ignored.whole
405 405 C .hgignore
406 406 C clean.whole
407 407
408 408 $ hg fix --working-dir
409 409
410 410 $ hg status --all
411 411 M modified.whole
412 412 A added.whole
413 413 R removed.whole
414 414 ! deleted.whole
415 415 ? unknown.whole
416 416 I ignored.whole
417 417 C .hgignore
418 418 C clean.whole
419 419
420 420 $ cat *.whole
421 421 ADDED
422 422 clean
423 423 ignored
424 424 MODIFIED!!!
425 425 unknown
426 426
427 427 $ printf "modified!!!\n" > modified.whole
428 428 $ printf "added\n" > added.whole
429 429
430 430 Listing the files explicitly causes untracked files to also be fixed, but
431 431 ignored files are still unaffected.
432 432
433 433 $ hg fix --working-dir *.whole
434 434
435 435 $ hg status --all
436 436 M clean.whole
437 437 M modified.whole
438 438 A added.whole
439 439 R removed.whole
440 440 ! deleted.whole
441 441 ? unknown.whole
442 442 I ignored.whole
443 443 C .hgignore
444 444
445 445 $ cat *.whole
446 446 ADDED
447 447 CLEAN
448 448 ignored
449 449 MODIFIED!!!
450 450 UNKNOWN
451 451
452 452 $ cd ..
453 453
454 454 Test that incremental fixing works on files with additions, deletions, and
455 455 changes in multiple line ranges. Note that deletions do not generally cause
456 456 neighboring lines to be fixed, so we don't return a line range for purely
457 457 deleted sections. In the future we should support a :deletion config that
458 458 allows fixers to know where deletions are located.
459 459
460 460 $ hg init incrementalfixedlines
461 461 $ cd incrementalfixedlines
462 462
463 463 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.txt
464 464 $ hg commit -Aqm "foo"
465 465 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.txt
466 466
467 467 $ hg --config "fix.fail:command=echo" \
468 468 > --config "fix.fail:linerange={first}:{last}" \
469 469 > --config "fix.fail:pattern=foo.txt" \
470 470 > fix --working-dir
471 471 $ cat foo.txt
472 472 1:1 4:6 8:8
473 473
474 474 $ cd ..
475 475
476 476 Test that --whole fixes all lines regardless of the diffs present.
477 477
478 478 $ hg init wholeignoresdiffs
479 479 $ cd wholeignoresdiffs
480 480
481 481 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.changed
482 482 $ hg commit -Aqm "foo"
483 483 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.changed
484 484
485 485 $ hg fix --working-dir
486 486 $ cat foo.changed
487 487 ZZ
488 488 a
489 489 c
490 490 DD
491 491 EE
492 492 FF
493 493 f
494 494 GG
495 495
496 496 $ hg fix --working-dir --whole
497 497 $ cat foo.changed
498 498 ZZ
499 499 A
500 500 C
501 501 DD
502 502 EE
503 503 FF
504 504 F
505 505 GG
506 506
507 507 $ cd ..
508 508
509 509 We should do nothing with symlinks, and their targets should be unaffected. Any
510 510 other behavior would be more complicated to implement and harder to document.
511 511
512 512 #if symlink
513 513 $ hg init dontmesswithsymlinks
514 514 $ cd dontmesswithsymlinks
515 515
516 516 $ printf "hello\n" > hello.whole
517 517 $ ln -s hello.whole hellolink
518 518 $ hg add
519 519 adding hello.whole
520 520 adding hellolink
521 521 $ hg fix --working-dir hellolink
522 522 $ hg status
523 523 A hello.whole
524 524 A hellolink
525 525
526 526 $ cd ..
527 527 #endif
528 528
529 529 We should allow fixers to run on binary files, even though this doesn't sound
530 530 like a common use case. There's not much benefit to disallowing it, and users
531 531 can add "and not binary()" to their filesets if needed. The Mercurial
532 532 philosophy is generally to not handle binary files specially anyway.
533 533
534 534 $ hg init cantouchbinaryfiles
535 535 $ cd cantouchbinaryfiles
536 536
537 537 $ printf "hello\0\n" > hello.whole
538 538 $ hg add
539 539 adding hello.whole
540 540 $ hg fix --working-dir 'set:binary()'
541 541 $ cat hello.whole
542 542 HELLO\x00 (esc)
543 543
544 544 $ cd ..
545 545
546 546 We have a config for the maximum size of file we will attempt to fix. This can
547 547 be helpful to avoid running unsuspecting fixer tools on huge inputs, which
548 548 could happen by accident without a well considered configuration. A more
549 549 precise configuration could use the size() fileset function if one global limit
550 550 is undesired.
551 551
552 552 $ hg init maxfilesize
553 553 $ cd maxfilesize
554 554
555 555 $ printf "this file is huge\n" > hello.whole
556 556 $ hg add
557 557 adding hello.whole
558 558 $ hg --config fix.maxfilesize=10 fix --working-dir
559 559 ignoring file larger than 10 bytes: hello.whole
560 560 $ cat hello.whole
561 561 this file is huge
562 562
563 563 $ cd ..
564 564
565 565 If we specify a file to fix, other files should be left alone, even if they
566 566 have changes.
567 567
568 568 $ hg init fixonlywhatitellyouto
569 569 $ cd fixonlywhatitellyouto
570 570
571 571 $ printf "fix me!\n" > fixme.whole
572 572 $ printf "not me.\n" > notme.whole
573 573 $ hg add
574 574 adding fixme.whole
575 575 adding notme.whole
576 576 $ hg fix --working-dir fixme.whole
577 577 $ cat *.whole
578 578 FIX ME!
579 579 not me.
580 580
581 581 $ cd ..
582 582
583 583 If we try to fix a missing file, we still fix other files.
584 584
585 585 $ hg init fixmissingfile
586 586 $ cd fixmissingfile
587 587
588 588 $ printf "fix me!\n" > foo.whole
589 589 $ hg add
590 590 adding foo.whole
591 591 $ hg fix --working-dir foo.whole bar.whole
592 592 bar.whole: $ENOENT$
593 593 $ cat *.whole
594 594 FIX ME!
595 595
596 596 $ cd ..
597 597
598 598 Specifying a directory name should fix all its files and subdirectories.
599 599
600 600 $ hg init fixdirectory
601 601 $ cd fixdirectory
602 602
603 603 $ mkdir -p dir1/dir2
604 604 $ printf "foo\n" > foo.whole
605 605 $ printf "bar\n" > dir1/bar.whole
606 606 $ printf "baz\n" > dir1/dir2/baz.whole
607 607 $ hg add
608 608 adding dir1/bar.whole
609 609 adding dir1/dir2/baz.whole
610 610 adding foo.whole
611 611 $ hg fix --working-dir dir1
612 612 $ cat foo.whole dir1/bar.whole dir1/dir2/baz.whole
613 613 foo
614 614 BAR
615 615 BAZ
616 616
617 617 $ cd ..
618 618
619 619 Fixing a file in the working directory that needs no fixes should not actually
620 620 write back to the file, so for example the mtime shouldn't change.
621 621
622 622 $ hg init donttouchunfixedfiles
623 623 $ cd donttouchunfixedfiles
624 624
625 625 $ printf "NO FIX NEEDED\n" > foo.whole
626 626 $ hg add
627 627 adding foo.whole
628 628 $ cp -p foo.whole foo.whole.orig
629 629 $ cp -p foo.whole.orig foo.whole
630 630 $ sleep 2 # mtime has a resolution of one or two seconds.
631 631 $ hg fix --working-dir
632 632 $ f foo.whole.orig --newer foo.whole
633 633 foo.whole.orig: newer than foo.whole
634 634
635 635 $ cd ..
636 636
637 637 When a fixer prints to stderr, we don't assume that it has failed. We show the
638 638 error messages to the user, and we still let the fixer affect the file it was
639 639 fixing if its exit code is zero. Some code formatters might emit error messages
640 640 on stderr and nothing on stdout, which would cause us the clear the file,
641 641 except that they also exit with a non-zero code. We show the user which fixer
642 642 emitted the stderr, and which revision, but we assume that the fixer will print
643 643 the filename if it is relevant (since the issue may be non-specific). There is
644 644 also a config to abort (without affecting any files whatsoever) if we see any
645 645 tool with a non-zero exit status.
646 646
647 647 $ hg init showstderr
648 648 $ cd showstderr
649 649
650 650 $ printf "hello\n" > hello.txt
651 651 $ hg add
652 652 adding hello.txt
653 653 $ cat > $TESTTMP/work.sh <<'EOF'
654 654 > printf 'HELLO\n'
655 655 > printf "$@: some\nerror that didn't stop the tool" >&2
656 656 > exit 0 # success despite the stderr output
657 657 > EOF
658 658 $ hg --config "fix.work:command=sh $TESTTMP/work.sh {rootpath}" \
659 659 > --config "fix.work:pattern=hello.txt" \
660 660 > fix --working-dir
661 661 [wdir] work: hello.txt: some
662 662 [wdir] work: error that didn't stop the tool
663 663 $ cat hello.txt
664 664 HELLO
665 665
666 666 $ printf "goodbye\n" > hello.txt
667 667 $ printf "foo\n" > foo.whole
668 668 $ hg add
669 669 adding foo.whole
670 670 $ cat > $TESTTMP/fail.sh <<'EOF'
671 671 > printf 'GOODBYE\n'
672 672 > printf "$@: some\nerror that did stop the tool\n" >&2
673 673 > exit 42 # success despite the stdout output
674 674 > EOF
675 675 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
676 676 > --config "fix.fail:pattern=hello.txt" \
677 677 > --config "fix.failure=abort" \
678 678 > fix --working-dir
679 679 [wdir] fail: hello.txt: some
680 680 [wdir] fail: error that did stop the tool
681 681 abort: no fixes will be applied
682 682 (use --config fix.failure=continue to apply any successful fixes anyway)
683 683 [255]
684 684 $ cat hello.txt
685 685 goodbye
686 686 $ cat foo.whole
687 687 foo
688 688
689 689 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
690 690 > --config "fix.fail:pattern=hello.txt" \
691 691 > fix --working-dir
692 692 [wdir] fail: hello.txt: some
693 693 [wdir] fail: error that did stop the tool
694 694 $ cat hello.txt
695 695 goodbye
696 696 $ cat foo.whole
697 697 FOO
698 698
699 699 $ hg --config "fix.fail:command=exit 42" \
700 700 > --config "fix.fail:pattern=hello.txt" \
701 701 > fix --working-dir
702 702 [wdir] fail: exited with status 42
703 703
704 704 $ cd ..
705 705
706 706 Fixing the working directory and its parent revision at the same time should
707 707 check out the replacement revision for the parent. This prevents any new
708 708 uncommitted changes from appearing. We test this for a clean working directory
709 709 and a dirty one. In both cases, all lines/files changed since the grandparent
710 710 will be fixed. The grandparent is the "baserev" for both the parent and the
711 711 working copy.
712 712
713 713 $ hg init fixdotandcleanwdir
714 714 $ cd fixdotandcleanwdir
715 715
716 716 $ printf "hello\n" > hello.whole
717 717 $ printf "world\n" > world.whole
718 718 $ hg commit -Aqm "the parent commit"
719 719
720 720 $ hg parents --template '{rev} {desc}\n'
721 721 0 the parent commit
722 722 $ hg fix --working-dir -r .
723 723 $ hg parents --template '{rev} {desc}\n'
724 724 1 the parent commit
725 725 $ hg cat -r . *.whole
726 726 HELLO
727 727 WORLD
728 728 $ cat *.whole
729 729 HELLO
730 730 WORLD
731 731 $ hg status
732 732
733 733 $ cd ..
734 734
735 735 Same test with a dirty working copy.
736 736
737 737 $ hg init fixdotanddirtywdir
738 738 $ cd fixdotanddirtywdir
739 739
740 740 $ printf "hello\n" > hello.whole
741 741 $ printf "world\n" > world.whole
742 742 $ hg commit -Aqm "the parent commit"
743 743
744 744 $ printf "hello,\n" > hello.whole
745 745 $ printf "world!\n" > world.whole
746 746
747 747 $ hg parents --template '{rev} {desc}\n'
748 748 0 the parent commit
749 749 $ hg fix --working-dir -r .
750 750 $ hg parents --template '{rev} {desc}\n'
751 751 1 the parent commit
752 752 $ hg cat -r . *.whole
753 753 HELLO
754 754 WORLD
755 755 $ cat *.whole
756 756 HELLO,
757 757 WORLD!
758 758 $ hg status
759 759 M hello.whole
760 760 M world.whole
761 761
762 762 $ cd ..
763 763
764 764 When we have a chain of commits that change mutually exclusive lines of code,
765 765 we should be able to do incremental fixing that causes each commit in the chain
766 766 to include fixes made to the previous commits. This prevents children from
767 767 backing out the fixes made in their parents. A dirty working directory is
768 768 conceptually similar to another commit in the chain.
769 769
770 770 $ hg init incrementallyfixchain
771 771 $ cd incrementallyfixchain
772 772
773 773 $ cat > file.changed <<EOF
774 774 > first
775 775 > second
776 776 > third
777 777 > fourth
778 778 > fifth
779 779 > EOF
780 780 $ hg commit -Aqm "the common ancestor (the baserev)"
781 781 $ cat > file.changed <<EOF
782 782 > first (changed)
783 783 > second
784 784 > third
785 785 > fourth
786 786 > fifth
787 787 > EOF
788 788 $ hg commit -Aqm "the first commit to fix"
789 789 $ cat > file.changed <<EOF
790 790 > first (changed)
791 791 > second
792 792 > third (changed)
793 793 > fourth
794 794 > fifth
795 795 > EOF
796 796 $ hg commit -Aqm "the second commit to fix"
797 797 $ cat > file.changed <<EOF
798 798 > first (changed)
799 799 > second
800 800 > third (changed)
801 801 > fourth
802 802 > fifth (changed)
803 803 > EOF
804 804
805 805 $ hg fix -r . -r '.^' --working-dir
806 806
807 807 $ hg parents --template '{rev}\n'
808 808 4
809 809 $ hg cat -r '.^^' file.changed
810 810 first
811 811 second
812 812 third
813 813 fourth
814 814 fifth
815 815 $ hg cat -r '.^' file.changed
816 816 FIRST (CHANGED)
817 817 second
818 818 third
819 819 fourth
820 820 fifth
821 821 $ hg cat -r . file.changed
822 822 FIRST (CHANGED)
823 823 second
824 824 THIRD (CHANGED)
825 825 fourth
826 826 fifth
827 827 $ cat file.changed
828 828 FIRST (CHANGED)
829 829 second
830 830 THIRD (CHANGED)
831 831 fourth
832 832 FIFTH (CHANGED)
833 833
834 834 $ cd ..
835 835
836 836 If we incrementally fix a merge commit, we should fix any lines that changed
837 837 versus either parent. You could imagine only fixing the intersection or some
838 838 other subset, but this is necessary if either parent is being fixed. It
839 839 prevents us from forgetting fixes made in either parent.
840 840
841 841 $ hg init incrementallyfixmergecommit
842 842 $ cd incrementallyfixmergecommit
843 843
844 844 $ printf "a\nb\nc\n" > file.changed
845 845 $ hg commit -Aqm "ancestor"
846 846
847 847 $ printf "aa\nb\nc\n" > file.changed
848 848 $ hg commit -m "change a"
849 849
850 850 $ hg checkout '.^'
851 851 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
852 852 $ printf "a\nb\ncc\n" > file.changed
853 853 $ hg commit -m "change c"
854 854 created new head
855 855
856 856 $ hg merge
857 857 merging file.changed
858 858 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
859 859 (branch merge, don't forget to commit)
860 860 $ hg commit -m "merge"
861 861 $ hg cat -r . file.changed
862 862 aa
863 863 b
864 864 cc
865 865
866 866 $ hg fix -r . --working-dir
867 867 $ hg cat -r . file.changed
868 868 AA
869 869 b
870 870 CC
871 871
872 872 $ cd ..
873 873
874 874 We should be allowed to fix the working (and only the working copy) while
875 875 merging.
876 876
877 877 $ hg init fixworkingcopywhilemerging
878 878 $ cd fixworkingcopywhilemerging
879 879
880 880 $ printf "a\nb\nc\n" > file.changed
881 881 $ hg commit -Aqm "ancestor"
882 882
883 883 $ printf "aa\nb\nc\n" > file.changed
884 884 $ hg commit -m "change a"
885 885
886 886 $ hg checkout '.^'
887 887 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
888 888 $ printf "a\nb\ncc\n" > file.changed
889 889 $ hg commit -m "change c"
890 890 created new head
891 891
892 892 $ hg merge
893 893 merging file.changed
894 894 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
895 895 (branch merge, don't forget to commit)
896 896 $ cat file.changed
897 897 aa
898 898 b
899 899 cc
900 900 Not allowed to fix a parent of the working copy while merging
901 901 $ hg fix -r . --working-dir
902 902 abort: outstanding uncommitted merge
903 903 (use 'hg commit' or 'hg merge --abort')
904 904 [20]
905 905 $ hg fix --working-dir
906 906 $ cat file.changed
907 907 AA
908 908 b
909 909 CC
910 910
911 911 $ cd ..
912 912
913 913 Abort fixing revisions if there is an unfinished operation. We don't want to
914 914 make things worse by editing files or stripping/obsoleting things. Also abort
915 915 fixing the working directory if there are unresolved merge conflicts.
916 916
917 917 $ hg init abortunresolved
918 918 $ cd abortunresolved
919 919
920 920 $ echo "foo1" > foo.whole
921 921 $ hg commit -Aqm "foo 1"
922 922
923 923 $ hg update null
924 924 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
925 925 $ echo "foo2" > foo.whole
926 926 $ hg commit -Aqm "foo 2"
927 927
928 928 $ hg --config extensions.rebase= rebase -r 1 -d 0
929 929 rebasing 1:c3b6dc0e177a tip "foo 2"
930 930 merging foo.whole
931 931 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
932 932 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
933 933 [240]
934 934
935 935 $ hg --config extensions.rebase= fix --working-dir
936 936 abort: unresolved conflicts
937 937 (use 'hg resolve')
938 938 [255]
939 939
940 940 $ hg --config extensions.rebase= fix -r .
941 941 abort: rebase in progress
942 942 (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop')
943 943 [20]
944 944
945 945 $ cd ..
946 946
947 947 When fixing a file that was renamed, we should diff against the source of the
948 948 rename for incremental fixing and we should correctly reproduce the rename in
949 949 the replacement revision.
950 950
951 951 $ hg init fixrenamecommit
952 952 $ cd fixrenamecommit
953 953
954 954 $ printf "a\nb\nc\n" > source.changed
955 955 $ hg commit -Aqm "source revision"
956 956 $ hg move source.changed dest.changed
957 957 $ printf "a\nb\ncc\n" > dest.changed
958 958 $ hg commit -m "dest revision"
959 959
960 960 $ hg fix -r .
961 961 $ hg log -r tip --copies --template "{file_copies}\n"
962 962 dest.changed (source.changed)
963 963 $ hg cat -r tip dest.changed
964 964 a
965 965 b
966 966 CC
967 967
968 968 $ cd ..
969 969
970 970 When fixing revisions that remove files we must ensure that the replacement
971 971 actually removes the file, whereas it could accidentally leave it unchanged or
972 972 write an empty string to it.
973 973
974 974 $ hg init fixremovedfile
975 975 $ cd fixremovedfile
976 976
977 977 $ printf "foo\n" > foo.whole
978 978 $ printf "bar\n" > bar.whole
979 979 $ hg commit -Aqm "add files"
980 980 $ hg remove bar.whole
981 981 $ hg commit -m "remove file"
982 982 $ hg status --change .
983 983 R bar.whole
984 984 $ hg fix -r . foo.whole
985 985 $ hg status --change tip
986 986 M foo.whole
987 987 R bar.whole
988 988
989 989 $ cd ..
990 990
991 991 If fixing a revision finds no fixes to make, no replacement revision should be
992 992 created.
993 993
994 994 $ hg init nofixesneeded
995 995 $ cd nofixesneeded
996 996
997 997 $ printf "FOO\n" > foo.whole
998 998 $ hg commit -Aqm "add file"
999 999 $ hg log --template '{rev}\n'
1000 1000 0
1001 1001 $ hg fix -r .
1002 1002 $ hg log --template '{rev}\n'
1003 1003 0
1004 1004
1005 1005 $ cd ..
1006 1006
1007 1007 If fixing a commit reverts all the changes in the commit, we replace it with a
1008 1008 commit that changes no files.
1009 1009
1010 1010 $ hg init nochangesleft
1011 1011 $ cd nochangesleft
1012 1012
1013 1013 $ printf "FOO\n" > foo.whole
1014 1014 $ hg commit -Aqm "add file"
1015 1015 $ printf "foo\n" > foo.whole
1016 1016 $ hg commit -m "edit file"
1017 1017 $ hg status --change .
1018 1018 M foo.whole
1019 1019 $ hg fix -r .
1020 1020 $ hg status --change tip
1021 1021
1022 1022 $ cd ..
1023 1023
1024 1024 If we fix a parent and child revision together, the child revision must be
1025 1025 replaced if the parent is replaced, even if the diffs of the child needed no
1026 1026 fixes. However, we're free to not replace revisions that need no fixes and have
1027 1027 no ancestors that are replaced.
1028 1028
1029 1029 $ hg init mustreplacechild
1030 1030 $ cd mustreplacechild
1031 1031
1032 1032 $ printf "FOO\n" > foo.whole
1033 1033 $ hg commit -Aqm "add foo"
1034 1034 $ printf "foo\n" > foo.whole
1035 1035 $ hg commit -m "edit foo"
1036 1036 $ printf "BAR\n" > bar.whole
1037 1037 $ hg commit -Aqm "add bar"
1038 1038
1039 1039 $ hg log --graph --template '{rev} {files}'
1040 1040 @ 2 bar.whole
1041 1041 |
1042 1042 o 1 foo.whole
1043 1043 |
1044 1044 o 0 foo.whole
1045 1045
1046 1046 $ hg fix -r 0:2
1047 1047 $ hg log --graph --template '{rev} {files}'
1048 1048 o 4 bar.whole
1049 1049 |
1050 1050 o 3
1051 1051 |
1052 1052 | @ 2 bar.whole
1053 1053 | |
1054 1054 | x 1 foo.whole
1055 1055 |/
1056 1056 o 0 foo.whole
1057 1057
1058 1058
1059 1059 $ cd ..
1060 1060
1061 1061 It's also possible that the child needs absolutely no changes, but we still
1062 1062 need to replace it to update its parent. If we skipped replacing the child
1063 1063 because it had no file content changes, it would become an orphan for no good
1064 1064 reason.
1065 1065
1066 1066 $ hg init mustreplacechildevenifnop
1067 1067 $ cd mustreplacechildevenifnop
1068 1068
1069 1069 $ printf "Foo\n" > foo.whole
1070 1070 $ hg commit -Aqm "add a bad foo"
1071 1071 $ printf "FOO\n" > foo.whole
1072 1072 $ hg commit -m "add a good foo"
1073 1073 $ hg fix -r . -r '.^'
1074 1074 $ hg log --graph --template '{rev} {desc}'
1075 1075 o 3 add a good foo
1076 1076 |
1077 1077 o 2 add a bad foo
1078 1078
1079 1079 @ 1 add a good foo
1080 1080 |
1081 1081 x 0 add a bad foo
1082 1082
1083 1083
1084 1084 $ cd ..
1085 1085
1086 1086 Similar to the case above, the child revision may become empty as a result of
1087 1087 fixing its parent. We should still create an empty replacement child.
1088 1088 TODO: determine how this should interact with ui.allowemptycommit given that
1089 1089 the empty replacement could have children.
1090 1090
1091 1091 $ hg init mustreplacechildevenifempty
1092 1092 $ cd mustreplacechildevenifempty
1093 1093
1094 1094 $ printf "foo\n" > foo.whole
1095 1095 $ hg commit -Aqm "add foo"
1096 1096 $ printf "Foo\n" > foo.whole
1097 1097 $ hg commit -m "edit foo"
1098 1098 $ hg fix -r . -r '.^'
1099 1099 $ hg log --graph --template '{rev} {desc}\n' --stat
1100 1100 o 3 edit foo
1101 1101 |
1102 1102 o 2 add foo
1103 1103 foo.whole | 1 +
1104 1104 1 files changed, 1 insertions(+), 0 deletions(-)
1105 1105
1106 1106 @ 1 edit foo
1107 1107 | foo.whole | 2 +-
1108 1108 | 1 files changed, 1 insertions(+), 1 deletions(-)
1109 1109 |
1110 1110 x 0 add foo
1111 1111 foo.whole | 1 +
1112 1112 1 files changed, 1 insertions(+), 0 deletions(-)
1113 1113
1114 1114
1115 1115 $ cd ..
1116 1116
1117 1117 Fixing a secret commit should replace it with another secret commit.
1118 1118
1119 1119 $ hg init fixsecretcommit
1120 1120 $ cd fixsecretcommit
1121 1121
1122 1122 $ printf "foo\n" > foo.whole
1123 1123 $ hg commit -Aqm "add foo" --secret
1124 1124 $ hg fix -r .
1125 1125 $ hg log --template '{rev} {phase}\n'
1126 1126 1 secret
1127 1127 0 secret
1128 1128
1129 1129 $ cd ..
1130 1130
1131 1131 We should also preserve phase when fixing a draft commit while the user has
1132 1132 their default set to secret.
1133 1133
1134 1134 $ hg init respectphasesnewcommit
1135 1135 $ cd respectphasesnewcommit
1136 1136
1137 1137 $ printf "foo\n" > foo.whole
1138 1138 $ hg commit -Aqm "add foo"
1139 1139 $ hg --config phases.newcommit=secret fix -r .
1140 1140 $ hg log --template '{rev} {phase}\n'
1141 1141 1 draft
1142 1142 0 draft
1143 1143
1144 1144 $ cd ..
1145 1145
1146 1146 Debug output should show what fixer commands are being subprocessed, which is
1147 1147 useful for anyone trying to set up a new config.
1148 1148
1149 1149 $ hg init debugoutput
1150 1150 $ cd debugoutput
1151 1151
1152 1152 $ printf "foo\nbar\nbaz\n" > foo.changed
1153 1153 $ hg commit -Aqm "foo"
1154 1154 $ printf "Foo\nbar\nBaz\n" > foo.changed
1155 1155 $ hg --debug fix --working-dir
1156 1156 subprocess: * $TESTTMP/uppercase.py 1-1 3-3 (glob)
1157 1157
1158 1158 $ cd ..
1159 1159
1160 1160 Fixing an obsolete revision can cause divergence, so we abort unless the user
1161 1161 configures to allow it. This is not yet smart enough to know whether there is a
1162 1162 successor, but even then it is not likely intentional or idiomatic to fix an
1163 1163 obsolete revision.
1164 1164
1165 1165 $ hg init abortobsoleterev
1166 1166 $ cd abortobsoleterev
1167 1167
1168 1168 $ printf "foo\n" > foo.changed
1169 1169 $ hg commit -Aqm "foo"
1170 1170 $ hg ci --amend -m rewritten
1171 1171 $ hg --hidden fix -r 0
1172 1172 abort: fixing obsolete revision could cause divergence
1173 1173 [255]
1174 1174
1175 1175 $ hg --hidden fix -r 0 --config experimental.evolution.allowdivergence=true
1176 1176 2 new content-divergent changesets
1177 1177 $ hg cat -r tip foo.changed
1178 1178 FOO
1179 1179
1180 1180 $ cd ..
1181 1181
1182 1182 Test all of the available substitution values for fixer commands.
1183 1183
1184 1184 $ hg init substitution
1185 1185 $ cd substitution
1186 1186
1187 1187 $ mkdir foo
1188 1188 $ printf "hello\ngoodbye\n" > foo/bar
1189 1189 $ hg add
1190 1190 adding foo/bar
1191 1191 $ hg --config "fix.fail:command=printf '%s\n' '{rootpath}' '{basename}'" \
1192 1192 > --config "fix.fail:linerange='{first}' '{last}'" \
1193 1193 > --config "fix.fail:pattern=foo/bar" \
1194 1194 > fix --working-dir
1195 1195 $ cat foo/bar
1196 1196 foo/bar
1197 1197 bar
1198 1198 1
1199 1199 2
1200 1200
1201 1201 $ cd ..
1202 1202
1203 1203 The --base flag should allow picking the revisions to diff against for changed
1204 1204 files and incremental line formatting.
1205 1205
1206 1206 $ hg init baseflag
1207 1207 $ cd baseflag
1208 1208
1209 1209 $ printf "one\ntwo\n" > foo.changed
1210 1210 $ printf "bar\n" > bar.changed
1211 1211 $ hg commit -Aqm "first"
1212 1212 $ printf "one\nTwo\n" > foo.changed
1213 1213 $ hg commit -m "second"
1214 1214 $ hg fix -w --base .
1215 1215 $ hg status
1216 1216 $ hg fix -w --base null
1217 1217 $ cat foo.changed
1218 1218 ONE
1219 1219 TWO
1220 1220 $ cat bar.changed
1221 1221 BAR
1222 1222
1223 1223 $ cd ..
1224 1224
1225 1225 If the user asks to fix the parent of another commit, they are asking to create
1226 1226 an orphan. We must respect experimental.evolution.allowunstable.
1227 1227
1228 1228 $ hg init allowunstable
1229 1229 $ cd allowunstable
1230 1230
1231 1231 $ printf "one\n" > foo.whole
1232 1232 $ hg commit -Aqm "first"
1233 1233 $ printf "two\n" > foo.whole
1234 1234 $ hg commit -m "second"
1235 1235 $ hg --config experimental.evolution.allowunstable=False fix -r '.^'
1236 1236 abort: cannot fix changeset, as that will orphan 1 descendants
1237 1237 (see 'hg help evolution.instability')
1238 1238 [10]
1239 1239 $ hg fix -r '.^'
1240 1240 1 new orphan changesets
1241 1241 $ hg cat -r 2 foo.whole
1242 1242 ONE
1243 1243
1244 1244 $ cd ..
1245 1245
1246 1246 The --base flag affects the set of files being fixed. So while the --whole flag
1247 1247 makes the base irrelevant for changed line ranges, it still changes the
1248 1248 meaning and effect of the command. In this example, no files or lines are fixed
1249 1249 until we specify the base, but then we do fix unchanged lines.
1250 1250
1251 1251 $ hg init basewhole
1252 1252 $ cd basewhole
1253 1253 $ printf "foo1\n" > foo.changed
1254 1254 $ hg commit -Aqm "first"
1255 1255 $ printf "foo2\n" >> foo.changed
1256 1256 $ printf "bar\n" > bar.changed
1257 1257 $ hg commit -Aqm "second"
1258 1258
1259 1259 $ hg fix --working-dir --whole
1260 1260 $ cat *.changed
1261 1261 bar
1262 1262 foo1
1263 1263 foo2
1264 1264
1265 1265 $ hg fix --working-dir --base 0 --whole
1266 1266 $ cat *.changed
1267 1267 BAR
1268 1268 FOO1
1269 1269 FOO2
1270 1270
1271 1271 $ cd ..
1272 1272
1273 1273 The execution order of tools can be controlled. This example doesn't work if
1274 1274 you sort after truncating, but the config defines the correct order while the
1275 1275 definitions are out of order (which might imply the incorrect order given the
1276 1276 implementation of fix). The goal is to use multiple tools to select the lowest
1277 1277 5 numbers in the file.
1278 1278
1279 1279 $ hg init priorityexample
1280 1280 $ cd priorityexample
1281 1281
1282 1282 $ cat >> .hg/hgrc <<EOF
1283 1283 > [fix]
1284 1284 > head:command = head -n 5
1285 1285 > head:pattern = numbers.txt
1286 1286 > head:priority = 1
1287 1287 > sort:command = sort -n
1288 1288 > sort:pattern = numbers.txt
1289 1289 > sort:priority = 2
1290 1290 > EOF
1291 1291
1292 1292 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1293 1293 $ hg add -q
1294 1294 $ hg fix -w
1295 1295 $ cat numbers.txt
1296 1296 0
1297 1297 1
1298 1298 2
1299 1299 3
1300 1300 4
1301 1301
1302 1302 And of course we should be able to break this by reversing the execution order.
1303 1303 Test negative priorities while we're at it.
1304 1304
1305 1305 $ cat >> .hg/hgrc <<EOF
1306 1306 > [fix]
1307 1307 > head:priority = -1
1308 1308 > sort:priority = -2
1309 1309 > EOF
1310 1310 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1311 1311 $ hg fix -w
1312 1312 $ cat numbers.txt
1313 1313 2
1314 1314 3
1315 1315 6
1316 1316 7
1317 1317 8
1318 1318
1319 1319 $ cd ..
1320 1320
1321 1321 It's possible for repeated applications of a fixer tool to create cycles in the
1322 1322 generated content of a file. For example, two users with different versions of
1323 1323 a code formatter might fight over the formatting when they run hg fix. In the
1324 1324 absence of other changes, this means we could produce commits with the same
1325 1325 hash in subsequent runs of hg fix. This is a problem unless we support
1326 1326 obsolescence cycles well. We avoid this by adding an extra field to the
1327 1327 successor which forces it to have a new hash. That's why this test creates
1328 1328 three revisions instead of two.
1329 1329
1330 1330 $ hg init cyclictool
1331 1331 $ cd cyclictool
1332 1332
1333 1333 $ cat >> .hg/hgrc <<EOF
1334 1334 > [fix]
1335 1335 > swapletters:command = tr ab ba
1336 1336 > swapletters:pattern = foo
1337 1337 > EOF
1338 1338
1339 1339 $ echo ab > foo
1340 1340 $ hg commit -Aqm foo
1341 1341
1342 1342 $ hg fix -r 0
1343 1343 $ hg fix -r 1
1344 1344
1345 1345 $ hg cat -r 0 foo --hidden
1346 1346 ab
1347 1347 $ hg cat -r 1 foo --hidden
1348 1348 ba
1349 1349 $ hg cat -r 2 foo
1350 1350 ab
1351 1351
1352 1352 $ cd ..
1353 1353
1354 1354 We run fixer tools in the repo root so they can look for config files or other
1355 1355 important things in the working directory. This does NOT mean we are
1356 1356 reconstructing a working copy of every revision being fixed; we're just giving
1357 1357 the tool knowledge of the repo's location in case it can do something
1358 1358 reasonable with that.
1359 1359
1360 1360 $ hg init subprocesscwd
1361 1361 $ cd subprocesscwd
1362 1362
1363 1363 $ cat >> .hg/hgrc <<EOF
1364 1364 > [fix]
1365 1365 > printcwd:command = "$PYTHON" -c "import os; print(os.getcwd())"
1366 1366 > printcwd:pattern = relpath:foo/bar
1367 1367 > filesetpwd:command = "$PYTHON" -c "import os; print('fs: ' + os.getcwd())"
1368 1368 > filesetpwd:pattern = set:**quux
1369 1369 > EOF
1370 1370
1371 1371 $ mkdir foo
1372 1372 $ printf "bar\n" > foo/bar
1373 1373 $ printf "quux\n" > quux
1374 1374 $ hg commit -Aqm blah
1375 1375
1376 1376 $ hg fix -w -r . foo/bar
1377 1377 $ hg cat -r tip foo/bar
1378 1378 $TESTTMP/subprocesscwd
1379 1379 $ cat foo/bar
1380 1380 $TESTTMP/subprocesscwd
1381 1381
1382 1382 $ cd foo
1383 1383
1384 1384 $ hg fix -w -r . bar
1385 1385 $ hg cat -r tip bar ../quux
1386 1386 $TESTTMP/subprocesscwd
1387 1387 quux
1388 1388 $ cat bar ../quux
1389 1389 $TESTTMP/subprocesscwd
1390 1390 quux
1391 1391 $ echo modified > bar
1392 1392 $ hg fix -w bar
1393 1393 $ cat bar
1394 1394 $TESTTMP/subprocesscwd
1395 1395
1396 1396 Apparently fixing p1() and its descendants doesn't include wdir() unless
1397 1397 explicitly stated.
1398 1398
1399 1399 $ hg fix -r '.::'
1400 1400 $ hg cat -r . ../quux
1401 1401 quux
1402 1402 $ hg cat -r tip ../quux
1403 1403 fs: $TESTTMP/subprocesscwd
1404 1404 $ cat ../quux
1405 1405 quux
1406 1406
1407 1407 Clean files are not fixed unless explicitly named
1408 1408 $ echo 'dirty' > ../quux
1409 1409
1410 1410 $ hg fix --working-dir
1411 1411 $ cat ../quux
1412 1412 fs: $TESTTMP/subprocesscwd
1413 1413
1414 1414 $ cd ../..
1415 1415
1416 1416 Tools configured without a pattern are ignored. It would be too dangerous to
1417 1417 run them on all files, because this might happen while testing a configuration
1418 1418 that also deletes all of the file content. There is no reasonable subset of the
1419 1419 files to use as a default. Users should be explicit about what files are
1420 1420 affected by a tool. This test also confirms that we don't crash when the
1421 1421 pattern config is missing, and that we only warn about it once.
1422 1422
1423 1423 $ hg init nopatternconfigured
1424 1424 $ cd nopatternconfigured
1425 1425
1426 1426 $ printf "foo" > foo
1427 1427 $ printf "bar" > bar
1428 1428 $ hg add -q
1429 1429 $ hg fix --debug --working-dir --config "fix.nopattern:command=echo fixed"
1430 1430 fixer tool has no pattern configuration: nopattern
1431 1431 $ cat foo bar
1432 1432 foobar (no-eol)
1433 1433 $ hg fix --debug --working-dir --config "fix.nocommand:pattern=foo.bar"
1434 1434 fixer tool has no command configuration: nocommand
1435 1435
1436 1436 $ cd ..
1437 1437
1438 1438 Tools can be disabled. Disabled tools do nothing but print a debug message.
1439 1439
1440 1440 $ hg init disabled
1441 1441 $ cd disabled
1442 1442
1443 1443 $ printf "foo\n" > foo
1444 1444 $ hg add -q
1445 1445 $ hg fix --debug --working-dir --config "fix.disabled:command=echo fixed" \
1446 1446 > --config "fix.disabled:pattern=foo" \
1447 1447 > --config "fix.disabled:enabled=false"
1448 1448 ignoring disabled fixer tool: disabled
1449 1449 $ cat foo
1450 1450 foo
1451 1451
1452 1452 $ cd ..
1453 1453
1454 1454 Test that we can configure a fixer to affect all files regardless of the cwd.
1455 1455 The way we invoke matching must not prohibit this.
1456 1456
1457 1457 $ hg init affectallfiles
1458 1458 $ cd affectallfiles
1459 1459
1460 1460 $ mkdir foo bar
1461 1461 $ printf "foo" > foo/file
1462 1462 $ printf "bar" > bar/file
1463 1463 $ printf "baz" > baz_file
1464 1464 $ hg add -q
1465 1465
1466 1466 $ cd bar
1467 1467 $ hg fix --working-dir --config "fix.cooltool:command=echo fixed" \
1468 1468 > --config "fix.cooltool:pattern=glob:**"
1469 1469 $ cd ..
1470 1470
1471 1471 $ cat foo/file
1472 1472 fixed
1473 1473 $ cat bar/file
1474 1474 fixed
1475 1475 $ cat baz_file
1476 1476 fixed
1477 1477
1478 1478 $ cd ..
1479 1479
1480 1480 Tools should be able to run on unchanged files, even if they set :linerange.
1481 1481 This includes a corner case where deleted chunks of a file are not considered
1482 1482 changes.
1483 1483
1484 1484 $ hg init skipclean
1485 1485 $ cd skipclean
1486 1486
1487 1487 $ printf "a\nb\nc\n" > foo
1488 1488 $ printf "a\nb\nc\n" > bar
1489 1489 $ printf "a\nb\nc\n" > baz
1490 1490 $ hg commit -Aqm "base"
1491 1491
1492 1492 $ printf "a\nc\n" > foo
1493 1493 $ printf "a\nx\nc\n" > baz
1494 1494
1495 1495 $ cat >> print.py <<EOF
1496 1496 > import sys
1497 1497 > for a in sys.argv[1:]:
1498 1498 > print(a)
1499 1499 > EOF
1500 1500
1501 1501 $ hg fix --working-dir foo bar baz \
1502 1502 > --config "fix.changedlines:command=\"$PYTHON\" print.py \"Line ranges:\"" \
1503 1503 > --config 'fix.changedlines:linerange="{first} through {last}"' \
1504 1504 > --config 'fix.changedlines:pattern=glob:**' \
1505 1505 > --config 'fix.changedlines:skipclean=false'
1506 1506
1507 1507 $ cat foo
1508 1508 Line ranges:
1509 1509 $ cat bar
1510 1510 Line ranges:
1511 1511 $ cat baz
1512 1512 Line ranges:
1513 1513 2 through 2
1514 1514
1515 1515 $ cd ..
1516 1516
1517 1517 Test various cases around merges. We were previously dropping files if they were
1518 1518 created on only the p2 side of the merge, so let's test permutations of:
1519 1519 * added, was fixed
1520 1520 * added, considered for fixing but was already good
1521 1521 * added, not considered for fixing
1522 1522 * modified, was fixed
1523 1523 * modified, considered for fixing but was already good
1524 1524 * modified, not considered for fixing
1525 1525
1526 1526 Before the bug was fixed where we would drop files, this test demonstrated the
1527 1527 following issues:
1528 1528 * new_in_r1.ignored, new_in_r1_already_good.changed, and
1529 1529 > mod_in_r1_already_good.changed were NOT in the manifest for the merge commit
1530 1530 * mod_in_r1.ignored had its contents from r0, NOT r1.
1531 1531
1532 1532 We're also setting a named branch for every commit to demonstrate that the
1533 1533 branch is kept intact and there aren't issues updating to another branch in the
1534 1534 middle of fix.
1535 1535
1536 1536 $ hg init merge_keeps_files
1537 1537 $ cd merge_keeps_files
1538 1538 $ for f in r0 mod_in_r1 mod_in_r2 mod_in_merge mod_in_child; do
1539 1539 > for c in changed whole ignored; do
1540 1540 > printf "hello\n" > $f.$c
1541 1541 > done
1542 1542 > printf "HELLO\n" > "mod_in_${f}_already_good.changed"
1543 1543 > done
1544 1544 $ hg branch -q r0
1545 1545 $ hg ci -Aqm 'r0'
1546 1546 $ hg phase -p
1547 1547 $ make_test_files() {
1548 1548 > printf "world\n" >> "mod_in_$1.changed"
1549 1549 > printf "world\n" >> "mod_in_$1.whole"
1550 1550 > printf "world\n" >> "mod_in_$1.ignored"
1551 1551 > printf "WORLD\n" >> "mod_in_$1_already_good.changed"
1552 1552 > printf "new in $1\n" > "new_in_$1.changed"
1553 1553 > printf "new in $1\n" > "new_in_$1.whole"
1554 1554 > printf "new in $1\n" > "new_in_$1.ignored"
1555 1555 > printf "ALREADY GOOD, NEW IN THIS REV\n" > "new_in_$1_already_good.changed"
1556 1556 > }
1557 1557 $ make_test_commit() {
1558 1558 > make_test_files "$1"
1559 1559 > hg branch -q "$1"
1560 1560 > hg ci -Aqm "$2"
1561 1561 > }
1562 1562 $ make_test_commit r1 "merge me, pt1"
1563 1563 $ hg co -q ".^"
1564 1564 $ make_test_commit r2 "merge me, pt2"
1565 1565 $ hg merge -qr 1
1566 1566 $ make_test_commit merge "evil merge"
1567 1567 $ make_test_commit child "child of merge"
1568 1568 $ make_test_files wdir
1569 1569 $ hg fix -r 'not public()' -w
1570 1570 $ hg log -G -T'{rev}:{shortest(node,8)}: branch:{branch} desc:{desc}'
1571 1571 @ 8:c22ce900: branch:child desc:child of merge
1572 1572 |
1573 1573 o 7:5a30615a: branch:merge desc:evil merge
1574 1574 |\
1575 1575 | o 6:4e5acdc4: branch:r2 desc:merge me, pt2
1576 1576 | |
1577 1577 o | 5:eea01878: branch:r1 desc:merge me, pt1
1578 1578 |/
1579 1579 o 0:0c548d87: branch:r0 desc:r0
1580 1580
1581 1581 $ hg files -r tip
1582 1582 mod_in_child.changed
1583 1583 mod_in_child.ignored
1584 1584 mod_in_child.whole
1585 1585 mod_in_child_already_good.changed
1586 1586 mod_in_merge.changed
1587 1587 mod_in_merge.ignored
1588 1588 mod_in_merge.whole
1589 1589 mod_in_merge_already_good.changed
1590 1590 mod_in_mod_in_child_already_good.changed
1591 1591 mod_in_mod_in_merge_already_good.changed
1592 1592 mod_in_mod_in_r1_already_good.changed
1593 1593 mod_in_mod_in_r2_already_good.changed
1594 1594 mod_in_r0_already_good.changed
1595 1595 mod_in_r1.changed
1596 1596 mod_in_r1.ignored
1597 1597 mod_in_r1.whole
1598 1598 mod_in_r1_already_good.changed
1599 1599 mod_in_r2.changed
1600 1600 mod_in_r2.ignored
1601 1601 mod_in_r2.whole
1602 1602 mod_in_r2_already_good.changed
1603 1603 new_in_child.changed
1604 1604 new_in_child.ignored
1605 1605 new_in_child.whole
1606 1606 new_in_child_already_good.changed
1607 1607 new_in_merge.changed
1608 1608 new_in_merge.ignored
1609 1609 new_in_merge.whole
1610 1610 new_in_merge_already_good.changed
1611 1611 new_in_r1.changed
1612 1612 new_in_r1.ignored
1613 1613 new_in_r1.whole
1614 1614 new_in_r1_already_good.changed
1615 1615 new_in_r2.changed
1616 1616 new_in_r2.ignored
1617 1617 new_in_r2.whole
1618 1618 new_in_r2_already_good.changed
1619 1619 r0.changed
1620 1620 r0.ignored
1621 1621 r0.whole
1622 1622 $ for f in "$(hg files -r tip)"; do hg cat -r tip $f -T'{path}:\n{data}\n'; done
1623 1623 mod_in_child.changed:
1624 1624 hello
1625 1625 WORLD
1626 1626
1627 1627 mod_in_child.ignored:
1628 1628 hello
1629 1629 world
1630 1630
1631 1631 mod_in_child.whole:
1632 1632 HELLO
1633 1633 WORLD
1634 1634
1635 1635 mod_in_child_already_good.changed:
1636 1636 WORLD
1637 1637
1638 1638 mod_in_merge.changed:
1639 1639 hello
1640 1640 WORLD
1641 1641
1642 1642 mod_in_merge.ignored:
1643 1643 hello
1644 1644 world
1645 1645
1646 1646 mod_in_merge.whole:
1647 1647 HELLO
1648 1648 WORLD
1649 1649
1650 1650 mod_in_merge_already_good.changed:
1651 1651 WORLD
1652 1652
1653 1653 mod_in_mod_in_child_already_good.changed:
1654 1654 HELLO
1655 1655
1656 1656 mod_in_mod_in_merge_already_good.changed:
1657 1657 HELLO
1658 1658
1659 1659 mod_in_mod_in_r1_already_good.changed:
1660 1660 HELLO
1661 1661
1662 1662 mod_in_mod_in_r2_already_good.changed:
1663 1663 HELLO
1664 1664
1665 1665 mod_in_r0_already_good.changed:
1666 1666 HELLO
1667 1667
1668 1668 mod_in_r1.changed:
1669 1669 hello
1670 1670 WORLD
1671 1671
1672 1672 mod_in_r1.ignored:
1673 1673 hello
1674 1674 world
1675 1675
1676 1676 mod_in_r1.whole:
1677 1677 HELLO
1678 1678 WORLD
1679 1679
1680 1680 mod_in_r1_already_good.changed:
1681 1681 WORLD
1682 1682
1683 1683 mod_in_r2.changed:
1684 1684 hello
1685 1685 WORLD
1686 1686
1687 1687 mod_in_r2.ignored:
1688 1688 hello
1689 1689 world
1690 1690
1691 1691 mod_in_r2.whole:
1692 1692 HELLO
1693 1693 WORLD
1694 1694
1695 1695 mod_in_r2_already_good.changed:
1696 1696 WORLD
1697 1697
1698 1698 new_in_child.changed:
1699 1699 NEW IN CHILD
1700 1700
1701 1701 new_in_child.ignored:
1702 1702 new in child
1703 1703
1704 1704 new_in_child.whole:
1705 1705 NEW IN CHILD
1706 1706
1707 1707 new_in_child_already_good.changed:
1708 1708 ALREADY GOOD, NEW IN THIS REV
1709 1709
1710 1710 new_in_merge.changed:
1711 1711 NEW IN MERGE
1712 1712
1713 1713 new_in_merge.ignored:
1714 1714 new in merge
1715 1715
1716 1716 new_in_merge.whole:
1717 1717 NEW IN MERGE
1718 1718
1719 1719 new_in_merge_already_good.changed:
1720 1720 ALREADY GOOD, NEW IN THIS REV
1721 1721
1722 1722 new_in_r1.changed:
1723 1723 NEW IN R1
1724 1724
1725 1725 new_in_r1.ignored:
1726 1726 new in r1
1727 1727
1728 1728 new_in_r1.whole:
1729 1729 NEW IN R1
1730 1730
1731 1731 new_in_r1_already_good.changed:
1732 1732 ALREADY GOOD, NEW IN THIS REV
1733 1733
1734 1734 new_in_r2.changed:
1735 1735 NEW IN R2
1736 1736
1737 1737 new_in_r2.ignored:
1738 1738 new in r2
1739 1739
1740 1740 new_in_r2.whole:
1741 1741 NEW IN R2
1742 1742
1743 1743 new_in_r2_already_good.changed:
1744 1744 ALREADY GOOD, NEW IN THIS REV
1745 1745
1746 1746 r0.changed:
1747 1747 hello
1748 1748
1749 1749 r0.ignored:
1750 1750 hello
1751 1751
1752 1752 r0.whole:
1753 1753 hello
1754 1754
1755 1755
1756 1756 We should execute the fixer tools as few times as possible, because they might
1757 1757 be slow or expensive to execute. The inputs to each execution are effectively
1758 1758 the file path, file content, and line ranges. So, we should be able to re-use
1759 1759 results whenever those inputs are repeated. That saves a lot of work when
1760 1760 fixing chains of commits that all have the same file revision for a path being
1761 1761 fixed.
1762 1762
1763 1763 $ hg init numberofinvocations
1764 1764 $ cd numberofinvocations
1765 1765
1766 1766 $ printf "bar1" > bar.log
1767 1767 $ printf "baz1" > baz.log
1768 1768 $ printf "foo1" > foo.log
1769 1769 $ printf "qux1" > qux.log
1770 1770 $ hg commit -Aqm "commit1"
1771 1771
1772 1772 $ printf "bar2" > bar.log
1773 1773 $ printf "baz2" > baz.log
1774 1774 $ printf "foo2" > foo.log
1775 1775 $ hg commit -Aqm "commit2"
1776 1776
1777 1777 $ printf "bar3" > bar.log
1778 1778 $ printf "baz3" > baz.log
1779 1779 $ hg commit -Aqm "commit3"
1780 1780
1781 1781 $ printf "bar4" > bar.log
1782 1782
1783 1783 $ LOGFILE=$TESTTMP/log
1784 1784 $ LOGGER=$TESTTMP/log.py
1785 1785 $ cat >> $LOGGER <<EOF
1786 1786 > # Appends the input file's name to the log file.
1787 1787 > import sys
1788 > with open('$LOGFILE', 'a') as f:
1788 > with open(r'$LOGFILE', 'a') as f:
1789 1789 > f.write(sys.argv[1] + '\n')
1790 1790 > sys.stdout.write(sys.stdin.read())
1791 1791 > EOF
1792 1792
1793 1793 $ hg fix --working-dir -r "all()" \
1794 1794 > --config "fix.log:command=\"$PYTHON\" \"$LOGGER\" {rootpath}" \
1795 1795 > --config "fix.log:pattern=glob:**.log"
1796 1796
1797 1797 $ cat $LOGFILE | sort | uniq -c
1798 1798 4 bar.log
1799 1799 4 baz.log
1800 1800 3 foo.log
1801 1801 2 qux.log
1802 1802
1803 1803 $ cd ..
1804 1804
1805 1805 For tools that support line ranges, it's wrong to blindly re-use fixed file
1806 1806 content for the same file revision if it appears twice with different baserevs,
1807 1807 because the line ranges could be different. Since computing line ranges is
1808 1808 ambiguous, this isn't a matter of correctness, but it affects the usability of
1809 1809 this extension. It could maybe be simpler if baserevs were computed on a
1810 1810 per-file basis to make this situation impossible to construct.
1811 1811
1812 1812 In the following example, we construct two subgraphs with the same file
1813 1813 revisions, and fix different sub-subgraphs to get different baserevs and
1814 1814 different changed line ranges. The key precondition is that revisions 1 and 4
1815 1815 have the same file revision, and the key result is that their successors don't
1816 1816 have the same file content, because we want to fix different areas of that same
1817 1817 file revision's content.
1818 1818
1819 1819 $ hg init differentlineranges
1820 1820 $ cd differentlineranges
1821 1821
1822 1822 $ printf "a\nb\n" > file.changed
1823 1823 $ hg commit -Aqm "0 ab"
1824 1824 $ printf "a\nx\n" > file.changed
1825 1825 $ hg commit -Aqm "1 ax"
1826 1826 $ hg remove file.changed
1827 1827 $ hg commit -Aqm "2 removed"
1828 1828 $ hg revert file.changed -r 0
1829 1829 $ hg commit -Aqm "3 ab (reverted)"
1830 1830 $ hg revert file.changed -r 1
1831 1831 $ hg commit -Aqm "4 ax (reverted)"
1832 1832
1833 1833 $ hg manifest --debug --template "{hash}\n" -r 0; \
1834 1834 > hg manifest --debug --template "{hash}\n" -r 3
1835 1835 418f692145676128d2fb518b027ddbac624be76e
1836 1836 418f692145676128d2fb518b027ddbac624be76e
1837 1837 $ hg manifest --debug --template "{hash}\n" -r 1; \
1838 1838 > hg manifest --debug --template "{hash}\n" -r 4
1839 1839 09b8b3ce5a507caaa282f7262679e6d04091426c
1840 1840 09b8b3ce5a507caaa282f7262679e6d04091426c
1841 1841
1842 1842 $ hg fix --working-dir -r 1+3+4
1843 1843 3 new orphan changesets
1844 1844
1845 1845 $ hg cat file.changed -r "successors(1)" --hidden
1846 1846 a
1847 1847 X
1848 1848 $ hg cat file.changed -r "successors(4)" --hidden
1849 1849 A
1850 1850 X
1851 1851
1852 1852 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now