2 # Tcl ignores the next line -*- tcl -*- \
5 set appvers {@@GITGUI_VERSION@@}
7 Copyright © 2006, 2007 Shawn Pearce, et. al.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}
23 ######################################################################
25 ## configure our library
27 set oguilib {@@GITGUI_LIBDIR@@}
28 if {[string match @@* $oguilib]} {
29 set oguilib [file join [file dirname [file normalize $argv0]] lib]
31 set auto_path [concat [list $oguilib] $auto_path]
33 if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
35 rename auto_load real__auto_load
36 proc auto_load {name args} {
37 puts stderr "auto_load $name"
38 return [uplevel 1 real__auto_load $name $args]
40 rename source real__source
42 puts stderr "source $name"
43 uplevel 1 real__source $name
47 ######################################################################
51 set _appname [lindex [file split $argv0] end]
67 return [eval [concat [list file join $_gitdir] $args]]
72 if {$_gitexec eq {}} {
73 if {[catch {set _gitexec [git --exec-path]} err]} {
74 error "Git not installed?\n\n$err"
80 return [eval [concat [list file join $_gitexec] $args]]
89 global tcl_platform tk_library
90 if {[tk windowingsystem] eq {aqua}} {
98 if {$tcl_platform(platform) eq {windows}} {
105 global tcl_platform _iscygwin
106 if {$_iscygwin eq {}} {
107 if {$tcl_platform(platform) eq {windows}} {
108 if {[catch {set p [exec cygpath --windir]} err]} {
120 proc is_enabled {option} {
121 global enabled_options
122 if {[catch {set on $enabled_options($option)}]} {return 0}
126 proc enable_option {option} {
127 global enabled_options
128 set enabled_options($option) 1
131 proc disable_option {option} {
132 global enabled_options
133 set enabled_options($option) 0
136 ######################################################################
140 proc is_many_config {name} {
141 switch -glob -- $name {
150 proc is_config_true {name} {
152 if {[catch {set v $repo_config($name)}]} {
154 } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
161 proc load_config {include_global} {
162 global repo_config global_config default_config
164 array unset global_config
165 if {$include_global} {
167 set fd_rc [open "| git config --global --list" r]
168 while {[gets $fd_rc line] >= 0} {
169 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
170 if {[is_many_config $name]} {
171 lappend global_config($name) $value
173 set global_config($name) $value
181 array unset repo_config
183 set fd_rc [open "| git config --list" r]
184 while {[gets $fd_rc line] >= 0} {
185 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
186 if {[is_many_config $name]} {
187 lappend repo_config($name) $value
189 set repo_config($name) $value
196 foreach name [array names default_config] {
197 if {[catch {set v $global_config($name)}]} {
198 set global_config($name) $default_config($name)
200 if {[catch {set v $repo_config($name)}]} {
201 set repo_config($name) $default_config($name)
206 ######################################################################
211 return [eval exec git $args]
214 auto_load tk_optionMenu
215 rename tk_optionMenu real__tkOptionMenu
216 proc tk_optionMenu {w varName args} {
217 set m [eval real__tkOptionMenu $w $varName $args]
218 $m configure -font font_ui
219 $w configure -font font_ui
223 ######################################################################
227 if {{--version} eq $argv || {version} eq $argv} {
228 puts "git-gui version $appvers"
235 if {[catch {set v [git --version]} err]} {
236 catch {wm withdraw .}
237 error_popup "Cannot determine Git version:
241 [appname] requires Git $req_maj.$req_min or later."
244 if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
245 if {$act_maj < $req_maj
246 || ($act_maj == $req_maj && $act_min < $req_min)} {
247 catch {wm withdraw .}
248 error_popup "[appname] requires Git $req_maj.$req_min or later.
254 catch {wm withdraw .}
255 error_popup "Cannot parse Git version string:\n\n$v"
258 unset -nocomplain v _junk act_maj act_min req_maj req_min
260 ######################################################################
264 if { [catch {set _gitdir $env(GIT_DIR)}]
265 && [catch {set _gitdir [git rev-parse --git-dir]} err]} {
266 catch {wm withdraw .}
267 error_popup "Cannot find the git directory:\n\n$err"
270 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
271 catch {set _gitdir [exec cygpath --unix $_gitdir]}
273 if {![file isdirectory $_gitdir]} {
274 catch {wm withdraw .}
275 error_popup "Git directory not found:\n\n$_gitdir"
278 if {[lindex [file split $_gitdir] end] ne {.git}} {
279 catch {wm withdraw .}
280 error_popup "Cannot use funny .git directory:\n\n$_gitdir"
283 if {[catch {cd [file dirname $_gitdir]} err]} {
284 catch {wm withdraw .}
285 error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
288 set _reponame [lindex [file split \
289 [file normalize [file dirname $_gitdir]]] \
292 ######################################################################
296 set current_diff_path {}
297 set current_diff_side {}
298 set diff_actions [list]
299 set ui_status_value {Initializing...}
303 set MERGE_HEAD [list]
306 set current_branch {}
307 set current_diff_path {}
308 set selected_commit_type new
310 ######################################################################
318 set disable_on_lock [list]
319 set index_lock_type none
321 proc lock_index {type} {
322 global index_lock_type disable_on_lock
324 if {$index_lock_type eq {none}} {
325 set index_lock_type $type
326 foreach w $disable_on_lock {
327 uplevel #0 $w disabled
330 } elseif {$index_lock_type eq "begin-$type"} {
331 set index_lock_type $type
337 proc unlock_index {} {
338 global index_lock_type disable_on_lock
340 set index_lock_type none
341 foreach w $disable_on_lock {
346 ######################################################################
350 proc repository_state {ctvar hdvar mhvar} {
351 global current_branch
352 upvar $ctvar ct $hdvar hd $mhvar mh
356 if {[catch {set current_branch [git symbolic-ref HEAD]}]} {
357 set current_branch {}
359 regsub ^refs/((heads|tags|remotes)/)? \
365 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
371 set merge_head [gitdir MERGE_HEAD]
372 if {[file exists $merge_head]} {
374 set fd_mh [open $merge_head r]
375 while {[gets $fd_mh line] >= 0} {
386 global PARENT empty_tree
388 set p [lindex $PARENT 0]
392 if {$empty_tree eq {}} {
393 set empty_tree [git mktree << {}]
398 proc rescan {after {honor_trustmtime 1}} {
399 global HEAD PARENT MERGE_HEAD commit_type
400 global ui_index ui_workdir ui_status_value ui_comm
401 global rescan_active file_states
404 if {$rescan_active > 0 || ![lock_index read]} return
406 repository_state newType newHEAD newMERGE_HEAD
407 if {[string match amend* $commit_type]
408 && $newType eq {normal}
409 && $newHEAD eq $HEAD} {
413 set MERGE_HEAD $newMERGE_HEAD
414 set commit_type $newType
417 array unset file_states
419 if {![$ui_comm edit modified]
420 || [string trim [$ui_comm get 0.0 end]] eq {}} {
421 if {[load_message GITGUI_MSG]} {
422 } elseif {[load_message MERGE_MSG]} {
423 } elseif {[load_message SQUASH_MSG]} {
426 $ui_comm edit modified false
429 if {[is_enabled branch]} {
434 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
435 rescan_stage2 {} $after
438 set ui_status_value {Refreshing file status...}
439 set cmd [list git update-index]
441 lappend cmd --unmerged
442 lappend cmd --ignore-missing
443 lappend cmd --refresh
444 set fd_rf [open "| $cmd" r]
445 fconfigure $fd_rf -blocking 0 -translation binary
446 fileevent $fd_rf readable \
447 [list rescan_stage2 $fd_rf $after]
451 proc rescan_stage2 {fd after} {
452 global ui_status_value
453 global rescan_active buf_rdi buf_rdf buf_rlo
457 if {![eof $fd]} return
461 set ls_others [list | git ls-files --others -z \
462 --exclude-per-directory=.gitignore]
463 set info_exclude [gitdir info exclude]
464 if {[file readable $info_exclude]} {
465 lappend ls_others "--exclude-from=$info_exclude"
473 set ui_status_value {Scanning for modified files ...}
474 set fd_di [open "| git diff-index --cached -z [PARENT]" r]
475 set fd_df [open "| git diff-files -z" r]
476 set fd_lo [open $ls_others r]
478 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
479 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
480 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
481 fileevent $fd_di readable [list read_diff_index $fd_di $after]
482 fileevent $fd_df readable [list read_diff_files $fd_df $after]
483 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
486 proc load_message {file} {
490 if {[file isfile $f]} {
491 if {[catch {set fd [open $f r]}]} {
494 set content [string trim [read $fd]]
496 regsub -all -line {[ \r\t]+$} $content {} content
497 $ui_comm delete 0.0 end
498 $ui_comm insert end $content
504 proc read_diff_index {fd after} {
507 append buf_rdi [read $fd]
509 set n [string length $buf_rdi]
511 set z1 [string first "\0" $buf_rdi $c]
514 set z2 [string first "\0" $buf_rdi $z1]
518 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
519 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
521 [encoding convertfrom $p] \
523 [list [lindex $i 0] [lindex $i 2]] \
529 set buf_rdi [string range $buf_rdi $c end]
534 rescan_done $fd buf_rdi $after
537 proc read_diff_files {fd after} {
540 append buf_rdf [read $fd]
542 set n [string length $buf_rdf]
544 set z1 [string first "\0" $buf_rdf $c]
547 set z2 [string first "\0" $buf_rdf $z1]
551 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
552 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
554 [encoding convertfrom $p] \
557 [list [lindex $i 0] [lindex $i 2]]
562 set buf_rdf [string range $buf_rdf $c end]
567 rescan_done $fd buf_rdf $after
570 proc read_ls_others {fd after} {
573 append buf_rlo [read $fd]
574 set pck [split $buf_rlo "\0"]
575 set buf_rlo [lindex $pck end]
576 foreach p [lrange $pck 0 end-1] {
577 merge_state [encoding convertfrom $p] ?O
579 rescan_done $fd buf_rlo $after
582 proc rescan_done {fd buf after} {
583 global rescan_active current_diff_path
584 global file_states repo_config
587 if {![eof $fd]} return
590 if {[incr rescan_active -1] > 0} return
595 if {$current_diff_path ne {}} reshow_diff
599 proc prune_selection {} {
600 global file_states selected_paths
602 foreach path [array names selected_paths] {
603 if {[catch {set still_here $file_states($path)}]} {
604 unset selected_paths($path)
609 ######################################################################
613 proc mapicon {w state path} {
616 if {[catch {set r $all_icons($state$w)}]} {
617 puts "error: no icon for $w state={$state} $path"
623 proc mapdesc {state path} {
626 if {[catch {set r $all_descs($state)}]} {
627 puts "error: no desc for state={$state} $path"
633 proc escape_path {path} {
634 regsub -all {\\} $path "\\\\" path
635 regsub -all "\n" $path "\\n" path
639 proc short_path {path} {
640 return [escape_path [lindex [file split $path] end]]
644 set null_sha1 [string repeat 0 40]
646 proc merge_state {path new_state {head_info {}} {index_info {}}} {
647 global file_states next_icon_id null_sha1
649 set s0 [string index $new_state 0]
650 set s1 [string index $new_state 1]
652 if {[catch {set info $file_states($path)}]} {
654 set icon n[incr next_icon_id]
656 set state [lindex $info 0]
657 set icon [lindex $info 1]
658 if {$head_info eq {}} {set head_info [lindex $info 2]}
659 if {$index_info eq {}} {set index_info [lindex $info 3]}
662 if {$s0 eq {?}} {set s0 [string index $state 0]} \
663 elseif {$s0 eq {_}} {set s0 _}
665 if {$s1 eq {?}} {set s1 [string index $state 1]} \
666 elseif {$s1 eq {_}} {set s1 _}
668 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
669 set head_info [list 0 $null_sha1]
670 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
671 && $head_info eq {}} {
672 set head_info $index_info
675 set file_states($path) [list $s0$s1 $icon \
676 $head_info $index_info \
681 proc display_file_helper {w path icon_name old_m new_m} {
685 set lno [lsearch -sorted -exact $file_lists($w) $path]
687 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
689 $w conf -state normal
690 $w delete $lno.0 [expr {$lno + 1}].0
691 $w conf -state disabled
693 } elseif {$old_m eq {_} && $new_m ne {_}} {
694 lappend file_lists($w) $path
695 set file_lists($w) [lsort -unique $file_lists($w)]
696 set lno [lsearch -sorted -exact $file_lists($w) $path]
698 $w conf -state normal
699 $w image create $lno.0 \
700 -align center -padx 5 -pady 1 \
702 -image [mapicon $w $new_m $path]
703 $w insert $lno.1 "[escape_path $path]\n"
704 $w conf -state disabled
705 } elseif {$old_m ne $new_m} {
706 $w conf -state normal
707 $w image conf $icon_name -image [mapicon $w $new_m $path]
708 $w conf -state disabled
712 proc display_file {path state} {
713 global file_states selected_paths
714 global ui_index ui_workdir
716 set old_m [merge_state $path $state]
717 set s $file_states($path)
718 set new_m [lindex $s 0]
719 set icon_name [lindex $s 1]
721 set o [string index $old_m 0]
722 set n [string index $new_m 0]
729 display_file_helper $ui_index $path $icon_name $o $n
731 if {[string index $old_m 0] eq {U}} {
734 set o [string index $old_m 1]
736 if {[string index $new_m 0] eq {U}} {
739 set n [string index $new_m 1]
741 display_file_helper $ui_workdir $path $icon_name $o $n
743 if {$new_m eq {__}} {
744 unset file_states($path)
745 catch {unset selected_paths($path)}
749 proc display_all_files_helper {w path icon_name m} {
752 lappend file_lists($w) $path
753 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
754 $w image create end \
755 -align center -padx 5 -pady 1 \
757 -image [mapicon $w $m $path]
758 $w insert end "[escape_path $path]\n"
761 proc display_all_files {} {
762 global ui_index ui_workdir
763 global file_states file_lists
766 $ui_index conf -state normal
767 $ui_workdir conf -state normal
769 $ui_index delete 0.0 end
770 $ui_workdir delete 0.0 end
773 set file_lists($ui_index) [list]
774 set file_lists($ui_workdir) [list]
776 foreach path [lsort [array names file_states]] {
777 set s $file_states($path)
779 set icon_name [lindex $s 1]
781 set s [string index $m 0]
782 if {$s ne {U} && $s ne {_}} {
783 display_all_files_helper $ui_index $path \
787 if {[string index $m 0] eq {U}} {
790 set s [string index $m 1]
793 display_all_files_helper $ui_workdir $path \
798 $ui_index conf -state disabled
799 $ui_workdir conf -state disabled
802 ######################################################################
807 #define mask_width 14
808 #define mask_height 15
809 static unsigned char mask_bits[] = {
810 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
811 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
812 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
815 image create bitmap file_plain -background white -foreground black -data {
816 #define plain_width 14
817 #define plain_height 15
818 static unsigned char plain_bits[] = {
819 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
820 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
821 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
822 } -maskdata $filemask
824 image create bitmap file_mod -background white -foreground blue -data {
826 #define mod_height 15
827 static unsigned char mod_bits[] = {
828 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
829 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
830 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
831 } -maskdata $filemask
833 image create bitmap file_fulltick -background white -foreground "#007000" -data {
834 #define file_fulltick_width 14
835 #define file_fulltick_height 15
836 static unsigned char file_fulltick_bits[] = {
837 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
838 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
839 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
840 } -maskdata $filemask
842 image create bitmap file_parttick -background white -foreground "#005050" -data {
843 #define parttick_width 14
844 #define parttick_height 15
845 static unsigned char parttick_bits[] = {
846 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
847 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
848 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
849 } -maskdata $filemask
851 image create bitmap file_question -background white -foreground black -data {
852 #define file_question_width 14
853 #define file_question_height 15
854 static unsigned char file_question_bits[] = {
855 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
856 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
857 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
858 } -maskdata $filemask
860 image create bitmap file_removed -background white -foreground red -data {
861 #define file_removed_width 14
862 #define file_removed_height 15
863 static unsigned char file_removed_bits[] = {
864 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
865 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
866 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
867 } -maskdata $filemask
869 image create bitmap file_merge -background white -foreground blue -data {
870 #define file_merge_width 14
871 #define file_merge_height 15
872 static unsigned char file_merge_bits[] = {
873 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
874 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
875 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
876 } -maskdata $filemask
879 #define file_width 18
880 #define file_height 18
881 static unsigned char file_bits[] = {
882 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
883 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
884 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
885 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
886 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
888 image create bitmap file_dir -background white -foreground blue \
889 -data $file_dir_data -maskdata $file_dir_data
892 set file_uplevel_data {
895 static unsigned char up_bits[] = {
896 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
897 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
898 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
900 image create bitmap file_uplevel -background white -foreground red \
901 -data $file_uplevel_data -maskdata $file_uplevel_data
902 unset file_uplevel_data
904 set ui_index .vpane.files.index.list
905 set ui_workdir .vpane.files.workdir.list
907 set all_icons(_$ui_index) file_plain
908 set all_icons(A$ui_index) file_fulltick
909 set all_icons(M$ui_index) file_fulltick
910 set all_icons(D$ui_index) file_removed
911 set all_icons(U$ui_index) file_merge
913 set all_icons(_$ui_workdir) file_plain
914 set all_icons(M$ui_workdir) file_mod
915 set all_icons(D$ui_workdir) file_question
916 set all_icons(U$ui_workdir) file_merge
917 set all_icons(O$ui_workdir) file_plain
919 set max_status_desc 0
923 {_M "Modified, not staged"}
924 {M_ "Staged for commit"}
925 {MM "Portions staged for commit"}
926 {MD "Staged for commit, missing"}
928 {_O "Untracked, not staged"}
929 {A_ "Staged for commit"}
930 {AM "Portions staged for commit"}
931 {AD "Staged for commit, missing"}
934 {D_ "Staged for removal"}
935 {DO "Staged for removal, still present"}
937 {U_ "Requires merge resolution"}
938 {UU "Requires merge resolution"}
939 {UM "Requires merge resolution"}
940 {UD "Requires merge resolution"}
942 if {$max_status_desc < [string length [lindex $i 1]]} {
943 set max_status_desc [string length [lindex $i 1]]
945 set all_descs([lindex $i 0]) [lindex $i 1]
949 ######################################################################
953 proc bind_button3 {w cmd} {
954 bind $w <Any-Button-3> $cmd
956 bind $w <Control-Button-1> $cmd
960 proc scrollbar2many {list mode args} {
961 foreach w $list {eval $w $mode $args}
964 proc many2scrollbar {list mode sb top bottom} {
966 foreach w $list {$w $mode moveto $top}
969 proc incr_font_size {font {amt 1}} {
970 set sz [font configure $font -size]
972 font configure $font -size $sz
973 font configure ${font}bold -size $sz
976 ######################################################################
980 set starting_gitk_msg {Starting gitk... please wait...}
982 proc do_gitk {revs} {
983 global env ui_status_value starting_gitk_msg
985 # -- Always start gitk through whatever we were loaded with. This
986 # lets us bypass using shell process on Windows systems.
988 set cmd [list [info nameofexecutable]]
989 lappend cmd [gitexec gitk]
995 if {[catch {eval exec $cmd &} err]} {
996 error_popup "Failed to start gitk:\n\n$err"
998 set ui_status_value $starting_gitk_msg
1000 if {$ui_status_value eq $starting_gitk_msg} {
1001 set ui_status_value {Ready.}
1010 global ui_comm is_quitting repo_config commit_type
1012 if {$is_quitting} return
1015 if {[winfo exists $ui_comm]} {
1016 # -- Stash our current commit buffer.
1018 set save [gitdir GITGUI_MSG]
1019 set msg [string trim [$ui_comm get 0.0 end]]
1020 regsub -all -line {[ \r\t]+$} $msg {} msg
1021 if {(![string match amend* $commit_type]
1022 || [$ui_comm edit modified])
1025 set fd [open $save w]
1026 puts -nonewline $fd $msg
1030 catch {file delete $save}
1033 # -- Stash our current window geometry into this repository.
1035 set cfg_geometry [list]
1036 lappend cfg_geometry [wm geometry .]
1037 lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1038 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1039 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1042 if {$cfg_geometry ne $rc_geometry} {
1043 catch {git config gui.geometry $cfg_geometry}
1051 rescan {set ui_status_value {Ready.}}
1058 proc toggle_or_diff {w x y} {
1059 global file_states file_lists current_diff_path ui_index ui_workdir
1060 global last_clicked selected_paths
1062 set pos [split [$w index @$x,$y] .]
1063 set lno [lindex $pos 0]
1064 set col [lindex $pos 1]
1065 set path [lindex $file_lists($w) [expr {$lno - 1}]]
1071 set last_clicked [list $w $lno]
1072 array unset selected_paths
1073 $ui_index tag remove in_sel 0.0 end
1074 $ui_workdir tag remove in_sel 0.0 end
1077 if {$current_diff_path eq $path} {
1078 set after {reshow_diff;}
1082 if {$w eq $ui_index} {
1084 "Unstaging [short_path $path] from commit" \
1086 [concat $after {set ui_status_value {Ready.}}]
1087 } elseif {$w eq $ui_workdir} {
1089 "Adding [short_path $path]" \
1091 [concat $after {set ui_status_value {Ready.}}]
1094 show_diff $path $w $lno
1098 proc add_one_to_selection {w x y} {
1099 global file_lists last_clicked selected_paths
1101 set lno [lindex [split [$w index @$x,$y] .] 0]
1102 set path [lindex $file_lists($w) [expr {$lno - 1}]]
1108 if {$last_clicked ne {}
1109 && [lindex $last_clicked 0] ne $w} {
1110 array unset selected_paths
1111 [lindex $last_clicked 0] tag remove in_sel 0.0 end
1114 set last_clicked [list $w $lno]
1115 if {[catch {set in_sel $selected_paths($path)}]} {
1119 unset selected_paths($path)
1120 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
1122 set selected_paths($path) 1
1123 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1127 proc add_range_to_selection {w x y} {
1128 global file_lists last_clicked selected_paths
1130 if {[lindex $last_clicked 0] ne $w} {
1131 toggle_or_diff $w $x $y
1135 set lno [lindex [split [$w index @$x,$y] .] 0]
1136 set lc [lindex $last_clicked 1]
1145 foreach path [lrange $file_lists($w) \
1146 [expr {$begin - 1}] \
1147 [expr {$end - 1}]] {
1148 set selected_paths($path) 1
1150 $w tag add in_sel $begin.0 [expr {$end + 1}].0
1153 ######################################################################
1157 set cursor_ptr arrow
1158 font create font_diff -family Courier -size 10
1162 eval font configure font_ui [font actual [.dummy cget -font]]
1166 font create font_uibold
1167 font create font_diffbold
1169 foreach class {Button Checkbutton Entry Label
1170 Labelframe Listbox Menu Message
1172 option add *$class.font font_ui
1179 } elseif {[is_MacOSX]} {
1187 proc apply_config {} {
1188 global repo_config font_descs
1190 foreach option $font_descs {
1191 set name [lindex $option 0]
1192 set font [lindex $option 1]
1194 foreach {cn cv} $repo_config(gui.$name) {
1195 font configure $font $cn $cv
1198 error_popup "Invalid font specified in gui.$name:\n\n$err"
1200 foreach {cn cv} [font configure $font] {
1201 font configure ${font}bold $cn $cv
1203 font configure ${font}bold -weight bold
1207 set default_config(merge.summary) false
1208 set default_config(merge.verbosity) 2
1209 set default_config(user.name) {}
1210 set default_config(user.email) {}
1212 set default_config(gui.trustmtime) false
1213 set default_config(gui.diffcontext) 5
1214 set default_config(gui.newbranchtemplate) {}
1215 set default_config(gui.fontui) [font configure font_ui]
1216 set default_config(gui.fontdiff) [font configure font_diff]
1218 {fontui font_ui {Main Font}}
1219 {fontdiff font_diff {Diff/Console Font}}
1224 ######################################################################
1226 ## feature option selection
1228 if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
1233 if {$subcommand eq {gui.sh}} {
1236 if {$subcommand eq {gui} && [llength $argv] > 0} {
1237 set subcommand [lindex $argv 0]
1238 set argv [lrange $argv 1 end]
1241 enable_option multicommit
1242 enable_option branch
1243 enable_option transport
1245 switch -- $subcommand {
1248 disable_option multicommit
1249 disable_option branch
1250 disable_option transport
1253 enable_option singlecommit
1255 disable_option multicommit
1256 disable_option branch
1257 disable_option transport
1261 ######################################################################
1269 menu .mbar -tearoff 0
1270 .mbar add cascade -label Repository -menu .mbar.repository
1271 .mbar add cascade -label Edit -menu .mbar.edit
1272 if {[is_enabled branch]} {
1273 .mbar add cascade -label Branch -menu .mbar.branch
1275 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1276 .mbar add cascade -label Commit -menu .mbar.commit
1278 if {[is_enabled transport]} {
1279 .mbar add cascade -label Merge -menu .mbar.merge
1280 .mbar add cascade -label Fetch -menu .mbar.fetch
1281 .mbar add cascade -label Push -menu .mbar.push
1283 . configure -menu .mbar
1285 # -- Repository Menu
1287 menu .mbar.repository
1289 .mbar.repository add command \
1290 -label {Browse Current Branch} \
1291 -command {new_browser $current_branch}
1292 trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1293 .mbar.repository add separator
1295 .mbar.repository add command \
1296 -label {Visualize Current Branch} \
1297 -command {do_gitk $current_branch}
1298 trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1299 .mbar.repository add command \
1300 -label {Visualize All Branches} \
1301 -command {do_gitk --all}
1302 .mbar.repository add separator
1304 if {[is_enabled multicommit]} {
1305 .mbar.repository add command -label {Database Statistics} \
1308 .mbar.repository add command -label {Compress Database} \
1311 .mbar.repository add command -label {Verify Database} \
1312 -command do_fsck_objects
1314 .mbar.repository add separator
1317 .mbar.repository add command \
1318 -label {Create Desktop Icon} \
1319 -command do_cygwin_shortcut
1320 } elseif {[is_Windows]} {
1321 .mbar.repository add command \
1322 -label {Create Desktop Icon} \
1323 -command do_windows_shortcut
1324 } elseif {[is_MacOSX]} {
1325 .mbar.repository add command \
1326 -label {Create Desktop Icon} \
1327 -command do_macosx_app
1331 .mbar.repository add command -label Quit \
1338 .mbar.edit add command -label Undo \
1339 -command {catch {[focus] edit undo}} \
1341 .mbar.edit add command -label Redo \
1342 -command {catch {[focus] edit redo}} \
1344 .mbar.edit add separator
1345 .mbar.edit add command -label Cut \
1346 -command {catch {tk_textCut [focus]}} \
1348 .mbar.edit add command -label Copy \
1349 -command {catch {tk_textCopy [focus]}} \
1351 .mbar.edit add command -label Paste \
1352 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
1354 .mbar.edit add command -label Delete \
1355 -command {catch {[focus] delete sel.first sel.last}} \
1357 .mbar.edit add separator
1358 .mbar.edit add command -label {Select All} \
1359 -command {catch {[focus] tag add sel 0.0 end}} \
1364 if {[is_enabled branch]} {
1367 .mbar.branch add command -label {Create...} \
1368 -command do_create_branch \
1370 lappend disable_on_lock [list .mbar.branch entryconf \
1371 [.mbar.branch index last] -state]
1373 .mbar.branch add command -label {Delete...} \
1374 -command do_delete_branch
1375 lappend disable_on_lock [list .mbar.branch entryconf \
1376 [.mbar.branch index last] -state]
1378 .mbar.branch add command -label {Reset...} \
1379 -command merge::reset_hard
1380 lappend disable_on_lock [list .mbar.branch entryconf \
1381 [.mbar.branch index last] -state]
1386 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1389 .mbar.commit add radiobutton \
1390 -label {New Commit} \
1391 -command do_select_commit_type \
1392 -variable selected_commit_type \
1394 lappend disable_on_lock \
1395 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1397 .mbar.commit add radiobutton \
1398 -label {Amend Last Commit} \
1399 -command do_select_commit_type \
1400 -variable selected_commit_type \
1402 lappend disable_on_lock \
1403 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1405 .mbar.commit add separator
1407 .mbar.commit add command -label Rescan \
1408 -command do_rescan \
1410 lappend disable_on_lock \
1411 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1413 .mbar.commit add command -label {Add To Commit} \
1414 -command do_add_selection
1415 lappend disable_on_lock \
1416 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1418 .mbar.commit add command -label {Add Existing To Commit} \
1419 -command do_add_all \
1421 lappend disable_on_lock \
1422 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1424 .mbar.commit add command -label {Unstage From Commit} \
1425 -command do_unstage_selection
1426 lappend disable_on_lock \
1427 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1429 .mbar.commit add command -label {Revert Changes} \
1430 -command do_revert_selection
1431 lappend disable_on_lock \
1432 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1434 .mbar.commit add separator
1436 .mbar.commit add command -label {Sign Off} \
1437 -command do_signoff \
1440 .mbar.commit add command -label Commit \
1441 -command do_commit \
1442 -accelerator $M1T-Return
1443 lappend disable_on_lock \
1444 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1449 if {[is_enabled branch]} {
1451 .mbar.merge add command -label {Local Merge...} \
1452 -command merge::dialog
1453 lappend disable_on_lock \
1454 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1455 .mbar.merge add command -label {Abort Merge...} \
1456 -command merge::reset_hard
1457 lappend disable_on_lock \
1458 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1464 if {[is_enabled transport]} {
1468 .mbar.push add command -label {Push...} \
1469 -command do_push_anywhere
1473 # -- Apple Menu (Mac OS X only)
1475 .mbar add cascade -label Apple -menu .mbar.apple
1478 .mbar.apple add command -label "About [appname]" \
1480 .mbar.apple add command -label "Options..." \
1485 .mbar.edit add separator
1486 .mbar.edit add command -label {Options...} \
1491 if {[file exists /usr/local/miga/lib/gui-miga]
1492 && [file exists .pvcsrc]} {
1494 global ui_status_value
1495 if {![lock_index update]} return
1496 set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1497 set miga_fd [open "|$cmd" r]
1498 fconfigure $miga_fd -blocking 0
1499 fileevent $miga_fd readable [list miga_done $miga_fd]
1500 set ui_status_value {Running miga...}
1502 proc miga_done {fd} {
1507 rescan [list set ui_status_value {Ready.}]
1510 .mbar add cascade -label Tools -menu .mbar.tools
1512 .mbar.tools add command -label "Migrate" \
1514 lappend disable_on_lock \
1515 [list .mbar.tools entryconf [.mbar.tools index last] -state]
1521 .mbar add cascade -label Help -menu .mbar.help
1525 .mbar.help add command -label "About [appname]" \
1530 catch {set browser $repo_config(instaweb.browser)}
1531 set doc_path [file dirname [gitexec]]
1532 set doc_path [file join $doc_path Documentation index.html]
1535 set doc_path [exec cygpath --mixed $doc_path]
1538 if {$browser eq {}} {
1541 } elseif {[is_Cygwin]} {
1542 set program_files [file dirname [exec cygpath --windir]]
1543 set program_files [file join $program_files {Program Files}]
1544 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
1545 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
1546 if {[file exists $firefox]} {
1547 set browser $firefox
1548 } elseif {[file exists $ie]} {
1551 unset program_files firefox ie
1555 if {[file isfile $doc_path]} {
1556 set doc_url "file:$doc_path"
1558 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
1561 if {$browser ne {}} {
1562 .mbar.help add command -label {Online Documentation} \
1563 -command [list exec $browser $doc_url &]
1565 unset browser doc_path doc_url
1567 # -- Standard bindings
1569 bind . <Destroy> do_quit
1570 bind all <$M1B-Key-q> do_quit
1571 bind all <$M1B-Key-Q> do_quit
1572 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
1573 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
1575 # -- Not a normal commit type invocation? Do that instead!
1577 switch -- $subcommand {
1579 if {[llength $argv] != 1} {
1580 puts stderr "usage: $argv0 browser commit"
1583 set current_branch [lindex $argv 0]
1584 new_browser $current_branch
1588 if {[llength $argv] != 2} {
1589 puts stderr "usage: $argv0 blame commit path"
1592 set current_branch [lindex $argv 0]
1593 show_blame $current_branch [lindex $argv 1]
1598 if {[llength $argv] != 0} {
1599 puts -nonewline stderr "usage: $argv0"
1600 if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
1601 puts -nonewline stderr " $subcommand"
1606 # fall through to setup UI for commits
1609 puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
1620 -text {Current Branch:} \
1624 -textvariable current_branch \
1627 pack .branch.l1 -side left
1628 pack .branch.cb -side left -fill x
1629 pack .branch -side top -fill x
1631 # -- Main Window Layout
1633 panedwindow .vpane -orient vertical
1634 panedwindow .vpane.files -orient horizontal
1635 .vpane add .vpane.files -sticky nsew -height 100 -width 200
1636 pack .vpane -anchor n -side top -fill both -expand 1
1638 # -- Index File List
1640 frame .vpane.files.index -height 100 -width 200
1641 label .vpane.files.index.title -text {Changes To Be Committed} \
1643 text $ui_index -background white -borderwidth 0 \
1644 -width 20 -height 10 \
1646 -cursor $cursor_ptr \
1647 -xscrollcommand {.vpane.files.index.sx set} \
1648 -yscrollcommand {.vpane.files.index.sy set} \
1650 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
1651 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
1652 pack .vpane.files.index.title -side top -fill x
1653 pack .vpane.files.index.sx -side bottom -fill x
1654 pack .vpane.files.index.sy -side right -fill y
1655 pack $ui_index -side left -fill both -expand 1
1656 .vpane.files add .vpane.files.index -sticky nsew
1658 # -- Working Directory File List
1660 frame .vpane.files.workdir -height 100 -width 200
1661 label .vpane.files.workdir.title -text {Changed But Not Updated} \
1663 text $ui_workdir -background white -borderwidth 0 \
1664 -width 20 -height 10 \
1666 -cursor $cursor_ptr \
1667 -xscrollcommand {.vpane.files.workdir.sx set} \
1668 -yscrollcommand {.vpane.files.workdir.sy set} \
1670 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
1671 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
1672 pack .vpane.files.workdir.title -side top -fill x
1673 pack .vpane.files.workdir.sx -side bottom -fill x
1674 pack .vpane.files.workdir.sy -side right -fill y
1675 pack $ui_workdir -side left -fill both -expand 1
1676 .vpane.files add .vpane.files.workdir -sticky nsew
1678 foreach i [list $ui_index $ui_workdir] {
1679 $i tag conf in_diff -font font_uibold
1680 $i tag conf in_sel \
1681 -background [$i cget -foreground] \
1682 -foreground [$i cget -background]
1686 # -- Diff and Commit Area
1688 frame .vpane.lower -height 300 -width 400
1689 frame .vpane.lower.commarea
1690 frame .vpane.lower.diff -relief sunken -borderwidth 1
1691 pack .vpane.lower.commarea -side top -fill x
1692 pack .vpane.lower.diff -side bottom -fill both -expand 1
1693 .vpane add .vpane.lower -sticky nsew
1695 # -- Commit Area Buttons
1697 frame .vpane.lower.commarea.buttons
1698 label .vpane.lower.commarea.buttons.l -text {} \
1701 pack .vpane.lower.commarea.buttons.l -side top -fill x
1702 pack .vpane.lower.commarea.buttons -side left -fill y
1704 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
1706 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
1707 lappend disable_on_lock \
1708 {.vpane.lower.commarea.buttons.rescan conf -state}
1710 button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
1712 pack .vpane.lower.commarea.buttons.incall -side top -fill x
1713 lappend disable_on_lock \
1714 {.vpane.lower.commarea.buttons.incall conf -state}
1716 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
1718 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
1720 button .vpane.lower.commarea.buttons.commit -text {Commit} \
1722 pack .vpane.lower.commarea.buttons.commit -side top -fill x
1723 lappend disable_on_lock \
1724 {.vpane.lower.commarea.buttons.commit conf -state}
1726 # -- Commit Message Buffer
1728 frame .vpane.lower.commarea.buffer
1729 frame .vpane.lower.commarea.buffer.header
1730 set ui_comm .vpane.lower.commarea.buffer.t
1731 set ui_coml .vpane.lower.commarea.buffer.header.l
1732 radiobutton .vpane.lower.commarea.buffer.header.new \
1733 -text {New Commit} \
1734 -command do_select_commit_type \
1735 -variable selected_commit_type \
1737 lappend disable_on_lock \
1738 [list .vpane.lower.commarea.buffer.header.new conf -state]
1739 radiobutton .vpane.lower.commarea.buffer.header.amend \
1740 -text {Amend Last Commit} \
1741 -command do_select_commit_type \
1742 -variable selected_commit_type \
1744 lappend disable_on_lock \
1745 [list .vpane.lower.commarea.buffer.header.amend conf -state]
1749 proc trace_commit_type {varname args} {
1750 global ui_coml commit_type
1751 switch -glob -- $commit_type {
1752 initial {set txt {Initial Commit Message:}}
1753 amend {set txt {Amended Commit Message:}}
1754 amend-initial {set txt {Amended Initial Commit Message:}}
1755 amend-merge {set txt {Amended Merge Commit Message:}}
1756 merge {set txt {Merge Commit Message:}}
1757 * {set txt {Commit Message:}}
1759 $ui_coml conf -text $txt
1761 trace add variable commit_type write trace_commit_type
1762 pack $ui_coml -side left -fill x
1763 pack .vpane.lower.commarea.buffer.header.amend -side right
1764 pack .vpane.lower.commarea.buffer.header.new -side right
1766 text $ui_comm -background white -borderwidth 1 \
1769 -autoseparators true \
1771 -width 75 -height 9 -wrap none \
1773 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
1774 scrollbar .vpane.lower.commarea.buffer.sby \
1775 -command [list $ui_comm yview]
1776 pack .vpane.lower.commarea.buffer.header -side top -fill x
1777 pack .vpane.lower.commarea.buffer.sby -side right -fill y
1778 pack $ui_comm -side left -fill y
1779 pack .vpane.lower.commarea.buffer -side left -fill y
1781 # -- Commit Message Buffer Context Menu
1783 set ctxm .vpane.lower.commarea.buffer.ctxm
1784 menu $ctxm -tearoff 0
1787 -command {tk_textCut $ui_comm}
1790 -command {tk_textCopy $ui_comm}
1793 -command {tk_textPaste $ui_comm}
1796 -command {$ui_comm delete sel.first sel.last}
1799 -label {Select All} \
1800 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
1804 $ui_comm tag add sel 0.0 end
1805 tk_textCopy $ui_comm
1806 $ui_comm tag remove sel 0.0 end
1812 bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
1816 proc trace_current_diff_path {varname args} {
1817 global current_diff_path diff_actions file_states
1818 if {$current_diff_path eq {}} {
1824 set p $current_diff_path
1825 set s [mapdesc [lindex $file_states($p) 0] $p]
1827 set p [escape_path $p]
1831 .vpane.lower.diff.header.status configure -text $s
1832 .vpane.lower.diff.header.file configure -text $f
1833 .vpane.lower.diff.header.path configure -text $p
1834 foreach w $diff_actions {
1838 trace add variable current_diff_path write trace_current_diff_path
1840 frame .vpane.lower.diff.header -background orange
1841 label .vpane.lower.diff.header.status \
1842 -background orange \
1843 -width $max_status_desc \
1846 label .vpane.lower.diff.header.file \
1847 -background orange \
1850 label .vpane.lower.diff.header.path \
1851 -background orange \
1854 pack .vpane.lower.diff.header.status -side left
1855 pack .vpane.lower.diff.header.file -side left
1856 pack .vpane.lower.diff.header.path -fill x
1857 set ctxm .vpane.lower.diff.header.ctxm
1858 menu $ctxm -tearoff 0
1866 -- $current_diff_path
1868 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1869 bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
1873 frame .vpane.lower.diff.body
1874 set ui_diff .vpane.lower.diff.body.t
1875 text $ui_diff -background white -borderwidth 0 \
1876 -width 80 -height 15 -wrap none \
1878 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
1879 -yscrollcommand {.vpane.lower.diff.body.sby set} \
1881 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
1882 -command [list $ui_diff xview]
1883 scrollbar .vpane.lower.diff.body.sby -orient vertical \
1884 -command [list $ui_diff yview]
1885 pack .vpane.lower.diff.body.sbx -side bottom -fill x
1886 pack .vpane.lower.diff.body.sby -side right -fill y
1887 pack $ui_diff -side left -fill both -expand 1
1888 pack .vpane.lower.diff.header -side top -fill x
1889 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
1891 $ui_diff tag conf d_cr -elide true
1892 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
1893 $ui_diff tag conf d_+ -foreground {#00a000}
1894 $ui_diff tag conf d_- -foreground red
1896 $ui_diff tag conf d_++ -foreground {#00a000}
1897 $ui_diff tag conf d_-- -foreground red
1898 $ui_diff tag conf d_+s \
1899 -foreground {#00a000} \
1900 -background {#e2effa}
1901 $ui_diff tag conf d_-s \
1903 -background {#e2effa}
1904 $ui_diff tag conf d_s+ \
1905 -foreground {#00a000} \
1907 $ui_diff tag conf d_s- \
1911 $ui_diff tag conf d<<<<<<< \
1912 -foreground orange \
1914 $ui_diff tag conf d======= \
1915 -foreground orange \
1917 $ui_diff tag conf d>>>>>>> \
1918 -foreground orange \
1921 $ui_diff tag raise sel
1923 # -- Diff Body Context Menu
1925 set ctxm .vpane.lower.diff.body.ctxm
1926 menu $ctxm -tearoff 0
1929 -command reshow_diff
1930 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1933 -command {tk_textCopy $ui_diff}
1934 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1936 -label {Select All} \
1937 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
1938 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1942 $ui_diff tag add sel 0.0 end
1943 tk_textCopy $ui_diff
1944 $ui_diff tag remove sel 0.0 end
1946 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1949 -label {Apply/Reverse Hunk} \
1950 -command {apply_hunk $cursorX $cursorY}
1951 set ui_diff_applyhunk [$ctxm index last]
1952 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
1955 -label {Decrease Font Size} \
1956 -command {incr_font_size font_diff -1}
1957 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1959 -label {Increase Font Size} \
1960 -command {incr_font_size font_diff 1}
1961 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1964 -label {Show Less Context} \
1965 -command {if {$repo_config(gui.diffcontext) >= 2} {
1966 incr repo_config(gui.diffcontext) -1
1969 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1971 -label {Show More Context} \
1973 incr repo_config(gui.diffcontext)
1976 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1978 $ctxm add command -label {Options...} \
1980 bind_button3 $ui_diff "
1983 if {\$ui_index eq \$current_diff_side} {
1984 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
1986 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
1988 tk_popup $ctxm %X %Y
1990 unset ui_diff_applyhunk
1994 label .status -textvariable ui_status_value \
1999 pack .status -anchor w -side bottom -fill x
2004 set gm $repo_config(gui.geometry)
2005 wm geometry . [lindex $gm 0]
2006 .vpane sash place 0 \
2007 [lindex [.vpane sash coord 0] 0] \
2009 .vpane.files sash place 0 \
2011 [lindex [.vpane.files sash coord 0] 1]
2017 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
2018 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
2019 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
2020 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
2021 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
2022 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
2023 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
2024 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
2025 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
2026 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2027 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2029 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
2030 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
2031 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
2032 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
2033 bind $ui_diff <$M1B-Key-v> {break}
2034 bind $ui_diff <$M1B-Key-V> {break}
2035 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2036 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2037 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
2038 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
2039 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
2040 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
2041 bind $ui_diff <Key-k> {catch {%W yview scroll -1 units};break}
2042 bind $ui_diff <Key-j> {catch {%W yview scroll 1 units};break}
2043 bind $ui_diff <Key-h> {catch {%W xview scroll -1 units};break}
2044 bind $ui_diff <Key-l> {catch {%W xview scroll 1 units};break}
2045 bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
2046 bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
2047 bind $ui_diff <Button-1> {focus %W}
2049 if {[is_enabled branch]} {
2050 bind . <$M1B-Key-n> do_create_branch
2051 bind . <$M1B-Key-N> do_create_branch
2054 bind all <Key-F5> do_rescan
2055 bind all <$M1B-Key-r> do_rescan
2056 bind all <$M1B-Key-R> do_rescan
2057 bind . <$M1B-Key-s> do_signoff
2058 bind . <$M1B-Key-S> do_signoff
2059 bind . <$M1B-Key-i> do_add_all
2060 bind . <$M1B-Key-I> do_add_all
2061 bind . <$M1B-Key-Return> do_commit
2062 foreach i [list $ui_index $ui_workdir] {
2063 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
2064 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2065 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
2069 set file_lists($ui_index) [list]
2070 set file_lists($ui_workdir) [list]
2072 wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2073 focus -force $ui_comm
2075 # -- Warn the user about environmental problems. Cygwin's Tcl
2076 # does *not* pass its env array onto any processes it spawns.
2077 # This means that git processes get none of our environment.
2082 set msg "Possible environment issues exist.
2084 The following environment variables are probably
2085 going to be ignored by any Git subprocess run
2089 foreach name [array names env] {
2090 switch -regexp -- $name {
2091 {^GIT_INDEX_FILE$} -
2092 {^GIT_OBJECT_DIRECTORY$} -
2093 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
2095 {^GIT_EXTERNAL_DIFF$} -
2099 {^GIT_CONFIG_LOCAL$} -
2100 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
2101 append msg " - $name\n"
2104 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
2105 append msg " - $name\n"
2107 set suggest_user $name
2111 if {$ignored_env > 0} {
2113 This is due to a known issue with the
2114 Tcl binary distributed by Cygwin."
2116 if {$suggest_user ne {}} {
2119 A good replacement for $suggest_user
2120 is placing values for the user.name and
2121 user.email settings into your personal
2127 unset ignored_env msg suggest_user name
2130 # -- Only initialize complex UI if we are going to stay running.
2132 if {[is_enabled transport]} {
2136 populate_branch_menu
2141 # -- Only suggest a gc run if we are going to stay running.
2143 if {[is_enabled multicommit]} {
2144 set object_limit 2000
2145 if {[is_Windows]} {set object_limit 200}
2146 regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
2147 if {$objects_current >= $object_limit} {
2149 "This repository currently has $objects_current loose objects.
2151 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2153 Compress the database now?"] eq yes} {
2157 unset object_limit _junk objects_current
2160 lock_index begin-read