From e369dbeb794a21c381dd7b06dac880763140bfb3 Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Wed, 20 Aug 2025 11:31:43 -0400 Subject: [PATCH 01/11] git-gui: simplify PATH de-duplication git-gui since 8fe7861c51 ("git-gui: assure PATH has only absolute elements.", 2025-04-11) uses a list to maintain order and a dict to detect duplicated elements without quadratic complexity. But, Tcl's dict explicitly maintains keys in the order first added, thus the list is not needed. Simplify the code. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt --- git-gui.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index a931d7f7c99029..9e0c47f842086b 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -103,7 +103,6 @@ if {[is_Windows]} { set _path_sep {:} } -set _search_path {} set _path_seen [dict create] foreach p [split $env(PATH) $_path_sep] { # Keep only absolute paths, getting rid of ., empty, etc. @@ -112,12 +111,9 @@ foreach p [split $env(PATH) $_path_sep] { } # Keep only the first occurence of any duplicates. set norm_p [file normalize $p] - if {[dict exists $_path_seen $norm_p]} { - continue - } dict set _path_seen $norm_p 1 - lappend _search_path $norm_p } +set _search_path [dict keys $_path_seen] unset _path_seen set env(PATH) [join $_search_path $_path_sep] From 1def7b5705885f8cf11afac1d37000c0c88c6c3f Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Wed, 20 Aug 2025 12:50:21 -0400 Subject: [PATCH 02/11] git-gui: simplify using nice(1) git-gui invokes some long running commands using "nice git $cmd" if nice is found and works, otherwise just "git $cmd". The current code is more complex than needed; let's simplify it. Signed-off-by: Mark Levedahl Signed-off-by: Johannes Sixt --- git-gui.sh | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/git-gui.sh b/git-gui.sh index 9e0c47f842086b..c827d9d3b89903 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -579,21 +579,6 @@ proc open_cmd_pipe {cmd path} { return [open |$run r] } -proc _lappend_nice {cmd_var} { - global _nice - upvar $cmd_var cmd - - if {![info exists _nice]} { - set _nice [_which nice] - if {[catch {safe_exec [list $_nice git version]}]} { - set _nice {} - } - } - if {$_nice ne {}} { - lappend cmd $_nice - } -} - proc git {args} { git_redir $args {} } @@ -627,15 +612,14 @@ proc git_read {cmd {redir {}}} { return [safe_open_command $cmdp $redir] } -proc git_read_nice {cmd} { - global _git - set opt [list] - - _lappend_nice opt - - set cmdp [concat [list $_git] $cmd] +set _nice [list [_which nice]] +if {[catch {safe_exec [list {*}$_nice git version]}]} { + set _nice {} +} - return [safe_open_command [concat $opt $cmdp]] +proc git_read_nice {cmd} { + set cmdp [list {*}$::_nice $::_git {*}$cmd] + return [safe_open_command $cmdp] } proc git_write {cmd} { From 7c06c19e66e7654031eb50b72fd79c380fa54158 Mon Sep 17 00:00:00 2001 From: Ruoyu Zhong Date: Sun, 24 Aug 2025 18:07:58 +0800 Subject: [PATCH 03/11] gitk: use for ctx menus on macOS with Tcl 8.7+ Commit d277e89f87fda01daa1e1a35fc1f7118678faa1f added special handling on macOS (OS X) that makes button 2 the right mouse button. As per TIP 474 [1], Tcl 8.7 has swapped buttons 2 and 3 such that button 3 is made the right mouse button as in other platforms. Therefore, the logic should be updated to use button 3 on macOS with Tcl 8.7+. [1]: https://core.tcl-lang.org/tips/doc/main/tip/474.md Signed-off-by: Ruoyu Zhong --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index 3b6acfc5922086..2e1b629d7dca50 100755 --- a/gitk +++ b/gitk @@ -12596,7 +12596,7 @@ set foundbgcolor yellow set currentsearchhitbgcolor orange # button for popping up context menus -if {[tk windowingsystem] eq "aqua"} { +if {[tk windowingsystem] eq "aqua" && [package vcompare $::tcl_version 8.7] < 0} { set ctxbut } else { set ctxbut From 432669914b2fb812bc62e3b52176a8bfc8e4d667 Mon Sep 17 00:00:00 2001 From: Ruoyu Zhong Date: Wed, 27 Aug 2025 10:12:19 +0800 Subject: [PATCH 04/11] gitk: fix trackpad scrolling for Tcl/Tk 8.7+ TIP 684 [1] introduced TouchpadScroll events in Tcl/Tk 8.7, separating trackpad gestures from traditional MouseWheel events. This broke trackpad scrolling in gitk where trackpads generate TouchpadScroll events instead of MouseWheel events. Fix that by adding TouchpadScroll event bindings for all scrollable widgets following the TIP 684 specification. Implement a new precisescrollval proc to handle the smaller delta values from TouchpadScroll events, using appropriate scaling factors that seem sensible on my MacBook. Fixes https://github.com/j6t/gitk/issues/31. [1]: https://core.tcl-lang.org/tips/doc/main/tip/684.md Signed-off-by: Ruoyu Zhong --- gitk | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/gitk b/gitk index 2e1b629d7dca50..6e4d71d5852533 100755 --- a/gitk +++ b/gitk @@ -2301,6 +2301,11 @@ proc scrollval {D {koff 0}} { return [expr int(-($D / $scroll_D0) * max(1, $kscroll-$koff))] } +proc precisescrollval {D {koff 0}} { + global kscroll + return [expr (-($D / 10.0) * max(1, $kscroll-$koff))] +} + proc bind_mousewheel {} { global canv cflist ctext bindall {allcanvs yview scroll [scrollval %D] units} @@ -2319,6 +2324,25 @@ proc bind_mousewheel {} { bind $cflist {$cflist yview scroll [scrollval 5*%D 2] units} bind $cflist break bind $canv {$canv xview scroll [scrollval 5*%D] units} + + bindall { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + allcanvs yview scroll [precisescrollval $deltaY] units + } + bind $ctext { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $ctext yview scroll [precisescrollval $deltaY 2] units + $ctext xview scroll [precisescrollval $deltaX 2] units + } + bind $cflist { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $cflist yview scroll [precisescrollval $deltaY 2] units + } + bind $canv { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $canv xview scroll [precisescrollval $deltaX] units + allcanvs yview scroll [precisescrollval $deltaY] units + } } } From ac8fec7d8de265e56441713faaf4e08f11c31469 Mon Sep 17 00:00:00 2001 From: Michael Rappazzo Date: Wed, 27 Aug 2025 18:59:56 -0400 Subject: [PATCH 05/11] gitk: add README with usage, build, and contribution details Signed-off-by: Michael Rappazzo Signed-off-by: Johannes Sixt --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000000000..2e307463c61d63 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +Gitk - The Git Repository Browser +================================= + +Gitk is a graphical Git repository browser. It displays the commit +history of a Git repository as a graph, showing the relationships +between commits, branches, and tags. + +Usage +===== + +To view the history of the current repository: +```bash +gitk +``` + +To view the history of specific files or directories: +```bash +gitk path/to/file +gitk path/to/directory +``` + +To view a specific branch or range of commits: +```bash +gitk branch-name +gitk v1.0..v2.0 +``` + +For more usage examples and options, see the [gitk manual](https://git-scm.com/docs/gitk). + +Building +======== + +Gitk is a Tcl/Tk application. It requires Tcl/Tk to be installed on +your system. + +Running directly +---------------- + +Gitk can be run from the source directory without installation: + +```bash +./gitk +``` + +This allows for quick testing of changes. + +Installation +------------ + +To install system-wide, you can use either `make` or `meson`: + +```bash +# Install to default location ($HOME/bin) +make install + +# Install to system-wide location +sudo make install prefix=/usr/local + +# Install to custom location +make install prefix=/opt/gitk + +# Using Meson +meson setup builddir +meson compile -C builddir +meson install -C builddir +``` + +Both build systems will handle setting the correct Tcl/Tk interpreter +path and installing translation files. + +Contributing +============ + +Contributions are welcome! The preferred method for submitting patches +is via email to the Git mailing list, as this allows for more thorough +review and broader community feedback. However, GitHub pull requests +are also accepted. + +All commits must be signed off (use `git commit --signoff`) and should +have commit messages prefixed with `gitk:`. + +Email Patches +------------- + +Send patches to git@vger.kernel.org and CC j6t@kdbg.org. See the Git +project's [patch submission guidelines](https://git-scm.com/docs/SubmittingPatches) +for detailed instructions on creating and sending patches. + +License +======= + +Gitk is distributed under the GNU General Public License, either +version 2, or (at your option) any later version. From e749c87e75f925540d1bf362b754fc7adf342960 Mon Sep 17 00:00:00 2001 From: Heiko Voigt Date: Thu, 28 Aug 2025 08:58:47 +0000 Subject: [PATCH 06/11] git-gui: provide question helper for retry fallback on Windows Make use of the new environment variable GIT_ASK_YESNO to support the recently implemented fallback in case unlink, rename or rmdir fail for files in use on Windows. The added dialog will present a yes/no question to the the user which will currently be used by the windows compat layer to let the user retry a failed file operation. Signed-off-by: Heiko Voigt Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt --- Makefile | 2 ++ git-gui--askyesno | 42 ++++++++++++++++++++++++++++++++++++++++++ git-gui.sh | 3 +++ 3 files changed, 47 insertions(+) create mode 100755 git-gui--askyesno diff --git a/Makefile b/Makefile index 2cd160b0575522..6385db44392908 100644 --- a/Makefile +++ b/Makefile @@ -185,6 +185,7 @@ install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(QUIET)$(INSTALL_X0)git-gui--askyesno $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' @@ -199,6 +200,7 @@ uninstall: $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1) $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1) + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askyesno $(REMOVE_F1) $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1) diff --git a/git-gui--askyesno b/git-gui--askyesno new file mode 100755 index 00000000000000..f7312c70c2ca68 --- /dev/null +++ b/git-gui--askyesno @@ -0,0 +1,42 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +# This is an implementation of a simple yes no dialog +# which is injected into the git commandline by git gui +# in case a yesno question needs to be answered. + +set NS {} +set use_ttk [package vsatisfies [package provide Tk] 8.5] +if {$use_ttk} { + set NS ttk +} + +if {$argc < 1} { + puts stderr "Usage: $argv0 " + exit 1 +} else { + set prompt [join $argv " "] +} + +${NS}::frame .t +${NS}::label .t.m -text $prompt -justify center -width 40 +.t.m configure -wraplength 400 +pack .t.m -side top -fill x -padx 20 -pady 20 -expand 1 +pack .t -side top -fill x -ipadx 20 -ipady 20 -expand 1 + +${NS}::frame .b +${NS}::frame .b.left -width 200 +${NS}::button .b.yes -text Yes -command {exit 0} +${NS}::button .b.no -text No -command {exit 1} + +pack .b.left -side left -expand 1 -fill x +pack .b.yes -side left -expand 1 +pack .b.no -side right -expand 1 -ipadx 5 +pack .b -side bottom -fill x -ipadx 20 -ipady 15 + +bind . {exit 0} +bind . {exit 1} + +wm title . "Question?" +tk::PlaceWindow . diff --git a/git-gui.sh b/git-gui.sh index a931d7f7c99029..1a4b73a3373a1f 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1130,6 +1130,9 @@ set argv0dir [file dirname [file normalize $::argv0]] if {![info exists env(SSH_ASKPASS)]} { set env(SSH_ASKPASS) [file join $argv0dir git-gui--askpass] } +if {![info exists env(GIT_ASK_YESNO)]} { + set env(GIT_ASK_YESNO) [file join $argv0dir git-gui--askyesno] +} unset argv0dir ###################################################################### From aeaabebc1a105fab4f64510a4f9ee02cba09ba90 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 28 Aug 2025 08:58:48 +0000 Subject: [PATCH 07/11] git gui: set GIT_ASKPASS=git-gui--askpass if not set yet Signed-off-by: Johannes Schindelin Signed-off-by: Johannes Sixt --- git-gui.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-gui.sh b/git-gui.sh index 1a4b73a3373a1f..16b87f45f51fc3 100755 --- a/git-gui.sh +++ b/git-gui.sh @@ -1130,6 +1130,9 @@ set argv0dir [file dirname [file normalize $::argv0]] if {![info exists env(SSH_ASKPASS)]} { set env(SSH_ASKPASS) [file join $argv0dir git-gui--askpass] } +if {![info exists env(GIT_ASKPASS)]} { + set env(GIT_ASKPASS) [file join $argv0dir git-gui--askpass] +} if {![info exists env(GIT_ASK_YESNO)]} { set env(GIT_ASK_YESNO) [file join $argv0dir git-gui--askyesno] } From 47b6aa869442aa41cdbd08349d273d72651fa230 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 28 Aug 2025 08:58:49 +0000 Subject: [PATCH 08/11] git-gui--askyesno: allow overriding the window title "Question?" is maybe not the most informative thing to ask. In the absence of better information, it is the best we can do, of course. However, Git for Windows' auto updater just learned the trick to use git-gui--askyesno to ask the user whether to update now or not. And in this scripted scenario, we can easily pass a command-line option to change the window title. So let's support that with the new `--title ` option. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- git-gui--askyesno | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/git-gui--askyesno b/git-gui--askyesno index f7312c70c2ca68..d0c801c42c7c94 100755 --- a/git-gui--askyesno +++ b/git-gui--askyesno @@ -5,6 +5,10 @@ exec wish "$0" -- "$@" # This is an implementation of a simple yes no dialog # which is injected into the git commandline by git gui # in case a yesno question needs to be answered. +# +# The window title, which defaults to "Question?", can be +# overridden via the optional `--title` command-line +# option. set NS {} set use_ttk [package vsatisfies [package provide Tk] 8.5] @@ -12,10 +16,15 @@ if {$use_ttk} { set NS ttk } +set title "Question?" if {$argc < 1} { puts stderr "Usage: $argv0 <question>" exit 1 } else { + if {$argc > 2 && [lindex $argv 0] == "--title"} { + set title [lindex $argv 1] + set argv [lreplace $argv 0 1] + } set prompt [join $argv " "] } @@ -38,5 +47,5 @@ pack .b -side bottom -fill x -ipadx 20 -ipady 15 bind . <Key-Return> {exit 0} bind . <Key-Escape> {exit 1} -wm title . "Question?" +wm title . $title tk::PlaceWindow . From 74c90b0f1b729b22b49b1725fa335500fd7aa6dc Mon Sep 17 00:00:00 2001 From: Johannes Schindelin <johannes.schindelin@gmx.de> Date: Thu, 28 Aug 2025 08:58:50 +0000 Subject: [PATCH 09/11] git-gui--askyesno (mingw): use Git for Windows' icon, if available This provides a unified look-and-feel in Git for Windows. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- git-gui--askyesno | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/git-gui--askyesno b/git-gui--askyesno index d0c801c42c7c94..142d1bc3de229b 100755 --- a/git-gui--askyesno +++ b/git-gui--askyesno @@ -47,5 +47,17 @@ pack .b -side bottom -fill x -ipadx 20 -ipady 15 bind . <Key-Return> {exit 0} bind . <Key-Escape> {exit 1} +if {$::tcl_platform(platform) eq {windows}} { + set icopath [file dirname [file normalize $argv0]] + if {[file tail $icopath] eq {git-core}} { + set icopath [file dirname $icopath] + } + set icopath [file dirname $icopath] + set icopath [file join $icopath share git git-for-windows.ico] + if {[file exists $icopath]} { + wm iconbitmap . -default $icopath + } +} + wm title . $title tk::PlaceWindow . From c461528cd4b17af941a353a41d65ce40f7a1db12 Mon Sep 17 00:00:00 2001 From: Johannes Sixt <j6t@kdbg.org> Date: Mon, 1 Sep 2025 20:20:08 +0200 Subject: [PATCH 10/11] git-gui: fix error handling of Revert Changes command The command Revert Changes has two different erroneous behaviors depending on the Tcl version used. The command uses a "chord" facility where different "notes" are evaluated asynchronously and any error is reported after all of them have finished. The intent is that a private namespace is used where the notes can store the error state. Tcl 9 changed namespace handling in a subtle way, as https://www.tcl-lang.org/software/tcltk/9.0.html summarizes under "Notable incompatibilities": Unqualified varnames resolved in current namespace, not global. Note that in almost all cases where this causes a change, the change is actually the removal of a latent bug. And that's exactly what happens here. - Under Tcl 9: - When the command operates without any errors, the variable `err` is never set. When the error handler wants to inspect `err` (in the correct private namespace), it does not find it and a Tcl error about an unset variable occurs. Incidentally, this is also the case when the user cancels the operation with the option "Do Nothing"! On the other hand, when an error occurs during the operation, `err` is set and found as intended. Check for the existence of the variable `err` before the attempt to read it. - Under Tcl 8.6: The error handler looks up `err` in the global namespace, which is bogus and unintended. The variable is set due to the many `catch ... err` that occur during startup in the global namespace. - When the command operates without any errors, the error handler finds the global `err`, which happens to be the empty string at this point, and no error is reported. On the other hand, when an error occurs during the operation, the global `err` is set and found, so that an error is reported as desired. However, the value of `err` persists in the global namespace. When the command is repeated, an error is reported again, even if there was actually no error, and even "Do Nothing" was used to cancel the operation. Clear the global `err` before the operation begins. The lingering error message is not a problem under Tcl 9, because a prestine namespace is established every time the command is used. This fixes https://github.com/j6t/git-gui/issues/21. Helped-by: Igor Stepushchik Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- lib/index.tcl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/index.tcl b/lib/index.tcl index 7aa09c7728267e..e1d38e54bee36a 100644 --- a/lib/index.tcl +++ b/lib/index.tcl @@ -425,6 +425,11 @@ proc revert_helper {txt paths} { if {![lock_index begin-update]} return + # Workaround for Tcl < 9.0: chord namespaces are not obeyed and + # operated in the global namespace. This clears an error that could + # have been left over from a previous operation. + set ::err {} + # Common "after" functionality that waits until multiple asynchronous # operations are complete (by waiting for them to activate their notes # on the chord). @@ -432,7 +437,7 @@ proc revert_helper {txt paths} { # The asynchronous operations are each indicated below by a comment # before the code block that starts the async operation. set after_chord [SimpleChord::new { - if {[string trim $err] != ""} { + if {[info exists err] && [string trim $err] ne ""} { rescan_on_error $err } else { unlock_index From 7ef77ec0054b3b4ef3cfee6ef97c43c01965be88 Mon Sep 17 00:00:00 2001 From: Adam Dinwoodie <adam@dinwoodie.org> Date: Sat, 15 Feb 2025 21:19:03 +0000 Subject: [PATCH 11/11] git-gui: sync Makefiles with git.git In git.git, commit 5309c1e9fb39 (Makefile: set default goals in makefiles, 2025-02-15) touched two Makefiles in the git-git/ directory. Import these changes, so that the trees can converge again with the next merge of this repository into git.git. Reported-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Johannes Sixt <j6t@kdbg.org> --- Makefile | 1 + po/glossary/Makefile | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 6385db44392908..69b0b844352bc1 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +# The default target of this Makefile is... all:: # Define V=1 to have a more verbose compile. diff --git a/po/glossary/Makefile b/po/glossary/Makefile index 749aa2e7ec1b02..e656b0d2b0fbd0 100644 --- a/po/glossary/Makefile +++ b/po/glossary/Makefile @@ -1,3 +1,6 @@ +# The default target of this Makefile is... +update-po:: + PO_TEMPLATE = git-gui-glossary.pot ALL_POFILES = $(wildcard *.po)