]> git.ipfire.org Git - thirdparty/git.git/blame - git-gui
git-gui: Describe deleted symlinks in a more friendly way.
[thirdparty/git.git] / git-gui
CommitLineData
cb07fc2a
SP
1# Tcl ignores the next line -*- tcl -*- \
2exec wish "$0" -- "$@"
3
4# Copyright (C) 2006 Shawn Pearce, Paul Mackerras. All rights reserved.
5# This program is free software; it may be used, copied, modified
6# and distributed under the terms of the GNU General Public Licence,
7# either version 2, or (at your option) any later version.
8
da5239dc
SP
9set appname [lindex [file split $argv0] end]
10set gitdir {}
11
2d19516d
SP
12######################################################################
13##
14## config
15
51f4d16b
SP
16proc is_many_config {name} {
17 switch -glob -- $name {
18 remote.*.fetch -
19 remote.*.push
20 {return 1}
21 *
22 {return 0}
23 }
24}
2d19516d 25
6bbd1cb9 26proc load_config {include_global} {
51f4d16b
SP
27 global repo_config global_config default_config
28
29 array unset global_config
6bbd1cb9
SP
30 if {$include_global} {
31 catch {
32 set fd_rc [open "| git repo-config --global --list" r]
33 while {[gets $fd_rc line] >= 0} {
34 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
35 if {[is_many_config $name]} {
36 lappend global_config($name) $value
37 } else {
38 set global_config($name) $value
39 }
51f4d16b
SP
40 }
41 }
6bbd1cb9 42 close $fd_rc
51f4d16b 43 }
51f4d16b 44 }
6bbd1cb9
SP
45
46 array unset repo_config
2d19516d
SP
47 catch {
48 set fd_rc [open "| git repo-config --list" r]
49 while {[gets $fd_rc line] >= 0} {
50 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
51f4d16b
SP
51 if {[is_many_config $name]} {
52 lappend repo_config($name) $value
53 } else {
54 set repo_config($name) $value
55 }
2d19516d
SP
56 }
57 }
58 close $fd_rc
59 }
60
51f4d16b
SP
61 foreach name [array names default_config] {
62 if {[catch {set v $global_config($name)}]} {
63 set global_config($name) $default_config($name)
64 }
65 if {[catch {set v $repo_config($name)}]} {
66 set repo_config($name) $default_config($name)
67 }
2d19516d
SP
68 }
69}
70
51f4d16b 71proc save_config {} {
92148d80
SP
72 global default_config font_descs
73 global repo_config global_config
51f4d16b 74 global repo_config_new global_config_new
2d19516d 75
92148d80
SP
76 foreach option $font_descs {
77 set name [lindex $option 0]
78 set font [lindex $option 1]
79 font configure $font \
80 -family $global_config_new(gui.$font^^family) \
81 -size $global_config_new(gui.$font^^size)
82 font configure ${font}bold \
83 -family $global_config_new(gui.$font^^family) \
84 -size $global_config_new(gui.$font^^size)
85 set global_config_new(gui.$name) [font configure $font]
86 unset global_config_new(gui.$font^^family)
87 unset global_config_new(gui.$font^^size)
88 }
89
90 foreach name [array names default_config] {
51f4d16b 91 set value $global_config_new($name)
043f7011
SP
92 if {$value ne $global_config($name)} {
93 if {$value eq $default_config($name)} {
51f4d16b
SP
94 catch {exec git repo-config --global --unset $name}
95 } else {
7b64d0b7
SP
96 regsub -all "\[{}\]" $value {"} value
97 exec git repo-config --global $name $value
51f4d16b
SP
98 }
99 set global_config($name) $value
043f7011 100 if {$value eq $repo_config($name)} {
51f4d16b
SP
101 catch {exec git repo-config --unset $name}
102 set repo_config($name) $value
103 }
104 }
2d19516d
SP
105 }
106
92148d80 107 foreach name [array names default_config] {
51f4d16b 108 set value $repo_config_new($name)
043f7011
SP
109 if {$value ne $repo_config($name)} {
110 if {$value eq $global_config($name)} {
51f4d16b
SP
111 catch {exec git repo-config --unset $name}
112 } else {
7b64d0b7
SP
113 regsub -all "\[{}\]" $value {"} value
114 exec git repo-config $name $value
51f4d16b
SP
115 }
116 set repo_config($name) $value
117 }
2d19516d
SP
118 }
119}
120
da5239dc
SP
121proc error_popup {msg} {
122 global gitdir appname
123
124 set title $appname
043f7011 125 if {$gitdir ne {}} {
da5239dc
SP
126 append title { (}
127 append title [lindex \
128 [file split [file normalize [file dirname $gitdir]]] \
129 end]
130 append title {)}
131 }
cbbaa28b 132 set cmd [list tk_messageBox \
da5239dc
SP
133 -icon error \
134 -type ok \
135 -title "$title: error" \
cbbaa28b
SP
136 -message $msg]
137 if {[winfo ismapped .]} {
138 lappend cmd -parent .
139 }
140 eval $cmd
da5239dc
SP
141}
142
16403d0b
SP
143proc info_popup {msg} {
144 global gitdir appname
145
146 set title $appname
043f7011 147 if {$gitdir ne {}} {
16403d0b
SP
148 append title { (}
149 append title [lindex \
150 [file split [file normalize [file dirname $gitdir]]] \
151 end]
152 append title {)}
153 }
154 tk_messageBox \
155 -parent . \
156 -icon error \
157 -type ok \
158 -title $title \
159 -message $msg
160}
161
2d19516d
SP
162######################################################################
163##
164## repository setup
165
fbee8500
SP
166if { [catch {set gitdir $env(GIT_DIR)}]
167 && [catch {set gitdir [exec git rev-parse --git-dir]} err]} {
44be340e
SP
168 catch {wm withdraw .}
169 error_popup "Cannot find the git directory:\n\n$err"
2d19516d
SP
170 exit 1
171}
dbccbbda
SP
172if {![file isdirectory $gitdir]} {
173 catch {wm withdraw .}
174 error_popup "Git directory not found:\n\n$gitdir"
175 exit 1
176}
177if {[lindex [file split $gitdir] end] ne {.git}} {
178 catch {wm withdraw .}
179 error_popup "Cannot use funny .git directory:\n\n$gitdir"
180 exit 1
181}
fbee8500
SP
182if {[catch {cd [file dirname $gitdir]} err]} {
183 catch {wm withdraw .}
184 error_popup "No working directory [file dirname $gitdir]:\n\n$err"
185 exit 1
2d19516d 186}
2d19516d 187
4ccdab02 188set single_commit 0
043f7011 189if {$appname eq {git-citool}} {
2d19516d
SP
190 set single_commit 1
191}
192
cb07fc2a
SP
193######################################################################
194##
e210e674 195## task management
cb07fc2a 196
8f52548a 197set rescan_active 0
131f503b 198set diff_active 0
24263b77 199set last_clicked {}
131f503b 200
e210e674
SP
201set disable_on_lock [list]
202set index_lock_type none
203
204proc lock_index {type} {
205 global index_lock_type disable_on_lock
131f503b 206
043f7011 207 if {$index_lock_type eq {none}} {
e210e674
SP
208 set index_lock_type $type
209 foreach w $disable_on_lock {
210 uplevel #0 $w disabled
211 }
212 return 1
53716a7b 213 } elseif {$index_lock_type eq "begin-$type"} {
e210e674 214 set index_lock_type $type
131f503b
SP
215 return 1
216 }
217 return 0
218}
cb07fc2a 219
e210e674
SP
220proc unlock_index {} {
221 global index_lock_type disable_on_lock
222
223 set index_lock_type none
224 foreach w $disable_on_lock {
225 uplevel #0 $w normal
226 }
227}
228
229######################################################################
230##
231## status
232
ec6b424a
SP
233proc repository_state {hdvar ctvar} {
234 global gitdir
235 upvar $hdvar hd $ctvar ct
236
237 if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
4539eacd 238 set hd {}
ec6b424a
SP
239 set ct initial
240 } elseif {[file exists [file join $gitdir MERGE_HEAD]]} {
241 set ct merge
242 } else {
243 set ct normal
244 }
245}
246
4539eacd
SP
247proc PARENT {} {
248 global PARENT empty_tree
249
250 if {$PARENT ne {}} {
251 return $PARENT
252 }
253 if {$empty_tree eq {}} {
254 set empty_tree [exec git mktree << {}]
255 }
256 return $empty_tree
257}
258
8f52548a 259proc rescan {after} {
e57ca85e 260 global HEAD PARENT commit_type
131f503b 261 global ui_index ui_other ui_status_value ui_comm
8f52548a 262 global rescan_active file_states
51f4d16b 263 global repo_config
cb07fc2a 264
8f52548a 265 if {$rescan_active > 0 || ![lock_index read]} return
cb07fc2a 266
e57ca85e 267 repository_state new_HEAD new_type
4539eacd 268 if {[string match amend* $commit_type]
043f7011
SP
269 && $new_type eq {normal}
270 && $new_HEAD eq $HEAD} {
e57ca85e
SP
271 } else {
272 set HEAD $new_HEAD
273 set PARENT $new_HEAD
274 set commit_type $new_type
275 }
276
cb07fc2a 277 array unset file_states
cb07fc2a 278
131f503b 279 if {![$ui_comm edit modified]
043f7011 280 || [string trim [$ui_comm get 0.0 end]] eq {}} {
131f503b
SP
281 if {[load_message GITGUI_MSG]} {
282 } elseif {[load_message MERGE_MSG]} {
283 } elseif {[load_message SQUASH_MSG]} {
284 }
285 $ui_comm edit modified false
b2c6fcf1 286 $ui_comm edit reset
131f503b
SP
287 }
288
043f7011 289 if {$repo_config(gui.trustmtime) eq {true}} {
8f52548a 290 rescan_stage2 {} $after
e534f3a8 291 } else {
8f52548a 292 set rescan_active 1
e534f3a8 293 set ui_status_value {Refreshing file status...}
16403d0b
SP
294 set cmd [list git update-index]
295 lappend cmd -q
296 lappend cmd --unmerged
297 lappend cmd --ignore-missing
298 lappend cmd --refresh
299 set fd_rf [open "| $cmd" r]
e534f3a8 300 fconfigure $fd_rf -blocking 0 -translation binary
390adaea 301 fileevent $fd_rf readable \
8f52548a 302 [list rescan_stage2 $fd_rf $after]
e534f3a8 303 }
131f503b
SP
304}
305
8f52548a 306proc rescan_stage2 {fd after} {
4539eacd
SP
307 global gitdir ui_status_value
308 global rescan_active buf_rdi buf_rdf buf_rlo
131f503b 309
043f7011 310 if {$fd ne {}} {
e534f3a8
SP
311 read $fd
312 if {![eof $fd]} return
313 close $fd
314 }
131f503b 315
cb07fc2a
SP
316 set ls_others [list | git ls-files --others -z \
317 --exclude-per-directory=.gitignore]
318 set info_exclude [file join $gitdir info exclude]
319 if {[file readable $info_exclude]} {
320 lappend ls_others "--exclude-from=$info_exclude"
321 }
322
868c8752
SP
323 set buf_rdi {}
324 set buf_rdf {}
325 set buf_rlo {}
326
8f52548a 327 set rescan_active 3
131f503b 328 set ui_status_value {Scanning for modified files ...}
4539eacd 329 set fd_di [open "| git diff-index --cached -z [PARENT]" r]
cb07fc2a
SP
330 set fd_df [open "| git diff-files -z" r]
331 set fd_lo [open $ls_others r]
cb07fc2a
SP
332
333 fconfigure $fd_di -blocking 0 -translation binary
334 fconfigure $fd_df -blocking 0 -translation binary
335 fconfigure $fd_lo -blocking 0 -translation binary
8f52548a
SP
336 fileevent $fd_di readable [list read_diff_index $fd_di $after]
337 fileevent $fd_df readable [list read_diff_files $fd_df $after]
338 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
cb07fc2a
SP
339}
340
131f503b
SP
341proc load_message {file} {
342 global gitdir ui_comm
343
344 set f [file join $gitdir $file]
e57ca85e 345 if {[file isfile $f]} {
131f503b
SP
346 if {[catch {set fd [open $f r]}]} {
347 return 0
348 }
e57ca85e 349 set content [string trim [read $fd]]
131f503b
SP
350 close $fd
351 $ui_comm delete 0.0 end
352 $ui_comm insert end $content
353 return 1
354 }
355 return 0
356}
357
8f52548a 358proc read_diff_index {fd after} {
cb07fc2a
SP
359 global buf_rdi
360
361 append buf_rdi [read $fd]
868c8752
SP
362 set c 0
363 set n [string length $buf_rdi]
364 while {$c < $n} {
365 set z1 [string first "\0" $buf_rdi $c]
366 if {$z1 == -1} break
367 incr z1
368 set z2 [string first "\0" $buf_rdi $z1]
369 if {$z2 == -1} break
370
868c8752 371 incr c
86291555 372 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
1461c5f3
SP
373 merge_state \
374 [string range $buf_rdi $z1 [expr {$z2 - 1}]] \
86291555
SP
375 [lindex $i 4]? \
376 [list [lindex $i 0] [lindex $i 2]] \
1461c5f3
SP
377 [list]
378 set c $z2
86291555 379 incr c
cb07fc2a 380 }
868c8752
SP
381 if {$c < $n} {
382 set buf_rdi [string range $buf_rdi $c end]
383 } else {
384 set buf_rdi {}
385 }
386
8f52548a 387 rescan_done $fd buf_rdi $after
cb07fc2a
SP
388}
389
8f52548a 390proc read_diff_files {fd after} {
cb07fc2a
SP
391 global buf_rdf
392
393 append buf_rdf [read $fd]
868c8752
SP
394 set c 0
395 set n [string length $buf_rdf]
396 while {$c < $n} {
397 set z1 [string first "\0" $buf_rdf $c]
398 if {$z1 == -1} break
399 incr z1
400 set z2 [string first "\0" $buf_rdf $z1]
401 if {$z2 == -1} break
402
868c8752 403 incr c
86291555 404 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
1461c5f3
SP
405 merge_state \
406 [string range $buf_rdf $z1 [expr {$z2 - 1}]] \
86291555 407 ?[lindex $i 4] \
1461c5f3 408 [list] \
86291555 409 [list [lindex $i 0] [lindex $i 2]]
1461c5f3 410 set c $z2
86291555 411 incr c
868c8752
SP
412 }
413 if {$c < $n} {
414 set buf_rdf [string range $buf_rdf $c end]
415 } else {
416 set buf_rdf {}
cb07fc2a 417 }
868c8752 418
8f52548a 419 rescan_done $fd buf_rdf $after
cb07fc2a
SP
420}
421
8f52548a 422proc read_ls_others {fd after} {
cb07fc2a
SP
423 global buf_rlo
424
425 append buf_rlo [read $fd]
426 set pck [split $buf_rlo "\0"]
427 set buf_rlo [lindex $pck end]
428 foreach p [lrange $pck 0 end-1] {
1461c5f3 429 merge_state $p ?O
cb07fc2a 430 }
8f52548a 431 rescan_done $fd buf_rlo $after
cb07fc2a
SP
432}
433
8f52548a
SP
434proc rescan_done {fd buf after} {
435 global rescan_active
f7f8d322 436 global file_states repo_config
7f1df79b 437 upvar $buf to_clear
cb07fc2a 438
f7f8d322
SP
439 if {![eof $fd]} return
440 set to_clear {}
441 close $fd
8f52548a 442 if {[incr rescan_active -1] > 0} return
93f654df 443
24263b77 444 prune_selection
f7f8d322
SP
445 unlock_index
446 display_all_files
447
448 if {$repo_config(gui.partialinclude) ne {true}} {
bbe3b3b9 449 set pathList [list]
f7f8d322
SP
450 foreach path [array names file_states] {
451 switch -- [lindex $file_states($path) 0] {
452 AM -
453 MM {lappend pathList $path}
454 }
455 }
456 if {$pathList ne {}} {
04b39382
SP
457 update_index \
458 "Updating included files" \
459 $pathList \
460 [concat {reshow_diff;} $after]
f7f8d322 461 return
cb07fc2a
SP
462 }
463 }
f7f8d322
SP
464
465 reshow_diff
8f52548a 466 uplevel #0 $after
cb07fc2a
SP
467}
468
24263b77
SP
469proc prune_selection {} {
470 global file_states selected_paths
471
472 foreach path [array names selected_paths] {
473 if {[catch {set still_here $file_states($path)}]} {
474 unset selected_paths($path)
475 }
476 }
477}
478
cb07fc2a
SP
479######################################################################
480##
481## diff
482
cb07fc2a 483proc clear_diff {} {
e8ab6446 484 global ui_diff current_diff ui_index ui_other
cb07fc2a
SP
485
486 $ui_diff conf -state normal
487 $ui_diff delete 0.0 end
488 $ui_diff conf -state disabled
03e4ec53 489
e8ab6446 490 set current_diff {}
03e4ec53
SP
491
492 $ui_index tag remove in_diff 0.0 end
493 $ui_other tag remove in_diff 0.0 end
cb07fc2a
SP
494}
495
7f1df79b 496proc reshow_diff {} {
e8ab6446 497 global current_diff ui_status_value file_states
7f1df79b 498
e8ab6446
SP
499 if {$current_diff eq {}
500 || [catch {set s $file_states($current_diff)}]} {
7f1df79b 501 clear_diff
73ad179b 502 } else {
e8ab6446 503 show_diff $current_diff
7f1df79b
SP
504 }
505}
506
16403d0b 507proc handle_empty_diff {} {
e8ab6446 508 global current_diff file_states file_lists
16403d0b 509
e8ab6446 510 set path $current_diff
16403d0b 511 set s $file_states($path)
043f7011 512 if {[lindex $s 0] ne {_M}} return
16403d0b
SP
513
514 info_popup "No differences detected.
515
516[short_path $path] has no changes.
517
a37eee44
SP
518The modification date of this file was updated
519by another application and you currently have
520the Trust File Modification Timestamps option
521enabled, so Git did not automatically detect
522that there are no content differences in this
523file.
524
525This file will now be removed from the modified
526files list, to prevent possible confusion.
16403d0b
SP
527"
528 if {[catch {exec git update-index -- $path} err]} {
529 error_popup "Failed to refresh index:\n\n$err"
530 }
531
532 clear_diff
533 set old_w [mapcol [lindex $file_states($path) 0] $path]
534 set lno [lsearch -sorted $file_lists($old_w) $path]
535 if {$lno >= 0} {
536 set file_lists($old_w) \
537 [lreplace $file_lists($old_w) $lno $lno]
538 incr lno
539 $old_w conf -state normal
24263b77 540 $old_w delete $lno.0 [expr {$lno + 1}].0
16403d0b
SP
541 $old_w conf -state disabled
542 }
543}
544
03e4ec53
SP
545proc show_diff {path {w {}} {lno {}}} {
546 global file_states file_lists
4539eacd 547 global diff_3way diff_active repo_config
e8ab6446 548 global ui_diff current_diff ui_status_value
cb07fc2a 549
e210e674 550 if {$diff_active || ![lock_index read]} return
cb07fc2a
SP
551
552 clear_diff
043f7011 553 if {$w eq {} || $lno == {}} {
03e4ec53
SP
554 foreach w [array names file_lists] {
555 set lno [lsearch -sorted $file_lists($w) $path]
556 if {$lno >= 0} {
557 incr lno
558 break
559 }
560 }
561 }
043f7011 562 if {$w ne {} && $lno >= 1} {
24263b77 563 $w tag add in_diff $lno.0 [expr {$lno + 1}].0
03e4ec53
SP
564 }
565
cb07fc2a
SP
566 set s $file_states($path)
567 set m [lindex $s 0]
568 set diff_3way 0
569 set diff_active 1
e8ab6446 570 set current_diff $path
68e009de 571 set ui_status_value "Loading diff of [escape_path $path]..."
cb07fc2a 572
fd2656fd
SP
573 set cmd [list | git diff-index]
574 lappend cmd --no-color
358d8de8
SP
575 if {$repo_config(gui.diffcontext) > 0} {
576 lappend cmd "-U$repo_config(gui.diffcontext)"
577 }
fd2656fd
SP
578 lappend cmd -p
579
cb07fc2a 580 switch $m {
cb07fc2a 581 MM {
fd2656fd 582 lappend cmd -c
cb07fc2a
SP
583 }
584 _O {
585 if {[catch {
586 set fd [open $path r]
587 set content [read $fd]
588 close $fd
589 } err ]} {
131f503b 590 set diff_active 0
e210e674 591 unlock_index
68e009de 592 set ui_status_value "Unable to display [escape_path $path]"
44be340e 593 error_popup "Error loading file:\n\n$err"
cb07fc2a
SP
594 return
595 }
596 $ui_diff conf -state normal
597 $ui_diff insert end $content
598 $ui_diff conf -state disabled
bd1e2b40
SP
599 set diff_active 0
600 unlock_index
601 set ui_status_value {Ready.}
cb07fc2a
SP
602 return
603 }
604 }
605
4539eacd 606 lappend cmd [PARENT]
fd2656fd
SP
607 lappend cmd --
608 lappend cmd $path
609
cb07fc2a 610 if {[catch {set fd [open $cmd r]} err]} {
131f503b 611 set diff_active 0
e210e674 612 unlock_index
68e009de 613 set ui_status_value "Unable to display [escape_path $path]"
44be340e 614 error_popup "Error loading diff:\n\n$err"
cb07fc2a
SP
615 return
616 }
617
6f6eed28 618 fconfigure $fd -blocking 0 -translation auto
cb07fc2a
SP
619 fileevent $fd readable [list read_diff $fd]
620}
621
622proc read_diff {fd} {
623 global ui_diff ui_status_value diff_3way diff_active
51f4d16b 624 global repo_config
cb07fc2a
SP
625
626 while {[gets $fd line] >= 0} {
6f6eed28
SP
627 if {[string match {diff --git *} $line]} continue
628 if {[string match {diff --combined *} $line]} continue
629 if {[string match {--- *} $line]} continue
630 if {[string match {+++ *} $line]} continue
0d5709cf
SP
631 if {$line eq {deleted file mode 120000}} {
632 set line "deleted symlink"
633 }
cb07fc2a
SP
634 if {[string match index* $line]} {
635 if {[string first , $line] >= 0} {
636 set diff_3way 1
637 }
638 }
639
640 $ui_diff conf -state normal
641 if {!$diff_3way} {
642 set x [string index $line 0]
643 switch -- $x {
644 "@" {set tags da}
645 "+" {set tags dp}
646 "-" {set tags dm}
647 default {set tags {}}
648 }
649 } else {
650 set x [string range $line 0 1]
651 switch -- $x {
652 default {set tags {}}
653 "@@" {set tags da}
654 "++" {set tags dp; set x " +"}
655 " +" {set tags {di bold}; set x "++"}
656 "+ " {set tags dni; set x "-+"}
657 "--" {set tags dm; set x " -"}
658 " -" {set tags {dm bold}; set x "--"}
659 "- " {set tags di; set x "+-"}
660 default {set tags {}}
661 }
662 set line [string replace $line 0 1 $x]
663 }
664 $ui_diff insert end $line $tags
665 $ui_diff insert end "\n"
666 $ui_diff conf -state disabled
667 }
668
669 if {[eof $fd]} {
670 close $fd
671 set diff_active 0
e210e674 672 unlock_index
cb07fc2a 673 set ui_status_value {Ready.}
16403d0b 674
043f7011
SP
675 if {$repo_config(gui.trustmtime) eq {true}
676 && [$ui_diff index end] eq {2.0}} {
16403d0b
SP
677 handle_empty_diff
678 }
cb07fc2a
SP
679 }
680}
681
ec6b424a
SP
682######################################################################
683##
684## commit
685
e57ca85e
SP
686proc load_last_commit {} {
687 global HEAD PARENT commit_type ui_comm
688
4539eacd 689 if {[string match amend* $commit_type]} return
043f7011 690 if {$commit_type ne {normal}} {
e57ca85e
SP
691 error_popup "Can't amend a $commit_type commit."
692 return
693 }
694
695 set msg {}
696 set parent {}
697 set parent_count 0
698 if {[catch {
699 set fd [open "| git cat-file commit $HEAD" r]
700 while {[gets $fd line] > 0} {
701 if {[string match {parent *} $line]} {
702 set parent [string range $line 7 end]
703 incr parent_count
704 }
705 }
706 set msg [string trim [read $fd]]
707 close $fd
708 } err]} {
44be340e 709 error_popup "Error loading commit data for amend:\n\n$err"
e57ca85e
SP
710 return
711 }
712
4539eacd
SP
713 if {$parent_count > 1} {
714 error_popup {Can't amend a merge commit.}
715 return
716 }
717
e57ca85e 718 if {$parent_count == 0} {
4539eacd 719 set commit_type amend-initial
e57ca85e 720 set PARENT {}
e57ca85e
SP
721 } elseif {$parent_count == 1} {
722 set commit_type amend
723 set PARENT $parent
e57ca85e 724 }
4539eacd
SP
725
726 $ui_comm delete 0.0 end
727 $ui_comm insert end $msg
728 $ui_comm edit modified false
729 $ui_comm edit reset
730 rescan {set ui_status_value {Ready.}}
e57ca85e
SP
731}
732
24ac9b75
SP
733proc create_new_commit {} {
734 global commit_type ui_comm
735
736 set commit_type normal
737 $ui_comm delete 0.0 end
738 $ui_comm edit modified false
739 $ui_comm edit reset
740 rescan {set ui_status_value {Ready.}}
741}
742
d63efae2
SP
743set GIT_COMMITTER_IDENT {}
744
745proc committer_ident {} {
746 global GIT_COMMITTER_IDENT
747
748 if {$GIT_COMMITTER_IDENT eq {}} {
749 if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} {
750 error_popup "Unable to obtain your identity:\n\n$err"
751 return {}
752 }
753 if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \
754 $me me GIT_COMMITTER_IDENT]} {
755 error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me"
756 return {}
757 }
758 }
759
760 return $GIT_COMMITTER_IDENT
761}
762
ec6b424a 763proc commit_tree {} {
bbe3b3b9 764 global HEAD commit_type file_states ui_comm repo_config
ec6b424a 765
333b0c74 766 if {![lock_index update]} return
d63efae2 767 if {[committer_ident] eq {}} return
ec6b424a
SP
768
769 # -- Our in memory state should match the repository.
770 #
771 repository_state curHEAD cur_type
4539eacd 772 if {[string match amend* $commit_type]
043f7011
SP
773 && $cur_type eq {normal}
774 && $curHEAD eq $HEAD} {
775 } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
bca680b0 776 info_popup {Last scanned state does not match repository state.
ec6b424a 777
bca680b0
SP
778Another Git program has modified this repository
779since the last scan. A rescan must be performed
780before another commit can be created.
bbe3b3b9 781
bca680b0 782The rescan will be automatically started now.
ec6b424a
SP
783}
784 unlock_index
8f52548a 785 rescan {set ui_status_value {Ready.}}
ec6b424a
SP
786 return
787 }
788
789 # -- At least one file should differ in the index.
790 #
791 set files_ready 0
792 foreach path [array names file_states] {
bbe3b3b9 793 switch -glob -- [lindex $file_states($path) 0] {
7f1df79b
SP
794 _? {continue}
795 A? -
796 D? -
797 M? {set files_ready 1; break}
798 U? {
ec6b424a
SP
799 error_popup "Unmerged files cannot be committed.
800
16403d0b 801File [short_path $path] has merge conflicts.
7fe7e733 802You must resolve them and include the file before committing.
ec6b424a
SP
803"
804 unlock_index
805 return
806 }
807 default {
808 error_popup "Unknown file state [lindex $s 0] detected.
809
16403d0b 810File [short_path $path] cannot be committed by this program.
ec6b424a
SP
811"
812 }
813 }
814 }
815 if {!$files_ready} {
7fe7e733 816 error_popup {No included files to commit.
ec6b424a 817
7fe7e733 818You must include at least 1 file before you can commit.
ec6b424a
SP
819}
820 unlock_index
821 return
822 }
823
824 # -- A message is required.
825 #
826 set msg [string trim [$ui_comm get 1.0 end]]
043f7011 827 if {$msg eq {}} {
ec6b424a
SP
828 error_popup {Please supply a commit message.
829
830A good commit message has the following format:
831
832- First line: Describe in one sentance what you did.
833- Second line: Blank
834- Remaining lines: Describe why this change is good.
835}
836 unlock_index
837 return
838 }
839
bbe3b3b9 840 # -- Update included files if partialincludes are off.
ec6b424a 841 #
bbe3b3b9
SP
842 if {$repo_config(gui.partialinclude) ne {true}} {
843 set pathList [list]
844 foreach path [array names file_states] {
845 switch -glob -- [lindex $file_states($path) 0] {
846 A? -
847 M? {lappend pathList $path}
848 }
849 }
850 if {$pathList ne {}} {
851 unlock_index
852 update_index \
853 "Updating included files" \
854 $pathList \
855 [concat {lock_index update;} \
856 [list commit_prehook $curHEAD $msg]]
857 return
858 }
859 }
860
861 commit_prehook $curHEAD $msg
862}
863
864proc commit_prehook {curHEAD msg} {
865 global tcl_platform gitdir ui_status_value pch_error
866
867 # On Cygwin [file executable] might lie so we need to ask
868 # the shell if the hook is executable. Yes that's annoying.
869
ec6b424a 870 set pchook [file join $gitdir hooks pre-commit]
bbe3b3b9
SP
871 if {$tcl_platform(platform) eq {windows}
872 && [file isfile $pchook]} {
4658b56f
SP
873 set pchook [list sh -c [concat \
874 "if test -x \"$pchook\";" \
875 "then exec \"$pchook\" 2>&1;" \
876 "fi"]]
ec6b424a 877 } elseif {[file executable $pchook]} {
4658b56f 878 set pchook [list $pchook |& cat]
ec6b424a 879 } else {
bbe3b3b9
SP
880 commit_writetree $curHEAD $msg
881 return
4658b56f 882 }
bbe3b3b9
SP
883
884 set ui_status_value {Calling pre-commit hook...}
885 set pch_error {}
886 set fd_ph [open "| $pchook" r]
887 fconfigure $fd_ph -blocking 0 -translation binary
888 fileevent $fd_ph readable \
889 [list commit_prehook_wait $fd_ph $curHEAD $msg]
4658b56f
SP
890}
891
bbe3b3b9 892proc commit_prehook_wait {fd_ph curHEAD msg} {
333b0c74 893 global pch_error ui_status_value
4658b56f
SP
894
895 append pch_error [read $fd_ph]
896 fconfigure $fd_ph -blocking 1
897 if {[eof $fd_ph]} {
898 if {[catch {close $fd_ph}]} {
899 set ui_status_value {Commit declined by pre-commit hook.}
900 hook_failed_popup pre-commit $pch_error
901 unlock_index
333b0c74 902 } else {
bbe3b3b9 903 commit_writetree $curHEAD $msg
4658b56f 904 }
333b0c74 905 set pch_error {}
bbe3b3b9 906 return
ec6b424a 907 }
bbe3b3b9 908 fconfigure $fd_ph -blocking 0
4658b56f
SP
909}
910
bbe3b3b9 911proc commit_writetree {curHEAD msg} {
4658b56f 912 global ui_status_value
ec6b424a 913
ec6b424a 914 set ui_status_value {Committing changes...}
ec6b424a 915 set fd_wt [open "| git write-tree" r]
bbe3b3b9
SP
916 fileevent $fd_wt readable \
917 [list commit_committree $fd_wt $curHEAD $msg]
ec6b424a
SP
918}
919
bbe3b3b9 920proc commit_committree {fd_wt curHEAD msg} {
c8ebafd8 921 global single_commit gitdir HEAD PARENT commit_type tcl_platform
24ac9b75 922 global ui_status_value ui_comm selected_commit_type
24263b77 923 global file_states selected_paths
ec6b424a
SP
924
925 gets $fd_wt tree_id
043f7011 926 if {$tree_id eq {} || [catch {close $fd_wt} err]} {
44be340e 927 error_popup "write-tree failed:\n\n$err"
ec6b424a
SP
928 set ui_status_value {Commit failed.}
929 unlock_index
930 return
931 }
932
933 # -- Create the commit.
934 #
935 set cmd [list git commit-tree $tree_id]
043f7011 936 if {$PARENT ne {}} {
e57ca85e 937 lappend cmd -p $PARENT
ec6b424a 938 }
043f7011 939 if {$commit_type eq {merge}} {
ec6b424a
SP
940 if {[catch {
941 set fd_mh [open [file join $gitdir MERGE_HEAD] r]
bd1e2b40
SP
942 while {[gets $fd_mh merge_head] >= 0} {
943 lappend cmd -p $merge_head
ec6b424a
SP
944 }
945 close $fd_mh
946 } err]} {
44be340e 947 error_popup "Loading MERGE_HEAD failed:\n\n$err"
ec6b424a
SP
948 set ui_status_value {Commit failed.}
949 unlock_index
950 return
951 }
952 }
043f7011 953 if {$PARENT eq {}} {
ec6b424a
SP
954 # git commit-tree writes to stderr during initial commit.
955 lappend cmd 2>/dev/null
956 }
957 lappend cmd << $msg
958 if {[catch {set cmt_id [eval exec $cmd]} err]} {
44be340e 959 error_popup "commit-tree failed:\n\n$err"
ec6b424a
SP
960 set ui_status_value {Commit failed.}
961 unlock_index
962 return
963 }
964
965 # -- Update the HEAD ref.
966 #
967 set reflogm commit
043f7011 968 if {$commit_type ne {normal}} {
ec6b424a
SP
969 append reflogm " ($commit_type)"
970 }
971 set i [string first "\n" $msg]
972 if {$i >= 0} {
24263b77 973 append reflogm {: } [string range $msg 0 [expr {$i - 1}]]
ec6b424a
SP
974 } else {
975 append reflogm {: } $msg
976 }
e57ca85e 977 set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD]
ec6b424a 978 if {[catch {eval exec $cmd} err]} {
44be340e 979 error_popup "update-ref failed:\n\n$err"
ec6b424a
SP
980 set ui_status_value {Commit failed.}
981 unlock_index
982 return
983 }
984
985 # -- Cleanup after ourselves.
986 #
987 catch {file delete [file join $gitdir MERGE_HEAD]}
988 catch {file delete [file join $gitdir MERGE_MSG]}
989 catch {file delete [file join $gitdir SQUASH_MSG]}
990 catch {file delete [file join $gitdir GITGUI_MSG]}
991
992 # -- Let rerere do its thing.
993 #
994 if {[file isdirectory [file join $gitdir rr-cache]]} {
995 catch {exec git rerere}
996 }
997
c8ebafd8
SP
998 # -- Run the post-commit hook.
999 #
1000 set pchook [file join $gitdir hooks post-commit]
043f7011 1001 if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} {
c8ebafd8
SP
1002 set pchook [list sh -c [concat \
1003 "if test -x \"$pchook\";" \
1004 "then exec \"$pchook\";" \
1005 "fi"]]
1006 } elseif {![file executable $pchook]} {
1007 set pchook {}
1008 }
043f7011 1009 if {$pchook ne {}} {
c8ebafd8
SP
1010 catch {exec $pchook &}
1011 }
1012
e57ca85e
SP
1013 $ui_comm delete 0.0 end
1014 $ui_comm edit modified false
b2c6fcf1 1015 $ui_comm edit reset
ec6b424a
SP
1016
1017 if {$single_commit} do_quit
1018
7f1df79b
SP
1019 # -- Update status without invoking any git commands.
1020 #
7f1df79b 1021 set commit_type normal
24ac9b75 1022 set selected_commit_type new
bd1e2b40
SP
1023 set HEAD $cmt_id
1024 set PARENT $cmt_id
7f1df79b
SP
1025
1026 foreach path [array names file_states] {
1027 set s $file_states($path)
1028 set m [lindex $s 0]
1029 switch -glob -- $m {
dde5974e
SP
1030 DD -
1031 AO {set m __}
7f1df79b
SP
1032 A? -
1033 M? -
1034 D? {set m _[string index $m 1]}
1035 }
1036
043f7011 1037 if {$m eq {__}} {
7f1df79b 1038 unset file_states($path)
24263b77 1039 catch {unset selected_paths($path)}
7f1df79b
SP
1040 } else {
1041 lset file_states($path) 0 $m
1042 }
1043 }
1044
1045 display_all_files
ec6b424a 1046 unlock_index
7f1df79b
SP
1047 reshow_diff
1048 set ui_status_value \
1049 "Changes committed as [string range $cmt_id 0 7]."
ec6b424a
SP
1050}
1051
8c0ce436
SP
1052######################################################################
1053##
1054## fetch pull push
1055
1056proc fetch_from {remote} {
1057 set w [new_console "fetch $remote" \
1058 "Fetching new changes from $remote"]
cc4b1c02 1059 set cmd [list git fetch]
8c0ce436 1060 lappend cmd $remote
cc4b1c02 1061 console_exec $w $cmd
8c0ce436
SP
1062}
1063
d33ba5fa 1064proc pull_remote {remote branch} {
ebf336b9 1065 global HEAD commit_type file_states repo_config
ec39d83a 1066
988b8a7d 1067 if {![lock_index update]} return
ec39d83a
SP
1068
1069 # -- Our in memory state should match the repository.
1070 #
1071 repository_state curHEAD cur_type
043f7011 1072 if {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
ec39d83a
SP
1073 error_popup {Last scanned state does not match repository state.
1074
1075Its highly likely that another Git program modified the
1076repository since our last scan. A rescan is required
1077before a pull can be started.
1078}
1079 unlock_index
8f52548a 1080 rescan {set ui_status_value {Ready.}}
ec39d83a
SP
1081 return
1082 }
1083
1084 # -- No differences should exist before a pull.
1085 #
1086 if {[array size file_states] != 0} {
1087 error_popup {Uncommitted but modified files are present.
1088
1089You should not perform a pull with unmodified files in your working
1090directory as Git would be unable to recover from an incorrect merge.
1091
1092Commit or throw away all changes before starting a pull operation.
1093}
1094 unlock_index
1095 return
1096 }
1097
d33ba5fa
SP
1098 set w [new_console "pull $remote $branch" \
1099 "Pulling new changes from branch $branch in $remote"]
1100 set cmd [list git pull]
043f7011 1101 if {$repo_config(gui.pullsummary) eq {false}} {
ebf336b9
SP
1102 lappend cmd --no-summary
1103 }
d33ba5fa
SP
1104 lappend cmd $remote
1105 lappend cmd $branch
1106 console_exec $w $cmd [list post_pull_remote $remote $branch]
1107}
1108
1109proc post_pull_remote {remote branch success} {
24ac9b75 1110 global HEAD PARENT commit_type selected_commit_type
ec39d83a
SP
1111 global ui_status_value
1112
988b8a7d 1113 unlock_index
d33ba5fa 1114 if {$success} {
ec39d83a
SP
1115 repository_state HEAD commit_type
1116 set PARENT $HEAD
24ac9b75 1117 set selected_commit_type new
8f52548a 1118 set $ui_status_value "Pulling $branch from $remote complete."
d33ba5fa 1119 } else {
8f52548a
SP
1120 set m "Conflicts detected while pulling $branch from $remote."
1121 rescan "set ui_status_value {$m}"
d33ba5fa
SP
1122 }
1123}
1124
8c0ce436
SP
1125proc push_to {remote} {
1126 set w [new_console "push $remote" \
1127 "Pushing changes to $remote"]
cc4b1c02 1128 set cmd [list git push]
8c0ce436 1129 lappend cmd $remote
cc4b1c02 1130 console_exec $w $cmd
8c0ce436
SP
1131}
1132
cb07fc2a
SP
1133######################################################################
1134##
1135## ui helpers
1136
1137proc mapcol {state path} {
6b292675 1138 global all_cols ui_other
cb07fc2a
SP
1139
1140 if {[catch {set r $all_cols($state)}]} {
1141 puts "error: no column for state={$state} $path"
6b292675 1142 return $ui_other
cb07fc2a
SP
1143 }
1144 return $r
1145}
1146
1147proc mapicon {state path} {
1148 global all_icons
1149
1150 if {[catch {set r $all_icons($state)}]} {
1151 puts "error: no icon for state={$state} $path"
1152 return file_plain
1153 }
1154 return $r
1155}
1156
1157proc mapdesc {state path} {
1158 global all_descs
1159
1160 if {[catch {set r $all_descs($state)}]} {
1161 puts "error: no desc for state={$state} $path"
1162 return $state
1163 }
1164 return $r
1165}
1166
68e009de
SP
1167proc escape_path {path} {
1168 regsub -all "\n" $path "\\n" path
1169 return $path
1170}
1171
16403d0b
SP
1172proc short_path {path} {
1173 return [escape_path [lindex [file split $path] end]]
1174}
1175
93f654df
SP
1176set next_icon_id 0
1177
1461c5f3 1178proc merge_state {path new_state {head_info {}} {index_info {}}} {
93f654df 1179 global file_states next_icon_id
cb07fc2a 1180
6b292675
SP
1181 set s0 [string index $new_state 0]
1182 set s1 [string index $new_state 1]
1183
1184 if {[catch {set info $file_states($path)}]} {
1185 set state __
1186 set icon n[incr next_icon_id]
cb07fc2a 1187 } else {
6b292675
SP
1188 set state [lindex $info 0]
1189 set icon [lindex $info 1]
1461c5f3
SP
1190 if {$head_info eq {}} {set head_info [lindex $info 2]}
1191 if {$index_info eq {}} {set index_info [lindex $info 3]}
cb07fc2a
SP
1192 }
1193
1461c5f3
SP
1194 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1195 elseif {$s0 eq {_}} {set s0 _}
1196
1197 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1198 elseif {$s1 eq {_}} {set s1 _}
cb07fc2a 1199
1461c5f3
SP
1200 if {$s0 ne {_} && [string index $state 0] eq {_}
1201 && $head_info eq {}} {
1202 set head_info $index_info
cb07fc2a
SP
1203 }
1204
1461c5f3
SP
1205 set file_states($path) [list $s0$s1 $icon \
1206 $head_info $index_info \
1207 ]
6b292675 1208 return $state
cb07fc2a
SP
1209}
1210
1211proc display_file {path state} {
1461c5f3 1212 global file_states file_lists selected_paths
cb07fc2a
SP
1213
1214 set old_m [merge_state $path $state]
1215 set s $file_states($path)
93f654df 1216 set new_m [lindex $s 0]
0fb8f9ce
SP
1217 set new_w [mapcol $new_m $path]
1218 set old_w [mapcol $old_m $path]
1219 set new_icon [mapicon $new_m $path]
cb07fc2a 1220
043f7011 1221 if {$new_w ne $old_w} {
03e4ec53 1222 set lno [lsearch -sorted $file_lists($old_w) $path]
cb07fc2a
SP
1223 if {$lno >= 0} {
1224 incr lno
93f654df 1225 $old_w conf -state normal
24263b77 1226 $old_w delete $lno.0 [expr {$lno + 1}].0
93f654df 1227 $old_w conf -state disabled
cb07fc2a 1228 }
93f654df 1229
03e4ec53
SP
1230 lappend file_lists($new_w) $path
1231 set file_lists($new_w) [lsort $file_lists($new_w)]
1232 set lno [lsearch -sorted $file_lists($new_w) $path]
1233 incr lno
93f654df
SP
1234 $new_w conf -state normal
1235 $new_w image create $lno.0 \
1236 -align center -padx 5 -pady 1 \
1237 -name [lindex $s 1] \
e4ee9af4 1238 -image $new_icon
68e009de 1239 $new_w insert $lno.1 "[escape_path $path]\n"
24263b77
SP
1240 if {[catch {set in_sel $selected_paths($path)}]} {
1241 set in_sel 0
1242 }
1243 if {$in_sel} {
1244 $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0
1245 }
93f654df 1246 $new_w conf -state disabled
043f7011 1247 } elseif {$new_icon ne [mapicon $old_m $path]} {
93f654df
SP
1248 $new_w conf -state normal
1249 $new_w image conf [lindex $s 1] -image $new_icon
1250 $new_w conf -state disabled
cb07fc2a 1251 }
93f654df 1252}
cb07fc2a 1253
93f654df 1254proc display_all_files {} {
24263b77
SP
1255 global ui_index ui_other
1256 global file_states file_lists
1257 global last_clicked selected_paths
93f654df
SP
1258
1259 $ui_index conf -state normal
1260 $ui_other conf -state normal
1261
7f1df79b
SP
1262 $ui_index delete 0.0 end
1263 $ui_other delete 0.0 end
24263b77 1264 set last_clicked {}
7f1df79b 1265
62aac80b
SP
1266 set file_lists($ui_index) [list]
1267 set file_lists($ui_other) [list]
1268
93f654df
SP
1269 foreach path [lsort [array names file_states]] {
1270 set s $file_states($path)
1271 set m [lindex $s 0]
6b292675 1272 set w [mapcol $m $path]
03e4ec53 1273 lappend file_lists($w) $path
24263b77 1274 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
6b292675 1275 $w image create end \
cb07fc2a 1276 -align center -padx 5 -pady 1 \
93f654df
SP
1277 -name [lindex $s 1] \
1278 -image [mapicon $m $path]
68e009de 1279 $w insert end "[escape_path $path]\n"
24263b77
SP
1280 if {[catch {set in_sel $selected_paths($path)}]} {
1281 set in_sel 0
1282 }
1283 if {$in_sel} {
1284 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1285 }
cb07fc2a 1286 }
93f654df
SP
1287
1288 $ui_index conf -state disabled
1289 $ui_other conf -state disabled
cb07fc2a
SP
1290}
1291
1461c5f3
SP
1292proc update_indexinfo {msg pathList after} {
1293 global update_index_cp ui_status_value
1294
1295 if {![lock_index update]} return
1296
1297 set update_index_cp 0
1298 set pathList [lsort $pathList]
1299 set totalCnt [llength $pathList]
1300 set batch [expr {int($totalCnt * .01) + 1}]
1301 if {$batch > 25} {set batch 25}
1302
1303 set ui_status_value [format \
1304 "$msg... %i/%i files (%.2f%%)" \
1305 $update_index_cp \
1306 $totalCnt \
1307 0.0]
1308 set fd [open "| git update-index -z --index-info" w]
1309 fconfigure $fd \
1310 -blocking 0 \
1311 -buffering full \
1312 -buffersize 512 \
1313 -translation binary
1314 fileevent $fd writable [list \
1315 write_update_indexinfo \
1316 $fd \
1317 $pathList \
1318 $totalCnt \
1319 $batch \
1320 $msg \
1321 $after \
1322 ]
1323}
1324
1325proc write_update_indexinfo {fd pathList totalCnt batch msg after} {
1326 global update_index_cp ui_status_value
1327 global file_states current_diff
1328
1329 if {$update_index_cp >= $totalCnt} {
1330 close $fd
1331 unlock_index
1332 uplevel #0 $after
1333 return
1334 }
1335
1336 for {set i $batch} \
1337 {$update_index_cp < $totalCnt && $i > 0} \
1338 {incr i -1} {
1339 set path [lindex $pathList $update_index_cp]
1340 incr update_index_cp
1341
1342 set s $file_states($path)
1343 switch -glob -- [lindex $s 0] {
1344 A? {set new _O}
1345 M? {set new _M}
1346 D? {set new _?}
1347 ?? {continue}
1348 }
1349 set info [lindex $s 2]
1350 if {$info eq {}} continue
1351
1352 puts -nonewline $fd $info
1353 puts -nonewline $fd "\t"
1354 puts -nonewline $fd $path
1355 puts -nonewline $fd "\0"
1356 display_file $path $new
1357 }
1358
1359 set ui_status_value [format \
1360 "$msg... %i/%i files (%.2f%%)" \
1361 $update_index_cp \
1362 $totalCnt \
1363 [expr {100.0 * $update_index_cp / $totalCnt}]]
1364}
1365
04b39382 1366proc update_index {msg pathList after} {
32e0bcab 1367 global update_index_cp ui_status_value
131f503b 1368
74e6b12f 1369 if {![lock_index update]} return
131f503b 1370
74e6b12f 1371 set update_index_cp 0
aaf1085a 1372 set pathList [lsort $pathList]
74e6b12f
SP
1373 set totalCnt [llength $pathList]
1374 set batch [expr {int($totalCnt * .01) + 1}]
1375 if {$batch > 25} {set batch 25}
1376
74e6b12f 1377 set ui_status_value [format \
04b39382 1378 "$msg... %i/%i files (%.2f%%)" \
74e6b12f
SP
1379 $update_index_cp \
1380 $totalCnt \
1381 0.0]
1382 set fd [open "| git update-index --add --remove -z --stdin" w]
7f09cfaf
SP
1383 fconfigure $fd \
1384 -blocking 0 \
1385 -buffering full \
1386 -buffersize 512 \
1387 -translation binary
74e6b12f
SP
1388 fileevent $fd writable [list \
1389 write_update_index \
1390 $fd \
1391 $pathList \
1392 $totalCnt \
1393 $batch \
04b39382
SP
1394 $msg \
1395 $after \
74e6b12f
SP
1396 ]
1397}
1398
04b39382 1399proc write_update_index {fd pathList totalCnt batch msg after} {
32e0bcab 1400 global update_index_cp ui_status_value
e8ab6446 1401 global file_states current_diff
131f503b 1402
74e6b12f
SP
1403 if {$update_index_cp >= $totalCnt} {
1404 close $fd
1405 unlock_index
04b39382 1406 uplevel #0 $after
74e6b12f 1407 return
131f503b 1408 }
131f503b 1409
74e6b12f
SP
1410 for {set i $batch} \
1411 {$update_index_cp < $totalCnt && $i > 0} \
1412 {incr i -1} {
1413 set path [lindex $pathList $update_index_cp]
1414 incr update_index_cp
1415
bbe3b3b9 1416 switch -glob -- [lindex $file_states($path) 0] {
74e6b12f 1417 AD -
bbe3b3b9 1418 MD -
dde5974e 1419 _D {set new DD}
bbe3b3b9
SP
1420
1421 _M -
1422 MM -
b6765112 1423 M_ {set new M_}
bbe3b3b9
SP
1424
1425 _O -
1426 AM -
b6765112 1427 A_ {set new A_}
bbe3b3b9
SP
1428
1429 ?? {continue}
74e6b12f 1430 }
cb07fc2a 1431
74e6b12f
SP
1432 puts -nonewline $fd $path
1433 puts -nonewline $fd "\0"
1434 display_file $path $new
cb07fc2a
SP
1435 }
1436
74e6b12f 1437 set ui_status_value [format \
04b39382 1438 "$msg... %i/%i files (%.2f%%)" \
74e6b12f
SP
1439 $update_index_cp \
1440 $totalCnt \
1441 [expr {100.0 * $update_index_cp / $totalCnt}]]
cb07fc2a
SP
1442}
1443
8c0ce436
SP
1444######################################################################
1445##
2d19516d 1446## remote management
0d4f3eb5 1447
8c0ce436 1448proc load_all_remotes {} {
0d4f3eb5 1449 global gitdir all_remotes repo_config
8c0ce436
SP
1450
1451 set all_remotes [list]
1452 set rm_dir [file join $gitdir remotes]
1453 if {[file isdirectory $rm_dir]} {
d47ae541
SP
1454 set all_remotes [concat $all_remotes [glob \
1455 -types f \
1456 -tails \
1457 -nocomplain \
1458 -directory $rm_dir *]]
8c0ce436
SP
1459 }
1460
0d4f3eb5
SP
1461 foreach line [array names repo_config remote.*.url] {
1462 if {[regexp ^remote\.(.*)\.url\$ $line line name]} {
8c0ce436
SP
1463 lappend all_remotes $name
1464 }
1465 }
8c0ce436
SP
1466
1467 set all_remotes [lsort -unique $all_remotes]
1468}
1469
c1237ae2
SP
1470proc populate_fetch_menu {m} {
1471 global gitdir all_remotes repo_config
8c0ce436 1472
c1237ae2
SP
1473 foreach r $all_remotes {
1474 set enable 0
1475 if {![catch {set a $repo_config(remote.$r.url)}]} {
1476 if {![catch {set a $repo_config(remote.$r.fetch)}]} {
1477 set enable 1
1478 }
1479 } else {
1480 catch {
1481 set fd [open [file join $gitdir remotes $r] r]
1482 while {[gets $fd n] >= 0} {
1483 if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
1484 set enable 1
1485 break
1486 }
1487 }
1488 close $fd
1489 }
1490 }
1491
1492 if {$enable} {
1493 $m add command \
1494 -label "Fetch from $r..." \
1495 -command [list fetch_from $r] \
1496 -font font_ui
1497 }
1498 }
1499}
1500
1501proc populate_push_menu {m} {
1502 global gitdir all_remotes repo_config
1503
1504 foreach r $all_remotes {
1505 set enable 0
1506 if {![catch {set a $repo_config(remote.$r.url)}]} {
1507 if {![catch {set a $repo_config(remote.$r.push)}]} {
1508 set enable 1
1509 }
1510 } else {
1511 catch {
1512 set fd [open [file join $gitdir remotes $r] r]
1513 while {[gets $fd n] >= 0} {
1514 if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
1515 set enable 1
1516 break
1517 }
1518 }
1519 close $fd
1520 }
1521 }
1522
1523 if {$enable} {
1524 $m add command \
1525 -label "Push to $r..." \
1526 -command [list push_to $r] \
1527 -font font_ui
1528 }
8c0ce436
SP
1529 }
1530}
1531
d33ba5fa 1532proc populate_pull_menu {m} {
b4946930 1533 global gitdir repo_config all_remotes disable_on_lock
d33ba5fa
SP
1534
1535 foreach remote $all_remotes {
1536 set rb {}
043f7011
SP
1537 if {[array get repo_config remote.$remote.url] ne {}} {
1538 if {[array get repo_config remote.$remote.fetch] ne {}} {
d33ba5fa
SP
1539 regexp {^([^:]+):} \
1540 [lindex $repo_config(remote.$remote.fetch) 0] \
1541 line rb
1542 }
1543 } else {
1544 catch {
1545 set fd [open [file join $gitdir remotes $remote] r]
1546 while {[gets $fd line] >= 0} {
1547 if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} {
1548 break
1549 }
1550 }
1551 close $fd
1552 }
1553 }
1554
1555 set rb_short $rb
1556 regsub ^refs/heads/ $rb {} rb_short
043f7011 1557 if {$rb_short ne {}} {
d33ba5fa
SP
1558 $m add command \
1559 -label "Branch $rb_short from $remote..." \
1560 -command [list pull_remote $remote $rb] \
b4946930 1561 -font font_ui
0a462d67
SP
1562 lappend disable_on_lock \
1563 [list $m entryconf [$m index last] -state]
d33ba5fa
SP
1564 }
1565 }
1566}
1567
cb07fc2a
SP
1568######################################################################
1569##
1570## icons
1571
1572set filemask {
1573#define mask_width 14
1574#define mask_height 15
1575static unsigned char mask_bits[] = {
1576 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1577 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1578 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1579}
1580
1581image create bitmap file_plain -background white -foreground black -data {
1582#define plain_width 14
1583#define plain_height 15
1584static unsigned char plain_bits[] = {
1585 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1586 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1587 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1588} -maskdata $filemask
1589
1590image create bitmap file_mod -background white -foreground blue -data {
1591#define mod_width 14
1592#define mod_height 15
1593static unsigned char mod_bits[] = {
1594 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1595 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1596 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1597} -maskdata $filemask
1598
131f503b
SP
1599image create bitmap file_fulltick -background white -foreground "#007000" -data {
1600#define file_fulltick_width 14
1601#define file_fulltick_height 15
1602static unsigned char file_fulltick_bits[] = {
cb07fc2a
SP
1603 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1604 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1605 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1606} -maskdata $filemask
1607
1608image create bitmap file_parttick -background white -foreground "#005050" -data {
1609#define parttick_width 14
1610#define parttick_height 15
1611static unsigned char parttick_bits[] = {
1612 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1613 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1614 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1615} -maskdata $filemask
1616
1617image create bitmap file_question -background white -foreground black -data {
1618#define file_question_width 14
1619#define file_question_height 15
1620static unsigned char file_question_bits[] = {
1621 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1622 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1623 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1624} -maskdata $filemask
1625
1626image create bitmap file_removed -background white -foreground red -data {
1627#define file_removed_width 14
1628#define file_removed_height 15
1629static unsigned char file_removed_bits[] = {
1630 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1631 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1632 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1633} -maskdata $filemask
1634
1635image create bitmap file_merge -background white -foreground blue -data {
1636#define file_merge_width 14
1637#define file_merge_height 15
1638static unsigned char file_merge_bits[] = {
1639 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1640 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1641 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1642} -maskdata $filemask
1643
6b292675
SP
1644set ui_index .vpane.files.index.list
1645set ui_other .vpane.files.other.list
131f503b 1646set max_status_desc 0
cb07fc2a 1647foreach i {
131f503b
SP
1648 {__ i plain "Unmodified"}
1649 {_M i mod "Modified"}
135f76ed 1650 {M_ i fulltick "Included in commit"}
7fe7e733 1651 {MM i parttick "Partially included"}
131f503b
SP
1652
1653 {_O o plain "Untracked"}
135f76ed 1654 {A_ o fulltick "Added by commit"}
131f503b 1655 {AM o parttick "Partially added"}
6b292675 1656 {AD o question "Added (but now gone)"}
131f503b
SP
1657
1658 {_D i question "Missing"}
135f76ed
SP
1659 {D_ i removed "Removed by commit"}
1660 {DD i removed "Removed by commit"}
131f503b
SP
1661 {DO i removed "Removed (still exists)"}
1662
1663 {UM i merge "Merge conflicts"}
1664 {U_ i merge "Merge conflicts"}
cb07fc2a 1665 } {
131f503b
SP
1666 if {$max_status_desc < [string length [lindex $i 3]]} {
1667 set max_status_desc [string length [lindex $i 3]]
1668 }
043f7011 1669 if {[lindex $i 1] eq {i}} {
6b292675
SP
1670 set all_cols([lindex $i 0]) $ui_index
1671 } else {
1672 set all_cols([lindex $i 0]) $ui_other
1673 }
131f503b
SP
1674 set all_icons([lindex $i 0]) file_[lindex $i 2]
1675 set all_descs([lindex $i 0]) [lindex $i 3]
cb07fc2a
SP
1676}
1677unset filemask i
1678
1679######################################################################
1680##
1681## util
1682
16fccd7a
SP
1683proc is_MacOSX {} {
1684 global tcl_platform tk_library
043f7011
SP
1685 if {$tcl_platform(platform) eq {unix}
1686 && $tcl_platform(os) eq {Darwin}
16fccd7a
SP
1687 && [string match /Library/Frameworks/* $tk_library]} {
1688 return 1
1689 }
1690 return 0
1691}
1692
1693proc bind_button3 {w cmd} {
1694 bind $w <Any-Button-3> $cmd
1695 if {[is_MacOSX]} {
1696 bind $w <Control-Button-1> $cmd
1697 }
1698}
1699
b4946930
SP
1700proc incr_font_size {font {amt 1}} {
1701 set sz [font configure $font -size]
1702 incr sz $amt
1703 font configure $font -size $sz
1704 font configure ${font}bold -size $sz
1705}
1706
6e27d826 1707proc hook_failed_popup {hook msg} {
b4946930 1708 global gitdir appname
6e27d826
SP
1709
1710 set w .hookfail
1711 toplevel $w
6e27d826
SP
1712
1713 frame $w.m
1714 label $w.m.l1 -text "$hook hook failed:" \
1715 -anchor w \
1716 -justify left \
b4946930 1717 -font font_uibold
6e27d826
SP
1718 text $w.m.t \
1719 -background white -borderwidth 1 \
1720 -relief sunken \
1721 -width 80 -height 10 \
b4946930 1722 -font font_diff \
6e27d826
SP
1723 -yscrollcommand [list $w.m.sby set]
1724 label $w.m.l2 \
1725 -text {You must correct the above errors before committing.} \
1726 -anchor w \
1727 -justify left \
b4946930 1728 -font font_uibold
6e27d826
SP
1729 scrollbar $w.m.sby -command [list $w.m.t yview]
1730 pack $w.m.l1 -side top -fill x
1731 pack $w.m.l2 -side bottom -fill x
1732 pack $w.m.sby -side right -fill y
1733 pack $w.m.t -side left -fill both -expand 1
1734 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1735
1736 $w.m.t insert 1.0 $msg
1737 $w.m.t conf -state disabled
1738
1739 button $w.ok -text OK \
1740 -width 15 \
b4946930 1741 -font font_ui \
6e27d826 1742 -command "destroy $w"
1e5c18fb 1743 pack $w.ok -side bottom -anchor e -pady 10 -padx 10
6e27d826
SP
1744
1745 bind $w <Visibility> "grab $w; focus $w"
1746 bind $w <Key-Return> "destroy $w"
d33ba5fa
SP
1747 wm title $w "$appname ([lindex [file split \
1748 [file normalize [file dirname $gitdir]]] \
1749 end]): error"
6e27d826
SP
1750 tkwait window $w
1751}
1752
8c0ce436
SP
1753set next_console_id 0
1754
1755proc new_console {short_title long_title} {
37af79d1
SP
1756 global next_console_id console_data
1757 set w .console[incr next_console_id]
1758 set console_data($w) [list $short_title $long_title]
1759 return [console_init $w]
1760}
1761
1762proc console_init {w} {
1763 global console_cr console_data
b4946930 1764 global gitdir appname M1B
8c0ce436 1765
ee3dc935 1766 set console_cr($w) 1.0
8c0ce436
SP
1767 toplevel $w
1768 frame $w.m
37af79d1 1769 label $w.m.l1 -text "[lindex $console_data($w) 1]:" \
8c0ce436
SP
1770 -anchor w \
1771 -justify left \
b4946930 1772 -font font_uibold
8c0ce436
SP
1773 text $w.m.t \
1774 -background white -borderwidth 1 \
1775 -relief sunken \
1776 -width 80 -height 10 \
b4946930 1777 -font font_diff \
8c0ce436
SP
1778 -state disabled \
1779 -yscrollcommand [list $w.m.sby set]
1e5c18fb
SP
1780 label $w.m.s -text {Working... please wait...} \
1781 -anchor w \
07123f40 1782 -justify left \
b4946930 1783 -font font_uibold
8c0ce436
SP
1784 scrollbar $w.m.sby -command [list $w.m.t yview]
1785 pack $w.m.l1 -side top -fill x
07123f40 1786 pack $w.m.s -side bottom -fill x
8c0ce436
SP
1787 pack $w.m.sby -side right -fill y
1788 pack $w.m.t -side left -fill both -expand 1
1789 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1790
0e794311
SP
1791 menu $w.ctxm -tearoff 0
1792 $w.ctxm add command -label "Copy" \
b4946930 1793 -font font_ui \
0e794311
SP
1794 -command "tk_textCopy $w.m.t"
1795 $w.ctxm add command -label "Select All" \
b4946930 1796 -font font_ui \
0e794311
SP
1797 -command "$w.m.t tag add sel 0.0 end"
1798 $w.ctxm add command -label "Copy All" \
b4946930 1799 -font font_ui \
0e794311
SP
1800 -command "
1801 $w.m.t tag add sel 0.0 end
1802 tk_textCopy $w.m.t
1803 $w.m.t tag remove sel 0.0 end
1804 "
1805
1e5c18fb 1806 button $w.ok -text {Close} \
b4946930 1807 -font font_ui \
8c0ce436
SP
1808 -state disabled \
1809 -command "destroy $w"
1e5c18fb 1810 pack $w.ok -side bottom -anchor e -pady 10 -padx 10
8c0ce436 1811
16fccd7a 1812 bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y"
62aac80b
SP
1813 bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
1814 bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
8c0ce436 1815 bind $w <Visibility> "focus $w"
d33ba5fa
SP
1816 wm title $w "$appname ([lindex [file split \
1817 [file normalize [file dirname $gitdir]]] \
1818 end]): [lindex $console_data($w) 0]"
8c0ce436
SP
1819 return $w
1820}
1821
d33ba5fa 1822proc console_exec {w cmd {after {}}} {
cc4b1c02
SP
1823 global tcl_platform
1824
1825 # -- Windows tosses the enviroment when we exec our child.
1826 # But most users need that so we have to relogin. :-(
1827 #
043f7011 1828 if {$tcl_platform(platform) eq {windows}} {
cc4b1c02
SP
1829 set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"]
1830 }
1831
1832 # -- Tcl won't let us redirect both stdout and stderr to
1833 # the same pipe. So pass it through cat...
1834 #
1835 set cmd [concat | $cmd |& cat]
1836
1837 set fd_f [open $cmd r]
ee3dc935 1838 fconfigure $fd_f -blocking 0 -translation binary
d33ba5fa 1839 fileevent $fd_f readable [list console_read $w $fd_f $after]
cc4b1c02
SP
1840}
1841
d33ba5fa 1842proc console_read {w fd after} {
37af79d1 1843 global console_cr console_data
ee3dc935 1844
ee3dc935 1845 set buf [read $fd]
043f7011 1846 if {$buf ne {}} {
37af79d1
SP
1847 if {![winfo exists $w]} {console_init $w}
1848 $w.m.t conf -state normal
1849 set c 0
1850 set n [string length $buf]
1851 while {$c < $n} {
1852 set cr [string first "\r" $buf $c]
1853 set lf [string first "\n" $buf $c]
24263b77
SP
1854 if {$cr < 0} {set cr [expr {$n + 1}]}
1855 if {$lf < 0} {set lf [expr {$n + 1}]}
37af79d1
SP
1856
1857 if {$lf < $cr} {
1858 $w.m.t insert end [string range $buf $c $lf]
1859 set console_cr($w) [$w.m.t index {end -1c}]
1860 set c $lf
1861 incr c
1862 } else {
1863 $w.m.t delete $console_cr($w) end
1864 $w.m.t insert end "\n"
1865 $w.m.t insert end [string range $buf $c $cr]
1866 set c $cr
1867 incr c
1868 }
ee3dc935 1869 }
37af79d1
SP
1870 $w.m.t conf -state disabled
1871 $w.m.t see end
8c0ce436 1872 }
8c0ce436 1873
07123f40 1874 fconfigure $fd -blocking 1
8c0ce436 1875 if {[eof $fd]} {
07123f40 1876 if {[catch {close $fd}]} {
37af79d1 1877 if {![winfo exists $w]} {console_init $w}
07123f40 1878 $w.m.s conf -background red -text {Error: Command Failed}
37af79d1 1879 $w.ok conf -state normal
d33ba5fa 1880 set ok 0
37af79d1 1881 } elseif {[winfo exists $w]} {
07123f40 1882 $w.m.s conf -background green -text {Success}
37af79d1 1883 $w.ok conf -state normal
d33ba5fa 1884 set ok 1
07123f40 1885 }
ee3dc935 1886 array unset console_cr $w
37af79d1 1887 array unset console_data $w
043f7011 1888 if {$after ne {}} {
d33ba5fa
SP
1889 uplevel #0 $after $ok
1890 }
07123f40 1891 return
8c0ce436 1892 }
07123f40 1893 fconfigure $fd -blocking 0
8c0ce436
SP
1894}
1895
cb07fc2a
SP
1896######################################################################
1897##
1898## ui commands
1899
e210e674 1900set starting_gitk_msg {Please wait... Starting gitk...}
cc4b1c02 1901
cb07fc2a 1902proc do_gitk {} {
e210e674
SP
1903 global tcl_platform ui_status_value starting_gitk_msg
1904
1905 set ui_status_value $starting_gitk_msg
e57ca85e 1906 after 10000 {
043f7011 1907 if {$ui_status_value eq $starting_gitk_msg} {
e210e674
SP
1908 set ui_status_value {Ready.}
1909 }
1910 }
cb07fc2a 1911
043f7011 1912 if {$tcl_platform(platform) eq {windows}} {
cb07fc2a
SP
1913 exec sh -c gitk &
1914 } else {
1915 exec gitk &
1916 }
1917}
1918
d1536c48
SP
1919proc do_repack {} {
1920 set w [new_console "repack" "Repacking the object database"]
1921 set cmd [list git repack]
1922 lappend cmd -a
1923 lappend cmd -d
1924 console_exec $w $cmd
1925}
1926
b5834d70 1927set is_quitting 0
c4fe7728 1928
cb07fc2a 1929proc do_quit {} {
51f4d16b 1930 global gitdir ui_comm is_quitting repo_config
c4fe7728 1931
b5834d70
SP
1932 if {$is_quitting} return
1933 set is_quitting 1
131f503b 1934
51f4d16b
SP
1935 # -- Stash our current commit buffer.
1936 #
131f503b 1937 set save [file join $gitdir GITGUI_MSG]
ec6b424a 1938 set msg [string trim [$ui_comm get 0.0 end]]
043f7011 1939 if {[$ui_comm edit modified] && $msg ne {}} {
131f503b
SP
1940 catch {
1941 set fd [open $save w]
1942 puts $fd [string trim [$ui_comm get 0.0 end]]
1943 close $fd
1944 }
043f7011 1945 } elseif {$msg eq {} && [file exists $save]} {
131f503b
SP
1946 file delete $save
1947 }
1948
51f4d16b
SP
1949 # -- Stash our current window geometry into this repository.
1950 #
1951 set cfg_geometry [list]
1952 lappend cfg_geometry [wm geometry .]
1953 lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1954 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1955 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1956 set rc_geometry {}
1957 }
043f7011 1958 if {$cfg_geometry ne $rc_geometry} {
51f4d16b
SP
1959 catch {exec git repo-config gui.geometry $cfg_geometry}
1960 }
1961
cb07fc2a
SP
1962 destroy .
1963}
1964
1965proc do_rescan {} {
8f52548a 1966 rescan {set ui_status_value {Ready.}}
cb07fc2a
SP
1967}
1968
1461c5f3
SP
1969proc remove_helper {txt paths} {
1970 global file_states current_diff
1971
1972 if {![lock_index begin-update]} return
1973
1974 set pathList [list]
1975 set after {}
1976 foreach path $paths {
1977 switch -glob -- [lindex $file_states($path) 0] {
1978 A? -
1979 M? -
1980 D? {
1981 lappend pathList $path
1982 if {$path eq $current_diff} {
1983 set after {reshow_diff;}
1984 }
1985 }
1986 }
1987 }
1988 if {$pathList eq {}} {
1989 unlock_index
1990 } else {
1991 update_indexinfo \
1992 $txt \
1993 $pathList \
1994 [concat $after {set ui_status_value {Ready.}}]
1995 }
1996}
1997
1998proc do_remove_selection {} {
1999 global current_diff selected_paths
2000
2001 if {[array size selected_paths] > 0} {
2002 remove_helper \
2003 {Removing selected files from commit} \
2004 [array names selected_paths]
2005 } elseif {$current_diff ne {}} {
2006 remove_helper \
2007 "Removing [short_path $current_diff] from commit" \
2008 [list $current_diff]
2009 }
2010}
2011
c4ed879f 2012proc include_helper {txt paths} {
32e0bcab 2013 global file_states current_diff
74e6b12f
SP
2014
2015 if {![lock_index begin-update]} return
2016
2017 set pathList [list]
32e0bcab 2018 set after {}
c4ed879f 2019 foreach path $paths {
32e0bcab 2020 switch -- [lindex $file_states($path) 0] {
74e6b12f 2021 AM -
54896cf7 2022 AD -
74e6b12f 2023 MM -
54896cf7
SP
2024 UM -
2025 U_ -
74e6b12f 2026 _M -
54896cf7
SP
2027 _D -
2028 _O {
32e0bcab
SP
2029 lappend pathList $path
2030 if {$path eq $current_diff} {
2031 set after {reshow_diff;}
2032 }
2033 }
131f503b 2034 }
74e6b12f 2035 }
043f7011 2036 if {$pathList eq {}} {
74e6b12f
SP
2037 unlock_index
2038 } else {
04b39382 2039 update_index \
c4ed879f 2040 $txt \
04b39382 2041 $pathList \
32e0bcab 2042 [concat $after {set ui_status_value {Ready to commit.}}]
131f503b
SP
2043 }
2044}
2045
c4ed879f
SP
2046proc do_include_selection {} {
2047 global current_diff selected_paths
2048
2049 if {[array size selected_paths] > 0} {
2050 include_helper \
2051 {Including selected files} \
2052 [array names selected_paths]
2053 } elseif {$current_diff ne {}} {
2054 include_helper \
2055 "Including [short_path $current_diff]" \
2056 [list $current_diff]
2057 }
2058}
2059
2060proc do_include_all {} {
2061 global file_states
54896cf7
SP
2062
2063 set paths [list]
2064 foreach path [array names file_states] {
2065 switch -- [lindex $file_states($path) 0] {
2066 AM -
2067 AD -
2068 MM -
2069 _M -
2070 _D {lappend paths $path}
2071 }
2072 }
c4ed879f
SP
2073 include_helper \
2074 {Including all modified files} \
54896cf7 2075 $paths
c4ed879f
SP
2076}
2077
131f503b 2078proc do_signoff {} {
d63efae2 2079 global ui_comm
131f503b 2080
d63efae2
SP
2081 set me [committer_ident]
2082 if {$me eq {}} return
97bf01c4 2083
d63efae2 2084 set sob "Signed-off-by: $me"
1daf1d0c 2085 set last [$ui_comm get {end -1c linestart} {end -1c}]
043f7011 2086 if {$last ne $sob} {
b2c6fcf1 2087 $ui_comm edit separator
043f7011 2088 if {$last ne {}
1daf1d0c
SP
2089 && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} {
2090 $ui_comm insert end "\n"
2091 }
2092 $ui_comm insert end "\n$sob"
b2c6fcf1 2093 $ui_comm edit separator
97bf01c4 2094 $ui_comm see end
131f503b
SP
2095 }
2096}
2097
24ac9b75
SP
2098proc do_select_commit_type {} {
2099 global commit_type selected_commit_type
2100
2101 if {$selected_commit_type eq {new}
2102 && [string match amend* $commit_type]} {
2103 create_new_commit
2104 } elseif {$selected_commit_type eq {amend}
2105 && ![string match amend* $commit_type]} {
2106 load_last_commit
2107
2108 # The amend request was rejected...
2109 #
2110 if {![string match amend* $commit_type]} {
54896cf7 2111 set selected_commit_type new
24ac9b75
SP
2112 }
2113 }
e57ca85e
SP
2114}
2115
6e27d826 2116proc do_commit {} {
ec6b424a 2117 commit_tree
6e27d826
SP
2118}
2119
51f4d16b 2120proc do_options {} {
92148d80 2121 global appname gitdir font_descs
51f4d16b
SP
2122 global repo_config global_config
2123 global repo_config_new global_config_new
2124
51f4d16b
SP
2125 array unset repo_config_new
2126 array unset global_config_new
2127 foreach name [array names repo_config] {
2128 set repo_config_new($name) $repo_config($name)
2129 }
358d8de8
SP
2130 load_config 1
2131 foreach name [array names repo_config] {
2132 switch -- $name {
2133 gui.diffcontext {continue}
2134 }
2135 set repo_config_new($name) $repo_config($name)
2136 }
51f4d16b
SP
2137 foreach name [array names global_config] {
2138 set global_config_new($name) $global_config($name)
2139 }
e01b4221
SP
2140 set reponame [lindex [file split \
2141 [file normalize [file dirname $gitdir]]] \
2142 end]
51f4d16b
SP
2143
2144 set w .options_editor
2145 toplevel $w
e01b4221 2146 wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
51f4d16b
SP
2147
2148 label $w.header -text "$appname Options" \
2149 -font font_uibold
2150 pack $w.header -side top -fill x
2151
2152 frame $w.buttons
92148d80
SP
2153 button $w.buttons.restore -text {Restore Defaults} \
2154 -font font_ui \
2155 -command do_restore_defaults
2156 pack $w.buttons.restore -side left
51f4d16b
SP
2157 button $w.buttons.save -text Save \
2158 -font font_ui \
92148d80 2159 -command [list do_save_config $w]
51f4d16b
SP
2160 pack $w.buttons.save -side right
2161 button $w.buttons.cancel -text {Cancel} \
2162 -font font_ui \
92148d80 2163 -command [list destroy $w]
51f4d16b 2164 pack $w.buttons.cancel -side right
92148d80 2165 pack $w.buttons -side bottom -fill x -pady 10 -padx 10
51f4d16b 2166
e01b4221 2167 labelframe $w.repo -text "$reponame Repository" \
92148d80 2168 -font font_ui \
51f4d16b
SP
2169 -relief raised -borderwidth 2
2170 labelframe $w.global -text {Global (All Repositories)} \
92148d80 2171 -font font_ui \
51f4d16b
SP
2172 -relief raised -borderwidth 2
2173 pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5
2174 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5
2175
2176 foreach option {
f7f8d322 2177 {b partialinclude {Allow Partially Included Files}}
358d8de8
SP
2178 {b pullsummary {Show Pull Summary}}
2179 {b trustmtime {Trust File Modification Timestamps}}
2180 {i diffcontext {Number of Diff Context Lines}}
51f4d16b 2181 } {
358d8de8
SP
2182 set type [lindex $option 0]
2183 set name [lindex $option 1]
2184 set text [lindex $option 2]
51f4d16b 2185 foreach f {repo global} {
358d8de8
SP
2186 switch $type {
2187 b {
2188 checkbutton $w.$f.$name -text $text \
2189 -variable ${f}_config_new(gui.$name) \
2190 -onvalue true \
2191 -offvalue false \
2192 -font font_ui
2193 pack $w.$f.$name -side top -anchor w
2194 }
2195 i {
2196 frame $w.$f.$name
2197 label $w.$f.$name.l -text "$text:" -font font_ui
2198 pack $w.$f.$name.l -side left -anchor w -fill x
2199 spinbox $w.$f.$name.v \
2200 -textvariable ${f}_config_new(gui.$name) \
2201 -from 1 -to 99 -increment 1 \
2202 -width 3 \
2203 -font font_ui
2204 pack $w.$f.$name.v -side right -anchor e
2205 pack $w.$f.$name -side top -anchor w -fill x
2206 }
2207 }
51f4d16b
SP
2208 }
2209 }
2210
92148d80
SP
2211 set all_fonts [lsort [font families]]
2212 foreach option $font_descs {
2213 set name [lindex $option 0]
2214 set font [lindex $option 1]
2215 set text [lindex $option 2]
2216
2217 set global_config_new(gui.$font^^family) \
2218 [font configure $font -family]
2219 set global_config_new(gui.$font^^size) \
2220 [font configure $font -size]
2221
2222 frame $w.global.$name
2223 label $w.global.$name.l -text "$text:" -font font_ui
2224 pack $w.global.$name.l -side left -anchor w -fill x
2225 eval tk_optionMenu $w.global.$name.family \
2226 global_config_new(gui.$font^^family) \
2227 $all_fonts
2228 spinbox $w.global.$name.size \
2229 -textvariable global_config_new(gui.$font^^size) \
2230 -from 2 -to 80 -increment 1 \
2231 -width 3 \
2232 -font font_ui
2233 pack $w.global.$name.size -side right -anchor e
2234 pack $w.global.$name.family -side right -anchor e
2235 pack $w.global.$name -side top -anchor w -fill x
2236 }
2237
51f4d16b
SP
2238 bind $w <Visibility> "grab $w; focus $w"
2239 bind $w <Key-Escape> "destroy $w"
e01b4221 2240 wm title $w "$appname ($reponame): Options"
51f4d16b
SP
2241 tkwait window $w
2242}
2243
92148d80 2244proc do_restore_defaults {} {
7b64d0b7 2245 global font_descs default_config repo_config
92148d80
SP
2246 global repo_config_new global_config_new
2247
2248 foreach name [array names default_config] {
2249 set repo_config_new($name) $default_config($name)
2250 set global_config_new($name) $default_config($name)
2251 }
2252
2253 foreach option $font_descs {
2254 set name [lindex $option 0]
7b64d0b7 2255 set repo_config(gui.$name) $default_config(gui.$name)
92148d80
SP
2256 }
2257 apply_config
2258
2259 foreach option $font_descs {
2260 set name [lindex $option 0]
2261 set font [lindex $option 1]
2262 set global_config_new(gui.$font^^family) \
2263 [font configure $font -family]
2264 set global_config_new(gui.$font^^size) \
2265 [font configure $font -size]
2266 }
2267}
2268
2269proc do_save_config {w} {
2270 if {[catch {save_config} err]} {
2271 error_popup "Failed to completely save options:\n\n$err"
2272 }
358d8de8 2273 reshow_diff
92148d80
SP
2274 destroy $w
2275}
2276
4aca740b
SP
2277proc do_windows_shortcut {} {
2278 global gitdir appname argv0
2279
2280 set reponame [lindex [file split \
2281 [file normalize [file dirname $gitdir]]] \
2282 end]
2283
2284 if {[catch {
2285 set desktop [exec cygpath \
2286 --windows \
2287 --absolute \
2288 --long-name \
2289 --desktop]
2290 }]} {
2291 set desktop .
2292 }
2293 set fn [tk_getSaveFile \
2294 -parent . \
2295 -title "$appname ($reponame): Create Desktop Icon" \
2296 -initialdir $desktop \
2297 -initialfile "Git $reponame.bat"]
2298 if {$fn != {}} {
2299 if {[catch {
2300 set fd [open $fn w]
2301 set sh [exec cygpath \
2302 --windows \
2303 --absolute \
2304 --long-name \
2305 /bin/sh]
2306 set me [exec cygpath \
2307 --unix \
2308 --absolute \
2309 $argv0]
2310 set gd [exec cygpath \
2311 --unix \
2312 --absolute \
2313 $gitdir]
306500fc
SP
2314 regsub -all ' $me "'\\''" me
2315 regsub -all ' $gd "'\\''" gd
4aca740b 2316 puts -nonewline $fd "\"$sh\" --login -c \""
dbccbbda
SP
2317 puts -nonewline $fd "GIT_DIR='$gd'"
2318 puts -nonewline $fd " '$me'"
4aca740b
SP
2319 puts $fd "&\""
2320 close $fd
2321 } err]} {
2322 error_popup "Cannot write script:\n\n$err"
2323 }
2324 }
2325}
2326
06c31115
SP
2327proc do_macosx_app {} {
2328 global gitdir appname argv0 env
2329
2330 set reponame [lindex [file split \
2331 [file normalize [file dirname $gitdir]]] \
2332 end]
2333
2334 set fn [tk_getSaveFile \
2335 -parent . \
2336 -title "$appname ($reponame): Create Desktop Icon" \
2337 -initialdir [file join $env(HOME) Desktop] \
2338 -initialfile "Git $reponame.app"]
2339 if {$fn != {}} {
2340 if {[catch {
2341 set Contents [file join $fn Contents]
2342 set MacOS [file join $Contents MacOS]
2343 set exe [file join $MacOS git-gui]
2344
2345 file mkdir $MacOS
2346
06c31115
SP
2347 set fd [open [file join $Contents Info.plist] w]
2348 puts $fd {<?xml version="1.0" encoding="UTF-8"?>
2349<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2350<plist version="1.0">
2351<dict>
2352 <key>CFBundleDevelopmentRegion</key>
2353 <string>English</string>
2354 <key>CFBundleExecutable</key>
2355 <string>git-gui</string>
2356 <key>CFBundleIdentifier</key>
2357 <string>org.spearce.git-gui</string>
2358 <key>CFBundleInfoDictionaryVersion</key>
2359 <string>6.0</string>
2360 <key>CFBundlePackageType</key>
2361 <string>APPL</string>
2362 <key>CFBundleSignature</key>
2363 <string>????</string>
2364 <key>CFBundleVersion</key>
2365 <string>1.0</string>
2366 <key>NSPrincipalClass</key>
2367 <string>NSApplication</string>
2368</dict>
2369</plist>}
2370 close $fd
2371
2372 set fd [open $exe w]
2373 set gd [file normalize $gitdir]
2374 set ep [file normalize [exec git --exec-path]]
2375 regsub -all ' $gd "'\\''" gd
2376 regsub -all ' $ep "'\\''" ep
2377 puts $fd "#!/bin/sh"
2378 foreach name [array names env] {
2379 if {[string match GIT_* $name]} {
2380 regsub -all ' $env($name) "'\\''" v
2381 puts $fd "export $name='$v'"
2382 }
2383 }
2384 puts $fd "export PATH='$ep':\$PATH"
2385 puts $fd "export GIT_DIR='$gd'"
2386 puts $fd "exec [file normalize $argv0]"
2387 close $fd
2388
2389 file attributes $exe -permissions u+x,g+x,o+x
2390 } err]} {
2391 error_popup "Cannot write icon:\n\n$err"
2392 }
2393 }
2394}
2395
24263b77 2396proc toggle_or_diff {w x y} {
74d18d2e 2397 global file_states file_lists current_diff ui_index ui_other
24263b77 2398 global last_clicked selected_paths
131f503b 2399
cb07fc2a
SP
2400 set pos [split [$w index @$x,$y] .]
2401 set lno [lindex $pos 0]
2402 set col [lindex $pos 1]
24263b77
SP
2403 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2404 if {$path eq {}} {
2405 set last_clicked {}
2406 return
2407 }
2408
2409 set last_clicked [list $w $lno]
2410 array unset selected_paths
2411 $ui_index tag remove in_sel 0.0 end
2412 $ui_other tag remove in_sel 0.0 end
cb07fc2a 2413
24263b77 2414 if {$col == 0} {
32e0bcab
SP
2415 if {$current_diff eq $path} {
2416 set after {reshow_diff;}
2417 } else {
2418 set after {}
2419 }
74d18d2e
SP
2420 switch -glob -- [lindex $file_states($path) 0] {
2421 A_ -
2422 AO -
2423 M_ -
dde5974e 2424 DD -
74d18d2e
SP
2425 D_ {
2426 update_indexinfo \
2427 "Removing [short_path $path] from commit" \
2428 [list $path] \
2429 [concat $after {set ui_status_value {Ready.}}]
2430 }
2431 ?? {
2432 update_index \
2433 "Including [short_path $path]" \
2434 [list $path] \
2435 [concat $after {set ui_status_value {Ready.}}]
2436 }
2437 }
24263b77 2438 } else {
03e4ec53 2439 show_diff $path $w $lno
cb07fc2a
SP
2440 }
2441}
2442
24263b77 2443proc add_one_to_selection {w x y} {
7f1df79b 2444 global file_lists
24263b77 2445 global last_clicked selected_paths
7f1df79b 2446
cb07fc2a
SP
2447 set pos [split [$w index @$x,$y] .]
2448 set lno [lindex $pos 0]
2449 set col [lindex $pos 1]
24263b77
SP
2450 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2451 if {$path eq {}} {
2452 set last_clicked {}
2453 return
2454 }
cb07fc2a 2455
24263b77
SP
2456 set last_clicked [list $w $lno]
2457 if {[catch {set in_sel $selected_paths($path)}]} {
2458 set in_sel 0
2459 }
2460 if {$in_sel} {
2461 unset selected_paths($path)
2462 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2463 } else {
2464 set selected_paths($path) 1
2465 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2466 }
2467}
2468
2469proc add_range_to_selection {w x y} {
2470 global file_lists
2471 global last_clicked selected_paths
2472
2473 if {[lindex $last_clicked 0] ne $w} {
2474 toggle_or_diff $w $x $y
2475 return
cb07fc2a 2476 }
24263b77
SP
2477
2478 set pos [split [$w index @$x,$y] .]
2479 set lno [lindex $pos 0]
2480 set lc [lindex $last_clicked 1]
2481 if {$lc < $lno} {
2482 set begin $lc
2483 set end $lno
2484 } else {
2485 set begin $lno
2486 set end $lc
2487 }
2488
2489 foreach path [lrange $file_lists($w) \
2490 [expr {$begin - 1}] \
2491 [expr {$end - 1}]] {
2492 set selected_paths($path) 1
2493 }
2494 $w tag add in_sel $begin.0 [expr {$end + 1}].0
cb07fc2a
SP
2495}
2496
2497######################################################################
2498##
92148d80 2499## config defaults
cb07fc2a 2500
00f949fb 2501set cursor_ptr arrow
b4946930
SP
2502font create font_diff -family Courier -size 10
2503font create font_ui
2504catch {
2505 label .dummy
2506 eval font configure font_ui [font actual [.dummy cget -font]]
2507 destroy .dummy
2508}
2509
92148d80
SP
2510font create font_uibold
2511font create font_diffbold
cb07fc2a 2512
16fccd7a
SP
2513set M1B M1
2514set M1T M1
043f7011 2515if {$tcl_platform(platform) eq {windows}} {
16fccd7a
SP
2516 set M1B Control
2517 set M1T Ctrl
2518} elseif {[is_MacOSX]} {
2519 set M1B M1
2520 set M1T Cmd
e210e674
SP
2521}
2522
92148d80
SP
2523proc apply_config {} {
2524 global repo_config font_descs
2525
2526 foreach option $font_descs {
2527 set name [lindex $option 0]
2528 set font [lindex $option 1]
2529 if {[catch {
2530 foreach {cn cv} $repo_config(gui.$name) {
2531 font configure $font $cn $cv
2532 }
2533 } err]} {
2534 error_popup "Invalid font specified in gui.$name:\n\n$err"
2535 }
2536 foreach {cn cv} [font configure $font] {
2537 font configure ${font}bold $cn $cv
2538 }
2539 font configure ${font}bold -weight bold
2540 }
2541}
2542
2543set default_config(gui.trustmtime) false
ebf336b9 2544set default_config(gui.pullsummary) true
f7f8d322 2545set default_config(gui.partialinclude) false
358d8de8 2546set default_config(gui.diffcontext) 5
92148d80
SP
2547set default_config(gui.fontui) [font configure font_ui]
2548set default_config(gui.fontdiff) [font configure font_diff]
2549set font_descs {
2550 {fontui font_ui {Main Font}}
2551 {fontdiff font_diff {Diff/Console Font}}
2552}
6bbd1cb9 2553load_config 0
92148d80
SP
2554apply_config
2555
2556######################################################################
2557##
2558## ui construction
2559
cb07fc2a 2560# -- Menu Bar
a49c67d1 2561#
b4946930 2562menu .mbar -tearoff 0
cb07fc2a 2563.mbar add cascade -label Project -menu .mbar.project
9861671d 2564.mbar add cascade -label Edit -menu .mbar.edit
cb07fc2a 2565.mbar add cascade -label Commit -menu .mbar.commit
4ccdab02
SP
2566if {!$single_commit} {
2567 .mbar add cascade -label Fetch -menu .mbar.fetch
2568 .mbar add cascade -label Pull -menu .mbar.pull
2569 .mbar add cascade -label Push -menu .mbar.push
2570}
cb07fc2a
SP
2571. configure -menu .mbar
2572
2573# -- Project Menu
a49c67d1 2574#
cb07fc2a 2575menu .mbar.project
6f6eed28 2576.mbar.project add command -label Visualize \
cb07fc2a 2577 -command do_gitk \
b4946930 2578 -font font_ui
4ccdab02
SP
2579if {!$single_commit} {
2580 .mbar.project add command -label {Repack Database} \
2581 -command do_repack \
2582 -font font_ui
4aca740b
SP
2583
2584 if {$tcl_platform(platform) eq {windows}} {
2585 .mbar.project add command \
2586 -label {Create Desktop Icon} \
2587 -command do_windows_shortcut \
2588 -font font_ui
06c31115
SP
2589 } elseif {[is_MacOSX]} {
2590 .mbar.project add command \
2591 -label {Create Desktop Icon} \
2592 -command do_macosx_app \
2593 -font font_ui
4aca740b 2594 }
4ccdab02 2595}
cb07fc2a
SP
2596.mbar.project add command -label Quit \
2597 -command do_quit \
e210e674 2598 -accelerator $M1T-Q \
b4946930 2599 -font font_ui
cb07fc2a 2600
9861671d
SP
2601# -- Edit Menu
2602#
2603menu .mbar.edit
2604.mbar.edit add command -label Undo \
2605 -command {catch {[focus] edit undo}} \
2606 -accelerator $M1T-Z \
b4946930 2607 -font font_ui
9861671d
SP
2608.mbar.edit add command -label Redo \
2609 -command {catch {[focus] edit redo}} \
2610 -accelerator $M1T-Y \
b4946930 2611 -font font_ui
9861671d
SP
2612.mbar.edit add separator
2613.mbar.edit add command -label Cut \
2614 -command {catch {tk_textCut [focus]}} \
2615 -accelerator $M1T-X \
b4946930 2616 -font font_ui
9861671d
SP
2617.mbar.edit add command -label Copy \
2618 -command {catch {tk_textCopy [focus]}} \
2619 -accelerator $M1T-C \
b4946930 2620 -font font_ui
9861671d
SP
2621.mbar.edit add command -label Paste \
2622 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
2623 -accelerator $M1T-V \
b4946930 2624 -font font_ui
9861671d
SP
2625.mbar.edit add command -label Delete \
2626 -command {catch {[focus] delete sel.first sel.last}} \
2627 -accelerator Del \
b4946930 2628 -font font_ui
9861671d
SP
2629.mbar.edit add separator
2630.mbar.edit add command -label {Select All} \
2631 -command {catch {[focus] tag add sel 0.0 end}} \
2632 -accelerator $M1T-A \
b4946930 2633 -font font_ui
51f4d16b
SP
2634.mbar.edit add separator
2635.mbar.edit add command -label {Options...} \
2636 -command do_options \
2637 -font font_ui
9861671d 2638
cb07fc2a 2639# -- Commit Menu
a49c67d1 2640#
cb07fc2a 2641menu .mbar.commit
24ac9b75
SP
2642
2643.mbar.commit add radiobutton \
2644 -label {New Commit} \
2645 -command do_select_commit_type \
2646 -variable selected_commit_type \
2647 -value new \
2648 -font font_ui
2649lappend disable_on_lock \
2650 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2651
2652.mbar.commit add radiobutton \
2653 -label {Amend Last Commit} \
2654 -command do_select_commit_type \
2655 -variable selected_commit_type \
2656 -value amend \
b4946930 2657 -font font_ui
e210e674
SP
2658lappend disable_on_lock \
2659 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75
SP
2660
2661.mbar.commit add separator
2662
2663.mbar.commit add command -label Rescan \
2664 -command do_rescan \
2665 -accelerator F5 \
b4946930 2666 -font font_ui
e57ca85e
SP
2667lappend disable_on_lock \
2668 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2669
1461c5f3
SP
2670.mbar.commit add command -label {Remove From Commit} \
2671 -command do_remove_selection \
2672 -font font_ui
2673lappend disable_on_lock \
2674 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2675
2676.mbar.commit add command -label {Include In Commit} \
c4ed879f
SP
2677 -command do_include_selection \
2678 -font font_ui
2679lappend disable_on_lock \
2680 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2681
1461c5f3 2682.mbar.commit add command -label {Include All} \
7fe7e733 2683 -command do_include_all \
49b86f01 2684 -accelerator $M1T-I \
b4946930 2685 -font font_ui
e210e674
SP
2686lappend disable_on_lock \
2687 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2688
1461c5f3
SP
2689.mbar.commit add separator
2690
131f503b
SP
2691.mbar.commit add command -label {Sign Off} \
2692 -command do_signoff \
e210e674 2693 -accelerator $M1T-S \
b4946930 2694 -font font_ui
24ac9b75 2695
131f503b
SP
2696.mbar.commit add command -label Commit \
2697 -command do_commit \
e210e674 2698 -accelerator $M1T-Return \
b4946930 2699 -font font_ui
e210e674
SP
2700lappend disable_on_lock \
2701 [list .mbar.commit entryconf [.mbar.commit index last] -state]
cb07fc2a 2702
a49c67d1
SP
2703# -- Transport menus
2704#
4ccdab02 2705if {!$single_commit} {
4ccdab02 2706 menu .mbar.fetch
4ccdab02 2707 menu .mbar.pull
4ccdab02
SP
2708 menu .mbar.push
2709}
8c0ce436 2710
cb07fc2a 2711# -- Main Window Layout
a49c67d1 2712#
cb07fc2a
SP
2713panedwindow .vpane -orient vertical
2714panedwindow .vpane.files -orient horizontal
6f6eed28 2715.vpane add .vpane.files -sticky nsew -height 100 -width 400
cb07fc2a
SP
2716pack .vpane -anchor n -side top -fill both -expand 1
2717
2718# -- Index File List
a49c67d1 2719#
cb07fc2a
SP
2720frame .vpane.files.index -height 100 -width 400
2721label .vpane.files.index.title -text {Modified Files} \
2722 -background green \
b4946930 2723 -font font_ui
cb07fc2a
SP
2724text $ui_index -background white -borderwidth 0 \
2725 -width 40 -height 10 \
b4946930 2726 -font font_ui \
6c6dd01a 2727 -cursor $cursor_ptr \
cb07fc2a 2728 -yscrollcommand {.vpane.files.index.sb set} \
cb07fc2a
SP
2729 -state disabled
2730scrollbar .vpane.files.index.sb -command [list $ui_index yview]
2731pack .vpane.files.index.title -side top -fill x
2732pack .vpane.files.index.sb -side right -fill y
2733pack $ui_index -side left -fill both -expand 1
2734.vpane.files add .vpane.files.index -sticky nsew
2735
2736# -- Other (Add) File List
a49c67d1 2737#
cb07fc2a
SP
2738frame .vpane.files.other -height 100 -width 100
2739label .vpane.files.other.title -text {Untracked Files} \
2740 -background red \
b4946930 2741 -font font_ui
cb07fc2a
SP
2742text $ui_other -background white -borderwidth 0 \
2743 -width 40 -height 10 \
b4946930 2744 -font font_ui \
6c6dd01a 2745 -cursor $cursor_ptr \
cb07fc2a 2746 -yscrollcommand {.vpane.files.other.sb set} \
cb07fc2a
SP
2747 -state disabled
2748scrollbar .vpane.files.other.sb -command [list $ui_other yview]
2749pack .vpane.files.other.title -side top -fill x
2750pack .vpane.files.other.sb -side right -fill y
2751pack $ui_other -side left -fill both -expand 1
2752.vpane.files add .vpane.files.other -sticky nsew
2753
24263b77
SP
2754foreach i [list $ui_index $ui_other] {
2755 $i tag conf in_diff -font font_uibold
2756 $i tag conf in_sel \
2757 -background [$i cget -foreground] \
2758 -foreground [$i cget -background]
2759}
2760unset i
131f503b 2761
0fb8f9ce 2762# -- Diff and Commit Area
a49c67d1 2763#
8009dcdc 2764frame .vpane.lower -height 300 -width 400
0fb8f9ce
SP
2765frame .vpane.lower.commarea
2766frame .vpane.lower.diff -relief sunken -borderwidth 1
2767pack .vpane.lower.commarea -side top -fill x
2768pack .vpane.lower.diff -side bottom -fill both -expand 1
2769.vpane add .vpane.lower -stick nsew
cb07fc2a
SP
2770
2771# -- Commit Area Buttons
a49c67d1 2772#
0fb8f9ce
SP
2773frame .vpane.lower.commarea.buttons
2774label .vpane.lower.commarea.buttons.l -text {} \
cb07fc2a
SP
2775 -anchor w \
2776 -justify left \
b4946930 2777 -font font_ui
0fb8f9ce
SP
2778pack .vpane.lower.commarea.buttons.l -side top -fill x
2779pack .vpane.lower.commarea.buttons -side left -fill y
131f503b 2780
0fb8f9ce 2781button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
cb07fc2a 2782 -command do_rescan \
b4946930 2783 -font font_ui
0fb8f9ce 2784pack .vpane.lower.commarea.buttons.rescan -side top -fill x
390adaea
SP
2785lappend disable_on_lock \
2786 {.vpane.lower.commarea.buttons.rescan conf -state}
131f503b 2787
7fe7e733
SP
2788button .vpane.lower.commarea.buttons.incall -text {Include All} \
2789 -command do_include_all \
b4946930 2790 -font font_ui
7fe7e733 2791pack .vpane.lower.commarea.buttons.incall -side top -fill x
390adaea
SP
2792lappend disable_on_lock \
2793 {.vpane.lower.commarea.buttons.incall conf -state}
131f503b 2794
0fb8f9ce 2795button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
131f503b 2796 -command do_signoff \
b4946930 2797 -font font_ui
0fb8f9ce 2798pack .vpane.lower.commarea.buttons.signoff -side top -fill x
131f503b 2799
0fb8f9ce 2800button .vpane.lower.commarea.buttons.commit -text {Commit} \
cb07fc2a 2801 -command do_commit \
b4946930 2802 -font font_ui
0fb8f9ce 2803pack .vpane.lower.commarea.buttons.commit -side top -fill x
390adaea
SP
2804lappend disable_on_lock \
2805 {.vpane.lower.commarea.buttons.commit conf -state}
cb07fc2a
SP
2806
2807# -- Commit Message Buffer
a49c67d1 2808#
0fb8f9ce 2809frame .vpane.lower.commarea.buffer
24ac9b75 2810frame .vpane.lower.commarea.buffer.header
0fb8f9ce 2811set ui_comm .vpane.lower.commarea.buffer.t
24ac9b75
SP
2812set ui_coml .vpane.lower.commarea.buffer.header.l
2813radiobutton .vpane.lower.commarea.buffer.header.new \
2814 -text {New Commit} \
2815 -command do_select_commit_type \
2816 -variable selected_commit_type \
2817 -value new \
2818 -font font_ui
2819lappend disable_on_lock \
2820 [list .vpane.lower.commarea.buffer.header.new conf -state]
2821radiobutton .vpane.lower.commarea.buffer.header.amend \
2822 -text {Amend Last Commit} \
2823 -command do_select_commit_type \
2824 -variable selected_commit_type \
2825 -value amend \
2826 -font font_ui
2827lappend disable_on_lock \
2828 [list .vpane.lower.commarea.buffer.header.amend conf -state]
a49c67d1 2829label $ui_coml \
cb07fc2a
SP
2830 -anchor w \
2831 -justify left \
b4946930 2832 -font font_ui
4539eacd
SP
2833proc trace_commit_type {varname args} {
2834 global ui_coml commit_type
2835 switch -glob -- $commit_type {
2836 initial {set txt {Initial Commit Message:}}
2837 amend {set txt {Amended Commit Message:}}
2838 amend-initial {set txt {Amended Initial Commit Message:}}
2839 merge {set txt {Merge Commit Message:}}
2840 * {set txt {Commit Message:}}
2841 }
2842 $ui_coml conf -text $txt
2843}
2844trace add variable commit_type write trace_commit_type
24ac9b75
SP
2845pack $ui_coml -side left -fill x
2846pack .vpane.lower.commarea.buffer.header.amend -side right
2847pack .vpane.lower.commarea.buffer.header.new -side right
2848
cb07fc2a 2849text $ui_comm -background white -borderwidth 1 \
9861671d 2850 -undo true \
b2c6fcf1 2851 -maxundo 20 \
9861671d 2852 -autoseparators true \
cb07fc2a 2853 -relief sunken \
0fb8f9ce 2854 -width 75 -height 9 -wrap none \
b4946930 2855 -font font_diff \
6c6dd01a 2856 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
390adaea
SP
2857scrollbar .vpane.lower.commarea.buffer.sby \
2858 -command [list $ui_comm yview]
24ac9b75 2859pack .vpane.lower.commarea.buffer.header -side top -fill x
0fb8f9ce 2860pack .vpane.lower.commarea.buffer.sby -side right -fill y
cb07fc2a 2861pack $ui_comm -side left -fill y
0fb8f9ce
SP
2862pack .vpane.lower.commarea.buffer -side left -fill y
2863
0e794311
SP
2864# -- Commit Message Buffer Context Menu
2865#
e8ab6446
SP
2866set ctxm .vpane.lower.commarea.buffer.ctxm
2867menu $ctxm -tearoff 0
2868$ctxm add command \
2869 -label {Cut} \
b4946930 2870 -font font_ui \
e8ab6446
SP
2871 -command {tk_textCut $ui_comm}
2872$ctxm add command \
2873 -label {Copy} \
b4946930 2874 -font font_ui \
e8ab6446
SP
2875 -command {tk_textCopy $ui_comm}
2876$ctxm add command \
2877 -label {Paste} \
b4946930 2878 -font font_ui \
e8ab6446
SP
2879 -command {tk_textPaste $ui_comm}
2880$ctxm add command \
2881 -label {Delete} \
b4946930 2882 -font font_ui \
e8ab6446
SP
2883 -command {$ui_comm delete sel.first sel.last}
2884$ctxm add separator
2885$ctxm add command \
2886 -label {Select All} \
b4946930 2887 -font font_ui \
e8ab6446
SP
2888 -command {$ui_comm tag add sel 0.0 end}
2889$ctxm add command \
2890 -label {Copy All} \
b4946930 2891 -font font_ui \
e8ab6446 2892 -command {
0e794311
SP
2893 $ui_comm tag add sel 0.0 end
2894 tk_textCopy $ui_comm
2895 $ui_comm tag remove sel 0.0 end
e8ab6446
SP
2896 }
2897$ctxm add separator
2898$ctxm add command \
2899 -label {Sign Off} \
b4946930 2900 -font font_ui \
0e794311 2901 -command do_signoff
e8ab6446 2902bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
0e794311 2903
0fb8f9ce 2904# -- Diff Header
a49c67d1 2905#
e8ab6446
SP
2906set current_diff {}
2907set diff_actions [list]
32e0bcab 2908proc trace_current_diff {varname args} {
e8ab6446
SP
2909 global current_diff diff_actions file_states
2910 if {$current_diff eq {}} {
2911 set s {}
2912 set f {}
2913 set p {}
2914 set o disabled
2915 } else {
2916 set p $current_diff
2917 set s [mapdesc [lindex $file_states($p) 0] $p]
2918 set f {File:}
2919 set p [escape_path $p]
2920 set o normal
2921 }
2922
2923 .vpane.lower.diff.header.status configure -text $s
2924 .vpane.lower.diff.header.file configure -text $f
2925 .vpane.lower.diff.header.path configure -text $p
2926 foreach w $diff_actions {
2927 uplevel #0 $w $o
2928 }
2929}
32e0bcab 2930trace add variable current_diff write trace_current_diff
e8ab6446 2931
0fb8f9ce 2932frame .vpane.lower.diff.header -background orange
e8ab6446 2933label .vpane.lower.diff.header.status \
3e7b0e1d
SP
2934 -background orange \
2935 -width $max_status_desc \
2936 -anchor w \
2937 -justify left \
2938 -font font_ui
e8ab6446 2939label .vpane.lower.diff.header.file \
0fb8f9ce 2940 -background orange \
e8ab6446
SP
2941 -anchor w \
2942 -justify left \
b4946930 2943 -font font_ui
e8ab6446 2944label .vpane.lower.diff.header.path \
0fb8f9ce 2945 -background orange \
fce89e46
SP
2946 -anchor w \
2947 -justify left \
b4946930 2948 -font font_ui
e8ab6446
SP
2949pack .vpane.lower.diff.header.status -side left
2950pack .vpane.lower.diff.header.file -side left
2951pack .vpane.lower.diff.header.path -fill x
2952set ctxm .vpane.lower.diff.header.ctxm
2953menu $ctxm -tearoff 0
2954$ctxm add command \
2955 -label {Copy} \
c11b5f20 2956 -font font_ui \
fce89e46
SP
2957 -command {
2958 clipboard clear
2959 clipboard append \
2960 -format STRING \
2961 -type STRING \
e8ab6446 2962 -- $current_diff
fce89e46 2963 }
e8ab6446
SP
2964lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2965bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
0fb8f9ce
SP
2966
2967# -- Diff Body
a49c67d1 2968#
0fb8f9ce
SP
2969frame .vpane.lower.diff.body
2970set ui_diff .vpane.lower.diff.body.t
2971text $ui_diff -background white -borderwidth 0 \
2972 -width 80 -height 15 -wrap none \
b4946930 2973 -font font_diff \
0fb8f9ce
SP
2974 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
2975 -yscrollcommand {.vpane.lower.diff.body.sby set} \
0fb8f9ce
SP
2976 -state disabled
2977scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
2978 -command [list $ui_diff xview]
2979scrollbar .vpane.lower.diff.body.sby -orient vertical \
2980 -command [list $ui_diff yview]
2981pack .vpane.lower.diff.body.sbx -side bottom -fill x
2982pack .vpane.lower.diff.body.sby -side right -fill y
2983pack $ui_diff -side left -fill both -expand 1
2984pack .vpane.lower.diff.header -side top -fill x
2985pack .vpane.lower.diff.body -side bottom -fill both -expand 1
2986
2987$ui_diff tag conf dm -foreground red
2988$ui_diff tag conf dp -foreground blue
b4946930
SP
2989$ui_diff tag conf di -foreground {#00a000}
2990$ui_diff tag conf dni -foreground {#a000a0}
2991$ui_diff tag conf da -font font_diffbold
2992$ui_diff tag conf bold -font font_diffbold
cb07fc2a 2993
0e794311
SP
2994# -- Diff Body Context Menu
2995#
e8ab6446
SP
2996set ctxm .vpane.lower.diff.body.ctxm
2997menu $ctxm -tearoff 0
2998$ctxm add command \
2999 -label {Copy} \
b4946930 3000 -font font_ui \
e8ab6446
SP
3001 -command {tk_textCopy $ui_diff}
3002lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3003$ctxm add command \
3004 -label {Select All} \
b4946930 3005 -font font_ui \
e8ab6446
SP
3006 -command {$ui_diff tag add sel 0.0 end}
3007lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3008$ctxm add command \
3009 -label {Copy All} \
b4946930 3010 -font font_ui \
e8ab6446 3011 -command {
0e794311
SP
3012 $ui_diff tag add sel 0.0 end
3013 tk_textCopy $ui_diff
3014 $ui_diff tag remove sel 0.0 end
e8ab6446
SP
3015 }
3016lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3017$ctxm add separator
3018$ctxm add command \
3019 -label {Decrease Font Size} \
b4946930
SP
3020 -font font_ui \
3021 -command {incr_font_size font_diff -1}
e8ab6446
SP
3022lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3023$ctxm add command \
3024 -label {Increase Font Size} \
b4946930
SP
3025 -font font_ui \
3026 -command {incr_font_size font_diff 1}
e8ab6446
SP
3027lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3028$ctxm add separator
3029$ctxm add command \
3030 -label {Show Less Context} \
358d8de8 3031 -font font_ui \
e8ab6446 3032 -command {if {$repo_config(gui.diffcontext) >= 2} {
358d8de8
SP
3033 incr repo_config(gui.diffcontext) -1
3034 reshow_diff
3035 }}
e8ab6446
SP
3036lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3037$ctxm add command \
3038 -label {Show More Context} \
358d8de8 3039 -font font_ui \
e8ab6446 3040 -command {
358d8de8
SP
3041 incr repo_config(gui.diffcontext)
3042 reshow_diff
e8ab6446
SP
3043 }
3044lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3045$ctxm add separator
3046$ctxm add command -label {Options...} \
8009dcdc
SP
3047 -font font_ui \
3048 -command do_options
e8ab6446 3049bind_button3 $ui_diff "tk_popup $ctxm %X %Y"
0e794311 3050
cb07fc2a 3051# -- Status Bar
e8ab6446 3052#
cb07fc2a
SP
3053set ui_status_value {Initializing...}
3054label .status -textvariable ui_status_value \
3055 -anchor w \
3056 -justify left \
3057 -borderwidth 1 \
3058 -relief sunken \
b4946930 3059 -font font_ui
cb07fc2a
SP
3060pack .status -anchor w -side bottom -fill x
3061
2d19516d 3062# -- Load geometry
e8ab6446 3063#
2d19516d 3064catch {
51f4d16b 3065set gm $repo_config(gui.geometry)
c4fe7728
SP
3066wm geometry . [lindex $gm 0]
3067.vpane sash place 0 \
3068 [lindex [.vpane sash coord 0] 0] \
3069 [lindex $gm 1]
3070.vpane.files sash place 0 \
3071 [lindex $gm 2] \
3072 [lindex [.vpane.files sash coord 0] 1]
c4fe7728 3073unset gm
390adaea 3074}
2d19516d 3075
cb07fc2a 3076# -- Key Bindings
e8ab6446 3077#
ec6b424a 3078bind $ui_comm <$M1B-Key-Return> {do_commit;break}
49b86f01
SP
3079bind $ui_comm <$M1B-Key-i> {do_include_all;break}
3080bind $ui_comm <$M1B-Key-I> {do_include_all;break}
9861671d
SP
3081bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
3082bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
3083bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
3084bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
3085bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
3086bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
3087bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3088bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
3089
3090bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
3091bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
3092bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
3093bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
3094bind $ui_diff <$M1B-Key-v> {break}
3095bind $ui_diff <$M1B-Key-V> {break}
3096bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3097bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
b2c6fcf1
SP
3098bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
3099bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
3100bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
3101bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
49b86f01 3102
07123f40
SP
3103bind . <Destroy> do_quit
3104bind all <Key-F5> do_rescan
3105bind all <$M1B-Key-r> do_rescan
3106bind all <$M1B-Key-R> do_rescan
3107bind . <$M1B-Key-s> do_signoff
3108bind . <$M1B-Key-S> do_signoff
49b86f01
SP
3109bind . <$M1B-Key-i> do_include_all
3110bind . <$M1B-Key-I> do_include_all
07123f40
SP
3111bind . <$M1B-Key-Return> do_commit
3112bind all <$M1B-Key-q> do_quit
3113bind all <$M1B-Key-Q> do_quit
3114bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
3115bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
cb07fc2a 3116foreach i [list $ui_index $ui_other] {
24263b77
SP
3117 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
3118 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
3119 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
cb07fc2a 3120}
62aac80b
SP
3121unset i
3122
3123set file_lists($ui_index) [list]
3124set file_lists($ui_other) [list]
a49c67d1
SP
3125
3126set HEAD {}
3127set PARENT {}
3128set commit_type {}
3129set empty_tree {}
e8ab6446 3130set current_diff {}
24ac9b75 3131set selected_commit_type new
cb07fc2a 3132
ec6b424a 3133wm title . "$appname ([file normalize [file dirname $gitdir]])"
cb07fc2a 3134focus -force $ui_comm
4ccdab02
SP
3135if {!$single_commit} {
3136 load_all_remotes
c1237ae2 3137 populate_fetch_menu .mbar.fetch
4ccdab02 3138 populate_pull_menu .mbar.pull
c1237ae2 3139 populate_push_menu .mbar.push
4ccdab02 3140}
53716a7b 3141lock_index begin-read
8f52548a 3142after 1 do_rescan