gitkon commit Use a panedwindow (0327d27)
   1#!/bin/sh
   2# Tcl ignores the next line -*- tcl -*- \
   3exec wish "$0" -- "${1+$@}"
   4
   5# Copyright (C) 2005 Paul Mackerras.  All rights reserved.
   6# This program is free software; it may be used, copied, modified
   7# and distributed under the terms of the GNU General Public Licence,
   8# either version 2, or (at your option) any later version.
   9
  10# CVS $Revision: 1.2 $
  11
  12set datemode 0
  13set boldnames 0
  14set revtreeargs {}
  15
  16foreach arg $argv {
  17    switch -regexp -- $arg {
  18        "^$" { }
  19        "^-d" { set datemode 1 }
  20        "^-b" { set boldnames 1 }
  21        "^-.*" {
  22            puts stderr "unrecognized option $arg"
  23            exit 1
  24        }
  25        default {
  26            lappend revtreeargs $arg
  27        }
  28    }
  29}
  30
  31proc getcommits {rargs} {
  32    global commits parents cdate nparents children nchildren
  33    if {$rargs == {}} {
  34        set rargs HEAD
  35    }
  36    set commits {}
  37    foreach c [split [eval exec git-rev-tree $rargs] "\n"] {
  38        set i 0
  39        set cid {}
  40        foreach f $c {
  41            if {$i == 0} {
  42                set d $f
  43            } else {
  44                set id [lindex [split $f :] 0]
  45                if {![info exists nchildren($id)]} {
  46                    set children($id) {}
  47                    set nchildren($id) 0
  48                }
  49                if {$i == 1} {
  50                    set cid $id
  51                    lappend commits $id
  52                    set parents($id) {}
  53                    set cdate($id) $d
  54                    set nparents($id) 0
  55                } else {
  56                    lappend parents($cid) $id
  57                    incr nparents($cid)
  58                    incr nchildren($id)
  59                    lappend children($id) $cid
  60                }
  61            }
  62            incr i
  63        }
  64    }
  65}
  66
  67proc readcommit {id} {
  68    global commitinfo commitsummary
  69    set inhdr 1
  70    set comment {}
  71    set headline {}
  72    set auname {}
  73    set audate {}
  74    set comname {}
  75    set comdate {}
  76    foreach line [split [exec git-cat-file commit $id] "\n"] {
  77        if {$inhdr} {
  78            if {$line == {}} {
  79                set inhdr 0
  80            } else {
  81                set tag [lindex $line 0]
  82                if {$tag == "author"} {
  83                    set x [expr {[llength $line] - 2}]
  84                    set audate [lindex $line $x]
  85                    set auname [lrange $line 1 [expr {$x - 1}]]
  86                } elseif {$tag == "committer"} {
  87                    set x [expr {[llength $line] - 2}]
  88                    set comdate [lindex $line $x]
  89                    set comname [lrange $line 1 [expr {$x - 1}]]
  90                }
  91            }
  92        } else {
  93            if {$comment == {}} {
  94                set headline $line
  95            } else {
  96                append comment "\n"
  97            }
  98            append comment $line
  99        }
 100    }
 101    if {$audate != {}} {
 102        set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
 103    }
 104    if {$comdate != {}} {
 105        set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
 106    }
 107    set commitinfo($id) [list $comment $auname $audate $comname $comdate]
 108    set commitsummary($id) [list $headline $auname $audate]
 109}
 110
 111proc makewindow {} {
 112    global canv linespc charspc ctext
 113    panedwindow .ctop -orient vertical
 114    frame .ctop.clist
 115    set canv .ctop.clist.canv
 116    canvas $canv -height [expr 30 * $linespc + 4] -width [expr 90 * $charspc] \
 117        -bg white -relief sunk -bd 1 \
 118        -yscrollincr $linespc -yscrollcommand ".ctop.clist.csb set"
 119    scrollbar .ctop.clist.csb -command "$canv yview" -highlightthickness 0
 120    pack .ctop.clist.csb -side right -fill y
 121    pack $canv -side bottom -fill both -expand 1
 122    .ctop add .ctop.clist
 123    #pack .ctop.clist -side top -fill both -expand 1
 124    set ctext .ctop.ctext
 125    text $ctext -bg white
 126    .ctop add .ctop.ctext
 127    #pack $ctext -side top -fill x -expand 1
 128    pack .ctop -side top -fill both -expand 1
 129
 130    bind $canv <1> {selcanvline %x %y}
 131    bind $canv <B1-Motion> {selcanvline %x %y}
 132    bind $canv <ButtonRelease-4> "$canv yview scroll -5 u"
 133    bind $canv <ButtonRelease-5> "$canv yview scroll 5 u"
 134    bind $canv <2> "$canv scan mark 0 %y"
 135    bind $canv <B2-Motion> "$canv scan dragto 0 %y"
 136    bind . <Key-Prior> "$canv yview scroll -1 p"
 137    bind . <Key-Next> "$canv yview scroll 1 p"
 138    bind . <Key-Delete> "$canv yview scroll -1 p"
 139    bind . <Key-BackSpace> "$canv yview scroll -1 p"
 140    bind . <Key-space> "$canv yview scroll 1 p"
 141    bind . <Key-Up> "$canv yview scroll -1 u"
 142    bind . <Key-Down> "$canv yview scroll 1 u"
 143    bind . Q "set stopped 1; destroy ."
 144}
 145
 146proc truncatetofit {str width font} {
 147    if {[font measure $font $str] <= $width} {
 148        return $str
 149    }
 150    set best 0
 151    set bad [string length $str]
 152    set tmp $str
 153    while {$best < $bad - 1} {
 154        set try [expr {int(($best + $bad) / 2)}]
 155        set tmp "[string range $str 0 [expr $try-1]]..."
 156        if {[font measure $font $tmp] <= $width} {
 157            set best $try
 158        } else {
 159            set bad $try
 160        }
 161    }
 162    return $tmp
 163}
 164
 165proc drawgraph {start} {
 166    global parents children nparents nchildren commits
 167    global canv mainfont namefont canvx0 canvy0 linespc namex datex
 168    global datemode cdate
 169    global lineid linehtag linentag linedtag commitsummary
 170
 171    set colors {green red blue magenta darkgrey brown orange}
 172    set ncolors [llength $colors]
 173    set nextcolor 0
 174    set colormap($start) [lindex $colors 0]
 175    foreach id $commits {
 176        set ncleft($id) $nchildren($id)
 177    }
 178    set todo [list $start]
 179    set level 0
 180    set canvy $canvy0
 181    set linestarty(0) $canvy
 182    set nullentry -1
 183    set lineno -1
 184    while 1 {
 185        incr lineno
 186        set nlines [llength $todo]
 187        set id [lindex $todo $level]
 188        set lineid($lineno) $id
 189        set actualparents {}
 190        foreach p $parents($id) {
 191            if {[info exists ncleft($p)]} {
 192                incr ncleft($p) -1
 193                lappend actualparents $p
 194            }
 195        }
 196        if {![info exists commitsummary($id)]} {
 197            readcommit $id
 198        }
 199        set x [expr $canvx0 + $level * $linespc]
 200        set y2 [expr $canvy + $linespc]
 201        if {$linestarty($level) < $canvy} {
 202            set t [$canv create line $x $linestarty($level) $x $canvy \
 203                       -width 2 -fill $colormap($id)]
 204            $canv lower $t
 205            set linestarty($level) $canvy
 206        }
 207        set t [$canv create oval [expr $x - 4] [expr $canvy - 4] \
 208                   [expr $x + 3] [expr $canvy + 3] \
 209                   -fill blue -outline black -width 1]
 210        $canv raise $t
 211        set xt [expr $canvx0 + $nlines * $linespc]
 212        set headline [lindex $commitsummary($id) 0]
 213        set name [lindex $commitsummary($id) 1]
 214        set date [lindex $commitsummary($id) 2]
 215        set headline [truncatetofit $headline [expr $namex-$xt-$linespc] \
 216                         $mainfont]
 217        set linehtag($lineno) [$canv create text $xt $canvy -anchor w \
 218                                   -text $headline -font $mainfont ]
 219        set name [truncatetofit $name [expr $datex-$namex-$linespc] $namefont]
 220        set linentag($lineno) [$canv create text $namex $canvy -anchor w \
 221                                   -text $name -font $namefont]
 222        set linedtag($lineno) [$canv create text $datex $canvy -anchor w \
 223                                 -text $date -font $mainfont]
 224        if {!$datemode && [llength $actualparents] == 1} {
 225            set p [lindex $actualparents 0]
 226            if {$ncleft($p) == 0 && [lsearch -exact $todo $p] < 0} {
 227                set todo [lreplace $todo $level $level $p]
 228                set colormap($p) $colormap($id)
 229                set canvy $y2
 230                $canv conf -scrollregion [list 0 0 0 $canvy]
 231                update
 232                continue
 233            }
 234        }
 235
 236        set oldtodo $todo
 237        set oldlevel $level
 238        set lines {}
 239        for {set i 0} {$i < $nlines} {incr i} {
 240            if {[lindex $todo $i] == {}} continue
 241            set oldstarty($i) $linestarty($i)
 242            if {$i != $level} {
 243                lappend lines [list $i [lindex $todo $i]]
 244            }
 245        }
 246        unset linestarty
 247        if {$nullentry >= 0} {
 248            set todo [lreplace $todo $nullentry $nullentry]
 249            if {$nullentry < $level} {
 250                incr level -1
 251            }
 252        }
 253
 254        set badcolors [list $colormap($id)]
 255        foreach p $actualparents {
 256            if {[info exists colormap($p)]} {
 257                lappend badcolors $colormap($p)
 258            }
 259        }
 260        set todo [lreplace $todo $level $level]
 261        if {$nullentry > $level} {
 262            incr nullentry -1
 263        }
 264        set i $level
 265        foreach p $actualparents {
 266            set k [lsearch -exact $todo $p]
 267            if {$k < 0} {
 268                set todo [linsert $todo $i $p]
 269                if {$nullentry >= $i} {
 270                    incr nullentry
 271                }
 272                if {$nparents($id) == 1 && $nparents($p) == 1
 273                    && $nchildren($p) == 1} {
 274                    set colormap($p) $colormap($id)
 275                } else {
 276                    for {set j 0} {$j <= $ncolors} {incr j} {
 277                        if {[incr nextcolor] >= $ncolors} {
 278                            set nextcolor 0
 279                        }
 280                        set c [lindex $colors $nextcolor]
 281                        # make sure the incoming and outgoing colors differ
 282                        if {[lsearch -exact $badcolors $c] < 0} break
 283                    }
 284                    set colormap($p) $c
 285                    lappend badcolors $c
 286                }
 287            }
 288            lappend lines [list $oldlevel $p]
 289        }
 290
 291        # choose which one to do next time around
 292        set todol [llength $todo]
 293        set level -1
 294        set latest {}
 295        for {set k $todol} {[incr k -1] >= 0} {} {
 296            set p [lindex $todo $k]
 297            if {$p == {}} continue
 298            if {$ncleft($p) == 0} {
 299                if {$datemode} {
 300                    if {$latest == {} || $cdate($p) > $latest} {
 301                        set level $k
 302                        set latest $cdate($p)
 303                    }
 304                } else {
 305                    set level $k
 306                    break
 307                }
 308            }
 309        }
 310        if {$level < 0} {
 311            if {$todo != {}} {
 312                puts "ERROR: none of the pending commits can be done yet:"
 313                foreach p $todo {
 314                    puts "  $p"
 315                }
 316            }
 317            break
 318        }
 319
 320        # If we are reducing, put in a null entry
 321        if {$todol < $nlines} {
 322            if {$nullentry >= 0} {
 323                set i $nullentry
 324                while {$i < $todol
 325                       && [lindex $oldtodo $i] == [lindex $todo $i]} {
 326                    incr i
 327                }
 328            } else {
 329                set i $oldlevel
 330                if {$level >= $i} {
 331                    incr i
 332                }
 333            }
 334            if {$i >= $todol} {
 335                set nullentry -1
 336            } else {
 337                set nullentry $i
 338                set todo [linsert $todo $nullentry {}]
 339                if {$level >= $i} {
 340                    incr level
 341                }
 342            }
 343        } else {
 344            set nullentry -1
 345        }
 346
 347        foreach l $lines {
 348            set i [lindex $l 0]
 349            set dst [lindex $l 1]
 350            set j [lsearch -exact $todo $dst]
 351            if {$i == $j} {
 352                set linestarty($i) $oldstarty($i)
 353                continue
 354            }
 355            set xi [expr {$canvx0 + $i * $linespc}]
 356            set xj [expr {$canvx0 + $j * $linespc}]
 357            set coords {}
 358            if {$oldstarty($i) < $canvy} {
 359                lappend coords $xi $oldstarty($i)
 360            }
 361            lappend coords $xi $canvy
 362            if {$j < $i - 1} {
 363                lappend coords [expr $xj + $linespc] $canvy
 364            } elseif {$j > $i + 1} {
 365                lappend coords [expr $xj - $linespc] $canvy
 366            }
 367            lappend coords $xj $y2
 368            set t [$canv create line $coords -width 2 -fill $colormap($dst)]
 369            $canv lower $t
 370            if {![info exists linestarty($j)]} {
 371                set linestarty($j) $y2
 372            }
 373        }
 374        set canvy $y2
 375        $canv conf -scrollregion [list 0 0 0 $canvy]
 376        update
 377    }
 378}
 379
 380proc selcanvline {x y} {
 381    global canv canvy0 ctext linespc selectedline
 382    global lineid linehtag linentag linedtag commitinfo
 383    set ymax [lindex [$canv cget -scrollregion] 3]
 384    set yfrac [lindex [$canv yview] 0]
 385    set y [expr {$y + $yfrac * $ymax}]
 386    set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
 387    if {$l < 0} {
 388        set l 0
 389    }
 390    if {[info exists selectedline] && $selectedline == $l} return
 391    if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
 392    $canv select clear
 393    $canv select from $linehtag($l) 0
 394    $canv select to $linehtag($l) end
 395    set id $lineid($l)
 396    $ctext delete 0.0 end
 397    set info $commitinfo($id)
 398    $ctext insert end "Author: [lindex $info 1]  \t[lindex $info 2]\n"
 399    $ctext insert end "Committer: [lindex $info 3]  \t[lindex $info 4]\n"
 400    $ctext insert end "\n"
 401    $ctext insert end [lindex $info 0]
 402}
 403
 404getcommits $revtreeargs
 405
 406set mainfont {Helvetica 9}
 407set namefont $mainfont
 408if {$boldnames} {
 409    lappend namefont bold
 410}
 411set linespc [font metrics $mainfont -linespace]
 412set charspc [font measure $mainfont "m"]
 413
 414set canvy0 [expr 3 + 0.5 * $linespc]
 415set canvx0 [expr 3 + 0.5 * $linespc]
 416set namex [expr 45 * $charspc]
 417set datex [expr 75 * $charspc]
 418
 419makewindow
 420
 421set start {}
 422foreach id $commits {
 423    if {$nchildren($id) == 0} {
 424        set start $id
 425        break
 426    }
 427}
 428if {$start != {}} {
 429    drawgraph $start
 430}