* 'master' of https://github.com/j6t/gitk:
  gitk: introduce support for the Meson build system
  gitk: extract script to build executable
  gitk: make the "list references" default window width wider
  gitk: fix arrow keys in input fields with Tcl/Tk >= 8.6
  gitk: Use an external icon file on Windows
  gitk: Unicode file name support
  gitk(Windows): avoid inadvertently calling executables in the worktree
This commit is contained in:
Junio C Hamano
2025-02-20 05:59:56 -08:00
5 changed files with 237 additions and 33 deletions

View File

@@ -8,6 +8,7 @@ gitk_libdir ?= $(sharedir)/gitk/lib
msgsdir ?= $(gitk_libdir)/msgs msgsdir ?= $(gitk_libdir)/msgs
msgsdir_SQ = $(subst ','\'',$(msgsdir)) msgsdir_SQ = $(subst ','\'',$(msgsdir))
SHELL_PATH ?= /bin/sh
TCL_PATH ?= tclsh TCL_PATH ?= tclsh
TCLTK_PATH ?= wish TCLTK_PATH ?= wish
INSTALL ?= install INSTALL ?= install
@@ -64,9 +65,7 @@ clean::
gitk-wish: gitk GIT-TCLTK-VARS gitk-wish: gitk GIT-TCLTK-VARS
$(QUIET_GEN)$(RM) $@ $@+ && \ $(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1,3s|^exec .* "$$0"|exec $(subst |,'\|',$(TCLTK_PATH_SQ)) "$$0"|' <gitk >$@+ && \ $(SHELL_PATH) ./generate-tcl.sh "$(TCLTK_PATH_SQ)" "$<" "$@"
chmod +x $@+ && \
mv -f $@+ $@
$(PO_TEMPLATE): gitk $(PO_TEMPLATE): gitk
$(XGETTEXT) -kmc -LTcl -o $@ gitk $(XGETTEXT) -kmc -LTcl -o $@ gitk

11
gitk-git/generate-tcl.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -e
WISH=$(echo "$1" | sed 's/|/\\|/g')
INPUT="$2"
OUTPUT="$3"
sed -e "1,3s|^exec .* \"\$0\"|exec $WISH \"\$0\"|" "$INPUT" >"$OUTPUT"+
chmod a+x "$OUTPUT"+
mv "$OUTPUT"+ "$OUTPUT"

View File

@@ -9,6 +9,141 @@ exec wish "$0" -- "$@"
package require Tk package require Tk
######################################################################
##
## Enabling platform-specific code paths
proc is_MacOSX {} {
if {[tk windowingsystem] eq {aqua}} {
return 1
}
return 0
}
proc is_Windows {} {
if {$::tcl_platform(platform) eq {windows}} {
return 1
}
return 0
}
set _iscygwin {}
proc is_Cygwin {} {
global _iscygwin
if {$_iscygwin eq {}} {
if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
set _iscygwin 1
} else {
set _iscygwin 0
}
}
return $_iscygwin
}
######################################################################
##
## PATH lookup
set _search_path {}
proc _which {what args} {
global env _search_exe _search_path
if {$_search_path eq {}} {
if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
set _search_path [split [exec cygpath \
--windows \
--path \
--absolute \
$env(PATH)] {;}]
set _search_exe .exe
} elseif {[is_Windows]} {
set gitguidir [file dirname [info script]]
regsub -all ";" $gitguidir "\\;" gitguidir
set env(PATH) "$gitguidir;$env(PATH)"
set _search_path [split $env(PATH) {;}]
# Skip empty `PATH` elements
set _search_path [lsearch -all -inline -not -exact \
$_search_path ""]
set _search_exe .exe
} else {
set _search_path [split $env(PATH) :]
set _search_exe {}
}
}
if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
set suffix {}
} else {
set suffix $_search_exe
}
foreach p $_search_path {
set p [file join $p $what$suffix]
if {[file exists $p]} {
return [file normalize $p]
}
}
return {}
}
proc sanitize_command_line {command_line from_index} {
set i $from_index
while {$i < [llength $command_line]} {
set cmd [lindex $command_line $i]
if {[file pathtype $cmd] ne "absolute"} {
set fullpath [_which $cmd]
if {$fullpath eq ""} {
throw {NOT-FOUND} "$cmd not found in PATH"
}
lset command_line $i $fullpath
}
# handle piped commands, e.g. `exec A | B`
for {incr i} {$i < [llength $command_line]} {incr i} {
if {[lindex $command_line $i] eq "|"} {
incr i
break
}
}
}
return $command_line
}
# Override `exec` to avoid unsafe PATH lookup
rename exec real_exec
proc exec {args} {
# skip options
for {set i 0} {$i < [llength $args]} {incr i} {
set arg [lindex $args $i]
if {$arg eq "--"} {
incr i
break
}
if {[string range $arg 0 0] ne "-"} {
break
}
}
set args [sanitize_command_line $args $i]
uplevel 1 real_exec $args
}
# Override `open` to avoid unsafe PATH lookup
rename open real_open
proc open {args} {
set arg0 [lindex $args 0]
if {[string range $arg0 0 0] eq "|"} {
set command_line [string trim [string range $arg0 1 end]]
lset args 0 "| [sanitize_command_line $command_line 0]"
}
uplevel 1 real_open $args
}
# End of safe PATH lookup stuff
proc hasworktree {} { proc hasworktree {} {
return [expr {[exec git rev-parse --is-bare-repository] == "false" && return [expr {[exec git rev-parse --is-bare-repository] == "false" &&
[exec git rev-parse --is-inside-git-dir] == "false"}] [exec git rev-parse --is-inside-git-dir] == "false"}]
@@ -2103,7 +2238,7 @@ proc makewindow {} {
global headctxmenu progresscanv progressitem progresscoords statusw global headctxmenu progresscanv progressitem progresscoords statusw
global fprogitem fprogcoord lastprogupdate progupdatepending global fprogitem fprogcoord lastprogupdate progupdatepending
global rprogitem rprogcoord rownumsel numcommits global rprogitem rprogcoord rownumsel numcommits
global have_tk85 use_ttk NS global have_tk85 have_tk86 use_ttk NS
global git_version global git_version
global worddiff global worddiff
@@ -2601,8 +2736,13 @@ proc makewindow {} {
bind . <Key-Down> "selnextline 1" bind . <Key-Down> "selnextline 1"
bind . <Shift-Key-Up> "dofind -1 0" bind . <Shift-Key-Up> "dofind -1 0"
bind . <Shift-Key-Down> "dofind 1 0" bind . <Shift-Key-Down> "dofind 1 0"
bindkey <Key-Right> "goforw" if {$have_tk86} {
bindkey <Key-Left> "goback" bindkey <<NextChar>> "goforw"
bindkey <<PrevChar>> "goback"
} else {
bindkey <Key-Right> "goforw"
bindkey <Key-Left> "goback"
}
bind . <Key-Prior> "selnextpage -1" bind . <Key-Prior> "selnextpage -1"
bind . <Key-Next> "selnextpage 1" bind . <Key-Next> "selnextpage 1"
bind . <$M1B-Home> "allcanvs yview moveto 0.0" bind . <$M1B-Home> "allcanvs yview moveto 0.0"
@@ -7720,7 +7860,7 @@ proc gettreeline {gtf id} {
if {[string index $fname 0] eq "\""} { if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0] set fname [lindex $fname 0]
} }
set fname [encoding convertfrom $fname] set fname [encoding convertfrom utf-8 $fname]
lappend treefilelist($id) $fname lappend treefilelist($id) $fname
} }
if {![eof $gtf]} { if {![eof $gtf]} {
@@ -7982,7 +8122,7 @@ proc gettreediffline {gdtf ids} {
if {[string index $file 0] eq "\""} { if {[string index $file 0] eq "\""} {
set file [lindex $file 0] set file [lindex $file 0]
} }
set file [encoding convertfrom $file] set file [encoding convertfrom utf-8 $file]
if {$file ne [lindex $treediff end]} { if {$file ne [lindex $treediff end]} {
lappend treediff $file lappend treediff $file
lappend sublist $file lappend sublist $file
@@ -8127,7 +8267,7 @@ proc makediffhdr {fname ids} {
global ctext curdiffstart treediffs diffencoding global ctext curdiffstart treediffs diffencoding
global ctext_file_names jump_to_here targetline diffline global ctext_file_names jump_to_here targetline diffline
set fname [encoding convertfrom $fname] set fname [encoding convertfrom utf-8 $fname]
set diffencoding [get_path_encoding $fname] set diffencoding [get_path_encoding $fname]
set i [lsearch -exact $treediffs($ids) $fname] set i [lsearch -exact $treediffs($ids) $fname]
if {$i >= 0} { if {$i >= 0} {
@@ -8189,7 +8329,7 @@ proc parseblobdiffline {ids line} {
if {![string compare -length 5 "diff " $line]} { if {![string compare -length 5 "diff " $line]} {
if {![regexp {^diff (--cc|--git) } $line m type]} { if {![regexp {^diff (--cc|--git) } $line m type]} {
set line [encoding convertfrom $line] set line [encoding convertfrom utf-8 $line]
$ctext insert end "$line\n" hunksep $ctext insert end "$line\n" hunksep
continue continue
} }
@@ -8238,7 +8378,7 @@ proc parseblobdiffline {ids line} {
makediffhdr $fname $ids makediffhdr $fname $ids
} elseif {![string compare -length 16 "* Unmerged path " $line]} { } elseif {![string compare -length 16 "* Unmerged path " $line]} {
set fname [encoding convertfrom [string range $line 16 end]] set fname [encoding convertfrom utf-8 [string range $line 16 end]]
$ctext insert end "\n" $ctext insert end "\n"
set curdiffstart [$ctext index "end - 1c"] set curdiffstart [$ctext index "end - 1c"]
lappend ctext_file_names $fname lappend ctext_file_names $fname
@@ -8291,7 +8431,7 @@ proc parseblobdiffline {ids line} {
if {[string index $fname 0] eq "\""} { if {[string index $fname 0] eq "\""} {
set fname [lindex $fname 0] set fname [lindex $fname 0]
} }
set fname [encoding convertfrom $fname] set fname [encoding convertfrom utf-8 $fname]
set i [lsearch -exact $treediffs($ids) $fname] set i [lsearch -exact $treediffs($ids) $fname]
if {$i >= 0} { if {$i >= 0} {
setinlist difffilestart $i $curdiffstart setinlist difffilestart $i $curdiffstart
@@ -8310,6 +8450,7 @@ proc parseblobdiffline {ids line} {
set diffinhdr 0 set diffinhdr 0
return return
} }
set line [encoding convertfrom utf-8 $line]
$ctext insert end "$line\n" filesep $ctext insert end "$line\n" filesep
} else { } else {
@@ -10068,7 +10209,7 @@ proc showrefs {} {
text $top.list -background $bgcolor -foreground $fgcolor \ text $top.list -background $bgcolor -foreground $fgcolor \
-selectbackground $selectbgcolor -font mainfont \ -selectbackground $selectbgcolor -font mainfont \
-xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \ -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
-width 30 -height 20 -cursor $maincursor \ -width 60 -height 20 -cursor $maincursor \
-spacing1 1 -spacing3 1 -state disabled -spacing1 1 -spacing3 1 -state disabled
$top.list tag configure highlight -background $selectbgcolor $top.list tag configure highlight -background $selectbgcolor
if {![lsearch -exact $bglist $top.list]} { if {![lsearch -exact $bglist $top.list]} {
@@ -12305,7 +12446,7 @@ proc cache_gitattr {attr pathlist} {
foreach row [split $rlist "\n"] { foreach row [split $rlist "\n"] {
if {[regexp "(.*): $attr: (.*)" $row m path value]} { if {[regexp "(.*): $attr: (.*)" $row m path value]} {
if {[string index $path 0] eq "\""} { if {[string index $path 0] eq "\""} {
set path [encoding convertfrom [lindex $path 0]] set path [encoding convertfrom utf-8 [lindex $path 0]]
} }
set path_attr_cache($attr,$path) $value set path_attr_cache($attr,$path) $value
} }
@@ -12335,7 +12476,6 @@ if { [info exists ::env(GITK_MSGSDIR)] } {
set gitk_prefix [file dirname [file dirname [file normalize $argv0]]] set gitk_prefix [file dirname [file dirname [file normalize $argv0]]]
set gitk_libdir [file join $gitk_prefix share gitk lib] set gitk_libdir [file join $gitk_prefix share gitk lib]
set gitk_msgsdir [file join $gitk_libdir msgs] set gitk_msgsdir [file join $gitk_libdir msgs]
unset gitk_prefix
} }
## Internationalization (i18n) through msgcat and gettext. See ## Internationalization (i18n) through msgcat and gettext. See
@@ -12637,6 +12777,7 @@ set nullid2 "0000000000000000000000000000000000000001"
set nullfile "/dev/null" set nullfile "/dev/null"
set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
set have_tk86 [expr {[package vcompare $tk_version "8.6"] >= 0}]
if {![info exists have_ttk]} { if {![info exists have_ttk]} {
set have_ttk [llength [info commands ::ttk::style]] set have_ttk [llength [info commands ::ttk::style]]
} }
@@ -12701,28 +12842,32 @@ if {[expr {[exec git rev-parse --is-inside-work-tree] == "true"}]} {
set worktree [gitworktree] set worktree [gitworktree]
setcoords setcoords
makewindow makewindow
catch { if {$::tcl_platform(platform) eq {windows} && [file exists $gitk_prefix/etc/git.ico]} {
image create photo gitlogo -width 16 -height 16 wm iconbitmap . -default $gitk_prefix/etc/git.ico
} else {
catch {
image create photo gitlogo -width 16 -height 16
image create photo gitlogominus -width 4 -height 2 image create photo gitlogominus -width 4 -height 2
gitlogominus put #C00000 -to 0 0 4 2 gitlogominus put #C00000 -to 0 0 4 2
gitlogo copy gitlogominus -to 1 5 gitlogo copy gitlogominus -to 1 5
gitlogo copy gitlogominus -to 6 5 gitlogo copy gitlogominus -to 6 5
gitlogo copy gitlogominus -to 11 5 gitlogo copy gitlogominus -to 11 5
image delete gitlogominus image delete gitlogominus
image create photo gitlogoplus -width 4 -height 4 image create photo gitlogoplus -width 4 -height 4
gitlogoplus put #008000 -to 1 0 3 4 gitlogoplus put #008000 -to 1 0 3 4
gitlogoplus put #008000 -to 0 1 4 3 gitlogoplus put #008000 -to 0 1 4 3
gitlogo copy gitlogoplus -to 1 9 gitlogo copy gitlogoplus -to 1 9
gitlogo copy gitlogoplus -to 6 9 gitlogo copy gitlogoplus -to 6 9
gitlogo copy gitlogoplus -to 11 9 gitlogo copy gitlogoplus -to 11 9
image delete gitlogoplus image delete gitlogoplus
image create photo gitlogo32 -width 32 -height 32 image create photo gitlogo32 -width 32 -height 32
gitlogo32 copy gitlogo -zoom 2 2 gitlogo32 copy gitlogo -zoom 2 2
wm iconphoto . -default gitlogo gitlogo32 wm iconphoto . -default gitlogo gitlogo32
}
} }
# wait for the window to become visible # wait for the window to become visible
if {![winfo viewable .]} {tkwait visibility .} if {![winfo viewable .]} {tkwait visibility .}

30
gitk-git/meson.build Normal file
View File

@@ -0,0 +1,30 @@
project('gitk')
shell = find_program('sh')
wish = find_program('wish')
# Verify that dependencies of "generate-tcl.sh" are satisfied.
foreach dependency : [ 'chmod', 'mv', 'sed' ]
find_program(dependency)
endforeach
custom_target(
command: [
shell,
meson.current_source_dir() / 'generate-tcl.sh',
wish.full_path(),
'@INPUT@',
'@OUTPUT@',
],
input: 'gitk',
output: 'gitk',
depend_files: [
'generate-tcl.sh',
],
install: true,
install_dir: get_option('bindir'),
)
if find_program('msgfmt').found()
subdir('po')
endif

19
gitk-git/po/meson.build Normal file
View File

@@ -0,0 +1,19 @@
import('i18n').gettext('gitk',
languages: [
'bg',
'ca',
'de',
'es',
'fr',
'hu',
'it',
'ja',
'pt_br',
'pt_pt',
'ru',
'sv',
'vi',
'zh_cn',
],
install: true,
)