Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 1 | # git-gui diff viewer |
| 2 | # Copyright (C) 2006, 2007 Shawn Pearce |
| 3 | |
| 4 | proc clear_diff {} { |
| 5 | global ui_diff current_diff_path current_diff_header |
| 6 | global ui_index ui_workdir |
| 7 | |
| 8 | $ui_diff conf -state normal |
| 9 | $ui_diff delete 0.0 end |
| 10 | $ui_diff conf -state disabled |
| 11 | |
| 12 | set current_diff_path {} |
| 13 | set current_diff_header {} |
| 14 | |
| 15 | $ui_index tag remove in_diff 0.0 end |
| 16 | $ui_workdir tag remove in_diff 0.0 end |
| 17 | } |
| 18 | |
| 19 | proc reshow_diff {} { |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 20 | global file_states file_lists |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 21 | global current_diff_path current_diff_side |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 22 | global ui_diff |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 23 | |
| 24 | set p $current_diff_path |
| 25 | if {$p eq {}} { |
| 26 | # No diff is being shown. |
| 27 | } elseif {$current_diff_side eq {} |
| 28 | || [catch {set s $file_states($p)}] |
| 29 | || [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} { |
| 30 | clear_diff |
| 31 | } else { |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 32 | set save_pos [lindex [$ui_diff yview] 0] |
| 33 | show_diff $p $current_diff_side {} $save_pos |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 34 | } |
| 35 | } |
| 36 | |
| 37 | proc handle_empty_diff {} { |
| 38 | global current_diff_path file_states file_lists |
| 39 | |
| 40 | set path $current_diff_path |
| 41 | set s $file_states($path) |
| 42 | if {[lindex $s 0] ne {_M}} return |
| 43 | |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 44 | info_popup [mc "No differences detected. |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 45 | |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 46 | %s has no changes. |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 47 | |
| 48 | The modification date of this file was updated by another application, but the content within the file was not changed. |
| 49 | |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 50 | A rescan will be automatically started to find other files which may have the same state." [short_path $path]] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 51 | |
| 52 | clear_diff |
| 53 | display_file $path __ |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 54 | rescan ui_ready 0 |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 55 | } |
| 56 | |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 57 | proc show_diff {path w {lno {}} {scroll_pos {}}} { |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 58 | global file_states file_lists |
| 59 | global is_3way_diff diff_active repo_config |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 60 | global ui_diff ui_index ui_workdir |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 61 | global current_diff_path current_diff_side current_diff_header |
| 62 | |
| 63 | if {$diff_active || ![lock_index read]} return |
| 64 | |
| 65 | clear_diff |
| 66 | if {$lno == {}} { |
| 67 | set lno [lsearch -sorted -exact $file_lists($w) $path] |
| 68 | if {$lno >= 0} { |
| 69 | incr lno |
| 70 | } |
| 71 | } |
| 72 | if {$lno >= 1} { |
| 73 | $w tag add in_diff $lno.0 [expr {$lno + 1}].0 |
| 74 | } |
| 75 | |
| 76 | set s $file_states($path) |
| 77 | set m [lindex $s 0] |
| 78 | set is_3way_diff 0 |
| 79 | set diff_active 1 |
| 80 | set current_diff_path $path |
| 81 | set current_diff_side $w |
| 82 | set current_diff_header {} |
Michele Ballabio | c8c4854 | 2007-09-13 15:19:05 +0200 | [diff] [blame] | 83 | ui_status [mc "Loading diff of %s..." [escape_path $path]] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 84 | |
| 85 | # - Git won't give us the diff, there's nothing to compare to! |
| 86 | # |
| 87 | if {$m eq {_O}} { |
| 88 | set max_sz [expr {128 * 1024}] |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 89 | set type unknown |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 90 | if {[catch { |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 91 | set type [file type $path] |
| 92 | switch -- $type { |
| 93 | directory { |
| 94 | set type submodule |
| 95 | set content {} |
| 96 | set sz 0 |
| 97 | } |
| 98 | link { |
Michele Ballabio | 2d19f8e | 2007-09-09 21:04:45 +0200 | [diff] [blame] | 99 | set content [file readlink $path] |
| 100 | set sz [string length $content] |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 101 | } |
| 102 | file { |
Michele Ballabio | 2d19f8e | 2007-09-09 21:04:45 +0200 | [diff] [blame] | 103 | set fd [open $path r] |
| 104 | fconfigure $fd -eofchar {} |
| 105 | set content [read $fd $max_sz] |
| 106 | close $fd |
| 107 | set sz [file size $path] |
| 108 | } |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 109 | default { |
| 110 | error "'$type' not supported" |
| 111 | } |
| 112 | } |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 113 | } err ]} { |
| 114 | set diff_active 0 |
| 115 | unlock_index |
Michele Ballabio | c8c4854 | 2007-09-13 15:19:05 +0200 | [diff] [blame] | 116 | ui_status [mc "Unable to display %s" [escape_path $path]] |
Shawn O. Pearce | 31bb1d1 | 2007-09-14 01:50:09 -0400 | [diff] [blame] | 117 | error_popup [strcat [mc "Error loading file:"] "\n\n$err"] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 118 | return |
| 119 | } |
| 120 | $ui_diff conf -state normal |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 121 | if {$type eq {submodule}} { |
Shawn O. Pearce | 5f51ccd | 2007-09-09 20:56:04 -0400 | [diff] [blame] | 122 | $ui_diff insert end [append \ |
| 123 | "* " \ |
| 124 | [mc "Git Repository (subproject)"] \ |
| 125 | "\n"] d_@ |
Shawn O. Pearce | 3b9dfde | 2007-09-09 20:13:10 -0400 | [diff] [blame] | 126 | } elseif {![catch {set type [exec file $path]}]} { |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 127 | set n [string length $path] |
| 128 | if {[string equal -length $n $path $type]} { |
| 129 | set type [string range $type $n end] |
| 130 | regsub {^:?\s*} $type {} type |
| 131 | } |
| 132 | $ui_diff insert end "* $type\n" d_@ |
| 133 | } |
| 134 | if {[string first "\0" $content] != -1} { |
| 135 | $ui_diff insert end \ |
Michele Ballabio | c8c4854 | 2007-09-13 15:19:05 +0200 | [diff] [blame] | 136 | [mc "* Binary file (not showing content)."] \ |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 137 | d_@ |
| 138 | } else { |
| 139 | if {$sz > $max_sz} { |
| 140 | $ui_diff insert end \ |
| 141 | "* Untracked file is $sz bytes. |
| 142 | * Showing only first $max_sz bytes. |
| 143 | " d_@ |
| 144 | } |
| 145 | $ui_diff insert end $content |
| 146 | if {$sz > $max_sz} { |
| 147 | $ui_diff insert end " |
| 148 | * Untracked file clipped here by [appname]. |
| 149 | * To see the entire file, use an external editor. |
| 150 | " d_@ |
| 151 | } |
| 152 | } |
| 153 | $ui_diff conf -state disabled |
| 154 | set diff_active 0 |
| 155 | unlock_index |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 156 | if {$scroll_pos ne {}} { |
| 157 | update |
| 158 | $ui_diff yview moveto $scroll_pos |
| 159 | } |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 160 | ui_ready |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 161 | return |
| 162 | } |
| 163 | |
Shawn O. Pearce | 0b81261 | 2007-07-09 01:17:09 -0400 | [diff] [blame] | 164 | set cmd [list] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 165 | if {$w eq $ui_index} { |
| 166 | lappend cmd diff-index |
| 167 | lappend cmd --cached |
| 168 | } elseif {$w eq $ui_workdir} { |
| 169 | if {[string index $m 0] eq {U}} { |
| 170 | lappend cmd diff |
| 171 | } else { |
| 172 | lappend cmd diff-files |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | lappend cmd -p |
| 177 | lappend cmd --no-color |
Clemens Buchacher | 55ba8a3 | 2008-08-30 18:45:27 +0200 | [diff] [blame] | 178 | if {$repo_config(gui.diffcontext) >= 1} { |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 179 | lappend cmd "-U$repo_config(gui.diffcontext)" |
| 180 | } |
| 181 | if {$w eq $ui_index} { |
| 182 | lappend cmd [PARENT] |
| 183 | } |
| 184 | lappend cmd -- |
| 185 | lappend cmd $path |
| 186 | |
Shawn O. Pearce | 0b81261 | 2007-07-09 01:17:09 -0400 | [diff] [blame] | 187 | if {[catch {set fd [eval git_read --nice $cmd]} err]} { |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 188 | set diff_active 0 |
| 189 | unlock_index |
Michele Ballabio | c8c4854 | 2007-09-13 15:19:05 +0200 | [diff] [blame] | 190 | ui_status [mc "Unable to display %s" [escape_path $path]] |
Shawn O. Pearce | 31bb1d1 | 2007-09-14 01:50:09 -0400 | [diff] [blame] | 191 | error_popup [strcat [mc "Error loading diff:"] "\n\n$err"] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 192 | return |
| 193 | } |
| 194 | |
Shawn O. Pearce | ca53c3f | 2008-09-04 21:46:56 -0700 | [diff] [blame] | 195 | set ::current_diff_inheader 1 |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 196 | fconfigure $fd \ |
| 197 | -blocking 0 \ |
| 198 | -encoding binary \ |
| 199 | -translation binary |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 200 | fileevent $fd readable [list read_diff $fd $scroll_pos] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 201 | } |
| 202 | |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 203 | proc read_diff {fd scroll_pos} { |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 204 | global ui_diff diff_active |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 205 | global is_3way_diff current_diff_header |
| 206 | |
| 207 | $ui_diff conf -state normal |
| 208 | while {[gets $fd line] >= 0} { |
| 209 | # -- Cleanup uninteresting diff header lines. |
| 210 | # |
Shawn O. Pearce | ca53c3f | 2008-09-04 21:46:56 -0700 | [diff] [blame] | 211 | if {$::current_diff_inheader} { |
| 212 | if { [string match {diff --git *} $line] |
| 213 | || [string match {diff --cc *} $line] |
| 214 | || [string match {diff --combined *} $line] |
| 215 | || [string match {--- *} $line] |
| 216 | || [string match {+++ *} $line]} { |
| 217 | append current_diff_header $line "\n" |
| 218 | continue |
| 219 | } |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 220 | } |
| 221 | if {[string match {index *} $line]} continue |
| 222 | if {$line eq {deleted file mode 120000}} { |
| 223 | set line "deleted symlink" |
| 224 | } |
Shawn O. Pearce | ca53c3f | 2008-09-04 21:46:56 -0700 | [diff] [blame] | 225 | set ::current_diff_inheader 0 |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 226 | |
| 227 | # -- Automatically detect if this is a 3 way diff. |
| 228 | # |
| 229 | if {[string match {@@@ *} $line]} {set is_3way_diff 1} |
| 230 | |
| 231 | if {[string match {mode *} $line] |
| 232 | || [string match {new file *} $line] |
Shawn O. Pearce | a4750dd | 2007-12-14 01:51:22 -0500 | [diff] [blame] | 233 | || [regexp {^(old|new) mode *} $line] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 234 | || [string match {deleted file *} $line] |
Michele Ballabio | 4ed1a19 | 2007-09-09 21:09:07 +0200 | [diff] [blame] | 235 | || [string match {deleted symlink} $line] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 236 | || [string match {Binary files * and * differ} $line] |
| 237 | || $line eq {\ No newline at end of file} |
| 238 | || [regexp {^\* Unmerged path } $line]} { |
| 239 | set tags {} |
| 240 | } elseif {$is_3way_diff} { |
| 241 | set op [string range $line 0 1] |
| 242 | switch -- $op { |
| 243 | { } {set tags {}} |
| 244 | {@@} {set tags d_@} |
| 245 | { +} {set tags d_s+} |
| 246 | { -} {set tags d_s-} |
| 247 | {+ } {set tags d_+s} |
| 248 | {- } {set tags d_-s} |
| 249 | {--} {set tags d_--} |
| 250 | {++} { |
| 251 | if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} { |
| 252 | set line [string replace $line 0 1 { }] |
| 253 | set tags d$op |
| 254 | } else { |
| 255 | set tags d_++ |
| 256 | } |
| 257 | } |
| 258 | default { |
| 259 | puts "error: Unhandled 3 way diff marker: {$op}" |
| 260 | set tags {} |
| 261 | } |
| 262 | } |
| 263 | } else { |
| 264 | set op [string index $line 0] |
| 265 | switch -- $op { |
| 266 | { } {set tags {}} |
| 267 | {@} {set tags d_@} |
| 268 | {-} {set tags d_-} |
| 269 | {+} { |
| 270 | if {[regexp {^\+([<>]{7} |={7})} $line _g op]} { |
| 271 | set line [string replace $line 0 0 { }] |
| 272 | set tags d$op |
| 273 | } else { |
| 274 | set tags d_+ |
| 275 | } |
| 276 | } |
| 277 | default { |
| 278 | puts "error: Unhandled 2 way diff marker: {$op}" |
| 279 | set tags {} |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | $ui_diff insert end $line $tags |
| 284 | if {[string index $line end] eq "\r"} { |
| 285 | $ui_diff tag add d_cr {end - 2c} |
| 286 | } |
| 287 | $ui_diff insert end "\n" $tags |
| 288 | } |
| 289 | $ui_diff conf -state disabled |
| 290 | |
| 291 | if {[eof $fd]} { |
| 292 | close $fd |
| 293 | set diff_active 0 |
| 294 | unlock_index |
Alexander Gavrilov | 25b8fb1 | 2008-07-27 10:35:38 +0400 | [diff] [blame] | 295 | if {$scroll_pos ne {}} { |
| 296 | update |
| 297 | $ui_diff yview moveto $scroll_pos |
| 298 | } |
Shawn O. Pearce | 699d560 | 2007-07-05 23:16:13 -0400 | [diff] [blame] | 299 | ui_ready |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 300 | |
| 301 | if {[$ui_diff index end] eq {2.0}} { |
| 302 | handle_empty_diff |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | proc apply_hunk {x y} { |
| 308 | global current_diff_path current_diff_header current_diff_side |
| 309 | global ui_diff ui_index file_states |
| 310 | |
| 311 | if {$current_diff_path eq {} || $current_diff_header eq {}} return |
| 312 | if {![lock_index apply_hunk]} return |
| 313 | |
Shawn O. Pearce | 0b81261 | 2007-07-09 01:17:09 -0400 | [diff] [blame] | 314 | set apply_cmd {apply --cached --whitespace=nowarn} |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 315 | set mi [lindex $file_states($current_diff_path) 0] |
| 316 | if {$current_diff_side eq $ui_index} { |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 317 | set failed_msg [mc "Failed to unstage selected hunk."] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 318 | lappend apply_cmd --reverse |
| 319 | if {[string index $mi 0] ne {M}} { |
| 320 | unlock_index |
| 321 | return |
| 322 | } |
| 323 | } else { |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 324 | set failed_msg [mc "Failed to stage selected hunk."] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 325 | if {[string index $mi 1] ne {M}} { |
| 326 | unlock_index |
| 327 | return |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | set s_lno [lindex [split [$ui_diff index @$x,$y] .] 0] |
| 332 | set s_lno [$ui_diff search -backwards -regexp ^@@ $s_lno.0 0.0] |
| 333 | if {$s_lno eq {}} { |
| 334 | unlock_index |
| 335 | return |
| 336 | } |
| 337 | |
| 338 | set e_lno [$ui_diff search -forwards -regexp ^@@ "$s_lno + 1 lines" end] |
| 339 | if {$e_lno eq {}} { |
| 340 | set e_lno end |
| 341 | } |
| 342 | |
| 343 | if {[catch { |
Shawn O. Pearce | 0b81261 | 2007-07-09 01:17:09 -0400 | [diff] [blame] | 344 | set p [eval git_write $apply_cmd] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 345 | fconfigure $p -translation binary -encoding binary |
| 346 | puts -nonewline $p $current_diff_header |
| 347 | puts -nonewline $p [$ui_diff get $s_lno $e_lno] |
| 348 | close $p} err]} { |
Christian Stimming | 1ac1795 | 2007-07-21 14:21:34 +0200 | [diff] [blame] | 349 | error_popup [append $failed_msg "\n\n$err"] |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 350 | unlock_index |
| 351 | return |
| 352 | } |
| 353 | |
| 354 | $ui_diff conf -state normal |
| 355 | $ui_diff delete $s_lno $e_lno |
| 356 | $ui_diff conf -state disabled |
| 357 | |
| 358 | if {[$ui_diff get 1.0 end] eq "\n"} { |
| 359 | set o _ |
| 360 | } else { |
| 361 | set o ? |
| 362 | } |
| 363 | |
| 364 | if {$current_diff_side eq $ui_index} { |
| 365 | set mi ${o}M |
| 366 | } elseif {[string index $mi 0] eq {_}} { |
| 367 | set mi M$o |
| 368 | } else { |
| 369 | set mi ?$o |
| 370 | } |
| 371 | unlock_index |
| 372 | display_file $current_diff_path $mi |
| 373 | if {$o eq {_}} { |
| 374 | clear_diff |
Shawn O. Pearce | a41e45e | 2008-01-16 01:29:39 -0500 | [diff] [blame] | 375 | } else { |
| 376 | set current_diff_path $current_diff_path |
Shawn O. Pearce | f522c9b | 2007-05-07 23:35:48 -0400 | [diff] [blame] | 377 | } |
| 378 | } |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 379 | |
| 380 | proc apply_line {x y} { |
| 381 | global current_diff_path current_diff_header current_diff_side |
| 382 | global ui_diff ui_index file_states |
| 383 | |
| 384 | if {$current_diff_path eq {} || $current_diff_header eq {}} return |
| 385 | if {![lock_index apply_hunk]} return |
| 386 | |
| 387 | set apply_cmd {apply --cached --whitespace=nowarn} |
| 388 | set mi [lindex $file_states($current_diff_path) 0] |
| 389 | if {$current_diff_side eq $ui_index} { |
| 390 | set failed_msg [mc "Failed to unstage selected line."] |
| 391 | set to_context {+} |
| 392 | lappend apply_cmd --reverse |
| 393 | if {[string index $mi 0] ne {M}} { |
| 394 | unlock_index |
| 395 | return |
| 396 | } |
| 397 | } else { |
| 398 | set failed_msg [mc "Failed to stage selected line."] |
| 399 | set to_context {-} |
| 400 | if {[string index $mi 1] ne {M}} { |
| 401 | unlock_index |
| 402 | return |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | set the_l [$ui_diff index @$x,$y] |
| 407 | |
| 408 | # operate only on change lines |
| 409 | set c1 [$ui_diff get "$the_l linestart"] |
| 410 | if {$c1 ne {+} && $c1 ne {-}} { |
| 411 | unlock_index |
| 412 | return |
| 413 | } |
| 414 | set sign $c1 |
| 415 | |
| 416 | set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0] |
| 417 | if {$i_l eq {}} { |
| 418 | unlock_index |
| 419 | return |
| 420 | } |
| 421 | # $i_l is now at the beginning of a line |
| 422 | |
| 423 | # pick start line number from hunk header |
| 424 | set hh [$ui_diff get $i_l "$i_l + 1 lines"] |
| 425 | set hh [lindex [split $hh ,] 0] |
| 426 | set hln [lindex [split $hh -] 1] |
| 427 | |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 428 | # There is a special situation to take care of. Consider this hunk: |
| 429 | # |
| 430 | # @@ -10,4 +10,4 @@ |
| 431 | # context before |
| 432 | # -old 1 |
| 433 | # -old 2 |
| 434 | # +new 1 |
| 435 | # +new 2 |
| 436 | # context after |
| 437 | # |
| 438 | # We used to keep the context lines in the order they appear in the |
| 439 | # hunk. But then it is not possible to correctly stage only |
| 440 | # "-old 1" and "+new 1" - it would result in this staged text: |
| 441 | # |
| 442 | # context before |
| 443 | # old 2 |
| 444 | # new 1 |
| 445 | # context after |
| 446 | # |
| 447 | # (By symmetry it is not possible to *un*stage "old 2" and "new 2".) |
| 448 | # |
| 449 | # We resolve the problem by introducing an asymmetry, namely, when |
| 450 | # a "+" line is *staged*, it is moved in front of the context lines |
| 451 | # that are generated from the "-" lines that are immediately before |
| 452 | # the "+" block. That is, we construct this patch: |
| 453 | # |
| 454 | # @@ -10,4 +10,5 @@ |
| 455 | # context before |
| 456 | # +new 1 |
| 457 | # old 1 |
| 458 | # old 2 |
| 459 | # context after |
| 460 | # |
| 461 | # But we do *not* treat "-" lines that are *un*staged in a special |
| 462 | # way. |
| 463 | # |
| 464 | # With this asymmetry it is possible to stage the change |
| 465 | # "old 1" -> "new 1" directly, and to stage the change |
| 466 | # "old 2" -> "new 2" by first staging the entire hunk and |
| 467 | # then unstaging the change "old 1" -> "new 1". |
| 468 | |
| 469 | # This is non-empty if and only if we are _staging_ changes; |
| 470 | # then it accumulates the consecutive "-" lines (after converting |
| 471 | # them to context lines) in order to be moved after the "+" change |
| 472 | # line. |
| 473 | set pre_context {} |
| 474 | |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 475 | set n 0 |
| 476 | set i_l [$ui_diff index "$i_l + 1 lines"] |
| 477 | set patch {} |
| 478 | while {[$ui_diff compare $i_l < "end - 1 chars"] && |
| 479 | [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} { |
| 480 | set next_l [$ui_diff index "$i_l + 1 lines"] |
| 481 | set c1 [$ui_diff get $i_l] |
| 482 | if {[$ui_diff compare $i_l <= $the_l] && |
| 483 | [$ui_diff compare $the_l < $next_l]} { |
| 484 | # the line to stage/unstage |
| 485 | set ln [$ui_diff get $i_l $next_l] |
Johannes Sixt | fa6b5b3 | 2008-07-15 23:11:00 +0200 | [diff] [blame] | 486 | if {$c1 eq {-}} { |
| 487 | set n [expr $n+1] |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 488 | set patch "$patch$pre_context$ln" |
| 489 | } else { |
| 490 | set patch "$patch$ln$pre_context" |
Johannes Sixt | fa6b5b3 | 2008-07-15 23:11:00 +0200 | [diff] [blame] | 491 | } |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 492 | set pre_context {} |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 493 | } elseif {$c1 ne {-} && $c1 ne {+}} { |
| 494 | # context line |
| 495 | set ln [$ui_diff get $i_l $next_l] |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 496 | set patch "$patch$pre_context$ln" |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 497 | set n [expr $n+1] |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 498 | set pre_context {} |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 499 | } elseif {$c1 eq $to_context} { |
| 500 | # turn change line into context line |
| 501 | set ln [$ui_diff get "$i_l + 1 chars" $next_l] |
Johannes Sixt | c7f7457 | 2008-07-17 15:21:51 +0200 | [diff] [blame] | 502 | if {$c1 eq {-}} { |
| 503 | set pre_context "$pre_context $ln" |
| 504 | } else { |
| 505 | set patch "$patch $ln" |
| 506 | } |
Johannes Sixt | 5821988 | 2008-06-27 09:22:01 +0200 | [diff] [blame] | 507 | set n [expr $n+1] |
| 508 | } |
| 509 | set i_l $next_l |
| 510 | } |
| 511 | set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch" |
| 512 | |
| 513 | if {[catch { |
| 514 | set p [eval git_write $apply_cmd] |
| 515 | fconfigure $p -translation binary -encoding binary |
| 516 | puts -nonewline $p $current_diff_header |
| 517 | puts -nonewline $p $patch |
| 518 | close $p} err]} { |
| 519 | error_popup [append $failed_msg "\n\n$err"] |
| 520 | } |
| 521 | |
| 522 | unlock_index |
| 523 | } |