gitkon commit Display the list of changed files in a listbox pane. (5ad588d)
   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.3 $
  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 gettreediffs {id} {
 112    global treediffs parents
 113    set p [lindex $parents($id) 0]
 114    set diff {}
 115    foreach line [split [exec git-diff-tree -r $p $id] "\n"] {
 116        set type [lindex $line 1]
 117        set file [lindex $line 3]
 118        if {$type == "blob"} {
 119            lappend diff $file
 120        }
 121    }
 122    set treediffs($id) $diff
 123}
 124
 125proc makewindow {} {
 126    global canv linespc charspc ctext cflist
 127    panedwindow .ctop -orient vertical
 128    frame .ctop.clist
 129    set canv .ctop.clist.canv
 130    canvas $canv -height [expr 30 * $linespc + 4] -width [expr 90 * $charspc] \
 131        -bg white -relief sunk -bd 1 \
 132        -yscrollincr $linespc -yscrollcommand ".ctop.clist.csb set"
 133    scrollbar .ctop.clist.csb -command "$canv yview" -highlightthickness 0
 134    pack .ctop.clist.csb -side right -fill y
 135    pack $canv -side bottom -fill both -expand 1
 136    .ctop add .ctop.clist
 137    #pack .ctop.clist -side top -fill both -expand 1
 138    panedwindow .ctop.cdet -orient horizontal
 139    .ctop add .ctop.cdet
 140    set ctext .ctop.cdet.ctext
 141    text $ctext -bg white -state disabled
 142    .ctop.cdet add $ctext
 143    #pack $ctext -side top -fill x -expand 1
 144    set cflist .ctop.cdet.cfiles
 145    listbox $cflist -width 30 -bg white
 146    .ctop.cdet add $cflist
 147    pack .ctop -side top -fill both -expand 1
 148
 149    bind $canv <1> {selcanvline %x %y}
 150    bind $canv <B1-Motion> {selcanvline %x %y}
 151    bind $canv <ButtonRelease-4> "$canv yview scroll -5 u"
 152    bind $canv <ButtonRelease-5> "$canv yview scroll 5 u"
 153    bind $canv <2> "$canv scan mark 0 %y"
 154    bind $canv <B2-Motion> "$canv scan dragto 0 %y"
 155    bind . <Key-Prior> "$canv yview scroll -1 p"
 156    bind . <Key-Next> "$canv yview scroll 1 p"
 157    bind . <Key-Delete> "$canv yview scroll -1 p"
 158    bind . <Key-BackSpace> "$canv yview scroll -1 p"
 159    bind . <Key-space> "$canv yview scroll 1 p"
 160    bind . <Key-Up> "selnextline -1"
 161    bind . <Key-Down> "selnextline 1"
 162    bind . Q "set stopped 1; destroy ."
 163}
 164
 165proc truncatetofit {str width font} {
 166    if {[font measure $font $str] <= $width} {
 167        return $str
 168    }
 169    set best 0
 170    set bad [string length $str]
 171    set tmp $str
 172    while {$best < $bad - 1} {
 173        set try [expr {int(($best + $bad) / 2)}]
 174        set tmp "[string range $str 0 [expr $try-1]]..."
 175        if {[font measure $font $tmp] <= $width} {
 176            set best $try
 177        } else {
 178            set bad $try
 179        }
 180    }
 181    return $tmp
 182}
 183
 184proc drawgraph {start} {
 185    global parents children nparents nchildren commits
 186    global canv mainfont namefont canvx0 canvy0 canvy linespc namex datex
 187    global datemode cdate
 188    global lineid linehtag linentag linedtag commitsummary
 189
 190    set colors {green red blue magenta darkgrey brown orange}
 191    set ncolors [llength $colors]
 192    set nextcolor 0
 193    set colormap($start) [lindex $colors 0]
 194    foreach id $commits {
 195        set ncleft($id) $nchildren($id)
 196    }
 197    set todo [list $start]
 198    set level 0
 199    set canvy $canvy0
 200    set linestarty(0) $canvy
 201    set nullentry -1
 202    set lineno -1
 203    while 1 {
 204        incr lineno
 205        set nlines [llength $todo]
 206        set id [lindex $todo $level]
 207        set lineid($lineno) $id
 208        set actualparents {}
 209        foreach p $parents($id) {
 210            if {[info exists ncleft($p)]} {
 211                incr ncleft($p) -1
 212                lappend actualparents $p
 213            }
 214        }
 215        if {![info exists commitsummary($id)]} {
 216            readcommit $id
 217        }
 218        set x [expr $canvx0 + $level * $linespc]
 219        set y2 [expr $canvy + $linespc]
 220        if {$linestarty($level) < $canvy} {
 221            set t [$canv create line $x $linestarty($level) $x $canvy \
 222                       -width 2 -fill $colormap($id)]
 223            $canv lower $t
 224            set linestarty($level) $canvy
 225        }
 226        set t [$canv create oval [expr $x - 4] [expr $canvy - 4] \
 227                   [expr $x + 3] [expr $canvy + 3] \
 228                   -fill blue -outline black -width 1]
 229        $canv raise $t
 230        set xt [expr $canvx0 + $nlines * $linespc]
 231        set headline [lindex $commitsummary($id) 0]
 232        set name [lindex $commitsummary($id) 1]
 233        set date [lindex $commitsummary($id) 2]
 234        set headline [truncatetofit $headline [expr $namex-$xt-$linespc] \
 235                         $mainfont]
 236        set linehtag($lineno) [$canv create text $xt $canvy -anchor w \
 237                                   -text $headline -font $mainfont ]
 238        set name [truncatetofit $name [expr $datex-$namex-$linespc] $namefont]
 239        set linentag($lineno) [$canv create text $namex $canvy -anchor w \
 240                                   -text $name -font $namefont]
 241        set linedtag($lineno) [$canv create text $datex $canvy -anchor w \
 242                                 -text $date -font $mainfont]
 243        if {!$datemode && [llength $actualparents] == 1} {
 244            set p [lindex $actualparents 0]
 245            if {$ncleft($p) == 0 && [lsearch -exact $todo $p] < 0} {
 246                set todo [lreplace $todo $level $level $p]
 247                set colormap($p) $colormap($id)
 248                set canvy $y2
 249                $canv conf -scrollregion [list 0 0 0 $canvy]
 250                update
 251                continue
 252            }
 253        }
 254
 255        set oldtodo $todo
 256        set oldlevel $level
 257        set lines {}
 258        for {set i 0} {$i < $nlines} {incr i} {
 259            if {[lindex $todo $i] == {}} continue
 260            set oldstarty($i) $linestarty($i)
 261            if {$i != $level} {
 262                lappend lines [list $i [lindex $todo $i]]
 263            }
 264        }
 265        unset linestarty
 266        if {$nullentry >= 0} {
 267            set todo [lreplace $todo $nullentry $nullentry]
 268            if {$nullentry < $level} {
 269                incr level -1
 270            }
 271        }
 272
 273        set badcolors [list $colormap($id)]
 274        foreach p $actualparents {
 275            if {[info exists colormap($p)]} {
 276                lappend badcolors $colormap($p)
 277            }
 278        }
 279        set todo [lreplace $todo $level $level]
 280        if {$nullentry > $level} {
 281            incr nullentry -1
 282        }
 283        set i $level
 284        foreach p $actualparents {
 285            set k [lsearch -exact $todo $p]
 286            if {$k < 0} {
 287                set todo [linsert $todo $i $p]
 288                if {$nullentry >= $i} {
 289                    incr nullentry
 290                }
 291                if {$nparents($id) == 1 && $nparents($p) == 1
 292                    && $nchildren($p) == 1} {
 293                    set colormap($p) $colormap($id)
 294                } else {
 295                    for {set j 0} {$j <= $ncolors} {incr j} {
 296                        if {[incr nextcolor] >= $ncolors} {
 297                            set nextcolor 0
 298                        }
 299                        set c [lindex $colors $nextcolor]
 300                        # make sure the incoming and outgoing colors differ
 301                        if {[lsearch -exact $badcolors $c] < 0} break
 302                    }
 303                    set colormap($p) $c
 304                    lappend badcolors $c
 305                }
 306            }
 307            lappend lines [list $oldlevel $p]
 308        }
 309
 310        # choose which one to do next time around
 311        set todol [llength $todo]
 312        set level -1
 313        set latest {}
 314        for {set k $todol} {[incr k -1] >= 0} {} {
 315            set p [lindex $todo $k]
 316            if {$p == {}} continue
 317            if {$ncleft($p) == 0} {
 318                if {$datemode} {
 319                    if {$latest == {} || $cdate($p) > $latest} {
 320                        set level $k
 321                        set latest $cdate($p)
 322                    }
 323                } else {
 324                    set level $k
 325                    break
 326                }
 327            }
 328        }
 329        if {$level < 0} {
 330            if {$todo != {}} {
 331                puts "ERROR: none of the pending commits can be done yet:"
 332                foreach p $todo {
 333                    puts "  $p"
 334                }
 335            }
 336            break
 337        }
 338
 339        # If we are reducing, put in a null entry
 340        if {$todol < $nlines} {
 341            if {$nullentry >= 0} {
 342                set i $nullentry
 343                while {$i < $todol
 344                       && [lindex $oldtodo $i] == [lindex $todo $i]} {
 345                    incr i
 346                }
 347            } else {
 348                set i $oldlevel
 349                if {$level >= $i} {
 350                    incr i
 351                }
 352            }
 353            if {$i >= $todol} {
 354                set nullentry -1
 355            } else {
 356                set nullentry $i
 357                set todo [linsert $todo $nullentry {}]
 358                if {$level >= $i} {
 359                    incr level
 360                }
 361            }
 362        } else {
 363            set nullentry -1
 364        }
 365
 366        foreach l $lines {
 367            set i [lindex $l 0]
 368            set dst [lindex $l 1]
 369            set j [lsearch -exact $todo $dst]
 370            if {$i == $j} {
 371                set linestarty($i) $oldstarty($i)
 372                continue
 373            }
 374            set xi [expr {$canvx0 + $i * $linespc}]
 375            set xj [expr {$canvx0 + $j * $linespc}]
 376            set coords {}
 377            if {$oldstarty($i) < $canvy} {
 378                lappend coords $xi $oldstarty($i)
 379            }
 380            lappend coords $xi $canvy
 381            if {$j < $i - 1} {
 382                lappend coords [expr $xj + $linespc] $canvy
 383            } elseif {$j > $i + 1} {
 384                lappend coords [expr $xj - $linespc] $canvy
 385            }
 386            lappend coords $xj $y2
 387            set t [$canv create line $coords -width 2 -fill $colormap($dst)]
 388            $canv lower $t
 389            if {![info exists linestarty($j)]} {
 390                set linestarty($j) $y2
 391            }
 392        }
 393        set canvy $y2
 394        $canv conf -scrollregion [list 0 0 0 $canvy]
 395        update
 396    }
 397}
 398
 399proc selcanvline {x y} {
 400    global canv canvy0 ctext linespc selectedline
 401    global lineid linehtag linentag linedtag commitinfo
 402    set ymax [lindex [$canv cget -scrollregion] 3]
 403    set yfrac [lindex [$canv yview] 0]
 404    set y [expr {$y + $yfrac * $ymax}]
 405    set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
 406    if {$l < 0} {
 407        set l 0
 408    }
 409    if {[info exists selectedline] && $selectedline == $l} return
 410    selectline $l
 411}
 412
 413proc selectline {l} {
 414    global canv ctext commitinfo selectedline lineid linehtag
 415    global canvy canvy0 linespc nparents
 416    global cflist treediffs
 417    if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
 418    $canv select clear
 419    $canv select from $linehtag($l) 0
 420    $canv select to $linehtag($l) end
 421    set y [expr {$canvy0 + $l * $linespc}]
 422    set ytop [expr {($y - $linespc / 2.0) / $canvy}]
 423    set ybot [expr {($y + $linespc / 2.0) / $canvy}]
 424    set wnow [$canv yview]
 425    if {$ytop < [lindex $wnow 0]} {
 426        $canv yview moveto $ytop
 427    } elseif {$ybot > [lindex $wnow 1]} {
 428        set wh [expr {[lindex $wnow 1] - [lindex $wnow 0]}]
 429        $canv yview moveto [expr {$ybot - $wh}]
 430    }
 431    set selectedline $l
 432
 433    set id $lineid($l)
 434    $ctext conf -state normal
 435    $ctext delete 0.0 end
 436    set info $commitinfo($id)
 437    $ctext insert end "Author: [lindex $info 1]  \t[lindex $info 2]\n"
 438    $ctext insert end "Committer: [lindex $info 3]  \t[lindex $info 4]\n"
 439    $ctext insert end "\n"
 440    $ctext insert end [lindex $info 0]
 441    $ctext conf -state disabled
 442
 443    $cflist delete 0 end
 444    if {$nparents($id) == 1} {
 445        if {![info exists treediffs($id)]} {
 446            gettreediffs $id
 447        }
 448        foreach f $treediffs($id) {
 449            $cflist insert end $f
 450        }
 451    }
 452
 453}
 454
 455proc selnextline {dir} {
 456    global selectedline
 457    if {![info exists selectedline]} return
 458    set l [expr $selectedline + $dir]
 459    selectline $l
 460}
 461
 462getcommits $revtreeargs
 463
 464set mainfont {Helvetica 9}
 465set namefont $mainfont
 466if {$boldnames} {
 467    lappend namefont bold
 468}
 469set linespc [font metrics $mainfont -linespace]
 470set charspc [font measure $mainfont "m"]
 471
 472set canvy0 [expr 3 + 0.5 * $linespc]
 473set canvx0 [expr 3 + 0.5 * $linespc]
 474set namex [expr 45 * $charspc]
 475set datex [expr 75 * $charspc]
 476
 477makewindow
 478
 479set start {}
 480foreach id $commits {
 481    if {$nchildren($id) == 0} {
 482        set start $id
 483        break
 484    }
 485}
 486if {$start != {}} {
 487    drawgraph $start
 488}