info_popup [mc "No differences detected.
[git-gui/kampbell.git] / lib / diff.tcl
blob9ccf12bda57842d3448796f1c28584cfff91846b
1 # git-gui diff viewer
2 # Copyright (C) 2006, 2007 Shawn Pearce
4 proc apply_tab_size {{firsttab {}}} {
5 global have_tk85 repo_config ui_diff
7 set w [font measure font_diff "0"]
8 if {$have_tk85 && $firsttab != 0} {
9 $ui_diff configure -tabs [list [expr {$firsttab * $w}] [expr {($firsttab + $repo_config(gui.tabsize)) * $w}]]
10 } elseif {$have_tk85 || $repo_config(gui.tabsize) != 8} {
11 $ui_diff configure -tabs [expr {$repo_config(gui.tabsize) * $w}]
12 } else {
13 $ui_diff configure -tabs {}
17 proc clear_diff {} {
18 global ui_diff current_diff_path current_diff_header
19 global ui_index ui_workdir
21 $ui_diff conf -state normal
22 $ui_diff delete 0.0 end
23 $ui_diff conf -state disabled
25 set current_diff_path {}
26 set current_diff_header {}
28 $ui_index tag remove in_diff 0.0 end
29 $ui_workdir tag remove in_diff 0.0 end
32 proc reshow_diff {{after {}}} {
33 global file_states file_lists
34 global current_diff_path current_diff_side
35 global ui_diff
37 set p $current_diff_path
38 if {$p eq {}} {
39 # No diff is being shown.
40 } elseif {$current_diff_side eq {}} {
41 clear_diff
42 } elseif {[catch {set s $file_states($p)}]
43 || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
45 if {[find_next_diff $current_diff_side $p {} {[^O]}]} {
46 next_diff $after
47 } else {
48 clear_diff
50 } else {
51 set save_pos [lindex [$ui_diff yview] 0]
52 show_diff $p $current_diff_side {} $save_pos $after
56 proc force_diff_encoding {enc} {
57 global current_diff_path
59 if {$current_diff_path ne {}} {
60 force_path_encoding $current_diff_path $enc
61 reshow_diff
65 proc handle_empty_diff {} {
66 global current_diff_path file_states file_lists
67 global diff_empty_count
69 set path $current_diff_path
70 set s $file_states($path)
71 if {[lindex $s 0] ne {_M} || [has_textconv $path]} return
73 # Prevent infinite rescan loops
74 incr diff_empty_count
75 if {$diff_empty_count > 1} return
77 clear_diff
78 display_file $path __
79 rescan ui_ready 0
82 proc show_diff {path w {lno {}} {scroll_pos {}} {callback {}}} {
83 global file_states file_lists
84 global is_3way_diff is_conflict_diff diff_active repo_config
85 global ui_diff ui_index ui_workdir
86 global current_diff_path current_diff_side current_diff_header
87 global current_diff_queue
89 if {$diff_active || ![lock_index read]} return
91 clear_diff
92 if {$lno == {}} {
93 set lno [lsearch -sorted -exact $file_lists($w) $path]
94 if {$lno >= 0} {
95 incr lno
98 if {$lno >= 1} {
99 $w tag add in_diff $lno.0 [expr {$lno + 1}].0
100 $w see $lno.0
103 set s $file_states($path)
104 set m [lindex $s 0]
105 set is_conflict_diff 0
106 set current_diff_path $path
107 set current_diff_side $w
108 set current_diff_queue {}
109 ui_status [mc "Loading diff of %s..." [escape_path $path]]
111 set cont_info [list $scroll_pos $callback]
113 apply_tab_size 0
115 if {[string first {U} $m] >= 0} {
116 merge_load_stages $path [list show_unmerged_diff $cont_info]
117 } elseif {$m eq {_O}} {
118 show_other_diff $path $w $m $cont_info
119 } else {
120 start_show_diff $cont_info
124 proc show_unmerged_diff {cont_info} {
125 global current_diff_path current_diff_side
126 global merge_stages ui_diff is_conflict_diff
127 global current_diff_queue
129 if {$merge_stages(2) eq {}} {
130 set is_conflict_diff 1
131 lappend current_diff_queue \
132 [list [mc "LOCAL: deleted\nREMOTE:\n"] d= \
133 [list ":1:$current_diff_path" ":3:$current_diff_path"]]
134 } elseif {$merge_stages(3) eq {}} {
135 set is_conflict_diff 1
136 lappend current_diff_queue \
137 [list [mc "REMOTE: deleted\nLOCAL:\n"] d= \
138 [list ":1:$current_diff_path" ":2:$current_diff_path"]]
139 } elseif {[lindex $merge_stages(1) 0] eq {120000}
140 || [lindex $merge_stages(2) 0] eq {120000}
141 || [lindex $merge_stages(3) 0] eq {120000}} {
142 set is_conflict_diff 1
143 lappend current_diff_queue \
144 [list [mc "LOCAL:\n"] d= \
145 [list ":1:$current_diff_path" ":2:$current_diff_path"]]
146 lappend current_diff_queue \
147 [list [mc "REMOTE:\n"] d= \
148 [list ":1:$current_diff_path" ":3:$current_diff_path"]]
149 } else {
150 start_show_diff $cont_info
151 return
154 advance_diff_queue $cont_info
157 proc advance_diff_queue {cont_info} {
158 global current_diff_queue ui_diff
160 set item [lindex $current_diff_queue 0]
161 set current_diff_queue [lrange $current_diff_queue 1 end]
163 $ui_diff conf -state normal
164 $ui_diff insert end [lindex $item 0] [lindex $item 1]
165 $ui_diff conf -state disabled
167 start_show_diff $cont_info [lindex $item 2]
170 proc show_other_diff {path w m cont_info} {
171 global file_states file_lists
172 global is_3way_diff diff_active repo_config
173 global ui_diff ui_index ui_workdir
174 global current_diff_path current_diff_side current_diff_header
176 # - Git won't give us the diff, there's nothing to compare to!
178 if {$m eq {_O}} {
179 set max_sz 100000
180 set type unknown
181 if {[catch {
182 set type [file type $path]
183 switch -- $type {
184 directory {
185 set type submodule
186 set content {}
187 set sz 0
189 link {
190 set content [file readlink $path]
191 set sz [string length $content]
193 file {
194 set fd [open $path r]
195 fconfigure $fd \
196 -eofchar {} \
197 -encoding [get_path_encoding $path]
198 set content [read $fd $max_sz]
199 close $fd
200 set sz [file size $path]
202 default {
203 error "'$type' not supported"
206 } err ]} {
207 set diff_active 0
208 unlock_index
209 ui_status [mc "Unable to display %s" [escape_path $path]]
210 error_popup [strcat [mc "Error loading file:"] "\n\n$err"]
211 return
213 $ui_diff conf -state normal
214 if {$type eq {submodule}} {
215 $ui_diff insert end [append \
216 "* " \
217 [mc "Git Repository (subproject)"] \
218 "\n"] d_info
219 } elseif {![catch {set type [exec file $path]}]} {
220 set n [string length $path]
221 if {[string equal -length $n $path $type]} {
222 set type [string range $type $n end]
223 regsub {^:?\s*} $type {} type
225 $ui_diff insert end "* $type\n" d_info
227 if {[string first "\0" $content] != -1} {
228 $ui_diff insert end \
229 [mc "* Binary file (not showing content)."] \
230 d_info
231 } else {
232 if {$sz > $max_sz} {
233 $ui_diff insert end [mc \
234 "* Untracked file is %d bytes.
235 * Showing only first %d bytes.
236 " $sz $max_sz] d_info
238 $ui_diff insert end $content
239 if {$sz > $max_sz} {
240 $ui_diff insert end [mc "
241 * Untracked file clipped here by %s.
242 * To see the entire file, use an external editor.
243 " [appname]] d_info
246 $ui_diff conf -state disabled
247 set diff_active 0
248 unlock_index
249 set scroll_pos [lindex $cont_info 0]
250 if {$scroll_pos ne {}} {
251 update
252 $ui_diff yview moveto $scroll_pos
254 ui_ready
255 set callback [lindex $cont_info 1]
256 if {$callback ne {}} {
257 eval $callback
259 return
263 proc get_conflict_marker_size {path} {
264 set size 7
265 catch {
266 set fd_rc [eval [list git_read check-attr "conflict-marker-size" -- $path]]
267 set ret [gets $fd_rc line]
268 close $fd_rc
269 if {$ret > 0} {
270 regexp {.*: conflict-marker-size: (\d+)$} $line line size
273 return $size
276 proc start_show_diff {cont_info {add_opts {}}} {
277 global file_states file_lists
278 global is_3way_diff is_submodule_diff diff_active repo_config
279 global ui_diff ui_index ui_workdir
280 global current_diff_path current_diff_side current_diff_header
282 set path $current_diff_path
283 set w $current_diff_side
285 set s $file_states($path)
286 set m [lindex $s 0]
287 set is_3way_diff 0
288 set is_submodule_diff 0
289 set diff_active 1
290 set current_diff_header {}
291 set conflict_size [get_conflict_marker_size $path]
293 set cmd [list]
294 if {$w eq $ui_index} {
295 lappend cmd diff-index
296 lappend cmd --cached
297 if {[git-version >= "1.7.2"]} {
298 lappend cmd --ignore-submodules=dirty
300 } elseif {$w eq $ui_workdir} {
301 if {[string first {U} $m] >= 0} {
302 lappend cmd diff
303 } else {
304 lappend cmd diff-files
307 if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} {
308 lappend cmd --textconv
311 if {[string match {160000 *} [lindex $s 2]]
312 || [string match {160000 *} [lindex $s 3]]} {
313 set is_submodule_diff 1
315 if {[git-version >= "1.6.6"]} {
316 lappend cmd --submodule
320 lappend cmd -p
321 lappend cmd --color
322 set cmd [concat $cmd $repo_config(gui.diffopts)]
323 if {$repo_config(gui.diffcontext) >= 1} {
324 lappend cmd "-U$repo_config(gui.diffcontext)"
326 if {$w eq $ui_index} {
327 lappend cmd [PARENT]
329 if {$add_opts ne {}} {
330 eval lappend cmd $add_opts
331 } else {
332 lappend cmd --
333 lappend cmd $path
336 if {$is_submodule_diff && [git-version < "1.6.6"]} {
337 if {$w eq $ui_index} {
338 set cmd [list submodule summary --cached -- $path]
339 } else {
340 set cmd [list submodule summary --files -- $path]
344 if {[catch {set fd [eval git_read --nice $cmd]} err]} {
345 set diff_active 0
346 unlock_index
347 ui_status [mc "Unable to display %s" [escape_path $path]]
348 error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
349 return
352 set ::current_diff_inheader 1
353 fconfigure $fd \
354 -blocking 0 \
355 -encoding [get_path_encoding $path] \
356 -translation lf
357 fileevent $fd readable [list read_diff $fd $conflict_size $cont_info]
360 proc parse_color_line {line} {
361 set start 0
362 set result ""
363 set markup [list]
364 set regexp {\033\[((?:\d+;)*\d+)?m}
365 set need_reset 0
366 while {[regexp -indices -start $start $regexp $line match code]} {
367 foreach {begin end} $match break
368 append result [string range $line $start [expr {$begin - 1}]]
369 set pos [string length $result]
370 set col [eval [linsert $code 0 string range $line]]
371 set start [incr end]
372 if {$col eq "0" || $col eq ""} {
373 if {!$need_reset} continue
374 set need_reset 0
375 } else {
376 set need_reset 1
378 lappend markup $pos $col
380 append result [string range $line $start end]
381 if {[llength $markup] < 4} {set markup {}}
382 return [list $result $markup]
385 proc read_diff {fd conflict_size cont_info} {
386 global ui_diff diff_active is_submodule_diff
387 global is_3way_diff is_conflict_diff current_diff_header
388 global current_diff_queue
389 global diff_empty_count
391 $ui_diff conf -state normal
392 while {[gets $fd line] >= 0} {
393 foreach {line markup} [parse_color_line $line] break
394 set line [string map {\033 ^} $line]
396 set tags {}
398 # -- Check for start of diff header.
399 if { [string match {diff --git *} $line]
400 || [string match {diff --cc *} $line]
401 || [string match {diff --combined *} $line]} {
402 set ::current_diff_inheader 1
405 # -- Check for end of diff header (any hunk line will do this).
407 if {[regexp {^@@+ } $line]} {set ::current_diff_inheader 0}
409 # -- Automatically detect if this is a 3 way diff.
411 if {[string match {@@@ *} $line]} {
412 set is_3way_diff 1
413 apply_tab_size 1
416 if {$::current_diff_inheader} {
418 # -- These two lines stop a diff header and shouldn't be in there
419 if { [string match {Binary files * and * differ} $line]
420 || [regexp {^\* Unmerged path } $line]} {
421 set ::current_diff_inheader 0
422 } else {
423 append current_diff_header $line "\n"
426 # -- Cleanup uninteresting diff header lines.
428 if { [string match {diff --git *} $line]
429 || [string match {diff --cc *} $line]
430 || [string match {diff --combined *} $line]
431 || [string match {--- *} $line]
432 || [string match {+++ *} $line]
433 || [string match {index *} $line]} {
434 continue
437 # -- Name it symlink, not 120000
438 # Note, that the original line is in $current_diff_header
439 regsub {^(deleted|new) file mode 120000} $line {\1 symlink} line
441 } elseif { $line eq {\ No newline at end of file}} {
442 # -- Handle some special lines
443 } elseif {$is_3way_diff} {
444 set op [string range $line 0 1]
445 switch -- $op {
446 { } {set tags {}}
447 {@@} {set tags d_@}
448 { +} {set tags d_s+}
449 { -} {set tags d_s-}
450 {+ } {set tags d_+s}
451 {- } {set tags d_-s}
452 {--} {set tags d_--}
453 {++} {
454 set regexp [string map [list %conflict_size $conflict_size]\
455 {^\+\+([<>=]){%conflict_size}(?: |$)}]
456 if {[regexp $regexp $line _g op]} {
457 set is_conflict_diff 1
458 set line [string replace $line 0 1 { }]
459 set tags d$op
460 } else {
461 set tags d_++
464 default {
465 puts "error: Unhandled 3 way diff marker: {$op}"
466 set tags {}
469 } elseif {$is_submodule_diff} {
470 if {$line == ""} continue
471 if {[regexp {^Submodule } $line]} {
472 set tags d_info
473 } elseif {[regexp {^\* } $line]} {
474 set line [string replace $line 0 1 {Submodule }]
475 set tags d_info
476 } else {
477 set op [string range $line 0 2]
478 switch -- $op {
479 { <} {set tags d_-}
480 { >} {set tags d_+}
481 { W} {set tags {}}
482 default {
483 puts "error: Unhandled submodule diff marker: {$op}"
484 set tags {}
488 } else {
489 set op [string index $line 0]
490 switch -- $op {
491 { } {set tags {}}
492 {@} {set tags d_@}
493 {-} {set tags d_-}
494 {+} {
495 set regexp [string map [list %conflict_size $conflict_size]\
496 {^\+([<>=]){%conflict_size}(?: |$)}]
497 if {[regexp $regexp $line _g op]} {
498 set is_conflict_diff 1
499 set tags d$op
500 } else {
501 set tags d_+
504 default {
505 puts "error: Unhandled 2 way diff marker: {$op}"
506 set tags {}
510 set mark [$ui_diff index "end - 1 line linestart"]
511 $ui_diff insert end $line $tags
512 if {[string index $line end] eq "\r"} {
513 $ui_diff tag add d_cr {end - 2c}
515 $ui_diff insert end "\n" $tags
517 foreach {posbegin colbegin posend colend} $markup {
518 set prefix clr
519 foreach style [lsort -integer [split $colbegin ";"]] {
520 if {$style eq "7"} {append prefix i; continue}
521 if {$style != 4 && ($style < 30 || $style > 47)} {continue}
522 set a "$mark linestart + $posbegin chars"
523 set b "$mark linestart + $posend chars"
524 catch {$ui_diff tag add $prefix$style $a $b}
528 $ui_diff conf -state disabled
530 if {[eof $fd]} {
531 close $fd
533 if {$current_diff_queue ne {}} {
534 advance_diff_queue $cont_info
535 return
538 set diff_active 0
539 unlock_index
540 set scroll_pos [lindex $cont_info 0]
541 if {$scroll_pos ne {}} {
542 update
543 $ui_diff yview moveto $scroll_pos
545 ui_ready
547 if {[$ui_diff index end] eq {2.0}} {
548 handle_empty_diff
549 } else {
550 set diff_empty_count 0
553 set callback [lindex $cont_info 1]
554 if {$callback ne {}} {
555 eval $callback
560 proc apply_hunk {x y} {
561 global current_diff_path current_diff_header current_diff_side
562 global ui_diff ui_index file_states
564 if {$current_diff_path eq {} || $current_diff_header eq {}} return
565 if {![lock_index apply_hunk]} return
567 set apply_cmd {apply --cached --whitespace=nowarn}
568 set mi [lindex $file_states($current_diff_path) 0]
569 if {$current_diff_side eq $ui_index} {
570 set failed_msg [mc "Failed to unstage selected hunk."]
571 lappend apply_cmd --reverse
572 if {[string index $mi 0] ne {M}} {
573 unlock_index
574 return
576 } else {
577 set failed_msg [mc "Failed to stage selected hunk."]
578 if {[string index $mi 1] ne {M}} {
579 unlock_index
580 return
584 set s_lno [lindex [split [$ui_diff index @$x,$y] .] 0]
585 set s_lno [$ui_diff search -backwards -regexp ^@@ $s_lno.0 0.0]
586 if {$s_lno eq {}} {
587 unlock_index
588 return
591 set e_lno [$ui_diff search -forwards -regexp ^@@ "$s_lno + 1 lines" end]
592 if {$e_lno eq {}} {
593 set e_lno end
596 if {[catch {
597 set enc [get_path_encoding $current_diff_path]
598 set p [eval git_write $apply_cmd]
599 fconfigure $p -translation binary -encoding $enc
600 puts -nonewline $p $current_diff_header
601 puts -nonewline $p [$ui_diff get $s_lno $e_lno]
602 close $p} err]} {
603 error_popup [append $failed_msg "\n\n$err"]
604 unlock_index
605 return
608 $ui_diff conf -state normal
609 $ui_diff delete $s_lno $e_lno
610 $ui_diff conf -state disabled
612 if {[$ui_diff get 1.0 end] eq "\n"} {
613 set o _
614 } else {
615 set o ?
618 if {$current_diff_side eq $ui_index} {
619 set mi ${o}M
620 } elseif {[string index $mi 0] eq {_}} {
621 set mi M$o
622 } else {
623 set mi ?$o
625 unlock_index
626 display_file $current_diff_path $mi
627 # This should trigger shift to the next changed file
628 if {$o eq {_}} {
629 reshow_diff
633 proc apply_range_or_line {x y} {
634 global current_diff_path current_diff_header current_diff_side
635 global ui_diff ui_index file_states
637 set selected [$ui_diff tag nextrange sel 0.0]
639 if {$selected == {}} {
640 set first [$ui_diff index "@$x,$y"]
641 set last $first
642 } else {
643 set first [lindex $selected 0]
644 set last [lindex $selected 1]
647 set first_l [$ui_diff index "$first linestart"]
648 set last_l [$ui_diff index "$last lineend"]
650 if {$current_diff_path eq {} || $current_diff_header eq {}} return
651 if {![lock_index apply_hunk]} return
653 set apply_cmd {apply --cached --whitespace=nowarn}
654 set mi [lindex $file_states($current_diff_path) 0]
655 if {$current_diff_side eq $ui_index} {
656 set failed_msg [mc "Failed to unstage selected line."]
657 set to_context {+}
658 lappend apply_cmd --reverse
659 if {[string index $mi 0] ne {M}} {
660 unlock_index
661 return
663 } else {
664 set failed_msg [mc "Failed to stage selected line."]
665 set to_context {-}
666 if {[string index $mi 1] ne {M}} {
667 unlock_index
668 return
672 set wholepatch {}
674 while {$first_l < $last_l} {
675 set i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0]
676 if {$i_l eq {}} {
677 # If there's not a @@ above, then the selected range
678 # must have come before the first_l @@
679 set i_l [$ui_diff search -regexp ^@@ $first_l $last_l]
681 if {$i_l eq {}} {
682 unlock_index
683 return
685 # $i_l is now at the beginning of a line
687 # pick start line number from hunk header
688 set hh [$ui_diff get $i_l "$i_l + 1 lines"]
689 set hh [lindex [split $hh ,] 0]
690 set hln [lindex [split $hh -] 1]
692 # There is a special situation to take care of. Consider this
693 # hunk:
695 # @@ -10,4 +10,4 @@
696 # context before
697 # -old 1
698 # -old 2
699 # +new 1
700 # +new 2
701 # context after
703 # We used to keep the context lines in the order they appear in
704 # the hunk. But then it is not possible to correctly stage only
705 # "-old 1" and "+new 1" - it would result in this staged text:
707 # context before
708 # old 2
709 # new 1
710 # context after
712 # (By symmetry it is not possible to *un*stage "old 2" and "new
713 # 2".)
715 # We resolve the problem by introducing an asymmetry, namely,
716 # when a "+" line is *staged*, it is moved in front of the
717 # context lines that are generated from the "-" lines that are
718 # immediately before the "+" block. That is, we construct this
719 # patch:
721 # @@ -10,4 +10,5 @@
722 # context before
723 # +new 1
724 # old 1
725 # old 2
726 # context after
728 # But we do *not* treat "-" lines that are *un*staged in a
729 # special way.
731 # With this asymmetry it is possible to stage the change "old
732 # 1" -> "new 1" directly, and to stage the change "old 2" ->
733 # "new 2" by first staging the entire hunk and then unstaging
734 # the change "old 1" -> "new 1".
736 # Applying multiple lines adds complexity to the special
737 # situation. The pre_context must be moved after the entire
738 # first block of consecutive staged "+" lines, so that
739 # staging both additions gives the following patch:
741 # @@ -10,4 +10,6 @@
742 # context before
743 # +new 1
744 # +new 2
745 # old 1
746 # old 2
747 # context after
749 # This is non-empty if and only if we are _staging_ changes;
750 # then it accumulates the consecutive "-" lines (after
751 # converting them to context lines) in order to be moved after
752 # "+" change lines.
753 set pre_context {}
755 set n 0
756 set m 0
757 set i_l [$ui_diff index "$i_l + 1 lines"]
758 set patch {}
759 while {[$ui_diff compare $i_l < "end - 1 chars"] &&
760 [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} {
761 set next_l [$ui_diff index "$i_l + 1 lines"]
762 set c1 [$ui_diff get $i_l]
763 if {[$ui_diff compare $first_l <= $i_l] &&
764 [$ui_diff compare $i_l < $last_l] &&
765 ($c1 eq {-} || $c1 eq {+})} {
766 # a line to stage/unstage
767 set ln [$ui_diff get $i_l $next_l]
768 if {$c1 eq {-}} {
769 set n [expr $n+1]
770 set patch "$patch$pre_context$ln"
771 set pre_context {}
772 } else {
773 set m [expr $m+1]
774 set patch "$patch$ln"
776 } elseif {$c1 ne {-} && $c1 ne {+}} {
777 # context line
778 set ln [$ui_diff get $i_l $next_l]
779 set patch "$patch$pre_context$ln"
780 # Skip the "\ No newline at end of
781 # file". Depending on the locale setting
782 # we don't know what this line looks
783 # like exactly. The only thing we do
784 # know is that it starts with "\ "
785 if {![string match {\\ *} $ln]} {
786 set n [expr $n+1]
787 set m [expr $m+1]
789 set pre_context {}
790 } elseif {$c1 eq $to_context} {
791 # turn change line into context line
792 set ln [$ui_diff get "$i_l + 1 chars" $next_l]
793 if {$c1 eq {-}} {
794 set pre_context "$pre_context $ln"
795 } else {
796 set patch "$patch $ln"
798 set n [expr $n+1]
799 set m [expr $m+1]
800 } else {
801 # a change in the opposite direction of
802 # to_context which is outside the range of
803 # lines to apply.
804 set patch "$patch$pre_context"
805 set pre_context {}
807 set i_l $next_l
809 set patch "$patch$pre_context"
810 set wholepatch "$wholepatch@@ -$hln,$n +$hln,$m @@\n$patch"
811 set first_l [$ui_diff index "$next_l + 1 lines"]
814 if {[catch {
815 set enc [get_path_encoding $current_diff_path]
816 set p [eval git_write $apply_cmd]
817 fconfigure $p -translation binary -encoding $enc
818 puts -nonewline $p $current_diff_header
819 puts -nonewline $p $wholepatch
820 close $p} err]} {
821 error_popup [append $failed_msg "\n\n$err"]
824 unlock_index