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