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