2 # Tcl ignores the next line -*- tcl -*- \
3 if test "z$*" = zversion \
4 ||
test "z$*" = z--version
; \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
11 set appvers
{@@GITGUI_VERSION@@
}
13 Copyright ©
2006, 2007 Shawn Pearce
, et. al.
15 This program is free software
; you can redistribute it and
/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation
; either version
2 of the License
, or
18 (at your option
) any later version.
20 This program is distributed
in the hope that it will be useful
,
21 but WITHOUT ANY WARRANTY
; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License
for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program
; if not
, write to the Free Software
27 Foundation
, Inc.
, 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
}
29 ######################################################################
31 ## Tcl/Tk sanity check
33 if {[catch
{package require Tcl
8.4} err
]
34 ||
[catch
{package require Tk
8.4} err
]
40 -title "git-gui: fatal error" \
45 ######################################################################
47 ## enable verbose loading?
49 if {![catch
{set _verbose
$env(GITGUI_VERBOSE
)}]} {
51 rename auto_load real__auto_load
52 proc auto_load
{name args
} {
53 puts stderr
"auto_load $name"
54 return [uplevel
1 real__auto_load
$name $args]
56 rename
source real__source
58 puts stderr
"source $name"
59 uplevel
1 real__source
$name
63 ######################################################################
65 ## configure our library
67 set oguilib
{@@GITGUI_LIBDIR@@
}
68 set oguirel
{@@GITGUI_RELATIVE@@
}
69 if {$oguirel eq
{1}} {
70 set oguilib
[file dirname [file dirname [file normalize
$argv0]]]
71 set oguilib
[file join $oguilib share git-gui lib
]
72 } elseif
{[string match @@
* $oguirel]} {
73 set oguilib
[file join [file dirname [file normalize
$argv0]] lib
]
76 set idx
[file join $oguilib tclIndex
]
77 if {[catch
{set fd
[open
$idx r
]} err
]} {
82 -title "git-gui: fatal error" \
86 if {[gets
$fd] eq
{# Autogenerated by git-gui Makefile}} {
88 while {[gets
$fd n
] >= 0} {
89 if {$n ne
{} && ![string match
#* $n]} {
101 if {[lsearch
-exact $loaded $p] >= 0} continue
102 source [file join $oguilib $p]
107 set auto_path
[concat
[list
$oguilib] $auto_path]
109 unset -nocomplain oguirel idx fd
111 ######################################################################
115 set _appname
[lindex
[file split $argv0] end
]
132 return [eval [list
file join $_gitdir] $args]
135 proc gitexec
{args
} {
137 if {$_gitexec eq
{}} {
138 if {[catch
{set _gitexec
[git
--exec-path]} err
]} {
139 error
"Git not installed?\n\n$err"
142 set _gitexec
[exec cygpath \
147 set _gitexec
[file normalize
$_gitexec]
153 return [eval [list
file join $_gitexec] $args]
162 global tcl_platform tk_library
163 if {[tk windowingsystem
] eq
{aqua
}} {
171 if {$tcl_platform(platform
) eq
{windows
}} {
178 global tcl_platform _iscygwin
179 if {$_iscygwin eq
{}} {
180 if {$tcl_platform(platform
) eq
{windows
}} {
181 if {[catch
{set p
[exec cygpath
--windir]} err
]} {
193 proc is_enabled
{option
} {
194 global enabled_options
195 if {[catch
{set on
$enabled_options($option)}]} {return 0}
199 proc enable_option
{option
} {
200 global enabled_options
201 set enabled_options
($option) 1
204 proc disable_option
{option
} {
205 global enabled_options
206 set enabled_options
($option) 0
209 ######################################################################
213 proc is_many_config
{name
} {
214 switch
-glob -- $name {
223 proc is_config_true
{name
} {
225 if {[catch
{set v
$repo_config($name)}]} {
227 } elseif
{$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes}} {
234 proc get_config
{name
} {
236 if {[catch
{set v
$repo_config($name)}]} {
243 proc load_config
{include_global
} {
244 global repo_config global_config default_config
246 array
unset global_config
247 if {$include_global} {
249 set fd_rc
[git_read config
--global --list]
250 while {[gets
$fd_rc line
] >= 0} {
251 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
252 if {[is_many_config
$name]} {
253 lappend global_config
($name) $value
255 set global_config
($name) $value
263 array
unset repo_config
265 set fd_rc
[git_read config
--list]
266 while {[gets
$fd_rc line
] >= 0} {
267 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
268 if {[is_many_config
$name]} {
269 lappend repo_config
($name) $value
271 set repo_config
($name) $value
278 foreach name
[array names default_config
] {
279 if {[catch
{set v
$global_config($name)}]} {
280 set global_config
($name) $default_config($name)
282 if {[catch
{set v
$repo_config($name)}]} {
283 set repo_config
($name) $default_config($name)
288 ######################################################################
292 proc _git_cmd
{name
} {
295 if {[catch
{set v
$_git_cmd_path($name)}]} {
299 --exec-path { return [list $
::_git
$name] }
302 set p
[gitexec git-
$name$
::_search_exe
]
303 if {[file exists
$p]} {
305 } elseif
{[is_Windows
] && [file exists
[gitexec git-
$name]]} {
306 # Try to determine what sort of magic will make
307 # git-$name go and do its thing, because native
308 # Tcl on Windows doesn't know it.
310 set p
[gitexec git-
$name]
317 #!*perl { set i perl }
318 #!*python { set i python }
319 default
{ error
"git-$name is not supported: $s" }
323 if {![info exists interp
]} {
324 set interp
[_which
$i]
327 error
"git-$name requires $i (not in PATH)"
329 set v
[list
$interp $p]
331 # Assume it is builtin to git somehow and we
332 # aren't actually able to see a file for it.
334 set v
[list $
::_git
$name]
336 set _git_cmd_path
($name) $v
342 global env _search_exe _search_path
344 if {$_search_path eq
{}} {
346 set _search_path
[split [exec cygpath \
352 } elseif
{[is_Windows
]} {
353 set _search_path
[split $env(PATH
) {;}]
356 set _search_path
[split $env(PATH
) :]
361 foreach p
$_search_path {
362 set p
[file join $p $what$_search_exe]
363 if {[file exists
$p]} {
364 return [file normalize
$p]
374 switch
-- [lindex
$args 0] {
388 set args
[lrange
$args 1 end
]
391 set cmdp
[_git_cmd
[lindex
$args 0]]
392 set args
[lrange
$args 1 end
]
394 return [eval $opt $cmdp $args]
397 proc _open_stdout_stderr
{cmd
} {
401 if { [lindex
$cmd end
] eq
{2>@
1}
402 && $err eq
{can not
find channel named
"1"}
404 # Older versions of Tcl 8.4 don't have this 2>@1 IO
405 # redirect operator. Fallback to |& cat for those.
406 # The command was not actually started, so its safe
407 # to try to start it a second time.
409 set fd
[open
[concat \
410 [lrange
$cmd 0 end-1
] \
417 fconfigure
$fd -eofchar {}
421 proc git_read
{args
} {
425 switch
-- [lindex
$args 0] {
443 set args
[lrange
$args 1 end
]
446 set cmdp
[_git_cmd
[lindex
$args 0]]
447 set args
[lrange
$args 1 end
]
449 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
452 proc git_write
{args
} {
456 switch
-- [lindex
$args 0] {
470 set args
[lrange
$args 1 end
]
473 set cmdp
[_git_cmd
[lindex
$args 0]]
474 set args
[lrange
$args 1 end
]
476 return [open
[concat
$opt $cmdp $args] w
]
480 regsub
-all ' $value "'\\''" value
484 proc load_current_branch {} {
485 global current_branch is_detached
487 set fd [open [gitdir HEAD] r]
488 if {[gets $fd ref] < 1} {
493 set pfx {ref: refs/heads/}
494 set len [string length $pfx]
495 if {[string equal -length $len $pfx $ref]} {
496 # We're on a branch. It might not exist. But
497 # HEAD looks good enough to be a branch.
499 set current_branch [string range $ref $len end]
502 # Assume this is a detached head.
504 set current_branch HEAD
509 auto_load tk_optionMenu
510 rename tk_optionMenu real__tkOptionMenu
511 proc tk_optionMenu {w varName args} {
512 set m [eval real__tkOptionMenu $w $varName $args]
513 $m configure -font font_ui
514 $w configure -font font_ui
518 ######################################################################
522 set _git [_which git]
524 catch {wm withdraw .}
525 error_popup "Cannot
find git
in PATH.
"
528 set _nice [_which nice]
530 ######################################################################
534 if {[catch {set _git_version [git --version]} err]} {
535 catch {wm withdraw .}
536 error_popup "Cannot determine Git version
:
540 [appname
] requires Git
1.5.0 or later.
"
543 if {![regsub {^git version } $_git_version {} _git_version]} {
544 catch {wm withdraw .}
545 error_popup "Cannot parse Git version string
:\n\n$_git_version"
548 regsub -- {-dirty$} $_git_version {} _git_version
549 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
550 regsub {\.rc[0-9]+$} $_git_version {} _git_version
551 regsub {\.GIT$} $_git_version {} _git_version
553 proc git-version {args} {
556 switch [llength $args] {
562 set op [lindex $args 0]
563 set vr [lindex $args 1]
564 set cm [package vcompare $_git_version $vr]
565 return [expr $cm $op 0]
569 set type [lindex $args 0]
570 set name [lindex $args 1]
571 set parm [lindex $args 2]
572 set body [lindex $args 3]
574 if {($type ne {proc} && $type ne {method})} {
575 error "Invalid arguments to git-version
"
577 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
578 error "Last arm of
$type $name must be default
"
581 foreach {op vr cb} [lrange $body 0 end-2] {
582 if {[git-version $op $vr]} {
583 return [uplevel [list $type $name $parm $cb]]
587 return [uplevel [list $type $name $parm [lindex $body end]]]
591 error "git-version
>= x
"
597 if {[git-version < 1.5]} {
598 catch {wm withdraw .}
599 error_popup "[appname
] requires Git
1.5.0 or later.
601 You are using
[git-version
]:
607 ######################################################################
612 set _gitdir $env(GIT_DIR)
616 set _gitdir [git rev-parse --git-dir]
617 set _prefix [git rev-parse --show-prefix]
619 catch {wm withdraw .}
620 error_popup "Cannot
find the git directory
:\n\n$err"
623 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
624 catch {set _gitdir [exec cygpath --unix $_gitdir]}
626 if {![file isdirectory $_gitdir]} {
627 catch {wm withdraw .}
628 error_popup "Git directory not found
:\n\n$_gitdir"
631 if {[lindex [file split $_gitdir] end] ne {.git}} {
632 catch {wm withdraw .}
633 error_popup "Cannot use funny .git directory
:\n\n$_gitdir"
636 if {[catch {cd [file dirname $_gitdir]} err]} {
637 catch {wm withdraw .}
638 error_popup "No working directory
[file dirname $_gitdir]:\n\n$err"
641 set _reponame [lindex [file split \
642 [file normalize [file dirname $_gitdir]]] \
645 ######################################################################
649 set current_diff_path {}
650 set current_diff_side {}
651 set diff_actions [list]
655 set MERGE_HEAD [list]
658 set current_branch {}
660 set current_diff_path {}
661 set selected_commit_type new
663 ######################################################################
671 set disable_on_lock [list]
672 set index_lock_type none
674 proc lock_index {type} {
675 global index_lock_type disable_on_lock
677 if {$index_lock_type eq {none}} {
678 set index_lock_type $type
679 foreach w $disable_on_lock {
680 uplevel #0 $w disabled
683 } elseif {$index_lock_type eq "begin-
$type"} {
684 set index_lock_type $type
690 proc unlock_index {} {
691 global index_lock_type disable_on_lock
693 set index_lock_type none
694 foreach w $disable_on_lock {
699 ######################################################################
703 proc repository_state {ctvar hdvar mhvar} {
704 global current_branch
705 upvar $ctvar ct $hdvar hd $mhvar mh
710 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
716 set merge_head [gitdir MERGE_HEAD]
717 if {[file exists $merge_head]} {
719 set fd_mh [open $merge_head r]
720 while {[gets $fd_mh line] >= 0} {
731 global PARENT empty_tree
733 set p [lindex $PARENT 0]
737 if {$empty_tree eq {}} {
738 set empty_tree [git mktree << {}]
743 proc rescan {after {honor_trustmtime 1}} {
744 global HEAD PARENT MERGE_HEAD commit_type
745 global ui_index ui_workdir ui_comm
746 global rescan_active file_states
749 if {$rescan_active > 0 || ![lock_index read]} return
751 repository_state newType newHEAD newMERGE_HEAD
752 if {[string match amend* $commit_type]
753 && $newType eq {normal}
754 && $newHEAD eq $HEAD} {
758 set MERGE_HEAD $newMERGE_HEAD
759 set commit_type $newType
762 array unset file_states
764 if {![$ui_comm edit modified]
765 || [string trim [$ui_comm get 0.0 end]] eq {}} {
766 if {[string match amend* $commit_type]} {
767 } elseif {[load_message GITGUI_MSG]} {
768 } elseif {[load_message MERGE_MSG]} {
769 } elseif {[load_message SQUASH_MSG]} {
772 $ui_comm edit modified false
775 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
776 rescan_stage2 {} $after
779 ui_status {Refreshing file status...}
780 set fd_rf [git_read update-index \
786 fconfigure $fd_rf -blocking 0 -translation binary
787 fileevent $fd_rf readable \
788 [list rescan_stage2 $fd_rf $after]
792 proc rescan_stage2 {fd after} {
793 global rescan_active buf_rdi buf_rdf buf_rlo
797 if {![eof $fd]} return
801 set ls_others [list --exclude-per-directory=.gitignore]
802 set info_exclude [gitdir info exclude]
803 if {[file readable $info_exclude]} {
804 lappend ls_others "--exclude-from=$info_exclude"
812 ui_status {Scanning for modified files ...}
813 set fd_di [git_read diff-index --cached -z [PARENT]]
814 set fd_df [git_read diff-files -z]
815 set fd_lo [eval git_read ls-files --others -z $ls_others]
817 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
818 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
819 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
820 fileevent $fd_di readable [list read_diff_index $fd_di $after]
821 fileevent $fd_df readable [list read_diff_files $fd_df $after]
822 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
825 proc load_message {file} {
829 if {[file isfile $f]} {
830 if {[catch {set fd [open $f r]}]} {
833 fconfigure $fd -eofchar {}
834 set content [string trim [read $fd]]
836 regsub -all -line {[ \r\t]+$} $content {} content
837 $ui_comm delete 0.0 end
838 $ui_comm insert end $content
844 proc read_diff_index {fd after} {
847 append buf_rdi [read $fd]
849 set n [string length $buf_rdi]
851 set z1 [string first "\
0" $buf_rdi $c]
854 set z2 [string first "\
0" $buf_rdi $z1]
858 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
859 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
861 [encoding convertfrom $p] \
863 [list [lindex $i 0] [lindex $i 2]] \
869 set buf_rdi [string range $buf_rdi $c end]
874 rescan_done $fd buf_rdi $after
877 proc read_diff_files {fd after} {
880 append buf_rdf [read $fd]
882 set n [string length $buf_rdf]
884 set z1 [string first "\
0" $buf_rdf $c]
887 set z2 [string first "\
0" $buf_rdf $z1]
891 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
892 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
894 [encoding convertfrom $p] \
897 [list [lindex $i 0] [lindex $i 2]]
902 set buf_rdf [string range $buf_rdf $c end]
907 rescan_done $fd buf_rdf $after
910 proc read_ls_others {fd after} {
913 append buf_rlo [read $fd]
914 set pck [split $buf_rlo "\
0"]
915 set buf_rlo [lindex $pck end]
916 foreach p [lrange $pck 0 end-1] {
917 merge_state [encoding convertfrom $p] ?O
919 rescan_done $fd buf_rlo $after
922 proc rescan_done {fd buf after} {
923 global rescan_active current_diff_path
924 global file_states repo_config
927 if {![eof $fd]} return
930 if {[incr rescan_active -1] > 0} return
935 if {$current_diff_path ne {}} reshow_diff
939 proc prune_selection {} {
940 global file_states selected_paths
942 foreach path [array names selected_paths] {
943 if {[catch {set still_here $file_states($path)}]} {
944 unset selected_paths($path)
949 ######################################################################
953 proc mapicon {w state path} {
956 if {[catch {set r $all_icons($state$w)}]} {
957 puts "error
: no icon
for $w state
={$state} $path"
963 proc mapdesc {state path} {
966 if {[catch {set r $all_descs($state)}]} {
967 puts "error
: no desc
for state
={$state} $path"
973 proc ui_status {msg} {
974 $::main_status show $msg
977 proc ui_ready {{test {}}} {
978 $::main_status show {Ready.} $test
981 proc escape_path {path} {
982 regsub -all {\\} $path "\\\\" path
983 regsub -all "\n" $path "\\n
" path
987 proc short_path {path} {
988 return [escape_path [lindex [file split $path] end]]
992 set null_sha1 [string repeat 0 40]
994 proc merge_state {path new_state {head_info {}} {index_info {}}} {
995 global file_states next_icon_id null_sha1
997 set s0 [string index $new_state 0]
998 set s1 [string index $new_state 1]
1000 if {[catch {set info $file_states($path)}]} {
1002 set icon n[incr next_icon_id]
1004 set state [lindex $info 0]
1005 set icon [lindex $info 1]
1006 if {$head_info eq {}} {set head_info [lindex $info 2]}
1007 if {$index_info eq {}} {set index_info [lindex $info 3]}
1010 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1011 elseif {$s0 eq {_}} {set s0 _}
1013 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1014 elseif {$s1 eq {_}} {set s1 _}
1016 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1017 set head_info [list 0 $null_sha1]
1018 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1019 && $head_info eq {}} {
1020 set head_info $index_info
1023 set file_states($path) [list $s0$s1 $icon \
1024 $head_info $index_info \
1029 proc display_file_helper {w path icon_name old_m new_m} {
1032 if {$new_m eq {_}} {
1033 set lno [lsearch -sorted -exact $file_lists($w) $path]
1035 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1037 $w conf -state normal
1038 $w delete $lno.0 [expr {$lno + 1}].0
1039 $w conf -state disabled
1041 } elseif {$old_m eq {_} && $new_m ne {_}} {
1042 lappend file_lists($w) $path
1043 set file_lists($w) [lsort -unique $file_lists($w)]
1044 set lno [lsearch -sorted -exact $file_lists($w) $path]
1046 $w conf -state normal
1047 $w image create $lno.0 \
1048 -align center -padx 5 -pady 1 \
1050 -image [mapicon $w $new_m $path]
1051 $w insert $lno.1 "[escape_path
$path]\n"
1052 $w conf -state disabled
1053 } elseif {$old_m ne $new_m} {
1054 $w conf -state normal
1055 $w image conf $icon_name -image [mapicon $w $new_m $path]
1056 $w conf -state disabled
1060 proc display_file {path state} {
1061 global file_states selected_paths
1062 global ui_index ui_workdir
1064 set old_m [merge_state $path $state]
1065 set s $file_states($path)
1066 set new_m [lindex $s 0]
1067 set icon_name [lindex $s 1]
1069 set o [string index $old_m 0]
1070 set n [string index $new_m 0]
1077 display_file_helper $ui_index $path $icon_name $o $n
1079 if {[string index $old_m 0] eq {U}} {
1082 set o [string index $old_m 1]
1084 if {[string index $new_m 0] eq {U}} {
1087 set n [string index $new_m 1]
1089 display_file_helper $ui_workdir $path $icon_name $o $n
1091 if {$new_m eq {__}} {
1092 unset file_states($path)
1093 catch {unset selected_paths($path)}
1097 proc display_all_files_helper {w path icon_name m} {
1100 lappend file_lists($w) $path
1101 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1102 $w image create end \
1103 -align center -padx 5 -pady 1 \
1105 -image [mapicon $w $m $path]
1106 $w insert end "[escape_path
$path]\n"
1109 proc display_all_files {} {
1110 global ui_index ui_workdir
1111 global file_states file_lists
1114 $ui_index conf -state normal
1115 $ui_workdir conf -state normal
1117 $ui_index delete 0.0 end
1118 $ui_workdir delete 0.0 end
1121 set file_lists($ui_index) [list]
1122 set file_lists($ui_workdir) [list]
1124 foreach path [lsort [array names file_states]] {
1125 set s $file_states($path)
1127 set icon_name [lindex $s 1]
1129 set s [string index $m 0]
1130 if {$s ne {U} && $s ne {_}} {
1131 display_all_files_helper $ui_index $path \
1135 if {[string index $m 0] eq {U}} {
1138 set s [string index $m 1]
1141 display_all_files_helper $ui_workdir $path \
1146 $ui_index conf -state disabled
1147 $ui_workdir conf -state disabled
1150 ######################################################################
1155 #define mask_width 14
1156 #define mask_height 15
1157 static unsigned char mask_bits[] = {
1158 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1159 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1160 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1163 image create bitmap file_plain -background white -foreground black -data {
1164 #define plain_width 14
1165 #define plain_height 15
1166 static unsigned char plain_bits[] = {
1167 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1168 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1169 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1170 } -maskdata $filemask
1172 image create bitmap file_mod -background white -foreground blue -data {
1173 #define mod_width 14
1174 #define mod_height 15
1175 static unsigned char mod_bits[] = {
1176 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1177 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1178 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1179 } -maskdata $filemask
1181 image create bitmap file_fulltick -background white -foreground "#007000" -data {
1182 #define file_fulltick_width 14
1183 #define file_fulltick_height 15
1184 static unsigned char file_fulltick_bits
[] = {
1185 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1186 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1187 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1188 } -maskdata $filemask
1190 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
1191 #define parttick_width 14
1192 #define parttick_height 15
1193 static unsigned char parttick_bits
[] = {
1194 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1195 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1196 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1197 } -maskdata $filemask
1199 image create bitmap file_question
-background white
-foreground black
-data {
1200 #define file_question_width 14
1201 #define file_question_height 15
1202 static unsigned char file_question_bits
[] = {
1203 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1204 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1205 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1206 } -maskdata $filemask
1208 image create bitmap file_removed
-background white
-foreground red
-data {
1209 #define file_removed_width 14
1210 #define file_removed_height 15
1211 static unsigned char file_removed_bits
[] = {
1212 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1213 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1214 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1215 } -maskdata $filemask
1217 image create bitmap file_merge
-background white
-foreground blue
-data {
1218 #define file_merge_width 14
1219 #define file_merge_height 15
1220 static unsigned char file_merge_bits
[] = {
1221 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1222 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1223 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1224 } -maskdata $filemask
1227 #define file_width 18
1228 #define file_height 18
1229 static unsigned char file_bits
[] = {
1230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
1231 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
1232 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
1233 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
1234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1236 image create bitmap file_dir
-background white
-foreground blue \
1237 -data $file_dir_data -maskdata $file_dir_data
1240 set file_uplevel_data
{
1242 #define up_height 15
1243 static unsigned char up_bits
[] = {
1244 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
1245 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
1246 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
1248 image create bitmap file_uplevel
-background white
-foreground red \
1249 -data $file_uplevel_data -maskdata $file_uplevel_data
1250 unset file_uplevel_data
1252 set ui_index .vpane.files.index.list
1253 set ui_workdir .vpane.files.workdir.list
1255 set all_icons
(_
$ui_index) file_plain
1256 set all_icons
(A
$ui_index) file_fulltick
1257 set all_icons
(M
$ui_index) file_fulltick
1258 set all_icons
(D
$ui_index) file_removed
1259 set all_icons
(U
$ui_index) file_merge
1261 set all_icons
(_
$ui_workdir) file_plain
1262 set all_icons
(M
$ui_workdir) file_mod
1263 set all_icons
(D
$ui_workdir) file_question
1264 set all_icons
(U
$ui_workdir) file_merge
1265 set all_icons
(O
$ui_workdir) file_plain
1267 set max_status_desc
0
1271 {_M
"Modified, not staged"}
1272 {M_
"Staged for commit"}
1273 {MM
"Portions staged for commit"}
1274 {MD
"Staged for commit, missing"}
1276 {_O
"Untracked, not staged"}
1277 {A_
"Staged for commit"}
1278 {AM
"Portions staged for commit"}
1279 {AD
"Staged for commit, missing"}
1282 {D_
"Staged for removal"}
1283 {DO
"Staged for removal, still present"}
1285 {U_
"Requires merge resolution"}
1286 {UU
"Requires merge resolution"}
1287 {UM
"Requires merge resolution"}
1288 {UD
"Requires merge resolution"}
1290 if {$max_status_desc < [string length
[lindex
$i 1]]} {
1291 set max_status_desc
[string length
[lindex
$i 1]]
1293 set all_descs
([lindex
$i 0]) [lindex
$i 1]
1297 ######################################################################
1301 proc bind_button3
{w cmd
} {
1302 bind $w <Any-Button-3
> $cmd
1304 bind $w <Control-Button-1
> $cmd
1308 proc scrollbar2many
{list mode args
} {
1309 foreach w
$list {eval $w $mode $args}
1312 proc many2scrollbar
{list mode sb top bottom
} {
1313 $sb set $top $bottom
1314 foreach w
$list {$w $mode moveto
$top}
1317 proc incr_font_size
{font
{amt
1}} {
1318 set sz
[font configure
$font -size]
1320 font configure
$font -size $sz
1321 font configure
${font}bold
-size $sz
1322 font configure
${font}italic
-size $sz
1325 ######################################################################
1329 set starting_gitk_msg
{Starting gitk... please
wait...
}
1331 proc do_gitk
{revs
} {
1332 # -- Always start gitk through whatever we were loaded with. This
1333 # lets us bypass using shell process on Windows systems.
1335 set exe
[file join [file dirname $
::_git
] gitk
]
1336 set cmd
[list
[info nameofexecutable
] $exe]
1337 if {! [file exists
$exe]} {
1338 error_popup
"Unable to start gitk:\n\n$exe does not exist"
1340 eval exec $cmd $revs &
1341 ui_status $
::starting_gitk_msg
1343 ui_ready
$starting_gitk_msg
1351 global ui_comm is_quitting repo_config commit_type
1353 if {$is_quitting} return
1356 if {[winfo exists
$ui_comm]} {
1357 # -- Stash our current commit buffer.
1359 set save
[gitdir GITGUI_MSG
]
1360 set msg
[string trim
[$ui_comm get
0.0 end
]]
1361 regsub
-all -line {[ \r\t]+$
} $msg {} msg
1362 if {(![string match amend
* $commit_type]
1363 ||
[$ui_comm edit modified
])
1366 set fd
[open
$save w
]
1367 puts
-nonewline $fd $msg
1371 catch
{file delete
$save}
1374 # -- Stash our current window geometry into this repository.
1376 set cfg_geometry
[list
]
1377 lappend cfg_geometry
[wm geometry .
]
1378 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
1379 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
1380 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
1383 if {$cfg_geometry ne
$rc_geometry} {
1384 catch
{git config gui.geometry
$cfg_geometry}
1399 proc toggle_or_diff
{w x y
} {
1400 global file_states file_lists current_diff_path ui_index ui_workdir
1401 global last_clicked selected_paths
1403 set pos
[split [$w index @
$x,$y] .
]
1404 set lno
[lindex
$pos 0]
1405 set col [lindex
$pos 1]
1406 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1412 set last_clicked
[list
$w $lno]
1413 array
unset selected_paths
1414 $ui_index tag remove in_sel
0.0 end
1415 $ui_workdir tag remove in_sel
0.0 end
1418 if {$current_diff_path eq
$path} {
1419 set after
{reshow_diff
;}
1423 if {$w eq
$ui_index} {
1425 "Unstaging [short_path $path] from commit" \
1427 [concat
$after [list ui_ready
]]
1428 } elseif
{$w eq
$ui_workdir} {
1430 "Adding [short_path $path]" \
1432 [concat
$after [list ui_ready
]]
1435 show_diff
$path $w $lno
1439 proc add_one_to_selection
{w x y
} {
1440 global file_lists last_clicked selected_paths
1442 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1443 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1449 if {$last_clicked ne
{}
1450 && [lindex
$last_clicked 0] ne
$w} {
1451 array
unset selected_paths
1452 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
1455 set last_clicked
[list
$w $lno]
1456 if {[catch
{set in_sel
$selected_paths($path)}]} {
1460 unset selected_paths
($path)
1461 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
1463 set selected_paths
($path) 1
1464 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1468 proc add_range_to_selection
{w x y
} {
1469 global file_lists last_clicked selected_paths
1471 if {[lindex
$last_clicked 0] ne
$w} {
1472 toggle_or_diff
$w $x $y
1476 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1477 set lc
[lindex
$last_clicked 1]
1486 foreach path
[lrange
$file_lists($w) \
1487 [expr {$begin - 1}] \
1488 [expr {$end - 1}]] {
1489 set selected_paths
($path) 1
1491 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
1494 ######################################################################
1498 set cursor_ptr arrow
1499 font create font_diff
-family Courier
-size 10
1503 eval font configure font_ui
[font actual
[.dummy cget
-font]]
1507 font create font_uiitalic
1508 font create font_uibold
1509 font create font_diffbold
1510 font create font_diffitalic
1512 foreach class
{Button Checkbutton Entry Label
1513 Labelframe Listbox Menu Message
1514 Radiobutton Spinbox Text
} {
1515 option add
*$class.font font_ui
1519 if {[is_Windows
] ||
[is_MacOSX
]} {
1520 option add
*Menu.tearOff
0
1531 proc apply_config
{} {
1532 global repo_config font_descs
1534 foreach option
$font_descs {
1535 set name
[lindex
$option 0]
1536 set font
[lindex
$option 1]
1538 foreach
{cn cv
} $repo_config(gui.
$name) {
1539 font configure
$font $cn $cv
1542 error_popup
"Invalid font specified in gui.$name:\n\n$err"
1544 foreach
{cn cv
} [font configure
$font] {
1545 font configure
${font}bold
$cn $cv
1546 font configure
${font}italic
$cn $cv
1548 font configure
${font}bold
-weight bold
1549 font configure
${font}italic
-slant italic
1553 set default_config
(merge.diffstat
) true
1554 set default_config
(merge.summary
) false
1555 set default_config
(merge.verbosity
) 2
1556 set default_config
(user.name
) {}
1557 set default_config
(user.email
) {}
1559 set default_config
(gui.matchtrackingbranch
) false
1560 set default_config
(gui.pruneduringfetch
) false
1561 set default_config
(gui.trustmtime
) false
1562 set default_config
(gui.diffcontext
) 5
1563 set default_config
(gui.newbranchtemplate
) {}
1564 set default_config
(gui.fontui
) [font configure font_ui
]
1565 set default_config
(gui.fontdiff
) [font configure font_diff
]
1567 {fontui font_ui
{Main Font
}}
1568 {fontdiff font_diff
{Diff
/Console Font
}}
1573 ######################################################################
1575 ## feature option selection
1577 if {[regexp
{^git-
(.
+)$
} [appname
] _junk subcommand
]} {
1582 if {$subcommand eq
{gui.sh
}} {
1585 if {$subcommand eq
{gui
} && [llength
$argv] > 0} {
1586 set subcommand
[lindex
$argv 0]
1587 set argv
[lrange
$argv 1 end
]
1590 enable_option multicommit
1591 enable_option branch
1592 enable_option transport
1594 switch
-- $subcommand {
1597 disable_option multicommit
1598 disable_option branch
1599 disable_option transport
1602 enable_option singlecommit
1604 disable_option multicommit
1605 disable_option branch
1606 disable_option transport
1610 ######################################################################
1618 menu .mbar
-tearoff 0
1619 .mbar add cascade
-label Repository
-menu .mbar.repository
1620 .mbar add cascade
-label Edit
-menu .mbar.edit
1621 if {[is_enabled branch
]} {
1622 .mbar add cascade
-label Branch
-menu .mbar.branch
1624 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1625 .mbar add cascade
-label Commit
-menu .mbar.commit
1627 if {[is_enabled transport
]} {
1628 .mbar add cascade
-label Merge
-menu .mbar.merge
1629 .mbar add cascade
-label Fetch
-menu .mbar.fetch
1630 .mbar add cascade
-label Push
-menu .mbar.push
1632 . configure
-menu .mbar
1634 # -- Repository Menu
1636 menu .mbar.repository
1638 .mbar.repository add
command \
1639 -label {Browse Current Branch
} \
1640 -command {browser
::new
$current_branch}
1641 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1642 .mbar.repository add separator
1644 .mbar.repository add
command \
1645 -label {Visualize Current Branch
} \
1646 -command {do_gitk
$current_branch}
1647 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1648 .mbar.repository add
command \
1649 -label {Visualize All Branches
} \
1650 -command {do_gitk
--all}
1651 .mbar.repository add separator
1653 if {[is_enabled multicommit
]} {
1654 .mbar.repository add
command -label {Database Statistics
} \
1657 .mbar.repository add
command -label {Compress Database
} \
1660 .mbar.repository add
command -label {Verify Database
} \
1661 -command do_fsck_objects
1663 .mbar.repository add separator
1666 .mbar.repository add
command \
1667 -label {Create Desktop Icon
} \
1668 -command do_cygwin_shortcut
1669 } elseif
{[is_Windows
]} {
1670 .mbar.repository add
command \
1671 -label {Create Desktop Icon
} \
1672 -command do_windows_shortcut
1673 } elseif
{[is_MacOSX
]} {
1674 .mbar.repository add
command \
1675 -label {Create Desktop Icon
} \
1676 -command do_macosx_app
1680 .mbar.repository add
command -label Quit \
1687 .mbar.edit add
command -label Undo \
1688 -command {catch
{[focus
] edit undo
}} \
1690 .mbar.edit add
command -label Redo \
1691 -command {catch
{[focus
] edit redo
}} \
1693 .mbar.edit add separator
1694 .mbar.edit add
command -label Cut \
1695 -command {catch
{tk_textCut
[focus
]}} \
1697 .mbar.edit add
command -label Copy \
1698 -command {catch
{tk_textCopy
[focus
]}} \
1700 .mbar.edit add
command -label Paste \
1701 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
1703 .mbar.edit add
command -label Delete \
1704 -command {catch
{[focus
] delete sel.first sel.last
}} \
1706 .mbar.edit add separator
1707 .mbar.edit add
command -label {Select All
} \
1708 -command {catch
{[focus
] tag add sel
0.0 end
}} \
1713 if {[is_enabled branch
]} {
1716 .mbar.branch add
command -label {Create...
} \
1717 -command branch_create
::dialog \
1719 lappend disable_on_lock
[list .mbar.branch entryconf \
1720 [.mbar.branch index last
] -state]
1722 .mbar.branch add
command -label {Checkout...
} \
1723 -command branch_checkout
::dialog \
1725 lappend disable_on_lock
[list .mbar.branch entryconf \
1726 [.mbar.branch index last
] -state]
1728 .mbar.branch add
command -label {Rename...
} \
1729 -command branch_rename
::dialog
1730 lappend disable_on_lock
[list .mbar.branch entryconf \
1731 [.mbar.branch index last
] -state]
1733 .mbar.branch add
command -label {Delete...
} \
1734 -command branch_delete
::dialog
1735 lappend disable_on_lock
[list .mbar.branch entryconf \
1736 [.mbar.branch index last
] -state]
1738 .mbar.branch add
command -label {Reset...
} \
1739 -command merge
::reset_hard
1740 lappend disable_on_lock
[list .mbar.branch entryconf \
1741 [.mbar.branch index last
] -state]
1746 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1749 .mbar.commit add radiobutton \
1750 -label {New Commit
} \
1751 -command do_select_commit_type \
1752 -variable selected_commit_type \
1754 lappend disable_on_lock \
1755 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1757 .mbar.commit add radiobutton \
1758 -label {Amend Last Commit
} \
1759 -command do_select_commit_type \
1760 -variable selected_commit_type \
1762 lappend disable_on_lock \
1763 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1765 .mbar.commit add separator
1767 .mbar.commit add
command -label Rescan \
1768 -command do_rescan \
1770 lappend disable_on_lock \
1771 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1773 .mbar.commit add
command -label {Add To Commit
} \
1774 -command do_add_selection
1775 lappend disable_on_lock \
1776 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1778 .mbar.commit add
command -label {Add Existing To Commit
} \
1779 -command do_add_all \
1781 lappend disable_on_lock \
1782 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1784 .mbar.commit add
command -label {Unstage From Commit
} \
1785 -command do_unstage_selection
1786 lappend disable_on_lock \
1787 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1789 .mbar.commit add
command -label {Revert Changes
} \
1790 -command do_revert_selection
1791 lappend disable_on_lock \
1792 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1794 .mbar.commit add separator
1796 .mbar.commit add
command -label {Sign Off
} \
1797 -command do_signoff \
1800 .mbar.commit add
command -label Commit \
1801 -command do_commit \
1802 -accelerator $M1T-Return
1803 lappend disable_on_lock \
1804 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1809 if {[is_enabled branch
]} {
1811 .mbar.merge add
command -label {Local Merge...
} \
1812 -command merge
::dialog
1813 lappend disable_on_lock \
1814 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1815 .mbar.merge add
command -label {Abort Merge...
} \
1816 -command merge
::reset_hard
1817 lappend disable_on_lock \
1818 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1824 if {[is_enabled transport
]} {
1828 .mbar.push add
command -label {Push...
} \
1829 -command do_push_anywhere \
1831 .mbar.push add
command -label {Delete...
} \
1832 -command remote_branch_delete
::dialog
1836 # -- Apple Menu (Mac OS X only)
1838 .mbar add cascade
-label Apple
-menu .mbar.apple
1841 .mbar.apple add
command -label "About [appname]" \
1843 .mbar.apple add
command -label "Options..." \
1848 .mbar.edit add separator
1849 .mbar.edit add
command -label {Options...
} \
1854 if {[is_Cygwin
] && [file exists
/usr
/local
/miga
/lib
/gui-miga
]} {
1856 if {![lock_index update
]} return
1857 set cmd
[list sh
--login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1858 set miga_fd
[open
"|$cmd" r
]
1859 fconfigure
$miga_fd -blocking 0
1860 fileevent
$miga_fd readable
[list miga_done
$miga_fd]
1861 ui_status
{Running miga...
}
1863 proc miga_done
{fd
} {
1871 .mbar add cascade
-label Tools
-menu .mbar.tools
1873 .mbar.tools add
command -label "Migrate" \
1875 lappend disable_on_lock \
1876 [list .mbar.tools entryconf
[.mbar.tools index last
] -state]
1882 .mbar add cascade
-label Help
-menu .mbar.
help
1886 .mbar.
help add
command -label "About [appname]" \
1891 catch
{set browser
$repo_config(instaweb.browser
)}
1892 set doc_path
[file dirname [gitexec
]]
1893 set doc_path
[file join $doc_path Documentation index.html
]
1896 set doc_path
[exec cygpath
--mixed $doc_path]
1899 if {$browser eq
{}} {
1902 } elseif
{[is_Cygwin
]} {
1903 set program_files
[file dirname [exec cygpath
--windir]]
1904 set program_files
[file join $program_files {Program Files
}]
1905 set firefox
[file join $program_files {Mozilla Firefox
} firefox.exe
]
1906 set ie
[file join $program_files {Internet Explorer
} IEXPLORE.EXE
]
1907 if {[file exists
$firefox]} {
1908 set browser
$firefox
1909 } elseif
{[file exists
$ie]} {
1912 unset program_files firefox ie
1916 if {[file isfile
$doc_path]} {
1917 set doc_url
"file:$doc_path"
1919 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
1922 if {$browser ne
{}} {
1923 .mbar.
help add
command -label {Online Documentation
} \
1924 -command [list
exec $browser $doc_url &]
1926 unset browser doc_path doc_url
1928 # -- Standard bindings
1930 wm protocol . WM_DELETE_WINDOW do_quit
1931 bind all
<$M1B-Key-q> do_quit
1932 bind all
<$M1B-Key-Q> do_quit
1933 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
1934 bind all
<$M1B-Key-W> {destroy
[winfo toplevel
%W
]}
1936 set subcommand_args
{}
1938 puts stderr
"usage: $::argv0 $::subcommand $::subcommand_args"
1942 # -- Not a normal commit type invocation? Do that instead!
1944 switch
-- $subcommand {
1946 set subcommand_args
{rev?
}
1947 switch
[llength
$argv] {
1948 0 { load_current_branch
}
1950 set current_branch
[lindex
$argv 0]
1951 if {[regexp
{^
[0-9a-f]{1,39}$
} $current_branch]} {
1953 set current_branch \
1954 [git rev-parse
--verify $current_branch]
1963 browser
::new
$current_branch
1967 set subcommand_args
{rev? path?
}
1972 if {$is_path ||
[file exists
$_prefix$a]} {
1973 if {$path ne
{}} usage
1976 } elseif
{$a eq
{--}} {
1978 if {$head ne
{}} usage
1983 } elseif
{$head eq
{}} {
1984 if {$head ne
{}} usage
1995 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
1997 set head [git rev-parse
--verify $head]
2003 set current_branch
$head
2006 if {$path eq
{}} usage
2007 blame
::new
$head $path
2012 if {[llength
$argv] != 0} {
2013 puts
-nonewline stderr
"usage: $argv0"
2014 if {$subcommand ne
{gui
} && [appname
] ne
"git-$subcommand"} {
2015 puts
-nonewline stderr
" $subcommand"
2020 # fall through to setup UI for commits
2023 puts stderr
"usage: $argv0 \[{blame|browser|citool}\]"
2034 -text {Current Branch
:} \
2038 -textvariable current_branch \
2041 pack .branch.l1
-side left
2042 pack .branch.cb
-side left
-fill x
2043 pack .branch
-side top
-fill x
2045 # -- Main Window Layout
2047 panedwindow .vpane
-orient vertical
2048 panedwindow .vpane.files
-orient horizontal
2049 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
2050 pack .vpane
-anchor n
-side top
-fill both
-expand 1
2052 # -- Index File List
2054 frame .vpane.files.index
-height 100 -width 200
2055 label .vpane.files.index.title
-text {Staged Changes
(Will Be Committed
)} \
2056 -background lightgreen
2057 text
$ui_index -background white
-borderwidth 0 \
2058 -width 20 -height 10 \
2060 -cursor $cursor_ptr \
2061 -xscrollcommand {.vpane.files.index.sx
set} \
2062 -yscrollcommand {.vpane.files.index.sy
set} \
2064 scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
2065 scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
2066 pack .vpane.files.index.title
-side top
-fill x
2067 pack .vpane.files.index.sx
-side bottom
-fill x
2068 pack .vpane.files.index.sy
-side right
-fill y
2069 pack
$ui_index -side left
-fill both
-expand 1
2070 .vpane.files add .vpane.files.index
-sticky nsew
2072 # -- Working Directory File List
2074 frame .vpane.files.workdir
-height 100 -width 200
2075 label .vpane.files.workdir.title
-text {Unstaged Changes
(Will Not Be Committed
)} \
2076 -background lightsalmon
2077 text
$ui_workdir -background white
-borderwidth 0 \
2078 -width 20 -height 10 \
2080 -cursor $cursor_ptr \
2081 -xscrollcommand {.vpane.files.workdir.sx
set} \
2082 -yscrollcommand {.vpane.files.workdir.sy
set} \
2084 scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
2085 scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
2086 pack .vpane.files.workdir.title
-side top
-fill x
2087 pack .vpane.files.workdir.sx
-side bottom
-fill x
2088 pack .vpane.files.workdir.sy
-side right
-fill y
2089 pack
$ui_workdir -side left
-fill both
-expand 1
2090 .vpane.files add .vpane.files.workdir
-sticky nsew
2092 foreach i
[list
$ui_index $ui_workdir] {
2093 $i tag conf in_diff
-background lightgray
2094 $i tag conf in_sel
-background lightgray
2098 # -- Diff and Commit Area
2100 frame .vpane.lower
-height 300 -width 400
2101 frame .vpane.lower.commarea
2102 frame .vpane.lower.
diff -relief sunken
-borderwidth 1
2103 pack .vpane.lower.commarea
-side top
-fill x
2104 pack .vpane.lower.
diff -side bottom
-fill both
-expand 1
2105 .vpane add .vpane.lower
-sticky nsew
2107 # -- Commit Area Buttons
2109 frame .vpane.lower.commarea.buttons
2110 label .vpane.lower.commarea.buttons.l
-text {} \
2113 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
2114 pack .vpane.lower.commarea.buttons
-side left
-fill y
2116 button .vpane.lower.commarea.buttons.rescan
-text {Rescan
} \
2118 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
2119 lappend disable_on_lock \
2120 {.vpane.lower.commarea.buttons.rescan conf
-state}
2122 button .vpane.lower.commarea.buttons.incall
-text {Add Existing
} \
2124 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
2125 lappend disable_on_lock \
2126 {.vpane.lower.commarea.buttons.incall conf
-state}
2128 button .vpane.lower.commarea.buttons.signoff
-text {Sign Off
} \
2130 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
2132 button .vpane.lower.commarea.buttons.commit
-text {Commit
} \
2134 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
2135 lappend disable_on_lock \
2136 {.vpane.lower.commarea.buttons.commit conf
-state}
2138 button .vpane.lower.commarea.buttons.push
-text {Push
} \
2139 -command do_push_anywhere
2140 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
2142 # -- Commit Message Buffer
2144 frame .vpane.lower.commarea.buffer
2145 frame .vpane.lower.commarea.buffer.header
2146 set ui_comm .vpane.lower.commarea.buffer.t
2147 set ui_coml .vpane.lower.commarea.buffer.header.l
2148 radiobutton .vpane.lower.commarea.buffer.header.new \
2149 -text {New Commit
} \
2150 -command do_select_commit_type \
2151 -variable selected_commit_type \
2153 lappend disable_on_lock \
2154 [list .vpane.lower.commarea.buffer.header.new conf
-state]
2155 radiobutton .vpane.lower.commarea.buffer.header.amend \
2156 -text {Amend Last Commit
} \
2157 -command do_select_commit_type \
2158 -variable selected_commit_type \
2160 lappend disable_on_lock \
2161 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
2165 proc trace_commit_type
{varname args
} {
2166 global ui_coml commit_type
2167 switch
-glob -- $commit_type {
2168 initial
{set txt
{Initial Commit Message
:}}
2169 amend
{set txt
{Amended Commit Message
:}}
2170 amend-initial
{set txt
{Amended Initial Commit Message
:}}
2171 amend-merge
{set txt
{Amended Merge Commit Message
:}}
2172 merge
{set txt
{Merge Commit Message
:}}
2173 * {set txt
{Commit Message
:}}
2175 $ui_coml conf
-text $txt
2177 trace add variable commit_type
write trace_commit_type
2178 pack
$ui_coml -side left
-fill x
2179 pack .vpane.lower.commarea.buffer.header.amend
-side right
2180 pack .vpane.lower.commarea.buffer.header.new
-side right
2182 text
$ui_comm -background white
-borderwidth 1 \
2185 -autoseparators true \
2187 -width 75 -height 9 -wrap none \
2189 -yscrollcommand {.vpane.lower.commarea.buffer.sby
set}
2190 scrollbar .vpane.lower.commarea.buffer.sby \
2191 -command [list
$ui_comm yview
]
2192 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
2193 pack .vpane.lower.commarea.buffer.sby
-side right
-fill y
2194 pack
$ui_comm -side left
-fill y
2195 pack .vpane.lower.commarea.buffer
-side left
-fill y
2197 # -- Commit Message Buffer Context Menu
2199 set ctxm .vpane.lower.commarea.buffer.ctxm
2200 menu
$ctxm -tearoff 0
2203 -command {tk_textCut
$ui_comm}
2206 -command {tk_textCopy
$ui_comm}
2209 -command {tk_textPaste
$ui_comm}
2212 -command {$ui_comm delete sel.first sel.last
}
2215 -label {Select All
} \
2216 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
2220 $ui_comm tag add sel
0.0 end
2221 tk_textCopy
$ui_comm
2222 $ui_comm tag remove sel
0.0 end
2228 bind_button3
$ui_comm "tk_popup $ctxm %X %Y"
2232 proc trace_current_diff_path
{varname args
} {
2233 global current_diff_path diff_actions file_states
2234 if {$current_diff_path eq
{}} {
2240 set p
$current_diff_path
2241 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
2243 set p
[escape_path
$p]
2247 .vpane.lower.
diff.header.status configure
-text $s
2248 .vpane.lower.
diff.header.
file configure
-text $f
2249 .vpane.lower.
diff.header.path configure
-text $p
2250 foreach w
$diff_actions {
2254 trace add variable current_diff_path
write trace_current_diff_path
2256 frame .vpane.lower.
diff.header
-background gold
2257 label .vpane.lower.
diff.header.status \
2259 -width $max_status_desc \
2262 label .vpane.lower.
diff.header.
file \
2266 label .vpane.lower.
diff.header.path \
2270 pack .vpane.lower.
diff.header.status
-side left
2271 pack .vpane.lower.
diff.header.
file -side left
2272 pack .vpane.lower.
diff.header.path
-fill x
2273 set ctxm .vpane.lower.
diff.header.ctxm
2274 menu
$ctxm -tearoff 0
2282 -- $current_diff_path
2284 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2285 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
2289 frame .vpane.lower.
diff.body
2290 set ui_diff .vpane.lower.
diff.body.t
2291 text
$ui_diff -background white
-borderwidth 0 \
2292 -width 80 -height 15 -wrap none \
2294 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
2295 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
2297 scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
2298 -command [list
$ui_diff xview
]
2299 scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
2300 -command [list
$ui_diff yview
]
2301 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
2302 pack .vpane.lower.
diff.body.sby
-side right
-fill y
2303 pack
$ui_diff -side left
-fill both
-expand 1
2304 pack .vpane.lower.
diff.header
-side top
-fill x
2305 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
2307 $ui_diff tag conf d_cr
-elide true
2308 $ui_diff tag conf d_@
-foreground blue
-font font_diffbold
2309 $ui_diff tag conf d_
+ -foreground {#00a000}
2310 $ui_diff tag conf d_-
-foreground red
2312 $ui_diff tag conf d_
++ -foreground {#00a000}
2313 $ui_diff tag conf d_--
-foreground red
2314 $ui_diff tag conf d_
+s \
2315 -foreground {#00a000} \
2316 -background {#e2effa}
2317 $ui_diff tag conf d_-s \
2319 -background {#e2effa}
2320 $ui_diff tag conf d_s
+ \
2321 -foreground {#00a000} \
2323 $ui_diff tag conf d_s- \
2327 $ui_diff tag conf d
<<<<<<< \
2328 -foreground orange \
2330 $ui_diff tag conf d
======= \
2331 -foreground orange \
2333 $ui_diff tag conf d
>>>>>>> \
2334 -foreground orange \
2337 $ui_diff tag raise sel
2339 # -- Diff Body Context Menu
2341 set ctxm .vpane.lower.
diff.body.ctxm
2342 menu
$ctxm -tearoff 0
2345 -command reshow_diff
2346 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2349 -command {tk_textCopy
$ui_diff}
2350 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2352 -label {Select All
} \
2353 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
2354 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2358 $ui_diff tag add sel
0.0 end
2359 tk_textCopy
$ui_diff
2360 $ui_diff tag remove sel
0.0 end
2362 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2365 -label {Apply
/Reverse Hunk
} \
2366 -command {apply_hunk
$cursorX $cursorY}
2367 set ui_diff_applyhunk
[$ctxm index last
]
2368 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
2371 -label {Decrease Font Size
} \
2372 -command {incr_font_size font_diff
-1}
2373 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2375 -label {Increase Font Size
} \
2376 -command {incr_font_size font_diff
1}
2377 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2380 -label {Show Less Context
} \
2381 -command {if {$repo_config(gui.diffcontext
) >= 1} {
2382 incr repo_config
(gui.diffcontext
) -1
2385 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2387 -label {Show More Context
} \
2388 -command {if {$repo_config(gui.diffcontext
) < 99} {
2389 incr repo_config
(gui.diffcontext
)
2392 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2394 $ctxm add
command -label {Options...
} \
2396 bind_button3
$ui_diff "
2399 if {\$ui_index eq \$current_diff_side} {
2400 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2402 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2404 tk_popup $ctxm %X %Y
2406 unset ui_diff_applyhunk
2410 set main_status
[::status_bar
::new .status
]
2411 pack .status
-anchor w
-side bottom
-fill x
2412 $main_status show
{Initializing...
}
2417 set gm
$repo_config(gui.geometry
)
2418 wm geometry .
[lindex
$gm 0]
2419 .vpane sash place
0 \
2420 [lindex
[.vpane sash coord
0] 0] \
2422 .vpane.files sash place
0 \
2424 [lindex
[.vpane.files sash coord
0] 1]
2430 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
2431 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
2432 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
2433 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
2434 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
2435 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
2436 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
2437 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
2438 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
2439 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2440 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2442 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
2443 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
2444 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
2445 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
2446 bind $ui_diff <$M1B-Key-v> {break}
2447 bind $ui_diff <$M1B-Key-V> {break}
2448 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2449 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2450 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
2451 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
2452 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
2453 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
2454 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
2455 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
2456 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
2457 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
2458 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
2459 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
2460 bind $ui_diff <Button-1
> {focus
%W
}
2462 if {[is_enabled branch
]} {
2463 bind .
<$M1B-Key-n> branch_create
::dialog
2464 bind .
<$M1B-Key-N> branch_create
::dialog
2465 bind .
<$M1B-Key-o> branch_checkout
::dialog
2466 bind .
<$M1B-Key-O> branch_checkout
::dialog
2468 if {[is_enabled transport
]} {
2469 bind .
<$M1B-Key-p> do_push_anywhere
2470 bind .
<$M1B-Key-P> do_push_anywhere
2473 bind .
<Key-F5
> do_rescan
2474 bind .
<$M1B-Key-r> do_rescan
2475 bind .
<$M1B-Key-R> do_rescan
2476 bind .
<$M1B-Key-s> do_signoff
2477 bind .
<$M1B-Key-S> do_signoff
2478 bind .
<$M1B-Key-i> do_add_all
2479 bind .
<$M1B-Key-I> do_add_all
2480 bind .
<$M1B-Key-Return> do_commit
2481 foreach i
[list
$ui_index $ui_workdir] {
2482 bind $i <Button-1
> "toggle_or_diff $i %x %y; break"
2483 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2484 bind $i <Shift-Button-1
> "add_range_to_selection $i %x %y; break"
2488 set file_lists
($ui_index) [list
]
2489 set file_lists
($ui_workdir) [list
]
2491 wm title .
"[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2492 focus
-force $ui_comm
2494 # -- Warn the user about environmental problems. Cygwin's Tcl
2495 # does *not* pass its env array onto any processes it spawns.
2496 # This means that git processes get none of our environment.
2501 set msg
"Possible environment issues exist.
2503 The following environment variables are probably
2504 going to be ignored by any Git subprocess run
2508 foreach name
[array names env
] {
2509 switch
-regexp -- $name {
2510 {^GIT_INDEX_FILE$
} -
2511 {^GIT_OBJECT_DIRECTORY$
} -
2512 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$
} -
2514 {^GIT_EXTERNAL_DIFF$
} -
2518 {^GIT_CONFIG_LOCAL$
} -
2519 {^GIT_
(AUTHOR|COMMITTER
)_DATE$
} {
2520 append msg
" - $name\n"
2523 {^GIT_
(AUTHOR|COMMITTER
)_
(NAME|EMAIL
)$
} {
2524 append msg
" - $name\n"
2526 set suggest_user
$name
2530 if {$ignored_env > 0} {
2532 This is due to a known issue with the
2533 Tcl binary distributed by Cygwin."
2535 if {$suggest_user ne
{}} {
2538 A good replacement for $suggest_user
2539 is placing values for the user.name and
2540 user.email settings into your personal
2546 unset ignored_env msg suggest_user name
2549 # -- Only initialize complex UI if we are going to stay running.
2551 if {[is_enabled transport
]} {
2558 # -- Only suggest a gc run if we are going to stay running.
2560 if {[is_enabled multicommit
]} {
2561 set object_limit
2000
2562 if {[is_Windows
]} {set object_limit
200}
2563 regexp
{^
([0-9]+) objects
,} [git count-objects
] _junk objects_current
2564 if {$objects_current >= $object_limit} {
2566 "This repository currently has $objects_current loose objects.
2568 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2570 Compress the database now?"] eq
yes} {
2574 unset object_limit _junk objects_current
2577 lock_index begin-read