e90cdde96c46af611aba61299303ebee650c4fcb
1#!/bin/sh
2# Tcl ignores the next line -*- tcl -*- \
3exec wish "$0" -- "$@"
4
5set appvers {@@GITGUI_VERSION@@}
6set copyright {
7Copyright © 2006, 2007 Shawn Pearce, et. al.
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2 of the License, or
12(at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}
22
23######################################################################
24##
25## configure our library
26
27set oguilib {@@GITGUI_LIBDIR@@}
28if {[string match @@* $oguilib]} {
29 set oguilib [file join [file dirname [file normalize $argv0]] lib]
30}
31set auto_path [concat [list $oguilib] $auto_path]
32
33if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
34 unset _verbose
35 rename auto_load real__auto_load
36 proc auto_load {name args} {
37 puts stderr "auto_load $name"
38 return [uplevel 1 real__auto_load $name $args]
39 }
40 rename source real__source
41 proc source {name} {
42 puts stderr "source $name"
43 uplevel 1 real__source $name
44 }
45}
46
47######################################################################
48##
49## read only globals
50
51set _appname [lindex [file split $argv0] end]
52set _gitdir {}
53set _gitexec {}
54set _reponame {}
55set _iscygwin {}
56
57proc appname {} {
58 global _appname
59 return $_appname
60}
61
62proc gitdir {args} {
63 global _gitdir
64 if {$args eq {}} {
65 return $_gitdir
66 }
67 return [eval [concat [list file join $_gitdir] $args]]
68}
69
70proc gitexec {args} {
71 global _gitexec
72 if {$_gitexec eq {}} {
73 if {[catch {set _gitexec [git --exec-path]} err]} {
74 error "Git not installed?\n\n$err"
75 }
76 }
77 if {$args eq {}} {
78 return $_gitexec
79 }
80 return [eval [concat [list file join $_gitexec] $args]]
81}
82
83proc reponame {} {
84 global _reponame
85 return $_reponame
86}
87
88proc is_MacOSX {} {
89 global tcl_platform tk_library
90 if {[tk windowingsystem] eq {aqua}} {
91 return 1
92 }
93 return 0
94}
95
96proc is_Windows {} {
97 global tcl_platform
98 if {$tcl_platform(platform) eq {windows}} {
99 return 1
100 }
101 return 0
102}
103
104proc is_Cygwin {} {
105 global tcl_platform _iscygwin
106 if {$_iscygwin eq {}} {
107 if {$tcl_platform(platform) eq {windows}} {
108 if {[catch {set p [exec cygpath --windir]} err]} {
109 set _iscygwin 0
110 } else {
111 set _iscygwin 1
112 }
113 } else {
114 set _iscygwin 0
115 }
116 }
117 return $_iscygwin
118}
119
120proc is_enabled {option} {
121 global enabled_options
122 if {[catch {set on $enabled_options($option)}]} {return 0}
123 return $on
124}
125
126proc enable_option {option} {
127 global enabled_options
128 set enabled_options($option) 1
129}
130
131proc disable_option {option} {
132 global enabled_options
133 set enabled_options($option) 0
134}
135
136######################################################################
137##
138## config
139
140proc is_many_config {name} {
141 switch -glob -- $name {
142 remote.*.fetch -
143 remote.*.push
144 {return 1}
145 *
146 {return 0}
147 }
148}
149
150proc is_config_true {name} {
151 global repo_config
152 if {[catch {set v $repo_config($name)}]} {
153 return 0
154 } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
155 return 1
156 } else {
157 return 0
158 }
159}
160
161proc load_config {include_global} {
162 global repo_config global_config default_config
163
164 array unset global_config
165 if {$include_global} {
166 catch {
167 set fd_rc [open "| git config --global --list" r]
168 while {[gets $fd_rc line] >= 0} {
169 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
170 if {[is_many_config $name]} {
171 lappend global_config($name) $value
172 } else {
173 set global_config($name) $value
174 }
175 }
176 }
177 close $fd_rc
178 }
179 }
180
181 array unset repo_config
182 catch {
183 set fd_rc [open "| git config --list" r]
184 while {[gets $fd_rc line] >= 0} {
185 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
186 if {[is_many_config $name]} {
187 lappend repo_config($name) $value
188 } else {
189 set repo_config($name) $value
190 }
191 }
192 }
193 close $fd_rc
194 }
195
196 foreach name [array names default_config] {
197 if {[catch {set v $global_config($name)}]} {
198 set global_config($name) $default_config($name)
199 }
200 if {[catch {set v $repo_config($name)}]} {
201 set repo_config($name) $default_config($name)
202 }
203 }
204}
205
206######################################################################
207##
208## handy utils
209
210proc git {args} {
211 return [eval exec git $args]
212}
213
214auto_load tk_optionMenu
215rename tk_optionMenu real__tkOptionMenu
216proc tk_optionMenu {w varName args} {
217 set m [eval real__tkOptionMenu $w $varName $args]
218 $m configure -font font_ui
219 $w configure -font font_ui
220 return $m
221}
222
223######################################################################
224##
225## version check
226
227if {{--version} eq $argv || {version} eq $argv} {
228 puts "git-gui version $appvers"
229 exit
230}
231
232set req_maj 1
233set req_min 5
234
235if {[catch {set v [git --version]} err]} {
236 catch {wm withdraw .}
237 error_popup "Cannot determine Git version:
238
239$err
240
241[appname] requires Git $req_maj.$req_min or later."
242 exit 1
243}
244if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
245 if {$act_maj < $req_maj
246 || ($act_maj == $req_maj && $act_min < $req_min)} {
247 catch {wm withdraw .}
248 error_popup "[appname] requires Git $req_maj.$req_min or later.
249
250You are using $v."
251 exit 1
252 }
253} else {
254 catch {wm withdraw .}
255 error_popup "Cannot parse Git version string:\n\n$v"
256 exit 1
257}
258unset -nocomplain v _junk act_maj act_min req_maj req_min
259
260######################################################################
261##
262## repository setup
263
264if {[catch {
265 set _gitdir $env(GIT_DIR)
266 set _prefix {}
267 }]
268 && [catch {
269 set _gitdir [git rev-parse --git-dir]
270 set _prefix [git rev-parse --show-prefix]
271 } err]} {
272 catch {wm withdraw .}
273 error_popup "Cannot find the git directory:\n\n$err"
274 exit 1
275}
276if {![file isdirectory $_gitdir] && [is_Cygwin]} {
277 catch {set _gitdir [exec cygpath --unix $_gitdir]}
278}
279if {![file isdirectory $_gitdir]} {
280 catch {wm withdraw .}
281 error_popup "Git directory not found:\n\n$_gitdir"
282 exit 1
283}
284if {[lindex [file split $_gitdir] end] ne {.git}} {
285 catch {wm withdraw .}
286 error_popup "Cannot use funny .git directory:\n\n$_gitdir"
287 exit 1
288}
289if {[catch {cd [file dirname $_gitdir]} err]} {
290 catch {wm withdraw .}
291 error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
292 exit 1
293}
294set _reponame [lindex [file split \
295 [file normalize [file dirname $_gitdir]]] \
296 end]
297
298######################################################################
299##
300## global init
301
302set current_diff_path {}
303set current_diff_side {}
304set diff_actions [list]
305set ui_status_value {Initializing...}
306
307set HEAD {}
308set PARENT {}
309set MERGE_HEAD [list]
310set commit_type {}
311set empty_tree {}
312set current_branch {}
313set current_diff_path {}
314set selected_commit_type new
315
316######################################################################
317##
318## task management
319
320set rescan_active 0
321set diff_active 0
322set last_clicked {}
323
324set disable_on_lock [list]
325set index_lock_type none
326
327proc lock_index {type} {
328 global index_lock_type disable_on_lock
329
330 if {$index_lock_type eq {none}} {
331 set index_lock_type $type
332 foreach w $disable_on_lock {
333 uplevel #0 $w disabled
334 }
335 return 1
336 } elseif {$index_lock_type eq "begin-$type"} {
337 set index_lock_type $type
338 return 1
339 }
340 return 0
341}
342
343proc unlock_index {} {
344 global index_lock_type disable_on_lock
345
346 set index_lock_type none
347 foreach w $disable_on_lock {
348 uplevel #0 $w normal
349 }
350}
351
352######################################################################
353##
354## status
355
356proc repository_state {ctvar hdvar mhvar} {
357 global current_branch
358 upvar $ctvar ct $hdvar hd $mhvar mh
359
360 set mh [list]
361
362 if {[catch {set current_branch [git symbolic-ref HEAD]}]} {
363 set current_branch {}
364 } else {
365 regsub ^refs/((heads|tags|remotes)/)? \
366 $current_branch \
367 {} \
368 current_branch
369 }
370
371 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
372 set hd {}
373 set ct initial
374 return
375 }
376
377 set merge_head [gitdir MERGE_HEAD]
378 if {[file exists $merge_head]} {
379 set ct merge
380 set fd_mh [open $merge_head r]
381 while {[gets $fd_mh line] >= 0} {
382 lappend mh $line
383 }
384 close $fd_mh
385 return
386 }
387
388 set ct normal
389}
390
391proc PARENT {} {
392 global PARENT empty_tree
393
394 set p [lindex $PARENT 0]
395 if {$p ne {}} {
396 return $p
397 }
398 if {$empty_tree eq {}} {
399 set empty_tree [git mktree << {}]
400 }
401 return $empty_tree
402}
403
404proc rescan {after {honor_trustmtime 1}} {
405 global HEAD PARENT MERGE_HEAD commit_type
406 global ui_index ui_workdir ui_status_value ui_comm
407 global rescan_active file_states
408 global repo_config
409
410 if {$rescan_active > 0 || ![lock_index read]} return
411
412 repository_state newType newHEAD newMERGE_HEAD
413 if {[string match amend* $commit_type]
414 && $newType eq {normal}
415 && $newHEAD eq $HEAD} {
416 } else {
417 set HEAD $newHEAD
418 set PARENT $newHEAD
419 set MERGE_HEAD $newMERGE_HEAD
420 set commit_type $newType
421 }
422
423 array unset file_states
424
425 if {![$ui_comm edit modified]
426 || [string trim [$ui_comm get 0.0 end]] eq {}} {
427 if {[load_message GITGUI_MSG]} {
428 } elseif {[load_message MERGE_MSG]} {
429 } elseif {[load_message SQUASH_MSG]} {
430 }
431 $ui_comm edit reset
432 $ui_comm edit modified false
433 }
434
435 if {[is_enabled branch]} {
436 load_all_heads
437 populate_branch_menu
438 }
439
440 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
441 rescan_stage2 {} $after
442 } else {
443 set rescan_active 1
444 set ui_status_value {Refreshing file status...}
445 set cmd [list git update-index]
446 lappend cmd -q
447 lappend cmd --unmerged
448 lappend cmd --ignore-missing
449 lappend cmd --refresh
450 set fd_rf [open "| $cmd" r]
451 fconfigure $fd_rf -blocking 0 -translation binary
452 fileevent $fd_rf readable \
453 [list rescan_stage2 $fd_rf $after]
454 }
455}
456
457proc rescan_stage2 {fd after} {
458 global ui_status_value
459 global rescan_active buf_rdi buf_rdf buf_rlo
460
461 if {$fd ne {}} {
462 read $fd
463 if {![eof $fd]} return
464 close $fd
465 }
466
467 set ls_others [list | git ls-files --others -z \
468 --exclude-per-directory=.gitignore]
469 set info_exclude [gitdir info exclude]
470 if {[file readable $info_exclude]} {
471 lappend ls_others "--exclude-from=$info_exclude"
472 }
473
474 set buf_rdi {}
475 set buf_rdf {}
476 set buf_rlo {}
477
478 set rescan_active 3
479 set ui_status_value {Scanning for modified files ...}
480 set fd_di [open "| git diff-index --cached -z [PARENT]" r]
481 set fd_df [open "| git diff-files -z" r]
482 set fd_lo [open $ls_others r]
483
484 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
485 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
486 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
487 fileevent $fd_di readable [list read_diff_index $fd_di $after]
488 fileevent $fd_df readable [list read_diff_files $fd_df $after]
489 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
490}
491
492proc load_message {file} {
493 global ui_comm
494
495 set f [gitdir $file]
496 if {[file isfile $f]} {
497 if {[catch {set fd [open $f r]}]} {
498 return 0
499 }
500 set content [string trim [read $fd]]
501 close $fd
502 regsub -all -line {[ \r\t]+$} $content {} content
503 $ui_comm delete 0.0 end
504 $ui_comm insert end $content
505 return 1
506 }
507 return 0
508}
509
510proc read_diff_index {fd after} {
511 global buf_rdi
512
513 append buf_rdi [read $fd]
514 set c 0
515 set n [string length $buf_rdi]
516 while {$c < $n} {
517 set z1 [string first "\0" $buf_rdi $c]
518 if {$z1 == -1} break
519 incr z1
520 set z2 [string first "\0" $buf_rdi $z1]
521 if {$z2 == -1} break
522
523 incr c
524 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
525 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
526 merge_state \
527 [encoding convertfrom $p] \
528 [lindex $i 4]? \
529 [list [lindex $i 0] [lindex $i 2]] \
530 [list]
531 set c $z2
532 incr c
533 }
534 if {$c < $n} {
535 set buf_rdi [string range $buf_rdi $c end]
536 } else {
537 set buf_rdi {}
538 }
539
540 rescan_done $fd buf_rdi $after
541}
542
543proc read_diff_files {fd after} {
544 global buf_rdf
545
546 append buf_rdf [read $fd]
547 set c 0
548 set n [string length $buf_rdf]
549 while {$c < $n} {
550 set z1 [string first "\0" $buf_rdf $c]
551 if {$z1 == -1} break
552 incr z1
553 set z2 [string first "\0" $buf_rdf $z1]
554 if {$z2 == -1} break
555
556 incr c
557 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
558 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
559 merge_state \
560 [encoding convertfrom $p] \
561 ?[lindex $i 4] \
562 [list] \
563 [list [lindex $i 0] [lindex $i 2]]
564 set c $z2
565 incr c
566 }
567 if {$c < $n} {
568 set buf_rdf [string range $buf_rdf $c end]
569 } else {
570 set buf_rdf {}
571 }
572
573 rescan_done $fd buf_rdf $after
574}
575
576proc read_ls_others {fd after} {
577 global buf_rlo
578
579 append buf_rlo [read $fd]
580 set pck [split $buf_rlo "\0"]
581 set buf_rlo [lindex $pck end]
582 foreach p [lrange $pck 0 end-1] {
583 merge_state [encoding convertfrom $p] ?O
584 }
585 rescan_done $fd buf_rlo $after
586}
587
588proc rescan_done {fd buf after} {
589 global rescan_active current_diff_path
590 global file_states repo_config
591 upvar $buf to_clear
592
593 if {![eof $fd]} return
594 set to_clear {}
595 close $fd
596 if {[incr rescan_active -1] > 0} return
597
598 prune_selection
599 unlock_index
600 display_all_files
601 if {$current_diff_path ne {}} reshow_diff
602 uplevel #0 $after
603}
604
605proc prune_selection {} {
606 global file_states selected_paths
607
608 foreach path [array names selected_paths] {
609 if {[catch {set still_here $file_states($path)}]} {
610 unset selected_paths($path)
611 }
612 }
613}
614
615######################################################################
616##
617## ui helpers
618
619proc mapicon {w state path} {
620 global all_icons
621
622 if {[catch {set r $all_icons($state$w)}]} {
623 puts "error: no icon for $w state={$state} $path"
624 return file_plain
625 }
626 return $r
627}
628
629proc mapdesc {state path} {
630 global all_descs
631
632 if {[catch {set r $all_descs($state)}]} {
633 puts "error: no desc for state={$state} $path"
634 return $state
635 }
636 return $r
637}
638
639proc escape_path {path} {
640 regsub -all {\\} $path "\\\\" path
641 regsub -all "\n" $path "\\n" path
642 return $path
643}
644
645proc short_path {path} {
646 return [escape_path [lindex [file split $path] end]]
647}
648
649set next_icon_id 0
650set null_sha1 [string repeat 0 40]
651
652proc merge_state {path new_state {head_info {}} {index_info {}}} {
653 global file_states next_icon_id null_sha1
654
655 set s0 [string index $new_state 0]
656 set s1 [string index $new_state 1]
657
658 if {[catch {set info $file_states($path)}]} {
659 set state __
660 set icon n[incr next_icon_id]
661 } else {
662 set state [lindex $info 0]
663 set icon [lindex $info 1]
664 if {$head_info eq {}} {set head_info [lindex $info 2]}
665 if {$index_info eq {}} {set index_info [lindex $info 3]}
666 }
667
668 if {$s0 eq {?}} {set s0 [string index $state 0]} \
669 elseif {$s0 eq {_}} {set s0 _}
670
671 if {$s1 eq {?}} {set s1 [string index $state 1]} \
672 elseif {$s1 eq {_}} {set s1 _}
673
674 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
675 set head_info [list 0 $null_sha1]
676 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
677 && $head_info eq {}} {
678 set head_info $index_info
679 }
680
681 set file_states($path) [list $s0$s1 $icon \
682 $head_info $index_info \
683 ]
684 return $state
685}
686
687proc display_file_helper {w path icon_name old_m new_m} {
688 global file_lists
689
690 if {$new_m eq {_}} {
691 set lno [lsearch -sorted -exact $file_lists($w) $path]
692 if {$lno >= 0} {
693 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
694 incr lno
695 $w conf -state normal
696 $w delete $lno.0 [expr {$lno + 1}].0
697 $w conf -state disabled
698 }
699 } elseif {$old_m eq {_} && $new_m ne {_}} {
700 lappend file_lists($w) $path
701 set file_lists($w) [lsort -unique $file_lists($w)]
702 set lno [lsearch -sorted -exact $file_lists($w) $path]
703 incr lno
704 $w conf -state normal
705 $w image create $lno.0 \
706 -align center -padx 5 -pady 1 \
707 -name $icon_name \
708 -image [mapicon $w $new_m $path]
709 $w insert $lno.1 "[escape_path $path]\n"
710 $w conf -state disabled
711 } elseif {$old_m ne $new_m} {
712 $w conf -state normal
713 $w image conf $icon_name -image [mapicon $w $new_m $path]
714 $w conf -state disabled
715 }
716}
717
718proc display_file {path state} {
719 global file_states selected_paths
720 global ui_index ui_workdir
721
722 set old_m [merge_state $path $state]
723 set s $file_states($path)
724 set new_m [lindex $s 0]
725 set icon_name [lindex $s 1]
726
727 set o [string index $old_m 0]
728 set n [string index $new_m 0]
729 if {$o eq {U}} {
730 set o _
731 }
732 if {$n eq {U}} {
733 set n _
734 }
735 display_file_helper $ui_index $path $icon_name $o $n
736
737 if {[string index $old_m 0] eq {U}} {
738 set o U
739 } else {
740 set o [string index $old_m 1]
741 }
742 if {[string index $new_m 0] eq {U}} {
743 set n U
744 } else {
745 set n [string index $new_m 1]
746 }
747 display_file_helper $ui_workdir $path $icon_name $o $n
748
749 if {$new_m eq {__}} {
750 unset file_states($path)
751 catch {unset selected_paths($path)}
752 }
753}
754
755proc display_all_files_helper {w path icon_name m} {
756 global file_lists
757
758 lappend file_lists($w) $path
759 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
760 $w image create end \
761 -align center -padx 5 -pady 1 \
762 -name $icon_name \
763 -image [mapicon $w $m $path]
764 $w insert end "[escape_path $path]\n"
765}
766
767proc display_all_files {} {
768 global ui_index ui_workdir
769 global file_states file_lists
770 global last_clicked
771
772 $ui_index conf -state normal
773 $ui_workdir conf -state normal
774
775 $ui_index delete 0.0 end
776 $ui_workdir delete 0.0 end
777 set last_clicked {}
778
779 set file_lists($ui_index) [list]
780 set file_lists($ui_workdir) [list]
781
782 foreach path [lsort [array names file_states]] {
783 set s $file_states($path)
784 set m [lindex $s 0]
785 set icon_name [lindex $s 1]
786
787 set s [string index $m 0]
788 if {$s ne {U} && $s ne {_}} {
789 display_all_files_helper $ui_index $path \
790 $icon_name $s
791 }
792
793 if {[string index $m 0] eq {U}} {
794 set s U
795 } else {
796 set s [string index $m 1]
797 }
798 if {$s ne {_}} {
799 display_all_files_helper $ui_workdir $path \
800 $icon_name $s
801 }
802 }
803
804 $ui_index conf -state disabled
805 $ui_workdir conf -state disabled
806}
807
808######################################################################
809##
810## icons
811
812set filemask {
813#define mask_width 14
814#define mask_height 15
815static unsigned char mask_bits[] = {
816 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
817 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
818 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
819}
820
821image create bitmap file_plain -background white -foreground black -data {
822#define plain_width 14
823#define plain_height 15
824static unsigned char plain_bits[] = {
825 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
826 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
827 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
828} -maskdata $filemask
829
830image create bitmap file_mod -background white -foreground blue -data {
831#define mod_width 14
832#define mod_height 15
833static unsigned char mod_bits[] = {
834 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
835 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
836 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
837} -maskdata $filemask
838
839image create bitmap file_fulltick -background white -foreground "#007000" -data {
840#define file_fulltick_width 14
841#define file_fulltick_height 15
842static unsigned char file_fulltick_bits[] = {
843 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
844 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
845 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
846} -maskdata $filemask
847
848image create bitmap file_parttick -background white -foreground "#005050" -data {
849#define parttick_width 14
850#define parttick_height 15
851static unsigned char parttick_bits[] = {
852 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
853 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
854 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
855} -maskdata $filemask
856
857image create bitmap file_question -background white -foreground black -data {
858#define file_question_width 14
859#define file_question_height 15
860static unsigned char file_question_bits[] = {
861 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
862 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
863 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
864} -maskdata $filemask
865
866image create bitmap file_removed -background white -foreground red -data {
867#define file_removed_width 14
868#define file_removed_height 15
869static unsigned char file_removed_bits[] = {
870 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
871 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
872 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
873} -maskdata $filemask
874
875image create bitmap file_merge -background white -foreground blue -data {
876#define file_merge_width 14
877#define file_merge_height 15
878static unsigned char file_merge_bits[] = {
879 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
880 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
881 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
882} -maskdata $filemask
883
884set file_dir_data {
885#define file_width 18
886#define file_height 18
887static unsigned char file_bits[] = {
888 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
889 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
890 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
891 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
892 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
893}
894image create bitmap file_dir -background white -foreground blue \
895 -data $file_dir_data -maskdata $file_dir_data
896unset file_dir_data
897
898set file_uplevel_data {
899#define up_width 15
900#define up_height 15
901static unsigned char up_bits[] = {
902 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
903 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
904 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
905}
906image create bitmap file_uplevel -background white -foreground red \
907 -data $file_uplevel_data -maskdata $file_uplevel_data
908unset file_uplevel_data
909
910set ui_index .vpane.files.index.list
911set ui_workdir .vpane.files.workdir.list
912
913set all_icons(_$ui_index) file_plain
914set all_icons(A$ui_index) file_fulltick
915set all_icons(M$ui_index) file_fulltick
916set all_icons(D$ui_index) file_removed
917set all_icons(U$ui_index) file_merge
918
919set all_icons(_$ui_workdir) file_plain
920set all_icons(M$ui_workdir) file_mod
921set all_icons(D$ui_workdir) file_question
922set all_icons(U$ui_workdir) file_merge
923set all_icons(O$ui_workdir) file_plain
924
925set max_status_desc 0
926foreach i {
927 {__ "Unmodified"}
928
929 {_M "Modified, not staged"}
930 {M_ "Staged for commit"}
931 {MM "Portions staged for commit"}
932 {MD "Staged for commit, missing"}
933
934 {_O "Untracked, not staged"}
935 {A_ "Staged for commit"}
936 {AM "Portions staged for commit"}
937 {AD "Staged for commit, missing"}
938
939 {_D "Missing"}
940 {D_ "Staged for removal"}
941 {DO "Staged for removal, still present"}
942
943 {U_ "Requires merge resolution"}
944 {UU "Requires merge resolution"}
945 {UM "Requires merge resolution"}
946 {UD "Requires merge resolution"}
947 } {
948 if {$max_status_desc < [string length [lindex $i 1]]} {
949 set max_status_desc [string length [lindex $i 1]]
950 }
951 set all_descs([lindex $i 0]) [lindex $i 1]
952}
953unset i
954
955######################################################################
956##
957## util
958
959proc bind_button3 {w cmd} {
960 bind $w <Any-Button-3> $cmd
961 if {[is_MacOSX]} {
962 bind $w <Control-Button-1> $cmd
963 }
964}
965
966proc scrollbar2many {list mode args} {
967 foreach w $list {eval $w $mode $args}
968}
969
970proc many2scrollbar {list mode sb top bottom} {
971 $sb set $top $bottom
972 foreach w $list {$w $mode moveto $top}
973}
974
975proc incr_font_size {font {amt 1}} {
976 set sz [font configure $font -size]
977 incr sz $amt
978 font configure $font -size $sz
979 font configure ${font}bold -size $sz
980}
981
982######################################################################
983##
984## ui commands
985
986set starting_gitk_msg {Starting gitk... please wait...}
987
988proc do_gitk {revs} {
989 global env ui_status_value starting_gitk_msg
990
991 # -- Always start gitk through whatever we were loaded with. This
992 # lets us bypass using shell process on Windows systems.
993 #
994 set cmd [list [info nameofexecutable]]
995 lappend cmd [gitexec gitk]
996 if {$revs ne {}} {
997 append cmd { }
998 append cmd $revs
999 }
1000
1001 if {[catch {eval exec $cmd &} err]} {
1002 error_popup "Failed to start gitk:\n\n$err"
1003 } else {
1004 set ui_status_value $starting_gitk_msg
1005 after 10000 {
1006 if {$ui_status_value eq $starting_gitk_msg} {
1007 set ui_status_value {Ready.}
1008 }
1009 }
1010 }
1011}
1012
1013set is_quitting 0
1014
1015proc do_quit {} {
1016 global ui_comm is_quitting repo_config commit_type
1017
1018 if {$is_quitting} return
1019 set is_quitting 1
1020
1021 if {[winfo exists $ui_comm]} {
1022 # -- Stash our current commit buffer.
1023 #
1024 set save [gitdir GITGUI_MSG]
1025 set msg [string trim [$ui_comm get 0.0 end]]
1026 regsub -all -line {[ \r\t]+$} $msg {} msg
1027 if {(![string match amend* $commit_type]
1028 || [$ui_comm edit modified])
1029 && $msg ne {}} {
1030 catch {
1031 set fd [open $save w]
1032 puts -nonewline $fd $msg
1033 close $fd
1034 }
1035 } else {
1036 catch {file delete $save}
1037 }
1038
1039 # -- Stash our current window geometry into this repository.
1040 #
1041 set cfg_geometry [list]
1042 lappend cfg_geometry [wm geometry .]
1043 lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1044 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1045 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1046 set rc_geometry {}
1047 }
1048 if {$cfg_geometry ne $rc_geometry} {
1049 catch {git config gui.geometry $cfg_geometry}
1050 }
1051 }
1052
1053 destroy .
1054}
1055
1056proc do_rescan {} {
1057 rescan {set ui_status_value {Ready.}}
1058}
1059
1060proc do_commit {} {
1061 commit_tree
1062}
1063
1064proc toggle_or_diff {w x y} {
1065 global file_states file_lists current_diff_path ui_index ui_workdir
1066 global last_clicked selected_paths
1067
1068 set pos [split [$w index @$x,$y] .]
1069 set lno [lindex $pos 0]
1070 set col [lindex $pos 1]
1071 set path [lindex $file_lists($w) [expr {$lno - 1}]]
1072 if {$path eq {}} {
1073 set last_clicked {}
1074 return
1075 }
1076
1077 set last_clicked [list $w $lno]
1078 array unset selected_paths
1079 $ui_index tag remove in_sel 0.0 end
1080 $ui_workdir tag remove in_sel 0.0 end
1081
1082 if {$col == 0} {
1083 if {$current_diff_path eq $path} {
1084 set after {reshow_diff;}
1085 } else {
1086 set after {}
1087 }
1088 if {$w eq $ui_index} {
1089 update_indexinfo \
1090 "Unstaging [short_path $path] from commit" \
1091 [list $path] \
1092 [concat $after {set ui_status_value {Ready.}}]
1093 } elseif {$w eq $ui_workdir} {
1094 update_index \
1095 "Adding [short_path $path]" \
1096 [list $path] \
1097 [concat $after {set ui_status_value {Ready.}}]
1098 }
1099 } else {
1100 show_diff $path $w $lno
1101 }
1102}
1103
1104proc add_one_to_selection {w x y} {
1105 global file_lists last_clicked selected_paths
1106
1107 set lno [lindex [split [$w index @$x,$y] .] 0]
1108 set path [lindex $file_lists($w) [expr {$lno - 1}]]
1109 if {$path eq {}} {
1110 set last_clicked {}
1111 return
1112 }
1113
1114 if {$last_clicked ne {}
1115 && [lindex $last_clicked 0] ne $w} {
1116 array unset selected_paths
1117 [lindex $last_clicked 0] tag remove in_sel 0.0 end
1118 }
1119
1120 set last_clicked [list $w $lno]
1121 if {[catch {set in_sel $selected_paths($path)}]} {
1122 set in_sel 0
1123 }
1124 if {$in_sel} {
1125 unset selected_paths($path)
1126 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
1127 } else {
1128 set selected_paths($path) 1
1129 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1130 }
1131}
1132
1133proc add_range_to_selection {w x y} {
1134 global file_lists last_clicked selected_paths
1135
1136 if {[lindex $last_clicked 0] ne $w} {
1137 toggle_or_diff $w $x $y
1138 return
1139 }
1140
1141 set lno [lindex [split [$w index @$x,$y] .] 0]
1142 set lc [lindex $last_clicked 1]
1143 if {$lc < $lno} {
1144 set begin $lc
1145 set end $lno
1146 } else {
1147 set begin $lno
1148 set end $lc
1149 }
1150
1151 foreach path [lrange $file_lists($w) \
1152 [expr {$begin - 1}] \
1153 [expr {$end - 1}]] {
1154 set selected_paths($path) 1
1155 }
1156 $w tag add in_sel $begin.0 [expr {$end + 1}].0
1157}
1158
1159######################################################################
1160##
1161## config defaults
1162
1163set cursor_ptr arrow
1164font create font_diff -family Courier -size 10
1165font create font_ui
1166catch {
1167 label .dummy
1168 eval font configure font_ui [font actual [.dummy cget -font]]
1169 destroy .dummy
1170}
1171
1172font create font_uibold
1173font create font_diffbold
1174
1175foreach class {Button Checkbutton Entry Label
1176 Labelframe Listbox Menu Message
1177 Radiobutton Text} {
1178 option add *$class.font font_ui
1179}
1180unset class
1181
1182if {[is_Windows]} {
1183 set M1B Control
1184 set M1T Ctrl
1185} elseif {[is_MacOSX]} {
1186 set M1B M1
1187 set M1T Cmd
1188} else {
1189 set M1B M1
1190 set M1T M1
1191}
1192
1193proc apply_config {} {
1194 global repo_config font_descs
1195
1196 foreach option $font_descs {
1197 set name [lindex $option 0]
1198 set font [lindex $option 1]
1199 if {[catch {
1200 foreach {cn cv} $repo_config(gui.$name) {
1201 font configure $font $cn $cv
1202 }
1203 } err]} {
1204 error_popup "Invalid font specified in gui.$name:\n\n$err"
1205 }
1206 foreach {cn cv} [font configure $font] {
1207 font configure ${font}bold $cn $cv
1208 }
1209 font configure ${font}bold -weight bold
1210 }
1211}
1212
1213set default_config(merge.summary) false
1214set default_config(merge.verbosity) 2
1215set default_config(user.name) {}
1216set default_config(user.email) {}
1217
1218set default_config(gui.trustmtime) false
1219set default_config(gui.diffcontext) 5
1220set default_config(gui.newbranchtemplate) {}
1221set default_config(gui.fontui) [font configure font_ui]
1222set default_config(gui.fontdiff) [font configure font_diff]
1223set font_descs {
1224 {fontui font_ui {Main Font}}
1225 {fontdiff font_diff {Diff/Console Font}}
1226}
1227load_config 0
1228apply_config
1229
1230######################################################################
1231##
1232## feature option selection
1233
1234if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
1235 unset _junk
1236} else {
1237 set subcommand gui
1238}
1239if {$subcommand eq {gui.sh}} {
1240 set subcommand gui
1241}
1242if {$subcommand eq {gui} && [llength $argv] > 0} {
1243 set subcommand [lindex $argv 0]
1244 set argv [lrange $argv 1 end]
1245}
1246
1247enable_option multicommit
1248enable_option branch
1249enable_option transport
1250
1251switch -- $subcommand {
1252browser -
1253blame {
1254 disable_option multicommit
1255 disable_option branch
1256 disable_option transport
1257}
1258citool {
1259 enable_option singlecommit
1260
1261 disable_option multicommit
1262 disable_option branch
1263 disable_option transport
1264}
1265}
1266
1267######################################################################
1268##
1269## ui construction
1270
1271set ui_comm {}
1272
1273# -- Menu Bar
1274#
1275menu .mbar -tearoff 0
1276.mbar add cascade -label Repository -menu .mbar.repository
1277.mbar add cascade -label Edit -menu .mbar.edit
1278if {[is_enabled branch]} {
1279 .mbar add cascade -label Branch -menu .mbar.branch
1280}
1281if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1282 .mbar add cascade -label Commit -menu .mbar.commit
1283}
1284if {[is_enabled transport]} {
1285 .mbar add cascade -label Merge -menu .mbar.merge
1286 .mbar add cascade -label Fetch -menu .mbar.fetch
1287 .mbar add cascade -label Push -menu .mbar.push
1288}
1289. configure -menu .mbar
1290
1291# -- Repository Menu
1292#
1293menu .mbar.repository
1294
1295.mbar.repository add command \
1296 -label {Browse Current Branch} \
1297 -command {browser::new $current_branch}
1298trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1299.mbar.repository add separator
1300
1301.mbar.repository add command \
1302 -label {Visualize Current Branch} \
1303 -command {do_gitk $current_branch}
1304trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1305.mbar.repository add command \
1306 -label {Visualize All Branches} \
1307 -command {do_gitk --all}
1308.mbar.repository add separator
1309
1310if {[is_enabled multicommit]} {
1311 .mbar.repository add command -label {Database Statistics} \
1312 -command do_stats
1313
1314 .mbar.repository add command -label {Compress Database} \
1315 -command do_gc
1316
1317 .mbar.repository add command -label {Verify Database} \
1318 -command do_fsck_objects
1319
1320 .mbar.repository add separator
1321
1322 if {[is_Cygwin]} {
1323 .mbar.repository add command \
1324 -label {Create Desktop Icon} \
1325 -command do_cygwin_shortcut
1326 } elseif {[is_Windows]} {
1327 .mbar.repository add command \
1328 -label {Create Desktop Icon} \
1329 -command do_windows_shortcut
1330 } elseif {[is_MacOSX]} {
1331 .mbar.repository add command \
1332 -label {Create Desktop Icon} \
1333 -command do_macosx_app
1334 }
1335}
1336
1337.mbar.repository add command -label Quit \
1338 -command do_quit \
1339 -accelerator $M1T-Q
1340
1341# -- Edit Menu
1342#
1343menu .mbar.edit
1344.mbar.edit add command -label Undo \
1345 -command {catch {[focus] edit undo}} \
1346 -accelerator $M1T-Z
1347.mbar.edit add command -label Redo \
1348 -command {catch {[focus] edit redo}} \
1349 -accelerator $M1T-Y
1350.mbar.edit add separator
1351.mbar.edit add command -label Cut \
1352 -command {catch {tk_textCut [focus]}} \
1353 -accelerator $M1T-X
1354.mbar.edit add command -label Copy \
1355 -command {catch {tk_textCopy [focus]}} \
1356 -accelerator $M1T-C
1357.mbar.edit add command -label Paste \
1358 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
1359 -accelerator $M1T-V
1360.mbar.edit add command -label Delete \
1361 -command {catch {[focus] delete sel.first sel.last}} \
1362 -accelerator Del
1363.mbar.edit add separator
1364.mbar.edit add command -label {Select All} \
1365 -command {catch {[focus] tag add sel 0.0 end}} \
1366 -accelerator $M1T-A
1367
1368# -- Branch Menu
1369#
1370if {[is_enabled branch]} {
1371 menu .mbar.branch
1372
1373 .mbar.branch add command -label {Create...} \
1374 -command do_create_branch \
1375 -accelerator $M1T-N
1376 lappend disable_on_lock [list .mbar.branch entryconf \
1377 [.mbar.branch index last] -state]
1378
1379 .mbar.branch add command -label {Delete...} \
1380 -command do_delete_branch
1381 lappend disable_on_lock [list .mbar.branch entryconf \
1382 [.mbar.branch index last] -state]
1383
1384 .mbar.branch add command -label {Reset...} \
1385 -command merge::reset_hard
1386 lappend disable_on_lock [list .mbar.branch entryconf \
1387 [.mbar.branch index last] -state]
1388}
1389
1390# -- Commit Menu
1391#
1392if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1393 menu .mbar.commit
1394
1395 .mbar.commit add radiobutton \
1396 -label {New Commit} \
1397 -command do_select_commit_type \
1398 -variable selected_commit_type \
1399 -value new
1400 lappend disable_on_lock \
1401 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1402
1403 .mbar.commit add radiobutton \
1404 -label {Amend Last Commit} \
1405 -command do_select_commit_type \
1406 -variable selected_commit_type \
1407 -value amend
1408 lappend disable_on_lock \
1409 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1410
1411 .mbar.commit add separator
1412
1413 .mbar.commit add command -label Rescan \
1414 -command do_rescan \
1415 -accelerator F5
1416 lappend disable_on_lock \
1417 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1418
1419 .mbar.commit add command -label {Add To Commit} \
1420 -command do_add_selection
1421 lappend disable_on_lock \
1422 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1423
1424 .mbar.commit add command -label {Add Existing To Commit} \
1425 -command do_add_all \
1426 -accelerator $M1T-I
1427 lappend disable_on_lock \
1428 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1429
1430 .mbar.commit add command -label {Unstage From Commit} \
1431 -command do_unstage_selection
1432 lappend disable_on_lock \
1433 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1434
1435 .mbar.commit add command -label {Revert Changes} \
1436 -command do_revert_selection
1437 lappend disable_on_lock \
1438 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1439
1440 .mbar.commit add separator
1441
1442 .mbar.commit add command -label {Sign Off} \
1443 -command do_signoff \
1444 -accelerator $M1T-S
1445
1446 .mbar.commit add command -label Commit \
1447 -command do_commit \
1448 -accelerator $M1T-Return
1449 lappend disable_on_lock \
1450 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1451}
1452
1453# -- Merge Menu
1454#
1455if {[is_enabled branch]} {
1456 menu .mbar.merge
1457 .mbar.merge add command -label {Local Merge...} \
1458 -command merge::dialog
1459 lappend disable_on_lock \
1460 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1461 .mbar.merge add command -label {Abort Merge...} \
1462 -command merge::reset_hard
1463 lappend disable_on_lock \
1464 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1465
1466}
1467
1468# -- Transport Menu
1469#
1470if {[is_enabled transport]} {
1471 menu .mbar.fetch
1472
1473 menu .mbar.push
1474 .mbar.push add command -label {Push...} \
1475 -command do_push_anywhere
1476}
1477
1478if {[is_MacOSX]} {
1479 # -- Apple Menu (Mac OS X only)
1480 #
1481 .mbar add cascade -label Apple -menu .mbar.apple
1482 menu .mbar.apple
1483
1484 .mbar.apple add command -label "About [appname]" \
1485 -command do_about
1486 .mbar.apple add command -label "Options..." \
1487 -command do_options
1488} else {
1489 # -- Edit Menu
1490 #
1491 .mbar.edit add separator
1492 .mbar.edit add command -label {Options...} \
1493 -command do_options
1494
1495 # -- Tools Menu
1496 #
1497 if {[file exists /usr/local/miga/lib/gui-miga]
1498 && [file exists .pvcsrc]} {
1499 proc do_miga {} {
1500 global ui_status_value
1501 if {![lock_index update]} return
1502 set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1503 set miga_fd [open "|$cmd" r]
1504 fconfigure $miga_fd -blocking 0
1505 fileevent $miga_fd readable [list miga_done $miga_fd]
1506 set ui_status_value {Running miga...}
1507 }
1508 proc miga_done {fd} {
1509 read $fd 512
1510 if {[eof $fd]} {
1511 close $fd
1512 unlock_index
1513 rescan [list set ui_status_value {Ready.}]
1514 }
1515 }
1516 .mbar add cascade -label Tools -menu .mbar.tools
1517 menu .mbar.tools
1518 .mbar.tools add command -label "Migrate" \
1519 -command do_miga
1520 lappend disable_on_lock \
1521 [list .mbar.tools entryconf [.mbar.tools index last] -state]
1522 }
1523}
1524
1525# -- Help Menu
1526#
1527.mbar add cascade -label Help -menu .mbar.help
1528menu .mbar.help
1529
1530if {![is_MacOSX]} {
1531 .mbar.help add command -label "About [appname]" \
1532 -command do_about
1533}
1534
1535set browser {}
1536catch {set browser $repo_config(instaweb.browser)}
1537set doc_path [file dirname [gitexec]]
1538set doc_path [file join $doc_path Documentation index.html]
1539
1540if {[is_Cygwin]} {
1541 set doc_path [exec cygpath --mixed $doc_path]
1542}
1543
1544if {$browser eq {}} {
1545 if {[is_MacOSX]} {
1546 set browser open
1547 } elseif {[is_Cygwin]} {
1548 set program_files [file dirname [exec cygpath --windir]]
1549 set program_files [file join $program_files {Program Files}]
1550 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
1551 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
1552 if {[file exists $firefox]} {
1553 set browser $firefox
1554 } elseif {[file exists $ie]} {
1555 set browser $ie
1556 }
1557 unset program_files firefox ie
1558 }
1559}
1560
1561if {[file isfile $doc_path]} {
1562 set doc_url "file:$doc_path"
1563} else {
1564 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
1565}
1566
1567if {$browser ne {}} {
1568 .mbar.help add command -label {Online Documentation} \
1569 -command [list exec $browser $doc_url &]
1570}
1571unset browser doc_path doc_url
1572
1573# -- Standard bindings
1574#
1575bind . <Destroy> do_quit
1576bind all <$M1B-Key-q> do_quit
1577bind all <$M1B-Key-Q> do_quit
1578bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
1579bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
1580
1581set subcommand_args {}
1582proc usage {} {
1583 puts stderr "usage: $::argv0 $::subcommand $::subcommand_args"
1584 exit 1
1585}
1586
1587# -- Not a normal commit type invocation? Do that instead!
1588#
1589switch -- $subcommand {
1590browser {
1591 set subcommand_args {rev?}
1592 switch [llength $argv] {
1593 0 {
1594 set current_branch [git symbolic-ref HEAD]
1595 regsub ^refs/((heads|tags|remotes)/)? \
1596 $current_branch {} current_branch
1597 }
1598 1 {
1599 set current_branch [lindex $argv 0]
1600 }
1601 default usage
1602 }
1603 browser::new $current_branch
1604 return
1605}
1606blame {
1607 set subcommand_args {rev? path?}
1608 set path {}
1609 set is_path 0
1610 foreach a $argv {
1611 if {$is_path || [file exists $_prefix$a]} {
1612 if {$path ne {}} usage
1613 set path $a
1614 break
1615 } elseif {$a eq {--}} {
1616 if {$path ne {}} {
1617 if {$current_branch ne {}} usage
1618 set current_branch $path
1619 set path {}
1620 }
1621 set is_path 1
1622 } elseif {$current_branch eq {}} {
1623 if {$current_branch ne {}} usage
1624 set current_branch $a
1625 } else {
1626 usage
1627 }
1628 }
1629 unset is_path
1630
1631 if {$current_branch eq {} && $path ne {}} {
1632 set current_branch [git symbolic-ref HEAD]
1633 regsub ^refs/((heads|tags|remotes)/)? \
1634 $current_branch {} current_branch
1635 }
1636 if {$current_branch eq {} || $path eq {}} usage
1637 blame::new $current_branch $path
1638 return
1639}
1640citool -
1641gui {
1642 if {[llength $argv] != 0} {
1643 puts -nonewline stderr "usage: $argv0"
1644 if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
1645 puts -nonewline stderr " $subcommand"
1646 }
1647 puts stderr {}
1648 exit 1
1649 }
1650 # fall through to setup UI for commits
1651}
1652default {
1653 puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
1654 exit 1
1655}
1656}
1657
1658# -- Branch Control
1659#
1660frame .branch \
1661 -borderwidth 1 \
1662 -relief sunken
1663label .branch.l1 \
1664 -text {Current Branch:} \
1665 -anchor w \
1666 -justify left
1667label .branch.cb \
1668 -textvariable current_branch \
1669 -anchor w \
1670 -justify left
1671pack .branch.l1 -side left
1672pack .branch.cb -side left -fill x
1673pack .branch -side top -fill x
1674
1675# -- Main Window Layout
1676#
1677panedwindow .vpane -orient vertical
1678panedwindow .vpane.files -orient horizontal
1679.vpane add .vpane.files -sticky nsew -height 100 -width 200
1680pack .vpane -anchor n -side top -fill both -expand 1
1681
1682# -- Index File List
1683#
1684frame .vpane.files.index -height 100 -width 200
1685label .vpane.files.index.title -text {Staged Changes (Will Be Committed)} \
1686 -background green
1687text $ui_index -background white -borderwidth 0 \
1688 -width 20 -height 10 \
1689 -wrap none \
1690 -cursor $cursor_ptr \
1691 -xscrollcommand {.vpane.files.index.sx set} \
1692 -yscrollcommand {.vpane.files.index.sy set} \
1693 -state disabled
1694scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
1695scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
1696pack .vpane.files.index.title -side top -fill x
1697pack .vpane.files.index.sx -side bottom -fill x
1698pack .vpane.files.index.sy -side right -fill y
1699pack $ui_index -side left -fill both -expand 1
1700.vpane.files add .vpane.files.index -sticky nsew
1701
1702# -- Working Directory File List
1703#
1704frame .vpane.files.workdir -height 100 -width 200
1705label .vpane.files.workdir.title -text {Unstaged Changes (Will Not Be Committed)} \
1706 -background red
1707text $ui_workdir -background white -borderwidth 0 \
1708 -width 20 -height 10 \
1709 -wrap none \
1710 -cursor $cursor_ptr \
1711 -xscrollcommand {.vpane.files.workdir.sx set} \
1712 -yscrollcommand {.vpane.files.workdir.sy set} \
1713 -state disabled
1714scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
1715scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
1716pack .vpane.files.workdir.title -side top -fill x
1717pack .vpane.files.workdir.sx -side bottom -fill x
1718pack .vpane.files.workdir.sy -side right -fill y
1719pack $ui_workdir -side left -fill both -expand 1
1720.vpane.files add .vpane.files.workdir -sticky nsew
1721
1722foreach i [list $ui_index $ui_workdir] {
1723 $i tag conf in_diff -font font_uibold
1724 $i tag conf in_sel \
1725 -background [$i cget -foreground] \
1726 -foreground [$i cget -background]
1727}
1728unset i
1729
1730# -- Diff and Commit Area
1731#
1732frame .vpane.lower -height 300 -width 400
1733frame .vpane.lower.commarea
1734frame .vpane.lower.diff -relief sunken -borderwidth 1
1735pack .vpane.lower.commarea -side top -fill x
1736pack .vpane.lower.diff -side bottom -fill both -expand 1
1737.vpane add .vpane.lower -sticky nsew
1738
1739# -- Commit Area Buttons
1740#
1741frame .vpane.lower.commarea.buttons
1742label .vpane.lower.commarea.buttons.l -text {} \
1743 -anchor w \
1744 -justify left
1745pack .vpane.lower.commarea.buttons.l -side top -fill x
1746pack .vpane.lower.commarea.buttons -side left -fill y
1747
1748button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
1749 -command do_rescan
1750pack .vpane.lower.commarea.buttons.rescan -side top -fill x
1751lappend disable_on_lock \
1752 {.vpane.lower.commarea.buttons.rescan conf -state}
1753
1754button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
1755 -command do_add_all
1756pack .vpane.lower.commarea.buttons.incall -side top -fill x
1757lappend disable_on_lock \
1758 {.vpane.lower.commarea.buttons.incall conf -state}
1759
1760button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
1761 -command do_signoff
1762pack .vpane.lower.commarea.buttons.signoff -side top -fill x
1763
1764button .vpane.lower.commarea.buttons.commit -text {Commit} \
1765 -command do_commit
1766pack .vpane.lower.commarea.buttons.commit -side top -fill x
1767lappend disable_on_lock \
1768 {.vpane.lower.commarea.buttons.commit conf -state}
1769
1770# -- Commit Message Buffer
1771#
1772frame .vpane.lower.commarea.buffer
1773frame .vpane.lower.commarea.buffer.header
1774set ui_comm .vpane.lower.commarea.buffer.t
1775set ui_coml .vpane.lower.commarea.buffer.header.l
1776radiobutton .vpane.lower.commarea.buffer.header.new \
1777 -text {New Commit} \
1778 -command do_select_commit_type \
1779 -variable selected_commit_type \
1780 -value new
1781lappend disable_on_lock \
1782 [list .vpane.lower.commarea.buffer.header.new conf -state]
1783radiobutton .vpane.lower.commarea.buffer.header.amend \
1784 -text {Amend Last Commit} \
1785 -command do_select_commit_type \
1786 -variable selected_commit_type \
1787 -value amend
1788lappend disable_on_lock \
1789 [list .vpane.lower.commarea.buffer.header.amend conf -state]
1790label $ui_coml \
1791 -anchor w \
1792 -justify left
1793proc trace_commit_type {varname args} {
1794 global ui_coml commit_type
1795 switch -glob -- $commit_type {
1796 initial {set txt {Initial Commit Message:}}
1797 amend {set txt {Amended Commit Message:}}
1798 amend-initial {set txt {Amended Initial Commit Message:}}
1799 amend-merge {set txt {Amended Merge Commit Message:}}
1800 merge {set txt {Merge Commit Message:}}
1801 * {set txt {Commit Message:}}
1802 }
1803 $ui_coml conf -text $txt
1804}
1805trace add variable commit_type write trace_commit_type
1806pack $ui_coml -side left -fill x
1807pack .vpane.lower.commarea.buffer.header.amend -side right
1808pack .vpane.lower.commarea.buffer.header.new -side right
1809
1810text $ui_comm -background white -borderwidth 1 \
1811 -undo true \
1812 -maxundo 20 \
1813 -autoseparators true \
1814 -relief sunken \
1815 -width 75 -height 9 -wrap none \
1816 -font font_diff \
1817 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
1818scrollbar .vpane.lower.commarea.buffer.sby \
1819 -command [list $ui_comm yview]
1820pack .vpane.lower.commarea.buffer.header -side top -fill x
1821pack .vpane.lower.commarea.buffer.sby -side right -fill y
1822pack $ui_comm -side left -fill y
1823pack .vpane.lower.commarea.buffer -side left -fill y
1824
1825# -- Commit Message Buffer Context Menu
1826#
1827set ctxm .vpane.lower.commarea.buffer.ctxm
1828menu $ctxm -tearoff 0
1829$ctxm add command \
1830 -label {Cut} \
1831 -command {tk_textCut $ui_comm}
1832$ctxm add command \
1833 -label {Copy} \
1834 -command {tk_textCopy $ui_comm}
1835$ctxm add command \
1836 -label {Paste} \
1837 -command {tk_textPaste $ui_comm}
1838$ctxm add command \
1839 -label {Delete} \
1840 -command {$ui_comm delete sel.first sel.last}
1841$ctxm add separator
1842$ctxm add command \
1843 -label {Select All} \
1844 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
1845$ctxm add command \
1846 -label {Copy All} \
1847 -command {
1848 $ui_comm tag add sel 0.0 end
1849 tk_textCopy $ui_comm
1850 $ui_comm tag remove sel 0.0 end
1851 }
1852$ctxm add separator
1853$ctxm add command \
1854 -label {Sign Off} \
1855 -command do_signoff
1856bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
1857
1858# -- Diff Header
1859#
1860proc trace_current_diff_path {varname args} {
1861 global current_diff_path diff_actions file_states
1862 if {$current_diff_path eq {}} {
1863 set s {}
1864 set f {}
1865 set p {}
1866 set o disabled
1867 } else {
1868 set p $current_diff_path
1869 set s [mapdesc [lindex $file_states($p) 0] $p]
1870 set f {File:}
1871 set p [escape_path $p]
1872 set o normal
1873 }
1874
1875 .vpane.lower.diff.header.status configure -text $s
1876 .vpane.lower.diff.header.file configure -text $f
1877 .vpane.lower.diff.header.path configure -text $p
1878 foreach w $diff_actions {
1879 uplevel #0 $w $o
1880 }
1881}
1882trace add variable current_diff_path write trace_current_diff_path
1883
1884frame .vpane.lower.diff.header -background orange
1885label .vpane.lower.diff.header.status \
1886 -background orange \
1887 -width $max_status_desc \
1888 -anchor w \
1889 -justify left
1890label .vpane.lower.diff.header.file \
1891 -background orange \
1892 -anchor w \
1893 -justify left
1894label .vpane.lower.diff.header.path \
1895 -background orange \
1896 -anchor w \
1897 -justify left
1898pack .vpane.lower.diff.header.status -side left
1899pack .vpane.lower.diff.header.file -side left
1900pack .vpane.lower.diff.header.path -fill x
1901set ctxm .vpane.lower.diff.header.ctxm
1902menu $ctxm -tearoff 0
1903$ctxm add command \
1904 -label {Copy} \
1905 -command {
1906 clipboard clear
1907 clipboard append \
1908 -format STRING \
1909 -type STRING \
1910 -- $current_diff_path
1911 }
1912lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1913bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
1914
1915# -- Diff Body
1916#
1917frame .vpane.lower.diff.body
1918set ui_diff .vpane.lower.diff.body.t
1919text $ui_diff -background white -borderwidth 0 \
1920 -width 80 -height 15 -wrap none \
1921 -font font_diff \
1922 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
1923 -yscrollcommand {.vpane.lower.diff.body.sby set} \
1924 -state disabled
1925scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
1926 -command [list $ui_diff xview]
1927scrollbar .vpane.lower.diff.body.sby -orient vertical \
1928 -command [list $ui_diff yview]
1929pack .vpane.lower.diff.body.sbx -side bottom -fill x
1930pack .vpane.lower.diff.body.sby -side right -fill y
1931pack $ui_diff -side left -fill both -expand 1
1932pack .vpane.lower.diff.header -side top -fill x
1933pack .vpane.lower.diff.body -side bottom -fill both -expand 1
1934
1935$ui_diff tag conf d_cr -elide true
1936$ui_diff tag conf d_@ -foreground blue -font font_diffbold
1937$ui_diff tag conf d_+ -foreground {#00a000}
1938$ui_diff tag conf d_- -foreground red
1939
1940$ui_diff tag conf d_++ -foreground {#00a000}
1941$ui_diff tag conf d_-- -foreground red
1942$ui_diff tag conf d_+s \
1943 -foreground {#00a000} \
1944 -background {#e2effa}
1945$ui_diff tag conf d_-s \
1946 -foreground red \
1947 -background {#e2effa}
1948$ui_diff tag conf d_s+ \
1949 -foreground {#00a000} \
1950 -background ivory1
1951$ui_diff tag conf d_s- \
1952 -foreground red \
1953 -background ivory1
1954
1955$ui_diff tag conf d<<<<<<< \
1956 -foreground orange \
1957 -font font_diffbold
1958$ui_diff tag conf d======= \
1959 -foreground orange \
1960 -font font_diffbold
1961$ui_diff tag conf d>>>>>>> \
1962 -foreground orange \
1963 -font font_diffbold
1964
1965$ui_diff tag raise sel
1966
1967# -- Diff Body Context Menu
1968#
1969set ctxm .vpane.lower.diff.body.ctxm
1970menu $ctxm -tearoff 0
1971$ctxm add command \
1972 -label {Refresh} \
1973 -command reshow_diff
1974lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1975$ctxm add command \
1976 -label {Copy} \
1977 -command {tk_textCopy $ui_diff}
1978lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1979$ctxm add command \
1980 -label {Select All} \
1981 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
1982lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1983$ctxm add command \
1984 -label {Copy All} \
1985 -command {
1986 $ui_diff tag add sel 0.0 end
1987 tk_textCopy $ui_diff
1988 $ui_diff tag remove sel 0.0 end
1989 }
1990lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1991$ctxm add separator
1992$ctxm add command \
1993 -label {Apply/Reverse Hunk} \
1994 -command {apply_hunk $cursorX $cursorY}
1995set ui_diff_applyhunk [$ctxm index last]
1996lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
1997$ctxm add separator
1998$ctxm add command \
1999 -label {Decrease Font Size} \
2000 -command {incr_font_size font_diff -1}
2001lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2002$ctxm add command \
2003 -label {Increase Font Size} \
2004 -command {incr_font_size font_diff 1}
2005lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2006$ctxm add separator
2007$ctxm add command \
2008 -label {Show Less Context} \
2009 -command {if {$repo_config(gui.diffcontext) >= 2} {
2010 incr repo_config(gui.diffcontext) -1
2011 reshow_diff
2012 }}
2013lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2014$ctxm add command \
2015 -label {Show More Context} \
2016 -command {
2017 incr repo_config(gui.diffcontext)
2018 reshow_diff
2019 }
2020lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2021$ctxm add separator
2022$ctxm add command -label {Options...} \
2023 -command do_options
2024bind_button3 $ui_diff "
2025 set cursorX %x
2026 set cursorY %y
2027 if {\$ui_index eq \$current_diff_side} {
2028 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2029 } else {
2030 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2031 }
2032 tk_popup $ctxm %X %Y
2033"
2034unset ui_diff_applyhunk
2035
2036# -- Status Bar
2037#
2038label .status -textvariable ui_status_value \
2039 -anchor w \
2040 -justify left \
2041 -borderwidth 1 \
2042 -relief sunken
2043pack .status -anchor w -side bottom -fill x
2044
2045# -- Load geometry
2046#
2047catch {
2048set gm $repo_config(gui.geometry)
2049wm geometry . [lindex $gm 0]
2050.vpane sash place 0 \
2051 [lindex [.vpane sash coord 0] 0] \
2052 [lindex $gm 1]
2053.vpane.files sash place 0 \
2054 [lindex $gm 2] \
2055 [lindex [.vpane.files sash coord 0] 1]
2056unset gm
2057}
2058
2059# -- Key Bindings
2060#
2061bind $ui_comm <$M1B-Key-Return> {do_commit;break}
2062bind $ui_comm <$M1B-Key-i> {do_add_all;break}
2063bind $ui_comm <$M1B-Key-I> {do_add_all;break}
2064bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
2065bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
2066bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
2067bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
2068bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
2069bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
2070bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2071bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2072
2073bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
2074bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
2075bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
2076bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
2077bind $ui_diff <$M1B-Key-v> {break}
2078bind $ui_diff <$M1B-Key-V> {break}
2079bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2080bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2081bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
2082bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
2083bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
2084bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
2085bind $ui_diff <Key-k> {catch {%W yview scroll -1 units};break}
2086bind $ui_diff <Key-j> {catch {%W yview scroll 1 units};break}
2087bind $ui_diff <Key-h> {catch {%W xview scroll -1 units};break}
2088bind $ui_diff <Key-l> {catch {%W xview scroll 1 units};break}
2089bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
2090bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
2091bind $ui_diff <Button-1> {focus %W}
2092
2093if {[is_enabled branch]} {
2094 bind . <$M1B-Key-n> do_create_branch
2095 bind . <$M1B-Key-N> do_create_branch
2096}
2097
2098bind all <Key-F5> do_rescan
2099bind all <$M1B-Key-r> do_rescan
2100bind all <$M1B-Key-R> do_rescan
2101bind . <$M1B-Key-s> do_signoff
2102bind . <$M1B-Key-S> do_signoff
2103bind . <$M1B-Key-i> do_add_all
2104bind . <$M1B-Key-I> do_add_all
2105bind . <$M1B-Key-Return> do_commit
2106foreach i [list $ui_index $ui_workdir] {
2107 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
2108 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2109 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
2110}
2111unset i
2112
2113set file_lists($ui_index) [list]
2114set file_lists($ui_workdir) [list]
2115
2116wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2117focus -force $ui_comm
2118
2119# -- Warn the user about environmental problems. Cygwin's Tcl
2120# does *not* pass its env array onto any processes it spawns.
2121# This means that git processes get none of our environment.
2122#
2123if {[is_Cygwin]} {
2124 set ignored_env 0
2125 set suggest_user {}
2126 set msg "Possible environment issues exist.
2127
2128The following environment variables are probably
2129going to be ignored by any Git subprocess run
2130by [appname]:
2131
2132"
2133 foreach name [array names env] {
2134 switch -regexp -- $name {
2135 {^GIT_INDEX_FILE$} -
2136 {^GIT_OBJECT_DIRECTORY$} -
2137 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
2138 {^GIT_DIFF_OPTS$} -
2139 {^GIT_EXTERNAL_DIFF$} -
2140 {^GIT_PAGER$} -
2141 {^GIT_TRACE$} -
2142 {^GIT_CONFIG$} -
2143 {^GIT_CONFIG_LOCAL$} -
2144 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
2145 append msg " - $name\n"
2146 incr ignored_env
2147 }
2148 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
2149 append msg " - $name\n"
2150 incr ignored_env
2151 set suggest_user $name
2152 }
2153 }
2154 }
2155 if {$ignored_env > 0} {
2156 append msg "
2157This is due to a known issue with the
2158Tcl binary distributed by Cygwin."
2159
2160 if {$suggest_user ne {}} {
2161 append msg "
2162
2163A good replacement for $suggest_user
2164is placing values for the user.name and
2165user.email settings into your personal
2166~/.gitconfig file.
2167"
2168 }
2169 warn_popup $msg
2170 }
2171 unset ignored_env msg suggest_user name
2172}
2173
2174# -- Only initialize complex UI if we are going to stay running.
2175#
2176if {[is_enabled transport]} {
2177 load_all_remotes
2178 load_all_heads
2179
2180 populate_branch_menu
2181 populate_fetch_menu
2182 populate_push_menu
2183}
2184
2185# -- Only suggest a gc run if we are going to stay running.
2186#
2187if {[is_enabled multicommit]} {
2188 set object_limit 2000
2189 if {[is_Windows]} {set object_limit 200}
2190 regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
2191 if {$objects_current >= $object_limit} {
2192 if {[ask_popup \
2193 "This repository currently has $objects_current loose objects.
2194
2195To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2196
2197Compress the database now?"] eq yes} {
2198 do_gc
2199 }
2200 }
2201 unset object_limit _junk objects_current
2202}
2203
2204lock_index begin-read
2205after 1 do_rescan