2 # Tcl ignores the next line -*- tcl -*- \
3 if test "z$*" = zversion \
4 ||
test "z$*" = z--version
; \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
11 set appvers
{@@GITGUI_VERSION@@
}
13 Copyright ©
2006, 2007 Shawn Pearce
, et. al.
15 This program is free software
; you can redistribute it and
/or modify
16 it under the terms of the GNU General Public License as published by
17 the Free Software Foundation
; either version
2 of the License
, or
18 (at your option
) any later version.
20 This program is distributed
in the hope that it will be useful
,
21 but WITHOUT ANY WARRANTY
; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License
for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program
; if not
, write to the Free Software
27 Foundation
, Inc.
, 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
}
29 ######################################################################
31 ## Tcl/Tk sanity check
33 if {[catch
{package require Tcl
8.4} err
]
34 ||
[catch
{package require Tk
8.4} err
]
40 -title "git-gui: fatal error" \
45 ######################################################################
47 ## enable verbose loading?
49 if {![catch
{set _verbose
$env(GITGUI_VERBOSE
)}]} {
51 rename auto_load real__auto_load
52 proc auto_load
{name args
} {
53 puts stderr
"auto_load $name"
54 return [uplevel
1 real__auto_load
$name $args]
56 rename
source real__source
58 puts stderr
"source $name"
59 uplevel
1 real__source
$name
63 ######################################################################
65 ## configure our library
67 set oguilib
{@@GITGUI_LIBDIR@@
}
68 set oguirel
{@@GITGUI_RELATIVE@@
}
69 if {$oguirel eq
{1}} {
70 set oguilib
[file dirname [file dirname [file normalize
$argv0]]]
71 set oguilib
[file join $oguilib share git-gui lib
]
72 } elseif
{[string match @@
* $oguirel]} {
73 set oguilib
[file join [file dirname [file normalize
$argv0]] lib
]
76 set idx
[file join $oguilib tclIndex
]
77 if {[catch
{set fd
[open
$idx r
]} err
]} {
82 -title "git-gui: fatal error" \
86 if {[gets
$fd] eq
{# Autogenerated by git-gui Makefile}} {
88 while {[gets
$fd n
] >= 0} {
89 if {$n ne
{} && ![string match
#* $n]} {
101 if {[lsearch
-exact $loaded $p] >= 0} continue
102 source [file join $oguilib $p]
107 set auto_path
[concat
[list
$oguilib] $auto_path]
109 unset -nocomplain oguirel idx fd
111 ######################################################################
115 set _appname
[lindex
[file split $argv0] end
]
132 return [eval [list
file join $_gitdir] $args]
135 proc gitexec
{args
} {
137 if {$_gitexec eq
{}} {
138 if {[catch
{set _gitexec
[git
--exec-path]} err
]} {
139 error
"Git not installed?\n\n$err"
142 set _gitexec
[exec cygpath \
147 set _gitexec
[file normalize
$_gitexec]
153 return [eval [list
file join $_gitexec] $args]
162 global tcl_platform tk_library
163 if {[tk windowingsystem
] eq
{aqua
}} {
171 if {$tcl_platform(platform
) eq
{windows
}} {
178 global tcl_platform _iscygwin
179 if {$_iscygwin eq
{}} {
180 if {$tcl_platform(platform
) eq
{windows
}} {
181 if {[catch
{set p
[exec cygpath
--windir]} err
]} {
193 proc is_enabled
{option
} {
194 global enabled_options
195 if {[catch
{set on
$enabled_options($option)}]} {return 0}
199 proc enable_option
{option
} {
200 global enabled_options
201 set enabled_options
($option) 1
204 proc disable_option
{option
} {
205 global enabled_options
206 set enabled_options
($option) 0
209 ######################################################################
213 proc is_many_config
{name
} {
214 switch
-glob -- $name {
223 proc is_config_true
{name
} {
225 if {[catch
{set v
$repo_config($name)}]} {
227 } elseif
{$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes}} {
234 proc get_config
{name
} {
236 if {[catch
{set v
$repo_config($name)}]} {
243 proc load_config
{include_global
} {
244 global repo_config global_config default_config
246 array
unset global_config
247 if {$include_global} {
249 set fd_rc
[git_read config
--global --list]
250 while {[gets
$fd_rc line
] >= 0} {
251 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
252 if {[is_many_config
$name]} {
253 lappend global_config
($name) $value
255 set global_config
($name) $value
263 array
unset repo_config
265 set fd_rc
[git_read config
--list]
266 while {[gets
$fd_rc line
] >= 0} {
267 if {[regexp
{^
([^
=]+)=(.
*)$
} $line line name value
]} {
268 if {[is_many_config
$name]} {
269 lappend repo_config
($name) $value
271 set repo_config
($name) $value
278 foreach name
[array names default_config
] {
279 if {[catch
{set v
$global_config($name)}]} {
280 set global_config
($name) $default_config($name)
282 if {[catch
{set v
$repo_config($name)}]} {
283 set repo_config
($name) $default_config($name)
288 ######################################################################
292 proc _git_cmd
{name
} {
295 if {[catch
{set v
$_git_cmd_path($name)}]} {
299 --exec-path { return [list $
::_git
$name] }
302 set p
[gitexec git-
$name$
::_search_exe
]
303 if {[file exists
$p]} {
305 } elseif
{[is_Windows
] && [file exists
[gitexec git-
$name]]} {
306 # Try to determine what sort of magic will make
307 # git-$name go and do its thing, because native
308 # Tcl on Windows doesn't know it.
310 set p
[gitexec git-
$name]
317 #!*perl { set i perl }
318 #!*python { set i python }
319 default
{ error
"git-$name is not supported: $s" }
323 if {![info exists interp
]} {
324 set interp
[_which
$i]
327 error
"git-$name requires $i (not in PATH)"
329 set v
[list
$interp $p]
331 # Assume it is builtin to git somehow and we
332 # aren't actually able to see a file for it.
334 set v
[list $
::_git
$name]
336 set _git_cmd_path
($name) $v
342 global env _search_exe _search_path
344 if {$_search_path eq
{}} {
346 set _search_path
[split [exec cygpath \
352 } elseif
{[is_Windows
]} {
353 set _search_path
[split $env(PATH
) {;}]
356 set _search_path
[split $env(PATH
) :]
361 foreach p
$_search_path {
362 set p
[file join $p $what$_search_exe]
363 if {[file exists
$p]} {
364 return [file normalize
$p]
374 switch
-- [lindex
$args 0] {
388 set args
[lrange
$args 1 end
]
391 set cmdp
[_git_cmd
[lindex
$args 0]]
392 set args
[lrange
$args 1 end
]
394 return [eval $opt $cmdp $args]
397 proc _open_stdout_stderr
{cmd
} {
401 if { [lindex
$cmd end
] eq
{2>@
1}
402 && $err eq
{can not
find channel named
"1"}
404 # Older versions of Tcl 8.4 don't have this 2>@1 IO
405 # redirect operator. Fallback to |& cat for those.
406 # The command was not actually started, so its safe
407 # to try to start it a second time.
409 set fd
[open
[concat \
410 [lrange
$cmd 0 end-1
] \
417 fconfigure
$fd -eofchar {}
421 proc git_read
{args
} {
425 switch
-- [lindex
$args 0] {
443 set args
[lrange
$args 1 end
]
446 set cmdp
[_git_cmd
[lindex
$args 0]]
447 set args
[lrange
$args 1 end
]
449 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
452 proc git_write
{args
} {
456 switch
-- [lindex
$args 0] {
470 set args
[lrange
$args 1 end
]
473 set cmdp
[_git_cmd
[lindex
$args 0]]
474 set args
[lrange
$args 1 end
]
476 return [open
[concat
$opt $cmdp $args] w
]
480 regsub
-all ' $value "'\\''" value
484 proc load_current_branch {} {
485 global current_branch is_detached
487 set fd [open [gitdir HEAD] r]
488 if {[gets $fd ref] < 1} {
493 set pfx {ref: refs/heads/}
494 set len [string length $pfx]
495 if {[string equal -length $len $pfx $ref]} {
496 # We're on a branch. It might not exist. But
497 # HEAD looks good enough to be a branch.
499 set current_branch [string range $ref $len end]
502 # Assume this is a detached head.
504 set current_branch HEAD
509 auto_load tk_optionMenu
510 rename tk_optionMenu real__tkOptionMenu
511 proc tk_optionMenu {w varName args} {
512 set m [eval real__tkOptionMenu $w $varName $args]
513 $m configure -font font_ui
514 $w configure -font font_ui
518 ######################################################################
522 set _git [_which git]
524 catch {wm withdraw .}
525 error_popup "Cannot
find git
in PATH.
"
528 set _nice [_which nice]
530 ######################################################################
534 if {[catch {set _git_version [git --version]} err]} {
535 catch {wm withdraw .}
536 error_popup "Cannot determine Git version
:
540 [appname
] requires Git
1.5.0 or later.
"
543 if {![regsub {^git version } $_git_version {} _git_version]} {
544 catch {wm withdraw .}
545 error_popup "Cannot parse Git version string
:\n\n$_git_version"
548 regsub -- {-dirty$} $_git_version {} _git_version
549 regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
550 regsub {\.rc[0-9]+$} $_git_version {} _git_version
552 proc git-version {args} {
555 switch [llength $args] {
561 set op [lindex $args 0]
562 set vr [lindex $args 1]
563 set cm [package vcompare $_git_version $vr]
564 return [expr $cm $op 0]
568 set type [lindex $args 0]
569 set name [lindex $args 1]
570 set parm [lindex $args 2]
571 set body [lindex $args 3]
573 if {($type ne {proc} && $type ne {method})} {
574 error "Invalid arguments to git-version
"
576 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
577 error "Last arm of
$type $name must be default
"
580 foreach {op vr cb} [lrange $body 0 end-2] {
581 if {[git-version $op $vr]} {
582 return [uplevel [list $type $name $parm $cb]]
586 return [uplevel [list $type $name $parm [lindex $body end]]]
590 error "git-version
>= x
"
596 if {[git-version < 1.5]} {
597 catch {wm withdraw .}
598 error_popup "[appname
] requires Git
1.5.0 or later.
600 You are using
[git-version
]:
606 ######################################################################
611 set _gitdir $env(GIT_DIR)
615 set _gitdir [git rev-parse --git-dir]
616 set _prefix [git rev-parse --show-prefix]
618 catch {wm withdraw .}
619 error_popup "Cannot
find the git directory
:\n\n$err"
622 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
623 catch {set _gitdir [exec cygpath --unix $_gitdir]}
625 if {![file isdirectory $_gitdir]} {
626 catch {wm withdraw .}
627 error_popup "Git directory not found
:\n\n$_gitdir"
630 if {[lindex [file split $_gitdir] end] ne {.git}} {
631 catch {wm withdraw .}
632 error_popup "Cannot use funny .git directory
:\n\n$_gitdir"
635 if {[catch {cd [file dirname $_gitdir]} err]} {
636 catch {wm withdraw .}
637 error_popup "No working directory
[file dirname $_gitdir]:\n\n$err"
640 set _reponame [lindex [file split \
641 [file normalize [file dirname $_gitdir]]] \
644 ######################################################################
648 set current_diff_path {}
649 set current_diff_side {}
650 set diff_actions [list]
654 set MERGE_HEAD [list]
657 set current_branch {}
659 set current_diff_path {}
660 set selected_commit_type new
662 ######################################################################
670 set disable_on_lock [list]
671 set index_lock_type none
673 proc lock_index {type} {
674 global index_lock_type disable_on_lock
676 if {$index_lock_type eq {none}} {
677 set index_lock_type $type
678 foreach w $disable_on_lock {
679 uplevel #0 $w disabled
682 } elseif {$index_lock_type eq "begin-
$type"} {
683 set index_lock_type $type
689 proc unlock_index {} {
690 global index_lock_type disable_on_lock
692 set index_lock_type none
693 foreach w $disable_on_lock {
698 ######################################################################
702 proc repository_state {ctvar hdvar mhvar} {
703 global current_branch
704 upvar $ctvar ct $hdvar hd $mhvar mh
709 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
715 set merge_head [gitdir MERGE_HEAD]
716 if {[file exists $merge_head]} {
718 set fd_mh [open $merge_head r]
719 while {[gets $fd_mh line] >= 0} {
730 global PARENT empty_tree
732 set p [lindex $PARENT 0]
736 if {$empty_tree eq {}} {
737 set empty_tree [git mktree << {}]
742 proc rescan {after {honor_trustmtime 1}} {
743 global HEAD PARENT MERGE_HEAD commit_type
744 global ui_index ui_workdir ui_comm
745 global rescan_active file_states
748 if {$rescan_active > 0 || ![lock_index read]} return
750 repository_state newType newHEAD newMERGE_HEAD
751 if {[string match amend* $commit_type]
752 && $newType eq {normal}
753 && $newHEAD eq $HEAD} {
757 set MERGE_HEAD $newMERGE_HEAD
758 set commit_type $newType
761 array unset file_states
763 if {![$ui_comm edit modified]
764 || [string trim [$ui_comm get 0.0 end]] eq {}} {
765 if {[string match amend* $commit_type]} {
766 } elseif {[load_message GITGUI_MSG]} {
767 } elseif {[load_message MERGE_MSG]} {
768 } elseif {[load_message SQUASH_MSG]} {
771 $ui_comm edit modified false
774 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
775 rescan_stage2 {} $after
778 ui_status {Refreshing file status...}
779 set fd_rf [git_read update-index \
785 fconfigure $fd_rf -blocking 0 -translation binary
786 fileevent $fd_rf readable \
787 [list rescan_stage2 $fd_rf $after]
791 proc rescan_stage2 {fd after} {
792 global rescan_active buf_rdi buf_rdf buf_rlo
796 if {![eof $fd]} return
800 set ls_others [list --exclude-per-directory=.gitignore]
801 set info_exclude [gitdir info exclude]
802 if {[file readable $info_exclude]} {
803 lappend ls_others "--exclude-from=$info_exclude"
811 ui_status {Scanning for modified files ...}
812 set fd_di [git_read diff-index --cached -z [PARENT]]
813 set fd_df [git_read diff-files -z]
814 set fd_lo [eval git_read ls-files --others -z $ls_others]
816 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
817 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
818 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
819 fileevent $fd_di readable [list read_diff_index $fd_di $after]
820 fileevent $fd_df readable [list read_diff_files $fd_df $after]
821 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
824 proc load_message {file} {
828 if {[file isfile $f]} {
829 if {[catch {set fd [open $f r]}]} {
832 fconfigure $fd -eofchar {}
833 set content [string trim [read $fd]]
835 regsub -all -line {[ \r\t]+$} $content {} content
836 $ui_comm delete 0.0 end
837 $ui_comm insert end $content
843 proc read_diff_index {fd after} {
846 append buf_rdi [read $fd]
848 set n [string length $buf_rdi]
850 set z1 [string first "\
0" $buf_rdi $c]
853 set z2 [string first "\
0" $buf_rdi $z1]
857 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
858 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
860 [encoding convertfrom $p] \
862 [list [lindex $i 0] [lindex $i 2]] \
868 set buf_rdi [string range $buf_rdi $c end]
873 rescan_done $fd buf_rdi $after
876 proc read_diff_files {fd after} {
879 append buf_rdf [read $fd]
881 set n [string length $buf_rdf]
883 set z1 [string first "\
0" $buf_rdf $c]
886 set z2 [string first "\
0" $buf_rdf $z1]
890 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
891 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
893 [encoding convertfrom $p] \
896 [list [lindex $i 0] [lindex $i 2]]
901 set buf_rdf [string range $buf_rdf $c end]
906 rescan_done $fd buf_rdf $after
909 proc read_ls_others {fd after} {
912 append buf_rlo [read $fd]
913 set pck [split $buf_rlo "\
0"]
914 set buf_rlo [lindex $pck end]
915 foreach p [lrange $pck 0 end-1] {
916 merge_state [encoding convertfrom $p] ?O
918 rescan_done $fd buf_rlo $after
921 proc rescan_done {fd buf after} {
922 global rescan_active current_diff_path
923 global file_states repo_config
926 if {![eof $fd]} return
929 if {[incr rescan_active -1] > 0} return
934 if {$current_diff_path ne {}} reshow_diff
938 proc prune_selection {} {
939 global file_states selected_paths
941 foreach path [array names selected_paths] {
942 if {[catch {set still_here $file_states($path)}]} {
943 unset selected_paths($path)
948 ######################################################################
952 proc mapicon {w state path} {
955 if {[catch {set r $all_icons($state$w)}]} {
956 puts "error
: no icon
for $w state
={$state} $path"
962 proc mapdesc {state path} {
965 if {[catch {set r $all_descs($state)}]} {
966 puts "error
: no desc
for state
={$state} $path"
972 proc ui_status {msg} {
973 $::main_status show $msg
976 proc ui_ready {{test {}}} {
977 $::main_status show {Ready.} $test
980 proc escape_path {path} {
981 regsub -all {\\} $path "\\\\" path
982 regsub -all "\n" $path "\\n
" path
986 proc short_path {path} {
987 return [escape_path [lindex [file split $path] end]]
991 set null_sha1 [string repeat 0 40]
993 proc merge_state {path new_state {head_info {}} {index_info {}}} {
994 global file_states next_icon_id null_sha1
996 set s0 [string index $new_state 0]
997 set s1 [string index $new_state 1]
999 if {[catch {set info $file_states($path)}]} {
1001 set icon n[incr next_icon_id]
1003 set state [lindex $info 0]
1004 set icon [lindex $info 1]
1005 if {$head_info eq {}} {set head_info [lindex $info 2]}
1006 if {$index_info eq {}} {set index_info [lindex $info 3]}
1009 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1010 elseif {$s0 eq {_}} {set s0 _}
1012 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1013 elseif {$s1 eq {_}} {set s1 _}
1015 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1016 set head_info [list 0 $null_sha1]
1017 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1018 && $head_info eq {}} {
1019 set head_info $index_info
1022 set file_states($path) [list $s0$s1 $icon \
1023 $head_info $index_info \
1028 proc display_file_helper {w path icon_name old_m new_m} {
1031 if {$new_m eq {_}} {
1032 set lno [lsearch -sorted -exact $file_lists($w) $path]
1034 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1036 $w conf -state normal
1037 $w delete $lno.0 [expr {$lno + 1}].0
1038 $w conf -state disabled
1040 } elseif {$old_m eq {_} && $new_m ne {_}} {
1041 lappend file_lists($w) $path
1042 set file_lists($w) [lsort -unique $file_lists($w)]
1043 set lno [lsearch -sorted -exact $file_lists($w) $path]
1045 $w conf -state normal
1046 $w image create $lno.0 \
1047 -align center -padx 5 -pady 1 \
1049 -image [mapicon $w $new_m $path]
1050 $w insert $lno.1 "[escape_path
$path]\n"
1051 $w conf -state disabled
1052 } elseif {$old_m ne $new_m} {
1053 $w conf -state normal
1054 $w image conf $icon_name -image [mapicon $w $new_m $path]
1055 $w conf -state disabled
1059 proc display_file {path state} {
1060 global file_states selected_paths
1061 global ui_index ui_workdir
1063 set old_m [merge_state $path $state]
1064 set s $file_states($path)
1065 set new_m [lindex $s 0]
1066 set icon_name [lindex $s 1]
1068 set o [string index $old_m 0]
1069 set n [string index $new_m 0]
1076 display_file_helper $ui_index $path $icon_name $o $n
1078 if {[string index $old_m 0] eq {U}} {
1081 set o [string index $old_m 1]
1083 if {[string index $new_m 0] eq {U}} {
1086 set n [string index $new_m 1]
1088 display_file_helper $ui_workdir $path $icon_name $o $n
1090 if {$new_m eq {__}} {
1091 unset file_states($path)
1092 catch {unset selected_paths($path)}
1096 proc display_all_files_helper {w path icon_name m} {
1099 lappend file_lists($w) $path
1100 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1101 $w image create end \
1102 -align center -padx 5 -pady 1 \
1104 -image [mapicon $w $m $path]
1105 $w insert end "[escape_path
$path]\n"
1108 proc display_all_files {} {
1109 global ui_index ui_workdir
1110 global file_states file_lists
1113 $ui_index conf -state normal
1114 $ui_workdir conf -state normal
1116 $ui_index delete 0.0 end
1117 $ui_workdir delete 0.0 end
1120 set file_lists($ui_index) [list]
1121 set file_lists($ui_workdir) [list]
1123 foreach path [lsort [array names file_states]] {
1124 set s $file_states($path)
1126 set icon_name [lindex $s 1]
1128 set s [string index $m 0]
1129 if {$s ne {U} && $s ne {_}} {
1130 display_all_files_helper $ui_index $path \
1134 if {[string index $m 0] eq {U}} {
1137 set s [string index $m 1]
1140 display_all_files_helper $ui_workdir $path \
1145 $ui_index conf -state disabled
1146 $ui_workdir conf -state disabled
1149 ######################################################################
1154 #define mask_width 14
1155 #define mask_height 15
1156 static unsigned char mask_bits[] = {
1157 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1158 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1159 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1162 image create bitmap file_plain -background white -foreground black -data {
1163 #define plain_width 14
1164 #define plain_height 15
1165 static unsigned char plain_bits[] = {
1166 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1167 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1168 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1169 } -maskdata $filemask
1171 image create bitmap file_mod -background white -foreground blue -data {
1172 #define mod_width 14
1173 #define mod_height 15
1174 static unsigned char mod_bits[] = {
1175 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1176 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1177 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1178 } -maskdata $filemask
1180 image create bitmap file_fulltick -background white -foreground "#007000" -data {
1181 #define file_fulltick_width 14
1182 #define file_fulltick_height 15
1183 static unsigned char file_fulltick_bits
[] = {
1184 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1185 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1186 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1187 } -maskdata $filemask
1189 image create bitmap file_parttick
-background white
-foreground "#005050" -data {
1190 #define parttick_width 14
1191 #define parttick_height 15
1192 static unsigned char parttick_bits
[] = {
1193 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1194 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1195 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1196 } -maskdata $filemask
1198 image create bitmap file_question
-background white
-foreground black
-data {
1199 #define file_question_width 14
1200 #define file_question_height 15
1201 static unsigned char file_question_bits
[] = {
1202 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1203 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1204 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1205 } -maskdata $filemask
1207 image create bitmap file_removed
-background white
-foreground red
-data {
1208 #define file_removed_width 14
1209 #define file_removed_height 15
1210 static unsigned char file_removed_bits
[] = {
1211 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1212 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1213 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1214 } -maskdata $filemask
1216 image create bitmap file_merge
-background white
-foreground blue
-data {
1217 #define file_merge_width 14
1218 #define file_merge_height 15
1219 static unsigned char file_merge_bits
[] = {
1220 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1221 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1222 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1223 } -maskdata $filemask
1226 #define file_width 18
1227 #define file_height 18
1228 static unsigned char file_bits
[] = {
1229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
1230 0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
1231 0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
1232 0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
1233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1235 image create bitmap file_dir
-background white
-foreground blue \
1236 -data $file_dir_data -maskdata $file_dir_data
1239 set file_uplevel_data
{
1241 #define up_height 15
1242 static unsigned char up_bits
[] = {
1243 0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
1244 0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
1245 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
1247 image create bitmap file_uplevel
-background white
-foreground red \
1248 -data $file_uplevel_data -maskdata $file_uplevel_data
1249 unset file_uplevel_data
1251 set ui_index .vpane.files.index.list
1252 set ui_workdir .vpane.files.workdir.list
1254 set all_icons
(_
$ui_index) file_plain
1255 set all_icons
(A
$ui_index) file_fulltick
1256 set all_icons
(M
$ui_index) file_fulltick
1257 set all_icons
(D
$ui_index) file_removed
1258 set all_icons
(U
$ui_index) file_merge
1260 set all_icons
(_
$ui_workdir) file_plain
1261 set all_icons
(M
$ui_workdir) file_mod
1262 set all_icons
(D
$ui_workdir) file_question
1263 set all_icons
(U
$ui_workdir) file_merge
1264 set all_icons
(O
$ui_workdir) file_plain
1266 set max_status_desc
0
1270 {_M
"Modified, not staged"}
1271 {M_
"Staged for commit"}
1272 {MM
"Portions staged for commit"}
1273 {MD
"Staged for commit, missing"}
1275 {_O
"Untracked, not staged"}
1276 {A_
"Staged for commit"}
1277 {AM
"Portions staged for commit"}
1278 {AD
"Staged for commit, missing"}
1281 {D_
"Staged for removal"}
1282 {DO
"Staged for removal, still present"}
1284 {U_
"Requires merge resolution"}
1285 {UU
"Requires merge resolution"}
1286 {UM
"Requires merge resolution"}
1287 {UD
"Requires merge resolution"}
1289 if {$max_status_desc < [string length
[lindex
$i 1]]} {
1290 set max_status_desc
[string length
[lindex
$i 1]]
1292 set all_descs
([lindex
$i 0]) [lindex
$i 1]
1296 ######################################################################
1300 proc bind_button3
{w cmd
} {
1301 bind $w <Any-Button-3
> $cmd
1303 bind $w <Control-Button-1
> $cmd
1307 proc scrollbar2many
{list mode args
} {
1308 foreach w
$list {eval $w $mode $args}
1311 proc many2scrollbar
{list mode sb top bottom
} {
1312 $sb set $top $bottom
1313 foreach w
$list {$w $mode moveto
$top}
1316 proc incr_font_size
{font
{amt
1}} {
1317 set sz
[font configure
$font -size]
1319 font configure
$font -size $sz
1320 font configure
${font}bold
-size $sz
1321 font configure
${font}italic
-size $sz
1324 ######################################################################
1328 set starting_gitk_msg
{Starting gitk... please
wait...
}
1330 proc do_gitk
{revs
} {
1331 # -- Always start gitk through whatever we were loaded with. This
1332 # lets us bypass using shell process on Windows systems.
1334 set exe
[file join [file dirname $
::_git
] gitk
]
1335 set cmd
[list
[info nameofexecutable
] $exe]
1336 if {! [file exists
$exe]} {
1337 error_popup
"Unable to start gitk:\n\n$exe does not exist"
1339 eval exec $cmd $revs &
1340 ui_status $
::starting_gitk_msg
1342 ui_ready
$starting_gitk_msg
1350 global ui_comm is_quitting repo_config commit_type
1352 if {$is_quitting} return
1355 if {[winfo exists
$ui_comm]} {
1356 # -- Stash our current commit buffer.
1358 set save
[gitdir GITGUI_MSG
]
1359 set msg
[string trim
[$ui_comm get
0.0 end
]]
1360 regsub
-all -line {[ \r\t]+$
} $msg {} msg
1361 if {(![string match amend
* $commit_type]
1362 ||
[$ui_comm edit modified
])
1365 set fd
[open
$save w
]
1366 puts
-nonewline $fd $msg
1370 catch
{file delete
$save}
1373 # -- Stash our current window geometry into this repository.
1375 set cfg_geometry
[list
]
1376 lappend cfg_geometry
[wm geometry .
]
1377 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 1]
1378 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 0]
1379 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
1382 if {$cfg_geometry ne
$rc_geometry} {
1383 catch
{git config gui.geometry
$cfg_geometry}
1398 proc toggle_or_diff
{w x y
} {
1399 global file_states file_lists current_diff_path ui_index ui_workdir
1400 global last_clicked selected_paths
1402 set pos
[split [$w index @
$x,$y] .
]
1403 set lno
[lindex
$pos 0]
1404 set col [lindex
$pos 1]
1405 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1411 set last_clicked
[list
$w $lno]
1412 array
unset selected_paths
1413 $ui_index tag remove in_sel
0.0 end
1414 $ui_workdir tag remove in_sel
0.0 end
1417 if {$current_diff_path eq
$path} {
1418 set after
{reshow_diff
;}
1422 if {$w eq
$ui_index} {
1424 "Unstaging [short_path $path] from commit" \
1426 [concat
$after [list ui_ready
]]
1427 } elseif
{$w eq
$ui_workdir} {
1429 "Adding [short_path $path]" \
1431 [concat
$after [list ui_ready
]]
1434 show_diff
$path $w $lno
1438 proc add_one_to_selection
{w x y
} {
1439 global file_lists last_clicked selected_paths
1441 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1442 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
1448 if {$last_clicked ne
{}
1449 && [lindex
$last_clicked 0] ne
$w} {
1450 array
unset selected_paths
1451 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
1454 set last_clicked
[list
$w $lno]
1455 if {[catch
{set in_sel
$selected_paths($path)}]} {
1459 unset selected_paths
($path)
1460 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
1462 set selected_paths
($path) 1
1463 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
1467 proc add_range_to_selection
{w x y
} {
1468 global file_lists last_clicked selected_paths
1470 if {[lindex
$last_clicked 0] ne
$w} {
1471 toggle_or_diff
$w $x $y
1475 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
1476 set lc
[lindex
$last_clicked 1]
1485 foreach path
[lrange
$file_lists($w) \
1486 [expr {$begin - 1}] \
1487 [expr {$end - 1}]] {
1488 set selected_paths
($path) 1
1490 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
1493 ######################################################################
1497 set cursor_ptr arrow
1498 font create font_diff
-family Courier
-size 10
1502 eval font configure font_ui
[font actual
[.dummy cget
-font]]
1506 font create font_uiitalic
1507 font create font_uibold
1508 font create font_diffbold
1509 font create font_diffitalic
1511 foreach class
{Button Checkbutton Entry Label
1512 Labelframe Listbox Menu Message
1513 Radiobutton Spinbox Text
} {
1514 option add
*$class.font font_ui
1518 if {[is_Windows
] ||
[is_MacOSX
]} {
1519 option add
*Menu.tearOff
0
1530 proc apply_config
{} {
1531 global repo_config font_descs
1533 foreach option
$font_descs {
1534 set name
[lindex
$option 0]
1535 set font
[lindex
$option 1]
1537 foreach
{cn cv
} $repo_config(gui.
$name) {
1538 font configure
$font $cn $cv
1541 error_popup
"Invalid font specified in gui.$name:\n\n$err"
1543 foreach
{cn cv
} [font configure
$font] {
1544 font configure
${font}bold
$cn $cv
1545 font configure
${font}italic
$cn $cv
1547 font configure
${font}bold
-weight bold
1548 font configure
${font}italic
-slant italic
1552 set default_config
(merge.diffstat
) true
1553 set default_config
(merge.summary
) false
1554 set default_config
(merge.verbosity
) 2
1555 set default_config
(user.name
) {}
1556 set default_config
(user.email
) {}
1558 set default_config
(gui.matchtrackingbranch
) false
1559 set default_config
(gui.pruneduringfetch
) false
1560 set default_config
(gui.trustmtime
) false
1561 set default_config
(gui.diffcontext
) 5
1562 set default_config
(gui.newbranchtemplate
) {}
1563 set default_config
(gui.fontui
) [font configure font_ui
]
1564 set default_config
(gui.fontdiff
) [font configure font_diff
]
1566 {fontui font_ui
{Main Font
}}
1567 {fontdiff font_diff
{Diff
/Console Font
}}
1572 ######################################################################
1574 ## feature option selection
1576 if {[regexp
{^git-
(.
+)$
} [appname
] _junk subcommand
]} {
1581 if {$subcommand eq
{gui.sh
}} {
1584 if {$subcommand eq
{gui
} && [llength
$argv] > 0} {
1585 set subcommand
[lindex
$argv 0]
1586 set argv
[lrange
$argv 1 end
]
1589 enable_option multicommit
1590 enable_option branch
1591 enable_option transport
1593 switch
-- $subcommand {
1596 disable_option multicommit
1597 disable_option branch
1598 disable_option transport
1601 enable_option singlecommit
1603 disable_option multicommit
1604 disable_option branch
1605 disable_option transport
1609 ######################################################################
1617 menu .mbar
-tearoff 0
1618 .mbar add cascade
-label Repository
-menu .mbar.repository
1619 .mbar add cascade
-label Edit
-menu .mbar.edit
1620 if {[is_enabled branch
]} {
1621 .mbar add cascade
-label Branch
-menu .mbar.branch
1623 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1624 .mbar add cascade
-label Commit
-menu .mbar.commit
1626 if {[is_enabled transport
]} {
1627 .mbar add cascade
-label Merge
-menu .mbar.merge
1628 .mbar add cascade
-label Fetch
-menu .mbar.fetch
1629 .mbar add cascade
-label Push
-menu .mbar.push
1631 . configure
-menu .mbar
1633 # -- Repository Menu
1635 menu .mbar.repository
1637 .mbar.repository add
command \
1638 -label {Browse Current Branch
} \
1639 -command {browser
::new
$current_branch}
1640 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1641 .mbar.repository add separator
1643 .mbar.repository add
command \
1644 -label {Visualize Current Branch
} \
1645 -command {do_gitk
$current_branch}
1646 trace add variable current_branch
write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1647 .mbar.repository add
command \
1648 -label {Visualize All Branches
} \
1649 -command {do_gitk
--all}
1650 .mbar.repository add separator
1652 if {[is_enabled multicommit
]} {
1653 .mbar.repository add
command -label {Database Statistics
} \
1656 .mbar.repository add
command -label {Compress Database
} \
1659 .mbar.repository add
command -label {Verify Database
} \
1660 -command do_fsck_objects
1662 .mbar.repository add separator
1665 .mbar.repository add
command \
1666 -label {Create Desktop Icon
} \
1667 -command do_cygwin_shortcut
1668 } elseif
{[is_Windows
]} {
1669 .mbar.repository add
command \
1670 -label {Create Desktop Icon
} \
1671 -command do_windows_shortcut
1672 } elseif
{[is_MacOSX
]} {
1673 .mbar.repository add
command \
1674 -label {Create Desktop Icon
} \
1675 -command do_macosx_app
1679 .mbar.repository add
command -label Quit \
1686 .mbar.edit add
command -label Undo \
1687 -command {catch
{[focus
] edit undo
}} \
1689 .mbar.edit add
command -label Redo \
1690 -command {catch
{[focus
] edit redo
}} \
1692 .mbar.edit add separator
1693 .mbar.edit add
command -label Cut \
1694 -command {catch
{tk_textCut
[focus
]}} \
1696 .mbar.edit add
command -label Copy \
1697 -command {catch
{tk_textCopy
[focus
]}} \
1699 .mbar.edit add
command -label Paste \
1700 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
1702 .mbar.edit add
command -label Delete \
1703 -command {catch
{[focus
] delete sel.first sel.last
}} \
1705 .mbar.edit add separator
1706 .mbar.edit add
command -label {Select All
} \
1707 -command {catch
{[focus
] tag add sel
0.0 end
}} \
1712 if {[is_enabled branch
]} {
1715 .mbar.branch add
command -label {Create...
} \
1716 -command branch_create
::dialog \
1718 lappend disable_on_lock
[list .mbar.branch entryconf \
1719 [.mbar.branch index last
] -state]
1721 .mbar.branch add
command -label {Checkout...
} \
1722 -command branch_checkout
::dialog \
1724 lappend disable_on_lock
[list .mbar.branch entryconf \
1725 [.mbar.branch index last
] -state]
1727 .mbar.branch add
command -label {Rename...
} \
1728 -command branch_rename
::dialog
1729 lappend disable_on_lock
[list .mbar.branch entryconf \
1730 [.mbar.branch index last
] -state]
1732 .mbar.branch add
command -label {Delete...
} \
1733 -command branch_delete
::dialog
1734 lappend disable_on_lock
[list .mbar.branch entryconf \
1735 [.mbar.branch index last
] -state]
1737 .mbar.branch add
command -label {Reset...
} \
1738 -command merge
::reset_hard
1739 lappend disable_on_lock
[list .mbar.branch entryconf \
1740 [.mbar.branch index last
] -state]
1745 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
1748 .mbar.commit add radiobutton \
1749 -label {New Commit
} \
1750 -command do_select_commit_type \
1751 -variable selected_commit_type \
1753 lappend disable_on_lock \
1754 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1756 .mbar.commit add radiobutton \
1757 -label {Amend Last Commit
} \
1758 -command do_select_commit_type \
1759 -variable selected_commit_type \
1761 lappend disable_on_lock \
1762 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1764 .mbar.commit add separator
1766 .mbar.commit add
command -label Rescan \
1767 -command do_rescan \
1769 lappend disable_on_lock \
1770 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1772 .mbar.commit add
command -label {Add To Commit
} \
1773 -command do_add_selection
1774 lappend disable_on_lock \
1775 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1777 .mbar.commit add
command -label {Add Existing To Commit
} \
1778 -command do_add_all \
1780 lappend disable_on_lock \
1781 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1783 .mbar.commit add
command -label {Unstage From Commit
} \
1784 -command do_unstage_selection
1785 lappend disable_on_lock \
1786 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1788 .mbar.commit add
command -label {Revert Changes
} \
1789 -command do_revert_selection
1790 lappend disable_on_lock \
1791 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1793 .mbar.commit add separator
1795 .mbar.commit add
command -label {Sign Off
} \
1796 -command do_signoff \
1799 .mbar.commit add
command -label Commit \
1800 -command do_commit \
1801 -accelerator $M1T-Return
1802 lappend disable_on_lock \
1803 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
1808 if {[is_enabled branch
]} {
1810 .mbar.merge add
command -label {Local Merge...
} \
1811 -command merge
::dialog
1812 lappend disable_on_lock \
1813 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1814 .mbar.merge add
command -label {Abort Merge...
} \
1815 -command merge
::reset_hard
1816 lappend disable_on_lock \
1817 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
1823 if {[is_enabled transport
]} {
1827 .mbar.push add
command -label {Push...
} \
1828 -command do_push_anywhere \
1830 .mbar.push add
command -label {Delete...
} \
1831 -command remote_branch_delete
::dialog
1835 # -- Apple Menu (Mac OS X only)
1837 .mbar add cascade
-label Apple
-menu .mbar.apple
1840 .mbar.apple add
command -label "About [appname]" \
1842 .mbar.apple add
command -label "Options..." \
1847 .mbar.edit add separator
1848 .mbar.edit add
command -label {Options...
} \
1853 if {[is_Cygwin
] && [file exists
/usr
/local
/miga
/lib
/gui-miga
]} {
1855 if {![lock_index update
]} return
1856 set cmd
[list sh
--login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1857 set miga_fd
[open
"|$cmd" r
]
1858 fconfigure
$miga_fd -blocking 0
1859 fileevent
$miga_fd readable
[list miga_done
$miga_fd]
1860 ui_status
{Running miga...
}
1862 proc miga_done
{fd
} {
1870 .mbar add cascade
-label Tools
-menu .mbar.tools
1872 .mbar.tools add
command -label "Migrate" \
1874 lappend disable_on_lock \
1875 [list .mbar.tools entryconf
[.mbar.tools index last
] -state]
1881 .mbar add cascade
-label Help
-menu .mbar.
help
1885 .mbar.
help add
command -label "About [appname]" \
1890 catch
{set browser
$repo_config(instaweb.browser
)}
1891 set doc_path
[file dirname [gitexec
]]
1892 set doc_path
[file join $doc_path Documentation index.html
]
1895 set doc_path
[exec cygpath
--mixed $doc_path]
1898 if {$browser eq
{}} {
1901 } elseif
{[is_Cygwin
]} {
1902 set program_files
[file dirname [exec cygpath
--windir]]
1903 set program_files
[file join $program_files {Program Files
}]
1904 set firefox
[file join $program_files {Mozilla Firefox
} firefox.exe
]
1905 set ie
[file join $program_files {Internet Explorer
} IEXPLORE.EXE
]
1906 if {[file exists
$firefox]} {
1907 set browser
$firefox
1908 } elseif
{[file exists
$ie]} {
1911 unset program_files firefox ie
1915 if {[file isfile
$doc_path]} {
1916 set doc_url
"file:$doc_path"
1918 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
1921 if {$browser ne
{}} {
1922 .mbar.
help add
command -label {Online Documentation
} \
1923 -command [list
exec $browser $doc_url &]
1925 unset browser doc_path doc_url
1927 # -- Standard bindings
1929 wm protocol . WM_DELETE_WINDOW do_quit
1930 bind all
<$M1B-Key-q> do_quit
1931 bind all
<$M1B-Key-Q> do_quit
1932 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
1933 bind all
<$M1B-Key-W> {destroy
[winfo toplevel
%W
]}
1935 set subcommand_args
{}
1937 puts stderr
"usage: $::argv0 $::subcommand $::subcommand_args"
1941 # -- Not a normal commit type invocation? Do that instead!
1943 switch
-- $subcommand {
1945 set subcommand_args
{rev?
}
1946 switch
[llength
$argv] {
1947 0 { load_current_branch
}
1949 set current_branch
[lindex
$argv 0]
1950 if {[regexp
{^
[0-9a-f]{1,39}$
} $current_branch]} {
1952 set current_branch \
1953 [git rev-parse
--verify $current_branch]
1962 browser
::new
$current_branch
1966 set subcommand_args
{rev? path?
}
1971 if {$is_path ||
[file exists
$_prefix$a]} {
1972 if {$path ne
{}} usage
1975 } elseif
{$a eq
{--}} {
1977 if {$head ne
{}} usage
1982 } elseif
{$head eq
{}} {
1983 if {$head ne
{}} usage
1994 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
1996 set head [git rev-parse
--verify $head]
2002 set current_branch
$head
2005 if {$path eq
{}} usage
2006 blame
::new
$head $path
2011 if {[llength
$argv] != 0} {
2012 puts
-nonewline stderr
"usage: $argv0"
2013 if {$subcommand ne
{gui
} && [appname
] ne
"git-$subcommand"} {
2014 puts
-nonewline stderr
" $subcommand"
2019 # fall through to setup UI for commits
2022 puts stderr
"usage: $argv0 \[{blame|browser|citool}\]"
2033 -text {Current Branch
:} \
2037 -textvariable current_branch \
2040 pack .branch.l1
-side left
2041 pack .branch.cb
-side left
-fill x
2042 pack .branch
-side top
-fill x
2044 # -- Main Window Layout
2046 panedwindow .vpane
-orient vertical
2047 panedwindow .vpane.files
-orient horizontal
2048 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
2049 pack .vpane
-anchor n
-side top
-fill both
-expand 1
2051 # -- Index File List
2053 frame .vpane.files.index
-height 100 -width 200
2054 label .vpane.files.index.title
-text {Staged Changes
(Will Be Committed
)} \
2055 -background lightgreen
2056 text
$ui_index -background white
-borderwidth 0 \
2057 -width 20 -height 10 \
2059 -cursor $cursor_ptr \
2060 -xscrollcommand {.vpane.files.index.sx
set} \
2061 -yscrollcommand {.vpane.files.index.sy
set} \
2063 scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
2064 scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
2065 pack .vpane.files.index.title
-side top
-fill x
2066 pack .vpane.files.index.sx
-side bottom
-fill x
2067 pack .vpane.files.index.sy
-side right
-fill y
2068 pack
$ui_index -side left
-fill both
-expand 1
2069 .vpane.files add .vpane.files.index
-sticky nsew
2071 # -- Working Directory File List
2073 frame .vpane.files.workdir
-height 100 -width 200
2074 label .vpane.files.workdir.title
-text {Unstaged Changes
(Will Not Be Committed
)} \
2075 -background lightsalmon
2076 text
$ui_workdir -background white
-borderwidth 0 \
2077 -width 20 -height 10 \
2079 -cursor $cursor_ptr \
2080 -xscrollcommand {.vpane.files.workdir.sx
set} \
2081 -yscrollcommand {.vpane.files.workdir.sy
set} \
2083 scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
2084 scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
2085 pack .vpane.files.workdir.title
-side top
-fill x
2086 pack .vpane.files.workdir.sx
-side bottom
-fill x
2087 pack .vpane.files.workdir.sy
-side right
-fill y
2088 pack
$ui_workdir -side left
-fill both
-expand 1
2089 .vpane.files add .vpane.files.workdir
-sticky nsew
2091 foreach i
[list
$ui_index $ui_workdir] {
2092 $i tag conf in_diff
-background lightgray
2093 $i tag conf in_sel
-background lightgray
2097 # -- Diff and Commit Area
2099 frame .vpane.lower
-height 300 -width 400
2100 frame .vpane.lower.commarea
2101 frame .vpane.lower.
diff -relief sunken
-borderwidth 1
2102 pack .vpane.lower.commarea
-side top
-fill x
2103 pack .vpane.lower.
diff -side bottom
-fill both
-expand 1
2104 .vpane add .vpane.lower
-sticky nsew
2106 # -- Commit Area Buttons
2108 frame .vpane.lower.commarea.buttons
2109 label .vpane.lower.commarea.buttons.l
-text {} \
2112 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
2113 pack .vpane.lower.commarea.buttons
-side left
-fill y
2115 button .vpane.lower.commarea.buttons.rescan
-text {Rescan
} \
2117 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
2118 lappend disable_on_lock \
2119 {.vpane.lower.commarea.buttons.rescan conf
-state}
2121 button .vpane.lower.commarea.buttons.incall
-text {Add Existing
} \
2123 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
2124 lappend disable_on_lock \
2125 {.vpane.lower.commarea.buttons.incall conf
-state}
2127 button .vpane.lower.commarea.buttons.signoff
-text {Sign Off
} \
2129 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
2131 button .vpane.lower.commarea.buttons.commit
-text {Commit
} \
2133 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
2134 lappend disable_on_lock \
2135 {.vpane.lower.commarea.buttons.commit conf
-state}
2137 button .vpane.lower.commarea.buttons.push
-text {Push
} \
2138 -command do_push_anywhere
2139 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
2141 # -- Commit Message Buffer
2143 frame .vpane.lower.commarea.buffer
2144 frame .vpane.lower.commarea.buffer.header
2145 set ui_comm .vpane.lower.commarea.buffer.t
2146 set ui_coml .vpane.lower.commarea.buffer.header.l
2147 radiobutton .vpane.lower.commarea.buffer.header.new \
2148 -text {New Commit
} \
2149 -command do_select_commit_type \
2150 -variable selected_commit_type \
2152 lappend disable_on_lock \
2153 [list .vpane.lower.commarea.buffer.header.new conf
-state]
2154 radiobutton .vpane.lower.commarea.buffer.header.amend \
2155 -text {Amend Last Commit
} \
2156 -command do_select_commit_type \
2157 -variable selected_commit_type \
2159 lappend disable_on_lock \
2160 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
2164 proc trace_commit_type
{varname args
} {
2165 global ui_coml commit_type
2166 switch
-glob -- $commit_type {
2167 initial
{set txt
{Initial Commit Message
:}}
2168 amend
{set txt
{Amended Commit Message
:}}
2169 amend-initial
{set txt
{Amended Initial Commit Message
:}}
2170 amend-merge
{set txt
{Amended Merge Commit Message
:}}
2171 merge
{set txt
{Merge Commit Message
:}}
2172 * {set txt
{Commit Message
:}}
2174 $ui_coml conf
-text $txt
2176 trace add variable commit_type
write trace_commit_type
2177 pack
$ui_coml -side left
-fill x
2178 pack .vpane.lower.commarea.buffer.header.amend
-side right
2179 pack .vpane.lower.commarea.buffer.header.new
-side right
2181 text
$ui_comm -background white
-borderwidth 1 \
2184 -autoseparators true \
2186 -width 75 -height 9 -wrap none \
2188 -yscrollcommand {.vpane.lower.commarea.buffer.sby
set}
2189 scrollbar .vpane.lower.commarea.buffer.sby \
2190 -command [list
$ui_comm yview
]
2191 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
2192 pack .vpane.lower.commarea.buffer.sby
-side right
-fill y
2193 pack
$ui_comm -side left
-fill y
2194 pack .vpane.lower.commarea.buffer
-side left
-fill y
2196 # -- Commit Message Buffer Context Menu
2198 set ctxm .vpane.lower.commarea.buffer.ctxm
2199 menu
$ctxm -tearoff 0
2202 -command {tk_textCut
$ui_comm}
2205 -command {tk_textCopy
$ui_comm}
2208 -command {tk_textPaste
$ui_comm}
2211 -command {$ui_comm delete sel.first sel.last
}
2214 -label {Select All
} \
2215 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
2219 $ui_comm tag add sel
0.0 end
2220 tk_textCopy
$ui_comm
2221 $ui_comm tag remove sel
0.0 end
2227 bind_button3
$ui_comm "tk_popup $ctxm %X %Y"
2231 proc trace_current_diff_path
{varname args
} {
2232 global current_diff_path diff_actions file_states
2233 if {$current_diff_path eq
{}} {
2239 set p
$current_diff_path
2240 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
2242 set p
[escape_path
$p]
2246 .vpane.lower.
diff.header.status configure
-text $s
2247 .vpane.lower.
diff.header.
file configure
-text $f
2248 .vpane.lower.
diff.header.path configure
-text $p
2249 foreach w
$diff_actions {
2253 trace add variable current_diff_path
write trace_current_diff_path
2255 frame .vpane.lower.
diff.header
-background gold
2256 label .vpane.lower.
diff.header.status \
2258 -width $max_status_desc \
2261 label .vpane.lower.
diff.header.
file \
2265 label .vpane.lower.
diff.header.path \
2269 pack .vpane.lower.
diff.header.status
-side left
2270 pack .vpane.lower.
diff.header.
file -side left
2271 pack .vpane.lower.
diff.header.path
-fill x
2272 set ctxm .vpane.lower.
diff.header.ctxm
2273 menu
$ctxm -tearoff 0
2281 -- $current_diff_path
2283 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2284 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
2288 frame .vpane.lower.
diff.body
2289 set ui_diff .vpane.lower.
diff.body.t
2290 text
$ui_diff -background white
-borderwidth 0 \
2291 -width 80 -height 15 -wrap none \
2293 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
2294 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
2296 scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
2297 -command [list
$ui_diff xview
]
2298 scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
2299 -command [list
$ui_diff yview
]
2300 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
2301 pack .vpane.lower.
diff.body.sby
-side right
-fill y
2302 pack
$ui_diff -side left
-fill both
-expand 1
2303 pack .vpane.lower.
diff.header
-side top
-fill x
2304 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
2306 $ui_diff tag conf d_cr
-elide true
2307 $ui_diff tag conf d_@
-foreground blue
-font font_diffbold
2308 $ui_diff tag conf d_
+ -foreground {#00a000}
2309 $ui_diff tag conf d_-
-foreground red
2311 $ui_diff tag conf d_
++ -foreground {#00a000}
2312 $ui_diff tag conf d_--
-foreground red
2313 $ui_diff tag conf d_
+s \
2314 -foreground {#00a000} \
2315 -background {#e2effa}
2316 $ui_diff tag conf d_-s \
2318 -background {#e2effa}
2319 $ui_diff tag conf d_s
+ \
2320 -foreground {#00a000} \
2322 $ui_diff tag conf d_s- \
2326 $ui_diff tag conf d
<<<<<<< \
2327 -foreground orange \
2329 $ui_diff tag conf d
======= \
2330 -foreground orange \
2332 $ui_diff tag conf d
>>>>>>> \
2333 -foreground orange \
2336 $ui_diff tag raise sel
2338 # -- Diff Body Context Menu
2340 set ctxm .vpane.lower.
diff.body.ctxm
2341 menu
$ctxm -tearoff 0
2344 -command reshow_diff
2345 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2348 -command {tk_textCopy
$ui_diff}
2349 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2351 -label {Select All
} \
2352 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
2353 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2357 $ui_diff tag add sel
0.0 end
2358 tk_textCopy
$ui_diff
2359 $ui_diff tag remove sel
0.0 end
2361 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2364 -label {Apply
/Reverse Hunk
} \
2365 -command {apply_hunk
$cursorX $cursorY}
2366 set ui_diff_applyhunk
[$ctxm index last
]
2367 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
2370 -label {Decrease Font Size
} \
2371 -command {incr_font_size font_diff
-1}
2372 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2374 -label {Increase Font Size
} \
2375 -command {incr_font_size font_diff
1}
2376 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2379 -label {Show Less Context
} \
2380 -command {if {$repo_config(gui.diffcontext
) >= 1} {
2381 incr repo_config
(gui.diffcontext
) -1
2384 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2386 -label {Show More Context
} \
2387 -command {if {$repo_config(gui.diffcontext
) < 99} {
2388 incr repo_config
(gui.diffcontext
)
2391 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
2393 $ctxm add
command -label {Options...
} \
2395 bind_button3
$ui_diff "
2398 if {\$ui_index eq \$current_diff_side} {
2399 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2401 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2403 tk_popup $ctxm %X %Y
2405 unset ui_diff_applyhunk
2409 set main_status
[::status_bar
::new .status
]
2410 pack .status
-anchor w
-side bottom
-fill x
2411 $main_status show
{Initializing...
}
2416 set gm
$repo_config(gui.geometry
)
2417 wm geometry .
[lindex
$gm 0]
2418 .vpane sash place
0 \
2419 [lindex
[.vpane sash coord
0] 0] \
2421 .vpane.files sash place
0 \
2423 [lindex
[.vpane.files sash coord
0] 1]
2429 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
2430 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
2431 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
2432 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
2433 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
2434 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
2435 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
2436 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
2437 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
2438 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2439 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2441 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
2442 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
2443 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
2444 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
2445 bind $ui_diff <$M1B-Key-v> {break}
2446 bind $ui_diff <$M1B-Key-V> {break}
2447 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
2448 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
2449 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
2450 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
2451 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
2452 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
2453 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
2454 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
2455 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
2456 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
2457 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
2458 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
2459 bind $ui_diff <Button-1
> {focus
%W
}
2461 if {[is_enabled branch
]} {
2462 bind .
<$M1B-Key-n> branch_create
::dialog
2463 bind .
<$M1B-Key-N> branch_create
::dialog
2464 bind .
<$M1B-Key-o> branch_checkout
::dialog
2465 bind .
<$M1B-Key-O> branch_checkout
::dialog
2467 if {[is_enabled transport
]} {
2468 bind .
<$M1B-Key-p> do_push_anywhere
2469 bind .
<$M1B-Key-P> do_push_anywhere
2472 bind .
<Key-F5
> do_rescan
2473 bind .
<$M1B-Key-r> do_rescan
2474 bind .
<$M1B-Key-R> do_rescan
2475 bind .
<$M1B-Key-s> do_signoff
2476 bind .
<$M1B-Key-S> do_signoff
2477 bind .
<$M1B-Key-i> do_add_all
2478 bind .
<$M1B-Key-I> do_add_all
2479 bind .
<$M1B-Key-Return> do_commit
2480 foreach i
[list
$ui_index $ui_workdir] {
2481 bind $i <Button-1
> "toggle_or_diff $i %x %y; break"
2482 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2483 bind $i <Shift-Button-1
> "add_range_to_selection $i %x %y; break"
2487 set file_lists
($ui_index) [list
]
2488 set file_lists
($ui_workdir) [list
]
2490 wm title .
"[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2491 focus
-force $ui_comm
2493 # -- Warn the user about environmental problems. Cygwin's Tcl
2494 # does *not* pass its env array onto any processes it spawns.
2495 # This means that git processes get none of our environment.
2500 set msg
"Possible environment issues exist.
2502 The following environment variables are probably
2503 going to be ignored by any Git subprocess run
2507 foreach name
[array names env
] {
2508 switch
-regexp -- $name {
2509 {^GIT_INDEX_FILE$
} -
2510 {^GIT_OBJECT_DIRECTORY$
} -
2511 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$
} -
2513 {^GIT_EXTERNAL_DIFF$
} -
2517 {^GIT_CONFIG_LOCAL$
} -
2518 {^GIT_
(AUTHOR|COMMITTER
)_DATE$
} {
2519 append msg
" - $name\n"
2522 {^GIT_
(AUTHOR|COMMITTER
)_
(NAME|EMAIL
)$
} {
2523 append msg
" - $name\n"
2525 set suggest_user
$name
2529 if {$ignored_env > 0} {
2531 This is due to a known issue with the
2532 Tcl binary distributed by Cygwin."
2534 if {$suggest_user ne
{}} {
2537 A good replacement for $suggest_user
2538 is placing values for the user.name and
2539 user.email settings into your personal
2545 unset ignored_env msg suggest_user name
2548 # -- Only initialize complex UI if we are going to stay running.
2550 if {[is_enabled transport
]} {
2557 # -- Only suggest a gc run if we are going to stay running.
2559 if {[is_enabled multicommit
]} {
2560 set object_limit
2000
2561 if {[is_Windows
]} {set object_limit
200}
2562 regexp
{^
([0-9]+) objects
,} [git count-objects
] _junk objects_current
2563 if {$objects_current >= $object_limit} {
2565 "This repository currently has $objects_current loose objects.
2567 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2569 Compress the database now?"] eq
yes} {
2573 unset object_limit _junk objects_current
2576 lock_index begin-read