git-gui: Initial revision.
[git] / git-gui
1 #!/bin/sh
2 # Tcl ignores the next line -*- tcl -*- \
3 exec 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
15 set status_active 0
16
17 proc 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
58 proc 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
72 proc 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
86 proc 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
98 proc 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
118 set diff_active 0
119
120 proc 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
130 proc 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
180 proc 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
230 proc 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
240 proc 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
250 proc 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
260 proc 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
281 proc 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
309 proc 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
356 proc 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
392 set filemask {
393 #define mask_width 14
394 #define mask_height 15
395 static 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
401 image create bitmap file_plain -background white -foreground black -data {
402 #define plain_width 14
403 #define plain_height 15
404 static 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
410 image create bitmap file_mod -background white -foreground blue -data {
411 #define mod_width 14
412 #define mod_height 15
413 static 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
419 image create bitmap file_tick -background white -foreground "#007000" -data {
420 #define file_tick_width 14
421 #define file_tick_height 15
422 static 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
428 image create bitmap file_parttick -background white -foreground "#005050" -data {
429 #define parttick_width 14
430 #define parttick_height 15
431 static 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
437 image create bitmap file_question -background white -foreground black -data {
438 #define file_question_width 14
439 #define file_question_height 15
440 static 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
446 image create bitmap file_removed -background white -foreground red -data {
447 #define file_removed_width 14
448 #define file_removed_height 15
449 static 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
455 image create bitmap file_merge -background white -foreground blue -data {
456 #define file_merge_width 14
457 #define file_merge_height 15
458 static 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
464 foreach 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 }
486 unset filemask i
487
488 ######################################################################
489 ##
490 ## util
491
492 proc error_popup {msg} {
493         set w .error
494         toplevel $w
495         wm transient $w .
496         show_msg $w $w $msg
497 }
498
499 proc 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
513 proc 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
523 proc do_quit {} {
524         destroy .
525 }
526
527 proc do_rescan {} {
528         update_status
529 }
530
531 # shift == 1: left click
532 #          3: right click  
533 proc 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
545 proc 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
561 set mainfont {Helvetica 10}
562 set difffont {Courier 10}
563 set maincursor [. cget -cursor]
564
565 # -- Menu Bar
566 menu .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
574 menu .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
583 menu .mbar.commit
584 .mbar.commit add command -label Rescan \
585         -command do_rescan \
586         -font $mainfont
587
588 # -- Fetch Menu
589 menu .mbar.fetch
590
591 # -- Pull Menu
592 menu .mbar.pull
593
594 # -- Main Window Layout
595 panedwindow .vpane -orient vertical
596 panedwindow .vpane.files -orient horizontal
597 .vpane add .vpane.files -sticky nsew
598 pack .vpane -anchor n -side top -fill both -expand 1
599
600 # -- Index File List
601 set ui_index .vpane.files.index.list
602 frame .vpane.files.index -height 100 -width 400
603 label .vpane.files.index.title -text {Modified Files} \
604         -background green \
605         -font $mainfont
606 text $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
612 scrollbar .vpane.files.index.sb -command [list $ui_index yview]
613 pack .vpane.files.index.title -side top -fill x
614 pack .vpane.files.index.sb -side right -fill y
615 pack $ui_index -side left -fill both -expand 1
616 .vpane.files add .vpane.files.index -sticky nsew
617
618 # -- Other (Add) File List
619 set ui_other .vpane.files.other.list
620 frame .vpane.files.other -height 100 -width 100
621 label .vpane.files.other.title -text {Untracked Files} \
622         -background red \
623         -font $mainfont
624 text $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
630 scrollbar .vpane.files.other.sb -command [list $ui_other yview]
631 pack .vpane.files.other.title -side top -fill x
632 pack .vpane.files.other.sb -side right -fill y
633 pack $ui_other -side left -fill both -expand 1
634 .vpane.files add .vpane.files.other -sticky nsew
635
636 # -- Diff Header
637 set ui_fname_value {}
638 set ui_fstatus_value {}
639 frame .vpane.diff -height 100 -width 100
640 frame .vpane.diff.header
641 label .vpane.diff.header.l1 -text {File:} -font $mainfont
642 label .vpane.diff.header.l2 -textvariable ui_fname_value \
643         -anchor w \
644         -justify left \
645         -font $mainfont
646 label .vpane.diff.header.l3 -text {Status:} -font $mainfont
647 label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
648         -width 20 \
649         -anchor w \
650         -justify left \
651         -font $mainfont
652 pack .vpane.diff.header.l1 -side left
653 pack .vpane.diff.header.l2 -side left -fill x
654 pack .vpane.diff.header.l4 -side right
655 pack .vpane.diff.header.l3 -side right
656
657 # -- Diff Body
658 frame .vpane.diff.body
659 set ui_diff .vpane.diff.body.t
660 text $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
667 scrollbar .vpane.diff.body.sbx -orient horizontal \
668         -command [list $ui_diff xview]
669 scrollbar .vpane.diff.body.sby -orient vertical \
670         -command [list $ui_diff yview]
671 pack .vpane.diff.body.sbx -side bottom -fill x
672 pack .vpane.diff.body.sby -side right -fill y
673 pack $ui_diff -side left -fill both -expand 1
674 pack .vpane.diff.header -side top -fill x
675 pack .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
686 frame .vpane.commarea -height 50
687 .vpane add .vpane.commarea -stick nsew
688
689 # -- Commit Area Buttons
690 frame .vpane.commarea.buttons
691 label .vpane.commarea.buttons.l -text {} \
692         -anchor w \
693         -justify left \
694         -font $mainfont
695 pack .vpane.commarea.buttons.l -side top -fill x
696 button .vpane.commarea.buttons.rescan -text {Rescan} \
697         -command do_rescan \
698         -font $mainfont
699 pack .vpane.commarea.buttons.rescan -side top -fill x
700 button .vpane.commarea.buttons.ciall -text {Check-in All} \
701         -command do_checkin_all \
702         -font $mainfont
703 pack .vpane.commarea.buttons.ciall -side top -fill x
704 button .vpane.commarea.buttons.commit -text {Commit} \
705         -command do_commit \
706         -font $mainfont
707 pack .vpane.commarea.buttons.commit -side top -fill x
708 pack .vpane.commarea.buttons -side left -fill y
709
710 # -- Commit Message Buffer
711 frame .vpane.commarea.buffer
712 set ui_comm .vpane.commarea.buffer.t
713 label .vpane.commarea.buffer.l -text {Commit Message:} \
714         -anchor w \
715         -justify left \
716         -font $mainfont
717 text $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
723 scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
724 pack .vpane.commarea.buffer.l -side top -fill x
725 pack .vpane.commarea.buffer.sby -side right -fill y
726 pack $ui_comm -side left -fill y
727 pack .vpane.commarea.buffer -side left -fill y
728
729 # -- Status Bar
730 set ui_status_value {Initializing...}
731 label .status -textvariable ui_status_value \
732         -anchor w \
733         -justify left \
734         -borderwidth 1 \
735         -relief sunken \
736         -font $mainfont
737 pack .status -anchor w -side bottom -fill x
738
739 # -- Key Bindings
740 bind . <Destroy> do_quit
741 bind . <Key-F5> do_rescan
742 bind . <M1-Key-r> do_rescan
743 bind . <M1-Key-R> do_rescan
744 bind . <M1-Key-q> do_quit
745 bind . <M1-Key-Q> do_quit
746 foreach 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 }
751 unset i
752
753 ######################################################################
754 ##
755 ## main
756
757 if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
758         show_msg {} . "Cannot find the git directory: $err"
759         exit 1
760 }
761
762 wm title . "git-ui ([file normalize [file dirname $gitdir]])"
763 focus -force $ui_comm
764 update_status