Git fork
1# This script can be run in two different contexts:
2#
3# - From git, when the user invokes the "vimdiff" merge tool. In this context
4# this script expects the following environment variables (among others) to
5# be defined (which is something "git" takes care of):
6#
7# - $BASE
8# - $LOCAL
9# - $REMOTE
10# - $MERGED
11#
12# In this mode, all this script does is to run the next command:
13#
14# vim -f -c ... $LOCAL $BASE $REMOTE $MERGED
15#
16# ...where the "..." string depends on the value of the
17# "mergetool.vimdiff.layout" configuration variable and is used to open vim
18# with a certain layout of buffers, windows and tabs.
19#
20# - From a script inside the unit tests framework folder ("t" folder) by
21# sourcing this script and then manually calling "run_unit_tests", which
22# will run a battery of unit tests to make sure nothing breaks.
23# In this context this script does not expect any particular environment
24# variable to be set.
25
26
27################################################################################
28## Internal functions (not meant to be used outside this script)
29################################################################################
30
31debug_print () {
32 # Send message to stderr if global variable GIT_MERGETOOL_VIMDIFF_DEBUG
33 # is set.
34
35 if test -n "$GIT_MERGETOOL_VIMDIFF_DEBUG"
36 then
37 >&2 echo "$@"
38 fi
39}
40
41substring () {
42 # Return a substring of $1 containing $3 characters starting at
43 # zero-based offset $2.
44 #
45 # Examples:
46 #
47 # substring "Hello world" 0 4 --> "Hell"
48 # substring "Hello world" 3 4 --> "lo w"
49 # substring "Hello world" 3 10 --> "lo world"
50
51 STRING=$1
52 START=$2
53 LEN=$3
54
55 echo "$STRING" | cut -c$(( START + 1 ))-$(( START + $LEN ))
56}
57
58gen_cmd_aux () {
59 # Auxiliary function used from "gen_cmd()".
60 # Read that other function documentation for more details.
61
62 LAYOUT=$1
63 CMD=$2 # This is a second (hidden) argument used for recursion
64
65 debug_print
66 debug_print "LAYOUT : $LAYOUT"
67 debug_print "CMD : $CMD"
68
69 start=0
70 end=${#LAYOUT}
71
72 nested=0
73 nested_min=100
74
75 # Step 1:
76 #
77 # Increase/decrease "start"/"end" indices respectively to get rid of
78 # outer parenthesis.
79 #
80 # Example:
81 #
82 # - BEFORE: (( LOCAL , BASE ) / MERGED )
83 # - AFTER : ( LOCAL , BASE ) / MERGED
84
85 oldIFS=$IFS
86 IFS=#
87 for c in $(echo "$LAYOUT" | sed 's:.:&#:g')
88 do
89 if test -z "$c" || test "$c" = " "
90 then
91 continue
92 fi
93
94 if test "$c" = "("
95 then
96 nested=$(( nested + 1 ))
97 continue
98 fi
99
100 if test "$c" = ")"
101 then
102 nested=$(( nested - 1 ))
103 continue
104 fi
105
106 if test "$nested" -lt "$nested_min"
107 then
108 nested_min=$nested
109 fi
110 done
111 IFS=$oldIFS
112
113 debug_print "NESTED MIN: $nested_min"
114
115 while test "$nested_min" -gt "0"
116 do
117 start=$(( start + 1 ))
118 end=$(( end - 1 ))
119
120 start_minus_one=$(( start - 1 ))
121
122 while ! test "$(substring "$LAYOUT" "$start_minus_one" 1)" = "("
123 do
124 start=$(( start + 1 ))
125 start_minus_one=$(( start_minus_one + 1 ))
126 done
127
128 while ! test "$(substring "$LAYOUT" "$end" 1)" = ")"
129 do
130 end=$(( end - 1 ))
131 done
132
133 nested_min=$(( nested_min - 1 ))
134 done
135
136 debug_print "CLEAN : $(substring "$LAYOUT" "$start" "$(( end - start ))")"
137
138
139 # Step 2:
140 #
141 # Search for all valid separators ("/" or ",") which are *not*
142 # inside parenthesis. Save the index at which each of them makes the
143 # first appearance.
144
145 index_horizontal_split=""
146 index_vertical_split=""
147
148 nested=0
149 i=$(( start - 1 ))
150
151 oldIFS=$IFS
152 IFS=#
153 for c in $(substring "$LAYOUT" "$start" "$(( end - start ))" | sed 's:.:&#:g');
154 do
155 i=$(( i + 1 ))
156
157 if test "$c" = " "
158 then
159 continue
160 fi
161
162 if test "$c" = "("
163 then
164 nested=$(( nested + 1 ))
165 continue
166 fi
167
168 if test "$c" = ")"
169 then
170 nested=$(( nested - 1 ))
171 continue
172 fi
173
174 if test "$nested" = 0
175 then
176 current=$c
177
178 if test "$current" = "/"
179 then
180 if test -z "$index_horizontal_split"
181 then
182 index_horizontal_split=$i
183 fi
184
185 elif test "$current" = ","
186 then
187 if test -z "$index_vertical_split"
188 then
189 index_vertical_split=$i
190 fi
191 fi
192 fi
193 done
194 IFS=$oldIFS
195
196
197 # Step 3:
198 #
199 # Process the separator with the highest order of precedence
200 # (";" has the highest precedence and "|" the lowest one).
201 #
202 # By "process" I mean recursively call this function twice: the first
203 # one with the substring at the left of the separator and the second one
204 # with the one at its right.
205
206 terminate="false"
207
208 if ! test -z "$index_horizontal_split"
209 then
210 before="leftabove split"
211 after="wincmd j"
212 index=$index_horizontal_split
213 terminate="true"
214
215 elif ! test -z "$index_vertical_split"
216 then
217 before="leftabove vertical split"
218 after="wincmd l"
219 index=$index_vertical_split
220 terminate="true"
221 fi
222
223 if test "$terminate" = "true"
224 then
225 CMD="$CMD | $before"
226 CMD=$(gen_cmd_aux "$(substring "$LAYOUT" "$start" "$(( index - start ))")" "$CMD")
227 CMD="$CMD | $after"
228 CMD=$(gen_cmd_aux "$(substring "$LAYOUT" "$(( index + 1 ))" "$(( ${#LAYOUT} - index ))")" "$CMD")
229 echo "$CMD"
230 return
231 fi
232
233
234 # Step 4:
235 #
236 # If we reach this point, it means there are no separators and we just
237 # need to print the command to display the specified buffer
238
239 target=$(substring "$LAYOUT" "$start" "$(( end - start ))" | sed 's:[ @();|-]::g')
240
241 if test "$target" = "LOCAL"
242 then
243 CMD="$CMD | 1b"
244
245 elif test "$target" = "BASE"
246 then
247 CMD="$CMD | 2b"
248
249 elif test "$target" = "REMOTE"
250 then
251 CMD="$CMD | 3b"
252
253 elif test "$target" = "MERGED"
254 then
255 CMD="$CMD | 4b"
256
257 else
258 CMD="$CMD | ERROR: >$target<"
259 fi
260
261 echo "$CMD"
262 return
263}
264
265
266gen_cmd () {
267 # This function returns (in global variable FINAL_CMD) the string that
268 # you can use when invoking "vim" (as shown next) to obtain a given
269 # layout:
270 #
271 # $ vim -f $FINAL_CMD "$LOCAL" "$BASE" "$REMOTE" "$MERGED"
272 #
273 # It takes one single argument: a string containing the desired layout
274 # definition.
275 #
276 # The syntax of the "layout definitions" is explained in "Documentation/
277 # mergetools/vimdiff.adoc" but you can already intuitively understand
278 # how it works by knowing that...
279 #
280 # * "+" means "a new vim tab"
281 # * "/" means "a new vim horizontal split"
282 # * "," means "a new vim vertical split"
283 #
284 # It also returns (in global variable FINAL_TARGET) the name ("LOCAL",
285 # "BASE", "REMOTE" or "MERGED") of the file that is marked with an "@",
286 # or "MERGED" if none of them is.
287 #
288 # Example:
289 #
290 # gen_cmd "@LOCAL , REMOTE"
291 # |
292 # `-> FINAL_CMD == "-c \"echo | leftabove vertical split | 1b | wincmd l | 3b | tabdo windo diffthis\" -c \"tabfirst\""
293 # FINAL_TARGET == "LOCAL"
294
295 LAYOUT=$1
296
297
298 # Search for a "@" in one of the files identifiers ("LOCAL", "BASE",
299 # "REMOTE", "MERGED"). If not found, use "MERGE" as the default file
300 # where changes will be saved.
301
302 if echo "$LAYOUT" | grep @LOCAL >/dev/null
303 then
304 FINAL_TARGET="LOCAL"
305 elif echo "$LAYOUT" | grep @BASE >/dev/null
306 then
307 FINAL_TARGET="BASE"
308 elif echo "$LAYOUT" | grep @REMOTE >/dev/null
309 then
310 FINAL_TARGET="REMOTE"
311 else
312 FINAL_TARGET="MERGED"
313 fi
314
315
316 # Obtain the first part of vim "-c" option to obtain the desired layout
317
318 CMD=
319 oldIFS=$IFS
320 IFS=+
321 for tab in $LAYOUT
322 do
323 if test -z "$CMD"
324 then
325 CMD="echo" # vim "nop" operator
326 else
327 CMD="$CMD | tabnew"
328 fi
329
330 # If this is a single window diff with all the buffers
331 if ! echo "$tab" | grep -E ",|/" >/dev/null
332 then
333 CMD="$CMD | silent execute 'bufdo diffthis'"
334 fi
335
336 CMD=$(gen_cmd_aux "$tab" "$CMD")
337 done
338 IFS=$oldIFS
339
340 CMD="$CMD | execute 'tabdo windo diffthis'"
341
342 FINAL_CMD="-c \"set hidden diffopt-=hiddenoff | $CMD | tabfirst\""
343}
344
345
346################################################################################
347## API functions (called from "git-mergetool--lib.sh")
348################################################################################
349
350diff_cmd () {
351 "$merge_tool_path" -R -f -d \
352 -c 'wincmd l' -c 'cd $GIT_PREFIX' "$LOCAL" "$REMOTE"
353}
354
355
356diff_cmd_help () {
357 TOOL=$1
358
359 case "$TOOL" in
360 nvimdiff*)
361 printf "Use Neovim"
362 ;;
363 gvimdiff*)
364 printf "Use gVim (requires a graphical session)"
365 ;;
366 vimdiff*)
367 printf "Use Vim"
368 ;;
369 esac
370
371 return 0
372}
373
374
375merge_cmd () {
376 TOOL=$1
377
378 layout=$(git config "mergetool.$TOOL.layout")
379
380 # backward compatibility:
381 if test -z "$layout"
382 then
383 layout=$(git config mergetool.vimdiff.layout)
384 fi
385
386 case "$TOOL" in
387 *vimdiff)
388 if test -z "$layout"
389 then
390 # Default layout when none is specified
391 layout="(LOCAL,BASE,REMOTE)/MERGED"
392 fi
393 ;;
394 *vimdiff1)
395 layout="@LOCAL,REMOTE"
396 ;;
397 *vimdiff2)
398 layout="LOCAL,MERGED,REMOTE"
399 ;;
400 *vimdiff3)
401 layout="MERGED"
402 ;;
403 esac
404
405 gen_cmd "$layout"
406
407 debug_print ""
408 debug_print "FINAL CMD : $FINAL_CMD"
409 debug_print "FINAL TAR : $FINAL_TARGET"
410
411 if $base_present
412 then
413 eval '"$merge_tool_path"' \
414 -f "$FINAL_CMD" '"$LOCAL"' '"$BASE"' '"$REMOTE"' '"$MERGED"'
415 else
416 # If there is no BASE (example: a merge conflict in a new file
417 # with the same name created in both branches which didn't exist
418 # before), close all BASE windows using vim's "quit" command
419
420 FINAL_CMD=$(echo "$FINAL_CMD" | \
421 sed -e 's:2b:quit:g' -e 's:3b:2b:g' -e 's:4b:3b:g')
422
423 eval '"$merge_tool_path"' \
424 -f "$FINAL_CMD" '"$LOCAL"' '"$REMOTE"' '"$MERGED"'
425 fi
426
427 ret="$?"
428
429 if test "$ret" -eq 0
430 then
431 case "$FINAL_TARGET" in
432 LOCAL)
433 source_path="$LOCAL"
434 ;;
435 REMOTE)
436 source_path="$REMOTE"
437 ;;
438 MERGED|*)
439 # Do nothing
440 source_path=
441 ;;
442 esac
443
444 if test -n "$source_path"
445 then
446 cp "$source_path" "$MERGED"
447 fi
448 fi
449
450 return "$ret"
451}
452
453
454merge_cmd_help () {
455 TOOL=$1
456
457 case "$TOOL" in
458 nvimdiff*)
459 printf "Use Neovim "
460 ;;
461 gvimdiff*)
462 printf "Use gVim (requires a graphical session) "
463 ;;
464 vimdiff*)
465 printf "Use Vim "
466 ;;
467 esac
468
469 case "$TOOL" in
470 *1)
471 echo "with a 2 panes layout (LOCAL and REMOTE)"
472 ;;
473 *2)
474 echo "with a 3 panes layout (LOCAL, MERGED and REMOTE)"
475 ;;
476 *3)
477 echo "where only the MERGED file is shown"
478 ;;
479 *)
480 echo "with a custom layout (see \`git help mergetool\`'s \`BACKEND SPECIFIC HINTS\` section)"
481 ;;
482 esac
483
484 return 0
485}
486
487
488translate_merge_tool_path () {
489 case "$1" in
490 nvimdiff*)
491 echo nvim
492 ;;
493 gvimdiff*)
494 echo gvim
495 ;;
496 vimdiff*)
497 echo vim
498 ;;
499 esac
500}
501
502
503exit_code_trustable () {
504 true
505}
506
507
508list_tool_variants () {
509 if test "$TOOL_MODE" = "diff"
510 then
511 for prefix in '' g n
512 do
513 echo "${prefix}vimdiff"
514 done
515 else
516 for prefix in '' g n
517 do
518 for suffix in '' 1 2 3
519 do
520 echo "${prefix}vimdiff${suffix}"
521 done
522 done
523 fi
524}
525
526
527################################################################################
528## Unit tests (called from scripts inside the "t" folder)
529################################################################################
530
531run_unit_tests () {
532 # Function to make sure that we don't break anything when modifying this
533 # script.
534
535 NUMBER_OF_TEST_CASES=19
536
537 TEST_CASE_01="(LOCAL,BASE,REMOTE)/MERGED" # default behaviour
538 TEST_CASE_02="@LOCAL,REMOTE" # when using vimdiff1
539 TEST_CASE_03="LOCAL,MERGED,REMOTE" # when using vimdiff2
540 TEST_CASE_04="MERGED" # when using vimdiff3
541 TEST_CASE_05="LOCAL/MERGED/REMOTE"
542 TEST_CASE_06="(LOCAL/REMOTE),MERGED"
543 TEST_CASE_07="MERGED,(LOCAL/REMOTE)"
544 TEST_CASE_08="(LOCAL,REMOTE)/MERGED"
545 TEST_CASE_09="MERGED/(LOCAL,REMOTE)"
546 TEST_CASE_10="(LOCAL/BASE/REMOTE),MERGED"
547 TEST_CASE_11="(LOCAL,BASE,REMOTE)/MERGED+BASE,LOCAL+BASE,REMOTE+(LOCAL/BASE/REMOTE),MERGED"
548 TEST_CASE_12="((LOCAL,REMOTE)/BASE),MERGED"
549 TEST_CASE_13="((LOCAL,REMOTE)/BASE),((LOCAL/REMOTE),MERGED)"
550 TEST_CASE_14="BASE,REMOTE+BASE,LOCAL"
551 TEST_CASE_15=" (( (LOCAL , BASE , REMOTE) / MERGED)) +(BASE) , LOCAL+ BASE , REMOTE+ (((LOCAL / BASE / REMOTE)) , MERGED ) "
552 TEST_CASE_16="LOCAL,BASE,REMOTE / MERGED + BASE,LOCAL + BASE,REMOTE + (LOCAL / BASE / REMOTE),MERGED"
553 TEST_CASE_17="(LOCAL,@BASE,REMOTE)/MERGED"
554 TEST_CASE_18="LOCAL,@REMOTE"
555 TEST_CASE_19="@REMOTE"
556
557 EXPECTED_CMD_01="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
558 EXPECTED_CMD_02="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
559 EXPECTED_CMD_03="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 4b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
560 EXPECTED_CMD_04="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 4b | execute 'tabdo windo diffthis' | tabfirst\""
561 EXPECTED_CMD_05="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 1b | wincmd j | leftabove split | 4b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\""
562 EXPECTED_CMD_06="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
563 EXPECTED_CMD_07="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 4b | wincmd l | leftabove split | 1b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst\""
564 EXPECTED_CMD_08="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
565 EXPECTED_CMD_09="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | 4b | wincmd j | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
566 EXPECTED_CMD_10="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
567 EXPECTED_CMD_11="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
568 EXPECTED_CMD_12="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
569 EXPECTED_CMD_13="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | leftabove split | leftabove vertical split | 1b | wincmd l | 3b | wincmd j | 2b | wincmd l | leftabove vertical split | leftabove split | 1b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
570 EXPECTED_CMD_14="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | execute 'tabdo windo diffthis' | tabfirst\""
571 EXPECTED_CMD_15="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
572 EXPECTED_CMD_16="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | tabnew | leftabove vertical split | 2b | wincmd l | 1b | tabnew | leftabove vertical split | 2b | wincmd l | 3b | tabnew | leftabove vertical split | leftabove split | 1b | wincmd j | leftabove split | 2b | wincmd j | 3b | wincmd l | 4b | execute 'tabdo windo diffthis' | tabfirst\""
573 EXPECTED_CMD_17="-c \"set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | 2b | wincmd l | 3b | wincmd j | 4b | execute 'tabdo windo diffthis' | tabfirst\""
574 EXPECTED_CMD_18="-c \"set hidden diffopt-=hiddenoff | echo | leftabove vertical split | 1b | wincmd l | 3b | execute 'tabdo windo diffthis' | tabfirst\""
575 EXPECTED_CMD_19="-c \"set hidden diffopt-=hiddenoff | echo | silent execute 'bufdo diffthis' | 3b | execute 'tabdo windo diffthis' | tabfirst\""
576
577 EXPECTED_TARGET_01="MERGED"
578 EXPECTED_TARGET_02="LOCAL"
579 EXPECTED_TARGET_03="MERGED"
580 EXPECTED_TARGET_04="MERGED"
581 EXPECTED_TARGET_05="MERGED"
582 EXPECTED_TARGET_06="MERGED"
583 EXPECTED_TARGET_07="MERGED"
584 EXPECTED_TARGET_08="MERGED"
585 EXPECTED_TARGET_09="MERGED"
586 EXPECTED_TARGET_10="MERGED"
587 EXPECTED_TARGET_11="MERGED"
588 EXPECTED_TARGET_12="MERGED"
589 EXPECTED_TARGET_13="MERGED"
590 EXPECTED_TARGET_14="MERGED"
591 EXPECTED_TARGET_15="MERGED"
592 EXPECTED_TARGET_16="MERGED"
593 EXPECTED_TARGET_17="BASE"
594 EXPECTED_TARGET_18="REMOTE"
595 EXPECTED_TARGET_19="REMOTE"
596
597 at_least_one_ko="false"
598
599 for i in $(seq -w 1 99)
600 do
601 if test "$i" -gt $NUMBER_OF_TEST_CASES
602 then
603 break
604 fi
605
606 gen_cmd "$(eval echo \${TEST_CASE_"$i"})"
607
608 if test "$FINAL_CMD" = "$(eval echo \${EXPECTED_CMD_"$i"})" \
609 && test "$FINAL_TARGET" = "$(eval echo \${EXPECTED_TARGET_"$i"})"
610 then
611 printf "Test Case #%02d: OK\n" "$(echo "$i" | sed 's/^0*//')"
612 else
613 printf "Test Case #%02d: KO !!!!\n" "$(echo "$i" | sed 's/^0*//')"
614 echo " FINAL_CMD : $FINAL_CMD"
615 echo " FINAL_CMD (expected) : $(eval echo \${EXPECTED_CMD_"$i"})"
616 echo " FINAL_TARGET : $FINAL_TARGET"
617 echo " FINAL_TARGET (expected): $(eval echo \${EXPECTED_TARGET_"$i"})"
618 at_least_one_ko="true"
619 fi
620 done
621
622 # verify that `merge_cmd` handles paths with spaces
623 record_parameters () {
624 >actual
625 for arg
626 do
627 echo "$arg" >>actual
628 done
629 }
630
631 base_present=false
632 LOCAL='lo cal'
633 BASE='ba se'
634 REMOTE="' '"
635 MERGED='mer ged'
636 merge_tool_path=record_parameters
637
638 merge_cmd vimdiff || at_least_one_ko=true
639
640 cat >expect <<-\EOF
641 -f
642 -c
643 set hidden diffopt-=hiddenoff | echo | leftabove split | leftabove vertical split | 1b | wincmd l | leftabove vertical split | quit | wincmd l | 2b | wincmd j | 3b | execute 'tabdo windo diffthis' | tabfirst
644 lo cal
645 ' '
646 mer ged
647 EOF
648
649 diff -u expect actual || at_least_one_ko=true
650
651 if test "$at_least_one_ko" = "true"
652 then
653 return 255
654 else
655 return 0
656 fi
657}