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
] \
420 proc git_read
{args
} {
424 switch
-- [lindex
$args 0] {
442 set args
[lrange
$args 1 end
]
445 set cmdp
[_git_cmd
[lindex
$args 0]]
446 set args
[lrange
$args 1 end
]
448 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
451 proc git_write
{args
} {
455 switch
-- [lindex
$args 0] {
469 set args
[lrange
$args 1 end
]
472 set cmdp
[_git_cmd
[lindex
$args 0]]
473 set args
[lrange
$args 1 end
]
475 return [open
[concat
$opt $cmdp $args] w
]
479 regsub
-all ' $value "'\\''" value
483 proc load_current_branch {} {
484 global current_branch is_detached
486 set fd [open [gitdir HEAD] r]
487 if {[gets $fd ref] < 1} {
492 set pfx {ref: refs/heads/}
493 set len [string length $pfx]
494 if {[string equal -length $len $pfx $ref]} {
495 # We're on a branch. It might not exist. But
496 # HEAD looks good enough to be a branch.
498 set current_branch [string range $ref $len end]
501 # Assume this is a detached head.
503 set current_branch HEAD
508 auto_load tk_optionMenu
509 rename tk_optionMenu real__tkOptionMenu
510 proc tk_optionMenu {w varName args} {
511 set m [eval real__tkOptionMenu $w $varName $args]
512 $m configure -font font_ui
513 $w configure -font font_ui
517 ######################################################################
521 set _git [_which git]
523 catch {wm withdraw .}
524 error_popup "Cannot
find git
in PATH.
"
527 set _nice [_which nice]
529 ######################################################################
533 if {[catch {set _git_version [git --version]} err]} {
534 catch {wm withdraw .}
535 error_popup "Cannot determine Git version
:
539 [appname
] requires Git
1.5.0 or later.
"
542 if {![regsub {^git version } $_git_version {} _git_version]} {
543 catch {wm withdraw .}
544 error_popup "Cannot parse Git version string
:\n\n$_git_version"
547 regsub {-dirty$} $_git_version {} _git_version
548 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
549 regsub {\.rc[0-9]+$} $_git_version {} _git_version
551 proc git-version {args} {
554 switch [llength $args] {
560 set op [lindex $args 0]
561 set vr [lindex $args 1]
562 set cm [package vcompare $_git_version $vr]
563 return [expr $cm $op 0]
567 set type [lindex $args 0]
568 set name [lindex $args 1]
569 set parm [lindex $args 2]
570 set body [lindex $args 3]
572 if {($type ne {proc} && $type ne {method})} {
573 error "Invalid arguments to git-version
"
575 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
576 error "Last arm of
$type $name must be default
"
579 foreach {op vr cb} [lrange $body 0 end-2] {
580 if {[git-version $op $vr]} {
581 return [uplevel [list $type $name $parm $cb]]
585 return [uplevel [list $type $name $parm [lindex $body end]]]
589 error "git-version
>= x
"
595 if {[git-version < 1.5]} {
596 catch {wm withdraw .}
597 error_popup "[appname
] requires Git
1.5.0 or later.
599 You are using
[git-version
]:
605 ######################################################################
610 set _gitdir $env(GIT_DIR)
614 set _gitdir [git rev-parse --git-dir]
615 set _prefix [git rev-parse --show-prefix]
617 catch {wm withdraw .}
618 error_popup "Cannot
find the git directory
:\n\n$err"
621 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
622 catch {set _gitdir [exec cygpath --unix $_gitdir]}
624 if {![file isdirectory $_gitdir]} {
625 catch {wm withdraw .}
626 error_popup "Git directory not found
:\n\n$_gitdir"
629 if {[lindex [file split $_gitdir] end] ne {.git}} {
630 catch {wm withdraw .}
631 error_popup "Cannot use funny .git directory
:\n\n$_gitdir"
634 if {[catch {cd [file dirname $_gitdir]} err]} {
635 catch {wm withdraw .}
636 error_popup "No working directory
[file dirname $_gitdir]:\n\n$err"
639 set _reponame [lindex [file split \
640 [file normalize [file dirname $_gitdir]]] \
643 ######################################################################
647 set current_diff_path {}
648 set current_diff_side {}
649 set diff_actions [list]
653 set MERGE_HEAD [list]
656 set current_branch {}
658 set current_diff_path {}
659 set selected_commit_type new
661 ######################################################################
669 set disable_on_lock [list]
670 set index_lock_type none
672 proc lock_index {type} {
673 global index_lock_type disable_on_lock
675 if {$index_lock_type eq {none}} {
676 set index_lock_type $type
677 foreach w $disable_on_lock {
678 uplevel #0 $w disabled
681 } elseif {$index_lock_type eq "begin-
$type"} {
682 set index_lock_type $type
688 proc unlock_index {} {
689 global index_lock_type disable_on_lock
691 set index_lock_type none
692 foreach w $disable_on_lock {
697 ######################################################################
701 proc repository_state {ctvar hdvar mhvar} {
702 global current_branch
703 upvar $ctvar ct $hdvar hd $mhvar mh
708 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
714 set merge_head [gitdir MERGE_HEAD]
715 if {[file exists $merge_head]} {
717 set fd_mh [open $merge_head r]
718 while {[gets $fd_mh line] >= 0} {
729 global PARENT empty_tree
731 set p [lindex $PARENT 0]
735 if {$empty_tree eq {}} {
736 set empty_tree [git mktree << {}]
741 proc rescan {after {honor_trustmtime 1}} {
742 global HEAD PARENT MERGE_HEAD commit_type
743 global ui_index ui_workdir ui_comm
744 global rescan_active file_states
747 if {$rescan_active > 0 || ![lock_index read]} return
749 repository_state newType newHEAD newMERGE_HEAD
750 if {[string match amend* $commit_type]
751 && $newType eq {normal}
752 && $newHEAD eq $HEAD} {
756 set MERGE_HEAD $newMERGE_HEAD
757 set commit_type $newType
760 array unset file_states
762 if {![$ui_comm edit modified]
763 || [string trim [$ui_comm get 0.0 end]] eq {}} {
764 if {[string match amend* $commit_type]} {
765 } elseif {[load_message GITGUI_MSG]} {
766 } elseif {[load_message MERGE_MSG]} {
767 } elseif {[load_message SQUASH_MSG]} {
770 $ui_comm edit modified false
773 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
774 rescan_stage2 {} $after
777 ui_status {Refreshing file status...}
778 set fd_rf [git_read update-index \
784 fconfigure $fd_rf -blocking 0 -translation binary
785 fileevent $fd_rf readable \
786 [list rescan_stage2 $fd_rf $after]
790 proc rescan_stage2 {fd after} {
791 global rescan_active buf_rdi buf_rdf buf_rlo
795 if {![eof $fd]} return
799 set ls_others [list --exclude-per-directory=.gitignore]
800 set info_exclude [gitdir info exclude]
801 if {[file readable $info_exclude]} {
802 lappend ls_others "--exclude-from=$info_exclude"
810 ui_status {Scanning for modified files ...}
811 set fd_di [git_read diff-index --cached -z [PARENT]]
812 set fd_df [git_read diff-files -z]
813 set fd_lo [eval git_read ls-files --others -z $ls_others]
815 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
816 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
817 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
818 fileevent $fd_di readable [list read_diff_index $fd_di $after]
819 fileevent $fd_df readable [list read_diff_files $fd_df $after]
820 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
823 proc load_message {file} {
827 if {[file isfile $f]} {
828 if {[catch {set fd [open $f r]}]} {
831 set content [string trim [read $fd]]
833 regsub -all -line {[ \r\t]+$} $content {} content
834 $ui_comm delete 0.0 end
835 $ui_comm insert end $content
841 proc read_diff_index {fd after} {
844 append buf_rdi [read $fd]
846 set n [string length $buf_rdi]
848 set z1 [string first "\
0" $buf_rdi $c]
851 set z2 [string first "\
0" $buf_rdi $z1]
855 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
856 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
858 [encoding convertfrom $p] \
860 [list [lindex $i 0] [lindex $i 2]] \
866 set buf_rdi [string range $buf_rdi $c end]
871 rescan_done $fd buf_rdi $after
874 proc read_diff_files {fd after} {
877 append buf_rdf [read $fd]
879 set n [string length $buf_rdf]
881 set z1 [string first "\
0" $buf_rdf $c]
884 set z2 [string first "\
0" $buf_rdf $z1]
888 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
889 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
891 [encoding convertfrom $p] \
894 [list [lindex $i 0] [lindex $i 2]]
899 set buf_rdf [string range $buf_rdf $c end]
904 rescan_done $fd buf_rdf $after
907 proc read_ls_others {fd after} {
910 append buf_rlo [read $fd]
911 set pck [split $buf_rlo "\
0"]
912 set buf_rlo [lindex $pck end]
913 foreach p [lrange $pck 0 end-1] {
914 merge_state [encoding convertfrom $p] ?O
916 rescan_done $fd buf_rlo $after
919 proc rescan_done {fd buf after} {
920 global rescan_active current_diff_path
921 global file_states repo_config
924 if {![eof $fd]} return
927 if {[incr rescan_active -1] > 0} return
932 if {$current_diff_path ne {}} reshow_diff
936 proc prune_selection {} {
937 global file_states selected_paths
939 foreach path [array names selected_paths] {
940 if {[catch {set still_here $file_states($path)}]} {
941 unset selected_paths($path)
946 ######################################################################
950 proc mapicon {w state path} {
953 if {[catch {set r $all_icons($state$w)}]} {
954 puts "error
: no icon
for $w state
={$state} $path"
960 proc mapdesc {state path} {
963 if {[catch {set r $all_descs($state)}]} {
964 puts "error
: no desc
for state
={$state} $path"
970 proc ui_status {msg} {
971 $::main_status show $msg
974 proc ui_ready {{test {}}} {
975 $::main_status show {Ready.} $test
978 proc escape_path {path} {
979 regsub -all {\\} $path "\\\\" path
980 regsub -all "\n" $path "\\n
" path
984 proc short_path {path} {
985 return [escape_path [lindex [file split $path] end]]
989 set null_sha1 [string repeat 0 40]
991 proc merge_state {path new_state {head_info {}} {index_info {}}} {
992 global file_states next_icon_id null_sha1
994 set s0 [string index $new_state 0]
995 set s1 [string index $new_state 1]
997 if {[catch {set info $file_states($path)}]} {
999 set icon n[incr next_icon_id]
1001 set state [lindex $info 0]
1002 set icon [lindex $info 1]
1003 if {$head_info eq {}} {set head_info [lindex $info 2]}
1004 if {$index_info eq {}} {set index_info [lindex $info 3]}
1007 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1008 elseif {$s0 eq {_}} {set s0 _}
1010 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1011 elseif {$s1 eq {_}} {set s1 _}
1013 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1014 set head_info [list 0 $null_sha1]
1015 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1016 && $head_info eq {}} {
1017 set head_info $index_info
1020 set file_states($path) [list $s0$s1 $icon \
1021 $head_info $index_info \
1026 proc display_file_helper {w path icon_name old_m new_m} {
1029 if {$new_m eq {_}} {
1030 set lno [lsearch -sorted -exact $file_lists($w) $path]
1032 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1034 $w conf -state normal
1035 $w delete $lno.0 [expr {$lno + 1}].0
1036 $w conf -state disabled
1038 } elseif {$old_m eq {_} && $new_m ne {_}} {
1039 lappend file_lists($w) $path
1040 set file_lists($w) [lsort -unique $file_lists($w)]
1041 set lno [lsearch -sorted -exact $file_lists($w) $path]
1043 $w conf -state normal
1044 $w image create $lno.0 \
1045 -align center -padx 5 -pady 1 \
1047 -image [mapicon $w $new_m $path]
1048 $w insert $lno.1 "[escape_path
$path]\n"
1049 $w conf -state disabled
1050 } elseif {$old_m ne $new_m} {
1051 $w conf -state normal
1052 $w image conf $icon_name -image [mapicon $w $new_m $path]
1053 $w conf -state disabled
1057 proc display_file {path state} {
1058 global file_states selected_paths
1059 global ui_index ui_workdir
1061 set old_m [merge_state $path $state]
1062 set s $file_states($path)
1063 set new_m [lindex $s 0]
1064 set icon_name [lindex $s 1]
1066 set o [string index $old_m 0]
1067 set n [string index $new_m 0]
1074 display_file_helper $ui_index $path $icon_name $o $n
1076 if {[string index $old_m 0] eq {U}} {
1079 set o [string index $old_m 1]
1081 if {[string index $new_m 0] eq {U}} {
1084 set n [string index $new_m 1]
1086 display_file_helper $ui_workdir $path $icon_name $o $n
1088 if {$new_m eq {__}} {
1089 unset file_states($path)
1090 catch {unset selected_paths($path)}
1094 proc display_all_files_helper {w path icon_name m} {
1097 lappend file_lists($w) $path
1098 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1099 $w image create end \
1100 -align center -padx 5 -pady 1 \
1102 -image [mapicon $w $m $path]
1103 $w insert end "[escape_path
$path]\n"
1106 proc display_all_files {} {
1107 global ui_index ui_workdir
1108 global file_states file_lists
1111 $ui_index conf -state normal
1112 $ui_workdir conf -state normal
1114 $ui_index delete 0.0 end
1115 $ui_workdir delete 0.0 end
1118 set file_lists($ui_index) [list]
1119 set file_lists($ui_workdir) [list]
1121 foreach path [lsort [array names file_states]] {
1122 set s $file_states($path)
1124 set icon_name [lindex $s 1]
1126 set s [string index $m 0]
1127 if {$s ne {U} && $s ne {_}} {
1128 display_all_files_helper $ui_index $path \
1132 if {[string index $m 0] eq {U}} {
1135 set s [string index $m 1]
1138 display_all_files_helper $ui_workdir $path \
1143 $ui_index conf -state disabled
1144 $ui_workdir conf -state disabled
1147 ######################################################################
1152 #define mask_width 14
1153 #define mask_height 15
1154 static unsigned char mask_bits[] = {
1155 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1156 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1157 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1160 image create bitmap file_plain -background white -foreground black -data {
1161 #define plain_width 14
1162 #define plain_height 15
1163 static unsigned char plain_bits[] = {
1164 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1165 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1166 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1167 } -maskdata $filemask
1169 image create bitmap file_mod -background white -foreground blue -data {
1170 #define mod_width 14
1171 #define mod_height 15
1172 static unsigned char mod_bits[] = {
1173 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1174 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1175 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1176 } -maskdata $filemask
1178 image create bitmap file_fulltick -background white -foreground "#007000" -data {
1179 #define file_fulltick_width 14
1180 #define file_fulltick_height 15
1181 static unsigned char file_fulltick_bits
[] = {
1182 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1183 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1184 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1185 } -maskdata $filemask
1187 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
1188 #define parttick_width 14
1189 #define parttick_height 15
1190 static unsigned char parttick_bits
[] = {
1191 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1192 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1193 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1194 } -maskdata $filemask
1196 image create bitmap file_question
-background white
-foreground black
-data {
1197 #define file_question_width 14
1198 #define file_question_height 15
1199 static unsigned char file_question_bits
[] = {
1200 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1201 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1202 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1203 } -maskdata $filemask
1205 image create bitmap file_removed
-background white
-foreground red
-data {
1206 #define file_removed_width 14
1207 #define file_removed_height 15
1208 static unsigned char file_removed_bits
[] = {
1209 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1210 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1211 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1212 } -maskdata $filemask
1214 image create bitmap file_merge
-background white
-foreground blue
-data {
1215 #define file_merge_width 14
1216 #define file_merge_height 15
1217 static unsigned char file_merge_bits
[] = {
1218 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1219 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1220 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1221 } -maskdata $filemask
1224 #define file_width 18
1225 #define file_height 18
1226 static unsigned char file_bits
[] = {
1227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
1228 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
1229 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
1230 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
1231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1233 image create bitmap file_dir
-background white
-foreground blue \
1234 -data $file_dir_data -maskdata $file_dir_data
1237 set file_uplevel_data
{
1239 #define up_height 15
1240 static unsigned char up_bits
[] = {
1241 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
1242 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
1243 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
1245 image create bitmap file_uplevel
-background white
-foreground red \
1246 -data $file_uplevel_data -maskdata $file_uplevel_data
1247 unset file_uplevel_data
1249 set ui_index .vpane.files.index.list
1250 set ui_workdir .vpane.files.workdir.list
1252 set all_icons
(_
$ui_index) file_plain
1253 set all_icons
(A
$ui_index) file_fulltick
1254 set all_icons
(M
$ui_index) file_fulltick
1255 set all_icons
(D
$ui_index) file_removed
1256 set all_icons
(U
$ui_index) file_merge
1258 set all_icons
(_
$ui_workdir) file_plain
1259 set all_icons
(M
$ui_workdir) file_mod
1260 set all_icons
(D
$ui_workdir) file_question
1261 set all_icons
(U
$ui_workdir) file_merge
1262 set all_icons
(O
$ui_workdir) file_plain
1264 set max_status_desc
0
1268 {_M
"Modified, not staged"}
1269 {M_
"Staged for commit"}
1270 {MM
"Portions staged for commit"}
1271 {MD
"Staged for commit, missing"}
1273 {_O
"Untracked, not staged"}
1274 {A_
"Staged for commit"}
1275 {AM
"Portions staged for commit"}
1276 {AD
"Staged for commit, missing"}
1279 {D_
"Staged for removal"}
1280 {DO
"Staged for removal, still present"}
1282 {U_
"Requires merge resolution"}
1283 {UU
"Requires merge resolution"}
1284 {UM
"Requires merge resolution"}
1285 {UD
"Requires merge resolution"}
1287 if {$max_status_desc < [string length
[lindex
$i 1]]} {
1288 set max_status_desc
[string length
[lindex
$i 1]]
1290 set all_descs
([lindex
$i 0]) [lindex
$i 1]
1294 ######################################################################
1298 proc bind_button3
{w cmd
} {
1299 bind $w <Any-Button-3
> $cmd
1301 bind $w <Control-Button-1
> $cmd
1305 proc scrollbar2many
{list mode args
} {
1306 foreach w
$list {eval $w $mode $args}
1309 proc many2scrollbar
{list mode sb top bottom
} {
1310 $sb set $top $bottom
1311 foreach w
$list {$w $mode moveto
$top}
1314 proc incr_font_size
{font
{amt
1}} {
1315 set sz
[font configure
$font -size]
1317 font configure
$font -size $sz
1318 font configure
${font}bold
-size $sz
1319 font configure
${font}italic
-size $sz
1322 ######################################################################
1326 set starting_gitk_msg
{Starting gitk... please
wait...
}
1328 proc do_gitk
{revs
} {
1329 # -- Always start gitk through whatever we were loaded with. This
1330 # lets us bypass using shell process on Windows systems.
1332 set exe
[file join [file dirname $
::_git
] gitk
]
1333 set cmd
[list
[info nameofexecutable
] $exe]
1334 if {! [file exists
$exe]} {
1335 error_popup
"Unable to start gitk:\n\n$exe does not exist"
1337 eval exec $cmd $revs &
1338 ui_status $
::starting_gitk_msg
1340 ui_ready
$starting_gitk_msg
1348 global ui_comm is_quitting repo_config commit_type
1350 if {$is_quitting} return
1353 if {[winfo exists
$ui_comm]} {
1354 # -- Stash our current commit buffer.
1356 set save
[gitdir GITGUI_MSG
]
1357 set msg
[string trim
[$ui_comm get
0.0 end
]]
1358 regsub
-all -line {[ \r\t]+$
} $msg {} msg
1359 if {(![string match amend
* $commit_type]
1360 ||
[$ui_comm edit modified
])
1363 set fd
[open
$save w
]
1364 puts
-nonewline $fd $msg
1368 catch
{file delete
$save}
1371 # -- Stash our current window geometry into this repository.
1373 set cfg_geometry
[list
]
1374 lappend cfg_geometry
[wm geometry .
]
1375 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
1376 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
1377 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
1380 if {$cfg_geometry ne
$rc_geometry} {
1381 catch
{git config gui.geometry
$cfg_geometry}
1396 proc toggle_or_diff
{w x y
} {
1397 global file_states file_lists current_diff_path ui_index ui_workdir
1398 global last_clicked selected_paths
1400 set pos
[split [$w index @
$x,$y] .
]
1401 set lno
[lindex
$pos 0]
1402 set col [lindex
$pos 1]
1403 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1409 set last_clicked
[list
$w $lno]
1410 array
unset selected_paths
1411 $ui_index tag remove in_sel
0.0 end
1412 $ui_workdir tag remove in_sel
0.0 end
1415 if {$current_diff_path eq
$path} {
1416 set after
{reshow_diff
;}
1420 if {$w eq
$ui_index} {
1422 "Unstaging [short_path $path] from commit" \
1424 [concat
$after [list ui_ready
]]
1425 } elseif
{$w eq
$ui_workdir} {
1427 "Adding [short_path $path]" \
1429 [concat
$after [list ui_ready
]]
1432 show_diff
$path $w $lno
1436 proc add_one_to_selection
{w x y
} {
1437 global file_lists last_clicked selected_paths
1439 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1440 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1446 if {$last_clicked ne
{}
1447 && [lindex
$last_clicked 0] ne
$w} {
1448 array
unset selected_paths
1449 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
1452 set last_clicked
[list
$w $lno]
1453 if {[catch
{set in_sel
$selected_paths($path)}]} {
1457 unset selected_paths
($path)
1458 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
1460 set selected_paths
($path) 1
1461 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1465 proc add_range_to_selection
{w x y
} {
1466 global file_lists last_clicked selected_paths
1468 if {[lindex
$last_clicked 0] ne
$w} {
1469 toggle_or_diff
$w $x $y
1473 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1474 set lc
[lindex
$last_clicked 1]
1483 foreach path
[lrange
$file_lists($w) \
1484 [expr {$begin - 1}] \
1485 [expr {$end - 1}]] {
1486 set selected_paths
($path) 1
1488 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
1491 ######################################################################
1495 set cursor_ptr arrow
1496 font create font_diff
-family Courier
-size 10
1500 eval font configure font_ui
[font actual
[.dummy cget
-font]]
1504 font create font_uiitalic
1505 font create font_uibold
1506 font create font_diffbold
1507 font create font_diffitalic
1509 foreach class
{Button Checkbutton Entry Label
1510 Labelframe Listbox Menu Message
1511 Radiobutton Spinbox Text
} {
1512 option add
*$class.font font_ui
1516 if {[is_Windows
] ||
[is_MacOSX
]} {
1517 option add
*Menu.tearOff
0
1528 proc apply_config
{} {
1529 global repo_config font_descs
1531 foreach option
$font_descs {
1532 set name
[lindex
$option 0]
1533 set font
[lindex
$option 1]
1535 foreach
{cn cv
} $repo_config(gui.
$name) {
1536 font configure
$font $cn $cv
1539 error_popup
"Invalid font specified in gui.$name:\n\n$err"
1541 foreach
{cn cv
} [font configure
$font] {
1542 font configure
${font}bold
$cn $cv
1543 font configure
${font}italic
$cn $cv
1545 font configure
${font}bold
-weight bold
1546 font configure
${font}italic
-slant italic
1550 set default_config
(merge.diffstat
) true
1551 set default_config
(merge.summary
) false
1552 set default_config
(merge.verbosity
) 2
1553 set default_config
(user.name
) {}
1554 set default_config
(user.email
) {}
1556 set default_config
(gui.matchtrackingbranch
) false
1557 set default_config
(gui.pruneduringfetch
) false
1558 set default_config
(gui.trustmtime
) false
1559 set default_config
(gui.diffcontext
) 5
1560 set default_config
(gui.newbranchtemplate
) {}
1561 set default_config
(gui.fontui
) [font configure font_ui
]
1562 set default_config
(gui.fontdiff
) [font configure font_diff
]
1564 {fontui font_ui
{Main Font
}}
1565 {fontdiff font_diff
{Diff
/Console Font
}}
1570 ######################################################################
1572 ## feature option selection
1574 if {[regexp
{^git-
(.
+)$
} [appname
] _junk subcommand
]} {
1579 if {$subcommand eq
{gui.sh
}} {
1582 if {$subcommand eq
{gui
} && [llength
$argv] > 0} {
1583 set subcommand
[lindex
$argv 0]
1584 set argv
[lrange
$argv 1 end
]
1587 enable_option multicommit
1588 enable_option branch
1589 enable_option transport
1591 switch
-- $subcommand {
1594 disable_option multicommit
1595 disable_option branch
1596 disable_option transport
1599 enable_option singlecommit
1601 disable_option multicommit
1602 disable_option branch
1603 disable_option transport
1607 ######################################################################
1615 menu .mbar
-tearoff 0
1616 .mbar add cascade
-label Repository
-menu .mbar.repository
1617 .mbar add cascade
-label Edit
-menu .mbar.edit
1618 if {[is_enabled branch
]} {
1619 .mbar add cascade
-label Branch
-menu .mbar.branch
1621 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1622 .mbar add cascade
-label Commit
-menu .mbar.commit
1624 if {[is_enabled transport
]} {
1625 .mbar add cascade
-label Merge
-menu .mbar.merge
1626 .mbar add cascade
-label Fetch
-menu .mbar.fetch
1627 .mbar add cascade
-label Push
-menu .mbar.push
1629 . configure
-menu .mbar
1631 # -- Repository Menu
1633 menu .mbar.repository
1635 .mbar.repository add
command \
1636 -label {Browse Current Branch
} \
1637 -command {browser
::new
$current_branch}
1638 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1639 .mbar.repository add separator
1641 .mbar.repository add
command \
1642 -label {Visualize Current Branch
} \
1643 -command {do_gitk
$current_branch}
1644 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1645 .mbar.repository add
command \
1646 -label {Visualize All Branches
} \
1647 -command {do_gitk
--all}
1648 .mbar.repository add separator
1650 if {[is_enabled multicommit
]} {
1651 .mbar.repository add
command -label {Database Statistics
} \
1654 .mbar.repository add
command -label {Compress Database
} \
1657 .mbar.repository add
command -label {Verify Database
} \
1658 -command do_fsck_objects
1660 .mbar.repository add separator
1663 .mbar.repository add
command \
1664 -label {Create Desktop Icon
} \
1665 -command do_cygwin_shortcut
1666 } elseif
{[is_Windows
]} {
1667 .mbar.repository add
command \
1668 -label {Create Desktop Icon
} \
1669 -command do_windows_shortcut
1670 } elseif
{[is_MacOSX
]} {
1671 .mbar.repository add
command \
1672 -label {Create Desktop Icon
} \
1673 -command do_macosx_app
1677 .mbar.repository add
command -label Quit \
1684 .mbar.edit add
command -label Undo \
1685 -command {catch
{[focus
] edit undo
}} \
1687 .mbar.edit add
command -label Redo \
1688 -command {catch
{[focus
] edit redo
}} \
1690 .mbar.edit add separator
1691 .mbar.edit add
command -label Cut \
1692 -command {catch
{tk_textCut
[focus
]}} \
1694 .mbar.edit add
command -label Copy \
1695 -command {catch
{tk_textCopy
[focus
]}} \
1697 .mbar.edit add
command -label Paste \
1698 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
1700 .mbar.edit add
command -label Delete \
1701 -command {catch
{[focus
] delete sel.first sel.last
}} \
1703 .mbar.edit add separator
1704 .mbar.edit add
command -label {Select All
} \
1705 -command {catch
{[focus
] tag add sel
0.0 end
}} \
1710 if {[is_enabled branch
]} {
1713 .mbar.branch add
command -label {Create...
} \
1714 -command branch_create
::dialog \
1716 lappend disable_on_lock
[list .mbar.branch entryconf \
1717 [.mbar.branch index last
] -state]
1719 .mbar.branch add
command -label {Checkout...
} \
1720 -command branch_checkout
::dialog \
1722 lappend disable_on_lock
[list .mbar.branch entryconf \
1723 [.mbar.branch index last
] -state]
1725 .mbar.branch add
command -label {Rename...
} \
1726 -command branch_rename
::dialog
1727 lappend disable_on_lock
[list .mbar.branch entryconf \
1728 [.mbar.branch index last
] -state]
1730 .mbar.branch add
command -label {Delete...
} \
1731 -command branch_delete
::dialog
1732 lappend disable_on_lock
[list .mbar.branch entryconf \
1733 [.mbar.branch index last
] -state]
1735 .mbar.branch add
command -label {Reset...
} \
1736 -command merge
::reset_hard
1737 lappend disable_on_lock
[list .mbar.branch entryconf \
1738 [.mbar.branch index last
] -state]
1743 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1746 .mbar.commit add radiobutton \
1747 -label {New Commit
} \
1748 -command do_select_commit_type \
1749 -variable selected_commit_type \
1751 lappend disable_on_lock \
1752 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1754 .mbar.commit add radiobutton \
1755 -label {Amend Last Commit
} \
1756 -command do_select_commit_type \
1757 -variable selected_commit_type \
1759 lappend disable_on_lock \
1760 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1762 .mbar.commit add separator
1764 .mbar.commit add
command -label Rescan \
1765 -command do_rescan \
1767 lappend disable_on_lock \
1768 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1770 .mbar.commit add
command -label {Add To Commit
} \
1771 -command do_add_selection
1772 lappend disable_on_lock \
1773 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1775 .mbar.commit add
command -label {Add Existing To Commit
} \
1776 -command do_add_all \
1778 lappend disable_on_lock \
1779 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1781 .mbar.commit add
command -label {Unstage From Commit
} \
1782 -command do_unstage_selection
1783 lappend disable_on_lock \
1784 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1786 .mbar.commit add
command -label {Revert Changes
} \
1787 -command do_revert_selection
1788 lappend disable_on_lock \
1789 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1791 .mbar.commit add separator
1793 .mbar.commit add
command -label {Sign Off
} \
1794 -command do_signoff \
1797 .mbar.commit add
command -label Commit \
1798 -command do_commit \
1799 -accelerator $M1T-Return
1800 lappend disable_on_lock \
1801 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1806 if {[is_enabled branch
]} {
1808 .mbar.merge add
command -label {Local Merge...
} \
1809 -command merge
::dialog
1810 lappend disable_on_lock \
1811 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1812 .mbar.merge add
command -label {Abort Merge...
} \
1813 -command merge
::reset_hard
1814 lappend disable_on_lock \
1815 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1821 if {[is_enabled transport
]} {
1825 .mbar.push add
command -label {Push...
} \
1826 -command do_push_anywhere \
1828 .mbar.push add
command -label {Delete...
} \
1829 -command remote_branch_delete
::dialog
1833 # -- Apple Menu (Mac OS X only)
1835 .mbar add cascade
-label Apple
-menu .mbar.apple
1838 .mbar.apple add
command -label "About [appname]" \
1840 .mbar.apple add
command -label "Options..." \
1845 .mbar.edit add separator
1846 .mbar.edit add
command -label {Options...
} \
1851 if {[is_Cygwin
] && [file exists
/usr
/local
/miga
/lib
/gui-miga
]} {
1853 if {![lock_index update
]} return
1854 set cmd
[list sh
--login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1855 set miga_fd
[open
"|$cmd" r
]
1856 fconfigure
$miga_fd -blocking 0
1857 fileevent
$miga_fd readable
[list miga_done
$miga_fd]
1858 ui_status
{Running miga...
}
1860 proc miga_done
{fd
} {
1868 .mbar add cascade
-label Tools
-menu .mbar.tools
1870 .mbar.tools add
command -label "Migrate" \
1872 lappend disable_on_lock \
1873 [list .mbar.tools entryconf
[.mbar.tools index last
] -state]
1879 .mbar add cascade
-label Help
-menu .mbar.
help
1883 .mbar.
help add
command -label "About [appname]" \
1888 catch
{set browser
$repo_config(instaweb.browser
)}
1889 set doc_path
[file dirname [gitexec
]]
1890 set doc_path
[file join $doc_path Documentation index.html
]
1893 set doc_path
[exec cygpath
--mixed $doc_path]
1896 if {$browser eq
{}} {
1899 } elseif
{[is_Cygwin
]} {
1900 set program_files
[file dirname [exec cygpath
--windir]]
1901 set program_files
[file join $program_files {Program Files
}]
1902 set firefox
[file join $program_files {Mozilla Firefox
} firefox.exe
]
1903 set ie
[file join $program_files {Internet Explorer
} IEXPLORE.EXE
]
1904 if {[file exists
$firefox]} {
1905 set browser
$firefox
1906 } elseif
{[file exists
$ie]} {
1909 unset program_files firefox ie
1913 if {[file isfile
$doc_path]} {
1914 set doc_url
"file:$doc_path"
1916 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
1919 if {$browser ne
{}} {
1920 .mbar.
help add
command -label {Online Documentation
} \
1921 -command [list
exec $browser $doc_url &]
1923 unset browser doc_path doc_url
1925 # -- Standard bindings
1927 wm protocol . WM_DELETE_WINDOW do_quit
1928 bind all
<$M1B-Key-q> do_quit
1929 bind all
<$M1B-Key-Q> do_quit
1930 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
1931 bind all
<$M1B-Key-W> {destroy
[winfo toplevel
%W
]}
1933 set subcommand_args
{}
1935 puts stderr
"usage: $::argv0 $::subcommand $::subcommand_args"
1939 # -- Not a normal commit type invocation? Do that instead!
1941 switch
-- $subcommand {
1943 set subcommand_args
{rev?
}
1944 switch
[llength
$argv] {
1945 0 { load_current_branch
}
1947 set current_branch
[lindex
$argv 0]
1948 if {[regexp
{^
[0-9a-f]{1,39}$
} $current_branch]} {
1950 set current_branch \
1951 [git rev-parse
--verify $current_branch]
1960 browser
::new
$current_branch
1964 set subcommand_args
{rev? path?
}
1969 if {$is_path ||
[file exists
$_prefix$a]} {
1970 if {$path ne
{}} usage
1973 } elseif
{$a eq
{--}} {
1975 if {$head ne
{}} usage
1980 } elseif
{$head eq
{}} {
1981 if {$head ne
{}} usage
1992 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
1994 set head [git rev-parse
--verify $head]
2000 set current_branch
$head
2003 if {$path eq
{}} usage
2004 blame
::new
$head $path
2009 if {[llength
$argv] != 0} {
2010 puts
-nonewline stderr
"usage: $argv0"
2011 if {$subcommand ne
{gui
} && [appname
] ne
"git-$subcommand"} {
2012 puts
-nonewline stderr
" $subcommand"
2017 # fall through to setup UI for commits
2020 puts stderr
"usage: $argv0 \[{blame|browser|citool}\]"
2031 -text {Current Branch
:} \
2035 -textvariable current_branch \
2038 pack .branch.l1
-side left
2039 pack .branch.cb
-side left
-fill x
2040 pack .branch
-side top
-fill x
2042 # -- Main Window Layout
2044 panedwindow .vpane
-orient vertical
2045 panedwindow .vpane.files
-orient horizontal
2046 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
2047 pack .vpane
-anchor n
-side top
-fill both
-expand 1
2049 # -- Index File List
2051 frame .vpane.files.index
-height 100 -width 200
2052 label .vpane.files.index.title
-text {Staged Changes
(Will Be Committed
)} \
2053 -background lightgreen
2054 text
$ui_index -background white
-borderwidth 0 \
2055 -width 20 -height 10 \
2057 -cursor $cursor_ptr \
2058 -xscrollcommand {.vpane.files.index.sx
set} \
2059 -yscrollcommand {.vpane.files.index.sy
set} \
2061 scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
2062 scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
2063 pack .vpane.files.index.title
-side top
-fill x
2064 pack .vpane.files.index.sx
-side bottom
-fill x
2065 pack .vpane.files.index.sy
-side right
-fill y
2066 pack
$ui_index -side left
-fill both
-expand 1
2067 .vpane.files add .vpane.files.index
-sticky nsew
2069 # -- Working Directory File List
2071 frame .vpane.files.workdir
-height 100 -width 200
2072 label .vpane.files.workdir.title
-text {Unstaged Changes
(Will Not Be Committed
)} \
2073 -background lightsalmon
2074 text
$ui_workdir -background white
-borderwidth 0 \
2075 -width 20 -height 10 \
2077 -cursor $cursor_ptr \
2078 -xscrollcommand {.vpane.files.workdir.sx
set} \
2079 -yscrollcommand {.vpane.files.workdir.sy
set} \
2081 scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
2082 scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
2083 pack .vpane.files.workdir.title
-side top
-fill x
2084 pack .vpane.files.workdir.sx
-side bottom
-fill x
2085 pack .vpane.files.workdir.sy
-side right
-fill y
2086 pack
$ui_workdir -side left
-fill both
-expand 1
2087 .vpane.files add .vpane.files.workdir
-sticky nsew
2089 foreach i
[list
$ui_index $ui_workdir] {
2090 $i tag conf in_diff
-background lightgray
2091 $i tag conf in_sel
-background lightgray
2095 # -- Diff and Commit Area
2097 frame .vpane.lower
-height 300 -width 400
2098 frame .vpane.lower.commarea
2099 frame .vpane.lower.
diff -relief sunken
-borderwidth 1
2100 pack .vpane.lower.commarea
-side top
-fill x
2101 pack .vpane.lower.
diff -side bottom
-fill both
-expand 1
2102 .vpane add .vpane.lower
-sticky nsew
2104 # -- Commit Area Buttons
2106 frame .vpane.lower.commarea.buttons
2107 label .vpane.lower.commarea.buttons.l
-text {} \
2110 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
2111 pack .vpane.lower.commarea.buttons
-side left
-fill y
2113 button .vpane.lower.commarea.buttons.rescan
-text {Rescan
} \
2115 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
2116 lappend disable_on_lock \
2117 {.vpane.lower.commarea.buttons.rescan conf
-state}
2119 button .vpane.lower.commarea.buttons.incall
-text {Add Existing
} \
2121 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
2122 lappend disable_on_lock \
2123 {.vpane.lower.commarea.buttons.incall conf
-state}
2125 button .vpane.lower.commarea.buttons.signoff
-text {Sign Off
} \
2127 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
2129 button .vpane.lower.commarea.buttons.commit
-text {Commit
} \
2131 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
2132 lappend disable_on_lock \
2133 {.vpane.lower.commarea.buttons.commit conf
-state}
2135 button .vpane.lower.commarea.buttons.push
-text {Push
} \
2136 -command do_push_anywhere
2137 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
2139 # -- Commit Message Buffer
2141 frame .vpane.lower.commarea.buffer
2142 frame .vpane.lower.commarea.buffer.header
2143 set ui_comm .vpane.lower.commarea.buffer.t
2144 set ui_coml .vpane.lower.commarea.buffer.header.l
2145 radiobutton .vpane.lower.commarea.buffer.header.new \
2146 -text {New Commit
} \
2147 -command do_select_commit_type \
2148 -variable selected_commit_type \
2150 lappend disable_on_lock \
2151 [list .vpane.lower.commarea.buffer.header.new conf
-state]
2152 radiobutton .vpane.lower.commarea.buffer.header.amend \
2153 -text {Amend Last Commit
} \
2154 -command do_select_commit_type \
2155 -variable selected_commit_type \
2157 lappend disable_on_lock \
2158 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
2162 proc trace_commit_type
{varname args
} {
2163 global ui_coml commit_type
2164 switch
-glob -- $commit_type {
2165 initial
{set txt
{Initial Commit Message
:}}
2166 amend
{set txt
{Amended Commit Message
:}}
2167 amend-initial
{set txt
{Amended Initial Commit Message
:}}
2168 amend-merge
{set txt
{Amended Merge Commit Message
:}}
2169 merge
{set txt
{Merge Commit Message
:}}
2170 * {set txt
{Commit Message
:}}
2172 $ui_coml conf
-text $txt
2174 trace add variable commit_type
write trace_commit_type
2175 pack
$ui_coml -side left
-fill x
2176 pack .vpane.lower.commarea.buffer.header.amend
-side right
2177 pack .vpane.lower.commarea.buffer.header.new
-side right
2179 text
$ui_comm -background white
-borderwidth 1 \
2182 -autoseparators true \
2184 -width 75 -height 9 -wrap none \
2186 -yscrollcommand {.vpane.lower.commarea.buffer.sby
set}
2187 scrollbar .vpane.lower.commarea.buffer.sby \
2188 -command [list
$ui_comm yview
]
2189 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
2190 pack .vpane.lower.commarea.buffer.sby
-side right
-fill y
2191 pack
$ui_comm -side left
-fill y
2192 pack .vpane.lower.commarea.buffer
-side left
-fill y
2194 # -- Commit Message Buffer Context Menu
2196 set ctxm .vpane.lower.commarea.buffer.ctxm
2197 menu
$ctxm -tearoff 0
2200 -command {tk_textCut
$ui_comm}
2203 -command {tk_textCopy
$ui_comm}
2206 -command {tk_textPaste
$ui_comm}
2209 -command {$ui_comm delete sel.first sel.last
}
2212 -label {Select All
} \
2213 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
2217 $ui_comm tag add sel
0.0 end
2218 tk_textCopy
$ui_comm
2219 $ui_comm tag remove sel
0.0 end
2225 bind_button3
$ui_comm "tk_popup $ctxm %X %Y"
2229 proc trace_current_diff_path
{varname args
} {
2230 global current_diff_path diff_actions file_states
2231 if {$current_diff_path eq
{}} {
2237 set p
$current_diff_path
2238 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
2240 set p
[escape_path
$p]
2244 .vpane.lower.
diff.header.status configure
-text $s
2245 .vpane.lower.
diff.header.
file configure
-text $f
2246 .vpane.lower.
diff.header.path configure
-text $p
2247 foreach w
$diff_actions {
2251 trace add variable current_diff_path
write trace_current_diff_path
2253 frame .vpane.lower.
diff.header
-background gold
2254 label .vpane.lower.
diff.header.status \
2256 -width $max_status_desc \
2259 label .vpane.lower.
diff.header.
file \
2263 label .vpane.lower.
diff.header.path \
2267 pack .vpane.lower.
diff.header.status
-side left
2268 pack .vpane.lower.
diff.header.
file -side left
2269 pack .vpane.lower.
diff.header.path
-fill x
2270 set ctxm .vpane.lower.
diff.header.ctxm
2271 menu
$ctxm -tearoff 0
2279 -- $current_diff_path
2281 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2282 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
2286 frame .vpane.lower.
diff.body
2287 set ui_diff .vpane.lower.
diff.body.t
2288 text
$ui_diff -background white
-borderwidth 0 \
2289 -width 80 -height 15 -wrap none \
2291 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
2292 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
2294 scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
2295 -command [list
$ui_diff xview
]
2296 scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
2297 -command [list
$ui_diff yview
]
2298 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
2299 pack .vpane.lower.
diff.body.sby
-side right
-fill y
2300 pack
$ui_diff -side left
-fill both
-expand 1
2301 pack .vpane.lower.
diff.header
-side top
-fill x
2302 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
2304 $ui_diff tag conf d_cr
-elide true
2305 $ui_diff tag conf d_@
-foreground blue
-font font_diffbold
2306 $ui_diff tag conf d_
+ -foreground {#00a000}
2307 $ui_diff tag conf d_-
-foreground red
2309 $ui_diff tag conf d_
++ -foreground {#00a000}
2310 $ui_diff tag conf d_--
-foreground red
2311 $ui_diff tag conf d_
+s \
2312 -foreground {#00a000} \
2313 -background {#e2effa}
2314 $ui_diff tag conf d_-s \
2316 -background {#e2effa}
2317 $ui_diff tag conf d_s
+ \
2318 -foreground {#00a000} \
2320 $ui_diff tag conf d_s- \
2324 $ui_diff tag conf d
<<<<<<< \
2325 -foreground orange \
2327 $ui_diff tag conf d
======= \
2328 -foreground orange \
2330 $ui_diff tag conf d
>>>>>>> \
2331 -foreground orange \
2334 $ui_diff tag raise sel
2336 # -- Diff Body Context Menu
2338 set ctxm .vpane.lower.
diff.body.ctxm
2339 menu
$ctxm -tearoff 0
2342 -command reshow_diff
2343 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2346 -command {tk_textCopy
$ui_diff}
2347 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2349 -label {Select All
} \
2350 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
2351 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2355 $ui_diff tag add sel
0.0 end
2356 tk_textCopy
$ui_diff
2357 $ui_diff tag remove sel
0.0 end
2359 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2362 -label {Apply
/Reverse Hunk
} \
2363 -command {apply_hunk
$cursorX $cursorY}
2364 set ui_diff_applyhunk
[$ctxm index last
]
2365 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
2368 -label {Decrease Font Size
} \
2369 -command {incr_font_size font_diff
-1}
2370 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2372 -label {Increase Font Size
} \
2373 -command {incr_font_size font_diff
1}
2374 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2377 -label {Show Less Context
} \
2378 -command {if {$repo_config(gui.diffcontext
) >= 1} {
2379 incr repo_config
(gui.diffcontext
) -1
2382 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2384 -label {Show More Context
} \
2385 -command {if {$repo_config(gui.diffcontext
) < 99} {
2386 incr repo_config
(gui.diffcontext
)
2389 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2391 $ctxm add
command -label {Options...
} \
2393 bind_button3
$ui_diff "
2396 if {\$ui_index eq \$current_diff_side} {
2397 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2399 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2401 tk_popup $ctxm %X %Y
2403 unset ui_diff_applyhunk
2407 set main_status
[::status_bar
::new .status
]
2408 pack .status
-anchor w
-side bottom
-fill x
2409 $main_status show
{Initializing...
}
2414 set gm
$repo_config(gui.geometry
)
2415 wm geometry .
[lindex
$gm 0]
2416 .vpane sash place
0 \
2417 [lindex
[.vpane sash coord
0] 0] \
2419 .vpane.files sash place
0 \
2421 [lindex
[.vpane.files sash coord
0] 1]
2427 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
2428 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
2429 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
2430 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
2431 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
2432 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
2433 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
2434 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
2435 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
2436 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2437 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2439 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
2440 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
2441 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
2442 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
2443 bind $ui_diff <$M1B-Key-v> {break}
2444 bind $ui_diff <$M1B-Key-V> {break}
2445 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2446 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2447 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
2448 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
2449 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
2450 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
2451 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
2452 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
2453 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
2454 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
2455 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
2456 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
2457 bind $ui_diff <Button-1
> {focus
%W
}
2459 if {[is_enabled branch
]} {
2460 bind .
<$M1B-Key-n> branch_create
::dialog
2461 bind .
<$M1B-Key-N> branch_create
::dialog
2462 bind .
<$M1B-Key-o> branch_checkout
::dialog
2463 bind .
<$M1B-Key-O> branch_checkout
::dialog
2465 if {[is_enabled transport
]} {
2466 bind .
<$M1B-Key-p> do_push_anywhere
2467 bind .
<$M1B-Key-P> do_push_anywhere
2470 bind .
<Key-F5
> do_rescan
2471 bind .
<$M1B-Key-r> do_rescan
2472 bind .
<$M1B-Key-R> do_rescan
2473 bind .
<$M1B-Key-s> do_signoff
2474 bind .
<$M1B-Key-S> do_signoff
2475 bind .
<$M1B-Key-i> do_add_all
2476 bind .
<$M1B-Key-I> do_add_all
2477 bind .
<$M1B-Key-Return> do_commit
2478 foreach i
[list
$ui_index $ui_workdir] {
2479 bind $i <Button-1
> "toggle_or_diff $i %x %y; break"
2480 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2481 bind $i <Shift-Button-1
> "add_range_to_selection $i %x %y; break"
2485 set file_lists
($ui_index) [list
]
2486 set file_lists
($ui_workdir) [list
]
2488 wm title .
"[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2489 focus
-force $ui_comm
2491 # -- Warn the user about environmental problems. Cygwin's Tcl
2492 # does *not* pass its env array onto any processes it spawns.
2493 # This means that git processes get none of our environment.
2498 set msg
"Possible environment issues exist.
2500 The following environment variables are probably
2501 going to be ignored by any Git subprocess run
2505 foreach name
[array names env
] {
2506 switch
-regexp -- $name {
2507 {^GIT_INDEX_FILE$
} -
2508 {^GIT_OBJECT_DIRECTORY$
} -
2509 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$
} -
2511 {^GIT_EXTERNAL_DIFF$
} -
2515 {^GIT_CONFIG_LOCAL$
} -
2516 {^GIT_
(AUTHOR|COMMITTER
)_DATE$
} {
2517 append msg
" - $name\n"
2520 {^GIT_
(AUTHOR|COMMITTER
)_
(NAME|EMAIL
)$
} {
2521 append msg
" - $name\n"
2523 set suggest_user
$name
2527 if {$ignored_env > 0} {
2529 This is due to a known issue with the
2530 Tcl binary distributed by Cygwin."
2532 if {$suggest_user ne
{}} {
2535 A good replacement for $suggest_user
2536 is placing values for the user.name and
2537 user.email settings into your personal
2543 unset ignored_env msg suggest_user name
2546 # -- Only initialize complex UI if we are going to stay running.
2548 if {[is_enabled transport
]} {
2555 # -- Only suggest a gc run if we are going to stay running.
2557 if {[is_enabled multicommit
]} {
2558 set object_limit
2000
2559 if {[is_Windows
]} {set object_limit
200}
2560 regexp
{^
([0-9]+) objects
,} [git count-objects
] _junk objects_current
2561 if {$objects_current >= $object_limit} {
2563 "This repository currently has $objects_current loose objects.
2565 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2567 Compress the database now?"] eq
yes} {
2571 unset object_limit _junk objects_current
2574 lock_index begin-read