##// END OF EJS Templates
fix: add a test case around the effect of cwd on pattern matching...
Danny Hooper -
r42881:22c4bd7d default
parent child Browse files
Show More
@@ -1,1244 +1,1269 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 sys
7 7 > from mercurial.utils.procutil import setbinary
8 8 > setbinary(sys.stdin)
9 9 > setbinary(sys.stdout)
10 10 > lines = set()
11 11 > for arg in sys.argv[1:]:
12 12 > if arg == 'all':
13 13 > sys.stdout.write(sys.stdin.read().upper())
14 14 > sys.exit(0)
15 15 > else:
16 16 > first, last = arg.split('-')
17 17 > lines.update(range(int(first), int(last) + 1))
18 18 > for i, line in enumerate(sys.stdin.readlines()):
19 19 > if i + 1 in lines:
20 20 > sys.stdout.write(line.upper())
21 21 > else:
22 22 > sys.stdout.write(line)
23 23 > EOF
24 24 $ TESTLINES="foo\nbar\nbaz\nqux\n"
25 25 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY
26 26 foo
27 27 bar
28 28 baz
29 29 qux
30 30 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY all
31 31 FOO
32 32 BAR
33 33 BAZ
34 34 QUX
35 35 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-1
36 36 FOO
37 37 bar
38 38 baz
39 39 qux
40 40 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 1-2
41 41 FOO
42 42 BAR
43 43 baz
44 44 qux
45 45 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-3
46 46 foo
47 47 BAR
48 48 BAZ
49 49 qux
50 50 $ printf $TESTLINES | "$PYTHON" $UPPERCASEPY 2-2 4-4
51 51 foo
52 52 BAR
53 53 baz
54 54 QUX
55 55
56 56 Set up the config with two simple fixers: one that fixes specific line ranges,
57 57 and one that always fixes the whole file. They both "fix" files by converting
58 58 letters to uppercase. They use different file extensions, so each test case can
59 59 choose which behavior to use by naming files.
60 60
61 61 $ cat >> $HGRCPATH <<EOF
62 62 > [extensions]
63 63 > fix =
64 64 > [experimental]
65 65 > evolution.createmarkers=True
66 66 > evolution.allowunstable=True
67 67 > [fix]
68 68 > uppercase-whole-file:command="$PYTHON" $UPPERCASEPY all
69 69 > uppercase-whole-file:pattern=set:**.whole
70 70 > uppercase-changed-lines:command="$PYTHON" $UPPERCASEPY
71 71 > uppercase-changed-lines:linerange={first}-{last}
72 72 > uppercase-changed-lines:pattern=set:**.changed
73 73 > EOF
74 74
75 75 Help text for fix.
76 76
77 77 $ hg help fix
78 78 hg fix [OPTION]... [FILE]...
79 79
80 80 rewrite file content in changesets or working directory
81 81
82 82 Runs any configured tools to fix the content of files. Only affects files
83 83 with changes, unless file arguments are provided. Only affects changed
84 84 lines of files, unless the --whole flag is used. Some tools may always
85 85 affect the whole file regardless of --whole.
86 86
87 87 If revisions are specified with --rev, those revisions will be checked,
88 88 and they may be replaced with new revisions that have fixed file content.
89 89 It is desirable to specify all descendants of each specified revision, so
90 90 that the fixes propagate to the descendants. If all descendants are fixed
91 91 at the same time, no merging, rebasing, or evolution will be required.
92 92
93 93 If --working-dir is used, files with uncommitted changes in the working
94 94 copy will be fixed. If the checked-out revision is also fixed, the working
95 95 directory will update to the replacement revision.
96 96
97 97 When determining what lines of each file to fix at each revision, the
98 98 whole set of revisions being fixed is considered, so that fixes to earlier
99 99 revisions are not forgotten in later ones. The --base flag can be used to
100 100 override this default behavior, though it is not usually desirable to do
101 101 so.
102 102
103 103 (use 'hg help -e fix' to show help for the fix extension)
104 104
105 105 options ([+] can be repeated):
106 106
107 107 --all fix all non-public non-obsolete revisions
108 108 --base REV [+] revisions to diff against (overrides automatic selection,
109 109 and applies to every revision being fixed)
110 110 -r --rev REV [+] revisions to fix
111 111 -w --working-dir fix the working directory
112 112 --whole always fix every line of a file
113 113
114 114 (some details hidden, use --verbose to show complete help)
115 115
116 116 $ hg help -e fix
117 117 fix extension - rewrite file content in changesets or working copy
118 118 (EXPERIMENTAL)
119 119
120 120 Provides a command that runs configured tools on the contents of modified
121 121 files, writing back any fixes to the working copy or replacing changesets.
122 122
123 123 Here is an example configuration that causes 'hg fix' to apply automatic
124 124 formatting fixes to modified lines in C++ code:
125 125
126 126 [fix]
127 127 clang-format:command=clang-format --assume-filename={rootpath}
128 128 clang-format:linerange=--lines={first}:{last}
129 129 clang-format:pattern=set:**.cpp or **.hpp
130 130
131 131 The :command suboption forms the first part of the shell command that will be
132 132 used to fix a file. The content of the file is passed on standard input, and
133 133 the fixed file content is expected on standard output. Any output on standard
134 134 error will be displayed as a warning. If the exit status is not zero, the file
135 135 will not be affected. A placeholder warning is displayed if there is a non-
136 136 zero exit status but no standard error output. Some values may be substituted
137 137 into the command:
138 138
139 139 {rootpath} The path of the file being fixed, relative to the repo root
140 140 {basename} The name of the file being fixed, without the directory path
141 141
142 142 If the :linerange suboption is set, the tool will only be run if there are
143 143 changed lines in a file. The value of this suboption is appended to the shell
144 144 command once for every range of changed lines in the file. Some values may be
145 145 substituted into the command:
146 146
147 147 {first} The 1-based line number of the first line in the modified range
148 148 {last} The 1-based line number of the last line in the modified range
149 149
150 150 The :pattern suboption determines which files will be passed through each
151 151 configured tool. See 'hg help patterns' for possible values. If there are file
152 152 arguments to 'hg fix', the intersection of these patterns is used.
153 153
154 154 There is also a configurable limit for the maximum size of file that will be
155 155 processed by 'hg fix':
156 156
157 157 [fix]
158 158 maxfilesize = 2MB
159 159
160 160 Normally, execution of configured tools will continue after a failure
161 161 (indicated by a non-zero exit status). It can also be configured to abort
162 162 after the first such failure, so that no files will be affected if any tool
163 163 fails. This abort will also cause 'hg fix' to exit with a non-zero status:
164 164
165 165 [fix]
166 166 failure = abort
167 167
168 168 When multiple tools are configured to affect a file, they execute in an order
169 169 defined by the :priority suboption. The priority suboption has a default value
170 170 of zero for each tool. Tools are executed in order of descending priority. The
171 171 execution order of tools with equal priority is unspecified. For example, you
172 172 could use the 'sort' and 'head' utilities to keep only the 10 smallest numbers
173 173 in a text file by ensuring that 'sort' runs before 'head':
174 174
175 175 [fix]
176 176 sort:command = sort -n
177 177 head:command = head -n 10
178 178 sort:pattern = numbers.txt
179 179 head:pattern = numbers.txt
180 180 sort:priority = 2
181 181 head:priority = 1
182 182
183 183 To account for changes made by each tool, the line numbers used for
184 184 incremental formatting are recomputed before executing the next tool. So, each
185 185 tool may see different values for the arguments added by the :linerange
186 186 suboption.
187 187
188 188 Each fixer tool is allowed to return some metadata in addition to the fixed
189 189 file content. The metadata must be placed before the file content on stdout,
190 190 separated from the file content by a zero byte. The metadata is parsed as a
191 191 JSON value (so, it should be UTF-8 encoded and contain no zero bytes). A fixer
192 192 tool is expected to produce this metadata encoding if and only if the
193 193 :metadata suboption is true:
194 194
195 195 [fix]
196 196 tool:command = tool --prepend-json-metadata
197 197 tool:metadata = true
198 198
199 199 The metadata values are passed to hooks, which can be used to print summaries
200 200 or perform other post-fixing work. The supported hooks are:
201 201
202 202 "postfixfile"
203 203 Run once for each file in each revision where any fixer tools made changes
204 204 to the file content. Provides "$HG_REV" and "$HG_PATH" to identify the file,
205 205 and "$HG_METADATA" with a map of fixer names to metadata values from fixer
206 206 tools that affected the file. Fixer tools that didn't affect the file have a
207 207 valueof None. Only fixer tools that executed are present in the metadata.
208 208
209 209 "postfix"
210 210 Run once after all files and revisions have been handled. Provides
211 211 "$HG_REPLACEMENTS" with information about what revisions were created and
212 212 made obsolete. Provides a boolean "$HG_WDIRWRITTEN" to indicate whether any
213 213 files in the working copy were updated. Provides a list "$HG_METADATA"
214 214 mapping fixer tool names to lists of metadata values returned from
215 215 executions that modified a file. This aggregates the same metadata
216 216 previously passed to the "postfixfile" hook.
217 217
218 218 list of commands:
219 219
220 220 fix rewrite file content in changesets or working directory
221 221
222 222 (use 'hg help -v -e fix' to show built-in aliases and global options)
223 223
224 224 There is no default behavior in the absence of --rev and --working-dir.
225 225
226 226 $ hg init badusage
227 227 $ cd badusage
228 228
229 229 $ hg fix
230 230 abort: no changesets specified
231 231 (use --rev or --working-dir)
232 232 [255]
233 233 $ hg fix --whole
234 234 abort: no changesets specified
235 235 (use --rev or --working-dir)
236 236 [255]
237 237 $ hg fix --base 0
238 238 abort: no changesets specified
239 239 (use --rev or --working-dir)
240 240 [255]
241 241
242 242 Fixing a public revision isn't allowed. It should abort early enough that
243 243 nothing happens, even to the working directory.
244 244
245 245 $ printf "hello\n" > hello.whole
246 246 $ hg commit -Aqm "hello"
247 247 $ hg phase -r 0 --public
248 248 $ hg fix -r 0
249 249 abort: can't fix immutable changeset 0:6470986d2e7b
250 250 [255]
251 251 $ hg fix -r 0 --working-dir
252 252 abort: can't fix immutable changeset 0:6470986d2e7b
253 253 [255]
254 254 $ hg cat -r tip hello.whole
255 255 hello
256 256 $ cat hello.whole
257 257 hello
258 258
259 259 $ cd ..
260 260
261 261 Fixing a clean working directory should do nothing. Even the --whole flag
262 262 shouldn't cause any clean files to be fixed. Specifying a clean file explicitly
263 263 should only fix it if the fixer always fixes the whole file. The combination of
264 264 an explicit filename and --whole should format the entire file regardless.
265 265
266 266 $ hg init fixcleanwdir
267 267 $ cd fixcleanwdir
268 268
269 269 $ printf "hello\n" > hello.changed
270 270 $ printf "world\n" > hello.whole
271 271 $ hg commit -Aqm "foo"
272 272 $ hg fix --working-dir
273 273 $ hg diff
274 274 $ hg fix --working-dir --whole
275 275 $ hg diff
276 276 $ hg fix --working-dir *
277 277 $ cat *
278 278 hello
279 279 WORLD
280 280 $ hg revert --all --no-backup
281 281 reverting hello.whole
282 282 $ hg fix --working-dir * --whole
283 283 $ cat *
284 284 HELLO
285 285 WORLD
286 286
287 287 The same ideas apply to fixing a revision, so we create a revision that doesn't
288 288 modify either of the files in question and try fixing it. This also tests that
289 289 we ignore a file that doesn't match any configured fixer.
290 290
291 291 $ hg revert --all --no-backup
292 292 reverting hello.changed
293 293 reverting hello.whole
294 294 $ printf "unimportant\n" > some.file
295 295 $ hg commit -Aqm "some other file"
296 296
297 297 $ hg fix -r .
298 298 $ hg cat -r tip *
299 299 hello
300 300 world
301 301 unimportant
302 302 $ hg fix -r . --whole
303 303 $ hg cat -r tip *
304 304 hello
305 305 world
306 306 unimportant
307 307 $ hg fix -r . *
308 308 $ hg cat -r tip *
309 309 hello
310 310 WORLD
311 311 unimportant
312 312 $ hg fix -r . * --whole --config experimental.evolution.allowdivergence=true
313 313 2 new content-divergent changesets
314 314 $ hg cat -r tip *
315 315 HELLO
316 316 WORLD
317 317 unimportant
318 318
319 319 $ cd ..
320 320
321 321 Fixing the working directory should still work if there are no revisions.
322 322
323 323 $ hg init norevisions
324 324 $ cd norevisions
325 325
326 326 $ printf "something\n" > something.whole
327 327 $ hg add
328 328 adding something.whole
329 329 $ hg fix --working-dir
330 330 $ cat something.whole
331 331 SOMETHING
332 332
333 333 $ cd ..
334 334
335 335 Test the effect of fixing the working directory for each possible status, with
336 336 and without providing explicit file arguments.
337 337
338 338 $ hg init implicitlyfixstatus
339 339 $ cd implicitlyfixstatus
340 340
341 341 $ printf "modified\n" > modified.whole
342 342 $ printf "removed\n" > removed.whole
343 343 $ printf "deleted\n" > deleted.whole
344 344 $ printf "clean\n" > clean.whole
345 345 $ printf "ignored.whole" > .hgignore
346 346 $ hg commit -Aqm "stuff"
347 347
348 348 $ printf "modified!!!\n" > modified.whole
349 349 $ printf "unknown\n" > unknown.whole
350 350 $ printf "ignored\n" > ignored.whole
351 351 $ printf "added\n" > added.whole
352 352 $ hg add added.whole
353 353 $ hg remove removed.whole
354 354 $ rm deleted.whole
355 355
356 356 $ hg status --all
357 357 M modified.whole
358 358 A added.whole
359 359 R removed.whole
360 360 ! deleted.whole
361 361 ? unknown.whole
362 362 I ignored.whole
363 363 C .hgignore
364 364 C clean.whole
365 365
366 366 $ hg fix --working-dir
367 367
368 368 $ hg status --all
369 369 M modified.whole
370 370 A added.whole
371 371 R removed.whole
372 372 ! deleted.whole
373 373 ? unknown.whole
374 374 I ignored.whole
375 375 C .hgignore
376 376 C clean.whole
377 377
378 378 $ cat *.whole
379 379 ADDED
380 380 clean
381 381 ignored
382 382 MODIFIED!!!
383 383 unknown
384 384
385 385 $ printf "modified!!!\n" > modified.whole
386 386 $ printf "added\n" > added.whole
387 387
388 388 Listing the files explicitly causes untracked files to also be fixed, but
389 389 ignored files are still unaffected.
390 390
391 391 $ hg fix --working-dir *.whole
392 392
393 393 $ hg status --all
394 394 M clean.whole
395 395 M modified.whole
396 396 A added.whole
397 397 R removed.whole
398 398 ! deleted.whole
399 399 ? unknown.whole
400 400 I ignored.whole
401 401 C .hgignore
402 402
403 403 $ cat *.whole
404 404 ADDED
405 405 CLEAN
406 406 ignored
407 407 MODIFIED!!!
408 408 UNKNOWN
409 409
410 410 $ cd ..
411 411
412 412 Test that incremental fixing works on files with additions, deletions, and
413 413 changes in multiple line ranges. Note that deletions do not generally cause
414 414 neighboring lines to be fixed, so we don't return a line range for purely
415 415 deleted sections. In the future we should support a :deletion config that
416 416 allows fixers to know where deletions are located.
417 417
418 418 $ hg init incrementalfixedlines
419 419 $ cd incrementalfixedlines
420 420
421 421 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.txt
422 422 $ hg commit -Aqm "foo"
423 423 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.txt
424 424
425 425 $ hg --config "fix.fail:command=echo" \
426 426 > --config "fix.fail:linerange={first}:{last}" \
427 427 > --config "fix.fail:pattern=foo.txt" \
428 428 > fix --working-dir
429 429 $ cat foo.txt
430 430 1:1 4:6 8:8
431 431
432 432 $ cd ..
433 433
434 434 Test that --whole fixes all lines regardless of the diffs present.
435 435
436 436 $ hg init wholeignoresdiffs
437 437 $ cd wholeignoresdiffs
438 438
439 439 $ printf "a\nb\nc\nd\ne\nf\ng\n" > foo.changed
440 440 $ hg commit -Aqm "foo"
441 441 $ printf "zz\na\nc\ndd\nee\nff\nf\ngg\n" > foo.changed
442 442 $ hg fix --working-dir --whole
443 443 $ cat foo.changed
444 444 ZZ
445 445 A
446 446 C
447 447 DD
448 448 EE
449 449 FF
450 450 F
451 451 GG
452 452
453 453 $ cd ..
454 454
455 455 We should do nothing with symlinks, and their targets should be unaffected. Any
456 456 other behavior would be more complicated to implement and harder to document.
457 457
458 458 #if symlink
459 459 $ hg init dontmesswithsymlinks
460 460 $ cd dontmesswithsymlinks
461 461
462 462 $ printf "hello\n" > hello.whole
463 463 $ ln -s hello.whole hellolink
464 464 $ hg add
465 465 adding hello.whole
466 466 adding hellolink
467 467 $ hg fix --working-dir hellolink
468 468 $ hg status
469 469 A hello.whole
470 470 A hellolink
471 471
472 472 $ cd ..
473 473 #endif
474 474
475 475 We should allow fixers to run on binary files, even though this doesn't sound
476 476 like a common use case. There's not much benefit to disallowing it, and users
477 477 can add "and not binary()" to their filesets if needed. The Mercurial
478 478 philosophy is generally to not handle binary files specially anyway.
479 479
480 480 $ hg init cantouchbinaryfiles
481 481 $ cd cantouchbinaryfiles
482 482
483 483 $ printf "hello\0\n" > hello.whole
484 484 $ hg add
485 485 adding hello.whole
486 486 $ hg fix --working-dir 'set:binary()'
487 487 $ cat hello.whole
488 488 HELLO\x00 (esc)
489 489
490 490 $ cd ..
491 491
492 492 We have a config for the maximum size of file we will attempt to fix. This can
493 493 be helpful to avoid running unsuspecting fixer tools on huge inputs, which
494 494 could happen by accident without a well considered configuration. A more
495 495 precise configuration could use the size() fileset function if one global limit
496 496 is undesired.
497 497
498 498 $ hg init maxfilesize
499 499 $ cd maxfilesize
500 500
501 501 $ printf "this file is huge\n" > hello.whole
502 502 $ hg add
503 503 adding hello.whole
504 504 $ hg --config fix.maxfilesize=10 fix --working-dir
505 505 ignoring file larger than 10 bytes: hello.whole
506 506 $ cat hello.whole
507 507 this file is huge
508 508
509 509 $ cd ..
510 510
511 511 If we specify a file to fix, other files should be left alone, even if they
512 512 have changes.
513 513
514 514 $ hg init fixonlywhatitellyouto
515 515 $ cd fixonlywhatitellyouto
516 516
517 517 $ printf "fix me!\n" > fixme.whole
518 518 $ printf "not me.\n" > notme.whole
519 519 $ hg add
520 520 adding fixme.whole
521 521 adding notme.whole
522 522 $ hg fix --working-dir fixme.whole
523 523 $ cat *.whole
524 524 FIX ME!
525 525 not me.
526 526
527 527 $ cd ..
528 528
529 529 Specifying a directory name should fix all its files and subdirectories.
530 530
531 531 $ hg init fixdirectory
532 532 $ cd fixdirectory
533 533
534 534 $ mkdir -p dir1/dir2
535 535 $ printf "foo\n" > foo.whole
536 536 $ printf "bar\n" > dir1/bar.whole
537 537 $ printf "baz\n" > dir1/dir2/baz.whole
538 538 $ hg add
539 539 adding dir1/bar.whole
540 540 adding dir1/dir2/baz.whole
541 541 adding foo.whole
542 542 $ hg fix --working-dir dir1
543 543 $ cat foo.whole dir1/bar.whole dir1/dir2/baz.whole
544 544 foo
545 545 BAR
546 546 BAZ
547 547
548 548 $ cd ..
549 549
550 550 Fixing a file in the working directory that needs no fixes should not actually
551 551 write back to the file, so for example the mtime shouldn't change.
552 552
553 553 $ hg init donttouchunfixedfiles
554 554 $ cd donttouchunfixedfiles
555 555
556 556 $ printf "NO FIX NEEDED\n" > foo.whole
557 557 $ hg add
558 558 adding foo.whole
559 559 $ cp -p foo.whole foo.whole.orig
560 560 $ cp -p foo.whole.orig foo.whole
561 561 $ sleep 2 # mtime has a resolution of one or two seconds.
562 562 $ hg fix --working-dir
563 563 $ f foo.whole.orig --newer foo.whole
564 564 foo.whole.orig: newer than foo.whole
565 565
566 566 $ cd ..
567 567
568 568 When a fixer prints to stderr, we don't assume that it has failed. We show the
569 569 error messages to the user, and we still let the fixer affect the file it was
570 570 fixing if its exit code is zero. Some code formatters might emit error messages
571 571 on stderr and nothing on stdout, which would cause us the clear the file,
572 572 except that they also exit with a non-zero code. We show the user which fixer
573 573 emitted the stderr, and which revision, but we assume that the fixer will print
574 574 the filename if it is relevant (since the issue may be non-specific). There is
575 575 also a config to abort (without affecting any files whatsoever) if we see any
576 576 tool with a non-zero exit status.
577 577
578 578 $ hg init showstderr
579 579 $ cd showstderr
580 580
581 581 $ printf "hello\n" > hello.txt
582 582 $ hg add
583 583 adding hello.txt
584 584 $ cat > $TESTTMP/work.sh <<'EOF'
585 585 > printf 'HELLO\n'
586 586 > printf "$@: some\nerror that didn't stop the tool" >&2
587 587 > exit 0 # success despite the stderr output
588 588 > EOF
589 589 $ hg --config "fix.work:command=sh $TESTTMP/work.sh {rootpath}" \
590 590 > --config "fix.work:pattern=hello.txt" \
591 591 > fix --working-dir
592 592 [wdir] work: hello.txt: some
593 593 [wdir] work: error that didn't stop the tool
594 594 $ cat hello.txt
595 595 HELLO
596 596
597 597 $ printf "goodbye\n" > hello.txt
598 598 $ printf "foo\n" > foo.whole
599 599 $ hg add
600 600 adding foo.whole
601 601 $ cat > $TESTTMP/fail.sh <<'EOF'
602 602 > printf 'GOODBYE\n'
603 603 > printf "$@: some\nerror that did stop the tool\n" >&2
604 604 > exit 42 # success despite the stdout output
605 605 > EOF
606 606 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
607 607 > --config "fix.fail:pattern=hello.txt" \
608 608 > --config "fix.failure=abort" \
609 609 > fix --working-dir
610 610 [wdir] fail: hello.txt: some
611 611 [wdir] fail: error that did stop the tool
612 612 abort: no fixes will be applied
613 613 (use --config fix.failure=continue to apply any successful fixes anyway)
614 614 [255]
615 615 $ cat hello.txt
616 616 goodbye
617 617 $ cat foo.whole
618 618 foo
619 619
620 620 $ hg --config "fix.fail:command=sh $TESTTMP/fail.sh {rootpath}" \
621 621 > --config "fix.fail:pattern=hello.txt" \
622 622 > fix --working-dir
623 623 [wdir] fail: hello.txt: some
624 624 [wdir] fail: error that did stop the tool
625 625 $ cat hello.txt
626 626 goodbye
627 627 $ cat foo.whole
628 628 FOO
629 629
630 630 $ hg --config "fix.fail:command=exit 42" \
631 631 > --config "fix.fail:pattern=hello.txt" \
632 632 > fix --working-dir
633 633 [wdir] fail: exited with status 42
634 634
635 635 $ cd ..
636 636
637 637 Fixing the working directory and its parent revision at the same time should
638 638 check out the replacement revision for the parent. This prevents any new
639 639 uncommitted changes from appearing. We test this for a clean working directory
640 640 and a dirty one. In both cases, all lines/files changed since the grandparent
641 641 will be fixed. The grandparent is the "baserev" for both the parent and the
642 642 working copy.
643 643
644 644 $ hg init fixdotandcleanwdir
645 645 $ cd fixdotandcleanwdir
646 646
647 647 $ printf "hello\n" > hello.whole
648 648 $ printf "world\n" > world.whole
649 649 $ hg commit -Aqm "the parent commit"
650 650
651 651 $ hg parents --template '{rev} {desc}\n'
652 652 0 the parent commit
653 653 $ hg fix --working-dir -r .
654 654 $ hg parents --template '{rev} {desc}\n'
655 655 1 the parent commit
656 656 $ hg cat -r . *.whole
657 657 HELLO
658 658 WORLD
659 659 $ cat *.whole
660 660 HELLO
661 661 WORLD
662 662 $ hg status
663 663
664 664 $ cd ..
665 665
666 666 Same test with a dirty working copy.
667 667
668 668 $ hg init fixdotanddirtywdir
669 669 $ cd fixdotanddirtywdir
670 670
671 671 $ printf "hello\n" > hello.whole
672 672 $ printf "world\n" > world.whole
673 673 $ hg commit -Aqm "the parent commit"
674 674
675 675 $ printf "hello,\n" > hello.whole
676 676 $ printf "world!\n" > world.whole
677 677
678 678 $ hg parents --template '{rev} {desc}\n'
679 679 0 the parent commit
680 680 $ hg fix --working-dir -r .
681 681 $ hg parents --template '{rev} {desc}\n'
682 682 1 the parent commit
683 683 $ hg cat -r . *.whole
684 684 HELLO
685 685 WORLD
686 686 $ cat *.whole
687 687 HELLO,
688 688 WORLD!
689 689 $ hg status
690 690 M hello.whole
691 691 M world.whole
692 692
693 693 $ cd ..
694 694
695 695 When we have a chain of commits that change mutually exclusive lines of code,
696 696 we should be able to do incremental fixing that causes each commit in the chain
697 697 to include fixes made to the previous commits. This prevents children from
698 698 backing out the fixes made in their parents. A dirty working directory is
699 699 conceptually similar to another commit in the chain.
700 700
701 701 $ hg init incrementallyfixchain
702 702 $ cd incrementallyfixchain
703 703
704 704 $ cat > file.changed <<EOF
705 705 > first
706 706 > second
707 707 > third
708 708 > fourth
709 709 > fifth
710 710 > EOF
711 711 $ hg commit -Aqm "the common ancestor (the baserev)"
712 712 $ cat > file.changed <<EOF
713 713 > first (changed)
714 714 > second
715 715 > third
716 716 > fourth
717 717 > fifth
718 718 > EOF
719 719 $ hg commit -Aqm "the first commit to fix"
720 720 $ cat > file.changed <<EOF
721 721 > first (changed)
722 722 > second
723 723 > third (changed)
724 724 > fourth
725 725 > fifth
726 726 > EOF
727 727 $ hg commit -Aqm "the second commit to fix"
728 728 $ cat > file.changed <<EOF
729 729 > first (changed)
730 730 > second
731 731 > third (changed)
732 732 > fourth
733 733 > fifth (changed)
734 734 > EOF
735 735
736 736 $ hg fix -r . -r '.^' --working-dir
737 737
738 738 $ hg parents --template '{rev}\n'
739 739 4
740 740 $ hg cat -r '.^^' file.changed
741 741 first
742 742 second
743 743 third
744 744 fourth
745 745 fifth
746 746 $ hg cat -r '.^' file.changed
747 747 FIRST (CHANGED)
748 748 second
749 749 third
750 750 fourth
751 751 fifth
752 752 $ hg cat -r . file.changed
753 753 FIRST (CHANGED)
754 754 second
755 755 THIRD (CHANGED)
756 756 fourth
757 757 fifth
758 758 $ cat file.changed
759 759 FIRST (CHANGED)
760 760 second
761 761 THIRD (CHANGED)
762 762 fourth
763 763 FIFTH (CHANGED)
764 764
765 765 $ cd ..
766 766
767 767 If we incrementally fix a merge commit, we should fix any lines that changed
768 768 versus either parent. You could imagine only fixing the intersection or some
769 769 other subset, but this is necessary if either parent is being fixed. It
770 770 prevents us from forgetting fixes made in either parent.
771 771
772 772 $ hg init incrementallyfixmergecommit
773 773 $ cd incrementallyfixmergecommit
774 774
775 775 $ printf "a\nb\nc\n" > file.changed
776 776 $ hg commit -Aqm "ancestor"
777 777
778 778 $ printf "aa\nb\nc\n" > file.changed
779 779 $ hg commit -m "change a"
780 780
781 781 $ hg checkout '.^'
782 782 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
783 783 $ printf "a\nb\ncc\n" > file.changed
784 784 $ hg commit -m "change c"
785 785 created new head
786 786
787 787 $ hg merge
788 788 merging file.changed
789 789 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
790 790 (branch merge, don't forget to commit)
791 791 $ hg commit -m "merge"
792 792 $ hg cat -r . file.changed
793 793 aa
794 794 b
795 795 cc
796 796
797 797 $ hg fix -r . --working-dir
798 798 $ hg cat -r . file.changed
799 799 AA
800 800 b
801 801 CC
802 802
803 803 $ cd ..
804 804
805 805 Abort fixing revisions if there is an unfinished operation. We don't want to
806 806 make things worse by editing files or stripping/obsoleting things. Also abort
807 807 fixing the working directory if there are unresolved merge conflicts.
808 808
809 809 $ hg init abortunresolved
810 810 $ cd abortunresolved
811 811
812 812 $ echo "foo1" > foo.whole
813 813 $ hg commit -Aqm "foo 1"
814 814
815 815 $ hg update null
816 816 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
817 817 $ echo "foo2" > foo.whole
818 818 $ hg commit -Aqm "foo 2"
819 819
820 820 $ hg --config extensions.rebase= rebase -r 1 -d 0
821 821 rebasing 1:c3b6dc0e177a "foo 2" (tip)
822 822 merging foo.whole
823 823 warning: conflicts while merging foo.whole! (edit, then use 'hg resolve --mark')
824 824 unresolved conflicts (see hg resolve, then hg rebase --continue)
825 825 [1]
826 826
827 827 $ hg --config extensions.rebase= fix --working-dir
828 828 abort: unresolved conflicts
829 829 (use 'hg resolve')
830 830 [255]
831 831
832 832 $ hg --config extensions.rebase= fix -r .
833 833 abort: rebase in progress
834 834 (use 'hg rebase --continue' or 'hg rebase --abort')
835 835 [255]
836 836
837 837 $ cd ..
838 838
839 839 When fixing a file that was renamed, we should diff against the source of the
840 840 rename for incremental fixing and we should correctly reproduce the rename in
841 841 the replacement revision.
842 842
843 843 $ hg init fixrenamecommit
844 844 $ cd fixrenamecommit
845 845
846 846 $ printf "a\nb\nc\n" > source.changed
847 847 $ hg commit -Aqm "source revision"
848 848 $ hg move source.changed dest.changed
849 849 $ printf "a\nb\ncc\n" > dest.changed
850 850 $ hg commit -m "dest revision"
851 851
852 852 $ hg fix -r .
853 853 $ hg log -r tip --copies --template "{file_copies}\n"
854 854 dest.changed (source.changed)
855 855 $ hg cat -r tip dest.changed
856 856 a
857 857 b
858 858 CC
859 859
860 860 $ cd ..
861 861
862 862 When fixing revisions that remove files we must ensure that the replacement
863 863 actually removes the file, whereas it could accidentally leave it unchanged or
864 864 write an empty string to it.
865 865
866 866 $ hg init fixremovedfile
867 867 $ cd fixremovedfile
868 868
869 869 $ printf "foo\n" > foo.whole
870 870 $ printf "bar\n" > bar.whole
871 871 $ hg commit -Aqm "add files"
872 872 $ hg remove bar.whole
873 873 $ hg commit -m "remove file"
874 874 $ hg status --change .
875 875 R bar.whole
876 876 $ hg fix -r . foo.whole
877 877 $ hg status --change tip
878 878 M foo.whole
879 879 R bar.whole
880 880
881 881 $ cd ..
882 882
883 883 If fixing a revision finds no fixes to make, no replacement revision should be
884 884 created.
885 885
886 886 $ hg init nofixesneeded
887 887 $ cd nofixesneeded
888 888
889 889 $ printf "FOO\n" > foo.whole
890 890 $ hg commit -Aqm "add file"
891 891 $ hg log --template '{rev}\n'
892 892 0
893 893 $ hg fix -r .
894 894 $ hg log --template '{rev}\n'
895 895 0
896 896
897 897 $ cd ..
898 898
899 899 If fixing a commit reverts all the changes in the commit, we replace it with a
900 900 commit that changes no files.
901 901
902 902 $ hg init nochangesleft
903 903 $ cd nochangesleft
904 904
905 905 $ printf "FOO\n" > foo.whole
906 906 $ hg commit -Aqm "add file"
907 907 $ printf "foo\n" > foo.whole
908 908 $ hg commit -m "edit file"
909 909 $ hg status --change .
910 910 M foo.whole
911 911 $ hg fix -r .
912 912 $ hg status --change tip
913 913
914 914 $ cd ..
915 915
916 916 If we fix a parent and child revision together, the child revision must be
917 917 replaced if the parent is replaced, even if the diffs of the child needed no
918 918 fixes. However, we're free to not replace revisions that need no fixes and have
919 919 no ancestors that are replaced.
920 920
921 921 $ hg init mustreplacechild
922 922 $ cd mustreplacechild
923 923
924 924 $ printf "FOO\n" > foo.whole
925 925 $ hg commit -Aqm "add foo"
926 926 $ printf "foo\n" > foo.whole
927 927 $ hg commit -m "edit foo"
928 928 $ printf "BAR\n" > bar.whole
929 929 $ hg commit -Aqm "add bar"
930 930
931 931 $ hg log --graph --template '{rev} {files}'
932 932 @ 2 bar.whole
933 933 |
934 934 o 1 foo.whole
935 935 |
936 936 o 0 foo.whole
937 937
938 938 $ hg fix -r 0:2
939 939 $ hg log --graph --template '{rev} {files}'
940 940 o 4 bar.whole
941 941 |
942 942 o 3
943 943 |
944 944 | @ 2 bar.whole
945 945 | |
946 946 | x 1 foo.whole
947 947 |/
948 948 o 0 foo.whole
949 949
950 950
951 951 $ cd ..
952 952
953 953 It's also possible that the child needs absolutely no changes, but we still
954 954 need to replace it to update its parent. If we skipped replacing the child
955 955 because it had no file content changes, it would become an orphan for no good
956 956 reason.
957 957
958 958 $ hg init mustreplacechildevenifnop
959 959 $ cd mustreplacechildevenifnop
960 960
961 961 $ printf "Foo\n" > foo.whole
962 962 $ hg commit -Aqm "add a bad foo"
963 963 $ printf "FOO\n" > foo.whole
964 964 $ hg commit -m "add a good foo"
965 965 $ hg fix -r . -r '.^'
966 966 $ hg log --graph --template '{rev} {desc}'
967 967 o 3 add a good foo
968 968 |
969 969 o 2 add a bad foo
970 970
971 971 @ 1 add a good foo
972 972 |
973 973 x 0 add a bad foo
974 974
975 975
976 976 $ cd ..
977 977
978 978 Similar to the case above, the child revision may become empty as a result of
979 979 fixing its parent. We should still create an empty replacement child.
980 980 TODO: determine how this should interact with ui.allowemptycommit given that
981 981 the empty replacement could have children.
982 982
983 983 $ hg init mustreplacechildevenifempty
984 984 $ cd mustreplacechildevenifempty
985 985
986 986 $ printf "foo\n" > foo.whole
987 987 $ hg commit -Aqm "add foo"
988 988 $ printf "Foo\n" > foo.whole
989 989 $ hg commit -m "edit foo"
990 990 $ hg fix -r . -r '.^'
991 991 $ hg log --graph --template '{rev} {desc}\n' --stat
992 992 o 3 edit foo
993 993 |
994 994 o 2 add foo
995 995 foo.whole | 1 +
996 996 1 files changed, 1 insertions(+), 0 deletions(-)
997 997
998 998 @ 1 edit foo
999 999 | foo.whole | 2 +-
1000 1000 | 1 files changed, 1 insertions(+), 1 deletions(-)
1001 1001 |
1002 1002 x 0 add foo
1003 1003 foo.whole | 1 +
1004 1004 1 files changed, 1 insertions(+), 0 deletions(-)
1005 1005
1006 1006
1007 1007 $ cd ..
1008 1008
1009 1009 Fixing a secret commit should replace it with another secret commit.
1010 1010
1011 1011 $ hg init fixsecretcommit
1012 1012 $ cd fixsecretcommit
1013 1013
1014 1014 $ printf "foo\n" > foo.whole
1015 1015 $ hg commit -Aqm "add foo" --secret
1016 1016 $ hg fix -r .
1017 1017 $ hg log --template '{rev} {phase}\n'
1018 1018 1 secret
1019 1019 0 secret
1020 1020
1021 1021 $ cd ..
1022 1022
1023 1023 We should also preserve phase when fixing a draft commit while the user has
1024 1024 their default set to secret.
1025 1025
1026 1026 $ hg init respectphasesnewcommit
1027 1027 $ cd respectphasesnewcommit
1028 1028
1029 1029 $ printf "foo\n" > foo.whole
1030 1030 $ hg commit -Aqm "add foo"
1031 1031 $ hg --config phases.newcommit=secret fix -r .
1032 1032 $ hg log --template '{rev} {phase}\n'
1033 1033 1 draft
1034 1034 0 draft
1035 1035
1036 1036 $ cd ..
1037 1037
1038 1038 Debug output should show what fixer commands are being subprocessed, which is
1039 1039 useful for anyone trying to set up a new config.
1040 1040
1041 1041 $ hg init debugoutput
1042 1042 $ cd debugoutput
1043 1043
1044 1044 $ printf "foo\nbar\nbaz\n" > foo.changed
1045 1045 $ hg commit -Aqm "foo"
1046 1046 $ printf "Foo\nbar\nBaz\n" > foo.changed
1047 1047 $ hg --debug fix --working-dir
1048 1048 subprocess: * $TESTTMP/uppercase.py 1-1 3-3 (glob)
1049 1049
1050 1050 $ cd ..
1051 1051
1052 1052 Fixing an obsolete revision can cause divergence, so we abort unless the user
1053 1053 configures to allow it. This is not yet smart enough to know whether there is a
1054 1054 successor, but even then it is not likely intentional or idiomatic to fix an
1055 1055 obsolete revision.
1056 1056
1057 1057 $ hg init abortobsoleterev
1058 1058 $ cd abortobsoleterev
1059 1059
1060 1060 $ printf "foo\n" > foo.changed
1061 1061 $ hg commit -Aqm "foo"
1062 1062 $ hg debugobsolete `hg parents --template '{node}'`
1063 1063 obsoleted 1 changesets
1064 1064 $ hg --hidden fix -r 0
1065 1065 abort: fixing obsolete revision could cause divergence
1066 1066 [255]
1067 1067
1068 1068 $ hg --hidden fix -r 0 --config experimental.evolution.allowdivergence=true
1069 1069 $ hg cat -r tip foo.changed
1070 1070 FOO
1071 1071
1072 1072 $ cd ..
1073 1073
1074 1074 Test all of the available substitution values for fixer commands.
1075 1075
1076 1076 $ hg init substitution
1077 1077 $ cd substitution
1078 1078
1079 1079 $ mkdir foo
1080 1080 $ printf "hello\ngoodbye\n" > foo/bar
1081 1081 $ hg add
1082 1082 adding foo/bar
1083 1083 $ hg --config "fix.fail:command=printf '%s\n' '{rootpath}' '{basename}'" \
1084 1084 > --config "fix.fail:linerange='{first}' '{last}'" \
1085 1085 > --config "fix.fail:pattern=foo/bar" \
1086 1086 > fix --working-dir
1087 1087 $ cat foo/bar
1088 1088 foo/bar
1089 1089 bar
1090 1090 1
1091 1091 2
1092 1092
1093 1093 $ cd ..
1094 1094
1095 1095 The --base flag should allow picking the revisions to diff against for changed
1096 1096 files and incremental line formatting.
1097 1097
1098 1098 $ hg init baseflag
1099 1099 $ cd baseflag
1100 1100
1101 1101 $ printf "one\ntwo\n" > foo.changed
1102 1102 $ printf "bar\n" > bar.changed
1103 1103 $ hg commit -Aqm "first"
1104 1104 $ printf "one\nTwo\n" > foo.changed
1105 1105 $ hg commit -m "second"
1106 1106 $ hg fix -w --base .
1107 1107 $ hg status
1108 1108 $ hg fix -w --base null
1109 1109 $ cat foo.changed
1110 1110 ONE
1111 1111 TWO
1112 1112 $ cat bar.changed
1113 1113 BAR
1114 1114
1115 1115 $ cd ..
1116 1116
1117 1117 If the user asks to fix the parent of another commit, they are asking to create
1118 1118 an orphan. We must respect experimental.evolution.allowunstable.
1119 1119
1120 1120 $ hg init allowunstable
1121 1121 $ cd allowunstable
1122 1122
1123 1123 $ printf "one\n" > foo.whole
1124 1124 $ hg commit -Aqm "first"
1125 1125 $ printf "two\n" > foo.whole
1126 1126 $ hg commit -m "second"
1127 1127 $ hg --config experimental.evolution.allowunstable=False fix -r '.^'
1128 1128 abort: can only fix a changeset together with all its descendants
1129 1129 [255]
1130 1130 $ hg fix -r '.^'
1131 1131 1 new orphan changesets
1132 1132 $ hg cat -r 2 foo.whole
1133 1133 ONE
1134 1134
1135 1135 $ cd ..
1136 1136
1137 1137 The --base flag affects the set of files being fixed. So while the --whole flag
1138 1138 makes the base irrelevant for changed line ranges, it still changes the
1139 1139 meaning and effect of the command. In this example, no files or lines are fixed
1140 1140 until we specify the base, but then we do fix unchanged lines.
1141 1141
1142 1142 $ hg init basewhole
1143 1143 $ cd basewhole
1144 1144 $ printf "foo1\n" > foo.changed
1145 1145 $ hg commit -Aqm "first"
1146 1146 $ printf "foo2\n" >> foo.changed
1147 1147 $ printf "bar\n" > bar.changed
1148 1148 $ hg commit -Aqm "second"
1149 1149
1150 1150 $ hg fix --working-dir --whole
1151 1151 $ cat *.changed
1152 1152 bar
1153 1153 foo1
1154 1154 foo2
1155 1155
1156 1156 $ hg fix --working-dir --base 0 --whole
1157 1157 $ cat *.changed
1158 1158 BAR
1159 1159 FOO1
1160 1160 FOO2
1161 1161
1162 1162 $ cd ..
1163 1163
1164 1164 The execution order of tools can be controlled. This example doesn't work if
1165 1165 you sort after truncating, but the config defines the correct order while the
1166 1166 definitions are out of order (which might imply the incorrect order given the
1167 1167 implementation of fix). The goal is to use multiple tools to select the lowest
1168 1168 5 numbers in the file.
1169 1169
1170 1170 $ hg init priorityexample
1171 1171 $ cd priorityexample
1172 1172
1173 1173 $ cat >> .hg/hgrc <<EOF
1174 1174 > [fix]
1175 1175 > head:command = head -n 5
1176 1176 > head:pattern = numbers.txt
1177 1177 > head:priority = 1
1178 1178 > sort:command = sort -n
1179 1179 > sort:pattern = numbers.txt
1180 1180 > sort:priority = 2
1181 1181 > EOF
1182 1182
1183 1183 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1184 1184 $ hg add -q
1185 1185 $ hg fix -w
1186 1186 $ cat numbers.txt
1187 1187 0
1188 1188 1
1189 1189 2
1190 1190 3
1191 1191 4
1192 1192
1193 1193 And of course we should be able to break this by reversing the execution order.
1194 1194 Test negative priorities while we're at it.
1195 1195
1196 1196 $ cat >> .hg/hgrc <<EOF
1197 1197 > [fix]
1198 1198 > head:priority = -1
1199 1199 > sort:priority = -2
1200 1200 > EOF
1201 1201 $ printf "8\n2\n3\n6\n7\n4\n9\n5\n1\n0\n" > numbers.txt
1202 1202 $ hg fix -w
1203 1203 $ cat numbers.txt
1204 1204 2
1205 1205 3
1206 1206 6
1207 1207 7
1208 1208 8
1209 1209
1210 1210 $ cd ..
1211 1211
1212 1212 It's possible for repeated applications of a fixer tool to create cycles in the
1213 1213 generated content of a file. For example, two users with different versions of
1214 1214 a code formatter might fight over the formatting when they run hg fix. In the
1215 1215 absence of other changes, this means we could produce commits with the same
1216 1216 hash in subsequent runs of hg fix. This is a problem unless we support
1217 1217 obsolescence cycles well. We avoid this by adding an extra field to the
1218 1218 successor which forces it to have a new hash. That's why this test creates
1219 1219 three revisions instead of two.
1220 1220
1221 1221 $ hg init cyclictool
1222 1222 $ cd cyclictool
1223 1223
1224 1224 $ cat >> .hg/hgrc <<EOF
1225 1225 > [fix]
1226 1226 > swapletters:command = tr ab ba
1227 1227 > swapletters:pattern = foo
1228 1228 > EOF
1229 1229
1230 1230 $ echo ab > foo
1231 1231 $ hg commit -Aqm foo
1232 1232
1233 1233 $ hg fix -r 0
1234 1234 $ hg fix -r 1
1235 1235
1236 1236 $ hg cat -r 0 foo --hidden
1237 1237 ab
1238 1238 $ hg cat -r 1 foo --hidden
1239 1239 ba
1240 1240 $ hg cat -r 2 foo
1241 1241 ab
1242 1242
1243 1243 $ cd ..
1244 1244
1245 Test that we can configure a fixer to affect all files regardless of the cwd.
1246 The way we invoke matching must not prohibit this.
1247
1248 $ hg init affectallfiles
1249 $ cd affectallfiles
1250
1251 $ mkdir foo bar
1252 $ printf "foo" > foo/file
1253 $ printf "bar" > bar/file
1254 $ printf "baz" > baz_file
1255 $ hg add -q
1256
1257 $ cd bar
1258 $ hg fix --working-dir --config "fix.cooltool:command=echo fixed" \
1259 > --config "fix.cooltool:pattern=rootglob:**"
1260 $ cd ..
1261
1262 $ cat foo/file
1263 fixed
1264 $ cat bar/file
1265 fixed
1266 $ cat baz_file
1267 fixed
1268
1269 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now