git-gui: allow Ctrl+T to toggle multiple paths
[git/git.git] / lib / index.tcl
CommitLineData
f522c9b5
SP
1# git-gui index (add/remove) support
2# Copyright (C) 2006, 2007 Shawn Pearce
3
d4e890e5
SP
4proc _delete_indexlock {} {
5 if {[catch {file delete -- [gitdir index.lock]} err]} {
6 error_popup [strcat [mc "Unable to unlock the index."] "\n\n$err"]
7 }
8}
9
10proc _close_updateindex {fd after} {
c80d7be5 11 global use_ttk NS
d4e890e5
SP
12 fconfigure $fd -blocking 1
13 if {[catch {close $fd} err]} {
14 set w .indexfried
c80d7be5
PT
15 Dialog $w
16 wm withdraw $w
d4e890e5
SP
17 wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]]
18 wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
ea888f84
PT
19 set s [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."]
20 text $w.msg -yscrollcommand [list $w.vs set] \
21 -width [string length $s] -relief flat \
22 -borderwidth 0 -highlightthickness 0 \
c80d7be5 23 -background [get_bg_color $w]
ea888f84 24 $w.msg tag configure bold -font font_uibold -justify center
c80d7be5 25 ${NS}::scrollbar $w.vs -command [list $w.msg yview]
ea888f84
PT
26 $w.msg insert end $s bold \n\n$err {}
27 $w.msg configure -state disabled
28
c80d7be5 29 ${NS}::button $w.continue \
d4e890e5
SP
30 -text [mc "Continue"] \
31 -command [list destroy $w]
c80d7be5 32 ${NS}::button $w.unlock \
d4e890e5
SP
33 -text [mc "Unlock Index"] \
34 -command "destroy $w; _delete_indexlock"
ea888f84
PT
35 grid $w.msg - $w.vs -sticky news
36 grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2
37 grid columnconfigure $w 0 -weight 1
38 grid rowconfigure $w 0 -weight 1
d4e890e5
SP
39
40 wm protocol $w WM_DELETE_WINDOW update
ea888f84 41 bind $w.continue <Visibility> "
d4e890e5 42 grab $w
ea888f84 43 focus %W
d4e890e5 44 "
c80d7be5 45 wm deiconify $w
d4e890e5
SP
46 tkwait window $w
47
48 $::main_status stop
49 unlock_index
50 rescan $after 0
51 return
52 }
53
f4e9996b 54 $::main_status stop
d4e890e5
SP
55 unlock_index
56 uplevel #0 $after
57}
58
f522c9b5 59proc update_indexinfo {msg pathList after} {
699d5601 60 global update_index_cp
f522c9b5
SP
61
62 if {![lock_index update]} return
63
64 set update_index_cp 0
65 set pathList [lsort $pathList]
66 set totalCnt [llength $pathList]
67 set batch [expr {int($totalCnt * .01) + 1}]
68 if {$batch > 25} {set batch 25}
69
1cad232f 70 $::main_status start $msg [mc "files"]
0b812616 71 set fd [git_write update-index -z --index-info]
f522c9b5
SP
72 fconfigure $fd \
73 -blocking 0 \
74 -buffering full \
75 -buffersize 512 \
76 -encoding binary \
77 -translation binary
78 fileevent $fd writable [list \
79 write_update_indexinfo \
80 $fd \
81 $pathList \
82 $totalCnt \
83 $batch \
f522c9b5
SP
84 $after \
85 ]
86}
87
1cad232f 88proc write_update_indexinfo {fd pathList totalCnt batch after} {
699d5601 89 global update_index_cp
f522c9b5
SP
90 global file_states current_diff_path
91
92 if {$update_index_cp >= $totalCnt} {
d4e890e5 93 _close_updateindex $fd $after
f522c9b5
SP
94 return
95 }
96
97 for {set i $batch} \
98 {$update_index_cp < $totalCnt && $i > 0} \
99 {incr i -1} {
100 set path [lindex $pathList $update_index_cp]
101 incr update_index_cp
102
103 set s $file_states($path)
104 switch -glob -- [lindex $s 0] {
105 A? {set new _O}
7587f4d3
BW
106 MT -
107 TM -
e681cb7d 108 T_ {set new _T}
7587f4d3
BW
109 M? {set new _M}
110 TD -
f522c9b5
SP
111 D_ {set new _D}
112 D? {set new _?}
113 ?? {continue}
114 }
115 set info [lindex $s 2]
116 if {$info eq {}} continue
117
e2039e94 118 puts -nonewline $fd "$info\t[encoding convertto utf-8 $path]\0"
f522c9b5
SP
119 display_file $path $new
120 }
121
1cad232f 122 $::main_status update $update_index_cp $totalCnt
f522c9b5
SP
123}
124
125proc update_index {msg pathList after} {
699d5601 126 global update_index_cp
f522c9b5
SP
127
128 if {![lock_index update]} return
129
130 set update_index_cp 0
131 set pathList [lsort $pathList]
132 set totalCnt [llength $pathList]
133 set batch [expr {int($totalCnt * .01) + 1}]
134 if {$batch > 25} {set batch 25}
135
1cad232f 136 $::main_status start $msg [mc "files"]
0b812616 137 set fd [git_write update-index --add --remove -z --stdin]
f522c9b5
SP
138 fconfigure $fd \
139 -blocking 0 \
140 -buffering full \
141 -buffersize 512 \
142 -encoding binary \
143 -translation binary
144 fileevent $fd writable [list \
145 write_update_index \
146 $fd \
147 $pathList \
148 $totalCnt \
149 $batch \
f522c9b5
SP
150 $after \
151 ]
152}
153
1cad232f 154proc write_update_index {fd pathList totalCnt batch after} {
699d5601 155 global update_index_cp
f522c9b5
SP
156 global file_states current_diff_path
157
158 if {$update_index_cp >= $totalCnt} {
d4e890e5 159 _close_updateindex $fd $after
f522c9b5
SP
160 return
161 }
162
163 for {set i $batch} \
164 {$update_index_cp < $totalCnt && $i > 0} \
165 {incr i -1} {
166 set path [lindex $pathList $update_index_cp]
167 incr update_index_cp
168
169 switch -glob -- [lindex $file_states($path) 0] {
170 AD {set new __}
171 ?D {set new D_}
172 _O -
7587f4d3 173 AT -
f522c9b5 174 AM {set new A_}
7587f4d3
BW
175 TM -
176 MT -
e681cb7d 177 _T {set new T_}
ff515d81 178 _U -
f522c9b5
SP
179 U? {
180 if {[file exists $path]} {
181 set new M_
182 } else {
183 set new D_
184 }
185 }
186 ?M {set new M_}
187 ?? {continue}
188 }
e2039e94 189 puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
f522c9b5
SP
190 display_file $path $new
191 }
192
1cad232f 193 $::main_status update $update_index_cp $totalCnt
f522c9b5
SP
194}
195
196proc checkout_index {msg pathList after} {
699d5601 197 global update_index_cp
f522c9b5
SP
198
199 if {![lock_index update]} return
200
201 set update_index_cp 0
202 set pathList [lsort $pathList]
203 set totalCnt [llength $pathList]
204 set batch [expr {int($totalCnt * .01) + 1}]
205 if {$batch > 25} {set batch 25}
206
1cad232f 207 $::main_status start $msg [mc "files"]
0b812616
SP
208 set fd [git_write checkout-index \
209 --index \
210 --quiet \
211 --force \
212 -z \
213 --stdin \
214 ]
f522c9b5
SP
215 fconfigure $fd \
216 -blocking 0 \
217 -buffering full \
218 -buffersize 512 \
219 -encoding binary \
220 -translation binary
221 fileevent $fd writable [list \
222 write_checkout_index \
223 $fd \
224 $pathList \
225 $totalCnt \
226 $batch \
f522c9b5
SP
227 $after \
228 ]
229}
230
1cad232f 231proc write_checkout_index {fd pathList totalCnt batch after} {
699d5601 232 global update_index_cp
f522c9b5
SP
233 global file_states current_diff_path
234
235 if {$update_index_cp >= $totalCnt} {
d4e890e5 236 _close_updateindex $fd $after
f522c9b5
SP
237 return
238 }
239
240 for {set i $batch} \
241 {$update_index_cp < $totalCnt && $i > 0} \
242 {incr i -1} {
243 set path [lindex $pathList $update_index_cp]
244 incr update_index_cp
245 switch -glob -- [lindex $file_states($path) 0] {
246 U? {continue}
247 ?M -
e681cb7d 248 ?T -
f522c9b5 249 ?D {
e2039e94 250 puts -nonewline $fd "[encoding convertto utf-8 $path]\0"
f522c9b5
SP
251 display_file $path ?_
252 }
253 }
254 }
255
1cad232f 256 $::main_status update $update_index_cp $totalCnt
f522c9b5
SP
257}
258
259proc unstage_helper {txt paths} {
260 global file_states current_diff_path
261
262 if {![lock_index begin-update]} return
263
264 set pathList [list]
265 set after {}
266 foreach path $paths {
267 switch -glob -- [lindex $file_states($path) 0] {
268 A? -
269 M? -
7587f4d3 270 T? -
f522c9b5
SP
271 D? {
272 lappend pathList $path
273 if {$path eq $current_diff_path} {
274 set after {reshow_diff;}
275 }
276 }
277 }
278 }
279 if {$pathList eq {}} {
280 unlock_index
281 } else {
282 update_indexinfo \
283 $txt \
284 $pathList \
699d5601 285 [concat $after [list ui_ready]]
f522c9b5
SP
286 }
287}
288
289proc do_unstage_selection {} {
290 global current_diff_path selected_paths
291
292 if {[array size selected_paths] > 0} {
293 unstage_helper \
124356b6 294 [mc "Unstaging selected files from commit"] \
f522c9b5
SP
295 [array names selected_paths]
296 } elseif {$current_diff_path ne {}} {
297 unstage_helper \
c8c4854b 298 [mc "Unstaging %s from commit" [short_path $current_diff_path]] \
f522c9b5
SP
299 [list $current_diff_path]
300 }
301}
302
303proc add_helper {txt paths} {
304 global file_states current_diff_path
305
306 if {![lock_index begin-update]} return
307
308 set pathList [list]
309 set after {}
310 foreach path $paths {
311 switch -glob -- [lindex $file_states($path) 0] {
0aea2842
AG
312 _U -
313 U? {
314 if {$path eq $current_diff_path} {
315 unlock_index
316 merge_stage_workdir $path
317 return
318 }
319 }
f522c9b5
SP
320 _O -
321 ?M -
322 ?D -
0aea2842 323 ?T {
f522c9b5
SP
324 lappend pathList $path
325 if {$path eq $current_diff_path} {
326 set after {reshow_diff;}
327 }
328 }
329 }
330 }
331 if {$pathList eq {}} {
332 unlock_index
333 } else {
334 update_index \
335 $txt \
336 $pathList \
5e6d7768 337 [concat $after {ui_status [mc "Ready to commit."]}]
f522c9b5
SP
338 }
339}
340
341proc do_add_selection {} {
342 global current_diff_path selected_paths
343
344 if {[array size selected_paths] > 0} {
345 add_helper \
124356b6 346 [mc "Adding selected files"] \
f522c9b5
SP
347 [array names selected_paths]
348 } elseif {$current_diff_path ne {}} {
349 add_helper \
c8c4854b 350 [mc "Adding %s" [short_path $current_diff_path]] \
f522c9b5
SP
351 [list $current_diff_path]
352 }
353}
354
355proc do_add_all {} {
356 global file_states
357
358 set paths [list]
526aa2b2 359 set untracked_paths [list]
f522c9b5
SP
360 foreach path [array names file_states] {
361 switch -glob -- [lindex $file_states($path) 0] {
362 U? {continue}
363 ?M -
e681cb7d 364 ?T -
f522c9b5 365 ?D {lappend paths $path}
526aa2b2 366 ?O {lappend untracked_paths $path}
856c2d75
HV
367 }
368 }
526aa2b2 369 if {[llength $untracked_paths]} {
bb196e26
BW
370 set reply 0
371 switch -- [get_config gui.stageuntracked] {
372 no {
373 set reply 0
374 }
375 yes {
376 set reply 1
377 }
378 ask -
379 default {
99665fc5
PT
380 set reply [ask_popup [mc "Stage %d untracked files?" \
381 [llength $untracked_paths]]]
bb196e26
BW
382 }
383 }
856c2d75 384 if {$reply} {
526aa2b2 385 set paths [concat $paths $untracked_paths]
f522c9b5
SP
386 }
387 }
124356b6 388 add_helper [mc "Adding all changed files"] $paths
f522c9b5
SP
389}
390
391proc revert_helper {txt paths} {
392 global file_states current_diff_path
393
394 if {![lock_index begin-update]} return
395
396 set pathList [list]
397 set after {}
398 foreach path $paths {
399 switch -glob -- [lindex $file_states($path) 0] {
400 U? {continue}
401 ?M -
e681cb7d 402 ?T -
f522c9b5
SP
403 ?D {
404 lappend pathList $path
405 if {$path eq $current_diff_path} {
406 set after {reshow_diff;}
407 }
408 }
409 }
410 }
411
1ac17950
CS
412
413 # Split question between singular and plural cases, because
414 # such distinction is needed in some languages. Previously, the
415 # code used "Revert changes in" for both, but that can't work
416 # in languages where 'in' must be combined with word from
73fd416b 417 # rest of string (in different way for both cases of course).
1ac17950
CS
418 #
419 # FIXME: Unfortunately, even that isn't enough in some languages
420 # as they have quite complex plural-form rules. Unfortunately,
421 # msgcat doesn't seem to support that kind of string translation.
422 #
f522c9b5
SP
423 set n [llength $pathList]
424 if {$n == 0} {
425 unlock_index
426 return
427 } elseif {$n == 1} {
1ac17950 428 set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
f522c9b5 429 } else {
1ac17950 430 set query [mc "Revert changes in these %i files?" $n]
f522c9b5
SP
431 }
432
433 set reply [tk_dialog \
434 .confirm_revert \
435 "[appname] ([reponame])" \
d4544601
CS
436 "$query
437
438[mc "Any unstaged changes will be permanently lost by the revert."]" \
f522c9b5
SP
439 question \
440 1 \
1ac17950
CS
441 [mc "Do Nothing"] \
442 [mc "Revert Changes"] \
f522c9b5
SP
443 ]
444 if {$reply == 1} {
445 checkout_index \
446 $txt \
447 $pathList \
699d5601 448 [concat $after [list ui_ready]]
f522c9b5
SP
449 } else {
450 unlock_index
451 }
452}
453
454proc do_revert_selection {} {
455 global current_diff_path selected_paths
456
457 if {[array size selected_paths] > 0} {
458 revert_helper \
700e5603 459 [mc "Reverting selected files"] \
f522c9b5
SP
460 [array names selected_paths]
461 } elseif {$current_diff_path ne {}} {
462 revert_helper \
700e5603 463 [mc "Reverting %s" [short_path $current_diff_path]] \
f522c9b5
SP
464 [list $current_diff_path]
465 }
466}
467
468proc do_select_commit_type {} {
469 global commit_type selected_commit_type
470
471 if {$selected_commit_type eq {new}
472 && [string match amend* $commit_type]} {
473 create_new_commit
474 } elseif {$selected_commit_type eq {amend}
475 && ![string match amend* $commit_type]} {
476 load_last_commit
477
478 # The amend request was rejected...
479 #
480 if {![string match amend* $commit_type]} {
481 set selected_commit_type new
482 }
483 }
484}