2 # Tcl ignores the next line -*- tcl -*- \
3 if test "z$*" = zversion \
4 ||
test "z$*" = z--version
; \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
10 exec wish
"$argv0" -- "$@"
12 set appvers
{@@GITGUI_VERSION@@
}
13 set copyright
[string map
[list
(c
) \u00a9] {
14 Copyright
(c
) 2006-2010 Shawn Pearce
, et. al.
16 This program is free software
; you can redistribute it and
/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation
; either version
2 of the License
, or
19 (at your option
) any later version.
21 This program is distributed
in the hope that it will be useful
,
22 but WITHOUT ANY WARRANTY
; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License
for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program
; if not
, write to the Free Software
28 Foundation
, Inc.
, 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
}]
30 ######################################################################
32 ## Tcl/Tk sanity check
34 if {[catch
{package require Tcl
8.4} err
]
35 ||
[catch
{package require Tk
8.4} err
]
41 -title "git-gui: fatal error" \
46 catch
{rename send
{}} ; # What an evil concept...
48 ######################################################################
52 set oguilib
{@@GITGUI_LIBDIR@@
}
53 set oguirel
{@@GITGUI_RELATIVE@@
}
54 if {$oguirel eq
{1}} {
55 set oguilib
[file dirname [file normalize
$argv0]]
56 if {[file tail $oguilib] eq
{git-core
}} {
57 set oguilib
[file dirname $oguilib]
59 set oguilib
[file dirname $oguilib]
60 set oguilib
[file join $oguilib share git-gui lib
]
61 set oguimsg
[file join $oguilib msgs
]
62 } elseif
{[string match @@
* $oguirel]} {
63 set oguilib
[file join [file dirname [file normalize
$argv0]] lib
]
64 set oguimsg
[file join [file dirname [file normalize
$argv0]] po
]
66 set oguimsg
[file join $oguilib msgs
]
70 ######################################################################
72 ## enable verbose loading?
74 if {![catch
{set _verbose
$env(GITGUI_VERBOSE
)}]} {
76 rename auto_load real__auto_load
77 proc auto_load
{name args
} {
78 puts stderr
"auto_load $name"
79 return [uplevel
1 real__auto_load
$name $args]
81 rename
source real__source
83 puts stderr
"source $name"
84 uplevel
1 real__source
$name
86 if {[tk windowingsystem
] eq
"win32"} { console show
}
89 ######################################################################
91 ## Internationalization (i18n) through msgcat and gettext. See
92 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
94 package require msgcat
97 set cmk
[string first @@
$fmt]
99 return [string range
$fmt 0 [expr {$cmk - 1}]]
104 proc mc
{en_fmt args
} {
105 set fmt [_mc_trim
[::msgcat
::mc
$en_fmt]]
106 if {[catch
{set msg
[eval [list format
$fmt] $args]} err
]} {
107 set msg
[eval [list format
[_mc_trim
$en_fmt]] $args]
113 return [join $args {}]
116 ::msgcat
::mcload
$oguimsg
119 ######################################################################
123 set _appname
{Git Gui
}
132 set _shellpath
{@@SHELL_PATH@@
}
134 set _trace
[lsearch
-exact $argv --trace]
136 set argv
[lreplace
$argv $_trace $_trace]
142 # variable for the last merged branch (useful for a default when deleting
144 set _last_merged_branch
{}
147 global _shellpath env
148 if {[string match @@
* $_shellpath]} {
149 if {[info exists env
(SHELL
)]} {
168 return [eval [list
file join $_gitdir] $args]
171 proc gitexec
{args
} {
173 if {$_gitexec eq
{}} {
174 if {[catch
{set _gitexec
[git
--exec-path]} err
]} {
175 error
"Git not installed?\n\n$err"
178 set _gitexec
[exec cygpath \
183 set _gitexec
[file normalize
$_gitexec]
189 return [eval [list
file join $_gitexec] $args]
192 proc githtmldir
{args
} {
194 if {$_githtmldir eq
{}} {
195 if {[catch
{set _githtmldir
[git
--html-path]}]} {
196 # Git not installed or option not yet supported
200 set _githtmldir
[exec cygpath \
205 set _githtmldir
[file normalize
$_githtmldir]
211 return [eval [list
file join $_githtmldir] $args]
219 if {[tk windowingsystem
] eq
{aqua
}} {
226 if {$
::tcl_platform
(platform
) eq
{windows
}} {
234 if {$_iscygwin eq
{}} {
235 if {$
::tcl_platform
(platform
) eq
{windows
}} {
236 if {[catch
{set p
[exec cygpath
--windir]} err
]} {
248 proc is_enabled
{option
} {
249 global enabled_options
250 if {[catch
{set on
$enabled_options($option)}]} {return 0}
254 proc enable_option
{option
} {
255 global enabled_options
256 set enabled_options
($option) 1
259 proc disable_option
{option
} {
260 global enabled_options
261 set enabled_options
($option) 0
264 ######################################################################
268 proc is_many_config
{name
} {
269 switch
-glob -- $name {
279 proc is_config_true
{name
} {
281 if {[catch
{set v
$repo_config($name)}]} {
283 } elseif
{$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes}} {
290 proc is_config_false
{name
} {
292 if {[catch
{set v
$repo_config($name)}]} {
294 } elseif
{$v eq
{false
} ||
$v eq
{0} ||
$v eq
{no
}} {
301 proc get_config
{name
} {
303 if {[catch
{set v
$repo_config($name)}]} {
315 if {$_isbare eq
{}} {
317 set _bare
[git rev-parse
--is-bare-repository]
319 true
{ set _isbare
1 }
320 false
{ set _isbare
0}
324 if {[is_config_true core.bare
]
325 ||
($_gitworktree eq
{}
326 && [lindex
[file split $_gitdir] end
] ne
{.git
})} {
336 ######################################################################
340 proc _trace_exec
{cmd
} {
341 if {!$
::_trace
} return
347 if {[regexp
{[ \t\r\n'"$?*]} $v]} {
355 #'" fix poor old emacs font-lock mode
357 proc _git_cmd {name} {
360 if {[catch {set v $_git_cmd_path($name)}]} {
364 --exec-path { return [list $::_git $name] }
367 set p [gitexec git-$name$::_search_exe]
368 if {[file exists $p]} {
370 } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
371 # Try to determine what sort of magic will make
372 # git-$name go and do its thing, because native
373 # Tcl on Windows doesn't know it.
375 set p [gitexec git-$name]
380 switch -glob -- [lindex $s 0] {
382 #!*perl { set i perl }
383 #!*python { set i python }
384 default { error "git-
$name is not supported
: $s" }
388 if {![info exists interp]} {
389 set interp [_which $i]
392 error "git-
$name requires
$i (not
in PATH
)"
394 set v [concat [list $interp] [lrange $s 1 end] [list $p]]
396 # Assume it is builtin to git somehow and we
397 # aren't actually able to see a file for it.
399 set v [list $::_git $name]
401 set _git_cmd_path($name) $v
406 proc _which {what args} {
407 global env _search_exe _search_path
409 if {$_search_path eq {}} {
410 if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
411 set _search_path [split [exec cygpath \
417 } elseif {[is_Windows]} {
418 set gitguidir [file dirname [info script]]
419 regsub -all ";" $gitguidir "\\;" gitguidir
420 set env(PATH) "$gitguidir;$env(PATH
)"
421 set _search_path [split $env(PATH) {;}]
424 set _search_path [split $env(PATH) :]
429 if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
432 set suffix $_search_exe
435 foreach p $_search_path {
436 set p [file join $p $what$suffix]
437 if {[file exists $p]} {
438 return [file normalize $p]
444 proc _lappend_nice {cmd_var} {
448 if {![info exists _nice]} {
449 set _nice [_which nice]
450 if {[catch {exec $_nice git version}]} {
452 } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
465 switch -- [lindex $args 0] {
476 set args [lrange $args 1 end]
479 set cmdp [_git_cmd [lindex $args 0]]
480 set args [lrange $args 1 end]
482 _trace_exec [concat $opt $cmdp $args]
483 set result [eval exec $opt $cmdp $args]
485 puts stderr "< $result"
490 proc _open_stdout_stderr {cmd} {
493 set fd [open [concat [list | ] $cmd] r]
495 if { [lindex $cmd end] eq {2>@1}
496 && $err eq {can not find channel named "1"}
498 # Older versions of Tcl 8.4 don't have this 2>@1 IO
499 # redirect operator. Fallback to |& cat for those.
500 # The command was not actually started, so its safe
501 # to try to start it a second time.
503 set fd [open [concat \
505 [lrange $cmd 0 end-1] \
512 fconfigure $fd -eofchar {}
516 proc git_read {args} {
520 switch -- [lindex $args 0] {
535 set args [lrange $args 1 end]
538 set cmdp [_git_cmd [lindex $args 0]]
539 set args [lrange $args 1 end]
541 return [_open_stdout_stderr [concat $opt $cmdp $args]]
544 proc git_write {args} {
548 switch -- [lindex $args 0] {
559 set args [lrange $args 1 end]
562 set cmdp [_git_cmd [lindex $args 0]]
563 set args [lrange $args 1 end]
565 _trace_exec [concat $opt $cmdp $args]
566 return [open [concat [list | ] $opt $cmdp $args] w]
569 proc githook_read {hook_name args} {
570 set pchook [gitdir hooks $hook_name]
573 # On Windows [file executable] might lie so we need to ask
574 # the shell if the hook is executable. Yes that's annoying.
578 if {![info exists interp]} {
579 set interp [_which sh]
582 error "hook execution requires sh
(not
in PATH
)"
585 set scr {if test -x "$1";then exec "$@
";fi}
586 set sh_c [list $interp -c $scr $interp $pchook]
587 return [_open_stdout_stderr [concat $sh_c $args]]
590 if {[file executable $pchook]} {
591 return [_open_stdout_stderr [concat [list $pchook] $args]]
597 proc kill_file_process {fd} {
598 set process [pid $fd]
602 # Use a Cygwin-specific flag to allow killing
603 # native Windows processes
604 exec kill -f $process
611 proc gitattr {path attr default} {
612 if {[catch {set r [git check-attr $attr -- $path]}]} {
615 set r [join [lrange [split $r :] 2 end] :]
618 if {$r eq {unspecified}} {
625 regsub -all ' $value "'\\''" value
629 proc load_current_branch {} {
630 global current_branch is_detached
632 set fd [open [gitdir HEAD] r]
633 if {[gets $fd ref] < 1} {
638 set pfx {ref: refs/heads/}
639 set len [string length $pfx]
640 if {[string equal -length $len $pfx $ref]} {
641 # We're on a branch. It might not exist. But
642 # HEAD looks good enough to be a branch.
644 set current_branch
[string range
$ref $len end
]
647 # Assume this is a detached head.
649 set current_branch HEAD
654 auto_load tk_optionMenu
655 rename tk_optionMenu real__tkOptionMenu
656 proc tk_optionMenu
{w varName args
} {
657 set m
[eval real__tkOptionMenu
$w $varName $args]
658 $m configure
-font font_ui
659 $w configure
-font font_ui
663 proc rmsel_tag
{text
} {
665 -background [$text cget
-background] \
666 -foreground [$text cget
-foreground] \
668 $text tag conf in_sel
-background lightgray
669 bind $text <Motion
> break
675 bind .
<Visibility
> {
676 bind .
<Visibility
> {}
681 wm iconbitmap .
-default $oguilib/git-gui.ico
682 set ::tk
::AlwaysShowSelection
1
683 bind .
<Control-F2
> {console show
}
685 # Spoof an X11 display for SSH
686 if {![info exists env
(DISPLAY
)]} {
687 set env
(DISPLAY
) :9999
691 image create photo gitlogo
-width 16 -height 16
693 gitlogo put
#33CC33 -to 7 0 9 2
694 gitlogo put
#33CC33 -to 4 2 12 4
695 gitlogo put
#33CC33 -to 7 4 9 6
696 gitlogo put
#CC3333 -to 4 6 12 8
697 gitlogo put gray26
-to 4 9 6 10
698 gitlogo put gray26
-to 3 10 6 12
699 gitlogo put gray26
-to 8 9 13 11
700 gitlogo put gray26
-to 8 11 10 12
701 gitlogo put gray26
-to 11 11 13 14
702 gitlogo put gray26
-to 3 12 5 14
703 gitlogo put gray26
-to 5 13
704 gitlogo put gray26
-to 10 13
705 gitlogo put gray26
-to 4 14 12 15
706 gitlogo put gray26
-to 5 15 11 16
709 wm iconphoto .
-default gitlogo
713 ######################################################################
719 if {[lsearch
-exact [font names
] TkDefaultFont
] != -1} {
720 eval [linsert
[font actual TkDefaultFont
] 0 font configure font_ui
]
721 eval [linsert
[font actual TkFixedFont
] 0 font create font_diff
]
723 font create font_diff
-family Courier
-size 10
726 eval font configure font_ui
[font actual
[.dummy cget
-font]]
731 font create font_uiitalic
732 font create font_uibold
733 font create font_diffbold
734 font create font_diffitalic
736 foreach class
{Button Checkbutton Entry Label
737 Labelframe Listbox Message
738 Radiobutton Spinbox Text
} {
739 option add
*$class.font font_ui
742 option add
*Menu.font font_ui
743 option add
*Entry.borderWidth
1 startupFile
744 option add
*Entry.relief sunken startupFile
745 option add
*RadioButton.anchor w startupFile
749 if {[is_Windows
] ||
[is_MacOSX
]} {
750 option add
*Menu.tearOff
0
761 proc bind_button3
{w cmd
} {
762 bind $w <Any-Button-3
> $cmd
764 # Mac OS X sends Button-2 on right click through three-button mouse,
765 # or through trackpad right-clicking (two-finger touch + click).
766 bind $w <Any-Button-2
> $cmd
767 bind $w <Control-Button-1
> $cmd
771 proc apply_config
{} {
772 global repo_config font_descs
774 foreach option
$font_descs {
775 set name
[lindex
$option 0]
776 set font
[lindex
$option 1]
779 foreach
{cn cv
} $repo_config(gui.
$name) {
780 if {$cn eq
{-weight}} {
783 font configure
$font $cn $cv
786 font configure
$font -weight normal
789 error_popup
[strcat
[mc
"Invalid font specified in %s:" "gui.$name"] "\n\n$err"]
791 foreach
{cn cv
} [font configure
$font] {
792 font configure
${font}bold
$cn $cv
793 font configure
${font}italic
$cn $cv
795 font configure
${font}bold
-weight bold
796 font configure
${font}italic
-slant italic
802 if {$repo_config(gui.usettk
)} {
803 set use_ttk
[package vsatisfies
[package provide Tk
] 8.5]
806 bind [winfo class .
] <<ThemeChanged>> [list InitTheme]
812 set default_config(branch.autosetupmerge) true
813 set default_config(merge.tool) {}
814 set default_config(mergetool.keepbackup) true
815 set default_config(merge.diffstat) true
816 set default_config(merge.summary) false
817 set default_config(merge.verbosity) 2
818 set default_config(user.name) {}
819 set default_config(user.email) {}
821 set default_config(gui.encoding) [encoding system]
822 set default_config(gui.matchtrackingbranch) false
823 set default_config(gui.textconv) true
824 set default_config(gui.pruneduringfetch) false
825 set default_config(gui.trustmtime) false
826 set default_config(gui.fastcopyblame) false
827 set default_config(gui.copyblamethreshold) 40
828 set default_config(gui.blamehistoryctx) 7
829 set default_config(gui.diffcontext) 5
830 set default_config(gui.commitmsgwidth) 75
831 set default_config(gui.newbranchtemplate) {}
832 set default_config(gui.spellingdictionary) {}
833 set default_config(gui.fontui) [font configure font_ui]
834 set default_config(gui.fontdiff) [font configure font_diff]
835 # TODO: this option should be added to the git-config documentation
836 set default_config(gui.maxfilesdisplayed) 5000
837 set default_config(gui.usettk) 1
839 {fontui font_ui {mc "Main Font"}}
840 {fontdiff font_diff {mc "Diff/Console Font"}}
843 ######################################################################
847 set _git [_which git]
849 catch {wm withdraw .}
853 -title [mc "git-gui: fatal error"] \
854 -message [mc "Cannot find git in PATH."]
858 ######################################################################
862 if {[catch {set _git_version [git --version]} err]} {
863 catch {wm withdraw .}
867 -title [mc "git-gui: fatal error"] \
868 -message "Cannot determine Git version:
872 [appname] requires Git 1.5.0 or later."
875 if {![regsub {^git version } $_git_version {} _git_version]} {
876 catch {wm withdraw .}
880 -title [mc "git-gui: fatal error"] \
881 -message [strcat [mc "Cannot parse Git version string:"] "\n\n$_git_version"]
885 proc get_trimmed_version {s} {
887 foreach x [split $s -._] {
888 if {[string is integer -strict $x]} {
896 set _real_git_version $_git_version
897 set _git_version [get_trimmed_version $_git_version]
899 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
900 catch {wm withdraw .}
905 -title "[appname]: warning" \
906 -message [mc "Git version cannot be determined.
908 %s claims it is version '%s'.
910 %s requires at least Git 1.5.0 or later.
912 Assume '%s' is version 1.5.0?
913 " $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} {
914 set _git_version 1.5.0
919 unset _real_git_version
921 proc git-version {args} {
924 switch [llength $args] {
930 set op [lindex $args 0]
931 set vr [lindex $args 1]
932 set cm [package vcompare $_git_version $vr]
933 return [expr $cm $op 0]
937 set type [lindex $args 0]
938 set name [lindex $args 1]
939 set parm [lindex $args 2]
940 set body [lindex $args 3]
942 if {($type ne {proc} && $type ne {method})} {
943 error "Invalid arguments to git-version"
945 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
946 error "Last arm of $type $name must be default"
949 foreach {op vr cb} [lrange $body 0 end-2] {
950 if {[git-version $op $vr]} {
951 return [uplevel [list $type $name $parm $cb]]
955 return [uplevel [list $type $name $parm [lindex $body end]]]
959 error "git-version >= x"
965 if {[git-version < 1.5]} {
966 catch {wm withdraw .}
970 -title [mc "git-gui: fatal error"] \
971 -message "[appname] requires Git 1.5.0 or later.
973 You are using [git-version]:
979 ######################################################################
981 ## configure our library
983 set idx [file join $oguilib tclIndex]
984 if {[catch {set fd [open $idx r]} err]} {
985 catch {wm withdraw .}
989 -title [mc "git-gui: fatal error"] \
993 if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
995 while {[gets $fd n] >= 0} {
996 if {$n ne {} && ![string match #* $n]} {
1008 if {[lsearch -exact $loaded $p] >= 0} continue
1009 source [file join $oguilib $p]
1014 set auto_path [concat [list $oguilib] $auto_path]
1016 unset -nocomplain idx fd
1018 ######################################################################
1020 ## config file parsing
1022 git-version proc _parse_config {arr_name args} {
1029 [list git_read config] \
1031 [list --null --list]]
1032 fconfigure $fd_rc -translation binary
1033 set buf [read $fd_rc]
1036 foreach line [split $buf "\0"] {
1037 if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} {
1038 if {[is_many_config $name]} {
1039 lappend arr($name) $value
1041 set arr($name) $value
1050 set fd_rc [eval [list git_read config --list] $args]
1051 while {[gets $fd_rc line] >= 0} {
1052 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
1053 if {[is_many_config $name]} {
1054 lappend arr($name) $value
1056 set arr($name) $value
1065 proc load_config {include_global} {
1066 global repo_config global_config system_config default_config
1068 if {$include_global} {
1069 _parse_config system_config --system
1070 _parse_config global_config --global
1072 _parse_config repo_config
1074 foreach name [array names default_config] {
1075 if {[catch {set v $system_config($name)}]} {
1076 set system_config($name) $default_config($name)
1079 foreach name [array names system_config] {
1080 if {[catch {set v $global_config($name)}]} {
1081 set global_config($name) $system_config($name)
1083 if {[catch {set v $repo_config($name)}]} {
1084 set repo_config($name) $system_config($name)
1089 ######################################################################
1091 ## feature option selection
1093 if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
1098 if {$subcommand eq {gui.sh}} {
1101 if {$subcommand eq {gui} && [llength $argv] > 0} {
1102 set subcommand [lindex $argv 0]
1103 set argv [lrange $argv 1 end]
1106 enable_option multicommit
1107 enable_option branch
1108 enable_option transport
1111 switch -- $subcommand {
1116 disable_option multicommit
1117 disable_option branch
1118 disable_option transport
1121 enable_option singlecommit
1122 enable_option retcode
1124 disable_option multicommit
1125 disable_option branch
1126 disable_option transport
1128 while {[llength $argv] > 0} {
1129 set a [lindex $argv 0]
1132 enable_option initialamend
1135 enable_option nocommit
1136 enable_option nocommitmsg
1139 disable_option nocommitmsg
1146 set argv [lrange $argv 1 end]
1151 ######################################################################
1153 ## execution environment
1155 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1157 # Suggest our implementation of askpass, if none is set
1158 if {![info exists env(SSH_ASKPASS)]} {
1159 set env(SSH_ASKPASS) [gitexec git-gui--askpass]
1162 ######################################################################
1168 set _gitdir $env(GIT_DIR)
1172 # beware that from the .git dir this sets _gitdir to .
1173 # and _prefix to the empty string
1174 set _gitdir [git rev-parse --git-dir]
1175 set _prefix [git rev-parse --show-prefix]
1179 choose_repository::pick
1183 # we expand the _gitdir when it's just a single dot (i.e. when we're being
1184 # run from the .git dir itself) lest the routines to find the worktree
1186 if {$_gitdir eq "."} {
1190 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
1191 catch {set _gitdir [exec cygpath --windows $_gitdir]}
1193 if {![file isdirectory $_gitdir]} {
1194 catch {wm withdraw .}
1195 error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
1198 # _gitdir exists, so try loading the config
1202 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
1203 if {[package vsatisfies $_git_version 1.7.0]} {
1204 set _gitworktree [git rev-parse --show-toplevel]
1206 # try to set work tree from environment, core.worktree or use
1207 # cdup to obtain a relative path to the top of the worktree. If
1208 # run from the top, the ./ prefix ensures normalize expands pwd.
1209 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
1210 set _gitworktree [get_config core.worktree]
1211 if {$_gitworktree eq ""} {
1212 set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
1217 if {$_prefix ne {}} {
1218 if {$_gitworktree eq {}} {
1219 regsub -all {[^/]+/} $_prefix ../ cdup
1221 set cdup $_gitworktree
1223 if {[catch {cd $cdup} err]} {
1224 catch {wm withdraw .}
1225 error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
1228 set _gitworktree [pwd]
1230 } elseif {![is_enabled bare]} {
1232 catch {wm withdraw .}
1233 error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
1236 if {$_gitworktree eq {}} {
1237 set _gitworktree [file dirname $_gitdir]
1239 if {[catch {cd $_gitworktree} err]} {
1240 catch {wm withdraw .}
1241 error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
1244 set _gitworktree [pwd]
1246 set _reponame [file split [file normalize $_gitdir]]
1247 if {[lindex $_reponame end] eq {.git}} {
1248 set _reponame [lindex $_reponame end-1]
1250 set _reponame [lindex $_reponame end]
1253 set env(GIT_DIR) $_gitdir
1254 set env(GIT_WORK_TREE) $_gitworktree
1256 ######################################################################
1260 set current_diff_path {}
1261 set current_diff_side {}
1262 set diff_actions [list]
1266 set MERGE_HEAD [list]
1269 set current_branch {}
1271 set current_diff_path {}
1273 set is_submodule_diff 0
1274 set is_conflict_diff 0
1275 set selected_commit_type new
1276 set diff_empty_count 0
1278 set nullid "0000000000000000000000000000000000000000"
1279 set nullid2 "0000000000000000000000000000000000000001"
1281 ######################################################################
1289 set disable_on_lock [list]
1290 set index_lock_type none
1292 proc lock_index {type} {
1293 global index_lock_type disable_on_lock
1295 if {$index_lock_type eq {none}} {
1296 set index_lock_type $type
1297 foreach w $disable_on_lock {
1298 uplevel #0 $w disabled
1301 } elseif {$index_lock_type eq "begin-$type"} {
1302 set index_lock_type $type
1308 proc unlock_index {} {
1309 global index_lock_type disable_on_lock
1311 set index_lock_type none
1312 foreach w $disable_on_lock {
1313 uplevel #0 $w normal
1317 ######################################################################
1321 proc repository_state {ctvar hdvar mhvar} {
1322 global current_branch
1323 upvar $ctvar ct $hdvar hd $mhvar mh
1328 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
1334 set merge_head [gitdir MERGE_HEAD]
1335 if {[file exists $merge_head]} {
1337 set fd_mh [open $merge_head r]
1338 while {[gets $fd_mh line] >= 0} {
1349 global PARENT empty_tree
1351 set p [lindex $PARENT 0]
1355 if {$empty_tree eq {}} {
1356 set empty_tree [git mktree << {}]
1361 proc force_amend {} {
1362 global selected_commit_type
1363 global HEAD PARENT MERGE_HEAD commit_type
1365 repository_state newType newHEAD newMERGE_HEAD
1368 set MERGE_HEAD $newMERGE_HEAD
1369 set commit_type $newType
1371 set selected_commit_type amend
1372 do_select_commit_type
1375 proc rescan {after {honor_trustmtime 1}} {
1376 global HEAD PARENT MERGE_HEAD commit_type
1377 global ui_index ui_workdir ui_comm
1378 global rescan_active file_states
1381 if {$rescan_active > 0 || ![lock_index read]} return
1383 repository_state newType newHEAD newMERGE_HEAD
1384 if {[string match amend* $commit_type]
1385 && $newType eq {normal}
1386 && $newHEAD eq $HEAD} {
1390 set MERGE_HEAD $newMERGE_HEAD
1391 set commit_type $newType
1394 array unset file_states
1396 if {!$::GITGUI_BCK_exists &&
1397 (![$ui_comm edit modified]
1398 || [string trim [$ui_comm get 0.0 end]] eq {})} {
1399 if {[string match amend* $commit_type]} {
1400 } elseif {[load_message GITGUI_MSG]} {
1401 } elseif {[run_prepare_commit_msg_hook]} {
1402 } elseif {[load_message MERGE_MSG]} {
1403 } elseif {[load_message SQUASH_MSG]} {
1406 $ui_comm edit modified false
1409 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
1410 rescan_stage2 {} $after
1413 ui_status [mc "Refreshing file status..."]
1414 set fd_rf [git_read update-index \
1420 fconfigure $fd_rf -blocking 0 -translation binary
1421 fileevent $fd_rf readable \
1422 [list rescan_stage2 $fd_rf $after]
1427 set is_git_info_exclude {}
1428 proc have_info_exclude {} {
1429 global is_git_info_exclude
1431 if {$is_git_info_exclude eq {}} {
1432 if {[catch {exec test -f [gitdir info exclude]}]} {
1433 set is_git_info_exclude 0
1435 set is_git_info_exclude 1
1438 return $is_git_info_exclude
1441 proc have_info_exclude {} {
1442 return [file readable [gitdir info exclude]]
1446 proc rescan_stage2 {fd after} {
1447 global rescan_active buf_rdi buf_rdf buf_rlo
1451 if {![eof $fd]} return
1455 if {[package vsatisfies $::_git_version 1.6.3]} {
1456 set ls_others [list --exclude-standard]
1458 set ls_others [list --exclude-per-directory=.gitignore]
1459 if {[have_info_exclude]} {
1460 lappend ls_others "--exclude-from=[gitdir info exclude]"
1462 set user_exclude [get_config core.excludesfile]
1463 if {$user_exclude ne {} && [file readable $user_exclude]} {
1464 lappend ls_others "--exclude-from=[file normalize $user_exclude]"
1473 ui_status [mc "Scanning for modified files ..."]
1474 set fd_di [git_read diff-index --cached -z [PARENT]]
1475 set fd_df [git_read diff-files -z]
1476 set fd_lo [eval git_read ls-files --others -z $ls_others]
1478 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1479 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
1480 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
1481 fileevent $fd_di readable [list read_diff_index $fd_di $after]
1482 fileevent $fd_df readable [list read_diff_files $fd_df $after]
1483 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
1486 proc load_message {file} {
1489 set f [gitdir $file]
1490 if {[file isfile $f]} {
1491 if {[catch {set fd [open $f r]}]} {
1494 fconfigure $fd -eofchar {}
1495 set content [string trim [read $fd]]
1497 regsub -all -line {[ \r\t]+$} $content {} content
1498 $ui_comm delete 0.0 end
1499 $ui_comm insert end $content
1505 proc run_prepare_commit_msg_hook {} {
1508 # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
1509 # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
1510 # empty file but existant file.
1512 set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
1514 if {[file isfile [gitdir MERGE_MSG]]} {
1515 set pcm_source "merge"
1516 set fd_mm [open [gitdir MERGE_MSG] r]
1517 puts -nonewline $fd_pcm [read $fd_mm]
1519 } elseif {[file isfile [gitdir SQUASH_MSG]]} {
1520 set pcm_source "squash"
1521 set fd_sm [open [gitdir SQUASH_MSG] r]
1522 puts -nonewline $fd_pcm [read $fd_sm]
1530 set fd_ph [githook_read prepare-commit-msg \
1531 [gitdir PREPARE_COMMIT_MSG] $pcm_source]
1533 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1537 ui_status [mc "Calling prepare-commit-msg hook..."]
1540 fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
1541 fileevent $fd_ph readable \
1542 [list prepare_commit_msg_hook_wait $fd_ph]
1547 proc prepare_commit_msg_hook_wait {fd_ph} {
1550 append pch_error [read $fd_ph]
1551 fconfigure $fd_ph -blocking 1
1553 if {[catch {close $fd_ph}]} {
1554 ui_status [mc "Commit declined by prepare-commit-msg hook."]
1555 hook_failed_popup prepare-commit-msg $pch_error
1556 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1559 load_message PREPARE_COMMIT_MSG
1562 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1565 fconfigure $fd_ph -blocking 0
1566 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1569 proc read_diff_index {fd after} {
1572 append buf_rdi [read $fd]
1574 set n [string length $buf_rdi]
1576 set z1 [string first "\0" $buf_rdi $c]
1577 if {$z1 == -1} break
1579 set z2 [string first "\0" $buf_rdi $z1]
1580 if {$z2 == -1} break
1583 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
1584 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
1586 [encoding convertfrom $p] \
1588 [list [lindex $i 0] [lindex $i 2]] \
1594 set buf_rdi [string range $buf_rdi $c end]
1599 rescan_done $fd buf_rdi $after
1602 proc read_diff_files {fd after} {
1605 append buf_rdf [read $fd]
1607 set n [string length $buf_rdf]
1609 set z1 [string first "\0" $buf_rdf $c]
1610 if {$z1 == -1} break
1612 set z2 [string first "\0" $buf_rdf $z1]
1613 if {$z2 == -1} break
1616 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
1617 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
1619 [encoding convertfrom $p] \
1622 [list [lindex $i 0] [lindex $i 2]]
1627 set buf_rdf [string range $buf_rdf $c end]
1632 rescan_done $fd buf_rdf $after
1635 proc read_ls_others {fd after} {
1638 append buf_rlo [read $fd]
1639 set pck [split $buf_rlo "\0"]
1640 set buf_rlo [lindex $pck end]
1641 foreach p [lrange $pck 0 end-1] {
1642 set p [encoding convertfrom $p]
1643 if {[string index $p end] eq {/}} {
1644 set p [string range $p 0 end-1]
1648 rescan_done $fd buf_rlo $after
1651 proc rescan_done {fd buf after} {
1652 global rescan_active current_diff_path
1653 global file_states repo_config
1656 if {![eof $fd]} return
1659 if {[incr rescan_active -1] > 0} return
1664 if {$current_diff_path ne {}} { reshow_diff $after }
1665 if {$current_diff_path eq {}} { select_first_diff $after }
1668 proc prune_selection {} {
1669 global file_states selected_paths
1671 foreach path [array names selected_paths] {
1672 if {[catch {set still_here $file_states($path)}]} {
1673 unset selected_paths($path)
1678 ######################################################################
1682 proc mapicon {w state path} {
1685 if {[catch {set r $all_icons($state$w)}]} {
1686 puts "error: no icon for $w state={$state} $path"
1692 proc mapdesc {state path} {
1695 if {[catch {set r $all_descs($state)}]} {
1696 puts "error: no desc for state={$state} $path"
1702 proc ui_status {msg} {
1704 if {[info exists main_status]} {
1705 $main_status show $msg
1709 proc ui_ready {{test {}}} {
1711 if {[info exists main_status]} {
1712 $main_status show [mc "Ready."] $test
1716 proc escape_path {path} {
1717 regsub -all {\\} $path "\\\\" path
1718 regsub -all "\n" $path "\\n" path
1722 proc short_path {path} {
1723 return [escape_path [lindex [file split $path] end]]
1727 set null_sha1 [string repeat 0 40]
1729 proc merge_state {path new_state {head_info {}} {index_info {}}} {
1730 global file_states next_icon_id null_sha1
1732 set s0 [string index $new_state 0]
1733 set s1 [string index $new_state 1]
1735 if {[catch {set info $file_states($path)}]} {
1737 set icon n[incr next_icon_id]
1739 set state [lindex $info 0]
1740 set icon [lindex $info 1]
1741 if {$head_info eq {}} {set head_info [lindex $info 2]}
1742 if {$index_info eq {}} {set index_info [lindex $info 3]}
1745 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1746 elseif {$s0 eq {_}} {set s0 _}
1748 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1749 elseif {$s1 eq {_}} {set s1 _}
1751 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1752 set head_info [list 0 $null_sha1]
1753 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1754 && $head_info eq {}} {
1755 set head_info $index_info
1756 } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
1757 set index_info $head_info
1761 set file_states($path) [list $s0$s1 $icon \
1762 $head_info $index_info \
1767 proc display_file_helper {w path icon_name old_m new_m} {
1770 if {$new_m eq {_}} {
1771 set lno [lsearch -sorted -exact $file_lists($w) $path]
1773 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1775 $w conf -state normal
1776 $w delete $lno.0 [expr {$lno + 1}].0
1777 $w conf -state disabled
1779 } elseif {$old_m eq {_} && $new_m ne {_}} {
1780 lappend file_lists($w) $path
1781 set file_lists($w) [lsort -unique $file_lists($w)]
1782 set lno [lsearch -sorted -exact $file_lists($w) $path]
1784 $w conf -state normal
1785 $w image create $lno.0 \
1786 -align center -padx 5 -pady 1 \
1788 -image [mapicon $w $new_m $path]
1789 $w insert $lno.1 "[escape_path $path]\n"
1790 $w conf -state disabled
1791 } elseif {$old_m ne $new_m} {
1792 $w conf -state normal
1793 $w image conf $icon_name -image [mapicon $w $new_m $path]
1794 $w conf -state disabled
1798 proc display_file {path state} {
1799 global file_states selected_paths
1800 global ui_index ui_workdir
1802 set old_m [merge_state $path $state]
1803 set s $file_states($path)
1804 set new_m [lindex $s 0]
1805 set icon_name [lindex $s 1]
1807 set o [string index $old_m 0]
1808 set n [string index $new_m 0]
1815 display_file_helper $ui_index $path $icon_name $o $n
1817 if {[string index $old_m 0] eq {U}} {
1820 set o [string index $old_m 1]
1822 if {[string index $new_m 0] eq {U}} {
1825 set n [string index $new_m 1]
1827 display_file_helper $ui_workdir $path $icon_name $o $n
1829 if {$new_m eq {__}} {
1830 unset file_states($path)
1831 catch {unset selected_paths($path)}
1835 proc display_all_files_helper {w path icon_name m} {
1838 lappend file_lists($w) $path
1839 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1840 $w image create end \
1841 -align center -padx 5 -pady 1 \
1843 -image [mapicon $w $m $path]
1844 $w insert end "[escape_path $path]\n"
1848 proc display_all_files {} {
1849 global ui_index ui_workdir
1850 global file_states file_lists
1852 global files_warning
1854 $ui_index conf -state normal
1855 $ui_workdir conf -state normal
1857 $ui_index delete 0.0 end
1858 $ui_workdir delete 0.0 end
1861 set file_lists($ui_index) [list]
1862 set file_lists($ui_workdir) [list]
1864 set to_display [lsort [array names file_states]]
1865 set display_limit [get_config gui.maxfilesdisplayed]
1866 if {[llength $to_display] > $display_limit} {
1867 if {!$files_warning} {
1868 # do not repeatedly warn:
1870 info_popup [mc "Displaying only %s of %s files." \
1871 $display_limit [llength $to_display]]
1873 set to_display [lrange $to_display 0 [expr {$display_limit-1}]]
1875 foreach path $to_display {
1876 set s $file_states($path)
1878 set icon_name [lindex $s 1]
1880 set s [string index $m 0]
1881 if {$s ne {U} && $s ne {_}} {
1882 display_all_files_helper $ui_index $path \
1886 if {[string index $m 0] eq {U}} {
1889 set s [string index $m 1]
1892 display_all_files_helper $ui_workdir $path \
1897 $ui_index conf -state disabled
1898 $ui_workdir conf -state disabled
1901 ######################################################################
1906 #define mask_width 14
1907 #define mask_height 15
1908 static unsigned char mask_bits[] = {
1909 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1910 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1911 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1914 image create bitmap file_plain -background white -foreground black -data {
1915 #define plain_width 14
1916 #define plain_height 15
1917 static unsigned char plain_bits[] = {
1918 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1919 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1920 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1921 } -maskdata $filemask
1923 image create bitmap file_mod -background white -foreground blue -data {
1924 #define mod_width 14
1925 #define mod_height 15
1926 static unsigned char mod_bits[] = {
1927 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1928 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1929 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1930 } -maskdata $filemask
1932 image create bitmap file_fulltick -background white -foreground "#007000" -data {
1933 #define file_fulltick_width 14
1934 #define file_fulltick_height 15
1935 static unsigned char file_fulltick_bits[] = {
1936 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1937 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1938 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1939 } -maskdata $filemask
1941 image create bitmap file_question -background white -foreground black -data {
1942 #define file_question_width 14
1943 #define file_question_height 15
1944 static unsigned char file_question_bits[] = {
1945 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1946 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1947 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1948 } -maskdata $filemask
1950 image create bitmap file_removed -background white -foreground red -data {
1951 #define file_removed_width 14
1952 #define file_removed_height 15
1953 static unsigned char file_removed_bits[] = {
1954 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1955 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1956 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1957 } -maskdata $filemask
1959 image create bitmap file_merge -background white -foreground blue -data {
1960 #define file_merge_width 14
1961 #define file_merge_height 15
1962 static unsigned char file_merge_bits[] = {
1963 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1964 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1965 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1966 } -maskdata $filemask
1968 image create bitmap file_statechange -background white -foreground green -data {
1969 #define file_statechange_width 14
1970 #define file_statechange_height 15
1971 static unsigned char file_statechange_bits[] = {
1972 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
1973 0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
1974 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1975 } -maskdata $filemask
1977 set ui_index .vpane.files.index.list
1978 set ui_workdir .vpane.files.workdir.list
1980 set all_icons(_$ui_index) file_plain
1981 set all_icons(A$ui_index) file_plain
1982 set all_icons(M$ui_index) file_fulltick
1983 set all_icons(D$ui_index) file_removed
1984 set all_icons(U$ui_index) file_merge
1985 set all_icons(T$ui_index) file_statechange
1987 set all_icons(_$ui_workdir) file_plain
1988 set all_icons(M$ui_workdir) file_mod
1989 set all_icons(D$ui_workdir) file_question
1990 set all_icons(U$ui_workdir) file_merge
1991 set all_icons(O$ui_workdir) file_plain
1992 set all_icons(T$ui_workdir) file_statechange
1994 set max_status_desc 0
1996 {__ {mc "Unmodified"}}
1998 {_M {mc "Modified, not staged"}}
1999 {M_ {mc "Staged for commit"}}
2000 {MM {mc "Portions staged for commit"}}
2001 {MD {mc "Staged for commit, missing"}}
2003 {_T {mc "File type changed, not staged"}}
2004 {MT {mc "File type changed, old type staged for commit"}}
2005 {AT {mc "File type changed, old type staged for commit"}}
2006 {T_ {mc "File type changed, staged"}}
2007 {TM {mc "File type change staged, modification not staged"}}
2008 {TD {mc "File type change staged, file missing"}}
2010 {_O {mc "Untracked, not staged"}}
2011 {A_ {mc "Staged for commit"}}
2012 {AM {mc "Portions staged for commit"}}
2013 {AD {mc "Staged for commit, missing"}}
2016 {D_ {mc "Staged for removal"}}
2017 {DO {mc "Staged for removal, still present"}}
2019 {_U {mc "Requires merge resolution"}}
2020 {U_ {mc "Requires merge resolution"}}
2021 {UU {mc "Requires merge resolution"}}
2022 {UM {mc "Requires merge resolution"}}
2023 {UD {mc "Requires merge resolution"}}
2024 {UT {mc "Requires merge resolution"}}
2026 set text [eval [lindex $i 1]]
2027 if {$max_status_desc < [string length $text]} {
2028 set max_status_desc [string length $text]
2030 set all_descs([lindex $i 0]) $text
2034 ######################################################################
2038 proc scrollbar2many {list mode args} {
2039 foreach w $list {eval $w $mode $args}
2042 proc many2scrollbar {list mode sb top bottom} {
2043 $sb set $top $bottom
2044 foreach w $list {$w $mode moveto $top}
2047 proc incr_font_size {font {amt 1}} {
2048 set sz [font configure $font -size]
2050 font configure $font -size $sz
2051 font configure ${font}bold -size $sz
2052 font configure ${font}italic -size $sz
2055 ######################################################################
2059 set starting_gitk_msg [mc "Starting gitk... please wait..."]
2061 proc do_gitk {revs {is_submodule false}} {
2062 global current_diff_path file_states current_diff_side ui_index
2063 global _gitdir _gitworktree
2065 # -- Always start gitk through whatever we were loaded with. This
2066 # lets us bypass using shell process on Windows systems.
2068 set exe [_which gitk -script]
2069 set cmd [list [info nameofexecutable] $exe]
2071 error_popup [mc "Couldn't find gitk in PATH"]
2077 if {!$is_submodule} {
2082 cd $current_diff_path
2083 if {$revs eq {--}} {
2084 set s $file_states($current_diff_path)
2087 switch -glob -- [lindex $s 0] {
2088 M_ { set old_sha1 [lindex [lindex $s 2] 1] }
2089 _M { set old_sha1 [lindex [lindex $s 3] 1] }
2091 if {$current_diff_side eq $ui_index} {
2092 set old_sha1 [lindex [lindex $s 2] 1]
2093 set new_sha1 [lindex [lindex $s 3] 1]
2095 set old_sha1 [lindex [lindex $s 3] 1]
2099 set revs $old_sha1...$new_sha1
2101 # GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
2102 # we've been using for the main repository, so unset them.
2103 # TODO we could make life easier (start up faster?) for gitk
2104 # by setting these to the appropriate values to allow gitk
2105 # to skip the heuristics to find their proper value
2107 unset env(GIT_WORK_TREE)
2109 eval exec $cmd $revs "--" "--" &
2111 set env(GIT_DIR) $_gitdir
2112 set env(GIT_WORK_TREE) $_gitworktree
2115 ui_status $::starting_gitk_msg
2117 ui_ready $starting_gitk_msg
2122 proc do_git_gui {} {
2123 global current_diff_path
2125 # -- Always start git gui through whatever we were loaded with. This
2126 # lets us bypass using shell process on Windows systems.
2128 set exe [list [_which git]]
2130 error_popup [mc "Couldn't find git gui in PATH"]
2133 global _gitdir _gitworktree
2135 # see note in do_gitk about unsetting these vars when
2136 # running tools in a submodule
2138 unset env(GIT_WORK_TREE)
2141 cd $current_diff_path
2143 eval exec $exe gui &
2145 set env(GIT_DIR) $_gitdir
2146 set env(GIT_WORK_TREE) $_gitworktree
2149 ui_status $::starting_gitk_msg
2151 ui_ready $starting_gitk_msg
2156 proc do_explore {} {
2159 if {[is_Cygwin] || [is_Windows]} {
2160 set explorer "explorer.exe"
2161 } elseif {[is_MacOSX]} {
2164 # freedesktop.org-conforming system is our best shot
2165 set explorer "xdg-open"
2167 eval exec $explorer [list [file nativename $_gitworktree]] &
2173 proc terminate_me {win} {
2175 if {$win ne {.}} return
2179 proc do_quit {{rc {1}}} {
2180 global ui_comm is_quitting repo_config commit_type
2181 global GITGUI_BCK_exists GITGUI_BCK_i
2182 global ui_comm_spell
2183 global ret_code use_ttk
2185 if {$is_quitting} return
2188 if {[winfo exists $ui_comm]} {
2189 # -- Stash our current commit buffer.
2191 set save [gitdir GITGUI_MSG]
2192 if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
2193 file rename -force [gitdir GITGUI_BCK] $save
2194 set GITGUI_BCK_exists 0
2196 set msg [string trim [$ui_comm get 0.0 end]]
2197 regsub -all -line {[ \r\t]+$} $msg {} msg
2198 if {(![string match amend* $commit_type]
2199 || [$ui_comm edit modified])
2202 set fd [open $save w]
2203 puts -nonewline $fd $msg
2207 catch {file delete $save}
2211 # -- Cancel our spellchecker if its running.
2213 if {[info exists ui_comm_spell]} {
2217 # -- Remove our editor backup, its not needed.
2219 after cancel $GITGUI_BCK_i
2220 if {$GITGUI_BCK_exists} {
2221 catch {file delete [gitdir GITGUI_BCK]}
2224 # -- Stash our current window geometry into this repository.
2226 set cfg_wmstate [wm state .]
2227 if {[catch {set rc_wmstate $repo_config(gui.wmstate)}]} {
2230 if {$cfg_wmstate ne $rc_wmstate} {
2231 catch {git config gui.wmstate $cfg_wmstate}
2233 if {$cfg_wmstate eq {zoomed}} {
2234 # on Windows wm geometry will lie about window
2235 # position (but not size) when window is zoomed
2236 # restore the window before querying wm geometry
2239 set cfg_geometry [list]
2240 lappend cfg_geometry [wm geometry .]
2242 lappend cfg_geometry [.vpane sashpos 0]
2243 lappend cfg_geometry [.vpane.files sashpos 0]
2245 lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
2246 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1]
2248 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
2251 if {$cfg_geometry ne $rc_geometry} {
2252 catch {git config gui.geometry $cfg_geometry}
2258 # Briefly enable send again, working around Tk bug
2259 # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
2260 tk appname [appname]
2269 proc ui_do_rescan {} {
2270 rescan {force_first_diff ui_ready}
2277 proc next_diff {{after {}}} {
2278 global next_diff_p next_diff_w next_diff_i
2279 show_diff $next_diff_p $next_diff_w {} {} $after
2282 proc find_anchor_pos {lst name} {
2283 set lid [lsearch -sorted -exact $lst $name]
2287 foreach lname $lst {
2288 if {$lname >= $name} break
2296 proc find_file_from {flist idx delta path mmask} {
2299 set len [llength $flist]
2300 while {$idx >= 0 && $idx < $len} {
2301 set name [lindex $flist $idx]
2303 if {$name ne $path && [info exists file_states($name)]} {
2304 set state [lindex $file_states($name) 0]
2306 if {$mmask eq {} || [regexp $mmask $state]} {
2317 proc find_next_diff {w path {lno {}} {mmask {}}} {
2318 global next_diff_p next_diff_w next_diff_i
2319 global file_lists ui_index ui_workdir
2321 set flist $file_lists($w)
2323 set lno [find_anchor_pos $flist $path]
2328 if {$mmask ne {} && ![regexp {(^\^)|(\$$)} $mmask]} {
2329 if {$w eq $ui_index} {
2332 set mmask "$mmask\$"
2336 set idx [find_file_from $flist $lno 1 $path $mmask]
2339 set idx [find_file_from $flist $lno -1 $path $mmask]
2344 set next_diff_p [lindex $flist $idx]
2345 set next_diff_i [expr {$idx+1}]
2352 proc next_diff_after_action {w path {lno {}} {mmask {}}} {
2353 global current_diff_path
2355 if {$path ne $current_diff_path} {
2357 } elseif {[find_next_diff $w $path $lno $mmask]} {
2360 return {reshow_diff;}
2364 proc select_first_diff {after} {
2367 if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
2368 [find_next_diff $ui_workdir {} 1 {[^O]$}]} {
2375 proc force_first_diff {after} {
2376 global ui_workdir current_diff_path file_states
2378 if {[info exists file_states($current_diff_path)]} {
2379 set state [lindex $file_states($current_diff_path) 0]
2385 if {[string first {U} $state] >= 0} {
2386 # Already a conflict, do nothing
2387 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
2389 } elseif {[string index $state 1] ne {O}} {
2390 # Already a diff & no conflicts, do nothing
2391 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
2402 proc toggle_or_diff {w x y} {
2403 global file_states file_lists current_diff_path ui_index ui_workdir
2404 global last_clicked selected_paths
2406 set pos [split [$w index @$x,$y] .]
2407 set lno [lindex $pos 0]
2408 set col [lindex $pos 1]
2409 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2415 set last_clicked [list $w $lno]
2416 array unset selected_paths
2417 $ui_index tag remove in_sel 0.0 end
2418 $ui_workdir tag remove in_sel 0.0 end
2420 # Determine the state of the file
2421 if {[info exists file_states($path)]} {
2422 set state [lindex $file_states($path) 0]
2427 # Restage the file, or simply show the diff
2428 if {$col == 0 && $y > 1} {
2429 # Conflicts need special handling
2430 if {[string first {U} $state] >= 0} {
2431 # $w must always be $ui_workdir, but...
2432 if {$w ne $ui_workdir} { set lno {} }
2433 merge_stage_workdir $path $lno
2437 if {[string index $state 1] eq {O}} {
2443 set after [next_diff_after_action $w $path $lno $mmask]
2445 if {$w eq $ui_index} {
2447 "Unstaging [short_path $path] from commit" \
2449 [concat $after [list ui_ready]]
2450 } elseif {$w eq $ui_workdir} {
2452 "Adding [short_path $path]" \
2454 [concat $after [list ui_ready]]
2457 show_diff $path $w $lno
2461 proc add_one_to_selection {w x y} {
2462 global file_lists last_clicked selected_paths
2464 set lno [lindex [split [$w index @$x,$y] .] 0]
2465 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2471 if {$last_clicked ne {}
2472 && [lindex $last_clicked 0] ne $w} {
2473 array unset selected_paths
2474 [lindex $last_clicked 0] tag remove in_sel 0.0 end
2477 set last_clicked [list $w $lno]
2478 if {[catch {set in_sel $selected_paths($path)}]} {
2482 unset selected_paths($path)
2483 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2485 set selected_paths($path) 1
2486 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2490 proc add_range_to_selection {w x y} {
2491 global file_lists last_clicked selected_paths
2493 if {[lindex $last_clicked 0] ne $w} {
2494 toggle_or_diff $w $x $y
2498 set lno [lindex [split [$w index @$x,$y] .] 0]
2499 set lc [lindex $last_clicked 1]
2508 foreach path [lrange $file_lists($w) \
2509 [expr {$begin - 1}] \
2510 [expr {$end - 1}]] {
2511 set selected_paths($path) 1
2513 $w tag add in_sel $begin.0 [expr {$end + 1}].0
2516 proc show_more_context {} {
2518 if {$repo_config(gui.diffcontext) < 99} {
2519 incr repo_config(gui.diffcontext)
2524 proc show_less_context {} {
2526 if {$repo_config(gui.diffcontext) > 1} {
2527 incr repo_config(gui.diffcontext) -1
2532 ######################################################################
2540 menu .mbar -tearoff 0
2542 # -- Apple Menu (Mac OS X only)
2544 .mbar add cascade -label Apple -menu .mbar.apple
2547 .mbar add cascade -label [mc Repository] -menu .mbar.repository
2548 .mbar add cascade -label [mc Edit] -menu .mbar.edit
2549 if {[is_enabled branch]} {
2550 .mbar add cascade -label [mc Branch] -menu .mbar.branch
2552 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2553 .mbar add cascade -label [mc Commit@@noun] -menu .mbar.commit
2555 if {[is_enabled transport]} {
2556 .mbar add cascade -label [mc Merge] -menu .mbar.merge
2557 .mbar add cascade -label [mc Remote] -menu .mbar.remote
2559 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2560 .mbar add cascade -label [mc Tools] -menu .mbar.tools
2563 # -- Repository Menu
2565 menu .mbar.repository
2568 .mbar.repository add command \
2569 -label [mc "Explore Working Copy"] \
2570 -command {do_explore}
2571 .mbar.repository add separator
2574 .mbar.repository add command \
2575 -label [mc "Browse Current Branch's Files"] \
2576 -command {browser::new $current_branch}
2577 set ui_browse_current [.mbar.repository index last]
2578 .mbar.repository add command \
2579 -label [mc "Browse Branch Files..."] \
2580 -command browser_open::dialog
2581 .mbar.repository add separator
2583 .mbar.repository add command \
2584 -label [mc "Visualize Current Branch's History"] \
2585 -command {do_gitk $current_branch}
2586 set ui_visualize_current [.mbar.repository index last]
2587 .mbar.repository add command \
2588 -label [mc "Visualize All Branch History"] \
2589 -command {do_gitk --all}
2590 .mbar.repository add separator
2592 proc current_branch_write {args} {
2593 global current_branch
2594 .mbar.repository entryconf $::ui_browse_current \
2595 -label [mc "Browse %s's Files" $current_branch]
2596 .mbar.repository entryconf $::ui_visualize_current \
2597 -label [mc "Visualize %s's History" $current_branch]
2599 trace add variable current_branch write current_branch_write
2601 if {[is_enabled multicommit]} {
2602 .mbar.repository add command -label [mc "Database Statistics"] \
2605 .mbar.repository add command -label [mc "Compress Database"] \
2608 .mbar.repository add command -label [mc "Verify Database"] \
2609 -command do_fsck_objects
2611 .mbar.repository add separator
2614 .mbar.repository add command \
2615 -label [mc "Create Desktop Icon"] \
2616 -command do_cygwin_shortcut
2617 } elseif {[is_Windows]} {
2618 .mbar.repository add command \
2619 -label [mc "Create Desktop Icon"] \
2620 -command do_windows_shortcut
2621 } elseif {[is_MacOSX]} {
2622 .mbar.repository add command \
2623 -label [mc "Create Desktop Icon"] \
2624 -command do_macosx_app
2629 proc ::tk::mac::Quit {args} { do_quit }
2631 .mbar.repository add command -label [mc Quit] \
2639 .mbar.edit add command -label [mc Undo] \
2640 -command {catch {[focus] edit undo}} \
2642 .mbar.edit add command -label [mc Redo] \
2643 -command {catch {[focus] edit redo}} \
2645 .mbar.edit add separator
2646 .mbar.edit add command -label [mc Cut] \
2647 -command {catch {tk_textCut [focus]}} \
2649 .mbar.edit add command -label [mc Copy] \
2650 -command {catch {tk_textCopy [focus]}} \
2652 .mbar.edit add command -label [mc Paste] \
2653 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
2655 .mbar.edit add command -label [mc Delete] \
2656 -command {catch {[focus] delete sel.first sel.last}} \
2658 .mbar.edit add separator
2659 .mbar.edit add command -label [mc "Select All"] \
2660 -command {catch {[focus] tag add sel 0.0 end}} \
2665 if {[is_enabled branch]} {
2668 .mbar.branch add command -label [mc "Create..."] \
2669 -command branch_create::dialog \
2671 lappend disable_on_lock [list .mbar.branch entryconf \
2672 [.mbar.branch index last] -state]
2674 .mbar.branch add command -label [mc "Checkout..."] \
2675 -command branch_checkout::dialog \
2677 lappend disable_on_lock [list .mbar.branch entryconf \
2678 [.mbar.branch index last] -state]
2680 .mbar.branch add command -label [mc "Rename..."] \
2681 -command branch_rename::dialog
2682 lappend disable_on_lock [list .mbar.branch entryconf \
2683 [.mbar.branch index last] -state]
2685 .mbar.branch add command -label [mc "Delete..."] \
2686 -command branch_delete::dialog
2687 lappend disable_on_lock [list .mbar.branch entryconf \
2688 [.mbar.branch index last] -state]
2690 .mbar.branch add command -label [mc "Reset..."] \
2691 -command merge::reset_hard
2692 lappend disable_on_lock [list .mbar.branch entryconf \
2693 [.mbar.branch index last] -state]
2698 proc commit_btn_caption {} {
2699 if {[is_enabled nocommit]} {
2702 return [mc Commit@@verb]
2706 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2709 if {![is_enabled nocommit]} {
2710 .mbar.commit add radiobutton \
2711 -label [mc "New Commit"] \
2712 -command do_select_commit_type \
2713 -variable selected_commit_type \
2715 lappend disable_on_lock \
2716 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2718 .mbar.commit add radiobutton \
2719 -label [mc "Amend Last Commit"] \
2720 -command do_select_commit_type \
2721 -variable selected_commit_type \
2723 lappend disable_on_lock \
2724 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2726 .mbar.commit add separator
2729 .mbar.commit add command -label [mc Rescan] \
2730 -command ui_do_rescan \
2732 lappend disable_on_lock \
2733 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2735 .mbar.commit add command -label [mc "Stage To Commit"] \
2736 -command do_add_selection \
2738 lappend disable_on_lock \
2739 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2741 .mbar.commit add command -label [mc "Stage Changed Files To Commit"] \
2742 -command do_add_all \
2744 lappend disable_on_lock \
2745 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2747 .mbar.commit add command -label [mc "Unstage From Commit"] \
2748 -command do_unstage_selection \
2750 lappend disable_on_lock \
2751 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2753 .mbar.commit add command -label [mc "Revert Changes"] \
2754 -command do_revert_selection \
2756 lappend disable_on_lock \
2757 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2759 .mbar.commit add separator
2761 .mbar.commit add command -label [mc "Show Less Context"] \
2762 -command show_less_context \
2763 -accelerator $M1T-\-
2765 .mbar.commit add command -label [mc "Show More Context"] \
2766 -command show_more_context \
2769 .mbar.commit add separator
2771 if {![is_enabled nocommitmsg]} {
2772 .mbar.commit add command -label [mc "Sign Off"] \
2773 -command do_signoff \
2777 .mbar.commit add command -label [commit_btn_caption] \
2778 -command do_commit \
2779 -accelerator $M1T-Return
2780 lappend disable_on_lock \
2781 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2786 if {[is_enabled branch]} {
2788 .mbar.merge add command -label [mc "Local Merge..."] \
2789 -command merge::dialog \
2791 lappend disable_on_lock \
2792 [list .mbar.merge entryconf [.mbar.merge index last] -state]
2793 .mbar.merge add command -label [mc "Abort Merge..."] \
2794 -command merge::reset_hard
2795 lappend disable_on_lock \
2796 [list .mbar.merge entryconf [.mbar.merge index last] -state]
2801 if {[is_enabled transport]} {
2804 .mbar.remote add command \
2805 -label [mc "Add..."] \
2806 -command remote_add::dialog \
2808 .mbar.remote add command \
2809 -label [mc "Push..."] \
2810 -command do_push_anywhere \
2812 .mbar.remote add command \
2813 -label [mc "Delete Branch..."] \
2814 -command remote_branch_delete::dialog
2818 proc ::tk::mac::ShowPreferences {} {do_options}
2822 .mbar.edit add separator
2823 .mbar.edit add command -label [mc "Options..."] \
2829 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2830 set tools_menubar .mbar.tools
2832 $tools_menubar add separator
2833 $tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
2834 $tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
2836 if {[array names repo_config guitool.*.cmd] ne {}} {
2843 .mbar add cascade -label [mc Help] -menu .mbar.help
2847 .mbar.apple add command -label [mc "About %s" [appname]] \
2849 .mbar.apple add separator
2851 .mbar.help add command -label [mc "About %s" [appname]] \
2854 . configure -menu .mbar
2856 set doc_path [githtmldir]
2857 if {$doc_path ne {}} {
2858 set doc_path [file join $doc_path index.html]
2861 set doc_path [exec cygpath --mixed $doc_path]
2865 if {[file isfile $doc_path]} {
2866 set doc_url "file:$doc_path"
2868 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
2871 proc start_browser {url} {
2872 git "web--browse" $url
2875 .mbar.help add command -label [mc "Online Documentation"] \
2876 -command [list start_browser $doc_url]
2878 .mbar.help add command -label [mc "Show SSH Key"] \
2881 unset doc_path doc_url
2883 # -- Standard bindings
2885 wm protocol . WM_DELETE_WINDOW do_quit
2886 bind all <$M1B-Key-q> do_quit
2887 bind all <$M1B-Key-Q> do_quit
2888 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
2889 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
2891 set subcommand_args {}
2893 set s "usage: $::argv0 $::subcommand $::subcommand_args"
2894 if {[tk windowingsystem] eq "win32"} {
2896 tk_messageBox -icon info -message $s \
2904 proc normalize_relpath {path} {
2906 foreach item [file split $path] {
2907 if {$item eq {.}} continue
2908 if {$item eq {..} && [llength $elements] > 0
2909 && [lindex $elements end] ne {..}} {
2910 set elements [lrange $elements 0 end-1]
2913 lappend elements $item
2915 return [eval file join $elements]
2918 # -- Not a normal commit type invocation? Do that instead!
2920 switch -- $subcommand {
2923 if {$subcommand eq "blame"} {
2924 set subcommand_args {[--line=<num>] rev? path}
2926 set subcommand_args {rev? path}
2928 if {$argv eq {}} usage
2934 if {$is_path || [file exists $_prefix$a]} {
2935 if {$path ne {}} usage
2936 set path [normalize_relpath $_prefix$a]
2938 } elseif {$a eq {--}} {
2940 if {$head ne {}} usage
2945 } elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
2946 if {$jump_spec ne {} || $head ne {}} usage
2947 set jump_spec [list $lnum]
2948 } elseif {$head eq {}} {
2949 if {$head ne {}} usage
2958 if {$head ne {} && $path eq {}} {
2959 set path [normalize_relpath $_prefix$head]
2966 if {[regexp {^[0-9a-f]{1,39}$} $head]} {
2968 set head [git rev-parse --verify $head]
2970 if {[tk windowingsystem] eq "win32"} {
2971 tk_messageBox -icon error -title [mc Error] -message $err
2978 set current_branch $head
2982 switch -- $subcommand {
2984 if {$jump_spec ne {}} usage
2986 if {$path ne {} && [file isdirectory $path]} {
2987 set head $current_branch
2993 browser::new $head $path
2996 if {$head eq {} && ![file exists $path]} {
2997 catch {wm withdraw .}
3001 -title [mc "git-gui: fatal error"] \
3002 -message [mc "fatal: cannot stat path %s: No such file or directory" $path]
3005 blame::new $head $path $jump_spec
3012 if {[llength $argv] != 0} {
3015 # fall through to setup UI for commits
3018 set err "usage: $argv0 \[{blame|browser|citool}\]"
3019 if {[tk windowingsystem] eq "win32"} {
3021 tk_messageBox -icon error -message $err \
3032 ${NS}::frame .branch
3033 if {!$use_ttk} {.branch configure -borderwidth 1 -relief sunken}
3034 ${NS}::label .branch.l1 \
3035 -text [mc "Current Branch:"] \
3038 ${NS}::label .branch.cb \
3039 -textvariable current_branch \
3042 pack .branch.l1 -side left
3043 pack .branch.cb -side left -fill x
3044 pack .branch -side top -fill x
3046 # -- Main Window Layout
3048 ${NS}::panedwindow .vpane -orient horizontal
3049 ${NS}::panedwindow .vpane.files -orient vertical
3051 .vpane add .vpane.files
3053 .vpane add .vpane.files -sticky nsew -height 100 -width 200
3055 pack .vpane -anchor n -side top -fill both -expand 1
3057 # -- Index File List
3059 ${NS}::frame .vpane.files.index -height 100 -width 200
3060 tlabel .vpane.files.index.title \
3061 -text [mc "Staged Changes (Will Commit)"] \
3062 -background lightgreen -foreground black
3063 text $ui_index -background white -foreground black \
3065 -width 20 -height 10 \
3067 -cursor $cursor_ptr \
3068 -xscrollcommand {.vpane.files.index.sx set} \
3069 -yscrollcommand {.vpane.files.index.sy set} \
3071 ${NS}::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
3072 ${NS}::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
3073 pack .vpane.files.index.title -side top -fill x
3074 pack .vpane.files.index.sx -side bottom -fill x
3075 pack .vpane.files.index.sy -side right -fill y
3076 pack $ui_index -side left -fill both -expand 1
3078 # -- Working Directory File List
3080 ${NS}::frame .vpane.files.workdir -height 100 -width 200
3081 tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
3082 -background lightsalmon -foreground black
3083 text $ui_workdir -background white -foreground black \
3085 -width 20 -height 10 \
3087 -cursor $cursor_ptr \
3088 -xscrollcommand {.vpane.files.workdir.sx set} \
3089 -yscrollcommand {.vpane.files.workdir.sy set} \
3091 ${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
3092 ${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
3093 pack .vpane.files.workdir.title -side top -fill x
3094 pack .vpane.files.workdir.sx -side bottom -fill x
3095 pack .vpane.files.workdir.sy -side right -fill y
3096 pack $ui_workdir -side left -fill both -expand 1
3098 .vpane.files add .vpane.files.workdir
3099 .vpane.files add .vpane.files.index
3101 .vpane.files paneconfigure .vpane.files.workdir -sticky news
3102 .vpane.files paneconfigure .vpane.files.index -sticky news
3105 foreach i [list $ui_index $ui_workdir] {
3107 $i tag conf in_diff -background [$i tag cget in_sel -background]
3111 # -- Diff and Commit Area
3113 ${NS}::frame .vpane.lower -height 300 -width 400
3114 ${NS}::frame .vpane.lower.commarea
3115 ${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1
3116 pack .vpane.lower.diff -fill both -expand 1
3117 pack .vpane.lower.commarea -side bottom -fill x
3118 .vpane add .vpane.lower
3119 if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew}
3121 # -- Commit Area Buttons
3123 ${NS}::frame .vpane.lower.commarea.buttons
3124 ${NS}::label .vpane.lower.commarea.buttons.l -text {} \
3127 pack .vpane.lower.commarea.buttons.l -side top -fill x
3128 pack .vpane.lower.commarea.buttons -side left -fill y
3130 ${NS}::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
3131 -command ui_do_rescan
3132 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
3133 lappend disable_on_lock \
3134 {.vpane.lower.commarea.buttons.rescan conf -state}
3136 ${NS}::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \
3138 pack .vpane.lower.commarea.buttons.incall -side top -fill x
3139 lappend disable_on_lock \
3140 {.vpane.lower.commarea.buttons.incall conf -state}
3142 if {![is_enabled nocommitmsg]} {
3143 ${NS}::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
3145 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
3148 ${NS}::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
3150 pack .vpane.lower.commarea.buttons.commit -side top -fill x
3151 lappend disable_on_lock \
3152 {.vpane.lower.commarea.buttons.commit conf -state}
3154 if {![is_enabled nocommit]} {
3155 ${NS}::button .vpane.lower.commarea.buttons.push -text [mc Push] \
3156 -command do_push_anywhere
3157 pack .vpane.lower.commarea.buttons.push -side top -fill x
3160 # -- Commit Message Buffer
3162 ${NS}::frame .vpane.lower.commarea.buffer
3163 ${NS}::frame .vpane.lower.commarea.buffer.header
3164 set ui_comm .vpane.lower.commarea.buffer.t
3165 set ui_coml .vpane.lower.commarea.buffer.header.l
3167 if {![is_enabled nocommit]} {
3168 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
3169 -text [mc "New Commit"] \
3170 -command do_select_commit_type \
3171 -variable selected_commit_type \
3173 lappend disable_on_lock \
3174 [list .vpane.lower.commarea.buffer.header.new conf -state]
3175 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
3176 -text [mc "Amend Last Commit"] \
3177 -command do_select_commit_type \
3178 -variable selected_commit_type \
3180 lappend disable_on_lock \
3181 [list .vpane.lower.commarea.buffer.header.amend conf -state]
3184 ${NS}::label $ui_coml \
3187 proc trace_commit_type {varname args} {
3188 global ui_coml commit_type
3189 switch -glob -- $commit_type {
3190 initial {set txt [mc "Initial Commit Message:"]}
3191 amend {set txt [mc "Amended Commit Message:"]}
3192 amend-initial {set txt [mc "Amended Initial Commit Message:"]}
3193 amend-merge {set txt [mc "Amended Merge Commit Message:"]}
3194 merge {set txt [mc "Merge Commit Message:"]}
3195 * {set txt [mc "Commit Message:"]}
3197 $ui_coml conf -text $txt
3199 trace add variable commit_type write trace_commit_type
3200 pack $ui_coml -side left -fill x
3202 if {![is_enabled nocommit]} {
3203 pack .vpane.lower.commarea.buffer.header.amend -side right
3204 pack .vpane.lower.commarea.buffer.header.new -side right
3207 text $ui_comm -background white -foreground black \
3211 -autoseparators true \
3213 -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
3215 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
3216 ${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
3217 -command [list $ui_comm yview]
3218 pack .vpane.lower.commarea.buffer.header -side top -fill x
3219 pack .vpane.lower.commarea.buffer.sby -side right -fill y
3220 pack $ui_comm -side left -fill y
3221 pack .vpane.lower.commarea.buffer -side left -fill y
3223 # -- Commit Message Buffer Context Menu
3225 set ctxm .vpane.lower.commarea.buffer.ctxm
3226 menu $ctxm -tearoff 0
3229 -command {tk_textCut $ui_comm}
3232 -command {tk_textCopy $ui_comm}
3235 -command {tk_textPaste $ui_comm}
3237 -label [mc Delete] \
3238 -command {catch {$ui_comm delete sel.first sel.last}}
3241 -label [mc "Select All"] \
3242 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
3244 -label [mc "Copy All"] \
3246 $ui_comm tag add sel 0.0 end
3247 tk_textCopy $ui_comm
3248 $ui_comm tag remove sel 0.0 end
3252 -label [mc "Sign Off"] \
3254 set ui_comm_ctxm $ctxm
3258 proc trace_current_diff_path {varname args} {
3259 global current_diff_path diff_actions file_states
3260 if {$current_diff_path eq {}} {
3266 set p $current_diff_path
3267 set s [mapdesc [lindex $file_states($p) 0] $p]
3269 set p [escape_path $p]
3273 .vpane.lower.diff.header.status configure -text $s
3274 .vpane.lower.diff.header.file configure -text $f
3275 .vpane.lower.diff.header.path configure -text $p
3276 foreach w $diff_actions {
3280 trace add variable current_diff_path write trace_current_diff_path
3282 gold_frame .vpane.lower.diff.header
3283 tlabel .vpane.lower.diff.header.status \
3286 -width $max_status_desc \
3289 tlabel .vpane.lower.diff.header.file \
3294 tlabel .vpane.lower.diff.header.path \
3299 pack .vpane.lower.diff.header.status -side left
3300 pack .vpane.lower.diff.header.file -side left
3301 pack .vpane.lower.diff.header.path -fill x
3302 set ctxm .vpane.lower.diff.header.ctxm
3303 menu $ctxm -tearoff 0
3311 -- $current_diff_path
3313 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3314 bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
3318 ${NS}::frame .vpane.lower.diff.body
3319 set ui_diff .vpane.lower.diff.body.t
3320 text $ui_diff -background white -foreground black \
3322 -width 80 -height 5 -wrap none \
3324 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
3325 -yscrollcommand {.vpane.lower.diff.body.sby set} \
3327 catch {$ui_diff configure -tabstyle wordprocessor}
3328 ${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
3329 -command [list $ui_diff xview]
3330 ${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
3331 -command [list $ui_diff yview]
3332 pack .vpane.lower.diff.body.sbx -side bottom -fill x
3333 pack .vpane.lower.diff.body.sby -side right -fill y
3334 pack $ui_diff -side left -fill both -expand 1
3335 pack .vpane.lower.diff.header -side top -fill x
3336 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
3338 foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
3339 $ui_diff tag configure clr4$n -background $c
3340 $ui_diff tag configure clri4$n -foreground $c
3341 $ui_diff tag configure clr3$n -foreground $c
3342 $ui_diff tag configure clri3$n -background $c
3344 $ui_diff tag configure clr1 -font font_diffbold
3346 $ui_diff tag conf d_info -foreground blue -font font_diffbold
3348 $ui_diff tag conf d_cr -elide true
3349 $ui_diff tag conf d_@ -font font_diffbold
3350 $ui_diff tag conf d_+ -foreground {#00a000}
3351 $ui_diff tag conf d_- -foreground red
3353 $ui_diff tag conf d_++ -foreground {#00a000}
3354 $ui_diff tag conf d_-- -foreground red
3355 $ui_diff tag conf d_+s \
3356 -foreground {#00a000} \
3357 -background {#e2effa}
3358 $ui_diff tag conf d_-s \
3360 -background {#e2effa}
3361 $ui_diff tag conf d_s+ \
3362 -foreground {#00a000} \
3364 $ui_diff tag conf d_s- \
3368 $ui_diff tag conf d< \
3369 -foreground orange \
3371 $ui_diff tag conf d= \
3372 -foreground orange \
3374 $ui_diff tag conf d> \
3375 -foreground orange \
3378 $ui_diff tag raise sel
3380 # -- Diff Body Context Menu
3383 proc create_common_diff_popup {ctxm} {
3385 -label [mc Refresh] \
3386 -command reshow_diff
3387 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3390 -command {tk_textCopy $ui_diff}
3391 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3393 -label [mc "Select All"] \
3394 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
3395 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3397 -label [mc "Copy All"] \
3399 $ui_diff tag add sel 0.0 end
3400 tk_textCopy $ui_diff
3401 $ui_diff tag remove sel 0.0 end
3403 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3406 -label [mc "Decrease Font Size"] \
3407 -command {incr_font_size font_diff -1}
3408 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3410 -label [mc "Increase Font Size"] \
3411 -command {incr_font_size font_diff 1}
3412 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3416 build_encoding_menu $emenu [list force_diff_encoding]
3418 -label [mc "Encoding"] \
3420 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3422 $ctxm add command -label [mc "Options..."] \
3426 set ctxm .vpane.lower.diff.body.ctxm
3427 menu $ctxm -tearoff 0
3429 -label [mc "Apply/Reverse Hunk"] \
3430 -command {apply_hunk $cursorX $cursorY}
3431 set ui_diff_applyhunk [$ctxm index last]
3432 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
3434 -label [mc "Apply/Reverse Line"] \
3435 -command {apply_range_or_line $cursorX $cursorY; do_rescan}
3436 set ui_diff_applyline [$ctxm index last]
3437 lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
3440 -label [mc "Show Less Context"] \
3441 -command show_less_context
3442 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3444 -label [mc "Show More Context"] \
3445 -command show_more_context
3446 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3448 create_common_diff_popup $ctxm
3450 set ctxmmg .vpane.lower.diff.body.ctxmmg
3451 menu $ctxmmg -tearoff 0
3452 $ctxmmg add command \
3453 -label [mc "Run Merge Tool"] \
3454 -command {merge_resolve_tool}
3455 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3456 $ctxmmg add separator
3457 $ctxmmg add command \
3458 -label [mc "Use Remote Version"] \
3459 -command {merge_resolve_one 3}
3460 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3461 $ctxmmg add command \
3462 -label [mc "Use Local Version"] \
3463 -command {merge_resolve_one 2}
3464 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3465 $ctxmmg add command \
3466 -label [mc "Revert To Base"] \
3467 -command {merge_resolve_one 1}
3468 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3469 $ctxmmg add separator
3470 $ctxmmg add command \
3471 -label [mc "Show Less Context"] \
3472 -command show_less_context
3473 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3474 $ctxmmg add command \
3475 -label [mc "Show More Context"] \
3476 -command show_more_context
3477 lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3478 $ctxmmg add separator
3479 create_common_diff_popup $ctxmmg
3481 set ctxmsm .vpane.lower.diff.body.ctxmsm
3482 menu $ctxmsm -tearoff 0
3483 $ctxmsm add command \
3484 -label [mc "Visualize These Changes In The Submodule"] \
3485 -command {do_gitk -- true}
3486 lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3487 $ctxmsm add command \
3488 -label [mc "Visualize Current Branch History In The Submodule"] \
3489 -command {do_gitk {} true}
3490 lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3491 $ctxmsm add command \
3492 -label [mc "Visualize All Branch History In The Submodule"] \
3493 -command {do_gitk --all true}
3494 lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3495 $ctxmsm add separator
3496 $ctxmsm add command \
3497 -label [mc "Start git gui In The Submodule"] \
3498 -command {do_git_gui}
3499 lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3500 $ctxmsm add separator
3501 create_common_diff_popup $ctxmsm
3503 proc has_textconv {path} {
3504 if {[is_config_false gui.textconv]} {
3507 set filter [gitattr $path diff set]
3508 set textconv [get_config [join [list diff $filter textconv] .]]
3509 if {$filter ne {set} && $textconv ne {}} {
3516 proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
3517 global current_diff_path file_states
3520 if {[info exists file_states($current_diff_path)]} {
3521 set state [lindex $file_states($current_diff_path) 0]
3525 if {[string first {U} $state] >= 0} {
3526 tk_popup $ctxmmg $X $Y
3527 } elseif {$::is_submodule_diff} {
3528 tk_popup $ctxmsm $X $Y
3530 set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
3531 if {$::ui_index eq $::current_diff_side} {
3532 set l [mc "Unstage Hunk From Commit"]
3534 set t [mc "Unstage Lines From Commit"]
3536 set t [mc "Unstage Line From Commit"]
3539 set l [mc "Stage Hunk For Commit"]
3541 set t [mc "Stage Lines For Commit"]
3543 set t [mc "Stage Line For Commit"]
3547 || $current_diff_path eq {}
3550 || [string match {?T} $state]
3551 || [string match {T?} $state]
3552 || [has_textconv $current_diff_path]} {
3557 $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
3558 $ctxm entryconf $::ui_diff_applyline -state $s -label $t
3559 tk_popup $ctxm $X $Y
3562 bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg $ctxmsm %x %y %X %Y]
3566 set main_status [::status_bar::new .status]
3567 pack .status -anchor w -side bottom -fill x
3568 $main_status show [mc "Initializing..."]
3572 proc on_ttk_pane_mapped {w pane pos} {
3574 after 0 [list after idle [list $w sashpos $pane $pos]]
3576 proc on_tk_pane_mapped {w pane x y} {
3578 after 0 [list after idle [list $w sash place $pane $x $y]]
3580 proc on_application_mapped {} {
3581 global repo_config use_ttk
3583 set gm $repo_config(gui.geometry)
3586 [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
3587 bind .vpane.files <Map> \
3588 [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
3591 [list on_tk_pane_mapped %W 0 \
3593 [lindex [.vpane sash coord 0] 1]]
3594 bind .vpane.files <Map> \
3595 [list on_tk_pane_mapped %W 0 \
3596 [lindex [.vpane.files sash coord 0] 0] \
3599 wm geometry . [lindex $gm 0]
3601 if {[info exists repo_config(gui.geometry)]} {
3602 bind . <Map> [list on_application_mapped]
3603 wm geometry . [lindex $repo_config(gui.geometry) 0]
3606 # -- Load window state
3608 if {[info exists repo_config(gui.wmstate)]} {
3609 catch {wm state . $repo_config(gui.wmstate)}
3614 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
3615 bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
3616 bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
3617 bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
3618 bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
3619 bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
3620 bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
3621 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
3622 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
3623 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
3624 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
3625 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
3626 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
3627 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
3628 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
3629 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3630 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
3631 bind $ui_comm <$M1B-Key-minus> {show_less_context;break}
3632 bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
3633 bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
3634 bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
3635 bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
3637 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
3638 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
3639 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
3640 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
3641 bind $ui_diff <$M1B-Key-v> {break}
3642 bind $ui_diff <$M1B-Key-V> {break}
3643 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3644 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
3645 bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
3646 bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
3647 bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
3648 bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
3649 bind $ui_diff <Key-k> {catch {%W yview scroll -1 units};break}
3650 bind $ui_diff <Key-j> {catch {%W yview scroll 1 units};break}
3651 bind $ui_diff <Key-h> {catch {%W xview scroll -1 units};break}
3652 bind $ui_diff <Key-l> {catch {%W xview scroll 1 units};break}
3653 bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
3654 bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
3655 bind $ui_diff <Button-1> {focus %W}
3657 if {[is_enabled branch]} {
3658 bind . <$M1B-Key-n> branch_create::dialog
3659 bind . <$M1B-Key-N> branch_create::dialog
3660 bind . <$M1B-Key-o> branch_checkout::dialog
3661 bind . <$M1B-Key-O> branch_checkout::dialog
3662 bind . <$M1B-Key-m> merge::dialog
3663 bind . <$M1B-Key-M> merge::dialog
3665 if {[is_enabled transport]} {
3666 bind . <$M1B-Key-p> do_push_anywhere
3667 bind . <$M1B-Key-P> do_push_anywhere
3670 bind . <Key-F5> ui_do_rescan
3671 bind . <$M1B-Key-r> ui_do_rescan
3672 bind . <$M1B-Key-R> ui_do_rescan
3673 bind . <$M1B-Key-s> do_signoff
3674 bind . <$M1B-Key-S> do_signoff
3675 bind . <$M1B-Key-t> do_add_selection
3676 bind . <$M1B-Key-T> do_add_selection
3677 bind . <$M1B-Key-j> do_revert_selection
3678 bind . <$M1B-Key-J> do_revert_selection
3679 bind . <$M1B-Key-i> do_add_all
3680 bind . <$M1B-Key-I> do_add_all
3681 bind . <$M1B-Key-minus> {show_less_context;break}
3682 bind . <$M1B-Key-KP_Subtract> {show_less_context;break}
3683 bind . <$M1B-Key-equal> {show_more_context;break}
3684 bind . <$M1B-Key-plus> {show_more_context;break}
3685 bind . <$M1B-Key-KP_Add> {show_more_context;break}
3686 bind . <$M1B-Key-Return> do_commit
3687 foreach i [list $ui_index $ui_workdir] {
3688 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
3689 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
3690 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
3694 set file_lists($ui_index) [list]
3695 set file_lists($ui_workdir) [list]
3697 wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
3698 focus -force $ui_comm
3700 # -- Warn the user about environmental problems. Cygwin's Tcl
3701 # does *not* pass its env array onto any processes it spawns.
3702 # This means that git processes get none of our environment.
3707 set msg [mc "Possible environment issues exist.
3709 The following environment variables are probably
3710 going to be ignored by any Git subprocess run
3714 foreach name [array names env] {
3715 switch -regexp -- $name {
3716 {^GIT_INDEX_FILE$} -
3717 {^GIT_OBJECT_DIRECTORY$} -
3718 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
3720 {^GIT_EXTERNAL_DIFF$} -
3724 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
3725 append msg " - $name\n"
3728 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
3729 append msg " - $name\n"
3731 set suggest_user $name
3735 if {$ignored_env > 0} {
3737 This is due to a known issue with the
3738 Tcl binary distributed by Cygwin."]
3740 if {$suggest_user ne {}} {
3743 A good replacement for %s
3744 is placing values for the user.name and
3745 user.email settings into your personal
3751 unset ignored_env msg suggest_user name
3754 # -- Only initialize complex UI if we are going to stay running.
3756 if {[is_enabled transport]} {
3759 set n [.mbar.remote index end]
3760 populate_remotes_menu
3761 set n [expr {[.mbar.remote index end] - $n}]
3763 if {[.mbar.remote type 0] eq "tearoff"} { incr n }
3764 .mbar.remote insert $n separator
3769 if {[winfo exists $ui_comm]} {
3770 set GITGUI_BCK_exists [load_message GITGUI_BCK]
3772 # -- If both our backup and message files exist use the
3773 # newer of the two files to initialize the buffer.
3775 if {$GITGUI_BCK_exists} {
3776 set m [gitdir GITGUI_MSG]
3777 if {[file isfile $m]} {
3778 if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
3779 catch {file delete [gitdir GITGUI_MSG]}
3781 $ui_comm delete 0.0 end
3783 $ui_comm edit modified false
3784 catch {file delete [gitdir GITGUI_BCK]}
3785 set GITGUI_BCK_exists 0
3791 proc backup_commit_buffer {} {
3792 global ui_comm GITGUI_BCK_exists
3794 set m [$ui_comm edit modified]
3795 if {$m || $GITGUI_BCK_exists} {
3796 set msg [string trim [$ui_comm get 0.0 end]]
3797 regsub -all -line {[ \r\t]+$} $msg {} msg
3800 if {$GITGUI_BCK_exists} {
3801 catch {file delete [gitdir GITGUI_BCK]}
3802 set GITGUI_BCK_exists 0
3806 set fd [open [gitdir GITGUI_BCK] w]
3807 puts -nonewline $fd $msg
3809 set GITGUI_BCK_exists 1
3813 $ui_comm edit modified false
3816 set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
3819 backup_commit_buffer
3821 # -- If the user has aspell available we can drive it
3822 # in pipe mode to spellcheck the commit message.
3824 set spell_cmd [list |]
3825 set spell_dict [get_config gui.spellingdictionary]
3826 lappend spell_cmd aspell
3827 if {$spell_dict ne {}} {
3828 lappend spell_cmd --master=$spell_dict
3830 lappend spell_cmd --mode=none
3831 lappend spell_cmd --encoding=utf-8
3832 lappend spell_cmd pipe
3833 if {$spell_dict eq {none}
3834 || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
3835 bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
3837 set ui_comm_spell [spellcheck::init \
3843 unset -nocomplain spell_cmd spell_fd spell_err spell_dict
3846 lock_index begin-read
3847 if {![winfo ismapped .]} {
3851 if {[is_enabled initialamend]} {
3857 if {[is_enabled nocommitmsg]} {
3858 $ui_comm configure -state disabled -background gray
3861 if {[is_enabled multicommit]} {
3864 if {[is_enabled retcode]} {
3865 bind . <Destroy> {+terminate_me %W}
3867 if {$picked && [is_config_true gui.autoexplore]} {
3873 # indent-tabs-mode: t