git-gui: Support more merge tools.
[alt-git.git] / lib / index.tcl
blobd011406462b6f2ad3c83aba90612dd1d6628f385
1 # git-gui index (add/remove) support
2 # Copyright (C) 2006, 2007 Shawn Pearce
4 proc _delete_indexlock {} {
5 if {[catch {file delete -- [gitdir index.lock]} err]} {
6 error_popup [strcat [mc "Unable to unlock the index."] "\n\n$err"]
10 proc _close_updateindex {fd after} {
11 fconfigure $fd -blocking 1
12 if {[catch {close $fd} err]} {
13 set w .indexfried
14 toplevel $w
15 wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
16 wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
17 pack [label $w.msg \
18 -justify left \
19 -anchor w \
20 -text [strcat \
21 [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."] \
22 "\n\n$err"] \
23 ] -anchor w
25 frame $w.buttons
26 button $w.buttons.continue \
27 -text [mc "Continue"] \
28 -command [list destroy $w]
29 pack $w.buttons.continue -side right -padx 5
30 button $w.buttons.unlock \
31 -text [mc "Unlock Index"] \
32 -command "destroy $w; _delete_indexlock"
33 pack $w.buttons.unlock -side right
34 pack $w.buttons -side bottom -fill x -pady 10 -padx 10
36 wm protocol $w WM_DELETE_WINDOW update
37 bind $w.buttons.continue <Visibility> "
38 grab $w
39 focus $w.buttons.continue
41 tkwait window $w
43 $::main_status stop
44 unlock_index
45 rescan $after 0
46 return
49 $::main_status stop
50 unlock_index
51 uplevel #0 $after
54 proc update_indexinfo {msg pathList after} {
55 global update_index_cp
57 if {![lock_index update]} return
59 set update_index_cp 0
60 set pathList [lsort $pathList]
61 set totalCnt [llength $pathList]
62 set batch [expr {int($totalCnt * .01) + 1}]
63 if {$batch > 25} {set batch 25}
65 $::main_status start $msg [mc "files"]
66 set fd [git_write update-index -z --index-info]
67 fconfigure $fd \
68 -blocking 0 \
69 -buffering full \
70 -buffersize 512 \
71 -encoding binary \
72 -translation binary
73 fileevent $fd writable [list \
74 write_update_indexinfo \
75 $fd \
76 $pathList \
77 $totalCnt \
78 $batch \
79 $after \
83 proc write_update_indexinfo {fd pathList totalCnt batch after} {
84 global update_index_cp
85 global file_states current_diff_path
87 if {$update_index_cp >= $totalCnt} {
88 _close_updateindex $fd $after
89 return
92 for {set i $batch} \
93 {$update_index_cp < $totalCnt && $i > 0} \
94 {incr i -1} {
95 set path [lindex $pathList $update_index_cp]
96 incr update_index_cp
98 set s $file_states($path)
99 switch -glob -- [lindex $s 0] {
100 A? {set new _O}
101 M? {set new _M}
102 T_ {set new _T}
103 D_ {set new _D}
104 D? {set new _?}
105 ?? {continue}
107 set info [lindex $s 2]
108 if {$info eq {}} continue
110 puts -nonewline $fd "$info\t[encoding convertto $path]\0"
111 display_file $path $new
114 $::main_status update $update_index_cp $totalCnt
117 proc update_index {msg pathList after} {
118 global update_index_cp
120 if {![lock_index update]} return
122 set update_index_cp 0
123 set pathList [lsort $pathList]
124 set totalCnt [llength $pathList]
125 set batch [expr {int($totalCnt * .01) + 1}]
126 if {$batch > 25} {set batch 25}
128 $::main_status start $msg [mc "files"]
129 set fd [git_write update-index --add --remove -z --stdin]
130 fconfigure $fd \
131 -blocking 0 \
132 -buffering full \
133 -buffersize 512 \
134 -encoding binary \
135 -translation binary
136 fileevent $fd writable [list \
137 write_update_index \
138 $fd \
139 $pathList \
140 $totalCnt \
141 $batch \
142 $after \
146 proc write_update_index {fd pathList totalCnt batch after} {
147 global update_index_cp
148 global file_states current_diff_path
150 if {$update_index_cp >= $totalCnt} {
151 _close_updateindex $fd $after
152 return
155 for {set i $batch} \
156 {$update_index_cp < $totalCnt && $i > 0} \
157 {incr i -1} {
158 set path [lindex $pathList $update_index_cp]
159 incr update_index_cp
161 switch -glob -- [lindex $file_states($path) 0] {
162 AD {set new __}
163 ?D {set new D_}
164 _O -
165 AM {set new A_}
166 _T {set new T_}
167 U? {
168 if {[file exists $path]} {
169 set new M_
170 } else {
171 set new D_
174 ?M {set new M_}
175 ?? {continue}
177 puts -nonewline $fd "[encoding convertto $path]\0"
178 display_file $path $new
181 $::main_status update $update_index_cp $totalCnt
184 proc checkout_index {msg pathList after} {
185 global update_index_cp
187 if {![lock_index update]} return
189 set update_index_cp 0
190 set pathList [lsort $pathList]
191 set totalCnt [llength $pathList]
192 set batch [expr {int($totalCnt * .01) + 1}]
193 if {$batch > 25} {set batch 25}
195 $::main_status start $msg [mc "files"]
196 set fd [git_write checkout-index \
197 --index \
198 --quiet \
199 --force \
200 -z \
201 --stdin \
203 fconfigure $fd \
204 -blocking 0 \
205 -buffering full \
206 -buffersize 512 \
207 -encoding binary \
208 -translation binary
209 fileevent $fd writable [list \
210 write_checkout_index \
211 $fd \
212 $pathList \
213 $totalCnt \
214 $batch \
215 $after \
219 proc write_checkout_index {fd pathList totalCnt batch after} {
220 global update_index_cp
221 global file_states current_diff_path
223 if {$update_index_cp >= $totalCnt} {
224 _close_updateindex $fd $after
225 return
228 for {set i $batch} \
229 {$update_index_cp < $totalCnt && $i > 0} \
230 {incr i -1} {
231 set path [lindex $pathList $update_index_cp]
232 incr update_index_cp
233 switch -glob -- [lindex $file_states($path) 0] {
234 U? {continue}
235 ?M -
236 ?T -
237 ?D {
238 puts -nonewline $fd "[encoding convertto $path]\0"
239 display_file $path ?_
244 $::main_status update $update_index_cp $totalCnt
247 proc unstage_helper {txt paths} {
248 global file_states current_diff_path
250 if {![lock_index begin-update]} return
252 set pathList [list]
253 set after {}
254 foreach path $paths {
255 switch -glob -- [lindex $file_states($path) 0] {
256 A? -
257 M? -
258 T_ -
259 D? {
260 lappend pathList $path
261 if {$path eq $current_diff_path} {
262 set after {reshow_diff;}
267 if {$pathList eq {}} {
268 unlock_index
269 } else {
270 update_indexinfo \
271 $txt \
272 $pathList \
273 [concat $after [list ui_ready]]
277 proc do_unstage_selection {} {
278 global current_diff_path selected_paths
280 if {[array size selected_paths] > 0} {
281 unstage_helper \
282 {Unstaging selected files from commit} \
283 [array names selected_paths]
284 } elseif {$current_diff_path ne {}} {
285 unstage_helper \
286 [mc "Unstaging %s from commit" [short_path $current_diff_path]] \
287 [list $current_diff_path]
291 proc add_helper {txt paths} {
292 global file_states current_diff_path
294 if {![lock_index begin-update]} return
296 set pathList [list]
297 set after {}
298 foreach path $paths {
299 switch -glob -- [lindex $file_states($path) 0] {
300 _O -
301 ?M -
302 ?D -
303 ?T -
304 U? {
305 lappend pathList $path
306 if {$path eq $current_diff_path} {
307 set after {reshow_diff;}
312 if {$pathList eq {}} {
313 unlock_index
314 } else {
315 update_index \
316 $txt \
317 $pathList \
318 [concat $after {ui_status [mc "Ready to commit."]}]
322 proc do_add_selection {} {
323 global current_diff_path selected_paths
325 if {[array size selected_paths] > 0} {
326 add_helper \
327 {Adding selected files} \
328 [array names selected_paths]
329 } elseif {$current_diff_path ne {}} {
330 add_helper \
331 [mc "Adding %s" [short_path $current_diff_path]] \
332 [list $current_diff_path]
336 proc do_add_all {} {
337 global file_states
339 set paths [list]
340 foreach path [array names file_states] {
341 switch -glob -- [lindex $file_states($path) 0] {
342 U? {continue}
343 ?M -
344 ?T -
345 ?D {lappend paths $path}
348 add_helper {Adding all changed files} $paths
351 proc revert_helper {txt paths} {
352 global file_states current_diff_path
354 if {![lock_index begin-update]} return
356 set pathList [list]
357 set after {}
358 foreach path $paths {
359 switch -glob -- [lindex $file_states($path) 0] {
360 U? {continue}
361 ?M -
362 ?T -
363 ?D {
364 lappend pathList $path
365 if {$path eq $current_diff_path} {
366 set after {reshow_diff;}
373 # Split question between singular and plural cases, because
374 # such distinction is needed in some languages. Previously, the
375 # code used "Revert changes in" for both, but that can't work
376 # in languages where 'in' must be combined with word from
377 # rest of string (in diffrent way for both cases of course).
379 # FIXME: Unfortunately, even that isn't enough in some languages
380 # as they have quite complex plural-form rules. Unfortunately,
381 # msgcat doesn't seem to support that kind of string translation.
383 set n [llength $pathList]
384 if {$n == 0} {
385 unlock_index
386 return
387 } elseif {$n == 1} {
388 set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
389 } else {
390 set query [mc "Revert changes in these %i files?" $n]
393 set reply [tk_dialog \
394 .confirm_revert \
395 "[appname] ([reponame])" \
396 "$query
398 [mc "Any unstaged changes will be permanently lost by the revert."]" \
399 question \
401 [mc "Do Nothing"] \
402 [mc "Revert Changes"] \
404 if {$reply == 1} {
405 checkout_index \
406 $txt \
407 $pathList \
408 [concat $after [list ui_ready]]
409 } else {
410 unlock_index
414 proc do_revert_selection {} {
415 global current_diff_path selected_paths
417 if {[array size selected_paths] > 0} {
418 revert_helper \
419 [mc "Reverting selected files"] \
420 [array names selected_paths]
421 } elseif {$current_diff_path ne {}} {
422 revert_helper \
423 [mc "Reverting %s" [short_path $current_diff_path]] \
424 [list $current_diff_path]
428 proc do_select_commit_type {} {
429 global commit_type selected_commit_type
431 if {$selected_commit_type eq {new}
432 && [string match amend* $commit_type]} {
433 create_new_commit
434 } elseif {$selected_commit_type eq {amend}
435 && ![string match amend* $commit_type]} {
436 load_last_commit
438 # The amend request was rejected...
440 if {![string match amend* $commit_type]} {
441 set selected_commit_type new