gitk: Add support of SHA256 repositories

This patch adds a basic support of SHA256 Git repository to Gitk, so that Gitk can show and operate on both SHA1 and SHA256 repos gracefully. Since SHA256 has a longer ID length (64 char) than SHA1 (40 char), many field widths are adjusted to fit with it. A caveat is that the configuration of auto selection length is shared between SHA1 and SHA256 repos. That is, once when this value is saved and read, it's applied to both repo types, which may result in shorter selection than the full SHA256 ID. We may introduce another individual config for sha256 (actually I did write in the first version), but for simplicity, the common config is used as of writing this. Many lines still refer "sha1" although they may point to both SHA1 and SHA256. They are left untouched for making the changes simpler. This patch is based on the early work by Rostislav Krasny: https://patchwork.kernel.org/project/git/patch/pull.979.git.1623687519832.gitgitgadget@gmail.com I refreshed, revised and extended to the latest state. Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Johannes Sixt <j6t@kdbg.org>

Takashi Iwai committed Jun 17, 2025 at 07:59 UTC 59a3998252a8f7b452e8fd60b32f5a5e639b0600
1 file changed +58 -25
gitk
+58 -25
@@ -425,6 +425,7 @@ proc parseviewargs {n arglist} {
425
426 proc parseviewrevs {view revs} {
427 global vposids vnegids
428 + global hashlength
429
430 if {$revs eq {}} {
431 set revs HEAD
@@ -438,7 +439,7 @@ proc parseviewrevs {view revs} {
439 set badrev {}
440 for {set l 0} {$l < [llength $errlines]} {incr l} {
441 set line [lindex $errlines $l]
441 - if {!([string length $line] == 40 && [string is xdigit $line])} {
442 + if {!([string length $line] == $hashlength && [string is xdigit $line])} {
443 if {[string match "fatal:*" $line]} {
444 if {[string match "fatal: ambiguous argument*" $line]
445 && $badrev ne {}} {
@@ -655,6 +656,7 @@ proc updatecommits {} {
656 global hasworktree
657 global varcid vposids vnegids vflags vrevs
658 global show_notes
659 + global hashlength
660
661 set hasworktree [hasworktree]
662 rereadrefs
@@ -688,7 +690,7 @@ proc updatecommits {} {
690 # take out positive refs that we asked for before or
691 # that we have already seen
692 foreach rev $revs {
691 - if {[string length $rev] == 40} {
693 + if {[string length $rev] == $hashlength} {
694 if {[lsearch -exact $oldpos $rev] < 0
695 && ![info exists varcid($view,$rev)]} {
696 lappend newrevs $rev
@@ -1573,6 +1575,7 @@ proc getcommitlines {fd inst view updating} {
1575 global parents children curview hlview
1576 global idpending ordertok
1577 global varccommits varcid varctok vtokmod vfilelimit vshortids
1578 + global hashlength
1579
1580 set stuff [read $fd 500000]
1581 # git log doesn't terminate the last commit with a null...
@@ -1655,7 +1658,7 @@ proc getcommitlines {fd inst view updating} {
1658 }
1659 set ok 1
1660 foreach id $ids {
1658 - if {[string length $id] != 40} {
1661 + if {[string length $id] != $hashlength} {
1662 set ok 0
1663 break
1664 }
@@ -1901,8 +1904,8 @@ proc getcommit {id} {
1904 return 1
1905 }
1906
1904 -# Expand an abbreviated commit ID to a list of full 40-char IDs that match
1905 -# and are present in the current view.
1907 +# Expand an abbreviated commit ID to a list of full 40-char (or 64-char
1908 +# for SHA256 repo) IDs that match and are present in the current view.
1909 # This is fairly slow...
1910 proc longid {prefix} {
1911 global varcid curview vshortids
@@ -1935,6 +1938,7 @@ proc readrefs {} {
1938 global selecthead selectheadid
1939 global hideremotes
1940 global tclencoding
1941 + global hashlength
1942
1943 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
1944 unset -nocomplain $v
@@ -1944,9 +1948,9 @@ proc readrefs {} {
1948 fconfigure $refd -encoding $tclencoding
1949 }
1950 while {[gets $refd line] >= 0} {
1947 - if {[string index $line 40] ne " "} continue
1948 - set id [string range $line 0 39]
1949 - set ref [string range $line 41 end]
1951 + if {[string index $line $hashlength] ne " "} continue
1952 + set id [string range $line 0 [expr {$hashlength - 1}]]
1953 + set ref [string range $line [expr {$hashlength + 1}] end]
1954 if {![string match "refs/*" $ref]} continue
1955 set name [string range $ref 5 end]
1956 if {[string match "remotes/*" $name]} {
@@ -2241,6 +2245,7 @@ proc makewindow {} {
2245 global have_tk85 have_tk86 use_ttk NS
2246 global git_version
2247 global worddiff
2248 + global hashlength
2249
2250 # The "mc" arguments here are purely so that xgettext
2251 # sees the following string as needing to be translated
@@ -2366,7 +2371,7 @@ proc makewindow {} {
2371 -command gotocommit -width 8
2372 $sha1but conf -disabledforeground [$sha1but cget -foreground]
2373 pack .tf.bar.sha1label -side left
2369 - ${NS}::entry $sha1entry -width 40 -font textfont -textvariable sha1string
2374 + ${NS}::entry $sha1entry -width $hashlength -font textfont -textvariable sha1string
2375 trace add variable sha1string write sha1change
2376 pack $sha1entry -side left -pady 2
2377
@@ -4093,6 +4098,7 @@ proc stopblaming {} {
4098
4099 proc read_line_source {fd inst} {
4100 global blamestuff curview commfd blameinst nullid nullid2
4101 + global hashlength
4102
4103 while {[gets $fd line] >= 0} {
4104 lappend blamestuff($inst) $line
@@ -4113,7 +4119,7 @@ proc read_line_source {fd inst} {
4119 set line [split [lindex $blamestuff($inst) 0] " "]
4120 set id [lindex $line 0]
4121 set lnum [lindex $line 1]
4116 - if {[string length $id] == 40 && [string is xdigit $id] &&
4122 + if {[string length $id] == $hashlength && [string is xdigit $id] &&
4123 [string is digit -strict $lnum]} {
4124 # look for "filename" line
4125 foreach l $blamestuff($inst) {
@@ -5257,11 +5263,13 @@ proc askrelhighlight {row id} {
5263 # Graph layout functions
5264
5265 proc shortids {ids} {
5266 + global hashlength
5267 +
5268 set res {}
5269 foreach id $ids {
5270 if {[llength $id] > 1} {
5271 lappend res [shortids $id]
5264 - } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
5272 + } elseif {[regexp [string map "@@ $hashlength" {^[0-9a-f]{@@}$}] $id]} {
5273 lappend res [string range $id 0 7]
5274 } else {
5275 lappend res $id
@@ -5436,13 +5444,14 @@ proc get_viewmainhead {view} {
5444 # git rev-list should give us just 1 line to use as viewmainheadid($view)
5445 proc getviewhead {fd inst view} {
5446 global viewmainheadid commfd curview viewinstances showlocalchanges
5447 + global hashlength
5448
5449 set id {}
5450 if {[gets $fd line] < 0} {
5451 if {![eof $fd]} {
5452 return 1
5453 }
5445 - } elseif {[string length $line] == 40 && [string is xdigit $line]} {
5454 + } elseif {[string length $line] == $hashlength && [string is xdigit $line]} {
5455 set id $line
5456 }
5457 set viewmainheadid($view) $id
@@ -7206,10 +7215,11 @@ proc commit_descriptor {p} {
7215 # Also look for URLs of the form "http[s]://..." and make them web links.
7216 proc appendwithlinks {text tags} {
7217 global ctext linknum curview
7218 + global hashlength
7219
7220 set start [$ctext index "end - 1c"]
7221 $ctext insert end $text $tags
7212 - set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text]
7222 + set links [regexp -indices -all -inline [string map "@@ $hashlength" {(?:\m|-g)[0-9a-f]{6,@@}\M}] $text]
7223 foreach l $links {
7224 set s [lindex $l 0]
7225 set e [lindex $l 1]
@@ -7237,13 +7247,14 @@ proc appendwithlinks {text tags} {
7247 proc setlink {id lk} {
7248 global curview ctext pendinglinks
7249 global linkfgcolor
7250 + global hashlength
7251
7252 if {[string range $id 0 1] eq "-g"} {
7253 set id [string range $id 2 end]
7254 }
7255
7256 set known 0
7246 - if {[string length $id] < 40} {
7257 + if {[string length $id] < $hashlength} {
7258 set matches [longid $id]
7259 if {[llength $matches] > 0} {
7260 if {[llength $matches] > 1} return
@@ -8888,13 +8899,16 @@ proc incrfont {inc} {
8899
8900 proc clearsha1 {} {
8901 global sha1entry sha1string
8891 - if {[string length $sha1string] == 40} {
8902 + global hashlength
8903 +
8904 + if {[string length $sha1string] == $hashlength} {
8905 $sha1entry delete 0 end
8906 }
8907 }
8908
8909 proc sha1change {n1 n2 op} {
8910 global sha1string currentid sha1but
8911 +
8912 if {$sha1string == {}
8913 || ([info exists currentid] && $sha1string == $currentid)} {
8914 set state disabled
@@ -8911,6 +8925,7 @@ proc sha1change {n1 n2 op} {
8925
8926 proc gotocommit {} {
8927 global sha1string tagids headids curview varcid
8928 + global hashlength
8929
8930 if {$sha1string == {}
8931 || ([info exists currentid] && $sha1string == $currentid)} return
@@ -8920,7 +8935,7 @@ proc gotocommit {} {
8935 set id $headids($sha1string)
8936 } else {
8937 set id [string tolower $sha1string]
8923 - if {[regexp {^[0-9a-f]{4,39}$} $id]} {
8938 + if {[regexp {^[0-9a-f]{4,63}$} $id]} {
8939 set matches [longid $id]
8940 if {$matches ne {}} {
8941 if {[llength $matches] > 1} {
@@ -9409,6 +9424,7 @@ proc doseldiff {oldid newid} {
9424
9425 proc mkpatch {} {
9426 global rowmenuid currentid commitinfo patchtop patchnum NS
9427 + global hashlength
9428
9429 if {![info exists currentid]} return
9430 set oldid $currentid
@@ -9423,7 +9439,7 @@ proc mkpatch {} {
9439 ${NS}::label $top.title -text [mc "Generate patch"]
9440 grid $top.title - -pady 10
9441 ${NS}::label $top.from -text [mc "From:"]
9426 - ${NS}::entry $top.fromsha1 -width 40
9442 + ${NS}::entry $top.fromsha1 -width $hashlength
9443 $top.fromsha1 insert 0 $oldid
9444 $top.fromsha1 conf -state readonly
9445 grid $top.from $top.fromsha1 -sticky w
@@ -9432,7 +9448,7 @@ proc mkpatch {} {
9448 $top.fromhead conf -state readonly
9449 grid x $top.fromhead -sticky w
9450 ${NS}::label $top.to -text [mc "To:"]
9435 - ${NS}::entry $top.tosha1 -width 40
9451 + ${NS}::entry $top.tosha1 -width $hashlength
9452 $top.tosha1 insert 0 $newid
9453 $top.tosha1 conf -state readonly
9454 grid $top.to $top.tosha1 -sticky w
@@ -9501,6 +9517,7 @@ proc mkpatchcan {} {
9517
9518 proc mktag {} {
9519 global rowmenuid mktagtop commitinfo NS
9520 + global hashlength
9521
9522 set top .maketag
9523 set mktagtop $top
@@ -9510,7 +9527,7 @@ proc mktag {} {
9527 ${NS}::label $top.title -text [mc "Create tag"]
9528 grid $top.title - -pady 10
9529 ${NS}::label $top.id -text [mc "ID:"]
9513 - ${NS}::entry $top.sha1 -width 40
9530 + ${NS}::entry $top.sha1 -width $hashlength
9531 $top.sha1 insert 0 $rowmenuid
9532 $top.sha1 conf -state readonly
9533 grid $top.id $top.sha1 -sticky w
@@ -9618,10 +9635,11 @@ proc mktaggo {} {
9635
9636 proc copyreference {} {
9637 global rowmenuid autosellen
9638 + global hashlength
9639
9640 set format "%h (\"%s\", %ad)"
9641 set cmd [list git show -s --pretty=format:$format --date=short]
9624 - if {$autosellen < 40} {
9642 + if {$autosellen < $hashlength} {
9643 lappend cmd --abbrev=$autosellen
9644 }
9645 set reference [eval exec $cmd $rowmenuid]
@@ -9632,6 +9650,7 @@ proc copyreference {} {
9650
9651 proc writecommit {} {
9652 global rowmenuid wrcomtop commitinfo wrcomcmd NS
9653 + global hashlength
9654
9655 set top .writecommit
9656 set wrcomtop $top
@@ -9641,7 +9660,7 @@ proc writecommit {} {
9660 ${NS}::label $top.title -text [mc "Write commit to file"]
9661 grid $top.title - -pady 10
9662 ${NS}::label $top.id -text [mc "ID:"]
9644 - ${NS}::entry $top.sha1 -width 40
9663 + ${NS}::entry $top.sha1 -width $hashlength
9664 $top.sha1 insert 0 $rowmenuid
9665 $top.sha1 conf -state readonly
9666 grid $top.id $top.sha1 -sticky w
@@ -9721,6 +9740,7 @@ proc mvbranch {} {
9740
9741 proc branchdia {top valvar uivar} {
9742 global NS commitinfo
9743 + global hashlength
9744 upvar $valvar val $uivar ui
9745
9746 catch {destroy $top}
@@ -9729,7 +9749,7 @@ proc branchdia {top valvar uivar} {
9749 ${NS}::label $top.title -text $ui(title)
9750 grid $top.title - -pady 10
9751 ${NS}::label $top.id -text [mc "ID:"]
9732 - ${NS}::entry $top.sha1 -width 40
9752 + ${NS}::entry $top.sha1 -width $hashlength
9753 $top.sha1 insert 0 $val(id)
9754 $top.sha1 conf -state readonly
9755 grid $top.id $top.sha1 -sticky w
@@ -9739,7 +9759,7 @@ proc branchdia {top valvar uivar} {
9759 grid x $top.head -sticky ew
9760 grid columnconfigure $top 1 -weight 1
9761 ${NS}::label $top.nlab -text [mc "Name:"]
9742 - ${NS}::entry $top.name -width 40
9762 + ${NS}::entry $top.name -width $hashlength
9763 $top.name insert 0 $val(name)
9764 grid $top.nlab $top.name -sticky w
9765 ${NS}::frame $top.buts
@@ -11728,6 +11748,7 @@ proc prefspage_general {notebook} {
11748 global tabstop wrapcomment wrapdefault limitdiffs
11749 global autocopy autoselect autosellen extdifftool perfile_attrs
11750 global hideremotes want_ttk have_ttk maxrefs web_browser
11751 + global hashlength
11752
11753 set page [create_prefs_page $notebook.general]
11754
@@ -11756,7 +11777,8 @@ proc prefspage_general {notebook} {
11777 -variable autoselect
11778 grid x $page.autoselect -sticky w
11779 }
11759 - spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen
11780 +
11781 + spinbox $page.autosellen -from 1 -to $hashlength -width 4 -textvariable autosellen
11782 ${NS}::label $page.autosellenl -text [mc "Length of commit ID to copy"]
11783 grid x $page.autosellenl $page.autosellen -sticky w
11784
@@ -12537,6 +12559,17 @@ catch {
12559 }
12560 }
12561
12562 +# Use object format as hash algorightm (either "sha1" or "sha256")
12563 +set hashalgorithm [exec git rev-parse --show-object-format]
12564 +if {$hashalgorithm eq "sha1"} {
12565 + set hashlength 40
12566 +} elseif {$hashalgorithm eq "sha256"} {
12567 + set hashlength 64
12568 +} else {
12569 + puts stderr "Unknown hash algorithm: $hashalgorithm"
12570 + exit 1
12571 +}
12572 +
12573 set log_showroot true
12574 catch {
12575 set log_showroot [exec git config --bool --get log.showroot]
@@ -12578,7 +12611,7 @@ set limitdiffs 1
12611 set datetimeformat "%Y-%m-%d %H:%M:%S"
12612 set autocopy 0
12613 set autoselect 1
12581 -set autosellen 40
12614 +set autosellen $hashlength
12615 set perfile_attrs 0
12616 set want_ttk 1
12617