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