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]
370 proc _lappend_nice
{cmd_var
} {
374 if {![info exists _nice
]} {
375 set _nice
[_which nice
]
386 switch
-- [lindex
$args 0] {
397 set args
[lrange
$args 1 end
]
400 set cmdp
[_git_cmd
[lindex
$args 0]]
401 set args
[lrange
$args 1 end
]
403 return [eval $opt $cmdp $args]
406 proc _open_stdout_stderr
{cmd
} {
410 if { [lindex
$cmd end
] eq
{2>@
1}
411 && $err eq
{can not
find channel named
"1"}
413 # Older versions of Tcl 8.4 don't have this 2>@1 IO
414 # redirect operator. Fallback to |& cat for those.
415 # The command was not actually started, so its safe
416 # to try to start it a second time.
418 set fd
[open
[concat \
419 [lrange
$cmd 0 end-1
] \
426 fconfigure
$fd -eofchar {}
430 proc git_read
{args
} {
434 switch
-- [lindex
$args 0] {
449 set args
[lrange
$args 1 end
]
452 set cmdp
[_git_cmd
[lindex
$args 0]]
453 set args
[lrange
$args 1 end
]
455 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
458 proc git_write
{args
} {
462 switch
-- [lindex
$args 0] {
473 set args
[lrange
$args 1 end
]
476 set cmdp
[_git_cmd
[lindex
$args 0]]
477 set args
[lrange
$args 1 end
]
479 return [open
[concat
$opt $cmdp $args] w
]
483 regsub
-all ' $value "'\\''" value
487 proc load_current_branch {} {
488 global current_branch is_detached
490 set fd [open [gitdir HEAD] r]
491 if {[gets $fd ref] < 1} {
496 set pfx {ref: refs/heads/}
497 set len [string length $pfx]
498 if {[string equal -length $len $pfx $ref]} {
499 # We're on a branch. It might not exist. But
500 # HEAD looks good enough to be a branch.
502 set current_branch [string range $ref $len end]
505 # Assume this is a detached head.
507 set current_branch HEAD
512 auto_load tk_optionMenu
513 rename tk_optionMenu real__tkOptionMenu
514 proc tk_optionMenu {w varName args} {
515 set m [eval real__tkOptionMenu $w $varName $args]
516 $m configure -font font_ui
517 $w configure -font font_ui
521 ######################################################################
525 set _git [_which git]
527 catch {wm withdraw .}
528 error_popup "Cannot
find git
in PATH.
"
532 ######################################################################
536 if {[catch {set _git_version [git --version]} err]} {
537 catch {wm withdraw .}
538 error_popup "Cannot determine Git version
:
542 [appname
] requires Git
1.5.0 or later.
"
545 if {![regsub {^git version } $_git_version {} _git_version]} {
546 catch {wm withdraw .}
547 error_popup "Cannot parse Git version string
:\n\n$_git_version"
550 regsub -- {-dirty$} $_git_version {} _git_version
551 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
552 regsub {\.rc[0-9]+$} $_git_version {} _git_version
553 regsub {\.GIT$} $_git_version {} _git_version
555 proc git-version {args} {
558 switch [llength $args] {
564 set op [lindex $args 0]
565 set vr [lindex $args 1]
566 set cm [package vcompare $_git_version $vr]
567 return [expr $cm $op 0]
571 set type [lindex $args 0]
572 set name [lindex $args 1]
573 set parm [lindex $args 2]
574 set body [lindex $args 3]
576 if {($type ne {proc} && $type ne {method})} {
577 error "Invalid arguments to git-version
"
579 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
580 error "Last arm of
$type $name must be default
"
583 foreach {op vr cb} [lrange $body 0 end-2] {
584 if {[git-version $op $vr]} {
585 return [uplevel [list $type $name $parm $cb]]
589 return [uplevel [list $type $name $parm [lindex $body end]]]
593 error "git-version
>= x
"
599 if {[git-version < 1.5]} {
600 catch {wm withdraw .}
601 error_popup "[appname
] requires Git
1.5.0 or later.
603 You are using
[git-version
]:
609 ######################################################################
614 set _gitdir $env(GIT_DIR)
618 set _gitdir [git rev-parse --git-dir]
619 set _prefix [git rev-parse --show-prefix]
621 catch {wm withdraw .}
622 error_popup "Cannot
find the git directory
:\n\n$err"
625 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
626 catch {set _gitdir [exec cygpath --unix $_gitdir]}
628 if {![file isdirectory $_gitdir]} {
629 catch {wm withdraw .}
630 error_popup "Git directory not found
:\n\n$_gitdir"
633 if {[lindex [file split $_gitdir] end] ne {.git}} {
634 catch {wm withdraw .}
635 error_popup "Cannot use funny .git directory
:\n\n$_gitdir"
638 if {[catch {cd [file dirname $_gitdir]} err]} {
639 catch {wm withdraw .}
640 error_popup "No working directory
[file dirname $_gitdir]:\n\n$err"
643 set _reponame [lindex [file split \
644 [file normalize [file dirname $_gitdir]]] \
647 ######################################################################
651 set current_diff_path {}
652 set current_diff_side {}
653 set diff_actions [list]
657 set MERGE_HEAD [list]
660 set current_branch {}
662 set current_diff_path {}
663 set selected_commit_type new
665 ######################################################################
673 set disable_on_lock [list]
674 set index_lock_type none
676 proc lock_index {type} {
677 global index_lock_type disable_on_lock
679 if {$index_lock_type eq {none}} {
680 set index_lock_type $type
681 foreach w $disable_on_lock {
682 uplevel #0 $w disabled
685 } elseif {$index_lock_type eq "begin-
$type"} {
686 set index_lock_type $type
692 proc unlock_index {} {
693 global index_lock_type disable_on_lock
695 set index_lock_type none
696 foreach w $disable_on_lock {
701 ######################################################################
705 proc repository_state {ctvar hdvar mhvar} {
706 global current_branch
707 upvar $ctvar ct $hdvar hd $mhvar mh
712 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
718 set merge_head [gitdir MERGE_HEAD]
719 if {[file exists $merge_head]} {
721 set fd_mh [open $merge_head r]
722 while {[gets $fd_mh line] >= 0} {
733 global PARENT empty_tree
735 set p [lindex $PARENT 0]
739 if {$empty_tree eq {}} {
740 set empty_tree [git mktree << {}]
745 proc rescan {after {honor_trustmtime 1}} {
746 global HEAD PARENT MERGE_HEAD commit_type
747 global ui_index ui_workdir ui_comm
748 global rescan_active file_states
751 if {$rescan_active > 0 || ![lock_index read]} return
753 repository_state newType newHEAD newMERGE_HEAD
754 if {[string match amend* $commit_type]
755 && $newType eq {normal}
756 && $newHEAD eq $HEAD} {
760 set MERGE_HEAD $newMERGE_HEAD
761 set commit_type $newType
764 array unset file_states
766 if {![$ui_comm edit modified]
767 || [string trim [$ui_comm get 0.0 end]] eq {}} {
768 if {[string match amend* $commit_type]} {
769 } elseif {[load_message GITGUI_MSG]} {
770 } elseif {[load_message MERGE_MSG]} {
771 } elseif {[load_message SQUASH_MSG]} {
774 $ui_comm edit modified false
777 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
778 rescan_stage2 {} $after
781 ui_status {Refreshing file status...}
782 set fd_rf [git_read update-index \
788 fconfigure $fd_rf -blocking 0 -translation binary
789 fileevent $fd_rf readable \
790 [list rescan_stage2 $fd_rf $after]
794 proc rescan_stage2 {fd after} {
795 global rescan_active buf_rdi buf_rdf buf_rlo
799 if {![eof $fd]} return
803 set ls_others [list --exclude-per-directory=.gitignore]
804 set info_exclude [gitdir info exclude]
805 if {[file readable $info_exclude]} {
806 lappend ls_others "--exclude-from=$info_exclude"
814 ui_status {Scanning for modified files ...}
815 set fd_di [git_read diff-index --cached -z [PARENT]]
816 set fd_df [git_read diff-files -z]
817 set fd_lo [eval git_read ls-files --others -z $ls_others]
819 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
820 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
821 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
822 fileevent $fd_di readable [list read_diff_index $fd_di $after]
823 fileevent $fd_df readable [list read_diff_files $fd_df $after]
824 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
827 proc load_message {file} {
831 if {[file isfile $f]} {
832 if {[catch {set fd [open $f r]}]} {
835 fconfigure $fd -eofchar {}
836 set content [string trim [read $fd]]
838 regsub -all -line {[ \r\t]+$} $content {} content
839 $ui_comm delete 0.0 end
840 $ui_comm insert end $content
846 proc read_diff_index {fd after} {
849 append buf_rdi [read $fd]
851 set n [string length $buf_rdi]
853 set z1 [string first "\
0" $buf_rdi $c]
856 set z2 [string first "\
0" $buf_rdi $z1]
860 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
861 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
863 [encoding convertfrom $p] \
865 [list [lindex $i 0] [lindex $i 2]] \
871 set buf_rdi [string range $buf_rdi $c end]
876 rescan_done $fd buf_rdi $after
879 proc read_diff_files {fd after} {
882 append buf_rdf [read $fd]
884 set n [string length $buf_rdf]
886 set z1 [string first "\
0" $buf_rdf $c]
889 set z2 [string first "\
0" $buf_rdf $z1]
893 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
894 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
896 [encoding convertfrom $p] \
899 [list [lindex $i 0] [lindex $i 2]]
904 set buf_rdf [string range $buf_rdf $c end]
909 rescan_done $fd buf_rdf $after
912 proc read_ls_others {fd after} {
915 append buf_rlo [read $fd]
916 set pck [split $buf_rlo "\
0"]
917 set buf_rlo [lindex $pck end]
918 foreach p [lrange $pck 0 end-1] {
919 merge_state [encoding convertfrom $p] ?O
921 rescan_done $fd buf_rlo $after
924 proc rescan_done {fd buf after} {
925 global rescan_active current_diff_path
926 global file_states repo_config
929 if {![eof $fd]} return
932 if {[incr rescan_active -1] > 0} return
937 if {$current_diff_path ne {}} reshow_diff
941 proc prune_selection {} {
942 global file_states selected_paths
944 foreach path [array names selected_paths] {
945 if {[catch {set still_here $file_states($path)}]} {
946 unset selected_paths($path)
951 ######################################################################
955 proc mapicon {w state path} {
958 if {[catch {set r $all_icons($state$w)}]} {
959 puts "error
: no icon
for $w state
={$state} $path"
965 proc mapdesc {state path} {
968 if {[catch {set r $all_descs($state)}]} {
969 puts "error
: no desc
for state
={$state} $path"
975 proc ui_status {msg} {
976 $::main_status show $msg
979 proc ui_ready {{test {}}} {
980 $::main_status show {Ready.} $test
983 proc escape_path {path} {
984 regsub -all {\\} $path "\\\\" path
985 regsub -all "\n" $path "\\n
" path
989 proc short_path {path} {
990 return [escape_path [lindex [file split $path] end]]
994 set null_sha1 [string repeat 0 40]
996 proc merge_state {path new_state {head_info {}} {index_info {}}} {
997 global file_states next_icon_id null_sha1
999 set s0 [string index $new_state 0]
1000 set s1 [string index $new_state 1]
1002 if {[catch {set info $file_states($path)}]} {
1004 set icon n[incr next_icon_id]
1006 set state [lindex $info 0]
1007 set icon [lindex $info 1]
1008 if {$head_info eq {}} {set head_info [lindex $info 2]}
1009 if {$index_info eq {}} {set index_info [lindex $info 3]}
1012 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1013 elseif {$s0 eq {_}} {set s0 _}
1015 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1016 elseif {$s1 eq {_}} {set s1 _}
1018 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1019 set head_info [list 0 $null_sha1]
1020 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1021 && $head_info eq {}} {
1022 set head_info $index_info
1025 set file_states($path) [list $s0$s1 $icon \
1026 $head_info $index_info \
1031 proc display_file_helper {w path icon_name old_m new_m} {
1034 if {$new_m eq {_}} {
1035 set lno [lsearch -sorted -exact $file_lists($w) $path]
1037 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1039 $w conf -state normal
1040 $w delete $lno.0 [expr {$lno + 1}].0
1041 $w conf -state disabled
1043 } elseif {$old_m eq {_} && $new_m ne {_}} {
1044 lappend file_lists($w) $path
1045 set file_lists($w) [lsort -unique $file_lists($w)]
1046 set lno [lsearch -sorted -exact $file_lists($w) $path]
1048 $w conf -state normal
1049 $w image create $lno.0 \
1050 -align center -padx 5 -pady 1 \
1052 -image [mapicon $w $new_m $path]
1053 $w insert $lno.1 "[escape_path
$path]\n"
1054 $w conf -state disabled
1055 } elseif {$old_m ne $new_m} {
1056 $w conf -state normal
1057 $w image conf $icon_name -image [mapicon $w $new_m $path]
1058 $w conf -state disabled
1062 proc display_file {path state} {
1063 global file_states selected_paths
1064 global ui_index ui_workdir
1066 set old_m [merge_state $path $state]
1067 set s $file_states($path)
1068 set new_m [lindex $s 0]
1069 set icon_name [lindex $s 1]
1071 set o [string index $old_m 0]
1072 set n [string index $new_m 0]
1079 display_file_helper $ui_index $path $icon_name $o $n
1081 if {[string index $old_m 0] eq {U}} {
1084 set o [string index $old_m 1]
1086 if {[string index $new_m 0] eq {U}} {
1089 set n [string index $new_m 1]
1091 display_file_helper $ui_workdir $path $icon_name $o $n
1093 if {$new_m eq {__}} {
1094 unset file_states($path)
1095 catch {unset selected_paths($path)}
1099 proc display_all_files_helper {w path icon_name m} {
1102 lappend file_lists($w) $path
1103 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1104 $w image create end \
1105 -align center -padx 5 -pady 1 \
1107 -image [mapicon $w $m $path]
1108 $w insert end "[escape_path
$path]\n"
1111 proc display_all_files {} {
1112 global ui_index ui_workdir
1113 global file_states file_lists
1116 $ui_index conf -state normal
1117 $ui_workdir conf -state normal
1119 $ui_index delete 0.0 end
1120 $ui_workdir delete 0.0 end
1123 set file_lists($ui_index) [list]
1124 set file_lists($ui_workdir) [list]
1126 foreach path [lsort [array names file_states]] {
1127 set s $file_states($path)
1129 set icon_name [lindex $s 1]
1131 set s [string index $m 0]
1132 if {$s ne {U} && $s ne {_}} {
1133 display_all_files_helper $ui_index $path \
1137 if {[string index $m 0] eq {U}} {
1140 set s [string index $m 1]
1143 display_all_files_helper $ui_workdir $path \
1148 $ui_index conf -state disabled
1149 $ui_workdir conf -state disabled
1152 ######################################################################
1157 #define mask_width 14
1158 #define mask_height 15
1159 static unsigned char mask_bits[] = {
1160 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1161 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1162 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1165 image create bitmap file_plain -background white -foreground black -data {
1166 #define plain_width 14
1167 #define plain_height 15
1168 static unsigned char plain_bits[] = {
1169 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1170 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1171 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1172 } -maskdata $filemask
1174 image create bitmap file_mod -background white -foreground blue -data {
1175 #define mod_width 14
1176 #define mod_height 15
1177 static unsigned char mod_bits[] = {
1178 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1179 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1180 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1181 } -maskdata $filemask
1183 image create bitmap file_fulltick -background white -foreground "#007000" -data {
1184 #define file_fulltick_width 14
1185 #define file_fulltick_height 15
1186 static unsigned char file_fulltick_bits
[] = {
1187 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1188 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1189 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1190 } -maskdata $filemask
1192 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
1193 #define parttick_width 14
1194 #define parttick_height 15
1195 static unsigned char parttick_bits
[] = {
1196 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1197 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1198 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1199 } -maskdata $filemask
1201 image create bitmap file_question
-background white
-foreground black
-data {
1202 #define file_question_width 14
1203 #define file_question_height 15
1204 static unsigned char file_question_bits
[] = {
1205 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1206 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1207 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1208 } -maskdata $filemask
1210 image create bitmap file_removed
-background white
-foreground red
-data {
1211 #define file_removed_width 14
1212 #define file_removed_height 15
1213 static unsigned char file_removed_bits
[] = {
1214 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1215 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1216 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1217 } -maskdata $filemask
1219 image create bitmap file_merge
-background white
-foreground blue
-data {
1220 #define file_merge_width 14
1221 #define file_merge_height 15
1222 static unsigned char file_merge_bits
[] = {
1223 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1224 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1225 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1226 } -maskdata $filemask
1229 #define file_width 18
1230 #define file_height 18
1231 static unsigned char file_bits
[] = {
1232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
1233 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
1234 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
1235 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
1236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1238 image create bitmap file_dir
-background white
-foreground blue \
1239 -data $file_dir_data -maskdata $file_dir_data
1242 set file_uplevel_data
{
1244 #define up_height 15
1245 static unsigned char up_bits
[] = {
1246 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
1247 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
1248 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
1250 image create bitmap file_uplevel
-background white
-foreground red \
1251 -data $file_uplevel_data -maskdata $file_uplevel_data
1252 unset file_uplevel_data
1254 set ui_index .vpane.files.index.list
1255 set ui_workdir .vpane.files.workdir.list
1257 set all_icons
(_
$ui_index) file_plain
1258 set all_icons
(A
$ui_index) file_fulltick
1259 set all_icons
(M
$ui_index) file_fulltick
1260 set all_icons
(D
$ui_index) file_removed
1261 set all_icons
(U
$ui_index) file_merge
1263 set all_icons
(_
$ui_workdir) file_plain
1264 set all_icons
(M
$ui_workdir) file_mod
1265 set all_icons
(D
$ui_workdir) file_question
1266 set all_icons
(U
$ui_workdir) file_merge
1267 set all_icons
(O
$ui_workdir) file_plain
1269 set max_status_desc
0
1273 {_M
"Modified, not staged"}
1274 {M_
"Staged for commit"}
1275 {MM
"Portions staged for commit"}
1276 {MD
"Staged for commit, missing"}
1278 {_O
"Untracked, not staged"}
1279 {A_
"Staged for commit"}
1280 {AM
"Portions staged for commit"}
1281 {AD
"Staged for commit, missing"}
1284 {D_
"Staged for removal"}
1285 {DO
"Staged for removal, still present"}
1287 {U_
"Requires merge resolution"}
1288 {UU
"Requires merge resolution"}
1289 {UM
"Requires merge resolution"}
1290 {UD
"Requires merge resolution"}
1292 if {$max_status_desc < [string length
[lindex
$i 1]]} {
1293 set max_status_desc
[string length
[lindex
$i 1]]
1295 set all_descs
([lindex
$i 0]) [lindex
$i 1]
1299 ######################################################################
1303 proc bind_button3
{w cmd
} {
1304 bind $w <Any-Button-3
> $cmd
1306 bind $w <Control-Button-1
> $cmd
1310 proc scrollbar2many
{list mode args
} {
1311 foreach w
$list {eval $w $mode $args}
1314 proc many2scrollbar
{list mode sb top bottom
} {
1315 $sb set $top $bottom
1316 foreach w
$list {$w $mode moveto
$top}
1319 proc incr_font_size
{font
{amt
1}} {
1320 set sz
[font configure
$font -size]
1322 font configure
$font -size $sz
1323 font configure
${font}bold
-size $sz
1324 font configure
${font}italic
-size $sz
1327 ######################################################################
1331 set starting_gitk_msg
{Starting gitk... please
wait...
}
1333 proc do_gitk
{revs
} {
1334 # -- Always start gitk through whatever we were loaded with. This
1335 # lets us bypass using shell process on Windows systems.
1337 set exe
[file join [file dirname $
::_git
] gitk
]
1338 set cmd
[list
[info nameofexecutable
] $exe]
1339 if {! [file exists
$exe]} {
1340 error_popup
"Unable to start gitk:\n\n$exe does not exist"
1342 eval exec $cmd $revs &
1343 ui_status $
::starting_gitk_msg
1345 ui_ready
$starting_gitk_msg
1353 global ui_comm is_quitting repo_config commit_type
1355 if {$is_quitting} return
1358 if {[winfo exists
$ui_comm]} {
1359 # -- Stash our current commit buffer.
1361 set save
[gitdir GITGUI_MSG
]
1362 set msg
[string trim
[$ui_comm get
0.0 end
]]
1363 regsub
-all -line {[ \r\t]+$
} $msg {} msg
1364 if {(![string match amend
* $commit_type]
1365 ||
[$ui_comm edit modified
])
1368 set fd
[open
$save w
]
1369 puts
-nonewline $fd $msg
1373 catch
{file delete
$save}
1376 # -- Stash our current window geometry into this repository.
1378 set cfg_geometry
[list
]
1379 lappend cfg_geometry
[wm geometry .
]
1380 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
1381 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
1382 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
1385 if {$cfg_geometry ne
$rc_geometry} {
1386 catch
{git config gui.geometry
$cfg_geometry}
1401 proc toggle_or_diff
{w x y
} {
1402 global file_states file_lists current_diff_path ui_index ui_workdir
1403 global last_clicked selected_paths
1405 set pos
[split [$w index @
$x,$y] .
]
1406 set lno
[lindex
$pos 0]
1407 set col [lindex
$pos 1]
1408 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1414 set last_clicked
[list
$w $lno]
1415 array
unset selected_paths
1416 $ui_index tag remove in_sel
0.0 end
1417 $ui_workdir tag remove in_sel
0.0 end
1420 if {$current_diff_path eq
$path} {
1421 set after
{reshow_diff
;}
1425 if {$w eq
$ui_index} {
1427 "Unstaging [short_path $path] from commit" \
1429 [concat
$after [list ui_ready
]]
1430 } elseif
{$w eq
$ui_workdir} {
1432 "Adding [short_path $path]" \
1434 [concat
$after [list ui_ready
]]
1437 show_diff
$path $w $lno
1441 proc add_one_to_selection
{w x y
} {
1442 global file_lists last_clicked selected_paths
1444 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1445 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1451 if {$last_clicked ne
{}
1452 && [lindex
$last_clicked 0] ne
$w} {
1453 array
unset selected_paths
1454 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
1457 set last_clicked
[list
$w $lno]
1458 if {[catch
{set in_sel
$selected_paths($path)}]} {
1462 unset selected_paths
($path)
1463 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
1465 set selected_paths
($path) 1
1466 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1470 proc add_range_to_selection
{w x y
} {
1471 global file_lists last_clicked selected_paths
1473 if {[lindex
$last_clicked 0] ne
$w} {
1474 toggle_or_diff
$w $x $y
1478 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1479 set lc
[lindex
$last_clicked 1]
1488 foreach path
[lrange
$file_lists($w) \
1489 [expr {$begin - 1}] \
1490 [expr {$end - 1}]] {
1491 set selected_paths
($path) 1
1493 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
1496 ######################################################################
1500 set cursor_ptr arrow
1501 font create font_diff
-family Courier
-size 10
1505 eval font configure font_ui
[font actual
[.dummy cget
-font]]
1509 font create font_uiitalic
1510 font create font_uibold
1511 font create font_diffbold
1512 font create font_diffitalic
1514 foreach class
{Button Checkbutton Entry Label
1515 Labelframe Listbox Menu Message
1516 Radiobutton Spinbox Text
} {
1517 option add
*$class.font font_ui
1521 if {[is_Windows
] ||
[is_MacOSX
]} {
1522 option add
*Menu.tearOff
0
1533 proc apply_config
{} {
1534 global repo_config font_descs
1536 foreach option
$font_descs {
1537 set name
[lindex
$option 0]
1538 set font
[lindex
$option 1]
1540 foreach
{cn cv
} $repo_config(gui.
$name) {
1541 font configure
$font $cn $cv
1544 error_popup
"Invalid font specified in gui.$name:\n\n$err"
1546 foreach
{cn cv
} [font configure
$font] {
1547 font configure
${font}bold
$cn $cv
1548 font configure
${font}italic
$cn $cv
1550 font configure
${font}bold
-weight bold
1551 font configure
${font}italic
-slant italic
1555 set default_config
(merge.diffstat
) true
1556 set default_config
(merge.summary
) false
1557 set default_config
(merge.verbosity
) 2
1558 set default_config
(user.name
) {}
1559 set default_config
(user.email
) {}
1561 set default_config
(gui.matchtrackingbranch
) false
1562 set default_config
(gui.pruneduringfetch
) false
1563 set default_config
(gui.trustmtime
) false
1564 set default_config
(gui.diffcontext
) 5
1565 set default_config
(gui.newbranchtemplate
) {}
1566 set default_config
(gui.fontui
) [font configure font_ui
]
1567 set default_config
(gui.fontdiff
) [font configure font_diff
]
1569 {fontui font_ui
{Main Font
}}
1570 {fontdiff font_diff
{Diff
/Console Font
}}
1575 ######################################################################
1577 ## feature option selection
1579 if {[regexp
{^git-
(.
+)$
} [appname
] _junk subcommand
]} {
1584 if {$subcommand eq
{gui.sh
}} {
1587 if {$subcommand eq
{gui
} && [llength
$argv] > 0} {
1588 set subcommand
[lindex
$argv 0]
1589 set argv
[lrange
$argv 1 end
]
1592 enable_option multicommit
1593 enable_option branch
1594 enable_option transport
1596 switch
-- $subcommand {
1599 disable_option multicommit
1600 disable_option branch
1601 disable_option transport
1604 enable_option singlecommit
1606 disable_option multicommit
1607 disable_option branch
1608 disable_option transport
1612 ######################################################################
1620 menu .mbar
-tearoff 0
1621 .mbar add cascade
-label Repository
-menu .mbar.repository
1622 .mbar add cascade
-label Edit
-menu .mbar.edit
1623 if {[is_enabled branch
]} {
1624 .mbar add cascade
-label Branch
-menu .mbar.branch
1626 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1627 .mbar add cascade
-label Commit
-menu .mbar.commit
1629 if {[is_enabled transport
]} {
1630 .mbar add cascade
-label Merge
-menu .mbar.merge
1631 .mbar add cascade
-label Fetch
-menu .mbar.fetch
1632 .mbar add cascade
-label Push
-menu .mbar.push
1634 . configure
-menu .mbar
1636 # -- Repository Menu
1638 menu .mbar.repository
1640 .mbar.repository add
command \
1641 -label {Browse Current Branch
} \
1642 -command {browser
::new
$current_branch}
1643 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1644 .mbar.repository add separator
1646 .mbar.repository add
command \
1647 -label {Visualize Current Branch
} \
1648 -command {do_gitk
$current_branch}
1649 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1650 .mbar.repository add
command \
1651 -label {Visualize All Branches
} \
1652 -command {do_gitk
--all}
1653 .mbar.repository add separator
1655 if {[is_enabled multicommit
]} {
1656 .mbar.repository add
command -label {Database Statistics
} \
1659 .mbar.repository add
command -label {Compress Database
} \
1662 .mbar.repository add
command -label {Verify Database
} \
1663 -command do_fsck_objects
1665 .mbar.repository add separator
1668 .mbar.repository add
command \
1669 -label {Create Desktop Icon
} \
1670 -command do_cygwin_shortcut
1671 } elseif
{[is_Windows
]} {
1672 .mbar.repository add
command \
1673 -label {Create Desktop Icon
} \
1674 -command do_windows_shortcut
1675 } elseif
{[is_MacOSX
]} {
1676 .mbar.repository add
command \
1677 -label {Create Desktop Icon
} \
1678 -command do_macosx_app
1682 .mbar.repository add
command -label Quit \
1689 .mbar.edit add
command -label Undo \
1690 -command {catch
{[focus
] edit undo
}} \
1692 .mbar.edit add
command -label Redo \
1693 -command {catch
{[focus
] edit redo
}} \
1695 .mbar.edit add separator
1696 .mbar.edit add
command -label Cut \
1697 -command {catch
{tk_textCut
[focus
]}} \
1699 .mbar.edit add
command -label Copy \
1700 -command {catch
{tk_textCopy
[focus
]}} \
1702 .mbar.edit add
command -label Paste \
1703 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
1705 .mbar.edit add
command -label Delete \
1706 -command {catch
{[focus
] delete sel.first sel.last
}} \
1708 .mbar.edit add separator
1709 .mbar.edit add
command -label {Select All
} \
1710 -command {catch
{[focus
] tag add sel
0.0 end
}} \
1715 if {[is_enabled branch
]} {
1718 .mbar.branch add
command -label {Create...
} \
1719 -command branch_create
::dialog \
1721 lappend disable_on_lock
[list .mbar.branch entryconf \
1722 [.mbar.branch index last
] -state]
1724 .mbar.branch add
command -label {Checkout...
} \
1725 -command branch_checkout
::dialog \
1727 lappend disable_on_lock
[list .mbar.branch entryconf \
1728 [.mbar.branch index last
] -state]
1730 .mbar.branch add
command -label {Rename...
} \
1731 -command branch_rename
::dialog
1732 lappend disable_on_lock
[list .mbar.branch entryconf \
1733 [.mbar.branch index last
] -state]
1735 .mbar.branch add
command -label {Delete...
} \
1736 -command branch_delete
::dialog
1737 lappend disable_on_lock
[list .mbar.branch entryconf \
1738 [.mbar.branch index last
] -state]
1740 .mbar.branch add
command -label {Reset...
} \
1741 -command merge
::reset_hard
1742 lappend disable_on_lock
[list .mbar.branch entryconf \
1743 [.mbar.branch index last
] -state]
1748 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1751 .mbar.commit add radiobutton \
1752 -label {New Commit
} \
1753 -command do_select_commit_type \
1754 -variable selected_commit_type \
1756 lappend disable_on_lock \
1757 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1759 .mbar.commit add radiobutton \
1760 -label {Amend Last Commit
} \
1761 -command do_select_commit_type \
1762 -variable selected_commit_type \
1764 lappend disable_on_lock \
1765 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1767 .mbar.commit add separator
1769 .mbar.commit add
command -label Rescan \
1770 -command do_rescan \
1772 lappend disable_on_lock \
1773 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1775 .mbar.commit add
command -label {Add To Commit
} \
1776 -command do_add_selection
1777 lappend disable_on_lock \
1778 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1780 .mbar.commit add
command -label {Add Existing To Commit
} \
1781 -command do_add_all \
1783 lappend disable_on_lock \
1784 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1786 .mbar.commit add
command -label {Unstage From Commit
} \
1787 -command do_unstage_selection
1788 lappend disable_on_lock \
1789 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1791 .mbar.commit add
command -label {Revert Changes
} \
1792 -command do_revert_selection
1793 lappend disable_on_lock \
1794 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1796 .mbar.commit add separator
1798 .mbar.commit add
command -label {Sign Off
} \
1799 -command do_signoff \
1802 .mbar.commit add
command -label Commit \
1803 -command do_commit \
1804 -accelerator $M1T-Return
1805 lappend disable_on_lock \
1806 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1811 if {[is_enabled branch
]} {
1813 .mbar.merge add
command -label {Local Merge...
} \
1814 -command merge
::dialog
1815 lappend disable_on_lock \
1816 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1817 .mbar.merge add
command -label {Abort Merge...
} \
1818 -command merge
::reset_hard
1819 lappend disable_on_lock \
1820 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1826 if {[is_enabled transport
]} {
1830 .mbar.push add
command -label {Push...
} \
1831 -command do_push_anywhere \
1833 .mbar.push add
command -label {Delete...
} \
1834 -command remote_branch_delete
::dialog
1838 # -- Apple Menu (Mac OS X only)
1840 .mbar add cascade
-label Apple
-menu .mbar.apple
1843 .mbar.apple add
command -label "About [appname]" \
1845 .mbar.apple add
command -label "Options..." \
1850 .mbar.edit add separator
1851 .mbar.edit add
command -label {Options...
} \
1856 if {[is_Cygwin
] && [file exists
/usr
/local
/miga
/lib
/gui-miga
]} {
1858 if {![lock_index update
]} return
1859 set cmd
[list sh
--login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1860 set miga_fd
[open
"|$cmd" r
]
1861 fconfigure
$miga_fd -blocking 0
1862 fileevent
$miga_fd readable
[list miga_done
$miga_fd]
1863 ui_status
{Running miga...
}
1865 proc miga_done
{fd
} {
1873 .mbar add cascade
-label Tools
-menu .mbar.tools
1875 .mbar.tools add
command -label "Migrate" \
1877 lappend disable_on_lock \
1878 [list .mbar.tools entryconf
[.mbar.tools index last
] -state]
1884 .mbar add cascade
-label Help
-menu .mbar.
help
1888 .mbar.
help add
command -label "About [appname]" \
1893 catch
{set browser
$repo_config(instaweb.browser
)}
1894 set doc_path
[file dirname [gitexec
]]
1895 set doc_path
[file join $doc_path Documentation index.html
]
1898 set doc_path
[exec cygpath
--mixed $doc_path]
1901 if {$browser eq
{}} {
1904 } elseif
{[is_Cygwin
]} {
1905 set program_files
[file dirname [exec cygpath
--windir]]
1906 set program_files
[file join $program_files {Program Files
}]
1907 set firefox
[file join $program_files {Mozilla Firefox
} firefox.exe
]
1908 set ie
[file join $program_files {Internet Explorer
} IEXPLORE.EXE
]
1909 if {[file exists
$firefox]} {
1910 set browser
$firefox
1911 } elseif
{[file exists
$ie]} {
1914 unset program_files firefox ie
1918 if {[file isfile
$doc_path]} {
1919 set doc_url
"file:$doc_path"
1921 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
1924 if {$browser ne
{}} {
1925 .mbar.
help add
command -label {Online Documentation
} \
1926 -command [list
exec $browser $doc_url &]
1928 unset browser doc_path doc_url
1930 # -- Standard bindings
1932 wm protocol . WM_DELETE_WINDOW do_quit
1933 bind all
<$M1B-Key-q> do_quit
1934 bind all
<$M1B-Key-Q> do_quit
1935 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
1936 bind all
<$M1B-Key-W> {destroy
[winfo toplevel
%W
]}
1938 set subcommand_args
{}
1940 puts stderr
"usage: $::argv0 $::subcommand $::subcommand_args"
1944 # -- Not a normal commit type invocation? Do that instead!
1946 switch
-- $subcommand {
1948 set subcommand_args
{rev?
}
1949 switch
[llength
$argv] {
1950 0 { load_current_branch
}
1952 set current_branch
[lindex
$argv 0]
1953 if {[regexp
{^
[0-9a-f]{1,39}$
} $current_branch]} {
1955 set current_branch \
1956 [git rev-parse
--verify $current_branch]
1965 browser
::new
$current_branch
1969 set subcommand_args
{rev? path?
}
1974 if {$is_path ||
[file exists
$_prefix$a]} {
1975 if {$path ne
{}} usage
1978 } elseif
{$a eq
{--}} {
1980 if {$head ne
{}} usage
1985 } elseif
{$head eq
{}} {
1986 if {$head ne
{}} usage
1997 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
1999 set head [git rev-parse
--verify $head]
2005 set current_branch
$head
2008 if {$path eq
{}} usage
2009 blame
::new
$head $path
2014 if {[llength
$argv] != 0} {
2015 puts
-nonewline stderr
"usage: $argv0"
2016 if {$subcommand ne
{gui
} && [appname
] ne
"git-$subcommand"} {
2017 puts
-nonewline stderr
" $subcommand"
2022 # fall through to setup UI for commits
2025 puts stderr
"usage: $argv0 \[{blame|browser|citool}\]"
2036 -text {Current Branch
:} \
2040 -textvariable current_branch \
2043 pack .branch.l1
-side left
2044 pack .branch.cb
-side left
-fill x
2045 pack .branch
-side top
-fill x
2047 # -- Main Window Layout
2049 panedwindow .vpane
-orient vertical
2050 panedwindow .vpane.files
-orient horizontal
2051 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
2052 pack .vpane
-anchor n
-side top
-fill both
-expand 1
2054 # -- Index File List
2056 frame .vpane.files.index
-height 100 -width 200
2057 label .vpane.files.index.title
-text {Staged Changes
(Will Be Committed
)} \
2058 -background lightgreen
2059 text
$ui_index -background white
-borderwidth 0 \
2060 -width 20 -height 10 \
2062 -cursor $cursor_ptr \
2063 -xscrollcommand {.vpane.files.index.sx
set} \
2064 -yscrollcommand {.vpane.files.index.sy
set} \
2066 scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
2067 scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
2068 pack .vpane.files.index.title
-side top
-fill x
2069 pack .vpane.files.index.sx
-side bottom
-fill x
2070 pack .vpane.files.index.sy
-side right
-fill y
2071 pack
$ui_index -side left
-fill both
-expand 1
2072 .vpane.files add .vpane.files.index
-sticky nsew
2074 # -- Working Directory File List
2076 frame .vpane.files.workdir
-height 100 -width 200
2077 label .vpane.files.workdir.title
-text {Unstaged Changes
(Will Not Be Committed
)} \
2078 -background lightsalmon
2079 text
$ui_workdir -background white
-borderwidth 0 \
2080 -width 20 -height 10 \
2082 -cursor $cursor_ptr \
2083 -xscrollcommand {.vpane.files.workdir.sx
set} \
2084 -yscrollcommand {.vpane.files.workdir.sy
set} \
2086 scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
2087 scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
2088 pack .vpane.files.workdir.title
-side top
-fill x
2089 pack .vpane.files.workdir.sx
-side bottom
-fill x
2090 pack .vpane.files.workdir.sy
-side right
-fill y
2091 pack
$ui_workdir -side left
-fill both
-expand 1
2092 .vpane.files add .vpane.files.workdir
-sticky nsew
2094 foreach i
[list
$ui_index $ui_workdir] {
2095 $i tag conf in_diff
-background lightgray
2096 $i tag conf in_sel
-background lightgray
2100 # -- Diff and Commit Area
2102 frame .vpane.lower
-height 300 -width 400
2103 frame .vpane.lower.commarea
2104 frame .vpane.lower.
diff -relief sunken
-borderwidth 1
2105 pack .vpane.lower.commarea
-side top
-fill x
2106 pack .vpane.lower.
diff -side bottom
-fill both
-expand 1
2107 .vpane add .vpane.lower
-sticky nsew
2109 # -- Commit Area Buttons
2111 frame .vpane.lower.commarea.buttons
2112 label .vpane.lower.commarea.buttons.l
-text {} \
2115 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
2116 pack .vpane.lower.commarea.buttons
-side left
-fill y
2118 button .vpane.lower.commarea.buttons.rescan
-text {Rescan
} \
2120 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
2121 lappend disable_on_lock \
2122 {.vpane.lower.commarea.buttons.rescan conf
-state}
2124 button .vpane.lower.commarea.buttons.incall
-text {Add Existing
} \
2126 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
2127 lappend disable_on_lock \
2128 {.vpane.lower.commarea.buttons.incall conf
-state}
2130 button .vpane.lower.commarea.buttons.signoff
-text {Sign Off
} \
2132 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
2134 button .vpane.lower.commarea.buttons.commit
-text {Commit
} \
2136 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
2137 lappend disable_on_lock \
2138 {.vpane.lower.commarea.buttons.commit conf
-state}
2140 button .vpane.lower.commarea.buttons.push
-text {Push
} \
2141 -command do_push_anywhere
2142 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
2144 # -- Commit Message Buffer
2146 frame .vpane.lower.commarea.buffer
2147 frame .vpane.lower.commarea.buffer.header
2148 set ui_comm .vpane.lower.commarea.buffer.t
2149 set ui_coml .vpane.lower.commarea.buffer.header.l
2150 radiobutton .vpane.lower.commarea.buffer.header.new \
2151 -text {New Commit
} \
2152 -command do_select_commit_type \
2153 -variable selected_commit_type \
2155 lappend disable_on_lock \
2156 [list .vpane.lower.commarea.buffer.header.new conf
-state]
2157 radiobutton .vpane.lower.commarea.buffer.header.amend \
2158 -text {Amend Last Commit
} \
2159 -command do_select_commit_type \
2160 -variable selected_commit_type \
2162 lappend disable_on_lock \
2163 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
2167 proc trace_commit_type
{varname args
} {
2168 global ui_coml commit_type
2169 switch
-glob -- $commit_type {
2170 initial
{set txt
{Initial Commit Message
:}}
2171 amend
{set txt
{Amended Commit Message
:}}
2172 amend-initial
{set txt
{Amended Initial Commit Message
:}}
2173 amend-merge
{set txt
{Amended Merge Commit Message
:}}
2174 merge
{set txt
{Merge Commit Message
:}}
2175 * {set txt
{Commit Message
:}}
2177 $ui_coml conf
-text $txt
2179 trace add variable commit_type
write trace_commit_type
2180 pack
$ui_coml -side left
-fill x
2181 pack .vpane.lower.commarea.buffer.header.amend
-side right
2182 pack .vpane.lower.commarea.buffer.header.new
-side right
2184 text
$ui_comm -background white
-borderwidth 1 \
2187 -autoseparators true \
2189 -width 75 -height 9 -wrap none \
2191 -yscrollcommand {.vpane.lower.commarea.buffer.sby
set}
2192 scrollbar .vpane.lower.commarea.buffer.sby \
2193 -command [list
$ui_comm yview
]
2194 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
2195 pack .vpane.lower.commarea.buffer.sby
-side right
-fill y
2196 pack
$ui_comm -side left
-fill y
2197 pack .vpane.lower.commarea.buffer
-side left
-fill y
2199 # -- Commit Message Buffer Context Menu
2201 set ctxm .vpane.lower.commarea.buffer.ctxm
2202 menu
$ctxm -tearoff 0
2205 -command {tk_textCut
$ui_comm}
2208 -command {tk_textCopy
$ui_comm}
2211 -command {tk_textPaste
$ui_comm}
2214 -command {$ui_comm delete sel.first sel.last
}
2217 -label {Select All
} \
2218 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
2222 $ui_comm tag add sel
0.0 end
2223 tk_textCopy
$ui_comm
2224 $ui_comm tag remove sel
0.0 end
2230 bind_button3
$ui_comm "tk_popup $ctxm %X %Y"
2234 proc trace_current_diff_path
{varname args
} {
2235 global current_diff_path diff_actions file_states
2236 if {$current_diff_path eq
{}} {
2242 set p
$current_diff_path
2243 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
2245 set p
[escape_path
$p]
2249 .vpane.lower.
diff.header.status configure
-text $s
2250 .vpane.lower.
diff.header.
file configure
-text $f
2251 .vpane.lower.
diff.header.path configure
-text $p
2252 foreach w
$diff_actions {
2256 trace add variable current_diff_path
write trace_current_diff_path
2258 frame .vpane.lower.
diff.header
-background gold
2259 label .vpane.lower.
diff.header.status \
2261 -width $max_status_desc \
2264 label .vpane.lower.
diff.header.
file \
2268 label .vpane.lower.
diff.header.path \
2272 pack .vpane.lower.
diff.header.status
-side left
2273 pack .vpane.lower.
diff.header.
file -side left
2274 pack .vpane.lower.
diff.header.path
-fill x
2275 set ctxm .vpane.lower.
diff.header.ctxm
2276 menu
$ctxm -tearoff 0
2284 -- $current_diff_path
2286 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2287 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
2291 frame .vpane.lower.
diff.body
2292 set ui_diff .vpane.lower.
diff.body.t
2293 text
$ui_diff -background white
-borderwidth 0 \
2294 -width 80 -height 15 -wrap none \
2296 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
2297 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
2299 scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
2300 -command [list
$ui_diff xview
]
2301 scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
2302 -command [list
$ui_diff yview
]
2303 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
2304 pack .vpane.lower.
diff.body.sby
-side right
-fill y
2305 pack
$ui_diff -side left
-fill both
-expand 1
2306 pack .vpane.lower.
diff.header
-side top
-fill x
2307 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
2309 $ui_diff tag conf d_cr
-elide true
2310 $ui_diff tag conf d_@
-foreground blue
-font font_diffbold
2311 $ui_diff tag conf d_
+ -foreground {#00a000}
2312 $ui_diff tag conf d_-
-foreground red
2314 $ui_diff tag conf d_
++ -foreground {#00a000}
2315 $ui_diff tag conf d_--
-foreground red
2316 $ui_diff tag conf d_
+s \
2317 -foreground {#00a000} \
2318 -background {#e2effa}
2319 $ui_diff tag conf d_-s \
2321 -background {#e2effa}
2322 $ui_diff tag conf d_s
+ \
2323 -foreground {#00a000} \
2325 $ui_diff tag conf d_s- \
2329 $ui_diff tag conf d
<<<<<<< \
2330 -foreground orange \
2332 $ui_diff tag conf d
======= \
2333 -foreground orange \
2335 $ui_diff tag conf d
>>>>>>> \
2336 -foreground orange \
2339 $ui_diff tag raise sel
2341 # -- Diff Body Context Menu
2343 set ctxm .vpane.lower.
diff.body.ctxm
2344 menu
$ctxm -tearoff 0
2347 -command reshow_diff
2348 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2351 -command {tk_textCopy
$ui_diff}
2352 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2354 -label {Select All
} \
2355 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
2356 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2360 $ui_diff tag add sel
0.0 end
2361 tk_textCopy
$ui_diff
2362 $ui_diff tag remove sel
0.0 end
2364 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2367 -label {Apply
/Reverse Hunk
} \
2368 -command {apply_hunk
$cursorX $cursorY}
2369 set ui_diff_applyhunk
[$ctxm index last
]
2370 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
2373 -label {Decrease Font Size
} \
2374 -command {incr_font_size font_diff
-1}
2375 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2377 -label {Increase Font Size
} \
2378 -command {incr_font_size font_diff
1}
2379 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2382 -label {Show Less Context
} \
2383 -command {if {$repo_config(gui.diffcontext
) >= 1} {
2384 incr repo_config
(gui.diffcontext
) -1
2387 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2389 -label {Show More Context
} \
2390 -command {if {$repo_config(gui.diffcontext
) < 99} {
2391 incr repo_config
(gui.diffcontext
)
2394 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2396 $ctxm add
command -label {Options...
} \
2398 bind_button3
$ui_diff "
2401 if {\$ui_index eq \$current_diff_side} {
2402 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2404 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2406 tk_popup $ctxm %X %Y
2408 unset ui_diff_applyhunk
2412 set main_status
[::status_bar
::new .status
]
2413 pack .status
-anchor w
-side bottom
-fill x
2414 $main_status show
{Initializing...
}
2419 set gm
$repo_config(gui.geometry
)
2420 wm geometry .
[lindex
$gm 0]
2421 .vpane sash place
0 \
2422 [lindex
[.vpane sash coord
0] 0] \
2424 .vpane.files sash place
0 \
2426 [lindex
[.vpane.files sash coord
0] 1]
2432 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
2433 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
2434 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
2435 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
2436 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
2437 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
2438 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
2439 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
2440 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
2441 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2442 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2444 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
2445 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
2446 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
2447 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
2448 bind $ui_diff <$M1B-Key-v> {break}
2449 bind $ui_diff <$M1B-Key-V> {break}
2450 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2451 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2452 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
2453 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
2454 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
2455 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
2456 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
2457 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
2458 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
2459 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
2460 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
2461 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
2462 bind $ui_diff <Button-1
> {focus
%W
}
2464 if {[is_enabled branch
]} {
2465 bind .
<$M1B-Key-n> branch_create
::dialog
2466 bind .
<$M1B-Key-N> branch_create
::dialog
2467 bind .
<$M1B-Key-o> branch_checkout
::dialog
2468 bind .
<$M1B-Key-O> branch_checkout
::dialog
2470 if {[is_enabled transport
]} {
2471 bind .
<$M1B-Key-p> do_push_anywhere
2472 bind .
<$M1B-Key-P> do_push_anywhere
2475 bind .
<Key-F5
> do_rescan
2476 bind .
<$M1B-Key-r> do_rescan
2477 bind .
<$M1B-Key-R> do_rescan
2478 bind .
<$M1B-Key-s> do_signoff
2479 bind .
<$M1B-Key-S> do_signoff
2480 bind .
<$M1B-Key-i> do_add_all
2481 bind .
<$M1B-Key-I> do_add_all
2482 bind .
<$M1B-Key-Return> do_commit
2483 foreach i
[list
$ui_index $ui_workdir] {
2484 bind $i <Button-1
> "toggle_or_diff $i %x %y; break"
2485 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2486 bind $i <Shift-Button-1
> "add_range_to_selection $i %x %y; break"
2490 set file_lists
($ui_index) [list
]
2491 set file_lists
($ui_workdir) [list
]
2493 wm title .
"[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2494 focus
-force $ui_comm
2496 # -- Warn the user about environmental problems. Cygwin's Tcl
2497 # does *not* pass its env array onto any processes it spawns.
2498 # This means that git processes get none of our environment.
2503 set msg
"Possible environment issues exist.
2505 The following environment variables are probably
2506 going to be ignored by any Git subprocess run
2510 foreach name
[array names env
] {
2511 switch
-regexp -- $name {
2512 {^GIT_INDEX_FILE$
} -
2513 {^GIT_OBJECT_DIRECTORY$
} -
2514 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$
} -
2516 {^GIT_EXTERNAL_DIFF$
} -
2520 {^GIT_CONFIG_LOCAL$
} -
2521 {^GIT_
(AUTHOR|COMMITTER
)_DATE$
} {
2522 append msg
" - $name\n"
2525 {^GIT_
(AUTHOR|COMMITTER
)_
(NAME|EMAIL
)$
} {
2526 append msg
" - $name\n"
2528 set suggest_user
$name
2532 if {$ignored_env > 0} {
2534 This is due to a known issue with the
2535 Tcl binary distributed by Cygwin."
2537 if {$suggest_user ne
{}} {
2540 A good replacement for $suggest_user
2541 is placing values for the user.name and
2542 user.email settings into your personal
2548 unset ignored_env msg suggest_user name
2551 # -- Only initialize complex UI if we are going to stay running.
2553 if {[is_enabled transport
]} {
2560 # -- Only suggest a gc run if we are going to stay running.
2562 if {[is_enabled multicommit
]} {
2563 set object_limit
2000
2564 if {[is_Windows
]} {set object_limit
200}
2565 regexp
{^
([0-9]+) objects
,} [git count-objects
] _junk objects_current
2566 if {$objects_current >= $object_limit} {
2568 "This repository currently has $objects_current loose objects.
2570 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2572 Compress the database now?"] eq
yes} {
2576 unset object_limit _junk objects_current
2579 lock_index begin-read