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