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