Code

git-gui: Additional early feature development.
[git.git] / git-gui
1 #!/bin/sh
2 # Tcl ignores the next line -*- tcl -*- \
3 exec wish "$0" -- "$@"
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.
11 ######################################################################
12 ##
13 ## status
15 set status_active 0
16 set diff_active 0
17 set checkin_active 0
18 set update_index_fd {}
20 proc is_busy {} {
21         global status_active diff_active checkin_active update_index_fd
23         if {$status_active > 0
24                 || $diff_active
25                 || $checkin_active
26                 || $update_index_fd != {}} {
27                 return 1
28         }
29         return 0
30 }
32 proc update_status {} {
33         global gitdir HEAD commit_type
34         global ui_index ui_other ui_status_value ui_comm
35         global status_active file_states
37         if {[is_busy]} return
39         array unset file_states
40         foreach w [list $ui_index $ui_other] {
41                 $w conf -state normal
42                 $w delete 0.0 end
43                 $w conf -state disabled
44         }
46         if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} {
47                 set commit_type initial
48         } else {
49                 set commit_type normal
50         }
52         if {![$ui_comm edit modified]
53             || [string trim [$ui_comm get 0.0 end]] == {}} {
54                 if {[load_message GITGUI_MSG]} {
55                 } elseif {[load_message MERGE_MSG]} {
56                 } elseif {[load_message SQUASH_MSG]} {
57                 }
58                 $ui_comm edit modified false
59         }
61         set status_active 1
62         set ui_status_value {Refreshing file status...}
63         set fd_rf [open "| git update-index -q --unmerged --refresh" r]
64         fconfigure $fd_rf -blocking 0 -translation binary
65         fileevent $fd_rf readable [list read_refresh $fd_rf]
66 }
68 proc read_refresh {fd} {
69         global gitdir HEAD commit_type
70         global ui_index ui_other ui_status_value ui_comm
71         global status_active file_states
73         read $fd
74         if {![eof $fd]} return
75         close $fd
77         set ls_others [list | git ls-files --others -z \
78                 --exclude-per-directory=.gitignore]
79         set info_exclude [file join $gitdir info exclude]
80         if {[file readable $info_exclude]} {
81                 lappend ls_others "--exclude-from=$info_exclude"
82         }
84         set status_active 3
85         set ui_status_value {Scanning for modified files ...}
86         set fd_di [open "| git diff-index --cached -z $HEAD" r]
87         set fd_df [open "| git diff-files -z" r]
88         set fd_lo [open $ls_others r]
90         fconfigure $fd_di -blocking 0 -translation binary
91         fconfigure $fd_df -blocking 0 -translation binary
92         fconfigure $fd_lo -blocking 0 -translation binary
93         fileevent $fd_di readable [list read_diff_index $fd_di]
94         fileevent $fd_df readable [list read_diff_files $fd_df]
95         fileevent $fd_lo readable [list read_ls_others $fd_lo]
96 }
98 proc load_message {file} {
99         global gitdir ui_comm
101         set f [file join $gitdir $file]
102         if {[file exists $f]} {
103                 if {[catch {set fd [open $f r]}]} {
104                         return 0
105                 }
106                 set content [read $fd]
107                 close $fd
108                 $ui_comm delete 0.0 end
109                 $ui_comm insert end $content
110                 return 1
111         }
112         return 0
115 proc read_diff_index {fd} {
116         global buf_rdi
118         append buf_rdi [read $fd]
119         set pck [split $buf_rdi "\0"]
120         set buf_rdi [lindex $pck end]
121         foreach {m p} [lrange $pck 0 end-1] {
122                 if {$m != {} && $p != {}} {
123                         display_file $p [string index $m end]_
124                 }
125         }
126         status_eof $fd buf_rdi
129 proc read_diff_files {fd} {
130         global buf_rdf
132         append buf_rdf [read $fd]
133         set pck [split $buf_rdf "\0"]
134         set buf_rdf [lindex $pck end]
135         foreach {m p} [lrange $pck 0 end-1] {
136                 if {$m != {} && $p != {}} {
137                         display_file $p _[string index $m end]
138                 }
139         }
140         status_eof $fd buf_rdf
143 proc read_ls_others {fd} {
144         global buf_rlo
146         append buf_rlo [read $fd]
147         set pck [split $buf_rlo "\0"]
148         set buf_rlo [lindex $pck end]
149         foreach p [lrange $pck 0 end-1] {
150                 display_file $p _O
151         }
152         status_eof $fd buf_rlo
155 proc status_eof {fd buf} {
156         global status_active $buf
157         global ui_fname_value ui_status_value
159         if {[eof $fd]} {
160                 set $buf {}
161                 close $fd
162                 if {[incr status_active -1] == 0} {
163                         set ui_status_value {Ready.}
164                         if {$ui_fname_value != {}} {
165                                 show_diff $ui_fname_value
166                         }
167                 }
168         }
171 ######################################################################
172 ##
173 ## diff
175 proc clear_diff {} {
176         global ui_diff ui_fname_value ui_fstatus_value
178         $ui_diff conf -state normal
179         $ui_diff delete 0.0 end
180         $ui_diff conf -state disabled
181         set ui_fname_value {}
182         set ui_fstatus_value {}
185 proc show_diff {path} {
186         global file_states HEAD diff_3way diff_active
187         global ui_diff ui_fname_value ui_fstatus_value ui_status_value
189         if {[is_busy]} return
191         clear_diff
192         set s $file_states($path)
193         set m [lindex $s 0]
194         set diff_3way 0
195         set diff_active 1
196         set ui_fname_value $path
197         set ui_fstatus_value [mapdesc $m $path]
198         set ui_status_value "Loading diff of $path..."
200         set cmd [list | git diff-index -p $HEAD -- $path]
201         switch $m {
202         AM {
203         }
204         MM {
205                 set cmd [list | git diff-index -p -c $HEAD $path]
206         }
207         _O {
208                 if {[catch {
209                                 set fd [open $path r]
210                                 set content [read $fd]
211                                 close $fd
212                         } err ]} {
213                         set diff_active 0
214                         set ui_status_value "Unable to display $path"
215                         error_popup "Error loading file:\n$err"
216                         return
217                 }
218                 $ui_diff conf -state normal
219                 $ui_diff insert end $content
220                 $ui_diff conf -state disabled
221                 return
222         }
223         }
225         if {[catch {set fd [open $cmd r]} err]} {
226                 set diff_active 0
227                 set ui_status_value "Unable to display $path"
228                 error_popup "Error loading diff:\n$err"
229                 return
230         }
232         fconfigure $fd -blocking 0 -translation binary
233         fileevent $fd readable [list read_diff $fd]
236 proc read_diff {fd} {
237         global ui_diff ui_status_value diff_3way diff_active
239         while {[gets $fd line] >= 0} {
240                 if {[string match index* $line]} {
241                         if {[string first , $line] >= 0} {
242                                 set diff_3way 1
243                         }
244                 }
246                 $ui_diff conf -state normal
247                 if {!$diff_3way} {
248                         set x [string index $line 0]
249                         switch -- $x {
250                         "@" {set tags da}
251                         "+" {set tags dp}
252                         "-" {set tags dm}
253                         default {set tags {}}
254                         }
255                 } else {
256                         set x [string range $line 0 1]
257                         switch -- $x {
258                         default {set tags {}}
259                         "@@" {set tags da}
260                         "++" {set tags dp; set x " +"}
261                         " +" {set tags {di bold}; set x "++"}
262                         "+ " {set tags dni; set x "-+"}
263                         "--" {set tags dm; set x " -"}
264                         " -" {set tags {dm bold}; set x "--"}
265                         "- " {set tags di; set x "+-"}
266                         default {set tags {}}
267                         }
268                         set line [string replace $line 0 1 $x]
269                 }
270                 $ui_diff insert end $line $tags
271                 $ui_diff insert end "\n"
272                 $ui_diff conf -state disabled
273         }
275         if {[eof $fd]} {
276                 close $fd
277                 set diff_active 0
278                 set ui_status_value {Ready.}
279         }
282 ######################################################################
283 ##
284 ## ui helpers
286 proc mapcol {state path} {
287         global all_cols
289         if {[catch {set r $all_cols($state)}]} {
290                 puts "error: no column for state={$state} $path"
291                 return o
292         }
293         return $r
296 proc mapicon {state path} {
297         global all_icons
299         if {[catch {set r $all_icons($state)}]} {
300                 puts "error: no icon for state={$state} $path"
301                 return file_plain
302         }
303         return $r
306 proc mapdesc {state path} {
307         global all_descs
309         if {[catch {set r $all_descs($state)}]} {
310                 puts "error: no desc for state={$state} $path"
311                 return $state
312         }
313         return $r
316 proc bsearch {w path} {
317         set hi [expr [lindex [split [$w index end] .] 0] - 2]
318         if {$hi == 0} {
319                 return -1
320         }
321         set lo 0
322         while {$lo < $hi} {
323                 set mi [expr [expr $lo + $hi] / 2]
324                 set ti [expr $mi + 1]
325                 set cmp [string compare [$w get $ti.1 $ti.end] $path]
326                 if {$cmp < 0} {
327                         set lo $ti
328                 } elseif {$cmp == 0} {
329                         return $mi
330                 } else {
331                         set hi $mi
332                 }
333         }
334         return -[expr $lo + 1]
337 proc merge_state {path state} {
338         global file_states
340         if {[array names file_states -exact $path] == {}}  {
341                 set o __
342                 set s [list $o none none]
343         } else {
344                 set s $file_states($path)
345                 set o [lindex $s 0]
346         }
348         set m [lindex $s 0]
349         if {[string index $state 0] == "_"} {
350                 set state [string index $m 0][string index $state 1]
351         } elseif {[string index $state 0] == "*"} {
352                 set state _[string index $state 1]
353         }
355         if {[string index $state 1] == "_"} {
356                 set state [string index $state 0][string index $m 1]
357         } elseif {[string index $state 1] == "*"} {
358                 set state [string index $state 0]_
359         }
361         set file_states($path) [lreplace $s 0 0 $state]
362         return $o
365 proc display_file {path state} {
366         global ui_index ui_other file_states
368         set old_m [merge_state $path $state]
369         set s $file_states($path)
370         set m [lindex $s 0]
372         if {[mapcol $m $path] == "o"} {
373                 set ii 1
374                 set ai 2
375                 set iw $ui_index
376                 set aw $ui_other
377         } else {
378                 set ii 2
379                 set ai 1
380                 set iw $ui_other
381                 set aw $ui_index
382         }
384         set d [lindex $s $ii]
385         if {$d != "none"} {
386                 set lno [bsearch $iw $path]
387                 if {$lno >= 0} {
388                         incr lno
389                         $iw conf -state normal
390                         $iw delete $lno.0 [expr $lno + 1].0
391                         $iw conf -state disabled
392                         set s [lreplace $s $ii $ii none]
393                 }
394         }
396         set d [lindex $s $ai]
397         if {$d == "none"} {
398                 set lno [expr abs([bsearch $aw $path] + 1) + 1]
399                 $aw conf -state normal
400                 set ico [$aw image create $lno.0 \
401                         -align center -padx 5 -pady 1 \
402                         -image [mapicon $m $path]]
403                 $aw insert $lno.1 "$path\n"
404                 $aw conf -state disabled
405                 set file_states($path) [lreplace $s $ai $ai [list $ico]]
406         } elseif {[mapicon $m $path] != [mapicon $old_m $path]} {
407                 set ico [lindex $d 0]
408                 $aw image conf $ico -image [mapicon $m $path]
409         }
412 proc with_update_index {body} {
413         global update_index_fd
415         if {$update_index_fd == {}} {
416                 set update_index_fd [open \
417                         "| git update-index --add --remove -z --stdin" \
418                         w]
419                 fconfigure $update_index_fd -translation binary
420                 uplevel 1 $body
421                 close $update_index_fd
422                 set update_index_fd {}
423         } else {
424                 uplevel 1 $body
425         }
428 proc update_index {path} {
429         global update_index_fd
431         if {$update_index_fd == {}} {
432                 error {not in with_update_index}
433         } else {
434                 puts -nonewline $update_index_fd "$path\0"
435         }
438 proc toggle_mode {path} {
439         global file_states
441         set s $file_states($path)
442         set m [lindex $s 0]
444         switch -- $m {
445         AM -
446         _O {set new A*}
447         _M -
448         MM {set new M*}
449         _D {set new D*}
450         default {return}
451         }
453         with_update_index {update_index $path}
454         display_file $path $new
457 ######################################################################
458 ##
459 ## icons
461 set filemask {
462 #define mask_width 14
463 #define mask_height 15
464 static unsigned char mask_bits[] = {
465    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
466    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
467    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
470 image create bitmap file_plain -background white -foreground black -data {
471 #define plain_width 14
472 #define plain_height 15
473 static unsigned char plain_bits[] = {
474    0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
475    0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
476    0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
477 } -maskdata $filemask
479 image create bitmap file_mod -background white -foreground blue -data {
480 #define mod_width 14
481 #define mod_height 15
482 static unsigned char mod_bits[] = {
483    0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
484    0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
485    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
486 } -maskdata $filemask
488 image create bitmap file_fulltick -background white -foreground "#007000" -data {
489 #define file_fulltick_width 14
490 #define file_fulltick_height 15
491 static unsigned char file_fulltick_bits[] = {
492    0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
493    0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
494    0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
495 } -maskdata $filemask
497 image create bitmap file_parttick -background white -foreground "#005050" -data {
498 #define parttick_width 14
499 #define parttick_height 15
500 static unsigned char parttick_bits[] = {
501    0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
502    0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
503    0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
504 } -maskdata $filemask
506 image create bitmap file_question -background white -foreground black -data {
507 #define file_question_width 14
508 #define file_question_height 15
509 static unsigned char file_question_bits[] = {
510    0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
511    0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
512    0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
513 } -maskdata $filemask
515 image create bitmap file_removed -background white -foreground red -data {
516 #define file_removed_width 14
517 #define file_removed_height 15
518 static unsigned char file_removed_bits[] = {
519    0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
520    0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
521    0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
522 } -maskdata $filemask
524 image create bitmap file_merge -background white -foreground blue -data {
525 #define file_merge_width 14
526 #define file_merge_height 15
527 static unsigned char file_merge_bits[] = {
528    0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
529    0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
530    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
531 } -maskdata $filemask
533 set max_status_desc 0
534 foreach i {
535                 {__ i plain    "Unmodified"}
536                 {_M i mod      "Modified"}
537                 {M_ i fulltick "Checked in"}
538                 {MM i parttick "Partially checked in"}
540                 {_O o plain    "Untracked"}
541                 {A_ o fulltick "Added"}
542                 {AM o parttick "Partially added"}
544                 {_D i question "Missing"}
545                 {D_ i removed  "Removed"}
546                 {DD i removed  "Removed"}
547                 {DO i removed  "Removed (still exists)"}
549                 {UM i merge    "Merge conflicts"}
550                 {U_ i merge    "Merge conflicts"}
551         } {
552         if {$max_status_desc < [string length [lindex $i 3]]} {
553                 set max_status_desc [string length [lindex $i 3]]
554         }
555         set all_cols([lindex $i 0]) [lindex $i 1]
556         set all_icons([lindex $i 0]) file_[lindex $i 2]
557         set all_descs([lindex $i 0]) [lindex $i 3]
559 unset filemask i
561 ######################################################################
562 ##
563 ## util
565 proc error_popup {msg} {
566         set w .error
567         toplevel $w
568         wm transient $w .
569         show_msg $w $w $msg
572 proc show_msg {w top msg} {
573         message $w.m -text $msg -justify center -aspect 400
574         pack $w.m -side top -fill x -padx 20 -pady 20
575         button $w.ok -text OK -command "destroy $top"
576         pack $w.ok -side bottom -fill x
577         bind $top <Visibility> "grab $top; focus $top"
578         bind $top <Key-Return> "destroy $top"
579         tkwait window $top
582 ######################################################################
583 ##
584 ## ui commands
586 proc do_gitk {} {
587         global tcl_platform
589     if {$tcl_platform(platform) == "windows"} {
590                 exec sh -c gitk &
591         } else {
592                 exec gitk &
593         }
596 proc do_quit {} {
597         global gitdir ui_comm
599         set save [file join $gitdir GITGUI_MSG]
600         if {[$ui_comm edit modified]
601             && [string trim [$ui_comm get 0.0 end]] != {}} {
602                 catch {
603                         set fd [open $save w]
604                         puts $fd [string trim [$ui_comm get 0.0 end]]
605                         close $fd
606                 }
607         } elseif {[file exists $save]} {
608                 file delete $save
609         }
611         destroy .
614 proc do_rescan {} {
615         update_status
618 proc do_checkin_all {} {
619         global checkin_active ui_status_value
621         if {[is_busy]} return
623         set checkin_active 1
624         set ui_status_value {Checking in all files...}
625         after 1 {
626                 with_update_index {
627                         foreach path [array names file_states] {
628                                 set s $file_states($path)
629                                 set m [lindex $s 0]
630                                 switch -- $m {
631                                 AM -
632                                 MM -
633                                 _M -
634                                 _D {toggle_mode $path}
635                                 }
636                         }
637                 }
638                 set checkin_active 0
639                 set ui_status_value {Ready.}
640         }
643 proc do_signoff {} {
644         global ui_comm
646         catch {
647                 set me [exec git var GIT_COMMITTER_IDENT]
648                 if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} {
649                         set str "Signed-off-by: $name"
650                         if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
651                                 $ui_comm insert end "\n"
652                                 $ui_comm insert end $str
653                                 $ui_comm see end
654                         }
655                 }
656         }
659 # shift == 1: left click
660 #          3: right click  
661 proc click {w x y shift wx wy} {
662         global ui_index ui_other
664         set pos [split [$w index @$x,$y] .]
665         set lno [lindex $pos 0]
666         set col [lindex $pos 1]
667         set path [$w get $lno.1 $lno.end]
668         if {$path == {}} return
670         if {$col > 0 && $shift == 1} {
671                 $ui_index tag remove in_diff 0.0 end
672                 $ui_other tag remove in_diff 0.0 end
673                 $w tag add in_diff $lno.0 [expr $lno + 1].0
674                 show_diff $path
675         }
678 proc unclick {w x y} {
679         set pos [split [$w index @$x,$y] .]
680         set lno [lindex $pos 0]
681         set col [lindex $pos 1]
682         set path [$w get $lno.1 $lno.end]
683         if {$path == {}} return
685         if {$col == 0 && ![is_busy]} {
686                 toggle_mode $path
687         }
690 ######################################################################
691 ##
692 ## ui init
694 set mainfont {Helvetica 10}
695 set difffont {Courier 10}
696 set maincursor [. cget -cursor]
698 # -- Menu Bar
699 menu .mbar -tearoff 0
700 .mbar add cascade -label Project -menu .mbar.project
701 .mbar add cascade -label Commit -menu .mbar.commit
702 .mbar add cascade -label Fetch -menu .mbar.fetch
703 .mbar add cascade -label Pull -menu .mbar.pull
704 . configure -menu .mbar
706 # -- Project Menu
707 menu .mbar.project
708 .mbar.project add command -label Visulize \
709         -command do_gitk \
710         -font $mainfont
711 .mbar.project add command -label Quit \
712         -command do_quit \
713         -font $mainfont
715 # -- Commit Menu
716 menu .mbar.commit
717 .mbar.commit add command -label Rescan \
718         -command do_rescan \
719         -font $mainfont
720 .mbar.commit add command -label {Check-in All Files} \
721         -command do_checkin_all \
722         -font $mainfont
723 .mbar.commit add command -label {Sign Off} \
724         -command do_signoff \
725         -font $mainfont
726 .mbar.commit add command -label Commit \
727         -command do_commit \
728         -font $mainfont
730 # -- Fetch Menu
731 menu .mbar.fetch
733 # -- Pull Menu
734 menu .mbar.pull
736 # -- Main Window Layout
737 panedwindow .vpane -orient vertical
738 panedwindow .vpane.files -orient horizontal
739 .vpane add .vpane.files -sticky nsew
740 pack .vpane -anchor n -side top -fill both -expand 1
742 # -- Index File List
743 set ui_index .vpane.files.index.list
744 frame .vpane.files.index -height 100 -width 400
745 label .vpane.files.index.title -text {Modified Files} \
746         -background green \
747         -font $mainfont
748 text $ui_index -background white -borderwidth 0 \
749         -width 40 -height 10 \
750         -font $mainfont \
751         -yscrollcommand {.vpane.files.index.sb set} \
752         -cursor $maincursor \
753         -state disabled
754 scrollbar .vpane.files.index.sb -command [list $ui_index yview]
755 pack .vpane.files.index.title -side top -fill x
756 pack .vpane.files.index.sb -side right -fill y
757 pack $ui_index -side left -fill both -expand 1
758 .vpane.files add .vpane.files.index -sticky nsew
760 # -- Other (Add) File List
761 set ui_other .vpane.files.other.list
762 frame .vpane.files.other -height 100 -width 100
763 label .vpane.files.other.title -text {Untracked Files} \
764         -background red \
765         -font $mainfont
766 text $ui_other -background white -borderwidth 0 \
767         -width 40 -height 10 \
768         -font $mainfont \
769         -yscrollcommand {.vpane.files.other.sb set} \
770         -cursor $maincursor \
771         -state disabled
772 scrollbar .vpane.files.other.sb -command [list $ui_other yview]
773 pack .vpane.files.other.title -side top -fill x
774 pack .vpane.files.other.sb -side right -fill y
775 pack $ui_other -side left -fill both -expand 1
776 .vpane.files add .vpane.files.other -sticky nsew
778 $ui_index tag conf in_diff -font [concat $mainfont bold]
779 $ui_other tag conf in_diff -font [concat $mainfont bold]
781 # -- Diff Header
782 set ui_fname_value {}
783 set ui_fstatus_value {}
784 frame .vpane.diff -height 50 -width 400
785 frame .vpane.diff.header
786 label .vpane.diff.header.l1 -text {File:} -font $mainfont
787 label .vpane.diff.header.l2 -textvariable ui_fname_value \
788         -anchor w \
789         -justify left \
790         -font $mainfont
791 label .vpane.diff.header.l3 -text {Status:} -font $mainfont
792 label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
793         -width $max_status_desc \
794         -anchor w \
795         -justify left \
796         -font $mainfont
797 pack .vpane.diff.header.l1 -side left
798 pack .vpane.diff.header.l2 -side left -fill x
799 pack .vpane.diff.header.l4 -side right
800 pack .vpane.diff.header.l3 -side right
802 # -- Diff Body
803 frame .vpane.diff.body
804 set ui_diff .vpane.diff.body.t
805 text $ui_diff -background white -borderwidth 0 \
806         -width 80 -height 15 \
807         -font $difffont \
808         -xscrollcommand {.vpane.diff.body.sbx set} \
809         -yscrollcommand {.vpane.diff.body.sby set} \
810         -cursor $maincursor \
811         -state disabled
812 scrollbar .vpane.diff.body.sbx -orient horizontal \
813         -command [list $ui_diff xview]
814 scrollbar .vpane.diff.body.sby -orient vertical \
815         -command [list $ui_diff yview]
816 pack .vpane.diff.body.sbx -side bottom -fill x
817 pack .vpane.diff.body.sby -side right -fill y
818 pack $ui_diff -side left -fill both -expand 1
819 pack .vpane.diff.header -side top -fill x
820 pack .vpane.diff.body -side bottom -fill both -expand 1
821 .vpane add .vpane.diff -stick nsew
823 $ui_diff tag conf dm -foreground red
824 $ui_diff tag conf dp -foreground blue
825 $ui_diff tag conf da -font [concat $difffont bold]
826 $ui_diff tag conf di -foreground "#00a000"
827 $ui_diff tag conf dni -foreground "#a000a0"
828 $ui_diff tag conf bold -font [concat $difffont bold]
830 # -- Commit Area
831 frame .vpane.commarea -height 50
832 .vpane add .vpane.commarea -stick nsew
834 # -- Commit Area Buttons
835 frame .vpane.commarea.buttons
836 label .vpane.commarea.buttons.l -text {} \
837         -anchor w \
838         -justify left \
839         -font $mainfont
840 pack .vpane.commarea.buttons.l -side top -fill x
841 pack .vpane.commarea.buttons -side left -fill y
843 button .vpane.commarea.buttons.rescan -text {Rescan} \
844         -command do_rescan \
845         -font $mainfont
846 pack .vpane.commarea.buttons.rescan -side top -fill x
848 button .vpane.commarea.buttons.ciall -text {Check-in All} \
849         -command do_checkin_all \
850         -font $mainfont
851 pack .vpane.commarea.buttons.ciall -side top -fill x
853 button .vpane.commarea.buttons.signoff -text {Sign Off} \
854         -command do_signoff \
855         -font $mainfont
856 pack .vpane.commarea.buttons.signoff -side top -fill x
858 button .vpane.commarea.buttons.commit -text {Commit} \
859         -command do_commit \
860         -font $mainfont
861 pack .vpane.commarea.buttons.commit -side top -fill x
863 # -- Commit Message Buffer
864 frame .vpane.commarea.buffer
865 set ui_comm .vpane.commarea.buffer.t
866 label .vpane.commarea.buffer.l -text {Commit Message:} \
867         -anchor w \
868         -justify left \
869         -font $mainfont
870 text $ui_comm -background white -borderwidth 1 \
871         -relief sunken \
872         -width 75 -height 10 -wrap none \
873         -font $difffont \
874         -yscrollcommand {.vpane.commarea.buffer.sby set} \
875         -cursor $maincursor
876 scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
877 pack .vpane.commarea.buffer.l -side top -fill x
878 pack .vpane.commarea.buffer.sby -side right -fill y
879 pack $ui_comm -side left -fill y
880 pack .vpane.commarea.buffer -side left -fill y
882 # -- Status Bar
883 set ui_status_value {Initializing...}
884 label .status -textvariable ui_status_value \
885         -anchor w \
886         -justify left \
887         -borderwidth 1 \
888         -relief sunken \
889         -font $mainfont
890 pack .status -anchor w -side bottom -fill x
892 # -- Key Bindings
893 bind . <Destroy> do_quit
894 bind . <Key-F5> do_rescan
895 bind . <M1-Key-r> do_rescan
896 bind . <M1-Key-R> do_rescan
897 bind . <M1-Key-s> do_signoff
898 bind . <M1-Key-S> do_signoff
899 bind . <M1-Key-u> do_checkin_all
900 bind . <M1-Key-U> do_checkin_all
901 bind . <M1-Key-Return> do_commit
902 bind . <M1-Key-q> do_quit
903 bind . <M1-Key-Q> do_quit
904 foreach i [list $ui_index $ui_other] {
905         bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
906         bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
907         bind $i <ButtonRelease-1> {unclick %W %x %y; break}
909 unset i
911 ######################################################################
912 ##
913 ## main
915 if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
916         show_msg {} . "Cannot find the git directory: $err"
917         exit 1
920 wm title . "git-ui ([file normalize [file dirname $gitdir]])"
921 focus -force $ui_comm
922 update_status