+proc apply_hunk {x y} {
+ global current_diff_path current_diff_header current_diff_side
+ global ui_diff ui_index file_states
+
+ if {$current_diff_path eq {} || $current_diff_header eq {}} return
+ if {![lock_index apply_hunk]} return
+
+ set apply_cmd {git apply --cached --whitespace=nowarn}
+ set mi [lindex $file_states($current_diff_path) 0]
+ if {$current_diff_side eq $ui_index} {
+ set mode unstage
+ lappend apply_cmd --reverse
+ if {[string index $mi 0] ne {M}} {
+ unlock_index
+ return
+ }
+ } else {
+ set mode stage
+ if {[string index $mi 1] ne {M}} {
+ unlock_index
+ return
+ }
+ }
+
+ set s_lno [lindex [split [$ui_diff index @$x,$y] .] 0]
+ set s_lno [$ui_diff search -backwards -regexp ^@@ $s_lno.0 0.0]
+ if {$s_lno eq {}} {
+ unlock_index
+ return
+ }
+
+ set e_lno [$ui_diff search -forwards -regexp ^@@ "$s_lno + 1 lines" end]
+ if {$e_lno eq {}} {
+ set e_lno end
+ }
+
+ if {[catch {
+ set p [open "| $apply_cmd" w]
+ fconfigure $p -translation binary -encoding binary
+ puts -nonewline $p $current_diff_header
+ puts -nonewline $p [$ui_diff get $s_lno $e_lno]
+ close $p} err]} {
+ error_popup "Failed to $mode selected hunk.\n\n$err"
+ unlock_index
+ return
+ }
+
+ $ui_diff conf -state normal
+ $ui_diff delete $s_lno $e_lno
+ $ui_diff conf -state disabled
+
+ if {[$ui_diff get 1.0 end] eq "\n"} {
+ set o _
+ } else {
+ set o ?
+ }
+
+ if {$current_diff_side eq $ui_index} {
+ set mi ${o}M
+ } elseif {[string index $mi 0] eq {_}} {
+ set mi M$o
+ } else {
+ set mi ?$o
+ }
+ unlock_index
+ display_file $current_diff_path $mi
+ if {$o eq {_}} {
+ clear_diff
+ }
+}
+