git-guion commit git-gui: Initial revision. (cb07fc2)
   1#!/bin/sh
   2# Tcl ignores the next line -*- tcl -*- \
   3exec wish "$0" -- "$@"
   4
   5# Copyright (C) 2006 Shawn Pearce, 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
  11######################################################################
  12##
  13## status
  14
  15set status_active 0
  16
  17proc update_status {} {
  18        global gitdir HEAD commit_type
  19        global ui_index ui_other ui_status_value
  20        global status_active file_states
  21
  22        if {$status_active > 0} return
  23
  24        array unset file_states
  25        set ui_status_value {Refreshing file status...}
  26        foreach w [list $ui_index $ui_other] {
  27                $w conf -state normal
  28                $w delete 0.0 end
  29                $w conf -state disabled
  30        }
  31
  32        if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} {
  33                set commit_type initial
  34        } else {
  35                set commit_type normal
  36        }
  37
  38        set ls_others [list | git ls-files --others -z \
  39                --exclude-per-directory=.gitignore]
  40        set info_exclude [file join $gitdir info exclude]
  41        if {[file readable $info_exclude]} {
  42                lappend ls_others "--exclude-from=$info_exclude"
  43        }
  44
  45        set fd_di [open "| git diff-index --cached -z $HEAD" r]
  46        set fd_df [open "| git diff-files -z" r]
  47        set fd_lo [open $ls_others r]
  48        set status_active 3
  49
  50        fconfigure $fd_di -blocking 0 -translation binary
  51        fconfigure $fd_df -blocking 0 -translation binary
  52        fconfigure $fd_lo -blocking 0 -translation binary
  53        fileevent $fd_di readable [list read_diff_index $fd_di]
  54        fileevent $fd_df readable [list read_diff_files $fd_df]
  55        fileevent $fd_lo readable [list read_ls_others $fd_lo]
  56}
  57
  58proc read_diff_index {fd} {
  59        global buf_rdi
  60
  61        append buf_rdi [read $fd]
  62        set pck [split $buf_rdi "\0"]
  63        set buf_rdi [lindex $pck end]
  64        foreach {m p} [lrange $pck 0 end-1] {
  65                if {$m != {} && $p != {}} {
  66                        display_file $p [string index $m end]_
  67                }
  68        }
  69        status_eof $fd buf_rdi
  70}
  71
  72proc read_diff_files {fd} {
  73        global buf_rdf
  74
  75        append buf_rdf [read $fd]
  76        set pck [split $buf_rdf "\0"]
  77        set buf_rdf [lindex $pck end]
  78        foreach {m p} [lrange $pck 0 end-1] {
  79                if {$m != {} && $p != {}} {
  80                        display_file $p _[string index $m end]
  81                }
  82        }
  83        status_eof $fd buf_rdf
  84}
  85
  86proc read_ls_others {fd} {
  87        global buf_rlo
  88
  89        append buf_rlo [read $fd]
  90        set pck [split $buf_rlo "\0"]
  91        set buf_rlo [lindex $pck end]
  92        foreach p [lrange $pck 0 end-1] {
  93                display_file $p _O
  94        }
  95        status_eof $fd buf_rlo
  96}
  97
  98proc status_eof {fd buf} {
  99        global status_active $buf
 100        global ui_fname_value ui_status_value
 101
 102        if {[eof $fd]} {
 103                set $buf {}
 104                close $fd
 105                if {[incr status_active -1] == 0} {
 106                        set ui_status_value {Ready.}
 107                        if {$ui_fname_value != {}} {
 108                                show_diff $ui_fname_value
 109                        }
 110                }
 111        }
 112}
 113
 114######################################################################
 115##
 116## diff
 117
 118set diff_active 0
 119
 120proc clear_diff {} {
 121        global ui_diff ui_fname_value ui_fstatus_value
 122
 123        $ui_diff conf -state normal
 124        $ui_diff delete 0.0 end
 125        $ui_diff conf -state disabled
 126        set ui_fname_value {}
 127        set ui_fstatus_value {}
 128}
 129
 130proc show_diff {path} {
 131        global file_states HEAD status_active diff_3way diff_active
 132        global ui_diff ui_fname_value ui_fstatus_value ui_status_value
 133
 134        if {$status_active > 0} return
 135        if {$diff_active} return
 136
 137        clear_diff
 138        set s $file_states($path)
 139        set m [lindex $s 0]
 140        set diff_3way 0
 141        set diff_active 1
 142        set ui_fname_value $path
 143        set ui_fstatus_value [mapdesc $m $path]
 144        set ui_status_value "Loading diff of $path..."
 145
 146        set cmd [list | git diff-index -p $HEAD -- $path]
 147        switch $m {
 148        AM {
 149        }
 150        MM {
 151                set cmd [list | git diff-index -p -c $HEAD $path]
 152        }
 153        _O {
 154                if {[catch {
 155                                set fd [open $path r]
 156                                set content [read $fd]
 157                                close $fd
 158                        } err ]} {
 159                        set ui_status_value "Unable to display $path"
 160                        error_popup "Error loading file:\n$err"
 161                        return
 162                }
 163                $ui_diff conf -state normal
 164                $ui_diff insert end $content
 165                $ui_diff conf -state disabled
 166                return
 167        }
 168        }
 169
 170        if {[catch {set fd [open $cmd r]} err]} {
 171                set ui_status_value "Unable to display $path"
 172                error_popup "Error loading diff:\n$err"
 173                return
 174        }
 175
 176        fconfigure $fd -blocking 0
 177        fileevent $fd readable [list read_diff $fd]
 178}
 179
 180proc read_diff {fd} {
 181        global ui_diff ui_status_value diff_3way diff_active
 182
 183        while {[gets $fd line] >= 0} {
 184                if {[string match index* $line]} {
 185                        if {[string first , $line] >= 0} {
 186                                set diff_3way 1
 187                        }
 188                }
 189
 190                $ui_diff conf -state normal
 191                if {!$diff_3way} {
 192                        set x [string index $line 0]
 193                        switch -- $x {
 194                        "@" {set tags da}
 195                        "+" {set tags dp}
 196                        "-" {set tags dm}
 197                        default {set tags {}}
 198                        }
 199                } else {
 200                        set x [string range $line 0 1]
 201                        switch -- $x {
 202                        default {set tags {}}
 203                        "@@" {set tags da}
 204                        "++" {set tags dp; set x " +"}
 205                        " +" {set tags {di bold}; set x "++"}
 206                        "+ " {set tags dni; set x "-+"}
 207                        "--" {set tags dm; set x " -"}
 208                        " -" {set tags {dm bold}; set x "--"}
 209                        "- " {set tags di; set x "+-"}
 210                        default {set tags {}}
 211                        }
 212                        set line [string replace $line 0 1 $x]
 213                }
 214                $ui_diff insert end $line $tags
 215                $ui_diff insert end "\n"
 216                $ui_diff conf -state disabled
 217        }
 218
 219        if {[eof $fd]} {
 220                close $fd
 221                set diff_active 0
 222                set ui_status_value {Ready.}
 223        }
 224}
 225
 226######################################################################
 227##
 228## ui helpers
 229
 230proc mapcol {state path} {
 231        global all_cols
 232
 233        if {[catch {set r $all_cols($state)}]} {
 234                puts "error: no column for state={$state} $path"
 235                return o
 236        }
 237        return $r
 238}
 239
 240proc mapicon {state path} {
 241        global all_icons
 242
 243        if {[catch {set r $all_icons($state)}]} {
 244                puts "error: no icon for state={$state} $path"
 245                return file_plain
 246        }
 247        return $r
 248}
 249
 250proc mapdesc {state path} {
 251        global all_descs
 252
 253        if {[catch {set r $all_descs($state)}]} {
 254                puts "error: no desc for state={$state} $path"
 255                return $state
 256        }
 257        return $r
 258}
 259
 260proc bsearch {w path} {
 261        set hi [expr [lindex [split [$w index end] .] 0] - 2]
 262        if {$hi == 0} {
 263                return -1
 264        }
 265        set lo 0
 266        while {$lo < $hi} {
 267                set mi [expr [expr $lo + $hi] / 2]
 268                set ti [expr $mi + 1]
 269                set cmp [string compare [$w get $ti.1 $ti.end] $path]
 270                if {$cmp < 0} {
 271                        set lo $ti
 272                } elseif {$cmp == 0} {
 273                        return $mi
 274                } else {
 275                        set hi $mi
 276                }
 277        }
 278        return -[expr $lo + 1]
 279}
 280
 281proc merge_state {path state} {
 282        global file_states
 283
 284        if {[array names file_states -exact $path] == {}}  {
 285                set o __
 286                set s [list $o none none]
 287        } else {
 288                set s $file_states($path)
 289                set o [lindex $s 0]
 290        }
 291
 292        set m [lindex $s 0]
 293        if {[string index $state 0] == "_"} {
 294                set state [string index $m 0][string index $state 1]
 295        } elseif {[string index $state 0] == "*"} {
 296                set state _[string index $state 1]
 297        }
 298
 299        if {[string index $state 1] == "_"} {
 300                set state [string index $state 0][string index $m 1]
 301        } elseif {[string index $state 1] == "*"} {
 302                set state [string index $state 0]_
 303        }
 304
 305        set file_states($path) [lreplace $s 0 0 $state]
 306        return $o
 307}
 308
 309proc display_file {path state} {
 310        global ui_index ui_other file_states
 311
 312        set old_m [merge_state $path $state]
 313        set s $file_states($path)
 314        set m [lindex $s 0]
 315
 316        if {[mapcol $m $path] == "o"} {
 317                set ii 1
 318                set ai 2
 319                set iw $ui_index
 320                set aw $ui_other
 321        } else {
 322                set ii 2
 323                set ai 1
 324                set iw $ui_other
 325                set aw $ui_index
 326        }
 327
 328        set d [lindex $s $ii]
 329        if {$d != "none"} {
 330                set lno [bsearch $iw $path]
 331                if {$lno >= 0} {
 332                        incr lno
 333                        $iw conf -state normal
 334                        $iw delete $lno.0 [expr $lno + 1].0
 335                        $iw conf -state disabled
 336                        set s [lreplace $s $ii $ii none]
 337                }
 338        }
 339
 340        set d [lindex $s $ai]
 341        if {$d == "none"} {
 342                set lno [expr abs([bsearch $aw $path] + 1) + 1]
 343                $aw conf -state normal
 344                set ico [$aw image create $lno.0 \
 345                        -align center -padx 5 -pady 1 \
 346                        -image [mapicon $m $path]]
 347                $aw insert $lno.1 "$path\n"
 348                $aw conf -state disabled
 349                set file_states($path) [lreplace $s $ai $ai [list $ico]]
 350        } elseif {[mapicon $m $path] != [mapicon $old_m $path]} {
 351                set ico [lindex $d 0]
 352                $aw image conf $ico -image [mapicon $m $path]
 353        }
 354}
 355
 356proc toggle_mode {path} {
 357        global file_states
 358
 359        set s $file_states($path)
 360        set m [lindex $s 0]
 361
 362        switch -- $m {
 363        AM -
 364        _O {
 365                set new A*
 366                set cmd [list exec git update-index --add $path]
 367        }
 368        MM {
 369                set new M*
 370                set cmd [list exec git update-index $path]
 371        }
 372        _D {
 373                set new D*
 374                set cmd [list exec git update-index --remove $path]
 375        }
 376        default {
 377                return
 378        }
 379        }
 380
 381        if {[catch {eval $cmd} err]} {
 382                error_popup "Error processing file:\n$err"
 383                return
 384        }
 385        display_file $path $new
 386}
 387
 388######################################################################
 389##
 390## icons
 391
 392set filemask {
 393#define mask_width 14
 394#define mask_height 15
 395static unsigned char mask_bits[] = {
 396   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
 397   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
 398   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
 399}
 400
 401image create bitmap file_plain -background white -foreground black -data {
 402#define plain_width 14
 403#define plain_height 15
 404static unsigned char plain_bits[] = {
 405   0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
 406   0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
 407   0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 408} -maskdata $filemask
 409
 410image create bitmap file_mod -background white -foreground blue -data {
 411#define mod_width 14
 412#define mod_height 15
 413static unsigned char mod_bits[] = {
 414   0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
 415   0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
 416   0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 417} -maskdata $filemask
 418
 419image create bitmap file_tick -background white -foreground "#007000" -data {
 420#define file_tick_width 14
 421#define file_tick_height 15
 422static unsigned char file_tick_bits[] = {
 423   0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
 424   0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
 425   0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 426} -maskdata $filemask
 427
 428image create bitmap file_parttick -background white -foreground "#005050" -data {
 429#define parttick_width 14
 430#define parttick_height 15
 431static unsigned char parttick_bits[] = {
 432   0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
 433   0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
 434   0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 435} -maskdata $filemask
 436
 437image create bitmap file_question -background white -foreground black -data {
 438#define file_question_width 14
 439#define file_question_height 15
 440static unsigned char file_question_bits[] = {
 441   0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
 442   0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
 443   0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 444} -maskdata $filemask
 445
 446image create bitmap file_removed -background white -foreground red -data {
 447#define file_removed_width 14
 448#define file_removed_height 15
 449static unsigned char file_removed_bits[] = {
 450   0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
 451   0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
 452   0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
 453} -maskdata $filemask
 454
 455image create bitmap file_merge -background white -foreground blue -data {
 456#define file_merge_width 14
 457#define file_merge_height 15
 458static unsigned char file_merge_bits[] = {
 459   0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
 460   0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
 461   0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 462} -maskdata $filemask
 463
 464foreach i {
 465                {__ i "Unmodified"           plain}
 466                {_M i "Modified"             mod}
 467                {M_ i "Checked in"           tick}
 468                {MM i "Partially checked in" parttick}
 469
 470                {_O o "Untracked"            plain}
 471                {A_ o "Added"                tick}
 472                {AM o "Partially added"      parttick}
 473
 474                {_D i "Missing"              question}
 475                {D_ i "Removed"              removed}
 476                {DD i "Removed"              removed}
 477                {DO i "Partially removed"    removed}
 478
 479                {UM i "Merge conflicts"      merge}
 480                {U_ i "Merge conflicts"      merge}
 481        } {
 482        set all_cols([lindex $i 0]) [lindex $i 1]
 483        set all_descs([lindex $i 0]) [lindex $i 2]
 484        set all_icons([lindex $i 0]) file_[lindex $i 3]
 485}
 486unset filemask i
 487
 488######################################################################
 489##
 490## util
 491
 492proc error_popup {msg} {
 493        set w .error
 494        toplevel $w
 495        wm transient $w .
 496        show_msg $w $w $msg
 497}
 498
 499proc show_msg {w top msg} {
 500        message $w.m -text $msg -justify center -aspect 400
 501        pack $w.m -side top -fill x -padx 20 -pady 20
 502        button $w.ok -text OK -command "destroy $top"
 503        pack $w.ok -side bottom -fill x
 504        bind $top <Visibility> "grab $top; focus $top"
 505        bind $top <Key-Return> "destroy $top"
 506        tkwait window $top
 507}
 508
 509######################################################################
 510##
 511## ui commands
 512
 513proc do_gitk {} {
 514        global tcl_platform
 515
 516    if {$tcl_platform(platform) == "windows"} {
 517                exec sh -c gitk &
 518        } else {
 519                exec gitk &
 520        }
 521}
 522
 523proc do_quit {} {
 524        destroy .
 525}
 526
 527proc do_rescan {} {
 528        update_status
 529}
 530
 531# shift == 1: left click
 532#          3: right click  
 533proc click {w x y shift wx wy} {
 534        set pos [split [$w index @$x,$y] .]
 535        set lno [lindex $pos 0]
 536        set col [lindex $pos 1]
 537        set path [$w get $lno.1 $lno.end]
 538        if {$path == {}} return
 539
 540        if {$col > 0 && $shift == 1} {
 541                show_diff $path
 542        }
 543}
 544
 545proc unclick {w x y} {
 546        set pos [split [$w index @$x,$y] .]
 547        set lno [lindex $pos 0]
 548        set col [lindex $pos 1]
 549        set path [$w get $lno.1 $lno.end]
 550        if {$path == {}} return
 551
 552        if {$col == 0} {
 553                toggle_mode $path
 554        }
 555}
 556
 557######################################################################
 558##
 559## ui init
 560
 561set mainfont {Helvetica 10}
 562set difffont {Courier 10}
 563set maincursor [. cget -cursor]
 564
 565# -- Menu Bar
 566menu .mbar -tearoff 0
 567.mbar add cascade -label Project -menu .mbar.project
 568.mbar add cascade -label Commit -menu .mbar.commit
 569.mbar add cascade -label Fetch -menu .mbar.fetch
 570.mbar add cascade -label Pull -menu .mbar.pull
 571. configure -menu .mbar
 572
 573# -- Project Menu
 574menu .mbar.project
 575.mbar.project add command -label Visulize \
 576        -command do_gitk \
 577        -font $mainfont
 578.mbar.project add command -label Quit \
 579        -command do_quit \
 580        -font $mainfont
 581
 582# -- Commit Menu
 583menu .mbar.commit
 584.mbar.commit add command -label Rescan \
 585        -command do_rescan \
 586        -font $mainfont
 587
 588# -- Fetch Menu
 589menu .mbar.fetch
 590
 591# -- Pull Menu
 592menu .mbar.pull
 593
 594# -- Main Window Layout
 595panedwindow .vpane -orient vertical
 596panedwindow .vpane.files -orient horizontal
 597.vpane add .vpane.files -sticky nsew
 598pack .vpane -anchor n -side top -fill both -expand 1
 599
 600# -- Index File List
 601set ui_index .vpane.files.index.list
 602frame .vpane.files.index -height 100 -width 400
 603label .vpane.files.index.title -text {Modified Files} \
 604        -background green \
 605        -font $mainfont
 606text $ui_index -background white -borderwidth 0 \
 607        -width 40 -height 10 \
 608        -font $mainfont \
 609        -yscrollcommand {.vpane.files.index.sb set} \
 610        -cursor $maincursor \
 611        -state disabled
 612scrollbar .vpane.files.index.sb -command [list $ui_index yview]
 613pack .vpane.files.index.title -side top -fill x
 614pack .vpane.files.index.sb -side right -fill y
 615pack $ui_index -side left -fill both -expand 1
 616.vpane.files add .vpane.files.index -sticky nsew
 617
 618# -- Other (Add) File List
 619set ui_other .vpane.files.other.list
 620frame .vpane.files.other -height 100 -width 100
 621label .vpane.files.other.title -text {Untracked Files} \
 622        -background red \
 623        -font $mainfont
 624text $ui_other -background white -borderwidth 0 \
 625        -width 40 -height 10 \
 626        -font $mainfont \
 627        -yscrollcommand {.vpane.files.other.sb set} \
 628        -cursor $maincursor \
 629        -state disabled
 630scrollbar .vpane.files.other.sb -command [list $ui_other yview]
 631pack .vpane.files.other.title -side top -fill x
 632pack .vpane.files.other.sb -side right -fill y
 633pack $ui_other -side left -fill both -expand 1
 634.vpane.files add .vpane.files.other -sticky nsew
 635
 636# -- Diff Header
 637set ui_fname_value {}
 638set ui_fstatus_value {}
 639frame .vpane.diff -height 100 -width 100
 640frame .vpane.diff.header
 641label .vpane.diff.header.l1 -text {File:} -font $mainfont
 642label .vpane.diff.header.l2 -textvariable ui_fname_value \
 643        -anchor w \
 644        -justify left \
 645        -font $mainfont
 646label .vpane.diff.header.l3 -text {Status:} -font $mainfont
 647label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
 648        -width 20 \
 649        -anchor w \
 650        -justify left \
 651        -font $mainfont
 652pack .vpane.diff.header.l1 -side left
 653pack .vpane.diff.header.l2 -side left -fill x
 654pack .vpane.diff.header.l4 -side right
 655pack .vpane.diff.header.l3 -side right
 656
 657# -- Diff Body
 658frame .vpane.diff.body
 659set ui_diff .vpane.diff.body.t
 660text $ui_diff -background white -borderwidth 0 \
 661        -width 40 -height 20 \
 662        -font $difffont \
 663        -xscrollcommand {.vpane.diff.body.sbx set} \
 664        -yscrollcommand {.vpane.diff.body.sby set} \
 665        -cursor $maincursor \
 666        -state disabled
 667scrollbar .vpane.diff.body.sbx -orient horizontal \
 668        -command [list $ui_diff xview]
 669scrollbar .vpane.diff.body.sby -orient vertical \
 670        -command [list $ui_diff yview]
 671pack .vpane.diff.body.sbx -side bottom -fill x
 672pack .vpane.diff.body.sby -side right -fill y
 673pack $ui_diff -side left -fill both -expand 1
 674pack .vpane.diff.header -side top -fill x
 675pack .vpane.diff.body -side bottom -fill both -expand 1
 676.vpane add .vpane.diff -stick nsew
 677
 678$ui_diff tag conf dm -foreground red
 679$ui_diff tag conf dp -foreground blue
 680$ui_diff tag conf da -font [concat $difffont bold]
 681$ui_diff tag conf di -foreground "#00a000"
 682$ui_diff tag conf dni -foreground "#a000a0"
 683$ui_diff tag conf bold -font [concat $difffont bold]
 684
 685# -- Commit Area
 686frame .vpane.commarea -height 50
 687.vpane add .vpane.commarea -stick nsew
 688
 689# -- Commit Area Buttons
 690frame .vpane.commarea.buttons
 691label .vpane.commarea.buttons.l -text {} \
 692        -anchor w \
 693        -justify left \
 694        -font $mainfont
 695pack .vpane.commarea.buttons.l -side top -fill x
 696button .vpane.commarea.buttons.rescan -text {Rescan} \
 697        -command do_rescan \
 698        -font $mainfont
 699pack .vpane.commarea.buttons.rescan -side top -fill x
 700button .vpane.commarea.buttons.ciall -text {Check-in All} \
 701        -command do_checkin_all \
 702        -font $mainfont
 703pack .vpane.commarea.buttons.ciall -side top -fill x
 704button .vpane.commarea.buttons.commit -text {Commit} \
 705        -command do_commit \
 706        -font $mainfont
 707pack .vpane.commarea.buttons.commit -side top -fill x
 708pack .vpane.commarea.buttons -side left -fill y
 709
 710# -- Commit Message Buffer
 711frame .vpane.commarea.buffer
 712set ui_comm .vpane.commarea.buffer.t
 713label .vpane.commarea.buffer.l -text {Commit Message:} \
 714        -anchor w \
 715        -justify left \
 716        -font $mainfont
 717text $ui_comm -background white -borderwidth 1 \
 718        -relief sunken \
 719        -width 75 -height 10 -wrap none \
 720        -font $difffont \
 721        -yscrollcommand {.vpane.commarea.buffer.sby set} \
 722        -cursor $maincursor
 723scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
 724pack .vpane.commarea.buffer.l -side top -fill x
 725pack .vpane.commarea.buffer.sby -side right -fill y
 726pack $ui_comm -side left -fill y
 727pack .vpane.commarea.buffer -side left -fill y
 728
 729# -- Status Bar
 730set ui_status_value {Initializing...}
 731label .status -textvariable ui_status_value \
 732        -anchor w \
 733        -justify left \
 734        -borderwidth 1 \
 735        -relief sunken \
 736        -font $mainfont
 737pack .status -anchor w -side bottom -fill x
 738
 739# -- Key Bindings
 740bind . <Destroy> do_quit
 741bind . <Key-F5> do_rescan
 742bind . <M1-Key-r> do_rescan
 743bind . <M1-Key-R> do_rescan
 744bind . <M1-Key-q> do_quit
 745bind . <M1-Key-Q> do_quit
 746foreach i [list $ui_index $ui_other] {
 747        bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
 748        bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
 749        bind $i <ButtonRelease-1> {unclick %W %x %y; break}
 750}
 751unset i
 752
 753######################################################################
 754##
 755## main
 756
 757if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
 758        show_msg {} . "Cannot find the git directory: $err"
 759        exit 1
 760}
 761
 762wm title . "git-ui ([file normalize [file dirname $gitdir]])"
 763focus -force $ui_comm
 764update_status