2 # Tcl ignores the next line -*- tcl -*- \
3 if test "z$*" = zversion \
4 ||
test "z$*" = z--version
; \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
10 exec wish
"$argv0" -- "$@"
12 set appvers
{@@GITGUI_VERSION@@
}
13 set copyright
[string map
[list
(c
) \u00a9] {
14 Copyright
(c
) 2006-2010 Shawn Pearce
, et. al.
16 This program is free software
; you can redistribute it and
/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation
; either version
2 of the License
, or
19 (at your option
) any later version.
21 This program is distributed
in the hope that it will be useful
,
22 but WITHOUT ANY WARRANTY
; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License
for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program
; if not
, write to the Free Software
28 Foundation
, Inc.
, 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
}]
30 ######################################################################
32 ## Tcl/Tk sanity check
34 if {[catch
{package require Tcl
8.4} err
]
35 ||
[catch
{package require Tk
8.4} err
]
41 -title "git-gui: fatal error" \
46 catch
{rename send
{}} ; # What an evil concept...
48 ######################################################################
52 set oguilib
{@@GITGUI_LIBDIR@@
}
53 set oguirel
{@@GITGUI_RELATIVE@@
}
54 if {$oguirel eq
{1}} {
55 set oguilib
[file dirname [file normalize
$argv0]]
56 if {[file tail $oguilib] eq
{git-core
}} {
57 set oguilib
[file dirname $oguilib]
59 set oguilib
[file dirname $oguilib]
60 set oguilib
[file join $oguilib share git-gui lib
]
61 set oguimsg
[file join $oguilib msgs
]
62 } elseif
{[string match @@
* $oguirel]} {
63 set oguilib
[file join [file dirname [file normalize
$argv0]] lib
]
64 set oguimsg
[file join [file dirname [file normalize
$argv0]] po
]
66 set oguimsg
[file join $oguilib msgs
]
70 ######################################################################
72 ## enable verbose loading?
74 if {![catch
{set _verbose
$env(GITGUI_VERBOSE
)}]} {
76 rename auto_load real__auto_load
77 proc auto_load
{name args
} {
78 puts stderr
"auto_load $name"
79 return [uplevel
1 real__auto_load
$name $args]
81 rename
source real__source
83 puts stderr
"source $name"
84 uplevel
1 real__source
$name
86 if {[tk windowingsystem
] eq
"win32"} { console show
}
89 ######################################################################
91 ## Internationalization (i18n) through msgcat and gettext. See
92 ## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
94 package require msgcat
96 # Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
97 if {[tk windowingsystem
] eq
"win32"
98 && [package vcompare
[package provide msgcat
] 1.4.4] < 0
100 proc _mc_update_locale
{} {
101 set key
{HKEY_CURRENT_USER\Control Panel\Desktop
}
103 package require registry
104 set uilocale
[registry get
$key "PreferredUILanguages"]
105 msgcat
::ConvertLocale
[string map
{- _
} [lindex
$uilocale 0]]
107 if {[string length
$uilocale] > 0} {
108 msgcat
::mclocale
$uilocale
115 proc _mc_trim
{fmt} {
116 set cmk
[string first @@
$fmt]
118 return [string range
$fmt 0 [expr {$cmk - 1}]]
123 proc mc
{en_fmt args
} {
124 set fmt [_mc_trim
[::msgcat
::mc
$en_fmt]]
125 if {[catch
{set msg
[eval [list format
$fmt] $args]} err
]} {
126 set msg
[eval [list format
[_mc_trim
$en_fmt]] $args]
132 return [join $args {}]
135 ::msgcat
::mcload
$oguimsg
138 ######################################################################
142 set _appname
{Git Gui
}
151 set _shellpath
{@@SHELL_PATH@@
}
153 set _trace
[lsearch
-exact $argv --trace]
155 set argv
[lreplace
$argv $_trace $_trace]
157 if {[tk windowingsystem
] eq
"win32"} { console show
}
162 # variable for the last merged branch (useful for a default when deleting
164 set _last_merged_branch
{}
167 global _shellpath env
168 if {[string match @@
* $_shellpath]} {
169 if {[info exists env
(SHELL
)]} {
188 return [eval [list
file join $_gitdir] $args]
191 proc gitexec
{args
} {
193 if {$_gitexec eq
{}} {
194 if {[catch
{set _gitexec
[git
--exec-path]} err
]} {
195 error
"Git not installed?\n\n$err"
198 set _gitexec
[exec cygpath \
203 set _gitexec
[file normalize
$_gitexec]
209 return [eval [list
file join $_gitexec] $args]
212 proc githtmldir
{args
} {
214 if {$_githtmldir eq
{}} {
215 if {[catch
{set _githtmldir
[git
--html-path]}]} {
216 # Git not installed or option not yet supported
220 set _githtmldir
[exec cygpath \
225 set _githtmldir
[file normalize
$_githtmldir]
231 return [eval [list
file join $_githtmldir] $args]
239 if {[tk windowingsystem
] eq
{aqua
}} {
246 if {$
::tcl_platform
(platform
) eq
{windows
}} {
254 if {$_iscygwin eq
{}} {
255 if {$
::tcl_platform
(platform
) eq
{windows
}} {
256 if {[catch
{set p
[exec cygpath
--windir]} err
]} {
268 proc is_enabled
{option
} {
269 global enabled_options
270 if {[catch
{set on
$enabled_options($option)}]} {return 0}
274 proc enable_option
{option
} {
275 global enabled_options
276 set enabled_options
($option) 1
279 proc disable_option
{option
} {
280 global enabled_options
281 set enabled_options
($option) 0
284 ######################################################################
288 proc is_many_config
{name
} {
289 switch
-glob -- $name {
299 proc is_config_true
{name
} {
301 if {[catch
{set v
$repo_config($name)}]} {
304 set v
[string tolower
$v]
305 if {$v eq
{} ||
$v eq
{true
} ||
$v eq
{1} ||
$v eq
{yes} ||
$v eq
{on
}} {
312 proc is_config_false
{name
} {
314 if {[catch
{set v
$repo_config($name)}]} {
317 set v
[string tolower
$v]
318 if {$v eq
{false
} ||
$v eq
{0} ||
$v eq
{no
} ||
$v eq
{off
}} {
325 proc get_config
{name
} {
327 if {[catch
{set v
$repo_config($name)}]} {
339 if {$_isbare eq
{}} {
341 set _bare
[git rev-parse
--is-bare-repository]
343 true
{ set _isbare
1 }
344 false
{ set _isbare
0}
348 if {[is_config_true core.bare
]
349 ||
($_gitworktree eq
{}
350 && [lindex
[file split $_gitdir] end
] ne
{.git
})} {
360 ######################################################################
364 proc _trace_exec
{cmd
} {
365 if {!$
::_trace
} return
371 if {[regexp
{[ \t\r\n'"$?*]} $v]} {
379 #'" fix poor old emacs font-lock mode
381 proc _git_cmd {name} {
384 if {[catch {set v $_git_cmd_path($name)}]} {
388 --exec-path { return [list $::_git $name] }
391 set p [gitexec git-$name$::_search_exe]
392 if {[file exists $p]} {
394 } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
395 # Try to determine what sort of magic will make
396 # git-$name go and do its thing, because native
397 # Tcl on Windows doesn't know it.
399 set p [gitexec git-$name]
404 switch -glob -- [lindex $s 0] {
406 #!*perl { set i perl }
407 #!*python { set i python }
408 default { error "git-
$name is not supported
: $s" }
412 if {![info exists interp]} {
413 set interp [_which $i]
416 error "git-
$name requires
$i (not
in PATH
)"
418 set v [concat [list $interp] [lrange $s 1 end] [list $p]]
420 # Assume it is builtin to git somehow and we
421 # aren't actually able to see a file for it.
423 set v [list $::_git $name]
425 set _git_cmd_path($name) $v
430 proc _which {what args} {
431 global env _search_exe _search_path
433 if {$_search_path eq {}} {
434 if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
435 set _search_path [split [exec cygpath \
441 } elseif {[is_Windows]} {
442 set gitguidir [file dirname [info script]]
443 regsub -all ";" $gitguidir "\\;" gitguidir
444 set env(PATH) "$gitguidir;$env(PATH
)"
445 set _search_path [split $env(PATH) {;}]
448 set _search_path [split $env(PATH) :]
453 if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
456 set suffix $_search_exe
459 foreach p $_search_path {
460 set p [file join $p $what$suffix]
461 if {[file exists $p]} {
462 return [file normalize $p]
468 # Test a file for a hashbang to identify executable scripts on Windows.
469 proc is_shellscript {filename} {
470 if {![file exists $filename]} {return 0}
471 set f [open $filename r]
472 fconfigure $f -encoding binary
473 set magic [read $f 2]
475 return [expr {$magic eq "#!"}]
478 # Run a command connected via pipes on stdout.
479 # This is for use with textconv filters and uses sh -c "..." to allow it to
480 # contain a command with arguments. On windows we must check for shell
481 # scripts specifically otherwise just call the filter command.
482 proc open_cmd_pipe
{cmd path
} {
484 if {![file executable
[shellpath
]]} {
485 set exe
[auto_execok
[lindex
$cmd 0]]
486 if {[is_shellscript
[lindex
$exe 0]]} {
487 set run
[linsert
[auto_execok sh
] end
-c "$cmd \"\$0\"" $path]
489 set run
[concat
$exe [lrange
$cmd 1 end
] $path]
492 set run
[list
[shellpath
] -c "$cmd \"\$0\"" $path]
494 return [open |
$run r
]
497 proc _lappend_nice
{cmd_var
} {
501 if {![info exists _nice
]} {
502 set _nice
[_which nice
]
503 if {[catch
{exec $_nice git version
}]} {
505 } elseif
{[is_Windows
] && [file dirname $_nice] ne
[file dirname $
::_git
]} {
518 switch
-- [lindex
$args 0] {
529 set args
[lrange
$args 1 end
]
532 set cmdp
[_git_cmd
[lindex
$args 0]]
533 set args
[lrange
$args 1 end
]
535 _trace_exec
[concat
$opt $cmdp $args]
536 set result
[eval exec $opt $cmdp $args]
538 puts stderr
"< $result"
543 proc _open_stdout_stderr
{cmd
} {
546 set fd
[open
[concat
[list |
] $cmd] r
]
548 if { [lindex
$cmd end
] eq
{2>@
1}
549 && $err eq
{can not
find channel named
"1"}
551 # Older versions of Tcl 8.4 don't have this 2>@1 IO
552 # redirect operator. Fallback to |& cat for those.
553 # The command was not actually started, so its safe
554 # to try to start it a second time.
556 set fd
[open
[concat \
558 [lrange
$cmd 0 end-1
] \
565 fconfigure
$fd -eofchar {}
569 proc git_read
{args
} {
573 switch
-- [lindex
$args 0] {
588 set args
[lrange
$args 1 end
]
591 set cmdp
[_git_cmd
[lindex
$args 0]]
592 set args
[lrange
$args 1 end
]
594 return [_open_stdout_stderr
[concat
$opt $cmdp $args]]
597 proc git_write
{args
} {
601 switch
-- [lindex
$args 0] {
612 set args
[lrange
$args 1 end
]
615 set cmdp
[_git_cmd
[lindex
$args 0]]
616 set args
[lrange
$args 1 end
]
618 _trace_exec
[concat
$opt $cmdp $args]
619 return [open
[concat
[list |
] $opt $cmdp $args] w
]
622 proc githook_read
{hook_name args
} {
623 set pchook
[gitdir hooks
$hook_name]
626 # On Windows [file executable] might lie so we need to ask
627 # the shell if the hook is executable. Yes that's annoying.
631 if {![info exists interp
]} {
632 set interp
[_which sh
]
635 error
"hook execution requires sh (not in PATH)"
638 set scr
{if test -x "$1";then exec "$@";fi}
639 set sh_c
[list
$interp -c $scr $interp $pchook]
640 return [_open_stdout_stderr
[concat
$sh_c $args]]
643 if {[file executable
$pchook]} {
644 return [_open_stdout_stderr
[concat
[list
$pchook] $args]]
650 proc kill_file_process
{fd
} {
651 set process
[pid
$fd]
655 # Use a Cygwin-specific flag to allow killing
656 # native Windows processes
657 exec kill -f $process
664 proc gitattr
{path attr default
} {
665 if {[catch
{set r
[git check-attr
$attr -- $path]}]} {
668 set r
[join [lrange
[split $r :] 2 end
] :]
671 if {$r eq
{unspecified
}} {
678 regsub
-all ' $value "'\\''" value
682 proc load_current_branch {} {
683 global current_branch is_detached
685 set fd [open [gitdir HEAD] r]
686 if {[gets $fd ref] < 1} {
691 set pfx {ref: refs/heads/}
692 set len [string length $pfx]
693 if {[string equal -length $len $pfx $ref]} {
694 # We're on a branch. It might not exist. But
695 # HEAD looks good enough to be a branch.
697 set current_branch [string range $ref $len end]
700 # Assume this is a detached head.
702 set current_branch HEAD
707 auto_load tk_optionMenu
708 rename tk_optionMenu real__tkOptionMenu
709 proc tk_optionMenu {w varName args} {
710 set m [eval real__tkOptionMenu $w $varName $args]
711 $m configure -font font_ui
712 $w configure -font font_ui
716 proc rmsel_tag {text} {
718 -background [$text cget -background] \
719 -foreground [$text cget -foreground] \
721 $text tag conf in_sel -background lightgray
722 bind $text <Motion> break
728 bind . <Visibility> {
729 bind . <Visibility> {}
734 wm iconbitmap . -default $oguilib/git-gui.ico
735 set ::tk::AlwaysShowSelection 1
736 bind . <Control-F2> {console show}
738 # Spoof an X11 display for SSH
739 if {![info exists env(DISPLAY)]} {
740 set env(DISPLAY) :9999
744 image create photo gitlogo -width 16 -height 16
746 gitlogo put #33CC33 -to 7 0 9 2
747 gitlogo put #33CC33 -to 4 2 12 4
748 gitlogo put #33CC33 -to 7 4 9 6
749 gitlogo put #CC3333 -to 4 6 12 8
750 gitlogo put gray26 -to 4 9 6 10
751 gitlogo put gray26 -to 3 10 6 12
752 gitlogo put gray26 -to 8 9 13 11
753 gitlogo put gray26 -to 8 11 10 12
754 gitlogo put gray26 -to 11 11 13 14
755 gitlogo put gray26 -to 3 12 5 14
756 gitlogo put gray26 -to 5 13
757 gitlogo put gray26 -to 10 13
758 gitlogo put gray26 -to 4 14 12 15
759 gitlogo put gray26 -to 5 15 11 16
762 image create photo gitlogo32 -width 32 -height 32
763 gitlogo32 copy gitlogo -zoom 2 2
765 wm iconphoto . -default gitlogo gitlogo32
769 ######################################################################
775 if {[lsearch -exact [font names] TkDefaultFont] != -1} {
776 eval [linsert [font actual TkDefaultFont] 0 font configure font_ui]
777 eval [linsert [font actual TkFixedFont] 0 font create font_diff]
779 font create font_diff -family Courier -size 10
782 eval font configure font_ui [font actual [.dummy cget -font]]
787 font create font_uiitalic
788 font create font_uibold
789 font create font_diffbold
790 font create font_diffitalic
792 foreach class {Button Checkbutton Entry Label
793 Labelframe Listbox Message
794 Radiobutton Spinbox Text} {
795 option add *$class.font font_ui
798 option add *Menu.font font_ui
799 option add *Entry.borderWidth 1 startupFile
800 option add *Entry.relief sunken startupFile
801 option add *RadioButton.anchor w startupFile
805 if {[is_Windows] || [is_MacOSX]} {
806 option add *Menu.tearOff 0
817 proc bind_button3 {w cmd} {
818 bind $w <Any-Button-3> $cmd
820 # Mac OS X sends Button-2 on right click through three-button mouse,
821 # or through trackpad right-clicking (two-finger touch + click).
822 bind $w <Any-Button-2> $cmd
823 bind $w <Control-Button-1> $cmd
827 proc apply_config {} {
828 global repo_config font_descs
830 foreach option $font_descs {
831 set name [lindex $option 0]
832 set font [lindex $option 1]
835 foreach {cn cv} $repo_config(gui.$name) {
836 if {$cn eq {-weight}} {
839 font configure $font $cn $cv
842 font configure $font -weight normal
845 error_popup [strcat [mc "Invalid font specified
in %s
:" "gui.
$name"] "\n\n$err"]
847 foreach {cn cv} [font configure $font] {
848 font configure ${font}bold $cn $cv
849 font configure ${font}italic $cn $cv
851 font configure ${font}bold -weight bold
852 font configure ${font}italic -slant italic
858 if {$repo_config(gui.usettk)} {
859 set use_ttk [package vsatisfies [package provide Tk] 8.5]
862 bind [winfo class .] <<ThemeChanged>> [list InitTheme]
868 set default_config(branch.autosetupmerge) true
869 set default_config(merge.tool) {}
870 set default_config(mergetool.keepbackup) true
871 set default_config(merge.diffstat) true
872 set default_config(merge.summary) false
873 set default_config(merge.verbosity) 2
874 set default_config(user.name) {}
875 set default_config(user.email) {}
877 set default_config(gui.encoding) [encoding system]
878 set default_config(gui.matchtrackingbranch) false
879 set default_config(gui.textconv) true
880 set default_config(gui.pruneduringfetch) false
881 set default_config(gui.trustmtime) false
882 set default_config(gui.fastcopyblame) false
883 set default_config(gui.copyblamethreshold) 40
884 set default_config(gui.blamehistoryctx) 7
885 set default_config(gui.diffcontext) 5
886 set default_config(gui.diffopts) {}
887 set default_config(gui.commitmsgwidth) 75
888 set default_config(gui.newbranchtemplate) {}
889 set default_config(gui.spellingdictionary) {}
890 set default_config(gui.fontui) [font configure font_ui]
891 set default_config(gui.fontdiff) [font configure font_diff]
892 # TODO: this option should be added to the git-config documentation
893 set default_config(gui.maxfilesdisplayed) 5000
894 set default_config(gui.usettk) 1
895 set default_config(gui.warndetachedcommit) 1
897 {fontui font_ui {mc "Main Font
"}}
898 {fontdiff font_diff {mc "Diff
/Console Font
"}}
900 set default_config(gui.stageuntracked) ask
902 ######################################################################
906 set _git [_which git]
908 catch {wm withdraw .}
912 -title [mc "git-gui
: fatal error
"] \
913 -message [mc "Cannot
find git
in PATH.
"]
917 ######################################################################
921 if {[catch {set _git_version [git --version]} err]} {
922 catch {wm withdraw .}
926 -title [mc "git-gui
: fatal error
"] \
927 -message "Cannot determine Git version
:
931 [appname
] requires Git
1.5.0 or later.
"
934 if {![regsub {^git version } $_git_version {} _git_version]} {
935 catch {wm withdraw .}
939 -title [mc "git-gui
: fatal error
"] \
940 -message [strcat [mc "Cannot parse Git version string
:"] "\n\n$_git_version"]
944 proc get_trimmed_version {s} {
946 foreach x [split $s -._] {
947 if {[string is integer -strict $x]} {
955 set _real_git_version $_git_version
956 set _git_version [get_trimmed_version $_git_version]
958 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
959 catch {wm withdraw .}
964 -title "[appname
]: warning
" \
965 -message [mc "Git version cannot be determined.
967 %s claims it is version
'%s'.
969 %s requires
at least Git
1.5.0 or later.
971 Assume
'%s' is version
1.5.0?
972 " $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} {
973 set _git_version 1.5.0
978 unset _real_git_version
980 proc git-version {args} {
983 switch [llength $args] {
989 set op [lindex $args 0]
990 set vr [lindex $args 1]
991 set cm [package vcompare $_git_version $vr]
992 return [expr $cm $op 0]
996 set type [lindex $args 0]
997 set name [lindex $args 1]
998 set parm [lindex $args 2]
999 set body [lindex $args 3]
1001 if {($type ne {proc} && $type ne {method})} {
1002 error "Invalid arguments to git-version
"
1004 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
1005 error "Last arm of
$type $name must be default
"
1008 foreach {op vr cb} [lrange $body 0 end-2] {
1009 if {[git-version $op $vr]} {
1010 return [uplevel [list $type $name $parm $cb]]
1014 return [uplevel [list $type $name $parm [lindex $body end]]]
1018 error "git-version
>= x
"
1024 if {[git-version < 1.5]} {
1025 catch {wm withdraw .}
1029 -title [mc "git-gui
: fatal error
"] \
1030 -message "[appname
] requires Git
1.5.0 or later.
1032 You are using
[git-version
]:
1038 ######################################################################
1040 ## configure our library
1042 set idx [file join $oguilib tclIndex]
1043 if {[catch {set fd [open $idx r]} err]} {
1044 catch {wm withdraw .}
1048 -title [mc "git-gui
: fatal error
"] \
1052 if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
1054 while {[gets $fd n] >= 0} {
1055 if {$n ne {} && ![string match #* $n]} {
1067 if {[lsearch -exact $loaded $p] >= 0} continue
1068 source [file join $oguilib $p]
1073 set auto_path [concat [list $oguilib] $auto_path]
1075 unset -nocomplain idx fd
1077 ######################################################################
1079 ## config file parsing
1081 git-version proc _parse_config {arr_name args} {
1088 [list git_read config] \
1090 [list --null --list]]
1091 fconfigure $fd_rc -translation binary
1092 set buf [read $fd_rc]
1095 foreach line [split $buf "\
0"] {
1096 if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} {
1097 if {[is_many_config $name]} {
1098 lappend arr($name) $value
1100 set arr($name) $value
1102 } elseif {[regexp {^([^\n]+)$} $line line name]} {
1103 # no value given, but interpreting them as
1104 # boolean will be handled as true
1113 set fd_rc [eval [list git_read config --list] $args]
1114 while {[gets $fd_rc line] >= 0} {
1115 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
1116 if {[is_many_config $name]} {
1117 lappend arr($name) $value
1119 set arr($name) $value
1121 } elseif {[regexp {^([^=]+)$} $line line name]} {
1122 # no value given, but interpreting them as
1123 # boolean will be handled as true
1132 proc load_config {include_global} {
1133 global repo_config global_config system_config default_config
1135 if {$include_global} {
1136 _parse_config system_config --system
1137 _parse_config global_config --global
1139 _parse_config repo_config
1141 foreach name [array names default_config] {
1142 if {[catch {set v $system_config($name)}]} {
1143 set system_config($name) $default_config($name)
1146 foreach name [array names system_config] {
1147 if {[catch {set v $global_config($name)}]} {
1148 set global_config($name) $system_config($name)
1150 if {[catch {set v $repo_config($name)}]} {
1151 set repo_config($name) $system_config($name)
1156 ######################################################################
1158 ## feature option selection
1160 if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
1165 if {$subcommand eq {gui.sh}} {
1168 if {$subcommand eq {gui} && [llength $argv] > 0} {
1169 set subcommand [lindex $argv 0]
1170 set argv [lrange $argv 1 end]
1173 enable_option multicommit
1174 enable_option branch
1175 enable_option transport
1178 switch -- $subcommand {
1183 disable_option multicommit
1184 disable_option branch
1185 disable_option transport
1188 enable_option singlecommit
1189 enable_option retcode
1191 disable_option multicommit
1192 disable_option branch
1193 disable_option transport
1195 while {[llength $argv] > 0} {
1196 set a [lindex $argv 0]
1199 enable_option initialamend
1202 enable_option nocommit
1203 enable_option nocommitmsg
1206 disable_option nocommitmsg
1213 set argv [lrange $argv 1 end]
1218 ######################################################################
1220 ## execution environment
1222 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1224 # Suggest our implementation of askpass, if none is set
1225 if {![info exists env(SSH_ASKPASS)]} {
1226 set env(SSH_ASKPASS) [gitexec git-gui--askpass]
1229 ######################################################################
1235 set _gitdir $env(GIT_DIR)
1239 # beware that from the .git dir this sets _gitdir to .
1240 # and _prefix to the empty string
1241 set _gitdir [git rev-parse --git-dir]
1242 set _prefix [git rev-parse --show-prefix]
1246 choose_repository::pick
1250 # we expand the _gitdir when it's just a single dot (i.e. when we're being
1251 # run from the .git dir itself) lest the routines to find the worktree
1253 if {$_gitdir eq ".
"} {
1257 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
1258 catch {set _gitdir [exec cygpath --windows $_gitdir]}
1260 if {![file isdirectory $_gitdir]} {
1261 catch {wm withdraw .}
1262 error_popup [strcat [mc "Git directory not found
:"] "\n\n$_gitdir"]
1265 # _gitdir exists, so try loading the config
1269 # v1.7.0 introduced --show-toplevel to return the canonical work-tree
1270 if {[package vsatisfies $_git_version 1.7.0]} {
1271 set _gitworktree [git rev-parse --show-toplevel]
1273 # try to set work tree from environment, core.worktree or use
1274 # cdup to obtain a relative path to the top of the worktree. If
1275 # run from the top, the ./ prefix ensures normalize expands pwd.
1276 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
1277 set _gitworktree [get_config core.worktree]
1278 if {$_gitworktree eq ""} {
1279 set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
1284 if {$_prefix ne {}} {
1285 if {$_gitworktree eq {}} {
1286 regsub -all {[^/]+/} $_prefix ../ cdup
1288 set cdup $_gitworktree
1290 if {[catch {cd $cdup} err]} {
1291 catch {wm withdraw .}
1292 error_popup [strcat [mc "Cannot move to top of working directory
:"] "\n\n$err"]
1295 set _gitworktree [pwd]
1297 } elseif {![is_enabled bare]} {
1299 catch {wm withdraw .}
1300 error_popup [strcat [mc "Cannot use bare repository
:"] "\n\n$_gitdir"]
1303 if {$_gitworktree eq {}} {
1304 set _gitworktree [file dirname $_gitdir]
1306 if {[catch {cd $_gitworktree} err]} {
1307 catch {wm withdraw .}
1308 error_popup [strcat [mc "No working directory
"] " $_gitworktree:\n\n$err"]
1311 set _gitworktree [pwd]
1313 set _reponame [file split [file normalize $_gitdir]]
1314 if {[lindex $_reponame end] eq {.git}} {
1315 set _reponame [lindex $_reponame end-1]
1317 set _reponame [lindex $_reponame end]
1320 ######################################################################
1324 set current_diff_path {}
1325 set current_diff_side {}
1326 set diff_actions [list]
1330 set MERGE_HEAD [list]
1333 set current_branch {}
1335 set current_diff_path {}
1337 set is_submodule_diff 0
1338 set is_conflict_diff 0
1339 set selected_commit_type new
1340 set diff_empty_count 0
1342 set nullid "0000000000000000000000000000000000000000"
1343 set nullid2 "0000000000000000000000000000000000000001"
1345 ######################################################################
1353 set disable_on_lock [list]
1354 set index_lock_type none
1356 proc lock_index {type} {
1357 global index_lock_type disable_on_lock
1359 if {$index_lock_type eq {none}} {
1360 set index_lock_type $type
1361 foreach w $disable_on_lock {
1362 uplevel #0 $w disabled
1365 } elseif {$index_lock_type eq "begin-
$type"} {
1366 set index_lock_type $type
1372 proc unlock_index {} {
1373 global index_lock_type disable_on_lock
1375 set index_lock_type none
1376 foreach w $disable_on_lock {
1377 uplevel #0 $w normal
1381 ######################################################################
1385 proc repository_state {ctvar hdvar mhvar} {
1386 global current_branch
1387 upvar $ctvar ct $hdvar hd $mhvar mh
1392 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
1398 set merge_head [gitdir MERGE_HEAD]
1399 if {[file exists $merge_head]} {
1401 set fd_mh [open $merge_head r]
1402 while {[gets $fd_mh line] >= 0} {
1413 global PARENT empty_tree
1415 set p [lindex $PARENT 0]
1419 if {$empty_tree eq {}} {
1420 set empty_tree [git mktree << {}]
1425 proc force_amend {} {
1426 global selected_commit_type
1427 global HEAD PARENT MERGE_HEAD commit_type
1429 repository_state newType newHEAD newMERGE_HEAD
1432 set MERGE_HEAD $newMERGE_HEAD
1433 set commit_type $newType
1435 set selected_commit_type amend
1436 do_select_commit_type
1439 proc rescan {after {honor_trustmtime 1}} {
1440 global HEAD PARENT MERGE_HEAD commit_type
1441 global ui_index ui_workdir ui_comm
1442 global rescan_active file_states
1445 if {$rescan_active > 0 || ![lock_index read]} return
1447 repository_state newType newHEAD newMERGE_HEAD
1448 if {[string match amend* $commit_type]
1449 && $newType eq {normal}
1450 && $newHEAD eq $HEAD} {
1454 set MERGE_HEAD $newMERGE_HEAD
1455 set commit_type $newType
1458 array unset file_states
1460 if {!$::GITGUI_BCK_exists &&
1461 (![$ui_comm edit modified]
1462 || [string trim [$ui_comm get 0.0 end]] eq {})} {
1463 if {[string match amend* $commit_type]} {
1464 } elseif {[load_message GITGUI_MSG utf-8]} {
1465 } elseif {[run_prepare_commit_msg_hook]} {
1466 } elseif {[load_message MERGE_MSG]} {
1467 } elseif {[load_message SQUASH_MSG]} {
1470 $ui_comm edit modified false
1473 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
1474 rescan_stage2 {} $after
1477 ui_status [mc "Refreshing
file status...
"]
1478 set fd_rf [git_read update-index \
1484 fconfigure $fd_rf -blocking 0 -translation binary
1485 fileevent $fd_rf readable \
1486 [list rescan_stage2 $fd_rf $after]
1491 set is_git_info_exclude {}
1492 proc have_info_exclude {} {
1493 global is_git_info_exclude
1495 if {$is_git_info_exclude eq {}} {
1496 if {[catch {exec test -f [gitdir info exclude]}]} {
1497 set is_git_info_exclude 0
1499 set is_git_info_exclude 1
1502 return $is_git_info_exclude
1505 proc have_info_exclude {} {
1506 return [file readable [gitdir info exclude]]
1510 proc rescan_stage2 {fd after} {
1511 global rescan_active buf_rdi buf_rdf buf_rlo
1515 if {![eof $fd]} return
1519 if {[package vsatisfies $::_git_version 1.6.3]} {
1520 set ls_others [list --exclude-standard]
1522 set ls_others [list --exclude-per-directory=.gitignore]
1523 if {[have_info_exclude]} {
1524 lappend ls_others "--exclude-from=[gitdir info exclude
]"
1526 set user_exclude [get_config core.excludesfile]
1527 if {$user_exclude ne {} && [file readable $user_exclude]} {
1528 lappend ls_others "--exclude-from=[file normalize
$user_exclude]"
1537 ui_status [mc "Scanning
for modified files ...
"]
1538 set fd_di [git_read diff-index --cached -z [PARENT]]
1539 set fd_df [git_read diff-files -z]
1540 set fd_lo [eval git_read ls-files --others -z $ls_others]
1542 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1543 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
1544 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
1545 fileevent $fd_di readable [list read_diff_index $fd_di $after]
1546 fileevent $fd_df readable [list read_diff_files $fd_df $after]
1547 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
1550 proc load_message {file {encoding {}}} {
1553 set f [gitdir $file]
1554 if {[file isfile $f]} {
1555 if {[catch {set fd [open $f r]}]} {
1558 fconfigure $fd -eofchar {}
1559 if {$encoding ne {}} {
1560 fconfigure $fd -encoding $encoding
1562 set content [string trim [read $fd]]
1564 regsub -all -line {[ \r\t]+$} $content {} content
1565 $ui_comm delete 0.0 end
1566 $ui_comm insert end $content
1572 proc run_prepare_commit_msg_hook {} {
1575 # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
1576 # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
1577 # empty file but existent file.
1579 set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
1581 if {[file isfile [gitdir MERGE_MSG]]} {
1582 set pcm_source "merge
"
1583 set fd_mm [open [gitdir MERGE_MSG] r]
1584 puts -nonewline $fd_pcm [read $fd_mm]
1586 } elseif {[file isfile [gitdir SQUASH_MSG]]} {
1587 set pcm_source "squash
"
1588 set fd_sm [open [gitdir SQUASH_MSG] r]
1589 puts -nonewline $fd_pcm [read $fd_sm]
1597 set fd_ph [githook_read prepare-commit-msg \
1598 [gitdir PREPARE_COMMIT_MSG] $pcm_source]
1600 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1604 ui_status [mc "Calling prepare-commit-msg hook...
"]
1607 fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
1608 fileevent $fd_ph readable \
1609 [list prepare_commit_msg_hook_wait $fd_ph]
1614 proc prepare_commit_msg_hook_wait {fd_ph} {
1617 append pch_error [read $fd_ph]
1618 fconfigure $fd_ph -blocking 1
1620 if {[catch {close $fd_ph}]} {
1621 ui_status [mc "Commit declined by prepare-commit-msg hook.
"]
1622 hook_failed_popup prepare-commit-msg $pch_error
1623 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1626 load_message PREPARE_COMMIT_MSG
1629 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1632 fconfigure $fd_ph -blocking 0
1633 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1636 proc read_diff_index {fd after} {
1639 append buf_rdi [read $fd]
1641 set n [string length $buf_rdi]
1643 set z1 [string first "\
0" $buf_rdi $c]
1644 if {$z1 == -1} break
1646 set z2 [string first "\
0" $buf_rdi $z1]
1647 if {$z2 == -1} break
1650 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
1651 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
1653 [encoding convertfrom $p] \
1655 [list [lindex $i 0] [lindex $i 2]] \
1661 set buf_rdi [string range $buf_rdi $c end]
1666 rescan_done $fd buf_rdi $after
1669 proc read_diff_files {fd after} {
1672 append buf_rdf [read $fd]
1674 set n [string length $buf_rdf]
1676 set z1 [string first "\
0" $buf_rdf $c]
1677 if {$z1 == -1} break
1679 set z2 [string first "\
0" $buf_rdf $z1]
1680 if {$z2 == -1} break
1683 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
1684 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
1686 [encoding convertfrom $p] \
1689 [list [lindex $i 0] [lindex $i 2]]
1694 set buf_rdf [string range $buf_rdf $c end]
1699 rescan_done $fd buf_rdf $after
1702 proc read_ls_others {fd after} {
1705 append buf_rlo [read $fd]
1706 set pck [split $buf_rlo "\
0"]
1707 set buf_rlo [lindex $pck end]
1708 foreach p [lrange $pck 0 end-1] {
1709 set p [encoding convertfrom $p]
1710 if {[string index $p end] eq {/}} {
1711 set p [string range $p 0 end-1]
1715 rescan_done $fd buf_rlo $after
1718 proc rescan_done {fd buf after} {
1719 global rescan_active current_diff_path
1720 global file_states repo_config
1723 if {![eof $fd]} return
1726 if {[incr rescan_active -1] > 0} return
1731 if {$current_diff_path ne {}} { reshow_diff $after }
1732 if {$current_diff_path eq {}} { select_first_diff $after }
1735 proc prune_selection {} {
1736 global file_states selected_paths
1738 foreach path [array names selected_paths] {
1739 if {[catch {set still_here $file_states($path)}]} {
1740 unset selected_paths($path)
1745 ######################################################################
1749 proc mapicon {w state path} {
1752 if {[catch {set r $all_icons($state$w)}]} {
1753 puts "error
: no icon
for $w state
={$state} $path"
1759 proc mapdesc {state path} {
1762 if {[catch {set r $all_descs($state)}]} {
1763 puts "error
: no desc
for state
={$state} $path"
1769 proc ui_status {msg} {
1771 if {[info exists main_status]} {
1772 $main_status show $msg
1776 proc ui_ready {{test {}}} {
1778 if {[info exists main_status]} {
1779 $main_status show [mc "Ready.
"] $test
1783 proc escape_path {path} {
1784 regsub -all {\\} $path "\\\\" path
1785 regsub -all "\n" $path "\\n
" path
1789 proc short_path {path} {
1790 return [escape_path [lindex [file split $path] end]]
1794 set null_sha1 [string repeat 0 40]
1796 proc merge_state {path new_state {head_info {}} {index_info {}}} {
1797 global file_states next_icon_id null_sha1
1799 set s0 [string index $new_state 0]
1800 set s1 [string index $new_state 1]
1802 if {[catch {set info $file_states($path)}]} {
1804 set icon n[incr next_icon_id]
1806 set state [lindex $info 0]
1807 set icon [lindex $info 1]
1808 if {$head_info eq {}} {set head_info [lindex $info 2]}
1809 if {$index_info eq {}} {set index_info [lindex $info 3]}
1812 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1813 elseif {$s0 eq {_}} {set s0 _}
1815 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1816 elseif {$s1 eq {_}} {set s1 _}
1818 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1819 set head_info [list 0 $null_sha1]
1820 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1821 && $head_info eq {}} {
1822 set head_info $index_info
1823 } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
1824 set index_info $head_info
1828 set file_states($path) [list $s0$s1 $icon \
1829 $head_info $index_info \
1834 proc display_file_helper {w path icon_name old_m new_m} {
1837 if {$new_m eq {_}} {
1838 set lno [lsearch -sorted -exact $file_lists($w) $path]
1840 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
1842 $w conf -state normal
1843 $w delete $lno.0 [expr {$lno + 1}].0
1844 $w conf -state disabled
1846 } elseif {$old_m eq {_} && $new_m ne {_}} {
1847 lappend file_lists($w) $path
1848 set file_lists($w) [lsort -unique $file_lists($w)]
1849 set lno [lsearch -sorted -exact $file_lists($w) $path]
1851 $w conf -state normal
1852 $w image create $lno.0 \
1853 -align center -padx 5 -pady 1 \
1855 -image [mapicon $w $new_m $path]
1856 $w insert $lno.1 "[escape_path
$path]\n"
1857 $w conf -state disabled
1858 } elseif {$old_m ne $new_m} {
1859 $w conf -state normal
1860 $w image conf $icon_name -image [mapicon $w $new_m $path]
1861 $w conf -state disabled
1865 proc display_file {path state} {
1866 global file_states selected_paths
1867 global ui_index ui_workdir
1869 set old_m [merge_state $path $state]
1870 set s $file_states($path)
1871 set new_m [lindex $s 0]
1872 set icon_name [lindex $s 1]
1874 set o [string index $old_m 0]
1875 set n [string index $new_m 0]
1882 display_file_helper $ui_index $path $icon_name $o $n
1884 if {[string index $old_m 0] eq {U}} {
1887 set o [string index $old_m 1]
1889 if {[string index $new_m 0] eq {U}} {
1892 set n [string index $new_m 1]
1894 display_file_helper $ui_workdir $path $icon_name $o $n
1896 if {$new_m eq {__}} {
1897 unset file_states($path)
1898 catch {unset selected_paths($path)}
1902 proc display_all_files_helper {w path icon_name m} {
1905 lappend file_lists($w) $path
1906 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1907 $w image create end \
1908 -align center -padx 5 -pady 1 \
1910 -image [mapicon $w $m $path]
1911 $w insert end "[escape_path
$path]\n"
1915 proc display_all_files {} {
1916 global ui_index ui_workdir
1917 global file_states file_lists
1919 global files_warning
1921 $ui_index conf -state normal
1922 $ui_workdir conf -state normal
1924 $ui_index delete 0.0 end
1925 $ui_workdir delete 0.0 end
1928 set file_lists($ui_index) [list]
1929 set file_lists($ui_workdir) [list]
1931 set to_display [lsort [array names file_states]]
1932 set display_limit [get_config gui.maxfilesdisplayed]
1933 if {[llength $to_display] > $display_limit} {
1934 if {!$files_warning} {
1935 # do not repeatedly warn:
1937 info_popup [mc "Displaying only
%s of
%s files.
" \
1938 $display_limit [llength $to_display]]
1940 set to_display [lrange $to_display 0 [expr {$display_limit-1}]]
1942 foreach path $to_display {
1943 set s $file_states($path)
1945 set icon_name [lindex $s 1]
1947 set s [string index $m 0]
1948 if {$s ne {U} && $s ne {_}} {
1949 display_all_files_helper $ui_index $path \
1953 if {[string index $m 0] eq {U}} {
1956 set s [string index $m 1]
1959 display_all_files_helper $ui_workdir $path \
1964 $ui_index conf -state disabled
1965 $ui_workdir conf -state disabled
1968 ######################################################################
1973 #define mask_width 14
1974 #define mask_height 15
1975 static unsigned char mask_bits[] = {
1976 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1977 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1978 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1981 image create bitmap file_plain -background white -foreground black -data {
1982 #define plain_width 14
1983 #define plain_height 15
1984 static unsigned char plain_bits[] = {
1985 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1986 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1987 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1988 } -maskdata $filemask
1990 image create bitmap file_mod -background white -foreground blue -data {
1991 #define mod_width 14
1992 #define mod_height 15
1993 static unsigned char mod_bits[] = {
1994 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1995 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1996 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1997 } -maskdata $filemask
1999 image create bitmap file_fulltick -background white -foreground "#007000" -data {
2000 #define file_fulltick_width 14
2001 #define file_fulltick_height 15
2002 static unsigned char file_fulltick_bits
[] = {
2003 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
2004 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
2005 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2006 } -maskdata $filemask
2008 image create bitmap file_question
-background white
-foreground black
-data {
2009 #define file_question_width 14
2010 #define file_question_height 15
2011 static unsigned char file_question_bits
[] = {
2012 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
2013 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
2014 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2015 } -maskdata $filemask
2017 image create bitmap file_removed
-background white
-foreground red
-data {
2018 #define file_removed_width 14
2019 #define file_removed_height 15
2020 static unsigned char file_removed_bits
[] = {
2021 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2022 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
2023 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
2024 } -maskdata $filemask
2026 image create bitmap file_merge
-background white
-foreground blue
-data {
2027 #define file_merge_width 14
2028 #define file_merge_height 15
2029 static unsigned char file_merge_bits
[] = {
2030 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
2031 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2032 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
2033 } -maskdata $filemask
2035 image create bitmap file_statechange
-background white
-foreground green
-data {
2036 #define file_statechange_width 14
2037 #define file_statechange_height 15
2038 static unsigned char file_statechange_bits
[] = {
2039 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
2040 0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
2041 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
2042 } -maskdata $filemask
2044 set ui_index .vpane.files.index.list
2045 set ui_workdir .vpane.files.workdir.list
2047 set all_icons
(_
$ui_index) file_plain
2048 set all_icons
(A
$ui_index) file_plain
2049 set all_icons
(M
$ui_index) file_fulltick
2050 set all_icons
(D
$ui_index) file_removed
2051 set all_icons
(U
$ui_index) file_merge
2052 set all_icons
(T
$ui_index) file_statechange
2054 set all_icons
(_
$ui_workdir) file_plain
2055 set all_icons
(M
$ui_workdir) file_mod
2056 set all_icons
(D
$ui_workdir) file_question
2057 set all_icons
(U
$ui_workdir) file_merge
2058 set all_icons
(O
$ui_workdir) file_plain
2059 set all_icons
(T
$ui_workdir) file_statechange
2061 set max_status_desc
0
2063 {__
{mc
"Unmodified"}}
2065 {_M
{mc
"Modified, not staged"}}
2066 {M_
{mc
"Staged for commit"}}
2067 {MM
{mc
"Portions staged for commit"}}
2068 {MD
{mc
"Staged for commit, missing"}}
2070 {_T
{mc
"File type changed, not staged"}}
2071 {MT
{mc
"File type changed, old type staged for commit"}}
2072 {AT
{mc
"File type changed, old type staged for commit"}}
2073 {T_
{mc
"File type changed, staged"}}
2074 {TM
{mc
"File type change staged, modification not staged"}}
2075 {TD
{mc
"File type change staged, file missing"}}
2077 {_O
{mc
"Untracked, not staged"}}
2078 {A_
{mc
"Staged for commit"}}
2079 {AM
{mc
"Portions staged for commit"}}
2080 {AD
{mc
"Staged for commit, missing"}}
2083 {D_
{mc
"Staged for removal"}}
2084 {DO
{mc
"Staged for removal, still present"}}
2086 {_U
{mc
"Requires merge resolution"}}
2087 {U_
{mc
"Requires merge resolution"}}
2088 {UU
{mc
"Requires merge resolution"}}
2089 {UM
{mc
"Requires merge resolution"}}
2090 {UD
{mc
"Requires merge resolution"}}
2091 {UT
{mc
"Requires merge resolution"}}
2093 set text
[eval [lindex
$i 1]]
2094 if {$max_status_desc < [string length
$text]} {
2095 set max_status_desc
[string length
$text]
2097 set all_descs
([lindex
$i 0]) $text
2101 ######################################################################
2105 proc scrollbar2many
{list mode args
} {
2106 foreach w
$list {eval $w $mode $args}
2109 proc many2scrollbar
{list mode sb top bottom
} {
2110 $sb set $top $bottom
2111 foreach w
$list {$w $mode moveto
$top}
2114 proc incr_font_size
{font
{amt
1}} {
2115 set sz
[font configure
$font -size]
2117 font configure
$font -size $sz
2118 font configure
${font}bold
-size $sz
2119 font configure
${font}italic
-size $sz
2122 ######################################################################
2126 set starting_gitk_msg
[mc
"Starting gitk... please wait..."]
2128 proc do_gitk
{revs
{is_submodule false
}} {
2129 global current_diff_path file_states current_diff_side ui_index
2132 # -- Always start gitk through whatever we were loaded with. This
2133 # lets us bypass using shell process on Windows systems.
2135 set exe
[_which gitk
-script]
2136 set cmd
[list
[info nameofexecutable
] $exe]
2138 error_popup
[mc
"Couldn't find gitk in PATH"]
2142 if {[info exists env
(GIT_DIR
)]} {
2143 set old_GIT_DIR
$env(GIT_DIR
)
2150 if {!$is_submodule} {
2154 set env
(GIT_DIR
) [file normalize
[gitdir
]]
2156 cd $current_diff_path
2157 if {$revs eq
{--}} {
2158 set s
$file_states($current_diff_path)
2161 switch
-glob -- [lindex
$s 0] {
2162 M_
{ set old_sha1
[lindex
[lindex
$s 2] 1] }
2163 _M
{ set old_sha1
[lindex
[lindex
$s 3] 1] }
2165 if {$current_diff_side eq
$ui_index} {
2166 set old_sha1
[lindex
[lindex
$s 2] 1]
2167 set new_sha1
[lindex
[lindex
$s 3] 1]
2169 set old_sha1
[lindex
[lindex
$s 3] 1]
2173 set revs
$old_sha1...
$new_sha1
2175 if {[info exists env
(GIT_DIR
)]} {
2179 eval exec $cmd $revs "--" "--" &
2181 if {$old_GIT_DIR ne
{}} {
2182 set env
(GIT_DIR
) $old_GIT_DIR
2186 ui_status $
::starting_gitk_msg
2188 ui_ready
$starting_gitk_msg
2193 proc do_git_gui
{} {
2194 global current_diff_path
2196 # -- Always start git gui through whatever we were loaded with. This
2197 # lets us bypass using shell process on Windows systems.
2199 set exe
[list
[_which git
]]
2201 error_popup
[mc
"Couldn't find git gui in PATH"]
2205 if {[info exists env
(GIT_DIR
)]} {
2206 set old_GIT_DIR
$env(GIT_DIR
)
2213 cd $current_diff_path
2215 eval exec $exe gui
&
2217 if {$old_GIT_DIR ne
{}} {
2218 set env
(GIT_DIR
) $old_GIT_DIR
2222 ui_status $
::starting_gitk_msg
2224 ui_ready
$starting_gitk_msg
2229 proc do_explore
{} {
2232 if {[is_Cygwin
] ||
[is_Windows
]} {
2233 set explorer
"explorer.exe"
2234 } elseif
{[is_MacOSX
]} {
2237 # freedesktop.org-conforming system is our best shot
2238 set explorer
"xdg-open"
2240 eval exec $explorer [list
[file nativename
$_gitworktree]] &
2246 proc terminate_me
{win
} {
2248 if {$win ne
{.
}} return
2252 proc do_quit
{{rc
{1}}} {
2253 global ui_comm is_quitting repo_config commit_type
2254 global GITGUI_BCK_exists GITGUI_BCK_i
2255 global ui_comm_spell
2256 global ret_code use_ttk
2258 if {$is_quitting} return
2261 if {[winfo exists
$ui_comm]} {
2262 # -- Stash our current commit buffer.
2264 set save
[gitdir GITGUI_MSG
]
2265 if {$GITGUI_BCK_exists && ![$ui_comm edit modified
]} {
2266 file rename
-force [gitdir GITGUI_BCK
] $save
2267 set GITGUI_BCK_exists
0
2269 set msg
[string trim
[$ui_comm get
0.0 end
]]
2270 regsub
-all -line {[ \r\t]+$
} $msg {} msg
2271 if {(![string match amend
* $commit_type]
2272 ||
[$ui_comm edit modified
])
2275 set fd
[open
$save w
]
2276 fconfigure
$fd -encoding utf-8
2277 puts
-nonewline $fd $msg
2281 catch
{file delete
$save}
2285 # -- Cancel our spellchecker if its running.
2287 if {[info exists ui_comm_spell
]} {
2291 # -- Remove our editor backup, its not needed.
2293 after cancel
$GITGUI_BCK_i
2294 if {$GITGUI_BCK_exists} {
2295 catch
{file delete
[gitdir GITGUI_BCK
]}
2298 # -- Stash our current window geometry into this repository.
2300 set cfg_wmstate
[wm state .
]
2301 if {[catch
{set rc_wmstate
$repo_config(gui.wmstate
)}]} {
2304 if {$cfg_wmstate ne
$rc_wmstate} {
2305 catch
{git config gui.wmstate
$cfg_wmstate}
2307 if {$cfg_wmstate eq
{zoomed
}} {
2308 # on Windows wm geometry will lie about window
2309 # position (but not size) when window is zoomed
2310 # restore the window before querying wm geometry
2313 set cfg_geometry
[list
]
2314 lappend cfg_geometry
[wm geometry .
]
2316 lappend cfg_geometry
[.vpane sashpos
0]
2317 lappend cfg_geometry
[.vpane.files sashpos
0]
2319 lappend cfg_geometry
[lindex
[.vpane sash coord
0] 0]
2320 lappend cfg_geometry
[lindex
[.vpane.files sash coord
0] 1]
2322 if {[catch
{set rc_geometry
$repo_config(gui.geometry
)}]} {
2325 if {$cfg_geometry ne
$rc_geometry} {
2326 catch
{git config gui.geometry
$cfg_geometry}
2332 # Briefly enable send again, working around Tk bug
2333 # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
2334 tk appname
[appname
]
2343 proc ui_do_rescan
{} {
2344 rescan
{force_first_diff ui_ready
}
2351 proc next_diff
{{after
{}}} {
2352 global next_diff_p next_diff_w next_diff_i
2353 show_diff
$next_diff_p $next_diff_w {} {} $after
2356 proc find_anchor_pos
{lst name
} {
2357 set lid
[lsearch
-sorted -exact $lst $name]
2361 foreach lname
$lst {
2362 if {$lname >= $name} break
2370 proc find_file_from
{flist idx delta path mmask
} {
2373 set len
[llength
$flist]
2374 while {$idx >= 0 && $idx < $len} {
2375 set name
[lindex
$flist $idx]
2377 if {$name ne
$path && [info exists file_states
($name)]} {
2378 set state
[lindex
$file_states($name) 0]
2380 if {$mmask eq
{} ||
[regexp
$mmask $state]} {
2391 proc find_next_diff
{w path
{lno
{}} {mmask
{}}} {
2392 global next_diff_p next_diff_w next_diff_i
2393 global file_lists ui_index ui_workdir
2395 set flist
$file_lists($w)
2397 set lno
[find_anchor_pos
$flist $path]
2402 if {$mmask ne
{} && ![regexp
{(^\^
)|
(\$$
)} $mmask]} {
2403 if {$w eq
$ui_index} {
2406 set mmask
"$mmask\$"
2410 set idx
[find_file_from
$flist $lno 1 $path $mmask]
2413 set idx
[find_file_from
$flist $lno -1 $path $mmask]
2418 set next_diff_p
[lindex
$flist $idx]
2419 set next_diff_i
[expr {$idx+1}]
2426 proc next_diff_after_action
{w path
{lno
{}} {mmask
{}}} {
2427 global current_diff_path
2429 if {$path ne
$current_diff_path} {
2431 } elseif
{[find_next_diff
$w $path $lno $mmask]} {
2434 return {reshow_diff
;}
2438 proc select_first_diff
{after
} {
2441 if {[find_next_diff
$ui_workdir {} 1 {^_?U
}] ||
2442 [find_next_diff
$ui_workdir {} 1 {[^O
]$
}]} {
2449 proc force_first_diff
{after
} {
2450 global ui_workdir current_diff_path file_states
2452 if {[info exists file_states
($current_diff_path)]} {
2453 set state
[lindex
$file_states($current_diff_path) 0]
2459 if {[string first
{U
} $state] >= 0} {
2460 # Already a conflict, do nothing
2461 } elseif
{[find_next_diff
$ui_workdir $current_diff_path {} {^_?U
}]} {
2463 } elseif
{[string index
$state 1] ne
{O
}} {
2464 # Already a diff & no conflicts, do nothing
2465 } elseif
{[find_next_diff
$ui_workdir $current_diff_path {} {[^O
]$
}]} {
2476 proc toggle_or_diff
{w x y
} {
2477 global file_states file_lists current_diff_path ui_index ui_workdir
2478 global last_clicked selected_paths
2480 set pos
[split [$w index @
$x,$y] .
]
2481 set lno
[lindex
$pos 0]
2482 set col [lindex
$pos 1]
2483 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
2489 set last_clicked
[list
$w $lno]
2490 array
unset selected_paths
2491 $ui_index tag remove in_sel
0.0 end
2492 $ui_workdir tag remove in_sel
0.0 end
2494 # Determine the state of the file
2495 if {[info exists file_states
($path)]} {
2496 set state
[lindex
$file_states($path) 0]
2501 # Restage the file, or simply show the diff
2502 if {$col == 0 && $y > 1} {
2503 # Conflicts need special handling
2504 if {[string first
{U
} $state] >= 0} {
2505 # $w must always be $ui_workdir, but...
2506 if {$w ne
$ui_workdir} { set lno
{} }
2507 merge_stage_workdir
$path $lno
2511 if {[string index
$state 1] eq
{O
}} {
2517 set after
[next_diff_after_action
$w $path $lno $mmask]
2519 if {$w eq
$ui_index} {
2521 "Unstaging [short_path $path] from commit" \
2523 [concat
$after [list ui_ready
]]
2524 } elseif
{$w eq
$ui_workdir} {
2526 "Adding [short_path $path]" \
2528 [concat
$after [list ui_ready
]]
2531 set selected_paths
($path) 1
2532 show_diff
$path $w $lno
2536 proc add_one_to_selection
{w x y
} {
2537 global file_lists last_clicked selected_paths
2539 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
2540 set path
[lindex
$file_lists($w) [expr {$lno - 1}]]
2546 if {$last_clicked ne
{}
2547 && [lindex
$last_clicked 0] ne
$w} {
2548 array
unset selected_paths
2549 [lindex
$last_clicked 0] tag remove in_sel
0.0 end
2552 set last_clicked
[list
$w $lno]
2553 if {[catch
{set in_sel
$selected_paths($path)}]} {
2557 unset selected_paths
($path)
2558 $w tag remove in_sel
$lno.0 [expr {$lno + 1}].0
2560 set selected_paths
($path) 1
2561 $w tag add in_sel
$lno.0 [expr {$lno + 1}].0
2565 proc add_range_to_selection
{w x y
} {
2566 global file_lists last_clicked selected_paths
2568 if {[lindex
$last_clicked 0] ne
$w} {
2569 toggle_or_diff
$w $x $y
2573 set lno
[lindex
[split [$w index @
$x,$y] .
] 0]
2574 set lc
[lindex
$last_clicked 1]
2583 foreach path
[lrange
$file_lists($w) \
2584 [expr {$begin - 1}] \
2585 [expr {$end - 1}]] {
2586 set selected_paths
($path) 1
2588 $w tag add in_sel
$begin.0 [expr {$end + 1}].0
2591 proc show_more_context
{} {
2593 if {$repo_config(gui.diffcontext
) < 99} {
2594 incr repo_config
(gui.diffcontext
)
2599 proc show_less_context
{} {
2601 if {$repo_config(gui.diffcontext
) > 1} {
2602 incr repo_config
(gui.diffcontext
) -1
2607 ######################################################################
2615 menu .mbar
-tearoff 0
2617 # -- Apple Menu (Mac OS X only)
2619 .mbar add cascade
-label Apple
-menu .mbar.apple
2622 .mbar add cascade
-label [mc Repository
] -menu .mbar.repository
2623 .mbar add cascade
-label [mc Edit
] -menu .mbar.edit
2624 if {[is_enabled branch
]} {
2625 .mbar add cascade
-label [mc Branch
] -menu .mbar.branch
2627 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2628 .mbar add cascade
-label [mc Commit@@noun
] -menu .mbar.commit
2630 if {[is_enabled transport
]} {
2631 .mbar add cascade
-label [mc Merge
] -menu .mbar.merge
2632 .mbar add cascade
-label [mc Remote
] -menu .mbar.remote
2634 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2635 .mbar add cascade
-label [mc Tools
] -menu .mbar.tools
2638 # -- Repository Menu
2640 menu .mbar.repository
2643 .mbar.repository add
command \
2644 -label [mc
"Explore Working Copy"] \
2645 -command {do_explore
}
2646 .mbar.repository add separator
2649 .mbar.repository add
command \
2650 -label [mc
"Browse Current Branch's Files"] \
2651 -command {browser
::new
$current_branch}
2652 set ui_browse_current
[.mbar.repository index last
]
2653 .mbar.repository add
command \
2654 -label [mc
"Browse Branch Files..."] \
2655 -command browser_open
::dialog
2656 .mbar.repository add separator
2658 .mbar.repository add
command \
2659 -label [mc
"Visualize Current Branch's History"] \
2660 -command {do_gitk
$current_branch}
2661 set ui_visualize_current
[.mbar.repository index last
]
2662 .mbar.repository add
command \
2663 -label [mc
"Visualize All Branch History"] \
2664 -command {do_gitk
--all}
2665 .mbar.repository add separator
2667 proc current_branch_write
{args
} {
2668 global current_branch
2669 .mbar.repository entryconf $
::ui_browse_current \
2670 -label [mc
"Browse %s's Files" $current_branch]
2671 .mbar.repository entryconf $
::ui_visualize_current \
2672 -label [mc
"Visualize %s's History" $current_branch]
2674 trace add variable current_branch
write current_branch_write
2676 if {[is_enabled multicommit
]} {
2677 .mbar.repository add
command -label [mc
"Database Statistics"] \
2680 .mbar.repository add
command -label [mc
"Compress Database"] \
2683 .mbar.repository add
command -label [mc
"Verify Database"] \
2684 -command do_fsck_objects
2686 .mbar.repository add separator
2689 .mbar.repository add
command \
2690 -label [mc
"Create Desktop Icon"] \
2691 -command do_cygwin_shortcut
2692 } elseif
{[is_Windows
]} {
2693 .mbar.repository add
command \
2694 -label [mc
"Create Desktop Icon"] \
2695 -command do_windows_shortcut
2696 } elseif
{[is_MacOSX
]} {
2697 .mbar.repository add
command \
2698 -label [mc
"Create Desktop Icon"] \
2699 -command do_macosx_app
2704 proc
::tk
::mac
::Quit
{args
} { do_quit
}
2706 .mbar.repository add
command -label [mc Quit
] \
2714 .mbar.edit add
command -label [mc Undo
] \
2715 -command {catch
{[focus
] edit undo
}} \
2717 .mbar.edit add
command -label [mc Redo
] \
2718 -command {catch
{[focus
] edit redo
}} \
2720 .mbar.edit add separator
2721 .mbar.edit add
command -label [mc Cut
] \
2722 -command {catch
{tk_textCut
[focus
]}} \
2724 .mbar.edit add
command -label [mc Copy
] \
2725 -command {catch
{tk_textCopy
[focus
]}} \
2727 .mbar.edit add
command -label [mc Paste
] \
2728 -command {catch
{tk_textPaste
[focus
]; [focus
] see insert
}} \
2730 .mbar.edit add
command -label [mc Delete
] \
2731 -command {catch
{[focus
] delete sel.first sel.last
}} \
2733 .mbar.edit add separator
2734 .mbar.edit add
command -label [mc
"Select All"] \
2735 -command {catch
{[focus
] tag add sel
0.0 end
}} \
2740 if {[is_enabled branch
]} {
2743 .mbar.branch add
command -label [mc
"Create..."] \
2744 -command branch_create
::dialog \
2746 lappend disable_on_lock
[list .mbar.branch entryconf \
2747 [.mbar.branch index last
] -state]
2749 .mbar.branch add
command -label [mc
"Checkout..."] \
2750 -command branch_checkout
::dialog \
2752 lappend disable_on_lock
[list .mbar.branch entryconf \
2753 [.mbar.branch index last
] -state]
2755 .mbar.branch add
command -label [mc
"Rename..."] \
2756 -command branch_rename
::dialog
2757 lappend disable_on_lock
[list .mbar.branch entryconf \
2758 [.mbar.branch index last
] -state]
2760 .mbar.branch add
command -label [mc
"Delete..."] \
2761 -command branch_delete
::dialog
2762 lappend disable_on_lock
[list .mbar.branch entryconf \
2763 [.mbar.branch index last
] -state]
2765 .mbar.branch add
command -label [mc
"Reset..."] \
2766 -command merge
::reset_hard
2767 lappend disable_on_lock
[list .mbar.branch entryconf \
2768 [.mbar.branch index last
] -state]
2773 proc commit_btn_caption
{} {
2774 if {[is_enabled nocommit
]} {
2777 return [mc Commit@@verb
]
2781 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2784 if {![is_enabled nocommit
]} {
2785 .mbar.commit add radiobutton \
2786 -label [mc
"New Commit"] \
2787 -command do_select_commit_type \
2788 -variable selected_commit_type \
2790 lappend disable_on_lock \
2791 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2793 .mbar.commit add radiobutton \
2794 -label [mc
"Amend Last Commit"] \
2795 -command do_select_commit_type \
2796 -variable selected_commit_type \
2798 lappend disable_on_lock \
2799 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2801 .mbar.commit add separator
2804 .mbar.commit add
command -label [mc Rescan
] \
2805 -command ui_do_rescan \
2807 lappend disable_on_lock \
2808 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2810 .mbar.commit add
command -label [mc
"Stage To Commit"] \
2811 -command do_add_selection \
2813 lappend disable_on_lock \
2814 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2816 .mbar.commit add
command -label [mc
"Stage Changed Files To Commit"] \
2817 -command do_add_all \
2819 lappend disable_on_lock \
2820 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2822 .mbar.commit add
command -label [mc
"Unstage From Commit"] \
2823 -command do_unstage_selection \
2825 lappend disable_on_lock \
2826 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2828 .mbar.commit add
command -label [mc
"Revert Changes"] \
2829 -command do_revert_selection \
2831 lappend disable_on_lock \
2832 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2834 .mbar.commit add separator
2836 .mbar.commit add
command -label [mc
"Show Less Context"] \
2837 -command show_less_context \
2838 -accelerator $M1T-\
-
2840 .mbar.commit add
command -label [mc
"Show More Context"] \
2841 -command show_more_context \
2844 .mbar.commit add separator
2846 if {![is_enabled nocommitmsg
]} {
2847 .mbar.commit add
command -label [mc
"Sign Off"] \
2848 -command do_signoff \
2852 .mbar.commit add
command -label [commit_btn_caption
] \
2853 -command do_commit \
2854 -accelerator $M1T-Return
2855 lappend disable_on_lock \
2856 [list .mbar.commit entryconf
[.mbar.commit index last
] -state]
2861 if {[is_enabled branch
]} {
2863 .mbar.merge add
command -label [mc
"Local Merge..."] \
2864 -command merge
::dialog \
2866 lappend disable_on_lock \
2867 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
2868 .mbar.merge add
command -label [mc
"Abort Merge..."] \
2869 -command merge
::reset_hard
2870 lappend disable_on_lock \
2871 [list .mbar.merge entryconf
[.mbar.merge index last
] -state]
2876 if {[is_enabled transport
]} {
2879 .mbar.remote add
command \
2880 -label [mc
"Add..."] \
2881 -command remote_add
::dialog \
2883 .mbar.remote add
command \
2884 -label [mc
"Push..."] \
2885 -command do_push_anywhere \
2887 .mbar.remote add
command \
2888 -label [mc
"Delete Branch..."] \
2889 -command remote_branch_delete
::dialog
2893 proc
::tk
::mac
::ShowPreferences
{} {do_options
}
2897 .mbar.edit add separator
2898 .mbar.edit add
command -label [mc
"Options..."] \
2904 if {[is_enabled multicommit
] ||
[is_enabled singlecommit
]} {
2905 set tools_menubar .mbar.tools
2907 $tools_menubar add separator
2908 $tools_menubar add
command -label [mc
"Add..."] -command tools_add
::dialog
2909 $tools_menubar add
command -label [mc
"Remove..."] -command tools_remove
::dialog
2911 if {[array names repo_config guitool.
*.cmd
] ne
{}} {
2918 .mbar add cascade
-label [mc Help
] -menu .mbar.
help
2922 .mbar.apple add
command -label [mc
"About %s" [appname
]] \
2924 .mbar.apple add separator
2926 .mbar.
help add
command -label [mc
"About %s" [appname
]] \
2929 . configure
-menu .mbar
2931 set doc_path
[githtmldir
]
2932 if {$doc_path ne
{}} {
2933 set doc_path
[file join $doc_path index.html
]
2936 set doc_path
[exec cygpath
--mixed $doc_path]
2940 if {[file isfile
$doc_path]} {
2941 set doc_url
"file:$doc_path"
2943 set doc_url
{http
://www.kernel.org
/pub
/software
/scm
/git
/docs
/}
2946 proc start_browser
{url
} {
2947 git
"web--browse" $url
2950 .mbar.
help add
command -label [mc
"Online Documentation"] \
2951 -command [list start_browser
$doc_url]
2953 .mbar.
help add
command -label [mc
"Show SSH Key"] \
2956 unset doc_path doc_url
2958 # -- Standard bindings
2960 wm protocol . WM_DELETE_WINDOW do_quit
2961 bind all
<$M1B-Key-q> do_quit
2962 bind all
<$M1B-Key-Q> do_quit
2963 bind all
<$M1B-Key-w> {destroy
[winfo toplevel
%W
]}
2964 bind all
<$M1B-Key-W> {destroy
[winfo toplevel
%W
]}
2966 set subcommand_args
{}
2968 set s
"usage: $::argv0 $::subcommand $::subcommand_args"
2969 if {[tk windowingsystem
] eq
"win32"} {
2971 tk_messageBox
-icon info
-message $s \
2979 proc normalize_relpath
{path
} {
2981 foreach item
[file split $path] {
2982 if {$item eq
{.
}} continue
2983 if {$item eq
{..
} && [llength
$elements] > 0
2984 && [lindex
$elements end
] ne
{..
}} {
2985 set elements
[lrange
$elements 0 end-1
]
2988 lappend elements
$item
2990 return [eval file join $elements]
2993 # -- Not a normal commit type invocation? Do that instead!
2995 switch
-- $subcommand {
2998 if {$subcommand eq
"blame"} {
2999 set subcommand_args
{[--line=<num
>] rev? path
}
3001 set subcommand_args
{rev? path
}
3003 if {$argv eq
{}} usage
3009 if {[file exists
$a]} {
3010 if {$path ne
{}} usage
3011 set path
[normalize_relpath
$a]
3013 } elseif
{[file exists
$_prefix$a]} {
3014 if {$path ne
{}} usage
3015 set path
[normalize_relpath
$_prefix$a]
3020 if {$path ne
{}} usage
3022 } elseif
{$a eq
{--}} {
3024 if {$head ne
{}} usage
3029 } elseif
{[regexp
{^
--line=(\d
+)$
} $a a lnum
]} {
3030 if {$jump_spec ne
{} ||
$head ne
{}} usage
3031 set jump_spec
[list
$lnum]
3032 } elseif
{$head eq
{}} {
3033 if {$head ne
{}} usage
3042 if {$head ne
{} && $path eq
{}} {
3043 if {[string index
$head 0] eq
{/}} {
3044 set path
[normalize_relpath
$head]
3047 set path
[normalize_relpath
$_prefix$head]
3055 if {[regexp
{^
[0-9a-f]{1,39}$
} $head]} {
3057 set head [git rev-parse
--verify $head]
3059 if {[tk windowingsystem
] eq
"win32"} {
3060 tk_messageBox
-icon error
-title [mc Error
] -message $err
3067 set current_branch
$head
3071 switch
-- $subcommand {
3073 if {$jump_spec ne
{}} usage
3075 if {$path ne
{} && [file isdirectory
$path]} {
3076 set head $current_branch
3082 browser
::new
$head $path
3085 if {$head eq
{} && ![file exists
$path]} {
3086 catch
{wm withdraw .
}
3090 -title [mc
"git-gui: fatal error"] \
3091 -message [mc
"fatal: cannot stat path %s: No such file or directory" $path]
3094 blame
::new
$head $path $jump_spec
3101 if {[llength
$argv] != 0} {
3104 # fall through to setup UI for commits
3107 set err
"usage: $argv0 \[{blame|browser|citool}\]"
3108 if {[tk windowingsystem
] eq
"win32"} {
3110 tk_messageBox
-icon error
-message $err \
3121 ${NS}::frame .branch
3122 if {!$use_ttk} {.branch configure
-borderwidth 1 -relief sunken
}
3123 ${NS}::label .branch.l1 \
3124 -text [mc
"Current Branch:"] \
3127 ${NS}::label .branch.cb \
3128 -textvariable current_branch \
3131 pack .branch.l1
-side left
3132 pack .branch.cb
-side left
-fill x
3133 pack .branch
-side top
-fill x
3135 # -- Main Window Layout
3137 ${NS}::panedwindow .vpane
-orient horizontal
3138 ${NS}::panedwindow .vpane.files
-orient vertical
3140 .vpane add .vpane.files
3142 .vpane add .vpane.files
-sticky nsew
-height 100 -width 200
3144 pack .vpane
-anchor n
-side top
-fill both
-expand 1
3146 # -- Index File List
3148 ${NS}::frame .vpane.files.index
-height 100 -width 200
3149 tlabel .vpane.files.index.title \
3150 -text [mc
"Staged Changes (Will Commit)"] \
3151 -background lightgreen
-foreground black
3152 text
$ui_index -background white
-foreground black \
3154 -width 20 -height 10 \
3156 -cursor $cursor_ptr \
3157 -xscrollcommand {.vpane.files.index.sx
set} \
3158 -yscrollcommand {.vpane.files.index.sy
set} \
3160 ${NS}::scrollbar .vpane.files.index.sx
-orient h
-command [list
$ui_index xview
]
3161 ${NS}::scrollbar .vpane.files.index.sy
-orient v
-command [list
$ui_index yview
]
3162 pack .vpane.files.index.title
-side top
-fill x
3163 pack .vpane.files.index.sx
-side bottom
-fill x
3164 pack .vpane.files.index.sy
-side right
-fill y
3165 pack
$ui_index -side left
-fill both
-expand 1
3167 # -- Working Directory File List
3169 ${NS}::frame .vpane.files.workdir
-height 100 -width 200
3170 tlabel .vpane.files.workdir.title
-text [mc
"Unstaged Changes"] \
3171 -background lightsalmon
-foreground black
3172 text
$ui_workdir -background white
-foreground black \
3174 -width 20 -height 10 \
3176 -cursor $cursor_ptr \
3177 -xscrollcommand {.vpane.files.workdir.sx
set} \
3178 -yscrollcommand {.vpane.files.workdir.sy
set} \
3180 ${NS}::scrollbar .vpane.files.workdir.sx
-orient h
-command [list
$ui_workdir xview
]
3181 ${NS}::scrollbar .vpane.files.workdir.sy
-orient v
-command [list
$ui_workdir yview
]
3182 pack .vpane.files.workdir.title
-side top
-fill x
3183 pack .vpane.files.workdir.sx
-side bottom
-fill x
3184 pack .vpane.files.workdir.sy
-side right
-fill y
3185 pack
$ui_workdir -side left
-fill both
-expand 1
3187 .vpane.files add .vpane.files.workdir
3188 .vpane.files add .vpane.files.index
3190 .vpane.files paneconfigure .vpane.files.workdir
-sticky news
3191 .vpane.files paneconfigure .vpane.files.index
-sticky news
3194 foreach i
[list
$ui_index $ui_workdir] {
3196 $i tag conf in_diff
-background [$i tag cget in_sel
-background]
3200 # -- Diff and Commit Area
3202 ${NS}::frame .vpane.lower
-height 300 -width 400
3203 ${NS}::frame .vpane.lower.commarea
3204 ${NS}::frame .vpane.lower.
diff -relief sunken
-borderwidth 1
3205 pack .vpane.lower.
diff -fill both
-expand 1
3206 pack .vpane.lower.commarea
-side bottom
-fill x
3207 .vpane add .vpane.lower
3208 if {!$use_ttk} {.vpane paneconfigure .vpane.lower
-sticky nsew
}
3210 # -- Commit Area Buttons
3212 ${NS}::frame .vpane.lower.commarea.buttons
3213 ${NS}::label .vpane.lower.commarea.buttons.l
-text {} \
3216 pack .vpane.lower.commarea.buttons.l
-side top
-fill x
3217 pack .vpane.lower.commarea.buttons
-side left
-fill y
3219 ${NS}::button .vpane.lower.commarea.buttons.rescan
-text [mc Rescan
] \
3220 -command ui_do_rescan
3221 pack .vpane.lower.commarea.buttons.rescan
-side top
-fill x
3222 lappend disable_on_lock \
3223 {.vpane.lower.commarea.buttons.rescan conf
-state}
3225 ${NS}::button .vpane.lower.commarea.buttons.incall
-text [mc
"Stage Changed"] \
3227 pack .vpane.lower.commarea.buttons.incall
-side top
-fill x
3228 lappend disable_on_lock \
3229 {.vpane.lower.commarea.buttons.incall conf
-state}
3231 if {![is_enabled nocommitmsg
]} {
3232 ${NS}::button .vpane.lower.commarea.buttons.signoff
-text [mc
"Sign Off"] \
3234 pack .vpane.lower.commarea.buttons.signoff
-side top
-fill x
3237 ${NS}::button .vpane.lower.commarea.buttons.commit
-text [commit_btn_caption
] \
3239 pack .vpane.lower.commarea.buttons.commit
-side top
-fill x
3240 lappend disable_on_lock \
3241 {.vpane.lower.commarea.buttons.commit conf
-state}
3243 if {![is_enabled nocommit
]} {
3244 ${NS}::button .vpane.lower.commarea.buttons.push
-text [mc Push
] \
3245 -command do_push_anywhere
3246 pack .vpane.lower.commarea.buttons.push
-side top
-fill x
3249 # -- Commit Message Buffer
3251 ${NS}::frame .vpane.lower.commarea.buffer
3252 ${NS}::frame .vpane.lower.commarea.buffer.header
3253 set ui_comm .vpane.lower.commarea.buffer.t
3254 set ui_coml .vpane.lower.commarea.buffer.header.l
3256 if {![is_enabled nocommit
]} {
3257 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
3258 -text [mc
"New Commit"] \
3259 -command do_select_commit_type \
3260 -variable selected_commit_type \
3262 lappend disable_on_lock \
3263 [list .vpane.lower.commarea.buffer.header.new conf
-state]
3264 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
3265 -text [mc
"Amend Last Commit"] \
3266 -command do_select_commit_type \
3267 -variable selected_commit_type \
3269 lappend disable_on_lock \
3270 [list .vpane.lower.commarea.buffer.header.amend conf
-state]
3273 ${NS}::label
$ui_coml \
3276 proc trace_commit_type
{varname args
} {
3277 global ui_coml commit_type
3278 switch
-glob -- $commit_type {
3279 initial
{set txt
[mc
"Initial Commit Message:"]}
3280 amend
{set txt
[mc
"Amended Commit Message:"]}
3281 amend-initial
{set txt
[mc
"Amended Initial Commit Message:"]}
3282 amend-merge
{set txt
[mc
"Amended Merge Commit Message:"]}
3283 merge
{set txt
[mc
"Merge Commit Message:"]}
3284 * {set txt
[mc
"Commit Message:"]}
3286 $ui_coml conf
-text $txt
3288 trace add variable commit_type
write trace_commit_type
3289 pack
$ui_coml -side left
-fill x
3291 if {![is_enabled nocommit
]} {
3292 pack .vpane.lower.commarea.buffer.header.amend
-side right
3293 pack .vpane.lower.commarea.buffer.header.new
-side right
3296 text
$ui_comm -background white
-foreground black \
3300 -autoseparators true \
3302 -width $repo_config(gui.commitmsgwidth
) -height 9 -wrap none \
3304 -yscrollcommand {.vpane.lower.commarea.buffer.sby
set}
3305 ${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
3306 -command [list
$ui_comm yview
]
3307 pack .vpane.lower.commarea.buffer.header
-side top
-fill x
3308 pack .vpane.lower.commarea.buffer.sby
-side right
-fill y
3309 pack
$ui_comm -side left
-fill y
3310 pack .vpane.lower.commarea.buffer
-side left
-fill y
3312 # -- Commit Message Buffer Context Menu
3314 set ctxm .vpane.lower.commarea.buffer.ctxm
3315 menu
$ctxm -tearoff 0
3318 -command {tk_textCut
$ui_comm}
3321 -command {tk_textCopy
$ui_comm}
3324 -command {tk_textPaste
$ui_comm}
3326 -label [mc Delete
] \
3327 -command {catch
{$ui_comm delete sel.first sel.last
}}
3330 -label [mc
"Select All"] \
3331 -command {focus
$ui_comm;$ui_comm tag add sel
0.0 end
}
3333 -label [mc
"Copy All"] \
3335 $ui_comm tag add sel
0.0 end
3336 tk_textCopy
$ui_comm
3337 $ui_comm tag remove sel
0.0 end
3341 -label [mc
"Sign Off"] \
3343 set ui_comm_ctxm
$ctxm
3347 proc trace_current_diff_path
{varname args
} {
3348 global current_diff_path diff_actions file_states
3349 if {$current_diff_path eq
{}} {
3355 set p
$current_diff_path
3356 set s
[mapdesc
[lindex
$file_states($p) 0] $p]
3358 set p
[escape_path
$p]
3362 .vpane.lower.
diff.header.status configure
-text $s
3363 .vpane.lower.
diff.header.
file configure
-text $f
3364 .vpane.lower.
diff.header.path configure
-text $p
3365 foreach w
$diff_actions {
3369 trace add variable current_diff_path
write trace_current_diff_path
3371 gold_frame .vpane.lower.
diff.header
3372 tlabel .vpane.lower.
diff.header.status \
3375 -width $max_status_desc \
3378 tlabel .vpane.lower.
diff.header.
file \
3383 tlabel .vpane.lower.
diff.header.path \
3388 pack .vpane.lower.
diff.header.status
-side left
3389 pack .vpane.lower.
diff.header.
file -side left
3390 pack .vpane.lower.
diff.header.path
-fill x
3391 set ctxm .vpane.lower.
diff.header.ctxm
3392 menu
$ctxm -tearoff 0
3400 -- $current_diff_path
3402 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3403 bind_button3 .vpane.lower.
diff.header.path
"tk_popup $ctxm %X %Y"
3407 ${NS}::frame .vpane.lower.
diff.body
3408 set ui_diff .vpane.lower.
diff.body.t
3409 text
$ui_diff -background white
-foreground black \
3411 -width 80 -height 5 -wrap none \
3413 -xscrollcommand {.vpane.lower.
diff.body.sbx
set} \
3414 -yscrollcommand {.vpane.lower.
diff.body.sby
set} \
3416 catch
{$ui_diff configure
-tabstyle wordprocessor
}
3417 ${NS}::scrollbar .vpane.lower.
diff.body.sbx
-orient horizontal \
3418 -command [list
$ui_diff xview
]
3419 ${NS}::scrollbar .vpane.lower.
diff.body.sby
-orient vertical \
3420 -command [list
$ui_diff yview
]
3421 pack .vpane.lower.
diff.body.sbx
-side bottom
-fill x
3422 pack .vpane.lower.
diff.body.sby
-side right
-fill y
3423 pack
$ui_diff -side left
-fill both
-expand 1
3424 pack .vpane.lower.
diff.header
-side top
-fill x
3425 pack .vpane.lower.
diff.body
-side bottom
-fill both
-expand 1
3427 foreach
{n c
} {0 black
1 red4
2 green4
3 yellow4
4 blue4
5 magenta4
6 cyan4
7 grey60
} {
3428 $ui_diff tag configure clr4
$n -background $c
3429 $ui_diff tag configure clri4
$n -foreground $c
3430 $ui_diff tag configure clr3
$n -foreground $c
3431 $ui_diff tag configure clri3
$n -background $c
3433 $ui_diff tag configure clr1
-font font_diffbold
3434 $ui_diff tag configure clr4
-underline 1
3436 $ui_diff tag conf d_info
-foreground blue
-font font_diffbold
3438 $ui_diff tag conf d_cr
-elide true
3439 $ui_diff tag conf d_@
-font font_diffbold
3440 $ui_diff tag conf d_
+ -foreground {#00a000}
3441 $ui_diff tag conf d_-
-foreground red
3443 $ui_diff tag conf d_
++ -foreground {#00a000}
3444 $ui_diff tag conf d_--
-foreground red
3445 $ui_diff tag conf d_
+s \
3446 -foreground {#00a000} \
3447 -background {#e2effa}
3448 $ui_diff tag conf d_-s \
3450 -background {#e2effa}
3451 $ui_diff tag conf d_s
+ \
3452 -foreground {#00a000} \
3454 $ui_diff tag conf d_s- \
3458 $ui_diff tag conf d
< \
3459 -foreground orange \
3461 $ui_diff tag conf d
= \
3462 -foreground orange \
3464 $ui_diff tag conf d
> \
3465 -foreground orange \
3468 $ui_diff tag raise sel
3470 # -- Diff Body Context Menu
3473 proc create_common_diff_popup
{ctxm
} {
3475 -label [mc Refresh
] \
3476 -command reshow_diff
3477 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3480 -command {tk_textCopy
$ui_diff}
3481 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3483 -label [mc
"Select All"] \
3484 -command {focus
$ui_diff;$ui_diff tag add sel
0.0 end
}
3485 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3487 -label [mc
"Copy All"] \
3489 $ui_diff tag add sel
0.0 end
3490 tk_textCopy
$ui_diff
3491 $ui_diff tag remove sel
0.0 end
3493 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3496 -label [mc
"Decrease Font Size"] \
3497 -command {incr_font_size font_diff
-1}
3498 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3500 -label [mc
"Increase Font Size"] \
3501 -command {incr_font_size font_diff
1}
3502 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3506 build_encoding_menu
$emenu [list force_diff_encoding
]
3508 -label [mc
"Encoding"] \
3510 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3512 $ctxm add
command -label [mc
"Options..."] \
3516 set ctxm .vpane.lower.
diff.body.ctxm
3517 menu
$ctxm -tearoff 0
3519 -label [mc
"Apply/Reverse Hunk"] \
3520 -command {apply_hunk
$cursorX $cursorY}
3521 set ui_diff_applyhunk
[$ctxm index last
]
3522 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyhunk -state]
3524 -label [mc
"Apply/Reverse Line"] \
3525 -command {apply_range_or_line
$cursorX $cursorY; do_rescan
}
3526 set ui_diff_applyline
[$ctxm index last
]
3527 lappend diff_actions
[list
$ctxm entryconf
$ui_diff_applyline -state]
3530 -label [mc
"Show Less Context"] \
3531 -command show_less_context
3532 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3534 -label [mc
"Show More Context"] \
3535 -command show_more_context
3536 lappend diff_actions
[list
$ctxm entryconf
[$ctxm index last
] -state]
3538 create_common_diff_popup
$ctxm
3540 set ctxmmg .vpane.lower.
diff.body.ctxmmg
3541 menu
$ctxmmg -tearoff 0
3542 $ctxmmg add
command \
3543 -label [mc
"Run Merge Tool"] \
3544 -command {merge_resolve_tool
}
3545 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3546 $ctxmmg add separator
3547 $ctxmmg add
command \
3548 -label [mc
"Use Remote Version"] \
3549 -command {merge_resolve_one
3}
3550 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3551 $ctxmmg add
command \
3552 -label [mc
"Use Local Version"] \
3553 -command {merge_resolve_one
2}
3554 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3555 $ctxmmg add
command \
3556 -label [mc
"Revert To Base"] \
3557 -command {merge_resolve_one
1}
3558 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3559 $ctxmmg add separator
3560 $ctxmmg add
command \
3561 -label [mc
"Show Less Context"] \
3562 -command show_less_context
3563 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3564 $ctxmmg add
command \
3565 -label [mc
"Show More Context"] \
3566 -command show_more_context
3567 lappend diff_actions
[list
$ctxmmg entryconf
[$ctxmmg index last
] -state]
3568 $ctxmmg add separator
3569 create_common_diff_popup
$ctxmmg
3571 set ctxmsm .vpane.lower.
diff.body.ctxmsm
3572 menu
$ctxmsm -tearoff 0
3573 $ctxmsm add
command \
3574 -label [mc
"Visualize These Changes In The Submodule"] \
3575 -command {do_gitk
-- true
}
3576 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3577 $ctxmsm add
command \
3578 -label [mc
"Visualize Current Branch History In The Submodule"] \
3579 -command {do_gitk
{} true
}
3580 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3581 $ctxmsm add
command \
3582 -label [mc
"Visualize All Branch History In The Submodule"] \
3583 -command {do_gitk
--all true
}
3584 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3585 $ctxmsm add separator
3586 $ctxmsm add
command \
3587 -label [mc
"Start git gui In The Submodule"] \
3588 -command {do_git_gui
}
3589 lappend diff_actions
[list
$ctxmsm entryconf
[$ctxmsm index last
] -state]
3590 $ctxmsm add separator
3591 create_common_diff_popup
$ctxmsm
3593 proc has_textconv
{path
} {
3594 if {[is_config_false gui.textconv
]} {
3597 set filter
[gitattr
$path diff set]
3598 set textconv
[get_config
[join [list
diff $filter textconv
] .
]]
3599 if {$filter ne
{set} && $textconv ne
{}} {
3606 proc popup_diff_menu
{ctxm ctxmmg ctxmsm x y X Y
} {
3607 global current_diff_path file_states
3610 if {[info exists file_states
($current_diff_path)]} {
3611 set state
[lindex
$file_states($current_diff_path) 0]
3615 if {[string first
{U
} $state] >= 0} {
3616 tk_popup
$ctxmmg $X $Y
3617 } elseif
{$
::is_submodule_diff
} {
3618 tk_popup
$ctxmsm $X $Y
3620 set has_range
[expr {[$
::ui_diff tag nextrange sel
0.0] != {}}]
3621 if {$
::ui_index eq $
::current_diff_side
} {
3622 set l
[mc
"Unstage Hunk From Commit"]
3624 set t
[mc
"Unstage Lines From Commit"]
3626 set t
[mc
"Unstage Line From Commit"]
3629 set l
[mc
"Stage Hunk For Commit"]
3631 set t
[mc
"Stage Lines For Commit"]
3633 set t
[mc
"Stage Line For Commit"]
3637 ||
$current_diff_path eq
{}
3640 ||
[string match
{?T
} $state]
3641 ||
[string match
{T?
} $state]
3642 ||
[has_textconv
$current_diff_path]} {
3647 $ctxm entryconf $
::ui_diff_applyhunk
-state $s -label $l
3648 $ctxm entryconf $
::ui_diff_applyline
-state $s -label $t
3649 tk_popup
$ctxm $X $Y
3652 bind_button3
$ui_diff [list popup_diff_menu
$ctxm $ctxmmg $ctxmsm %x
%y
%X
%Y
]
3656 set main_status
[::status_bar
::new .status
]
3657 pack .status
-anchor w
-side bottom
-fill x
3658 $main_status show
[mc
"Initializing..."]
3662 proc on_ttk_pane_mapped
{w pane pos
} {
3664 after
0 [list after idle
[list
$w sashpos
$pane $pos]]
3666 proc on_tk_pane_mapped
{w pane x y
} {
3668 after
0 [list after idle
[list
$w sash place
$pane $x $y]]
3670 proc on_application_mapped
{} {
3671 global repo_config use_ttk
3673 set gm
$repo_config(gui.geometry
)
3676 [list on_ttk_pane_mapped
%W
0 [lindex
$gm 1]]
3677 bind .vpane.files
<Map
> \
3678 [list on_ttk_pane_mapped
%W
0 [lindex
$gm 2]]
3681 [list on_tk_pane_mapped
%W
0 \
3683 [lindex
[.vpane sash coord
0] 1]]
3684 bind .vpane.files
<Map
> \
3685 [list on_tk_pane_mapped
%W
0 \
3686 [lindex
[.vpane.files sash coord
0] 0] \
3689 wm geometry .
[lindex
$gm 0]
3691 if {[info exists repo_config
(gui.geometry
)]} {
3692 bind .
<Map
> [list on_application_mapped
]
3693 wm geometry .
[lindex
$repo_config(gui.geometry
) 0]
3696 # -- Load window state
3698 if {[info exists repo_config
(gui.wmstate
)]} {
3699 catch
{wm state .
$repo_config(gui.wmstate
)}
3704 bind $ui_comm <$M1B-Key-Return> {do_commit
;break}
3705 bind $ui_comm <$M1B-Key-t> {do_add_selection
;break}
3706 bind $ui_comm <$M1B-Key-T> {do_add_selection
;break}
3707 bind $ui_comm <$M1B-Key-u> {do_unstage_selection
;break}
3708 bind $ui_comm <$M1B-Key-U> {do_unstage_selection
;break}
3709 bind $ui_comm <$M1B-Key-j> {do_revert_selection
;break}
3710 bind $ui_comm <$M1B-Key-J> {do_revert_selection
;break}
3711 bind $ui_comm <$M1B-Key-i> {do_add_all
;break}
3712 bind $ui_comm <$M1B-Key-I> {do_add_all
;break}
3713 bind $ui_comm <$M1B-Key-x> {tk_textCut
%W
;break}
3714 bind $ui_comm <$M1B-Key-X> {tk_textCut
%W
;break}
3715 bind $ui_comm <$M1B-Key-c> {tk_textCopy
%W
;break}
3716 bind $ui_comm <$M1B-Key-C> {tk_textCopy
%W
;break}
3717 bind $ui_comm <$M1B-Key-v> {tk_textPaste
%W
; %W see insert
; break}
3718 bind $ui_comm <$M1B-Key-V> {tk_textPaste
%W
; %W see insert
; break}
3719 bind $ui_comm <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
3720 bind $ui_comm <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
3721 bind $ui_comm <$M1B-Key-minus> {show_less_context
;break}
3722 bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context
;break}
3723 bind $ui_comm <$M1B-Key-equal> {show_more_context
;break}
3724 bind $ui_comm <$M1B-Key-plus> {show_more_context
;break}
3725 bind $ui_comm <$M1B-Key-KP_Add> {show_more_context
;break}
3727 bind $ui_diff <$M1B-Key-x> {tk_textCopy
%W
;break}
3728 bind $ui_diff <$M1B-Key-X> {tk_textCopy
%W
;break}
3729 bind $ui_diff <$M1B-Key-c> {tk_textCopy
%W
;break}
3730 bind $ui_diff <$M1B-Key-C> {tk_textCopy
%W
;break}
3731 bind $ui_diff <$M1B-Key-v> {break}
3732 bind $ui_diff <$M1B-Key-V> {break}
3733 bind $ui_diff <$M1B-Key-a> {%W tag add sel
0.0 end
;break}
3734 bind $ui_diff <$M1B-Key-A> {%W tag add sel
0.0 end
;break}
3735 bind $ui_diff <$M1B-Key-j> {do_revert_selection
;break}
3736 bind $ui_diff <$M1B-Key-J> {do_revert_selection
;break}
3737 bind $ui_diff <Key-Up
> {catch
{%W yview scroll
-1 units
};break}
3738 bind $ui_diff <Key-Down
> {catch
{%W yview scroll
1 units
};break}
3739 bind $ui_diff <Key-Left
> {catch
{%W xview scroll
-1 units
};break}
3740 bind $ui_diff <Key-Right
> {catch
{%W xview scroll
1 units
};break}
3741 bind $ui_diff <Key-k
> {catch
{%W yview scroll
-1 units
};break}
3742 bind $ui_diff <Key-j
> {catch
{%W yview scroll
1 units
};break}
3743 bind $ui_diff <Key-h
> {catch
{%W xview scroll
-1 units
};break}
3744 bind $ui_diff <Key-l
> {catch
{%W xview scroll
1 units
};break}
3745 bind $ui_diff <Control-Key-b
> {catch
{%W yview scroll
-1 pages
};break}
3746 bind $ui_diff <Control-Key-f
> {catch
{%W yview scroll
1 pages
};break}
3747 bind $ui_diff <Button-1
> {focus
%W
}
3749 if {[is_enabled branch
]} {
3750 bind .
<$M1B-Key-n> branch_create
::dialog
3751 bind .
<$M1B-Key-N> branch_create
::dialog
3752 bind .
<$M1B-Key-o> branch_checkout
::dialog
3753 bind .
<$M1B-Key-O> branch_checkout
::dialog
3754 bind .
<$M1B-Key-m> merge
::dialog
3755 bind .
<$M1B-Key-M> merge
::dialog
3757 if {[is_enabled transport
]} {
3758 bind .
<$M1B-Key-p> do_push_anywhere
3759 bind .
<$M1B-Key-P> do_push_anywhere
3762 bind .
<Key-F5
> ui_do_rescan
3763 bind .
<$M1B-Key-r> ui_do_rescan
3764 bind .
<$M1B-Key-R> ui_do_rescan
3765 bind .
<$M1B-Key-s> do_signoff
3766 bind .
<$M1B-Key-S> do_signoff
3767 bind .
<$M1B-Key-t> do_add_selection
3768 bind .
<$M1B-Key-T> do_add_selection
3769 bind .
<$M1B-Key-u> do_unstage_selection
3770 bind .
<$M1B-Key-U> do_unstage_selection
3771 bind .
<$M1B-Key-j> do_revert_selection
3772 bind .
<$M1B-Key-J> do_revert_selection
3773 bind .
<$M1B-Key-i> do_add_all
3774 bind .
<$M1B-Key-I> do_add_all
3775 bind .
<$M1B-Key-minus> {show_less_context
;break}
3776 bind .
<$M1B-Key-KP_Subtract> {show_less_context
;break}
3777 bind .
<$M1B-Key-equal> {show_more_context
;break}
3778 bind .
<$M1B-Key-plus> {show_more_context
;break}
3779 bind .
<$M1B-Key-KP_Add> {show_more_context
;break}
3780 bind .
<$M1B-Key-Return> do_commit
3781 foreach i
[list
$ui_index $ui_workdir] {
3782 bind $i <Button-1
> "toggle_or_diff $i %x %y; break"
3783 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
3784 bind $i <Shift-Button-1
> "add_range_to_selection $i %x %y; break"
3788 set file_lists
($ui_index) [list
]
3789 set file_lists
($ui_workdir) [list
]
3791 wm title .
"[appname] ([reponame]) [file normalize $_gitworktree]"
3792 focus
-force $ui_comm
3794 # -- Warn the user about environmental problems. Cygwin's Tcl
3795 # does *not* pass its env array onto any processes it spawns.
3796 # This means that git processes get none of our environment.
3801 set msg
[mc
"Possible environment issues exist.
3803 The following environment variables are probably
3804 going to be ignored by any Git subprocess run
3808 foreach name
[array names env
] {
3809 switch
-regexp -- $name {
3810 {^GIT_INDEX_FILE$
} -
3811 {^GIT_OBJECT_DIRECTORY$
} -
3812 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$
} -
3814 {^GIT_EXTERNAL_DIFF$
} -
3818 {^GIT_
(AUTHOR|COMMITTER
)_DATE$
} {
3819 append msg
" - $name\n"
3822 {^GIT_
(AUTHOR|COMMITTER
)_
(NAME|EMAIL
)$
} {
3823 append msg
" - $name\n"
3825 set suggest_user
$name
3829 if {$ignored_env > 0} {
3831 This is due to a known issue with the
3832 Tcl binary distributed by Cygwin."]
3834 if {$suggest_user ne
{}} {
3837 A good replacement for %s
3838 is placing values for the user.name and
3839 user.email settings into your personal
3845 unset ignored_env msg suggest_user name
3848 # -- Only initialize complex UI if we are going to stay running.
3850 if {[is_enabled transport
]} {
3853 set n
[.mbar.remote index end
]
3854 populate_remotes_menu
3855 set n
[expr {[.mbar.remote index end
] - $n}]
3857 if {[.mbar.remote
type 0] eq
"tearoff"} { incr n
}
3858 .mbar.remote insert
$n separator
3863 if {[winfo exists
$ui_comm]} {
3864 set GITGUI_BCK_exists
[load_message GITGUI_BCK utf-8
]
3866 # -- If both our backup and message files exist use the
3867 # newer of the two files to initialize the buffer.
3869 if {$GITGUI_BCK_exists} {
3870 set m
[gitdir GITGUI_MSG
]
3871 if {[file isfile
$m]} {
3872 if {[file mtime
[gitdir GITGUI_BCK
]] > [file mtime
$m]} {
3873 catch
{file delete
[gitdir GITGUI_MSG
]}
3875 $ui_comm delete
0.0 end
3877 $ui_comm edit modified false
3878 catch
{file delete
[gitdir GITGUI_BCK
]}
3879 set GITGUI_BCK_exists
0
3885 proc backup_commit_buffer
{} {
3886 global ui_comm GITGUI_BCK_exists
3888 set m
[$ui_comm edit modified
]
3889 if {$m ||
$GITGUI_BCK_exists} {
3890 set msg
[string trim
[$ui_comm get
0.0 end
]]
3891 regsub
-all -line {[ \r\t]+$
} $msg {} msg
3894 if {$GITGUI_BCK_exists} {
3895 catch
{file delete
[gitdir GITGUI_BCK
]}
3896 set GITGUI_BCK_exists
0
3900 set fd
[open
[gitdir GITGUI_BCK
] w
]
3901 fconfigure
$fd -encoding utf-8
3902 puts
-nonewline $fd $msg
3904 set GITGUI_BCK_exists
1
3908 $ui_comm edit modified false
3911 set ::GITGUI_BCK_i
[after
2000 backup_commit_buffer
]
3914 backup_commit_buffer
3916 # -- If the user has aspell available we can drive it
3917 # in pipe mode to spellcheck the commit message.
3919 set spell_cmd
[list |
]
3920 set spell_dict
[get_config gui.spellingdictionary
]
3921 lappend spell_cmd aspell
3922 if {$spell_dict ne
{}} {
3923 lappend spell_cmd
--master=$spell_dict
3925 lappend spell_cmd
--mode=none
3926 lappend spell_cmd
--encoding=utf-8
3927 lappend spell_cmd pipe
3928 if {$spell_dict eq
{none
}
3929 ||
[catch
{set spell_fd
[open
$spell_cmd r
+]} spell_err
]} {
3930 bind_button3
$ui_comm [list tk_popup
$ui_comm_ctxm %X
%Y
]
3932 set ui_comm_spell
[spellcheck
::init \
3938 unset -nocomplain spell_cmd spell_fd spell_err spell_dict
3941 lock_index begin-read
3942 if {![winfo ismapped .
]} {
3946 if {[is_enabled initialamend
]} {
3952 if {[is_enabled nocommitmsg
]} {
3953 $ui_comm configure
-state disabled
-background gray
3956 if {[is_enabled multicommit
] && ![is_config_false gui.gcwarning
]} {
3959 if {[is_enabled retcode
]} {
3960 bind .
<Destroy
> {+terminate_me
%W
}
3962 if {$picked && [is_config_true gui.autoexplore
]} {
3968 # indent-tabs-mode: t