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
, see
<http
://www.gnu.org
/licenses
/>.
}]
29 ######################################################################
31 ## Tcl/Tk sanity check
33 if {[catch
{package require Tcl
8.5} err
]
34 ||
[catch
{package require Tk
8.5} err
]
40 -title "git-gui: fatal error" \
45 catch
{rename send
{}} ; # What an evil concept...
47 ######################################################################
49 ## Enabling platform-specific code paths
52 if {[tk windowingsystem
] eq
{aqua
}} {
59 if {$
::tcl_platform
(platform
) eq
{windows
}} {
68 if {$_iscygwin eq
{}} {
69 if {[string match
"CYGWIN_*" $
::tcl_platform
(os
)]} {
78 ######################################################################
83 proc _which
{what args
} {
84 global env _search_exe _search_path
86 if {$_search_path eq
{}} {
88 set gitguidir
[file dirname [info
script]]
89 regsub
-all ";" $gitguidir "\\;" gitguidir
90 set env
(PATH
) "$gitguidir;$env(PATH)"
91 set _search_path
[split $env(PATH
) {;}]
92 # Skip empty `PATH` elements
93 set _search_path
[lsearch
-all -inline -not -exact \
97 set _search_path
[split $env(PATH
) :]
102 if {[is_Windows
] && [lsearch
-exact $args -script] >= 0} {
105 set suffix
$_search_exe
108 foreach p
$_search_path {
109 set p
[file join $p $what$suffix]
110 if {[file exists
$p]} {
111 return [file normalize
$p]
117 proc sanitize_command_line
{command_line from_index
} {
119 while {$i < [llength
$command_line]} {
120 set cmd
[lindex
$command_line $i]
121 if {[file pathtype
$cmd] ne
"absolute"} {
122 set fullpath
[_which
$cmd]
123 if {$fullpath eq
""} {
124 throw
{NOT-FOUND
} "$cmd not found in PATH"
126 lset command_line
$i $fullpath
129 # handle piped commands, e.g. `exec A | B`
130 for {incr i
} {$i < [llength
$command_line]} {incr i
} {
131 if {[lindex
$command_line $i] eq
"|"} {
140 # Override `exec` to avoid unsafe PATH lookup
142 rename
exec real_exec
146 for {set i
0} {$i < [llength
$args]} {incr i
} {
147 set arg
[lindex
$args $i]
152 if {[string range
$arg 0 0] ne
"-"} {
156 set args
[sanitize_command_line
$args $i]
157 uplevel
1 real_exec
$args
160 # Override `open` to avoid unsafe PATH lookup
162 rename open real_open
165 set arg0
[lindex
$args 0]
166 if {[string range
$arg0 0 0] eq
"|"} {
167 set command_line
[string trim
[string range
$arg0 1 end
]]
168 lset args
0 "| [sanitize_command_line $command_line 0]"
170 uplevel
1 real_open
$args
173 ######################################################################
175 ## locate our library
177 if { [info exists
::env
(GIT_GUI_LIB_DIR
) ] } {
178 set oguilib $
::env
(GIT_GUI_LIB_DIR
)
180 set oguilib
{@@GITGUI_LIBDIR@@
}
182 set oguirel
{@@GITGUI_RELATIVE@@
}
183 if {$oguirel eq
{1}} {
184 set oguilib
[file dirname [file normalize
$argv0]]
185 if {[file tail $oguilib] eq
{git-core
}} {
186 set oguilib
[file dirname $oguilib]
188 set oguilib
[file dirname $oguilib]
189 set oguilib
[file join $oguilib share git-gui lib
]
190 set oguimsg
[file join $oguilib msgs
]
191 } elseif
{[string match @@
* $oguirel]} {
192 set oguilib
[file join [file dirname [file normalize
$argv0]] lib
]
193 set oguimsg
[file join [file dirname [file normalize
$argv0]] po
]
195 set oguimsg
[file join $oguilib msgs
]
199 ######################################################################
201 ## enable verbose loading?
203 if {![catch
{set _verbose
$env(GITGUI_VERBOSE
)}]} {
205 rename auto_load real__auto_load
206 proc auto_load
{name args
} {
207 puts stderr
"auto_load $name"
208 return [uplevel
1 real__auto_load
$name $args]
210 rename
source real__source
212 puts stderr
"source $args"
213 uplevel
1 [linsert
$args 0 real__source
]
215 if {[tk windowingsystem
] eq
"win32"} { console show
}
218 ######################################################################
220 ## Internationalization (i18n) through msgcat and gettext. See
221 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
223 package require msgcat
225 # Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
226 if {[tk windowingsystem
] eq
"win32"
227 && [package vcompare
[package provide msgcat
] 1.4.4] < 0
229 proc _mc_update_locale
{} {
230 set key
{HKEY_CURRENT_USER\Control Panel\Desktop
}
232 package require registry
233 set uilocale
[registry get
$key "PreferredUILanguages"]
234 msgcat
::ConvertLocale
[string map
{- _
} [lindex
$uilocale 0]]
236 if {[string length
$uilocale] > 0} {
237 msgcat
::mclocale
$uilocale
244 proc _mc_trim
{fmt} {
245 set cmk
[string first @@
$fmt]
247 return [string range
$fmt 0 [expr {$cmk - 1}]]
252 proc mc
{en_fmt args
} {
253 set fmt [_mc_trim
[::msgcat
::mc
$en_fmt]]
254 if {[catch
{set msg
[eval [list format
$fmt] $args]} err
]} {
255 set msg
[eval [list format
[_mc_trim
$en_fmt]] $args]
261 return [join $args {}]
264 ::msgcat
::mcload
$oguimsg
267 ######################################################################
269 ## On Mac, bring the current Wish process window to front
271 if {[tk windowingsystem
] eq
"aqua"} {
273 exec osascript
-e [format
{
274 tell application
"System Events"
275 set frontmost of processes whose unix id is
%d to true
281 ######################################################################
285 set _appname
{Git Gui
}
292 set _shellpath
{@@SHELL_PATH@@
}
294 set _trace
[lsearch
-exact $argv --trace]
296 set argv
[lreplace
$argv $_trace $_trace]
298 if {[tk windowingsystem
] eq
"win32"} { console show
}
303 # variable for the last merged branch (useful for a default when deleting
305 set _last_merged_branch
{}
308 global _shellpath env
309 if {[string match @@
* $_shellpath]} {
310 if {[info exists env
(SHELL
)]} {
329 return [eval [list
file join $_gitdir] $args]
332 proc gitexec
{args
} {
334 if {$_gitexec eq
{}} {
335 if {[catch
{set _gitexec
[git
--exec-path]} err
]} {
336 error
"Git not installed?\n\n$err"
338 set _gitexec
[file normalize
$_gitexec]
343 return [eval [list
file join $_gitexec] $args]
346 proc githtmldir
{args
} {
348 if {$_githtmldir eq
{}} {
349 if {[catch
{set _githtmldir
[git
--html-path]}]} {
350 # Git not installed or option not yet supported
353 set _githtmldir
[file normalize
$_githtmldir]
358 return [eval [list
file join $_githtmldir] $args]
365 proc is_enabled
{option
} {
366 global enabled_options
367 if {[catch
{set on
$enabled_options($option)}]} {return 0}
371 proc enable_option
{option
} {
372 global enabled_options
373 set enabled_options
($option) 1
376 proc disable_option
{option
} {
377 global enabled_options
378 set enabled_options
($option) 0
381 ######################################################################
385 proc is_many_config
{name
} {
386 switch
-glob -- $name {
396 proc is_config_true
{name
} {
398 if {[catch
{set v
$repo_config($name)}]} {
401 set v
[string tolower
$v]
402 if {$v eq
{} ||
$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes} ||
$v eq
{on
}} {
409 proc is_config_false
{name
} {
411 if {[catch
{set v
$repo_config($name)}]} {
414 set v
[string tolower
$v]
415 if {$v eq
{false
} ||
$v eq
{0} ||
$v eq
{no
} ||
$v eq
{off
}} {
422 proc get_config
{name
} {
424 if {[catch
{set v
$repo_config($name)}]} {
436 if {$_isbare eq
{}} {
438 set _bare
[git rev-parse
--is-bare-repository]
440 true
{ set _isbare
1 }
441 false
{ set _isbare
0}
445 if {[is_config_true core.bare
]
446 ||
($_gitworktree eq
{}
447 && [lindex
[file split $_gitdir] end
] ne
{.git
})} {
457 ######################################################################
461 proc _trace_exec
{cmd
} {
462 if {!$
::_trace
} return
468 if {[regexp
{[ \t\r\n'"$?*]} $v]} {
476 #'" fix poor old emacs font-lock mode
478 proc _git_cmd {name} {
481 if {[catch {set v $_git_cmd_path($name)}]} {
485 --exec-path { return [list $::_git $name] }
488 set p [gitexec git-$name$::_search_exe]
489 if {[file exists $p]} {
491 } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
492 # Try to determine what sort of magic will make
493 # git-$name go and do its thing, because native
494 # Tcl on Windows doesn't know it.
496 set p [gitexec git-$name]
501 switch -glob -- [lindex $s 0] {
503 #!*perl { set i perl }
504 #!*python { set i python }
505 default { error "git-
$name is not supported
: $s" }
509 if {![info exists interp]} {
510 set interp [_which $i]
513 error "git-
$name requires
$i (not
in PATH
)"
515 set v [concat [list $interp] [lrange $s 1 end] [list $p]]
517 # Assume it is builtin to git somehow and we
518 # aren't actually able to see a file for it.
520 set v [list $::_git $name]
522 set _git_cmd_path($name) $v
527 # Test a file for a hashbang to identify executable scripts on Windows.
528 proc is_shellscript {filename} {
529 if {![file exists $filename]} {return 0}
530 set f [open $filename r]
531 fconfigure $f -encoding binary
532 set magic [read $f 2]
534 return [expr {$magic eq "#!"}]
537 # Run a command connected via pipes on stdout.
538 # This is for use with textconv filters and uses sh -c "..." to allow it to
539 # contain a command with arguments. On windows we must check for shell
540 # scripts specifically otherwise just call the filter command.
541 proc open_cmd_pipe
{cmd path
} {
543 if {![file executable
[shellpath
]]} {
544 set exe
[auto_execok
[lindex
$cmd 0]]
545 if {[is_shellscript
[lindex
$exe 0]]} {
546 set run
[linsert
[auto_execok sh
] end
-c "$cmd \"\$0\"" $path]
548 set run
[concat
$exe [lrange
$cmd 1 end
] $path]
551 set run
[list
[shellpath
] -c "$cmd \"\$0\"" $path]
553 return [open |
$run r
]
556 proc _lappend_nice
{cmd_var
} {
560 if {![info exists _nice
]} {
561 set _nice
[_which nice
]
562 if {[catch
{exec $_nice git version
}]} {
564 } elseif
{[is_Windows
] && [file dirname $_nice] ne
[file dirname $
::_git
]} {
574 set fd
[eval [list git_read
] $args]
575 fconfigure
$fd -translation binary
-encoding utf-8
576 set result
[string trimright
[read $fd] "\n"]
579 puts stderr
"< $result"
584 proc _open_stdout_stderr
{cmd
} {
587 set fd
[open
[concat
[list |
] $cmd] r
]
589 if { [lindex
$cmd end
] eq
{2>@
1}
590 && $err eq
{can not
find channel named
"1"}
592 # Older versions of Tcl 8.4 don't have this 2>@1 IO
593 # redirect operator. Fallback to |& cat for those.
594 # The command was not actually started, so its safe
595 # to try to start it a second time.
597 set fd
[open
[concat \
599 [lrange
$cmd 0 end-1
] \
606 fconfigure
$fd -eofchar {}
610 proc git_read
{args
} {
614 switch
-- [lindex
$args 0] {
629 set args
[lrange
$args 1 end
]
632 set cmdp
[_git_cmd
[lindex
$args 0]]
633 set args
[lrange
$args 1 end
]
635 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
638 proc git_write
{args
} {
642 switch
-- [lindex
$args 0] {
653 set args
[lrange
$args 1 end
]
656 set cmdp
[_git_cmd
[lindex
$args 0]]
657 set args
[lrange
$args 1 end
]
659 _trace_exec
[concat
$opt $cmdp $args]
660 return [open
[concat
[list |
] $opt $cmdp $args] w
]
663 proc githook_read
{hook_name args
} {
664 set pchook
[gitdir hooks
$hook_name]
667 # On Windows [file executable] might lie so we need to ask
668 # the shell if the hook is executable. Yes that's annoying.
672 if {![info exists interp
]} {
673 set interp
[_which sh
]
676 error
"hook execution requires sh (not in PATH)"
679 set scr
{if test -x "$1";then exec "$@";fi}
680 set sh_c
[list
$interp -c $scr $interp $pchook]
681 return [_open_stdout_stderr
[concat
$sh_c $args]]
684 if {[file executable
$pchook]} {
685 return [_open_stdout_stderr
[concat
[list
$pchook] $args]]
691 proc kill_file_process
{fd
} {
692 set process
[pid
$fd]
696 exec taskkill
/pid
$process
703 proc gitattr
{path attr default
} {
704 if {[catch
{set r
[git check-attr
$attr -- $path]}]} {
707 set r
[join [lrange
[split $r :] 2 end
] :]
710 if {$r eq
{unspecified
}} {
717 regsub
-all ' $value "'\\''" value
721 proc load_current_branch {} {
722 global current_branch is_detached
724 set fd [open [gitdir HEAD] r]
725 fconfigure $fd -translation binary -encoding utf-8
726 if {[gets $fd ref] < 1} {
731 set pfx {ref: refs/heads/}
732 set len [string length $pfx]
733 if {[string equal -length $len $pfx $ref]} {
734 # We're on a branch. It might not exist. But
735 # HEAD looks good enough to be a branch.
737 set current_branch [string range $ref $len end]
740 # Assume this is a detached head.
742 set current_branch HEAD
747 auto_load tk_optionMenu
748 rename tk_optionMenu real__tkOptionMenu
749 proc tk_optionMenu {w varName args} {
750 set m [eval real__tkOptionMenu $w $varName $args]
751 $m configure -font font_ui
752 $w configure -font font_ui
756 proc rmsel_tag {text} {
758 -background [$text cget -background] \
759 -foreground [$text cget -foreground] \
761 bind $text <Motion> break
767 bind . <Visibility> {
768 bind . <Visibility> {}
773 wm iconbitmap . -default $oguilib/git-gui.ico
774 set ::tk::AlwaysShowSelection 1
775 bind . <Control-F2> {console show}
777 # Spoof an X11 display for SSH
778 if {![info exists env(DISPLAY)]} {
779 set env(DISPLAY) :9999
783 image create photo gitlogo -width 16 -height 16
785 gitlogo put #33CC33 -to 7 0 9 2
786 gitlogo put #33CC33 -to 4 2 12 4
787 gitlogo put #33CC33 -to 7 4 9 6
788 gitlogo put #CC3333 -to 4 6 12 8
789 gitlogo put gray26 -to 4 9 6 10
790 gitlogo put gray26 -to 3 10 6 12
791 gitlogo put gray26 -to 8 9 13 11
792 gitlogo put gray26 -to 8 11 10 12
793 gitlogo put gray26 -to 11 11 13 14
794 gitlogo put gray26 -to 3 12 5 14
795 gitlogo put gray26 -to 5 13
796 gitlogo put gray26 -to 10 13
797 gitlogo put gray26 -to 4 14 12 15
798 gitlogo put gray26 -to 5 15 11 16
801 image create photo gitlogo32 -width 32 -height 32
802 gitlogo32 copy gitlogo -zoom 2 2
804 wm iconphoto . -default gitlogo gitlogo32
808 ######################################################################
814 if {[lsearch -exact [font names] TkDefaultFont] != -1} {
815 eval [linsert [font actual TkDefaultFont] 0 font configure font_ui]
816 eval [linsert [font actual TkFixedFont] 0 font create font_diff]
818 font create font_diff -family Courier -size 10
821 eval font configure font_ui [font actual [.dummy cget -font]]
826 font create font_uiitalic
827 font create font_uibold
828 font create font_diffbold
829 font create font_diffitalic
831 foreach class {Button Checkbutton Entry Label
832 Labelframe Listbox Message
833 Radiobutton Spinbox Text} {
834 option add *$class.font font_ui
837 option add *Menu.font font_ui
838 option add *Entry.borderWidth 1 startupFile
839 option add *Entry.relief sunken startupFile
840 option add *RadioButton.anchor w startupFile
844 if {[is_Windows] || [is_MacOSX]} {
845 option add *Menu.tearOff 0
856 proc bind_button3 {w cmd} {
857 bind $w <Any-Button-3> $cmd
859 # Mac OS X sends Button-2 on right click through three-button mouse,
860 # or through trackpad right-clicking (two-finger touch + click).
861 bind $w <Any-Button-2> $cmd
862 bind $w <Control-Button-1> $cmd
866 proc apply_config {} {
867 global repo_config font_descs
869 foreach option $font_descs {
870 set name [lindex $option 0]
871 set font [lindex $option 1]
874 foreach {cn cv} $repo_config(gui.$name) {
875 if {$cn eq {-weight}} {
878 font configure $font $cn $cv
881 font configure $font -weight normal
884 error_popup [strcat [mc "Invalid font specified
in %s
:" "gui.
$name"] "\n\n$err"]
886 foreach {cn cv} [font configure $font] {
887 font configure ${font}bold $cn $cv
888 font configure ${font}italic $cn $cv
890 font configure ${font}bold -weight bold
891 font configure ${font}italic -slant italic
897 if {$repo_config(gui.usettk)} {
898 set use_ttk [package vsatisfies [package provide Tk] 8.5]
901 bind [winfo class .] <<ThemeChanged>> [list InitTheme]
903 color::sync_with_theme
908 set default_config(branch.autosetupmerge) true
909 set default_config(merge.tool) {}
910 set default_config(mergetool.keepbackup) true
911 set default_config(merge.diffstat) true
912 set default_config(merge.summary) false
913 set default_config(merge.verbosity) 2
914 set default_config(user.name) {}
915 set default_config(user.email) {}
917 set default_config(gui.encoding) [encoding system]
918 set default_config(gui.matchtrackingbranch) false
919 set default_config(gui.textconv) true
920 set default_config(gui.pruneduringfetch) false
921 set default_config(gui.trustmtime) false
922 set default_config(gui.fastcopyblame) false
923 set default_config(gui.maxrecentrepo) 10
924 set default_config(gui.copyblamethreshold) 40
925 set default_config(gui.blamehistoryctx) 7
926 set default_config(gui.diffcontext) 5
927 set default_config(gui.diffopts) {}
928 set default_config(gui.commitmsgwidth) 75
929 set default_config(gui.newbranchtemplate) {}
930 set default_config(gui.spellingdictionary) {}
931 set default_config(gui.fontui) [font configure font_ui]
932 set default_config(gui.fontdiff) [font configure font_diff]
933 # TODO: this option should be added to the git-config documentation
934 set default_config(gui.maxfilesdisplayed) 5000
935 set default_config(gui.usettk) 1
936 set default_config(gui.warndetachedcommit) 1
937 set default_config(gui.tabsize) 8
939 {fontui font_ui {mc "Main Font
"}}
940 {fontdiff font_diff {mc "Diff
/Console Font
"}}
942 set default_config(gui.stageuntracked) ask
943 set default_config(gui.displayuntracked) true
945 ######################################################################
949 set _git [_which git]
951 catch {wm withdraw .}
955 -title [mc "git-gui
: fatal error
"] \
956 -message [mc "Cannot
find git
in PATH.
"]
960 ######################################################################
964 if {[catch {set _git_version [git --version]} err]} {
965 catch {wm withdraw .}
969 -title [mc "git-gui
: fatal error
"] \
970 -message "Cannot determine Git version
:
974 [appname
] requires Git
1.5.0 or later.
"
977 if {![regsub {^git version } $_git_version {} _git_version]} {
978 catch {wm withdraw .}
982 -title [mc "git-gui
: fatal error
"] \
983 -message [strcat [mc "Cannot parse Git version string
:"] "\n\n$_git_version"]
987 proc get_trimmed_version {s} {
989 foreach x [split $s -._] {
990 if {[string is integer -strict $x]} {
998 set _real_git_version $_git_version
999 set _git_version [get_trimmed_version $_git_version]
1001 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
1002 catch {wm withdraw .}
1003 if {[tk_messageBox \
1007 -title "[appname
]: warning
" \
1008 -message [mc "Git version cannot be determined.
1010 %s claims it is version
'%s'.
1012 %s requires
at least Git
1.5.0 or later.
1014 Assume
'%s' is version
1.5.0?
1015 " $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} {
1016 set _git_version 1.5.0
1021 unset _real_git_version
1023 proc git-version {args} {
1026 switch [llength $args] {
1028 return $_git_version
1032 set op [lindex $args 0]
1033 set vr [lindex $args 1]
1034 set cm [package vcompare $_git_version $vr]
1035 return [expr $cm $op 0]
1039 set type [lindex $args 0]
1040 set name [lindex $args 1]
1041 set parm [lindex $args 2]
1042 set body [lindex $args 3]
1044 if {($type ne {proc} && $type ne {method})} {
1045 error "Invalid arguments to git-version
"
1047 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
1048 error "Last arm of
$type $name must be default
"
1051 foreach {op vr cb} [lrange $body 0 end-2] {
1052 if {[git-version $op $vr]} {
1053 return [uplevel [list $type $name $parm $cb]]
1057 return [uplevel [list $type $name $parm [lindex $body end]]]
1061 error "git-version
>= x
"
1067 if {[git-version < 1.5]} {
1068 catch {wm withdraw .}
1072 -title [mc "git-gui
: fatal error
"] \
1073 -message "[appname
] requires Git
1.5.0 or later.
1075 You are using
[git-version
]:
1081 ######################################################################
1083 ## configure our library
1085 set idx [file join $oguilib tclIndex]
1086 if {[catch {set fd [open $idx r]} err]} {
1087 catch {wm withdraw .}
1091 -title [mc "git-gui
: fatal error
"] \
1095 if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
1097 while {[gets $fd n] >= 0} {
1098 if {$n ne {} && ![string match #* $n]} {
1110 if {[lsearch -exact $loaded $p] >= 0} continue
1111 source [file join $oguilib $p]
1116 set auto_path [concat [list $oguilib] $auto_path]
1118 unset -nocomplain idx fd
1120 ######################################################################
1122 ## config file parsing
1124 git-version proc _parse_config {arr_name args} {
1131 [list git_read config] \
1133 [list --null --list]]
1134 fconfigure $fd_rc -translation binary -encoding utf-8
1135 set buf [read $fd_rc]
1138 foreach line [split $buf "\
0"] {
1139 if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} {
1140 if {[is_many_config $name]} {
1141 lappend arr($name) $value
1143 set arr($name) $value
1145 } elseif {[regexp {^([^\n]+)$} $line line name]} {
1146 # no value given, but interpreting them as
1147 # boolean will be handled as true
1156 set fd_rc [eval [list git_read config --list] $args]
1157 while {[gets $fd_rc line] >= 0} {
1158 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
1159 if {[is_many_config $name]} {
1160 lappend arr($name) $value
1162 set arr($name) $value
1164 } elseif {[regexp {^([^=]+)$} $line line name]} {
1165 # no value given, but interpreting them as
1166 # boolean will be handled as true
1175 proc load_config {include_global} {
1176 global repo_config global_config system_config default_config
1178 if {$include_global} {
1179 _parse_config system_config --system
1180 _parse_config global_config --global
1182 _parse_config repo_config
1184 foreach name [array names default_config] {
1185 if {[catch {set v $system_config($name)}]} {
1186 set system_config($name) $default_config($name)
1189 foreach name [array names system_config] {
1190 if {[catch {set v $global_config($name)}]} {
1191 set global_config($name) $system_config($name)
1193 if {[catch {set v $repo_config($name)}]} {
1194 set repo_config($name) $system_config($name)
1199 ######################################################################
1201 ## feature option selection
1203 if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
1208 if {$subcommand eq {gui.sh}} {
1211 if {$subcommand eq {gui} && [llength $argv] > 0} {
1212 set subcommand [lindex $argv 0]
1213 set argv [lrange $argv 1 end]
1216 enable_option multicommit
1217 enable_option branch
1218 enable_option transport
1221 switch -- $subcommand {
1226 disable_option multicommit
1227 disable_option branch
1228 disable_option transport
1231 enable_option singlecommit
1232 enable_option retcode
1234 disable_option multicommit
1235 disable_option branch
1236 disable_option transport
1238 while {[llength $argv] > 0} {
1239 set a [lindex $argv 0]
1242 enable_option initialamend
1245 enable_option nocommit
1246 enable_option nocommitmsg
1249 disable_option nocommitmsg
1256 set argv [lrange $argv 1 end]
1261 ######################################################################
1263 ## execution environment
1265 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1267 # Suggest our implementation of askpass, if none is set
1268 if {![info exists env(SSH_ASKPASS)]} {
1269 set env(SSH_ASKPASS) [gitexec git-gui--askpass]
1272 ######################################################################
1278 set _gitdir $env(GIT_DIR)
1282 # beware that from the .git dir this sets _gitdir to .
1283 # and _prefix to the empty string
1284 set _gitdir [git rev-parse --git-dir]
1285 set _prefix [git rev-parse --show-prefix]
1289 choose_repository::pick
1293 # we expand the _gitdir when it's just a single dot (i.e. when we're being
1294 # run from the .git dir itself) lest the routines to find the worktree
1296 if {$_gitdir eq ".
"} {
1300 if {![file isdirectory $_gitdir]} {
1301 catch {wm withdraw .}
1302 error_popup [strcat [mc "Git directory not found
:"] "\n\n$_gitdir"]
1305 # _gitdir exists, so try loading the config
1309 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
1310 if {[package vcompare $_git_version 1.7.0] >= 0} {
1311 set _gitworktree [git rev-parse --show-toplevel]
1313 # try to set work tree from environment, core.worktree or use
1314 # cdup to obtain a relative path to the top of the worktree. If
1315 # run from the top, the ./ prefix ensures normalize expands pwd.
1316 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
1317 set _gitworktree [get_config core.worktree]
1318 if {$_gitworktree eq ""} {
1319 set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
1324 if {$_prefix ne {}} {
1325 if {$_gitworktree eq {}} {
1326 regsub -all {[^/]+/} $_prefix ../ cdup
1328 set cdup $_gitworktree
1330 if {[catch {cd $cdup} err]} {
1331 catch {wm withdraw .}
1332 error_popup [strcat [mc "Cannot move to top of working directory
:"] "\n\n$err"]
1335 set _gitworktree [pwd]
1337 } elseif {![is_enabled bare]} {
1339 catch {wm withdraw .}
1340 error_popup [strcat [mc "Cannot use bare repository
:"] "\n\n$_gitdir"]
1343 if {$_gitworktree eq {}} {
1344 set _gitworktree [file dirname $_gitdir]
1346 if {[catch {cd $_gitworktree} err]} {
1347 catch {wm withdraw .}
1348 error_popup [strcat [mc "No working directory
"] " $_gitworktree:\n\n$err"]
1351 set _gitworktree [pwd]
1353 set _reponame [file split [file normalize $_gitdir]]
1354 if {[lindex $_reponame end] eq {.git}} {
1355 set _reponame [lindex $_reponame end-1]
1357 set _reponame [lindex $_reponame end]
1360 set env(GIT_DIR) $_gitdir
1361 set env(GIT_WORK_TREE) $_gitworktree
1363 ######################################################################
1367 set current_diff_path {}
1368 set current_diff_side {}
1369 set diff_actions [list]
1373 set MERGE_HEAD [list]
1375 set commit_type_is_amend 0
1377 set current_branch {}
1379 set current_diff_path {}
1381 set is_submodule_diff 0
1382 set is_conflict_diff 0
1383 set diff_empty_count 0
1385 set last_revert_enc {}
1387 set nullid "0000000000000000000000000000000000000000"
1388 set nullid2 "0000000000000000000000000000000000000001"
1390 ######################################################################
1398 set disable_on_lock [list]
1399 set index_lock_type none
1401 proc lock_index {type} {
1402 global index_lock_type disable_on_lock
1404 if {$index_lock_type eq {none}} {
1405 set index_lock_type $type
1406 foreach w $disable_on_lock {
1407 uplevel #0 $w disabled
1410 } elseif {$index_lock_type eq "begin-
$type"} {
1411 set index_lock_type $type
1417 proc unlock_index {} {
1418 global index_lock_type disable_on_lock
1420 set index_lock_type none
1421 foreach w $disable_on_lock {
1422 uplevel #0 $w normal
1426 ######################################################################
1430 proc repository_state {ctvar hdvar mhvar} {
1431 global current_branch
1432 upvar $ctvar ct $hdvar hd $mhvar mh
1437 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
1443 set merge_head [gitdir MERGE_HEAD]
1444 if {[file exists $merge_head]} {
1446 set fd_mh [open $merge_head r]
1447 while {[gets $fd_mh line] >= 0} {
1458 global PARENT empty_tree
1460 set p [lindex $PARENT 0]
1464 if {$empty_tree eq {}} {
1465 set empty_tree [git mktree << {}]
1470 proc force_amend {} {
1471 global commit_type_is_amend
1472 global HEAD PARENT MERGE_HEAD commit_type
1474 repository_state newType newHEAD newMERGE_HEAD
1477 set MERGE_HEAD $newMERGE_HEAD
1478 set commit_type $newType
1480 set commit_type_is_amend 1
1481 do_select_commit_type
1484 proc rescan {after {honor_trustmtime 1}} {
1485 global HEAD PARENT MERGE_HEAD commit_type
1486 global ui_index ui_workdir ui_comm
1487 global rescan_active file_states
1490 if {$rescan_active > 0 || ![lock_index read]} return
1492 repository_state newType newHEAD newMERGE_HEAD
1493 if {[string match amend* $commit_type]
1494 && $newType eq {normal}
1495 && $newHEAD eq $HEAD} {
1499 set MERGE_HEAD $newMERGE_HEAD
1500 set commit_type $newType
1503 array unset file_states
1505 if {!$::GITGUI_BCK_exists &&
1506 (![$ui_comm edit modified]
1507 || [string trim [$ui_comm get 0.0 end]] eq {})} {
1508 if {[string match amend* $commit_type]} {
1509 } elseif {[load_message GITGUI_MSG utf-8]} {
1510 } elseif {[run_prepare_commit_msg_hook]} {
1511 } elseif {[load_message MERGE_MSG]} {
1512 } elseif {[load_message SQUASH_MSG]} {
1513 } elseif {[load_message [get_config commit.template]]} {
1516 $ui_comm edit modified false
1519 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
1520 rescan_stage2 {} $after
1523 ui_status [mc "Refreshing
file status...
"]
1524 set fd_rf [git_read update-index \
1530 fconfigure $fd_rf -blocking 0 -translation binary
1531 fileevent $fd_rf readable \
1532 [list rescan_stage2 $fd_rf $after]
1536 proc have_info_exclude {} {
1537 return [file readable [gitdir info exclude]]
1540 proc rescan_stage2 {fd after} {
1541 global rescan_active buf_rdi buf_rdf buf_rlo
1545 if {![eof $fd]} return
1549 if {[package vcompare $::_git_version 1.6.3] >= 0} {
1550 set ls_others [list --exclude-standard]
1552 set ls_others [list --exclude-per-directory=.gitignore]
1553 if {[have_info_exclude]} {
1554 lappend ls_others "--exclude-from=[gitdir info exclude
]"
1556 set user_exclude [get_config core.excludesfile]
1557 if {$user_exclude ne {} && [file readable $user_exclude]} {
1558 lappend ls_others "--exclude-from=[file normalize
$user_exclude]"
1567 ui_status [mc "Scanning
for modified files ...
"]
1568 if {[git-version >= "1.7.2"]} {
1569 set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]]
1571 set fd_di [git_read diff-index --cached -z [PARENT]]
1573 set fd_df [git_read diff-files -z]
1575 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1576 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
1578 fileevent $fd_di readable [list read_diff_index $fd_di $after]
1579 fileevent $fd_df readable [list read_diff_files $fd_df $after]
1581 if {[is_config_true gui.displayuntracked]} {
1582 set fd_lo [eval git_read ls-files --others -z $ls_others]
1583 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
1584 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
1589 proc load_message {file {encoding {}}} {
1592 set f [gitdir $file]
1593 if {[file isfile $f]} {
1594 if {[catch {set fd [open $f r]}]} {
1597 fconfigure $fd -eofchar {}
1598 if {$encoding ne {}} {
1599 fconfigure $fd -encoding $encoding
1601 set content [string trim [read $fd]]
1603 regsub -all -line {[ \r\t]+$} $content {} content
1604 $ui_comm delete 0.0 end
1605 $ui_comm insert end $content
1611 proc run_prepare_commit_msg_hook {} {
1614 # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
1615 # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
1616 # empty file but existent file.
1618 set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
1620 if {[file isfile [gitdir MERGE_MSG]]} {
1621 set pcm_source "merge
"
1622 set fd_mm [open [gitdir MERGE_MSG] r]
1623 fconfigure $fd_mm -encoding utf-8
1624 puts -nonewline $fd_pcm [read $fd_mm]
1626 } elseif {[file isfile [gitdir SQUASH_MSG]]} {
1627 set pcm_source "squash
"
1628 set fd_sm [open [gitdir SQUASH_MSG] r]
1629 fconfigure $fd_sm -encoding utf-8
1630 puts -nonewline $fd_pcm [read $fd_sm]
1632 } elseif {[file isfile [get_config commit.template]]} {
1633 set pcm_source "template
"
1634 set fd_sm [open [get_config commit.template] r]
1635 fconfigure $fd_sm -encoding utf-8
1636 puts -nonewline $fd_pcm [read $fd_sm]
1644 set fd_ph [githook_read prepare-commit-msg \
1645 [gitdir PREPARE_COMMIT_MSG] $pcm_source]
1647 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1651 ui_status [mc "Calling prepare-commit-msg hook...
"]
1654 fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
1655 fileevent $fd_ph readable \
1656 [list prepare_commit_msg_hook_wait $fd_ph]
1661 proc prepare_commit_msg_hook_wait {fd_ph} {
1664 append pch_error [read $fd_ph]
1665 fconfigure $fd_ph -blocking 1
1667 if {[catch {close $fd_ph}]} {
1668 ui_status [mc "Commit declined by prepare-commit-msg hook.
"]
1669 hook_failed_popup prepare-commit-msg $pch_error
1670 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1673 load_message PREPARE_COMMIT_MSG
1676 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1679 fconfigure $fd_ph -blocking 0
1680 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1683 proc read_diff_index {fd after} {
1686 append buf_rdi [read $fd]
1688 set n [string length $buf_rdi]
1690 set z1 [string first "\
0" $buf_rdi $c]
1691 if {$z1 == -1} break
1693 set z2 [string first "\
0" $buf_rdi $z1]
1694 if {$z2 == -1} break
1697 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
1698 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
1700 [encoding convertfrom utf-8 $p] \
1702 [list [lindex $i 0] [lindex $i 2]] \
1708 set buf_rdi [string range $buf_rdi $c end]
1713 rescan_done $fd buf_rdi $after
1716 proc read_diff_files {fd after} {
1719 append buf_rdf [read $fd]
1721 set n [string length $buf_rdf]
1723 set z1 [string first "\
0" $buf_rdf $c]
1724 if {$z1 == -1} break
1726 set z2 [string first "\
0" $buf_rdf $z1]
1727 if {$z2 == -1} break
1730 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
1731 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
1733 [encoding convertfrom utf-8 $p] \
1736 [list [lindex $i 0] [lindex $i 2]]
1741 set buf_rdf [string range $buf_rdf $c end]
1746 rescan_done $fd buf_rdf $after
1749 proc read_ls_others {fd after} {
1752 append buf_rlo [read $fd]
1753 set pck [split $buf_rlo "\
0"]
1754 set buf_rlo [lindex $pck end]
1755 foreach p [lrange $pck 0 end-1] {
1756 set p [encoding convertfrom utf-8 $p]
1757 if {[string index $p end] eq {/}} {
1758 set p [string range $p 0 end-1]
1762 rescan_done $fd buf_rlo $after
1765 proc rescan_done {fd buf after} {
1766 global rescan_active current_diff_path
1767 global file_states repo_config
1770 if {![eof $fd]} return
1773 if {[incr rescan_active -1] > 0} return
1778 if {$current_diff_path ne {}} { reshow_diff $after }
1779 if {$current_diff_path eq {}} { select_first_diff $after }
1782 proc prune_selection {} {
1783 global file_states selected_paths
1785 foreach path [array names selected_paths] {
1786 if {[catch {set still_here $file_states($path)}]} {
1787 unset selected_paths($path)
1792 ######################################################################
1796 proc mapicon {w state path} {
1799 if {[catch {set r $all_icons($state$w)}]} {
1800 puts "error
: no icon
for $w state
={$state} $path"
1806 proc mapdesc {state path} {
1809 if {[catch {set r $all_descs($state)}]} {
1810 puts "error
: no desc
for state
={$state} $path"
1816 proc ui_status {msg} {
1818 if {[info exists main_status]} {
1819 $main_status show $msg
1825 if {[info exists main_status]} {
1826 $main_status show [mc "Ready.
"]
1830 proc escape_path {path} {
1831 regsub -all {\\} $path "\\\\" path
1832 regsub -all "\n" $path "\\n
" path
1836 proc short_path {path} {
1837 return [escape_path [lindex [file split $path] end]]
1841 set null_sha1 [string repeat 0 40]
1843 proc merge_state {path new_state {head_info {}} {index_info {}}} {
1844 global file_states next_icon_id null_sha1
1846 set s0 [string index $new_state 0]
1847 set s1 [string index $new_state 1]
1849 if {[catch {set info $file_states($path)}]} {
1851 set icon n[incr next_icon_id]
1853 set state [lindex $info 0]
1854 set icon [lindex $info 1]
1855 if {$head_info eq {}} {set head_info [lindex $info 2]}
1856 if {$index_info eq {}} {set index_info [lindex $info 3]}
1859 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1860 elseif {$s0 eq {_}} {set s0 _}
1862 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1863 elseif {$s1 eq {_}} {set s1 _}
1865 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1866 set head_info [list 0 $null_sha1]
1867 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1868 && $head_info eq {}} {
1869 set head_info $index_info
1870 } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
1871 set index_info $head_info
1875 set file_states($path) [list $s0$s1 $icon \
1876 $head_info $index_info \
1881 proc display_file_helper {w path icon_name old_m new_m} {
1884 if {$new_m eq {_}} {
1885 set lno [lsearch -sorted -exact $file_lists($w) $path]
1887 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1889 $w conf -state normal
1890 $w delete $lno.0 [expr {$lno + 1}].0
1891 $w conf -state disabled
1893 } elseif {$old_m eq {_} && $new_m ne {_}} {
1894 lappend file_lists($w) $path
1895 set file_lists($w) [lsort -unique $file_lists($w)]
1896 set lno [lsearch -sorted -exact $file_lists($w) $path]
1898 $w conf -state normal
1899 $w image create $lno.0 \
1900 -align center -padx 5 -pady 1 \
1902 -image [mapicon $w $new_m $path]
1903 $w insert $lno.1 "[escape_path
$path]\n"
1904 $w conf -state disabled
1905 } elseif {$old_m ne $new_m} {
1906 $w conf -state normal
1907 $w image conf $icon_name -image [mapicon $w $new_m $path]
1908 $w conf -state disabled
1912 proc display_file {path state} {
1913 global file_states selected_paths
1914 global ui_index ui_workdir
1916 set old_m [merge_state $path $state]
1917 set s $file_states($path)
1918 set new_m [lindex $s 0]
1919 set icon_name [lindex $s 1]
1921 set o [string index $old_m 0]
1922 set n [string index $new_m 0]
1929 display_file_helper $ui_index $path $icon_name $o $n
1931 if {[string index $old_m 0] eq {U}} {
1934 set o [string index $old_m 1]
1936 if {[string index $new_m 0] eq {U}} {
1939 set n [string index $new_m 1]
1941 display_file_helper $ui_workdir $path $icon_name $o $n
1943 if {$new_m eq {__}} {
1944 unset file_states($path)
1945 catch {unset selected_paths($path)}
1949 proc display_all_files_helper {w path icon_name m} {
1952 lappend file_lists($w) $path
1953 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1954 $w image create end \
1955 -align center -padx 5 -pady 1 \
1957 -image [mapicon $w $m $path]
1958 $w insert end "[escape_path
$path]\n"
1962 proc display_all_files {} {
1963 global ui_index ui_workdir
1964 global file_states file_lists
1966 global files_warning
1968 $ui_index conf -state normal
1969 $ui_workdir conf -state normal
1971 $ui_index delete 0.0 end
1972 $ui_workdir delete 0.0 end
1975 set file_lists($ui_index) [list]
1976 set file_lists($ui_workdir) [list]
1978 set to_display [lsort [array names file_states]]
1979 set display_limit [get_config gui.maxfilesdisplayed]
1981 foreach path $to_display {
1982 set s $file_states($path)
1984 set icon_name [lindex $s 1]
1986 if {$displayed > $display_limit && [string index $m 1] eq {O} } {
1987 if {!$files_warning} {
1988 # do not repeatedly warn:
1990 info_popup [mc "Display limit
(gui.maxfilesdisplayed
= %s
) reached
, not showing all
%s files.
" \
1991 $display_limit [llength $to_display]]
1996 set s [string index $m 0]
1997 if {$s ne {U} && $s ne {_}} {
1998 display_all_files_helper $ui_index $path \
2002 if {[string index $m 0] eq {U}} {
2005 set s [string index $m 1]
2008 display_all_files_helper $ui_workdir $path \
2014 $ui_index conf -state disabled
2015 $ui_workdir conf -state disabled
2018 ######################################################################
2023 #define mask_width 14
2024 #define mask_height 15
2025 static unsigned char mask_bits[] = {
2026 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2027 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2028 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
2031 image create bitmap file_plain -background white -foreground black -data {
2032 #define plain_width 14
2033 #define plain_height 15
2034 static unsigned char plain_bits[] = {
2035 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2036 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
2037 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2038 } -maskdata $filemask
2040 image create bitmap file_mod -background white -foreground blue -data {
2041 #define mod_width 14
2042 #define mod_height 15
2043 static unsigned char mod_bits[] = {
2044 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
2045 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2046 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
2047 } -maskdata $filemask
2049 image create bitmap file_fulltick -background white -foreground "#007000" -data {
2050 #define file_fulltick_width 14
2051 #define file_fulltick_height 15
2052 static unsigned char file_fulltick_bits
[] = {
2053 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
2054 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
2055 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2056 } -maskdata $filemask
2058 image create bitmap file_question
-background white
-foreground black
-data {
2059 #define file_question_width 14
2060 #define file_question_height 15
2061 static unsigned char file_question_bits
[] = {
2062 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
2063 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
2064 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2065 } -maskdata $filemask
2067 image create bitmap file_removed
-background white
-foreground red
-data {
2068 #define file_removed_width 14
2069 #define file_removed_height 15
2070 static unsigned char file_removed_bits
[] = {
2071 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2072 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
2073 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
2074 } -maskdata $filemask
2076 image create bitmap file_merge
-background white
-foreground blue
-data {
2077 #define file_merge_width 14
2078 #define file_merge_height 15
2079 static unsigned char file_merge_bits
[] = {
2080 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
2081 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2082 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
2083 } -maskdata $filemask
2085 image create bitmap file_statechange
-background white
-foreground green
-data {
2086 #define file_statechange_width 14
2087 #define file_statechange_height 15
2088 static unsigned char file_statechange_bits
[] = {
2089 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
2090 0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
2091 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2092 } -maskdata $filemask
2094 set ui_index .vpane.files.index.list
2095 set ui_workdir .vpane.files.workdir.list
2097 set all_icons
(_
$ui_index) file_plain
2098 set all_icons
(A
$ui_index) file_plain
2099 set all_icons
(M
$ui_index) file_fulltick
2100 set all_icons
(D
$ui_index) file_removed
2101 set all_icons
(U
$ui_index) file_merge
2102 set all_icons
(T
$ui_index) file_statechange
2104 set all_icons
(_
$ui_workdir) file_plain
2105 set all_icons
(M
$ui_workdir) file_mod
2106 set all_icons
(D
$ui_workdir) file_question
2107 set all_icons
(U
$ui_workdir) file_merge
2108 set all_icons
(O
$ui_workdir) file_plain
2109 set all_icons
(T
$ui_workdir) file_statechange
2111 set max_status_desc
0
2113 {__
{mc
"Unmodified"}}
2115 {_M
{mc
"Modified, not staged"}}
2116 {M_
{mc
"Staged for commit"}}
2117 {MM
{mc
"Portions staged for commit"}}
2118 {MD
{mc
"Staged for commit, missing"}}
2120 {_T
{mc
"File type changed, not staged"}}
2121 {MT
{mc
"File type changed, old type staged for commit"}}
2122 {AT
{mc
"File type changed, old type staged for commit"}}
2123 {T_
{mc
"File type changed, staged"}}
2124 {TM
{mc
"File type change staged, modification not staged"}}
2125 {TD
{mc
"File type change staged, file missing"}}
2127 {_O
{mc
"Untracked, not staged"}}
2128 {A_
{mc
"Staged for commit"}}
2129 {AM
{mc
"Portions staged for commit"}}
2130 {AD
{mc
"Staged for commit, missing"}}
2133 {D_
{mc
"Staged for removal"}}
2134 {DO
{mc
"Staged for removal, still present"}}
2136 {_U
{mc
"Requires merge resolution"}}
2137 {U_
{mc
"Requires merge resolution"}}
2138 {UU
{mc
"Requires merge resolution"}}
2139 {UM
{mc
"Requires merge resolution"}}
2140 {UD
{mc
"Requires merge resolution"}}
2141 {UT
{mc
"Requires merge resolution"}}
2143 set text
[eval [lindex
$i 1]]
2144 if {$max_status_desc < [string length
$text]} {
2145 set max_status_desc
[string length
$text]
2147 set all_descs
([lindex
$i 0]) $text
2151 ######################################################################
2155 proc scrollbar2many
{list mode args
} {
2156 foreach w
$list {eval $w $mode $args}
2159 proc many2scrollbar
{list mode sb top bottom
} {
2160 $sb set $top $bottom
2161 foreach w
$list {$w $mode moveto
$top}
2164 proc incr_font_size
{font
{amt
1}} {
2165 set sz
[font configure
$font -size]
2167 font configure
$font -size $sz
2168 font configure
${font}bold
-size $sz
2169 font configure
${font}italic
-size $sz
2172 ######################################################################
2176 proc do_gitk
{revs
{is_submodule false
}} {
2177 global current_diff_path file_states current_diff_side ui_index
2178 global _gitdir _gitworktree
2180 # -- Always start gitk through whatever we were loaded with. This
2181 # lets us bypass using shell process on Windows systems.
2183 set exe
[_which gitk
-script]
2184 set cmd
[list
[info nameofexecutable
] $exe]
2186 error_popup
[mc
"Couldn't find gitk in PATH"]
2192 if {!$is_submodule} {
2197 cd $current_diff_path
2198 if {$revs eq
{--}} {
2199 set s
$file_states($current_diff_path)
2202 switch
-glob -- [lindex
$s 0] {
2203 M_
{ set old_sha1
[lindex
[lindex
$s 2] 1] }
2204 _M
{ set old_sha1
[lindex
[lindex
$s 3] 1] }
2206 if {$current_diff_side eq
$ui_index} {
2207 set old_sha1
[lindex
[lindex
$s 2] 1]
2208 set new_sha1
[lindex
[lindex
$s 3] 1]
2210 set old_sha1
[lindex
[lindex
$s 3] 1]
2214 set revs
$old_sha1...
$new_sha1
2216 # GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
2217 # we've been using for the main repository, so unset them.
2218 # TODO we could make life easier (start up faster?) for gitk
2219 # by setting these to the appropriate values to allow gitk
2220 # to skip the heuristics to find their proper value
2222 unset env
(GIT_WORK_TREE
)
2224 eval exec $cmd $revs "--" "--" &
2226 set env
(GIT_DIR
) $_gitdir
2227 set env
(GIT_WORK_TREE
) $_gitworktree
2230 if {[info exists main_status
]} {
2231 set status_operation
[$
::main_status \
2233 [mc
"Starting %s... please wait..." "gitk"]]
2235 after
3500 [list
$status_operation stop
]
2240 proc do_git_gui
{} {
2241 global current_diff_path
2243 # -- Always start git gui through whatever we were loaded with. This
2244 # lets us bypass using shell process on Windows systems.
2246 set exe
[list
[_which git
]]
2248 error_popup
[mc
"Couldn't find git gui in PATH"]
2251 global _gitdir _gitworktree
2253 # see note in do_gitk about unsetting these vars when
2254 # running tools in a submodule
2256 unset env
(GIT_WORK_TREE
)
2259 cd $current_diff_path
2261 eval exec $exe gui
&
2263 set env
(GIT_DIR
) $_gitdir
2264 set env
(GIT_WORK_TREE
) $_gitworktree
2267 set status_operation
[$
::main_status \
2269 [mc
"Starting %s... please wait..." "git-gui"]]
2271 after
3500 [list
$status_operation stop
]
2275 # Get the system-specific explorer app/command.
2276 proc get_explorer
{} {
2278 set explorer
"/bin/cygstart.exe --explore"
2279 } elseif
{[is_Windows
]} {
2280 set explorer
"explorer.exe"
2281 } elseif
{[is_MacOSX
]} {
2284 # freedesktop.org-conforming system is our best shot
2285 set explorer
"xdg-open"
2290 proc do_explore
{} {
2292 set explorer
[get_explorer
]
2293 eval exec $explorer [list
[file nativename
$_gitworktree]] &
2296 # Open file relative to the working tree by the default associated app.
2297 proc do_file_open
{file} {
2299 set explorer
[get_explorer
]
2300 set full_file_path
[file join $_gitworktree $file]
2301 exec $explorer [file nativename
$full_file_path] &
2307 proc terminate_me
{win
} {
2309 if {$win ne
{.
}} return
2313 proc do_quit
{{rc
{1}}} {
2314 global ui_comm is_quitting repo_config commit_type
2315 global GITGUI_BCK_exists GITGUI_BCK_i
2316 global ui_comm_spell
2317 global ret_code use_ttk
2319 if {$is_quitting} return
2322 if {[winfo exists
$ui_comm]} {
2323 # -- Stash our current commit buffer.
2325 set save
[gitdir GITGUI_MSG
]
2326 if {$GITGUI_BCK_exists && ![$ui_comm edit modified
]} {
2327 file rename
-force [gitdir GITGUI_BCK
] $save
2328 set GITGUI_BCK_exists
0
2329 } elseif
{[$ui_comm edit modified
]} {
2330 set msg
[string trim
[$ui_comm get
0.0 end
]]
2331 regsub
-all -line {[ \r\t]+$
} $msg {} msg
2332 if {![string match amend
* $commit_type]
2335 set fd
[open
$save w
]
2336 fconfigure
$fd -encoding utf-8
2337 puts
-nonewline $fd $msg
2341 catch
{file delete
$save}
2345 # -- Cancel our spellchecker if its running.
2347 if {[info exists ui_comm_spell
]} {
2351 # -- Remove our editor backup, its not needed.
2353 after cancel
$GITGUI_BCK_i
2354 if {$GITGUI_BCK_exists} {
2355 catch
{file delete
[gitdir GITGUI_BCK
]}
2358 # -- Stash our current window geometry into this repository.
2360 set cfg_wmstate
[wm state .
]
2361 if {[catch
{set rc_wmstate
$repo_config(gui.wmstate
)}]} {
2364 if {$cfg_wmstate ne
$rc_wmstate} {
2365 catch
{git config gui.wmstate
$cfg_wmstate}
2367 if {$cfg_wmstate eq
{zoomed
}} {
2368 # on Windows wm geometry will lie about window
2369 # position (but not size) when window is zoomed
2370 # restore the window before querying wm geometry
2373 set cfg_geometry
[list
]
2374 lappend cfg_geometry
[wm geometry .
]
2376 lappend cfg_geometry
[.vpane sashpos
0]
2377 lappend cfg_geometry
[.vpane.files sashpos
0]
2379 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 0]
2380 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 1]
2382 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
2385 if {$cfg_geometry ne
$rc_geometry} {
2386 catch
{git config gui.geometry
$cfg_geometry}
2392 # Briefly enable send again, working around Tk bug
2393 # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
2394 tk appname
[appname
]
2403 proc ui_do_rescan
{} {
2404 rescan
{force_first_diff ui_ready
}
2411 proc next_diff
{{after
{}}} {
2412 global next_diff_p next_diff_w next_diff_i
2413 show_diff
$next_diff_p $next_diff_w {} {} $after
2416 proc find_anchor_pos
{lst name
} {
2417 set lid
[lsearch
-sorted -exact $lst $name]
2421 foreach lname
$lst {
2422 if {$lname >= $name} break
2430 proc find_file_from
{flist idx delta path mmask
} {
2433 set len
[llength
$flist]
2434 while {$idx >= 0 && $idx < $len} {
2435 set name
[lindex
$flist $idx]
2437 if {$name ne
$path && [info exists file_states
($name)]} {
2438 set state
[lindex
$file_states($name) 0]
2440 if {$mmask eq
{} ||
[regexp
$mmask $state]} {
2451 proc find_next_diff
{w path
{lno
{}} {mmask
{}}} {
2452 global next_diff_p next_diff_w next_diff_i
2453 global file_lists ui_index ui_workdir
2455 set flist
$file_lists($w)
2457 set lno
[find_anchor_pos
$flist $path]
2462 if {$mmask ne
{} && ![regexp
{(^\^
)|
(\$$
)} $mmask]} {
2463 if {$w eq
$ui_index} {
2466 set mmask
"$mmask\$"
2470 set idx
[find_file_from
$flist $lno 1 $path $mmask]
2473 set idx
[find_file_from
$flist $lno -1 $path $mmask]
2478 set next_diff_p
[lindex
$flist $idx]
2479 set next_diff_i
[expr {$idx+1}]
2486 proc next_diff_after_action
{w path
{lno
{}} {mmask
{}}} {
2487 global current_diff_path
2489 if {$path ne
$current_diff_path} {
2491 } elseif
{[find_next_diff
$w $path $lno $mmask]} {
2494 return {reshow_diff
;}
2498 proc select_first_diff
{after
} {
2501 if {[find_next_diff
$ui_workdir {} 1 {^_?U
}] ||
2502 [find_next_diff
$ui_workdir {} 1 {[^O
]$
}]} {
2509 proc force_first_diff
{after
} {
2510 global ui_workdir current_diff_path file_states
2512 if {[info exists file_states
($current_diff_path)]} {
2513 set state
[lindex
$file_states($current_diff_path) 0]
2519 if {[string first
{U
} $state] >= 0} {
2520 # Already a conflict, do nothing
2521 } elseif
{[find_next_diff
$ui_workdir $current_diff_path {} {^_?U
}]} {
2523 } elseif
{[string index
$state 1] ne
{O
}} {
2524 # Already a diff & no conflicts, do nothing
2525 } elseif
{[find_next_diff
$ui_workdir $current_diff_path {} {[^O
]$
}]} {
2536 proc toggle_or_diff
{mode w args
} {
2537 global file_states file_lists current_diff_path ui_index ui_workdir
2538 global last_clicked selected_paths file_lists_last_clicked
2540 if {$mode eq
"click"} {
2541 foreach
{x y
} $args break
2542 set pos
[split [$w index @
$x,$y] .
]
2543 foreach
{lno
col} $pos break
2545 if {$mode eq
"toggle"} {
2546 if {$w eq
$ui_workdir} {
2551 if {$w eq
$ui_index} {
2552 do_unstage_selection
2558 if {$last_clicked ne
{}} {
2559 set lno
[lindex
$last_clicked 1]
2561 if {![info exists file_lists
]
2562 ||
![info exists file_lists
($w)]
2563 ||
[llength
$file_lists($w)] == 0} {
2567 set lno
[expr {int
([lindex
[$w tag ranges in_diff
] 0])}]
2569 if {$mode eq
"toggle"} {
2572 incr lno
[expr {$mode eq
"up" ?
-1 : 1}]
2577 if {![info exists file_lists
]
2578 ||
![info exists file_lists
($w)]
2579 ||
[llength
$file_lists($w)] < $lno - 1} {
2582 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
2589 set last_clicked
[list
$w $lno]
2591 array
unset selected_paths
2592 $ui_index tag remove in_sel
0.0 end
2593 $ui_workdir tag remove in_sel
0.0 end
2595 set file_lists_last_clicked
($w) $path
2597 # Determine the state of the file
2598 if {[info exists file_states
($path)]} {
2599 set state
[lindex
$file_states($path) 0]
2604 # Restage the file, or simply show the diff
2605 if {$col == 0 && $y > 1} {
2606 # Conflicts need special handling
2607 if {[string first
{U
} $state] >= 0} {
2608 # $w must always be $ui_workdir, but...
2609 if {$w ne
$ui_workdir} { set lno
{} }
2610 merge_stage_workdir
$path $lno
2614 if {[string index
$state 1] eq
{O
}} {
2620 set after
[next_diff_after_action
$w $path $lno $mmask]
2622 if {$w eq
$ui_index} {
2624 "Unstaging [short_path $path] from commit" \
2626 [concat
$after {ui_ready
;}]
2627 } elseif
{$w eq
$ui_workdir} {
2629 "Adding [short_path $path]" \
2631 [concat
$after {ui_ready
;}]
2634 set selected_paths
($path) 1
2635 show_diff
$path $w $lno
2639 proc add_one_to_selection
{w x y
} {
2640 global file_lists last_clicked selected_paths
2642 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
2643 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
2649 if {$last_clicked ne
{}
2650 && [lindex
$last_clicked 0] ne
$w} {
2651 array
unset selected_paths
2652 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
2655 set last_clicked
[list
$w $lno]
2656 if {[catch
{set in_sel
$selected_paths($path)}]} {
2660 unset selected_paths
($path)
2661 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
2663 set selected_paths
($path) 1
2664 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
2668 proc add_range_to_selection
{w x y
} {
2669 global file_lists last_clicked selected_paths
2671 if {[lindex
$last_clicked 0] ne
$w} {
2672 toggle_or_diff click
$w $x $y
2676 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
2677 set lc
[lindex
$last_clicked 1]
2686 foreach path
[lrange
$file_lists($w) \
2687 [expr {$begin - 1}] \
2688 [expr {$end - 1}]] {
2689 set selected_paths
($path) 1
2691 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
2694 proc show_more_context
{} {
2696 if {$repo_config(gui.diffcontext
) < 99} {
2697 incr repo_config
(gui.diffcontext
)
2702 proc show_less_context
{} {
2704 if {$repo_config(gui.diffcontext
) > 1} {
2705 incr repo_config
(gui.diffcontext
) -1
2710 proc focus_widget
{widget
} {
2711 global file_lists last_clicked selected_paths
2712 global file_lists_last_clicked
2714 if {[llength
$file_lists($widget)] > 0} {
2715 set path
$file_lists_last_clicked($widget)
2716 set index
[lsearch
-sorted -exact $file_lists($widget) $path]
2719 set path
[lindex
$file_lists($widget) $index]
2723 set last_clicked
[list
$widget [expr $index + 1]]
2724 array
unset selected_paths
2725 set selected_paths
($path) 1
2726 show_diff
$path $widget
2730 proc toggle_commit_type
{} {
2731 global commit_type_is_amend
2732 set commit_type_is_amend
[expr !$commit_type_is_amend]
2733 do_select_commit_type
2736 ######################################################################
2744 menu .mbar
-tearoff 0
2746 # -- Apple Menu (Mac OS X only)
2748 .mbar add cascade
-label Apple
-menu .mbar.apple
2751 .mbar add cascade
-label [mc Repository
] -menu .mbar.repository
2752 .mbar add cascade
-label [mc Edit
] -menu .mbar.edit
2753 if {[is_enabled branch
]} {
2754 .mbar add cascade
-label [mc Branch
] -menu .mbar.branch
2756 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2757 .mbar add cascade
-label [mc Commit@@noun
] -menu .mbar.commit
2759 if {[is_enabled transport
]} {
2760 .mbar add cascade
-label [mc Merge
] -menu .mbar.merge
2761 .mbar add cascade
-label [mc Remote
] -menu .mbar.remote
2763 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2764 .mbar add cascade
-label [mc Tools
] -menu .mbar.tools
2767 # -- Repository Menu
2769 menu .mbar.repository
2772 .mbar.repository add
command \
2773 -label [mc
"Explore Working Copy"] \
2774 -command {do_explore
}
2778 # Use /git-bash.exe if available
2779 set normalized
[file normalize $
::argv0
]
2780 regsub
"/mingw../libexec/git-core/git-gui$" \
2781 $normalized "/git-bash.exe" cmdLine
2782 if {$cmdLine != $normalized && [file exists
$cmdLine]} {
2783 set cmdLine
[list
"Git Bash" $cmdLine &]
2785 set cmdLine
[list
"Git Bash" bash
--login -l &]
2787 .mbar.repository add
command \
2788 -label [mc
"Git Bash"] \
2789 -command {eval exec [auto_execok start
] $cmdLine}
2792 if {[is_Windows
] ||
![is_bare
]} {
2793 .mbar.repository add separator
2796 .mbar.repository add
command \
2797 -label [mc
"Browse Current Branch's Files"] \
2798 -command {browser
::new
$current_branch}
2799 set ui_browse_current
[.mbar.repository index last
]
2800 .mbar.repository add
command \
2801 -label [mc
"Browse Branch Files..."] \
2802 -command browser_open
::dialog
2803 .mbar.repository add separator
2805 .mbar.repository add
command \
2806 -label [mc
"Visualize Current Branch's History"] \
2807 -command {do_gitk
$current_branch}
2808 set ui_visualize_current
[.mbar.repository index last
]
2809 .mbar.repository add
command \
2810 -label [mc
"Visualize All Branch History"] \
2811 -command {do_gitk
--all}
2812 .mbar.repository add separator
2814 proc current_branch_write
{args
} {
2815 global current_branch
2816 .mbar.repository entryconf $
::ui_browse_current \
2817 -label [mc
"Browse %s's Files" $current_branch]
2818 .mbar.repository entryconf $
::ui_visualize_current \
2819 -label [mc
"Visualize %s's History" $current_branch]
2821 trace add variable current_branch
write current_branch_write
2823 if {[is_enabled multicommit
]} {
2824 .mbar.repository add
command -label [mc
"Database Statistics"] \
2827 .mbar.repository add
command -label [mc
"Compress Database"] \
2830 .mbar.repository add
command -label [mc
"Verify Database"] \
2831 -command do_fsck_objects
2833 .mbar.repository add separator
2836 .mbar.repository add
command \
2837 -label [mc
"Create Desktop Icon"] \
2838 -command do_cygwin_shortcut
2839 } elseif
{[is_Windows
]} {
2840 .mbar.repository add
command \
2841 -label [mc
"Create Desktop Icon"] \
2842 -command do_windows_shortcut
2843 } elseif
{[is_MacOSX
]} {
2844 .mbar.repository add
command \
2845 -label [mc
"Create Desktop Icon"] \
2846 -command do_macosx_app
2851 proc
::tk
::mac
::Quit
{args
} { do_quit
}
2853 .mbar.repository add
command -label [mc Quit
] \
2861 .mbar.edit add
command -label [mc Undo
] \
2862 -command {catch
{[focus
] edit undo
}} \
2864 .mbar.edit add
command -label [mc Redo
] \
2865 -command {catch
{[focus
] edit redo
}} \
2867 .mbar.edit add separator
2868 .mbar.edit add
command -label [mc Cut
] \
2869 -command {catch
{tk_textCut
[focus
]}} \
2871 .mbar.edit add
command -label [mc Copy
] \
2872 -command {catch
{tk_textCopy
[focus
]}} \
2874 .mbar.edit add
command -label [mc Paste
] \
2875 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
2877 .mbar.edit add
command -label [mc Delete
] \
2878 -command {catch
{[focus
] delete sel.first sel.last
}} \
2880 .mbar.edit add separator
2881 .mbar.edit add
command -label [mc
"Select All"] \
2882 -command {catch
{[focus
] tag add sel
0.0 end
}} \
2887 if {[is_enabled branch
]} {
2890 .mbar.branch add
command -label [mc
"Create..."] \
2891 -command branch_create
::dialog \
2893 lappend disable_on_lock
[list .mbar.branch entryconf \
2894 [.mbar.branch index last
] -state]
2896 .mbar.branch add
command -label [mc
"Checkout..."] \
2897 -command branch_checkout
::dialog \
2899 lappend disable_on_lock
[list .mbar.branch entryconf \
2900 [.mbar.branch index last
] -state]
2902 .mbar.branch add
command -label [mc
"Rename..."] \
2903 -command branch_rename
::dialog
2904 lappend disable_on_lock
[list .mbar.branch entryconf \
2905 [.mbar.branch index last
] -state]
2907 .mbar.branch add
command -label [mc
"Delete..."] \
2908 -command branch_delete
::dialog
2909 lappend disable_on_lock
[list .mbar.branch entryconf \
2910 [.mbar.branch index last
] -state]
2912 .mbar.branch add
command -label [mc
"Reset..."] \
2913 -command merge
::reset_hard
2914 lappend disable_on_lock
[list .mbar.branch entryconf \
2915 [.mbar.branch index last
] -state]
2920 proc commit_btn_caption
{} {
2921 if {[is_enabled nocommit
]} {
2924 return [mc Commit@@verb
]
2928 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2931 if {![is_enabled nocommit
]} {
2932 .mbar.commit add checkbutton \
2933 -label [mc
"Amend Last Commit"] \
2934 -accelerator $M1T-E \
2935 -variable commit_type_is_amend \
2936 -command do_select_commit_type
2937 lappend disable_on_lock \
2938 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2940 .mbar.commit add separator
2943 .mbar.commit add
command -label [mc Rescan
] \
2944 -command ui_do_rescan \
2946 lappend disable_on_lock \
2947 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2949 .mbar.commit add
command -label [mc
"Stage To Commit"] \
2950 -command do_add_selection \
2952 lappend disable_on_lock \
2953 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2955 .mbar.commit add
command -label [mc
"Stage Changed Files To Commit"] \
2956 -command do_add_all \
2958 lappend disable_on_lock \
2959 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2961 .mbar.commit add
command -label [mc
"Unstage From Commit"] \
2962 -command do_unstage_selection \
2964 lappend disable_on_lock \
2965 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2967 .mbar.commit add
command -label [mc
"Revert Changes"] \
2968 -command do_revert_selection \
2970 lappend disable_on_lock \
2971 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2973 .mbar.commit add separator
2975 .mbar.commit add
command -label [mc
"Show Less Context"] \
2976 -command show_less_context \
2977 -accelerator $M1T-\
-
2979 .mbar.commit add
command -label [mc
"Show More Context"] \
2980 -command show_more_context \
2983 .mbar.commit add separator
2985 if {![is_enabled nocommitmsg
]} {
2986 .mbar.commit add
command -label [mc
"Sign Off"] \
2987 -command do_signoff \
2991 .mbar.commit add
command -label [commit_btn_caption
] \
2992 -command do_commit \
2993 -accelerator $M1T-Return
2994 lappend disable_on_lock \
2995 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
3000 if {[is_enabled branch
]} {
3002 .mbar.merge add
command -label [mc
"Local Merge..."] \
3003 -command merge
::dialog \
3005 lappend disable_on_lock \
3006 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
3007 .mbar.merge add
command -label [mc
"Abort Merge..."] \
3008 -command merge
::reset_hard
3009 lappend disable_on_lock \
3010 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
3015 if {[is_enabled transport
]} {
3018 .mbar.remote add
command \
3019 -label [mc
"Add..."] \
3020 -command remote_add
::dialog \
3022 .mbar.remote add
command \
3023 -label [mc
"Push..."] \
3024 -command do_push_anywhere \
3026 .mbar.remote add
command \
3027 -label [mc
"Delete Branch..."] \
3028 -command remote_branch_delete
::dialog
3032 proc
::tk
::mac
::ShowPreferences
{} {do_options
}
3036 .mbar.edit add separator
3037 .mbar.edit add
command -label [mc
"Options..."] \
3043 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
3044 set tools_menubar .mbar.tools
3046 $tools_menubar add separator
3047 $tools_menubar add
command -label [mc
"Add..."] -command tools_add
::dialog
3048 $tools_menubar add
command -label [mc
"Remove..."] -command tools_remove
::dialog
3050 if {[array names repo_config guitool.
*.cmd
] ne
{}} {
3057 .mbar add cascade
-label [mc Help
] -menu .mbar.
help
3061 .mbar.apple add
command -label [mc
"About %s" [appname
]] \
3063 .mbar.apple add separator
3065 .mbar.
help add
command -label [mc
"About %s" [appname
]] \
3068 . configure
-menu .mbar
3070 set doc_path
[githtmldir
]
3071 if {$doc_path ne
{}} {
3072 set doc_path
[file join $doc_path index.html
]
3075 if {[file isfile
$doc_path]} {
3076 set doc_url
"file:$doc_path"
3078 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
3081 proc start_browser
{url
} {
3082 git
"web--browse" $url
3085 .mbar.
help add
command -label [mc
"Online Documentation"] \
3086 -command [list start_browser
$doc_url]
3088 .mbar.
help add
command -label [mc
"Show SSH Key"] \
3091 unset doc_path doc_url
3093 # -- Standard bindings
3095 wm protocol . WM_DELETE_WINDOW do_quit
3096 bind all
<$M1B-Key-q> do_quit
3097 bind all
<$M1B-Key-Q> do_quit
3100 set toplvl_win
[winfo toplevel
%W
]
3102 # If we are destroying the main window, we should call do_quit to take
3103 # care of cleanup before exiting the program.
3104 if {$toplvl_win eq
"."} {
3111 bind all
<$M1B-Key-w> $m1b_w_script
3112 bind all
<$M1B-Key-W> $m1b_w_script
3116 set subcommand_args
{}
3118 set s
"[mc usage:] $::argv0 $::subcommand $::subcommand_args"
3119 if {[tk windowingsystem
] eq
"win32"} {
3121 tk_messageBox
-icon info
-message $s \
3129 proc normalize_relpath
{path
} {
3131 foreach item
[file split $path] {
3132 if {$item eq
{.
}} continue
3133 if {$item eq
{..
} && [llength
$elements] > 0
3134 && [lindex
$elements end
] ne
{..
}} {
3135 set elements
[lrange
$elements 0 end-1
]
3138 lappend elements
$item
3140 return [eval file join $elements]
3143 # -- Not a normal commit type invocation? Do that instead!
3145 switch
-- $subcommand {
3148 if {$subcommand eq
"blame"} {
3149 set subcommand_args
{[--line=<num
>] rev? path
}
3151 set subcommand_args
{rev? path
}
3153 if {$argv eq
{}} usage
3159 set p
[file join $_prefix $a]
3161 if {$is_path ||
[file exists
$p]} {
3162 if {$path ne
{}} usage
3163 set path
[normalize_relpath
$p]
3165 } elseif
{$a eq
{--}} {
3167 if {$head ne
{}} usage
3172 } elseif
{[regexp
{^
--line=(\d
+)$
} $a a lnum
]} {
3173 if {$jump_spec ne
{} ||
$head ne
{}} usage
3174 set jump_spec
[list
$lnum]
3175 } elseif
{$head eq
{}} {
3176 if {$head ne
{}} usage
3185 if {$head ne
{} && $path eq
{}} {
3186 if {[string index
$head 0] eq
{/}} {
3187 set path
[normalize_relpath
$head]
3190 set path
[normalize_relpath
$_prefix$head]
3198 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
3200 set head [git rev-parse
--verify $head]
3202 if {[tk windowingsystem
] eq
"win32"} {
3203 tk_messageBox
-icon error
-title [mc Error
] -message $err
3210 set current_branch
$head
3214 switch
-- $subcommand {
3216 if {$jump_spec ne
{}} usage
3218 if {$path ne
{} && [file isdirectory
$path]} {
3219 set head $current_branch
3225 browser
::new
$head $path
3228 if {$head eq
{} && ![file exists
$path]} {
3229 catch
{wm withdraw .
}
3233 -title [mc
"git-gui: fatal error"] \
3234 -message [mc
"fatal: cannot stat path %s: No such file or directory" $path]
3237 blame
::new
$head $path $jump_spec
3244 if {[llength
$argv] != 0} {
3247 # fall through to setup UI for commits
3250 set err
"[mc usage:] $argv0 \[{blame|browser|citool}\]"
3251 if {[tk windowingsystem
] eq
"win32"} {
3253 tk_messageBox
-icon error
-message $err \
3264 ${NS}::frame .branch
3265 if {!$use_ttk} {.branch configure
-borderwidth 1 -relief sunken
}
3266 ${NS}::label .branch.l1 \
3267 -text [mc
"Current Branch:"] \
3270 ${NS}::label .branch.cb \
3271 -textvariable current_branch \
3274 pack .branch.l1
-side left
3275 pack .branch.cb
-side left
-fill x
3276 pack .branch
-side top
-fill x
3278 # -- Main Window Layout
3280 ${NS}::panedwindow .vpane
-orient horizontal
3281 ${NS}::panedwindow .vpane.files
-orient vertical
3283 .vpane add .vpane.files
3285 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
3287 pack .vpane
-anchor n
-side top
-fill both
-expand 1
3289 # -- Working Directory File List
3291 textframe .vpane.files.workdir
-height 100 -width 200
3292 tlabel .vpane.files.workdir.title
-text [mc
"Unstaged Changes"] \
3293 -background lightsalmon
-foreground black
3296 -width 20 -height 10 \
3298 -takefocus 1 -highlightthickness 1\
3299 -cursor $cursor_ptr \
3300 -xscrollcommand {.vpane.files.workdir.sx
set} \
3301 -yscrollcommand {.vpane.files.workdir.sy
set} \
3303 ${NS}::scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
3304 ${NS}::scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
3305 pack .vpane.files.workdir.title
-side top
-fill x
3306 pack .vpane.files.workdir.sx
-side bottom
-fill x
3307 pack .vpane.files.workdir.sy
-side right
-fill y
3308 pack
$ui_workdir -side left
-fill both
-expand 1
3310 # -- Index File List
3312 textframe .vpane.files.index
-height 100 -width 200
3313 tlabel .vpane.files.index.title \
3314 -text [mc
"Staged Changes (Will Commit)"] \
3315 -background lightgreen
-foreground black
3318 -width 20 -height 10 \
3320 -takefocus 1 -highlightthickness 1\
3321 -cursor $cursor_ptr \
3322 -xscrollcommand {.vpane.files.index.sx
set} \
3323 -yscrollcommand {.vpane.files.index.sy
set} \
3325 ${NS}::scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
3326 ${NS}::scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
3327 pack .vpane.files.index.title
-side top
-fill x
3328 pack .vpane.files.index.sx
-side bottom
-fill x
3329 pack .vpane.files.index.sy
-side right
-fill y
3330 pack
$ui_index -side left
-fill both
-expand 1
3332 # -- Insert the workdir and index into the panes
3334 .vpane.files add .vpane.files.workdir
3335 .vpane.files add .vpane.files.index
3337 .vpane.files paneconfigure .vpane.files.workdir
-sticky news
3338 .vpane.files paneconfigure .vpane.files.index
-sticky news
3341 proc set_selection_colors
{w has_focus
} {
3342 foreach tag
[list in_diff in_sel
] {
3344 -background [expr {$has_focus ?
$color::select_bg
: $color::inactive_select_bg
}] \
3345 -foreground [expr {$has_focus ?
$color::select_fg
: $color::inactive_select_fg
}]
3349 foreach i
[list
$ui_index $ui_workdir] {
3352 set_selection_colors
$i 0
3353 bind $i <FocusIn
> { set_selection_colors
%W
1 }
3354 bind $i <FocusOut
> { set_selection_colors
%W
0 }
3358 # -- Diff and Commit Area
3361 ${NS}::panedwindow .vpane.lower
-orient vertical
3362 ${NS}::frame .vpane.lower.commarea
3363 ${NS}::frame .vpane.lower.
diff -relief sunken
-borderwidth 1 -height 500
3364 .vpane.lower add .vpane.lower.
diff
3365 .vpane.lower add .vpane.lower.commarea
3366 .vpane add .vpane.lower
3368 .vpane.lower pane .vpane.lower.
diff -weight 1
3369 .vpane.lower pane .vpane.lower.commarea
-weight 0
3371 .vpane.lower paneconfigure .vpane.lower.
diff -stretch always
3372 .vpane.lower paneconfigure .vpane.lower.commarea
-stretch never
3375 frame .vpane.lower
-height 300 -width 400
3376 frame .vpane.lower.commarea
3377 frame .vpane.lower.
diff -relief sunken
-borderwidth 1
3378 pack .vpane.lower.
diff -fill both
-expand 1
3379 pack .vpane.lower.commarea
-side bottom
-fill x
3380 .vpane add .vpane.lower
3381 .vpane paneconfigure .vpane.lower
-sticky nsew
3384 # -- Commit Area Buttons
3386 ${NS}::frame .vpane.lower.commarea.buttons
3387 ${NS}::label .vpane.lower.commarea.buttons.l
-text {} \
3390 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
3391 pack .vpane.lower.commarea.buttons
-side left
-fill y
3393 ${NS}::button .vpane.lower.commarea.buttons.rescan
-text [mc Rescan
] \
3394 -command ui_do_rescan
3395 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
3396 lappend disable_on_lock \
3397 {.vpane.lower.commarea.buttons.rescan conf
-state}
3399 ${NS}::button .vpane.lower.commarea.buttons.incall
-text [mc
"Stage Changed"] \
3401 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
3402 lappend disable_on_lock \
3403 {.vpane.lower.commarea.buttons.incall conf
-state}
3405 if {![is_enabled nocommitmsg
]} {
3406 ${NS}::button .vpane.lower.commarea.buttons.signoff
-text [mc
"Sign Off"] \
3408 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
3411 ${NS}::button .vpane.lower.commarea.buttons.commit
-text [commit_btn_caption
] \
3413 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
3414 lappend disable_on_lock \
3415 {.vpane.lower.commarea.buttons.commit conf
-state}
3417 if {![is_enabled nocommit
]} {
3418 ${NS}::button .vpane.lower.commarea.buttons.push
-text [mc Push
] \
3419 -command do_push_anywhere
3420 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
3423 # -- Commit Message Buffer
3425 ${NS}::frame .vpane.lower.commarea.buffer
3426 ${NS}::frame .vpane.lower.commarea.buffer.header
3427 set ui_comm .vpane.lower.commarea.buffer.frame.t
3428 set ui_coml .vpane.lower.commarea.buffer.header.l
3430 if {![is_enabled nocommit
]} {
3431 ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \
3432 -text [mc
"Amend Last Commit"] \
3433 -variable commit_type_is_amend \
3434 -command do_select_commit_type
3435 lappend disable_on_lock \
3436 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
3439 ${NS}::label
$ui_coml \
3442 proc trace_commit_type
{varname args
} {
3443 global ui_coml commit_type
3444 switch
-glob -- $commit_type {
3445 initial
{set txt
[mc
"Initial Commit Message:"]}
3446 amend
{set txt
[mc
"Amended Commit Message:"]}
3447 amend-initial
{set txt
[mc
"Amended Initial Commit Message:"]}
3448 amend-merge
{set txt
[mc
"Amended Merge Commit Message:"]}
3449 merge
{set txt
[mc
"Merge Commit Message:"]}
3450 * {set txt
[mc
"Commit Message:"]}
3452 $ui_coml conf
-text $txt
3454 trace add variable commit_type
write trace_commit_type
3455 pack
$ui_coml -side left
-fill x
3457 if {![is_enabled nocommit
]} {
3458 pack .vpane.lower.commarea.buffer.header.amend
-side right
3461 textframe .vpane.lower.commarea.buffer.frame
3466 -autoseparators true \
3468 -highlightthickness 1 \
3470 -width $repo_config(gui.commitmsgwidth
) -height 9 -wrap none \
3472 -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx
set} \
3473 -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby
set}
3474 ${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \
3475 -orient horizontal \
3476 -command [list
$ui_comm xview
]
3477 ${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
3479 -command [list
$ui_comm yview
]
3481 pack .vpane.lower.commarea.buffer.frame.sbx
-side bottom
-fill x
3482 pack .vpane.lower.commarea.buffer.frame.sby
-side right
-fill y
3483 pack
$ui_comm -side left
-fill y
3484 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
3485 pack .vpane.lower.commarea.buffer.frame
-side left
-fill y
3486 pack .vpane.lower.commarea.buffer
-side left
-fill y
3488 # -- Commit Message Buffer Context Menu
3490 set ctxm .vpane.lower.commarea.buffer.ctxm
3491 menu
$ctxm -tearoff 0
3494 -command {tk_textCut
$ui_comm}
3497 -command {tk_textCopy
$ui_comm}
3500 -command {tk_textPaste
$ui_comm}
3502 -label [mc Delete
] \
3503 -command {catch
{$ui_comm delete sel.first sel.last
}}
3506 -label [mc
"Select All"] \
3507 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
3509 -label [mc
"Copy All"] \
3511 $ui_comm tag add sel
0.0 end
3512 tk_textCopy
$ui_comm
3513 $ui_comm tag remove sel
0.0 end
3517 -label [mc
"Sign Off"] \
3519 set ui_comm_ctxm
$ctxm
3523 proc trace_current_diff_path
{varname args
} {
3524 global current_diff_path diff_actions file_states
3525 if {$current_diff_path eq
{}} {
3531 set p
$current_diff_path
3532 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
3534 set p
[escape_path
$p]
3538 .vpane.lower.
diff.header.status configure
-text $s
3539 .vpane.lower.
diff.header.
file configure
-text $f
3540 .vpane.lower.
diff.header.path configure
-text $p
3541 foreach w
$diff_actions {
3545 trace add variable current_diff_path
write trace_current_diff_path
3547 gold_frame .vpane.lower.
diff.header
3548 tlabel .vpane.lower.
diff.header.status \
3551 -width $max_status_desc \
3554 tlabel .vpane.lower.
diff.header.
file \
3559 tlabel .vpane.lower.
diff.header.path \
3564 -font [eval font create
[font configure font_ui
] -underline 1] \
3566 pack .vpane.lower.
diff.header.status
-side left
3567 pack .vpane.lower.
diff.header.
file -side left
3568 pack .vpane.lower.
diff.header.path
-fill x
3569 set ctxm .vpane.lower.
diff.header.ctxm
3570 menu
$ctxm -tearoff 0
3578 -- $current_diff_path
3582 -command {do_file_open
$current_diff_path}
3583 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3584 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
3585 bind .vpane.lower.
diff.header.path
<Button-1
> {do_file_open
$current_diff_path}
3589 textframe .vpane.lower.
diff.body
3590 set ui_diff .vpane.lower.
diff.body.t
3593 -width 80 -height 5 -wrap none \
3595 -takefocus 1 -highlightthickness 1 \
3596 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
3597 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
3599 catch
{$ui_diff configure
-tabstyle wordprocessor
}
3600 ${NS}::scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
3601 -command [list
$ui_diff xview
]
3602 ${NS}::scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
3603 -command [list
$ui_diff yview
]
3604 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
3605 pack .vpane.lower.
diff.body.sby
-side right
-fill y
3606 pack
$ui_diff -side left
-fill both
-expand 1
3607 pack .vpane.lower.
diff.header
-side top
-fill x
3608 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
3610 foreach
{n c
} {0 black
1 red4
2 green4
3 yellow4
4 blue4
5 magenta4
6 cyan4
7 grey60
} {
3611 $ui_diff tag configure clr4
$n -background $c
3612 $ui_diff tag configure clri4
$n -foreground $c
3613 $ui_diff tag configure clr3
$n -foreground $c
3614 $ui_diff tag configure clri3
$n -background $c
3616 $ui_diff tag configure clr1
-font font_diffbold
3617 $ui_diff tag configure clr4
-underline 1
3619 $ui_diff tag conf d_info
-foreground blue
-font font_diffbold
3621 $ui_diff tag conf d_cr
-elide true
3622 $ui_diff tag conf d_@
-font font_diffbold
3623 $ui_diff tag conf d_
+ -foreground {#00a000}
3624 $ui_diff tag conf d_-
-foreground red
3626 $ui_diff tag conf d_
++ -foreground {#00a000}
3627 $ui_diff tag conf d_--
-foreground red
3628 $ui_diff tag conf d_
+s \
3629 -foreground {#00a000} \
3630 -background {#e2effa}
3631 $ui_diff tag conf d_-s \
3633 -background {#e2effa}
3634 $ui_diff tag conf d_s
+ \
3635 -foreground {#00a000} \
3637 $ui_diff tag conf d_s- \
3641 $ui_diff tag conf d
< \
3642 -foreground orange \
3644 $ui_diff tag conf d| \
3645 -foreground orange \
3647 $ui_diff tag conf d
= \
3648 -foreground orange \
3650 $ui_diff tag conf d
> \
3651 -foreground orange \
3654 $ui_diff tag raise sel
3656 # -- Diff Body Context Menu
3659 proc create_common_diff_popup
{ctxm
} {
3661 -label [mc Refresh
] \
3662 -command reshow_diff
3663 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3666 -command {tk_textCopy
$ui_diff}
3667 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3669 -label [mc
"Select All"] \
3670 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
3671 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3673 -label [mc
"Copy All"] \
3675 $ui_diff tag add sel
0.0 end
3676 tk_textCopy
$ui_diff
3677 $ui_diff tag remove sel
0.0 end
3679 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3682 -label [mc
"Decrease Font Size"] \
3683 -command {incr_font_size font_diff
-1}
3684 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3686 -label [mc
"Increase Font Size"] \
3687 -command {incr_font_size font_diff
1}
3688 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3692 build_encoding_menu
$emenu [list force_diff_encoding
]
3694 -label [mc
"Encoding"] \
3696 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3698 $ctxm add
command -label [mc
"Options..."] \
3702 set ctxm .vpane.lower.
diff.body.ctxm
3703 menu
$ctxm -tearoff 0
3705 -label [mc
"Apply/Reverse Hunk"] \
3706 -command {apply_or_revert_hunk
$cursorX $cursorY 0}
3707 set ui_diff_applyhunk
[$ctxm index last
]
3708 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
3710 -label [mc
"Apply/Reverse Line"] \
3711 -command {apply_or_revert_range_or_line
$cursorX $cursorY 0; do_rescan
}
3712 set ui_diff_applyline
[$ctxm index last
]
3713 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyline -state]
3716 -label [mc
"Revert Hunk"] \
3717 -command {apply_or_revert_hunk
$cursorX $cursorY 1}
3718 set ui_diff_reverthunk
[$ctxm index last
]
3719 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_reverthunk -state]
3721 -label [mc
"Revert Line"] \
3722 -command {apply_or_revert_range_or_line
$cursorX $cursorY 1; do_rescan
}
3723 set ui_diff_revertline
[$ctxm index last
]
3724 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_revertline -state]
3726 -label [mc
"Undo Last Revert"] \
3727 -command {undo_last_revert
; do_rescan
}
3728 set ui_diff_undorevert
[$ctxm index last
]
3729 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_undorevert -state]
3732 -label [mc
"Show Less Context"] \
3733 -command show_less_context
3734 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3736 -label [mc
"Show More Context"] \
3737 -command show_more_context
3738 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3740 create_common_diff_popup
$ctxm
3742 set ctxmmg .vpane.lower.
diff.body.ctxmmg
3743 menu
$ctxmmg -tearoff 0
3744 $ctxmmg add
command \
3745 -label [mc
"Run Merge Tool"] \
3746 -command {merge_resolve_tool
}
3747 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3748 $ctxmmg add separator
3749 $ctxmmg add
command \
3750 -label [mc
"Use Remote Version"] \
3751 -command {merge_resolve_one
3}
3752 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3753 $ctxmmg add
command \
3754 -label [mc
"Use Local Version"] \
3755 -command {merge_resolve_one
2}
3756 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3757 $ctxmmg add
command \
3758 -label [mc
"Revert To Base"] \
3759 -command {merge_resolve_one
1}
3760 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3761 $ctxmmg add separator
3762 $ctxmmg add
command \
3763 -label [mc
"Show Less Context"] \
3764 -command show_less_context
3765 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3766 $ctxmmg add
command \
3767 -label [mc
"Show More Context"] \
3768 -command show_more_context
3769 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3770 $ctxmmg add separator
3771 create_common_diff_popup
$ctxmmg
3773 set ctxmsm .vpane.lower.
diff.body.ctxmsm
3774 menu
$ctxmsm -tearoff 0
3775 $ctxmsm add
command \
3776 -label [mc
"Visualize These Changes In The Submodule"] \
3777 -command {do_gitk
-- true
}
3778 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3779 $ctxmsm add
command \
3780 -label [mc
"Visualize Current Branch History In The Submodule"] \
3781 -command {do_gitk
{} true
}
3782 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3783 $ctxmsm add
command \
3784 -label [mc
"Visualize All Branch History In The Submodule"] \
3785 -command {do_gitk
--all true
}
3786 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3787 $ctxmsm add separator
3788 $ctxmsm add
command \
3789 -label [mc
"Start git gui In The Submodule"] \
3790 -command {do_git_gui
}
3791 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3792 $ctxmsm add separator
3793 create_common_diff_popup
$ctxmsm
3795 proc has_textconv
{path
} {
3796 if {[is_config_false gui.textconv
]} {
3799 set filter
[gitattr
$path diff set]
3800 set textconv
[get_config
[join [list
diff $filter textconv
] .
]]
3801 if {$filter ne
{set} && $textconv ne
{}} {
3808 proc popup_diff_menu
{ctxm ctxmmg ctxmsm x y X Y
} {
3809 global current_diff_path file_states last_revert
3812 if {[info exists file_states
($current_diff_path)]} {
3813 set state
[lindex
$file_states($current_diff_path) 0]
3817 if {[string first
{U
} $state] >= 0} {
3818 tk_popup
$ctxmmg $X $Y
3819 } elseif
{$
::is_submodule_diff
} {
3820 tk_popup
$ctxmsm $X $Y
3822 set has_range
[expr {[$
::ui_diff tag nextrange sel
0.0] != {}}]
3823 set u
[mc
"Undo Last Revert"]
3824 if {$
::ui_index eq $
::current_diff_side
} {
3825 set l
[mc
"Unstage Hunk From Commit"]
3826 set h
[mc
"Revert Hunk"]
3829 set t
[mc
"Unstage Lines From Commit"]
3830 set r
[mc
"Revert Lines"]
3832 set t
[mc
"Unstage Line From Commit"]
3833 set r
[mc
"Revert Line"]
3836 set l
[mc
"Stage Hunk For Commit"]
3837 set h
[mc
"Revert Hunk"]
3840 set t
[mc
"Stage Lines For Commit"]
3841 set r
[mc
"Revert Lines"]
3843 set t
[mc
"Stage Line For Commit"]
3844 set r
[mc
"Revert Line"]
3848 ||
$current_diff_path eq
{}
3851 ||
[string match
{?T
} $state]
3852 ||
[string match
{T?
} $state]
3853 ||
[has_textconv
$current_diff_path]} {
3855 set revert_state disabled
3859 # Only allow reverting changes in the working tree. If
3860 # the user wants to revert changes in the index, they
3861 # need to unstage those first.
3862 if {$
::ui_workdir eq $
::current_diff_side
} {
3863 set revert_state normal
3865 set revert_state disabled
3869 if {$last_revert eq
{}} {
3870 set undo_state disabled
3872 set undo_state normal
3875 $ctxm entryconf $
::ui_diff_applyhunk
-state $s -label $l
3876 $ctxm entryconf $
::ui_diff_applyline
-state $s -label $t
3877 $ctxm entryconf $
::ui_diff_revertline
-state $revert_state \
3879 $ctxm entryconf $
::ui_diff_reverthunk
-state $revert_state \
3881 $ctxm entryconf $
::ui_diff_undorevert
-state $undo_state \
3884 tk_popup
$ctxm $X $Y
3887 bind_button3
$ui_diff [list popup_diff_menu
$ctxm $ctxmmg $ctxmsm %x
%y
%X
%Y
]
3891 set main_status
[::status_bar
::new .status
]
3892 pack .status
-anchor w
-side bottom
-fill x
3893 $main_status show
[mc
"Initializing..."]
3897 proc on_ttk_pane_mapped
{w pane pos
} {
3899 after
0 [list after idle
[list
$w sashpos
$pane $pos]]
3901 proc on_tk_pane_mapped
{w pane x y
} {
3903 after
0 [list after idle
[list
$w sash place
$pane $x $y]]
3905 proc on_application_mapped
{} {
3906 global repo_config use_ttk
3908 set gm
$repo_config(gui.geometry
)
3911 [list on_ttk_pane_mapped
%W
0 [lindex
$gm 1]]
3912 bind .vpane.files
<Map
> \
3913 [list on_ttk_pane_mapped
%W
0 [lindex
$gm 2]]
3916 [list on_tk_pane_mapped
%W
0 \
3918 [lindex
[.vpane sash coord
0] 1]]
3919 bind .vpane.files
<Map
> \
3920 [list on_tk_pane_mapped
%W
0 \
3921 [lindex
[.vpane.files sash coord
0] 0] \
3924 wm geometry .
[lindex
$gm 0]
3926 if {[info exists repo_config
(gui.geometry
)]} {
3927 bind .
<Map
> [list on_application_mapped
]
3928 wm geometry .
[lindex
$repo_config(gui.geometry
) 0]
3931 # -- Load window state
3933 if {[info exists repo_config
(gui.wmstate
)]} {
3934 catch
{wm state .
$repo_config(gui.wmstate
)}
3939 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
3940 bind $ui_comm <$M1B-Key-t> {do_add_selection
;break}
3941 bind $ui_comm <$M1B-Key-T> {do_add_selection
;break}
3942 bind $ui_comm <$M1B-Key-u> {do_unstage_selection
;break}
3943 bind $ui_comm <$M1B-Key-U> {do_unstage_selection
;break}
3944 bind $ui_comm <$M1B-Key-j> {do_revert_selection
;break}
3945 bind $ui_comm <$M1B-Key-J> {do_revert_selection
;break}
3946 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
3947 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
3948 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
3949 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
3950 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
3951 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
3952 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
3953 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
3954 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
3955 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
3956 bind $ui_comm <$M1B-Key-minus> {show_less_context
;break}
3957 bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context
;break}
3958 bind $ui_comm <$M1B-Key-equal> {show_more_context
;break}
3959 bind $ui_comm <$M1B-Key-plus> {show_more_context
;break}
3960 bind $ui_comm <$M1B-Key-KP_Add> {show_more_context
;break}
3961 bind $ui_comm <$M1B-Key-BackSpace> {event generate
%W
<Meta-Delete
>;break}
3962 bind $ui_comm <$M1B-Key-Delete> {event generate
%W
<Meta-d
>;break}
3964 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
3965 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
3966 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
3967 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
3968 bind $ui_diff <$M1B-Key-v> {break}
3969 bind $ui_diff <$M1B-Key-V> {break}
3970 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
3971 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
3972 bind $ui_diff <$M1B-Key-j> {do_revert_selection
;break}
3973 bind $ui_diff <$M1B-Key-J> {do_revert_selection
;break}
3974 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
3975 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
3976 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
3977 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
3978 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
3979 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
3980 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
3981 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
3982 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
3983 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
3984 bind $ui_diff <Button-1
> {focus
%W
}
3986 if {[is_enabled branch
]} {
3987 bind .
<$M1B-Key-n> branch_create
::dialog
3988 bind .
<$M1B-Key-N> branch_create
::dialog
3989 bind .
<$M1B-Key-o> branch_checkout
::dialog
3990 bind .
<$M1B-Key-O> branch_checkout
::dialog
3991 bind .
<$M1B-Key-m> merge
::dialog
3992 bind .
<$M1B-Key-M> merge
::dialog
3994 if {[is_enabled transport
]} {
3995 bind .
<$M1B-Key-p> do_push_anywhere
3996 bind .
<$M1B-Key-P> do_push_anywhere
3999 bind .
<Key-F5
> ui_do_rescan
4000 bind .
<$M1B-Key-r> ui_do_rescan
4001 bind .
<$M1B-Key-R> ui_do_rescan
4002 bind .
<$M1B-Key-s> do_signoff
4003 bind .
<$M1B-Key-S> do_signoff
4004 bind .
<$M1B-Key-t> { toggle_or_diff toggle
%W
}
4005 bind .
<$M1B-Key-T> { toggle_or_diff toggle
%W
}
4006 bind .
<$M1B-Key-u> { toggle_or_diff toggle
%W
}
4007 bind .
<$M1B-Key-U> { toggle_or_diff toggle
%W
}
4008 bind .
<$M1B-Key-j> do_revert_selection
4009 bind .
<$M1B-Key-J> do_revert_selection
4010 bind .
<$M1B-Key-i> do_add_all
4011 bind .
<$M1B-Key-I> do_add_all
4012 bind .
<$M1B-Key-e> toggle_commit_type
4013 bind .
<$M1B-Key-E> toggle_commit_type
4014 bind .
<$M1B-Key-minus> {show_less_context
;break}
4015 bind .
<$M1B-Key-KP_Subtract> {show_less_context
;break}
4016 bind .
<$M1B-Key-equal> {show_more_context
;break}
4017 bind .
<$M1B-Key-plus> {show_more_context
;break}
4018 bind .
<$M1B-Key-KP_Add> {show_more_context
;break}
4019 bind .
<$M1B-Key-Return> do_commit
4020 bind .
<$M1B-Key-KP_Enter> do_commit
4021 foreach i
[list
$ui_index $ui_workdir] {
4022 bind $i <Button-1
> { toggle_or_diff click
%W
%x
%y
; break }
4023 bind $i <$M1B-Button-1> { add_one_to_selection
%W
%x
%y
; break }
4024 bind $i <Shift-Button-1
> { add_range_to_selection
%W
%x
%y
; break }
4025 bind $i <Key-Up
> { toggle_or_diff up
%W
; break }
4026 bind $i <Key-Down
> { toggle_or_diff down
%W
; break }
4030 bind .
<Alt-Key-1
> {focus_widget $
::ui_workdir
}
4031 bind .
<Alt-Key-2
> {focus_widget $
::ui_index
}
4032 bind .
<Alt-Key-3
> {focus $
::ui_diff
}
4033 bind .
<Alt-Key-4
> {focus $
::ui_comm
}
4035 set file_lists_last_clicked
($ui_index) {}
4036 set file_lists_last_clicked
($ui_workdir) {}
4038 set file_lists
($ui_index) [list
]
4039 set file_lists
($ui_workdir) [list
]
4041 wm title .
"[appname] ([reponame]) [file normalize $_gitworktree]"
4042 focus
-force $ui_comm
4044 # -- Only initialize complex UI if we are going to stay running.
4046 if {[is_enabled transport
]} {
4049 set n
[.mbar.remote index end
]
4050 populate_remotes_menu
4051 set n
[expr {[.mbar.remote index end
] - $n}]
4053 if {[.mbar.remote
type 0] eq
"tearoff"} { incr n
}
4054 .mbar.remote insert
$n separator
4059 if {[winfo exists
$ui_comm]} {
4060 set GITGUI_BCK_exists
[load_message GITGUI_BCK utf-8
]
4062 # -- If both our backup and message files exist use the
4063 # newer of the two files to initialize the buffer.
4065 if {$GITGUI_BCK_exists} {
4066 set m
[gitdir GITGUI_MSG
]
4067 if {[file isfile
$m]} {
4068 if {[file mtime
[gitdir GITGUI_BCK
]] > [file mtime
$m]} {
4069 catch
{file delete
[gitdir GITGUI_MSG
]}
4071 $ui_comm delete
0.0 end
4073 $ui_comm edit modified false
4074 catch
{file delete
[gitdir GITGUI_BCK
]}
4075 set GITGUI_BCK_exists
0
4081 proc backup_commit_buffer
{} {
4082 global ui_comm GITGUI_BCK_exists
4084 set m
[$ui_comm edit modified
]
4085 if {$m ||
$GITGUI_BCK_exists} {
4086 set msg
[string trim
[$ui_comm get
0.0 end
]]
4087 regsub
-all -line {[ \r\t]+$
} $msg {} msg
4090 if {$GITGUI_BCK_exists} {
4091 catch
{file delete
[gitdir GITGUI_BCK
]}
4092 set GITGUI_BCK_exists
0
4096 set fd
[open
[gitdir GITGUI_BCK
] w
]
4097 fconfigure
$fd -encoding utf-8
4098 puts
-nonewline $fd $msg
4100 set GITGUI_BCK_exists
1
4104 $ui_comm edit modified false
4107 set ::GITGUI_BCK_i
[after
2000 backup_commit_buffer
]
4110 backup_commit_buffer
4112 # -- If the user has aspell available we can drive it
4113 # in pipe mode to spellcheck the commit message.
4115 set spell_cmd
[list |
]
4116 set spell_dict
[get_config gui.spellingdictionary
]
4117 lappend spell_cmd aspell
4118 if {$spell_dict ne
{}} {
4119 lappend spell_cmd
--master=$spell_dict
4121 lappend spell_cmd
--mode=none
4122 lappend spell_cmd
--encoding=utf-8
4123 lappend spell_cmd pipe
4124 if {$spell_dict eq
{none
}
4125 ||
[catch
{set spell_fd
[open
$spell_cmd r
+]} spell_err
]} {
4126 bind_button3
$ui_comm [list tk_popup
$ui_comm_ctxm %X
%Y
]
4128 set ui_comm_spell
[spellcheck
::init \
4134 unset -nocomplain spell_cmd spell_fd spell_err spell_dict
4137 lock_index begin-read
4138 if {![winfo ismapped .
]} {
4142 if {[is_enabled initialamend
]} {
4148 if {[is_enabled nocommitmsg
]} {
4149 $ui_comm configure
-state disabled
-background gray
4152 if {[is_enabled multicommit
] && ![is_config_false gui.gcwarning
]} {
4155 if {[is_enabled retcode
]} {
4156 bind .
<Destroy
> {+terminate_me
%W
}
4158 if {$picked && [is_config_true gui.autoexplore
]} {
4162 # Clear "Initializing..." status
4163 after
500 {$main_status show
""}
4167 # indent-tabs-mode: t