1# git-gui index (add/remove) support
2# Copyright (C) 2006, 2007 Shawn Pearce
3
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} {
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
24
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
35
36 wm protocol $w WM_DELETE_WINDOW update
37 bind $w.buttons.continue <Visibility> "
38 grab $w
39 focus $w.buttons.continue
40 "
41 tkwait window $w
42
43 $::main_status stop
44 unlock_index
45 rescan $after 0
46 return
47 }
48
49 $::main_status stop
50 unlock_index
51 uplevel #0 $after
52}
53
54proc update_indexinfo {msg pathList after} {
55 global update_index_cp
56
57 if {![lock_index update]} return
58
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}
64
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 \
80 ]
81}
82
83proc write_update_indexinfo {fd pathList totalCnt batch after} {
84 global update_index_cp
85 global file_states current_diff_path
86
87 if {$update_index_cp >= $totalCnt} {
88 _close_updateindex $fd $after
89 return
90 }
91
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
97
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}
106 }
107 set info [lindex $s 2]
108 if {$info eq {}} continue
109
110 puts -nonewline $fd "$info\t[encoding convertto $path]\0"
111 display_file $path $new
112 }
113
114 $::main_status update $update_index_cp $totalCnt
115}
116
117proc update_index {msg pathList after} {
118 global update_index_cp
119
120 if {![lock_index update]} return
121
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}
127
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 \
143 ]
144}
145
146proc write_update_index {fd pathList totalCnt batch after} {
147 global update_index_cp
148 global file_states current_diff_path
149
150 if {$update_index_cp >= $totalCnt} {
151 _close_updateindex $fd $after
152 return
153 }
154
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
160
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 U? {
169 if {[file exists $path]} {
170 set new M_
171 } else {
172 set new D_
173 }
174 }
175 ?M {set new M_}
176 ?? {continue}
177 }
178 puts -nonewline $fd "[encoding convertto $path]\0"
179 display_file $path $new
180 }
181
182 $::main_status update $update_index_cp $totalCnt
183}
184
185proc checkout_index {msg pathList after} {
186 global update_index_cp
187
188 if {![lock_index update]} return
189
190 set update_index_cp 0
191 set pathList [lsort $pathList]
192 set totalCnt [llength $pathList]
193 set batch [expr {int($totalCnt * .01) + 1}]
194 if {$batch > 25} {set batch 25}
195
196 $::main_status start $msg [mc "files"]
197 set fd [git_write checkout-index \
198 --index \
199 --quiet \
200 --force \
201 -z \
202 --stdin \
203 ]
204 fconfigure $fd \
205 -blocking 0 \
206 -buffering full \
207 -buffersize 512 \
208 -encoding binary \
209 -translation binary
210 fileevent $fd writable [list \
211 write_checkout_index \
212 $fd \
213 $pathList \
214 $totalCnt \
215 $batch \
216 $after \
217 ]
218}
219
220proc write_checkout_index {fd pathList totalCnt batch after} {
221 global update_index_cp
222 global file_states current_diff_path
223
224 if {$update_index_cp >= $totalCnt} {
225 _close_updateindex $fd $after
226 return
227 }
228
229 for {set i $batch} \
230 {$update_index_cp < $totalCnt && $i > 0} \
231 {incr i -1} {
232 set path [lindex $pathList $update_index_cp]
233 incr update_index_cp
234 switch -glob -- [lindex $file_states($path) 0] {
235 U? {continue}
236 ?M -
237 ?T -
238 ?D {
239 puts -nonewline $fd "[encoding convertto $path]\0"
240 display_file $path ?_
241 }
242 }
243 }
244
245 $::main_status update $update_index_cp $totalCnt
246}
247
248proc unstage_helper {txt paths} {
249 global file_states current_diff_path
250
251 if {![lock_index begin-update]} return
252
253 set pathList [list]
254 set after {}
255 foreach path $paths {
256 switch -glob -- [lindex $file_states($path) 0] {
257 A? -
258 M? -
259 T_ -
260 D? {
261 lappend pathList $path
262 if {$path eq $current_diff_path} {
263 set after {reshow_diff;}
264 }
265 }
266 }
267 }
268 if {$pathList eq {}} {
269 unlock_index
270 } else {
271 update_indexinfo \
272 $txt \
273 $pathList \
274 [concat $after [list ui_ready]]
275 }
276}
277
278proc do_unstage_selection {} {
279 global current_diff_path selected_paths
280
281 if {[array size selected_paths] > 0} {
282 unstage_helper \
283 {Unstaging selected files from commit} \
284 [array names selected_paths]
285 } elseif {$current_diff_path ne {}} {
286 unstage_helper \
287 [mc "Unstaging %s from commit" [short_path $current_diff_path]] \
288 [list $current_diff_path]
289 }
290}
291
292proc add_helper {txt paths} {
293 global file_states current_diff_path
294
295 if {![lock_index begin-update]} return
296
297 set pathList [list]
298 set after {}
299 foreach path $paths {
300 switch -glob -- [lindex $file_states($path) 0] {
301 _O -
302 ?M -
303 ?D -
304 ?T -
305 U? {
306 lappend pathList $path
307 if {$path eq $current_diff_path} {
308 set after {reshow_diff;}
309 }
310 }
311 }
312 }
313 if {$pathList eq {}} {
314 unlock_index
315 } else {
316 update_index \
317 $txt \
318 $pathList \
319 [concat $after {ui_status [mc "Ready to commit."]}]
320 }
321}
322
323proc do_add_selection {} {
324 global current_diff_path selected_paths
325
326 if {[array size selected_paths] > 0} {
327 add_helper \
328 {Adding selected files} \
329 [array names selected_paths]
330 } elseif {$current_diff_path ne {}} {
331 add_helper \
332 [mc "Adding %s" [short_path $current_diff_path]] \
333 [list $current_diff_path]
334 }
335}
336
337proc do_add_all {} {
338 global file_states
339
340 set paths [list]
341 foreach path [array names file_states] {
342 switch -glob -- [lindex $file_states($path) 0] {
343 U? {continue}
344 ?M -
345 ?T -
346 ?D {lappend paths $path}
347 }
348 }
349 add_helper {Adding all changed files} $paths
350}
351
352proc revert_helper {txt paths} {
353 global file_states current_diff_path
354
355 if {![lock_index begin-update]} return
356
357 set pathList [list]
358 set after {}
359 foreach path $paths {
360 switch -glob -- [lindex $file_states($path) 0] {
361 U? {continue}
362 ?M -
363 ?T -
364 ?D {
365 lappend pathList $path
366 if {$path eq $current_diff_path} {
367 set after {reshow_diff;}
368 }
369 }
370 }
371 }
372
373
374 # Split question between singular and plural cases, because
375 # such distinction is needed in some languages. Previously, the
376 # code used "Revert changes in" for both, but that can't work
377 # in languages where 'in' must be combined with word from
378 # rest of string (in diffrent way for both cases of course).
379 #
380 # FIXME: Unfortunately, even that isn't enough in some languages
381 # as they have quite complex plural-form rules. Unfortunately,
382 # msgcat doesn't seem to support that kind of string translation.
383 #
384 set n [llength $pathList]
385 if {$n == 0} {
386 unlock_index
387 return
388 } elseif {$n == 1} {
389 set query [mc "Revert changes in file %s?" [short_path [lindex $pathList]]]
390 } else {
391 set query [mc "Revert changes in these %i files?" $n]
392 }
393
394 set reply [tk_dialog \
395 .confirm_revert \
396 "[appname] ([reponame])" \
397 "$query
398
399[mc "Any unstaged changes will be permanently lost by the revert."]" \
400 question \
401 1 \
402 [mc "Do Nothing"] \
403 [mc "Revert Changes"] \
404 ]
405 if {$reply == 1} {
406 checkout_index \
407 $txt \
408 $pathList \
409 [concat $after [list ui_ready]]
410 } else {
411 unlock_index
412 }
413}
414
415proc do_revert_selection {} {
416 global current_diff_path selected_paths
417
418 if {[array size selected_paths] > 0} {
419 revert_helper \
420 [mc "Reverting selected files"] \
421 [array names selected_paths]
422 } elseif {$current_diff_path ne {}} {
423 revert_helper \
424 [mc "Reverting %s" [short_path $current_diff_path]] \
425 [list $current_diff_path]
426 }
427}
428
429proc do_select_commit_type {} {
430 global commit_type selected_commit_type
431
432 if {$selected_commit_type eq {new}
433 && [string match amend* $commit_type]} {
434 create_new_commit
435 } elseif {$selected_commit_type eq {amend}
436 && ![string match amend* $commit_type]} {
437 load_last_commit
438
439 # The amend request was rejected...
440 #
441 if {![string match amend* $commit_type]} {
442 set selected_commit_type new
443 }
444 }
445}