]> git.ipfire.org Git - thirdparty/git.git/blame - git-gui
git-gui: Fix menu item accelerator display on Mac OS X.
[thirdparty/git.git] / git-gui
CommitLineData
cb07fc2a
SP
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
cb07fc2a
SP
10######################################################################
11##
e210e674 12## task management
cb07fc2a 13
ec6b424a 14set single_commit 0
cb07fc2a 15set status_active 0
131f503b
SP
16set diff_active 0
17set checkin_active 0
ec6b424a 18set commit_active 0
131f503b
SP
19set update_index_fd {}
20
e210e674
SP
21set disable_on_lock [list]
22set index_lock_type none
23
e57ca85e
SP
24set HEAD {}
25set PARENT {}
26set commit_type {}
27
e210e674
SP
28proc lock_index {type} {
29 global index_lock_type disable_on_lock
131f503b 30
e210e674
SP
31 if {$index_lock_type == {none}} {
32 set index_lock_type $type
33 foreach w $disable_on_lock {
34 uplevel #0 $w disabled
35 }
36 return 1
37 } elseif {$index_lock_type == {begin-update} && $type == {update}} {
38 set index_lock_type $type
131f503b
SP
39 return 1
40 }
41 return 0
42}
cb07fc2a 43
e210e674
SP
44proc unlock_index {} {
45 global index_lock_type disable_on_lock
46
47 set index_lock_type none
48 foreach w $disable_on_lock {
49 uplevel #0 $w normal
50 }
51}
52
53######################################################################
54##
55## status
56
ec6b424a
SP
57proc repository_state {hdvar ctvar} {
58 global gitdir
59 upvar $hdvar hd $ctvar ct
60
61 if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
62 set ct initial
63 } elseif {[file exists [file join $gitdir MERGE_HEAD]]} {
64 set ct merge
65 } else {
66 set ct normal
67 }
68}
69
e57ca85e
SP
70proc update_status {{final Ready.}} {
71 global HEAD PARENT commit_type
131f503b 72 global ui_index ui_other ui_status_value ui_comm
cb07fc2a
SP
73 global status_active file_states
74
e210e674 75 if {$status_active || ![lock_index read]} return
cb07fc2a 76
e57ca85e
SP
77 repository_state new_HEAD new_type
78 if {$commit_type == {amend}
79 && $new_type == {normal}
80 && $new_HEAD == $HEAD} {
81 } else {
82 set HEAD $new_HEAD
83 set PARENT $new_HEAD
84 set commit_type $new_type
85 }
86
cb07fc2a 87 array unset file_states
cb07fc2a
SP
88 foreach w [list $ui_index $ui_other] {
89 $w conf -state normal
90 $w delete 0.0 end
91 $w conf -state disabled
92 }
93
131f503b 94 if {![$ui_comm edit modified]
cc4b1c02 95 || [string trim [$ui_comm get 0.0 end]] == {}} {
131f503b
SP
96 if {[load_message GITGUI_MSG]} {
97 } elseif {[load_message MERGE_MSG]} {
98 } elseif {[load_message SQUASH_MSG]} {
99 }
100 $ui_comm edit modified false
101 }
102
103 set status_active 1
104 set ui_status_value {Refreshing file status...}
105 set fd_rf [open "| git update-index -q --unmerged --refresh" r]
106 fconfigure $fd_rf -blocking 0 -translation binary
e57ca85e 107 fileevent $fd_rf readable [list read_refresh $fd_rf $final]
131f503b
SP
108}
109
e57ca85e
SP
110proc read_refresh {fd final} {
111 global gitdir PARENT commit_type
131f503b
SP
112 global ui_index ui_other ui_status_value ui_comm
113 global status_active file_states
114
115 read $fd
116 if {![eof $fd]} return
117 close $fd
118
cb07fc2a
SP
119 set ls_others [list | git ls-files --others -z \
120 --exclude-per-directory=.gitignore]
121 set info_exclude [file join $gitdir info exclude]
122 if {[file readable $info_exclude]} {
123 lappend ls_others "--exclude-from=$info_exclude"
124 }
125
131f503b
SP
126 set status_active 3
127 set ui_status_value {Scanning for modified files ...}
e57ca85e 128 set fd_di [open "| git diff-index --cached -z $PARENT" r]
cb07fc2a
SP
129 set fd_df [open "| git diff-files -z" r]
130 set fd_lo [open $ls_others r]
cb07fc2a
SP
131
132 fconfigure $fd_di -blocking 0 -translation binary
133 fconfigure $fd_df -blocking 0 -translation binary
134 fconfigure $fd_lo -blocking 0 -translation binary
e57ca85e
SP
135 fileevent $fd_di readable [list read_diff_index $fd_di $final]
136 fileevent $fd_df readable [list read_diff_files $fd_df $final]
137 fileevent $fd_lo readable [list read_ls_others $fd_lo $final]
cb07fc2a
SP
138}
139
131f503b
SP
140proc load_message {file} {
141 global gitdir ui_comm
142
143 set f [file join $gitdir $file]
e57ca85e 144 if {[file isfile $f]} {
131f503b
SP
145 if {[catch {set fd [open $f r]}]} {
146 return 0
147 }
e57ca85e 148 set content [string trim [read $fd]]
131f503b
SP
149 close $fd
150 $ui_comm delete 0.0 end
151 $ui_comm insert end $content
152 return 1
153 }
154 return 0
155}
156
e57ca85e 157proc read_diff_index {fd final} {
cb07fc2a
SP
158 global buf_rdi
159
160 append buf_rdi [read $fd]
161 set pck [split $buf_rdi "\0"]
162 set buf_rdi [lindex $pck end]
163 foreach {m p} [lrange $pck 0 end-1] {
164 if {$m != {} && $p != {}} {
165 display_file $p [string index $m end]_
166 }
167 }
e57ca85e 168 status_eof $fd buf_rdi $final
cb07fc2a
SP
169}
170
e57ca85e 171proc read_diff_files {fd final} {
cb07fc2a
SP
172 global buf_rdf
173
174 append buf_rdf [read $fd]
175 set pck [split $buf_rdf "\0"]
176 set buf_rdf [lindex $pck end]
177 foreach {m p} [lrange $pck 0 end-1] {
178 if {$m != {} && $p != {}} {
179 display_file $p _[string index $m end]
180 }
181 }
e57ca85e 182 status_eof $fd buf_rdf $final
cb07fc2a
SP
183}
184
e57ca85e 185proc read_ls_others {fd final} {
cb07fc2a
SP
186 global buf_rlo
187
188 append buf_rlo [read $fd]
189 set pck [split $buf_rlo "\0"]
190 set buf_rlo [lindex $pck end]
191 foreach p [lrange $pck 0 end-1] {
192 display_file $p _O
193 }
e57ca85e 194 status_eof $fd buf_rlo $final
cb07fc2a
SP
195}
196
e57ca85e 197proc status_eof {fd buf final} {
cb07fc2a 198 global status_active $buf
e57ca85e 199 global ui_fname_value ui_status_value file_states
cb07fc2a
SP
200
201 if {[eof $fd]} {
202 set $buf {}
203 close $fd
204 if {[incr status_active -1] == 0} {
e210e674 205 unlock_index
e57ca85e
SP
206
207 set ui_status_value $final
208 if {$ui_fname_value != {} && [array names file_states \
209 -exact $ui_fname_value] != {}} {
cb07fc2a 210 show_diff $ui_fname_value
e57ca85e
SP
211 } else {
212 clear_diff
cb07fc2a
SP
213 }
214 }
215 }
216}
217
218######################################################################
219##
220## diff
221
cb07fc2a
SP
222proc clear_diff {} {
223 global ui_diff ui_fname_value ui_fstatus_value
224
225 $ui_diff conf -state normal
226 $ui_diff delete 0.0 end
227 $ui_diff conf -state disabled
228 set ui_fname_value {}
229 set ui_fstatus_value {}
230}
231
232proc show_diff {path} {
e57ca85e 233 global file_states PARENT diff_3way diff_active
cb07fc2a
SP
234 global ui_diff ui_fname_value ui_fstatus_value ui_status_value
235
e210e674 236 if {$diff_active || ![lock_index read]} return
cb07fc2a
SP
237
238 clear_diff
239 set s $file_states($path)
240 set m [lindex $s 0]
241 set diff_3way 0
242 set diff_active 1
243 set ui_fname_value $path
244 set ui_fstatus_value [mapdesc $m $path]
245 set ui_status_value "Loading diff of $path..."
246
e57ca85e 247 set cmd [list | git diff-index -p $PARENT -- $path]
cb07fc2a
SP
248 switch $m {
249 AM {
250 }
251 MM {
e57ca85e 252 set cmd [list | git diff-index -p -c $PARENT $path]
cb07fc2a
SP
253 }
254 _O {
255 if {[catch {
256 set fd [open $path r]
257 set content [read $fd]
258 close $fd
259 } err ]} {
131f503b 260 set diff_active 0
e210e674 261 unlock_index
cb07fc2a
SP
262 set ui_status_value "Unable to display $path"
263 error_popup "Error loading file:\n$err"
264 return
265 }
266 $ui_diff conf -state normal
267 $ui_diff insert end $content
268 $ui_diff conf -state disabled
bd1e2b40
SP
269 set diff_active 0
270 unlock_index
271 set ui_status_value {Ready.}
cb07fc2a
SP
272 return
273 }
274 }
275
276 if {[catch {set fd [open $cmd r]} err]} {
131f503b 277 set diff_active 0
e210e674 278 unlock_index
cb07fc2a
SP
279 set ui_status_value "Unable to display $path"
280 error_popup "Error loading diff:\n$err"
281 return
282 }
283
6f6eed28 284 fconfigure $fd -blocking 0 -translation auto
cb07fc2a
SP
285 fileevent $fd readable [list read_diff $fd]
286}
287
288proc read_diff {fd} {
289 global ui_diff ui_status_value diff_3way diff_active
290
291 while {[gets $fd line] >= 0} {
6f6eed28
SP
292 if {[string match {diff --git *} $line]} continue
293 if {[string match {diff --combined *} $line]} continue
294 if {[string match {--- *} $line]} continue
295 if {[string match {+++ *} $line]} continue
cb07fc2a
SP
296 if {[string match index* $line]} {
297 if {[string first , $line] >= 0} {
298 set diff_3way 1
299 }
300 }
301
302 $ui_diff conf -state normal
303 if {!$diff_3way} {
304 set x [string index $line 0]
305 switch -- $x {
306 "@" {set tags da}
307 "+" {set tags dp}
308 "-" {set tags dm}
309 default {set tags {}}
310 }
311 } else {
312 set x [string range $line 0 1]
313 switch -- $x {
314 default {set tags {}}
315 "@@" {set tags da}
316 "++" {set tags dp; set x " +"}
317 " +" {set tags {di bold}; set x "++"}
318 "+ " {set tags dni; set x "-+"}
319 "--" {set tags dm; set x " -"}
320 " -" {set tags {dm bold}; set x "--"}
321 "- " {set tags di; set x "+-"}
322 default {set tags {}}
323 }
324 set line [string replace $line 0 1 $x]
325 }
326 $ui_diff insert end $line $tags
327 $ui_diff insert end "\n"
328 $ui_diff conf -state disabled
329 }
330
331 if {[eof $fd]} {
332 close $fd
333 set diff_active 0
e210e674 334 unlock_index
cb07fc2a
SP
335 set ui_status_value {Ready.}
336 }
337}
338
ec6b424a
SP
339######################################################################
340##
341## commit
342
e57ca85e
SP
343proc load_last_commit {} {
344 global HEAD PARENT commit_type ui_comm
345
346 if {$commit_type == {amend}} return
347 if {$commit_type != {normal}} {
348 error_popup "Can't amend a $commit_type commit."
349 return
350 }
351
352 set msg {}
353 set parent {}
354 set parent_count 0
355 if {[catch {
356 set fd [open "| git cat-file commit $HEAD" r]
357 while {[gets $fd line] > 0} {
358 if {[string match {parent *} $line]} {
359 set parent [string range $line 7 end]
360 incr parent_count
361 }
362 }
363 set msg [string trim [read $fd]]
364 close $fd
365 } err]} {
366 error_popup "Error loading commit data for amend:\n$err"
367 return
368 }
369
370 if {$parent_count == 0} {
371 set commit_type amend
372 set HEAD {}
373 set PARENT {}
374 update_status
375 } elseif {$parent_count == 1} {
376 set commit_type amend
377 set PARENT $parent
378 $ui_comm delete 0.0 end
379 $ui_comm insert end $msg
380 $ui_comm edit modified false
381 update_status
382 } else {
383 error_popup {You can't amend a merge commit.}
384 return
385 }
386}
387
ec6b424a
SP
388proc commit_tree {} {
389 global tcl_platform HEAD gitdir commit_type file_states
390 global commit_active ui_status_value
391 global ui_comm
392
393 if {$commit_active || ![lock_index update]} return
394
395 # -- Our in memory state should match the repository.
396 #
397 repository_state curHEAD cur_type
e57ca85e
SP
398 if {$commit_type == {amend}
399 && $cur_type == {normal}
400 && $curHEAD == $HEAD} {
401 } elseif {$commit_type != $cur_type || $HEAD != $curHEAD} {
ec6b424a
SP
402 error_popup {Last scanned state does not match repository state.
403
404Its highly likely that another Git program modified the
405repository since our last scan. A rescan is required
406before committing.
407}
408 unlock_index
409 update_status
410 return
411 }
412
413 # -- At least one file should differ in the index.
414 #
415 set files_ready 0
416 foreach path [array names file_states] {
417 set s $file_states($path)
418 switch -glob -- [lindex $s 0] {
419 _* {continue}
420 A* -
421 D* -
422 M* {set files_ready 1; break}
423 U* {
424 error_popup "Unmerged files cannot be committed.
425
426File $path has merge conflicts.
427You must resolve them and check the file in before committing.
428"
429 unlock_index
430 return
431 }
432 default {
433 error_popup "Unknown file state [lindex $s 0] detected.
434
435File $path cannot be committed by this program.
436"
437 }
438 }
439 }
440 if {!$files_ready} {
441 error_popup {No checked-in files to commit.
442
443You must check-in at least 1 file before you can commit.
444}
445 unlock_index
446 return
447 }
448
449 # -- A message is required.
450 #
451 set msg [string trim [$ui_comm get 1.0 end]]
452 if {$msg == {}} {
453 error_popup {Please supply a commit message.
454
455A good commit message has the following format:
456
457- First line: Describe in one sentance what you did.
458- Second line: Blank
459- Remaining lines: Describe why this change is good.
460}
461 unlock_index
462 return
463 }
464
465 # -- Ask the pre-commit hook for the go-ahead.
466 #
467 set pchook [file join $gitdir hooks pre-commit]
e57ca85e 468 if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} {
ec6b424a
SP
469 set pchook [list sh -c \
470 "if test -x \"$pchook\"; then exec \"$pchook\"; fi"]
471 } elseif {[file executable $pchook]} {
472 set pchook [list $pchook]
473 } else {
474 set pchook {}
475 }
476 if {$pchook != {} && [catch {eval exec $pchook} err]} {
477 hook_failed_popup pre-commit $err
478 unlock_index
479 return
480 }
481
482 # -- Write the tree in the background.
483 #
484 set commit_active 1
485 set ui_status_value {Committing changes...}
486
487 set fd_wt [open "| git write-tree" r]
bd1e2b40 488 fileevent $fd_wt readable [list commit_stage2 $fd_wt $curHEAD $msg]
ec6b424a
SP
489}
490
491proc commit_stage2 {fd_wt curHEAD msg} {
e57ca85e
SP
492 global single_commit gitdir PARENT commit_type
493 global commit_active ui_status_value ui_comm
ec6b424a
SP
494
495 gets $fd_wt tree_id
496 close $fd_wt
497
498 if {$tree_id == {}} {
499 error_popup "write-tree failed"
500 set commit_active 0
501 set ui_status_value {Commit failed.}
502 unlock_index
503 return
504 }
505
506 # -- Create the commit.
507 #
508 set cmd [list git commit-tree $tree_id]
e57ca85e
SP
509 if {$PARENT != {}} {
510 lappend cmd -p $PARENT
ec6b424a
SP
511 }
512 if {$commit_type == {merge}} {
513 if {[catch {
514 set fd_mh [open [file join $gitdir MERGE_HEAD] r]
bd1e2b40
SP
515 while {[gets $fd_mh merge_head] >= 0} {
516 lappend cmd -p $merge_head
ec6b424a
SP
517 }
518 close $fd_mh
519 } err]} {
520 error_popup "Loading MERGE_HEADs failed:\n$err"
521 set commit_active 0
522 set ui_status_value {Commit failed.}
523 unlock_index
524 return
525 }
526 }
e57ca85e 527 if {$PARENT == {}} {
ec6b424a
SP
528 # git commit-tree writes to stderr during initial commit.
529 lappend cmd 2>/dev/null
530 }
531 lappend cmd << $msg
532 if {[catch {set cmt_id [eval exec $cmd]} err]} {
533 error_popup "commit-tree failed:\n$err"
534 set commit_active 0
535 set ui_status_value {Commit failed.}
536 unlock_index
537 return
538 }
539
540 # -- Update the HEAD ref.
541 #
542 set reflogm commit
543 if {$commit_type != {normal}} {
544 append reflogm " ($commit_type)"
545 }
546 set i [string first "\n" $msg]
547 if {$i >= 0} {
548 append reflogm {: } [string range $msg 0 [expr $i - 1]]
549 } else {
550 append reflogm {: } $msg
551 }
e57ca85e 552 set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD]
ec6b424a
SP
553 if {[catch {eval exec $cmd} err]} {
554 error_popup "update-ref failed:\n$err"
555 set commit_active 0
556 set ui_status_value {Commit failed.}
557 unlock_index
558 return
559 }
560
561 # -- Cleanup after ourselves.
562 #
563 catch {file delete [file join $gitdir MERGE_HEAD]}
564 catch {file delete [file join $gitdir MERGE_MSG]}
565 catch {file delete [file join $gitdir SQUASH_MSG]}
566 catch {file delete [file join $gitdir GITGUI_MSG]}
567
568 # -- Let rerere do its thing.
569 #
570 if {[file isdirectory [file join $gitdir rr-cache]]} {
571 catch {exec git rerere}
572 }
573
e57ca85e
SP
574 $ui_comm delete 0.0 end
575 $ui_comm edit modified false
ec6b424a
SP
576
577 if {$single_commit} do_quit
578
e57ca85e 579 set commit_type {}
ec6b424a 580 set commit_active 0
bd1e2b40
SP
581 set HEAD $cmt_id
582 set PARENT $cmt_id
ec6b424a 583 unlock_index
e57ca85e 584 update_status "Changes committed as $cmt_id."
ec6b424a
SP
585}
586
8c0ce436
SP
587######################################################################
588##
589## fetch pull push
590
591proc fetch_from {remote} {
592 set w [new_console "fetch $remote" \
593 "Fetching new changes from $remote"]
cc4b1c02
SP
594 set cmd [list git fetch]
595 lappend cmd -v
8c0ce436 596 lappend cmd $remote
cc4b1c02 597 console_exec $w $cmd
8c0ce436
SP
598}
599
600proc push_to {remote} {
601 set w [new_console "push $remote" \
602 "Pushing changes to $remote"]
cc4b1c02 603 set cmd [list git push]
8c0ce436
SP
604 lappend -v
605 lappend cmd $remote
cc4b1c02 606 console_exec $w $cmd
8c0ce436
SP
607}
608
cb07fc2a
SP
609######################################################################
610##
611## ui helpers
612
613proc mapcol {state path} {
614 global all_cols
615
616 if {[catch {set r $all_cols($state)}]} {
617 puts "error: no column for state={$state} $path"
618 return o
619 }
620 return $r
621}
622
623proc mapicon {state path} {
624 global all_icons
625
626 if {[catch {set r $all_icons($state)}]} {
627 puts "error: no icon for state={$state} $path"
628 return file_plain
629 }
630 return $r
631}
632
633proc mapdesc {state path} {
634 global all_descs
635
636 if {[catch {set r $all_descs($state)}]} {
637 puts "error: no desc for state={$state} $path"
638 return $state
639 }
640 return $r
641}
642
643proc bsearch {w path} {
644 set hi [expr [lindex [split [$w index end] .] 0] - 2]
645 if {$hi == 0} {
646 return -1
647 }
648 set lo 0
649 while {$lo < $hi} {
650 set mi [expr [expr $lo + $hi] / 2]
651 set ti [expr $mi + 1]
652 set cmp [string compare [$w get $ti.1 $ti.end] $path]
653 if {$cmp < 0} {
654 set lo $ti
655 } elseif {$cmp == 0} {
656 return $mi
657 } else {
658 set hi $mi
659 }
660 }
661 return -[expr $lo + 1]
662}
663
664proc merge_state {path state} {
665 global file_states
666
667 if {[array names file_states -exact $path] == {}} {
668 set o __
669 set s [list $o none none]
670 } else {
671 set s $file_states($path)
672 set o [lindex $s 0]
673 }
674
675 set m [lindex $s 0]
676 if {[string index $state 0] == "_"} {
677 set state [string index $m 0][string index $state 1]
678 } elseif {[string index $state 0] == "*"} {
679 set state _[string index $state 1]
680 }
681
682 if {[string index $state 1] == "_"} {
683 set state [string index $state 0][string index $m 1]
684 } elseif {[string index $state 1] == "*"} {
685 set state [string index $state 0]_
686 }
687
688 set file_states($path) [lreplace $s 0 0 $state]
689 return $o
690}
691
692proc display_file {path state} {
693 global ui_index ui_other file_states
694
695 set old_m [merge_state $path $state]
696 set s $file_states($path)
697 set m [lindex $s 0]
698
699 if {[mapcol $m $path] == "o"} {
700 set ii 1
701 set ai 2
702 set iw $ui_index
703 set aw $ui_other
704 } else {
705 set ii 2
706 set ai 1
707 set iw $ui_other
708 set aw $ui_index
709 }
710
711 set d [lindex $s $ii]
712 if {$d != "none"} {
713 set lno [bsearch $iw $path]
714 if {$lno >= 0} {
715 incr lno
716 $iw conf -state normal
717 $iw delete $lno.0 [expr $lno + 1].0
718 $iw conf -state disabled
719 set s [lreplace $s $ii $ii none]
720 }
721 }
722
723 set d [lindex $s $ai]
724 if {$d == "none"} {
725 set lno [expr abs([bsearch $aw $path] + 1) + 1]
726 $aw conf -state normal
727 set ico [$aw image create $lno.0 \
728 -align center -padx 5 -pady 1 \
729 -image [mapicon $m $path]]
730 $aw insert $lno.1 "$path\n"
731 $aw conf -state disabled
732 set file_states($path) [lreplace $s $ai $ai [list $ico]]
733 } elseif {[mapicon $m $path] != [mapicon $old_m $path]} {
734 set ico [lindex $d 0]
735 $aw image conf $ico -image [mapicon $m $path]
736 }
737}
738
131f503b
SP
739proc with_update_index {body} {
740 global update_index_fd
741
742 if {$update_index_fd == {}} {
e210e674 743 if {![lock_index update]} return
131f503b
SP
744 set update_index_fd [open \
745 "| git update-index --add --remove -z --stdin" \
746 w]
747 fconfigure $update_index_fd -translation binary
748 uplevel 1 $body
749 close $update_index_fd
750 set update_index_fd {}
e210e674 751 unlock_index
131f503b
SP
752 } else {
753 uplevel 1 $body
754 }
755}
756
757proc update_index {path} {
758 global update_index_fd
759
760 if {$update_index_fd == {}} {
761 error {not in with_update_index}
762 } else {
763 puts -nonewline $update_index_fd "$path\0"
764 }
765}
766
cb07fc2a 767proc toggle_mode {path} {
bd1e2b40 768 global file_states ui_fname_value
cb07fc2a
SP
769
770 set s $file_states($path)
771 set m [lindex $s 0]
772
773 switch -- $m {
774 AM -
131f503b
SP
775 _O {set new A*}
776 _M -
777 MM {set new M*}
bd1e2b40 778 AD -
131f503b
SP
779 _D {set new D*}
780 default {return}
cb07fc2a
SP
781 }
782
131f503b 783 with_update_index {update_index $path}
cb07fc2a 784 display_file $path $new
bd1e2b40
SP
785 if {$ui_fname_value == $path} {
786 show_diff $path
787 }
cb07fc2a
SP
788}
789
8c0ce436
SP
790######################################################################
791##
792## config (fetch push pull)
793
794proc load_all_remotes {} {
795 global gitdir all_remotes
796
797 set all_remotes [list]
798 set rm_dir [file join $gitdir remotes]
799 if {[file isdirectory $rm_dir]} {
800 set all_remotes [concat $all_remotes \
801 [glob -types f -tails -directory $rm_dir * *]]
802 }
803
804 set fd_rc [open "| git repo-config --list" r]
805 while {[gets $fd_rc line] >= 0} {
806 if {[regexp ^remote\.(.*)\.url= $line line name]} {
807 lappend all_remotes $name
808 }
809 }
810 close $fd_rc
811
812 set all_remotes [lsort -unique $all_remotes]
813}
814
815proc populate_remote_menu {m pfx op} {
816 global gitdir all_remotes mainfont
817
818 foreach remote $all_remotes {
819 $m add command -label "$pfx $remote..." \
820 -command [list $op $remote] \
821 -font $mainfont
822 }
823}
824
cb07fc2a
SP
825######################################################################
826##
827## icons
828
829set filemask {
830#define mask_width 14
831#define mask_height 15
832static unsigned char mask_bits[] = {
833 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
834 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
835 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
836}
837
838image create bitmap file_plain -background white -foreground black -data {
839#define plain_width 14
840#define plain_height 15
841static unsigned char plain_bits[] = {
842 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
843 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
844 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
845} -maskdata $filemask
846
847image create bitmap file_mod -background white -foreground blue -data {
848#define mod_width 14
849#define mod_height 15
850static unsigned char mod_bits[] = {
851 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
852 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
853 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
854} -maskdata $filemask
855
131f503b
SP
856image create bitmap file_fulltick -background white -foreground "#007000" -data {
857#define file_fulltick_width 14
858#define file_fulltick_height 15
859static unsigned char file_fulltick_bits[] = {
cb07fc2a
SP
860 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
861 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
862 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
863} -maskdata $filemask
864
865image create bitmap file_parttick -background white -foreground "#005050" -data {
866#define parttick_width 14
867#define parttick_height 15
868static unsigned char parttick_bits[] = {
869 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
870 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
871 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
872} -maskdata $filemask
873
874image create bitmap file_question -background white -foreground black -data {
875#define file_question_width 14
876#define file_question_height 15
877static unsigned char file_question_bits[] = {
878 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
879 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
880 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
881} -maskdata $filemask
882
883image create bitmap file_removed -background white -foreground red -data {
884#define file_removed_width 14
885#define file_removed_height 15
886static unsigned char file_removed_bits[] = {
887 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
888 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
889 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
890} -maskdata $filemask
891
892image create bitmap file_merge -background white -foreground blue -data {
893#define file_merge_width 14
894#define file_merge_height 15
895static unsigned char file_merge_bits[] = {
896 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
897 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
898 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
899} -maskdata $filemask
900
131f503b 901set max_status_desc 0
cb07fc2a 902foreach i {
131f503b
SP
903 {__ i plain "Unmodified"}
904 {_M i mod "Modified"}
905 {M_ i fulltick "Checked in"}
906 {MM i parttick "Partially checked in"}
907
908 {_O o plain "Untracked"}
909 {A_ o fulltick "Added"}
910 {AM o parttick "Partially added"}
bd1e2b40 911 {AD o question "Added (but now gone)"}
131f503b
SP
912
913 {_D i question "Missing"}
914 {D_ i removed "Removed"}
915 {DD i removed "Removed"}
916 {DO i removed "Removed (still exists)"}
917
918 {UM i merge "Merge conflicts"}
919 {U_ i merge "Merge conflicts"}
cb07fc2a 920 } {
131f503b
SP
921 if {$max_status_desc < [string length [lindex $i 3]]} {
922 set max_status_desc [string length [lindex $i 3]]
923 }
cb07fc2a 924 set all_cols([lindex $i 0]) [lindex $i 1]
131f503b
SP
925 set all_icons([lindex $i 0]) file_[lindex $i 2]
926 set all_descs([lindex $i 0]) [lindex $i 3]
cb07fc2a
SP
927}
928unset filemask i
929
930######################################################################
931##
932## util
933
934proc error_popup {msg} {
935 set w .error
936 toplevel $w
937 wm transient $w .
938 show_msg $w $w $msg
939}
940
941proc show_msg {w top msg} {
b8ce6f0e 942 global gitdir appname mainfont
6e27d826
SP
943
944 message $w.m -text $msg -justify left -aspect 400
ec6b424a
SP
945 pack $w.m -side top -fill x -padx 5 -pady 10
946 button $w.ok -text OK \
947 -width 15 \
8c0ce436 948 -font $mainfont \
ec6b424a 949 -command "destroy $top"
6e27d826 950 pack $w.ok -side bottom
cb07fc2a
SP
951 bind $top <Visibility> "grab $top; focus $top"
952 bind $top <Key-Return> "destroy $top"
ec6b424a 953 wm title $top "error: $appname ([file normalize [file dirname $gitdir]])"
cb07fc2a
SP
954 tkwait window $top
955}
956
6e27d826 957proc hook_failed_popup {hook msg} {
ec6b424a 958 global gitdir mainfont difffont appname
6e27d826
SP
959
960 set w .hookfail
961 toplevel $w
962 wm transient $w .
963
964 frame $w.m
965 label $w.m.l1 -text "$hook hook failed:" \
966 -anchor w \
967 -justify left \
968 -font [concat $mainfont bold]
969 text $w.m.t \
970 -background white -borderwidth 1 \
971 -relief sunken \
972 -width 80 -height 10 \
973 -font $difffont \
974 -yscrollcommand [list $w.m.sby set]
975 label $w.m.l2 \
976 -text {You must correct the above errors before committing.} \
977 -anchor w \
978 -justify left \
979 -font [concat $mainfont bold]
980 scrollbar $w.m.sby -command [list $w.m.t yview]
981 pack $w.m.l1 -side top -fill x
982 pack $w.m.l2 -side bottom -fill x
983 pack $w.m.sby -side right -fill y
984 pack $w.m.t -side left -fill both -expand 1
985 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
986
987 $w.m.t insert 1.0 $msg
988 $w.m.t conf -state disabled
989
990 button $w.ok -text OK \
991 -width 15 \
8c0ce436 992 -font $mainfont \
6e27d826
SP
993 -command "destroy $w"
994 pack $w.ok -side bottom
995
996 bind $w <Visibility> "grab $w; focus $w"
997 bind $w <Key-Return> "destroy $w"
ec6b424a 998 wm title $w "error: $appname ([file normalize [file dirname $gitdir]])"
6e27d826
SP
999 tkwait window $w
1000}
1001
8c0ce436
SP
1002set next_console_id 0
1003
1004proc new_console {short_title long_title} {
1005 global next_console_id gitdir appname mainfont difffont
1006
1007 set w .console[incr next_console_id]
1008 toplevel $w
1009 frame $w.m
1010 label $w.m.l1 -text "$long_title:" \
1011 -anchor w \
1012 -justify left \
1013 -font [concat $mainfont bold]
1014 text $w.m.t \
1015 -background white -borderwidth 1 \
1016 -relief sunken \
1017 -width 80 -height 10 \
1018 -font $difffont \
1019 -state disabled \
1020 -yscrollcommand [list $w.m.sby set]
1021 scrollbar $w.m.sby -command [list $w.m.t yview]
1022 pack $w.m.l1 -side top -fill x
1023 pack $w.m.sby -side right -fill y
1024 pack $w.m.t -side left -fill both -expand 1
1025 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1026
1027 button $w.ok -text {OK} \
1028 -width 15 \
1029 -font $mainfont \
1030 -state disabled \
1031 -command "destroy $w"
1032 pack $w.ok -side bottom
1033
1034 bind $w <Visibility> "focus $w"
1035 bind $w <Destroy> break
1036 wm title $w "$appname ([file dirname [file normalize [file dirname $gitdir]]]): $short_title"
1037 return $w
1038}
1039
cc4b1c02
SP
1040proc console_exec {w cmd} {
1041 global tcl_platform
1042
1043 # -- Windows tosses the enviroment when we exec our child.
1044 # But most users need that so we have to relogin. :-(
1045 #
1046 if {$tcl_platform(platform) == {windows}} {
1047 set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"]
1048 }
1049
1050 # -- Tcl won't let us redirect both stdout and stderr to
1051 # the same pipe. So pass it through cat...
1052 #
1053 set cmd [concat | $cmd |& cat]
1054
1055 set fd_f [open $cmd r]
1056 fconfigure $fd_f -blocking 0 -translation auto
1057 fileevent $fd_f readable [list console_read $w $fd_f]
1058}
1059
8c0ce436
SP
1060proc console_read {w fd} {
1061 $w.m.t conf -state normal
1062 while {[gets $fd line] >= 0} {
1063 $w.m.t insert end $line
1064 $w.m.t insert end "\n"
1065 }
1066 $w.m.t conf -state disabled
cc4b1c02 1067 $w.m.t see end
8c0ce436
SP
1068
1069 if {[eof $fd]} {
1070 close $fd
1071 $w.ok conf -state normal
1072 }
1073}
1074
cb07fc2a
SP
1075######################################################################
1076##
1077## ui commands
1078
e210e674 1079set starting_gitk_msg {Please wait... Starting gitk...}
cc4b1c02 1080
cb07fc2a 1081proc do_gitk {} {
e210e674
SP
1082 global tcl_platform ui_status_value starting_gitk_msg
1083
1084 set ui_status_value $starting_gitk_msg
e57ca85e 1085 after 10000 {
e210e674
SP
1086 if {$ui_status_value == $starting_gitk_msg} {
1087 set ui_status_value {Ready.}
1088 }
1089 }
cb07fc2a 1090
cc4b1c02 1091 if {$tcl_platform(platform) == {windows}} {
cb07fc2a
SP
1092 exec sh -c gitk &
1093 } else {
1094 exec gitk &
1095 }
1096}
1097
1098proc do_quit {} {
131f503b
SP
1099 global gitdir ui_comm
1100
1101 set save [file join $gitdir GITGUI_MSG]
ec6b424a
SP
1102 set msg [string trim [$ui_comm get 0.0 end]]
1103 if {[$ui_comm edit modified] && $msg != {}} {
131f503b
SP
1104 catch {
1105 set fd [open $save w]
1106 puts $fd [string trim [$ui_comm get 0.0 end]]
1107 close $fd
1108 }
ec6b424a 1109 } elseif {$msg == {} && [file exists $save]} {
131f503b
SP
1110 file delete $save
1111 }
1112
cb07fc2a
SP
1113 destroy .
1114}
1115
1116proc do_rescan {} {
1117 update_status
1118}
1119
131f503b
SP
1120proc do_checkin_all {} {
1121 global checkin_active ui_status_value
1122
e210e674 1123 if {$checkin_active || ![lock_index begin-update]} return
131f503b
SP
1124
1125 set checkin_active 1
1126 set ui_status_value {Checking in all files...}
1127 after 1 {
1128 with_update_index {
1129 foreach path [array names file_states] {
1130 set s $file_states($path)
1131 set m [lindex $s 0]
1132 switch -- $m {
1133 AM -
1134 MM -
1135 _M -
1136 _D {toggle_mode $path}
1137 }
1138 }
1139 }
1140 set checkin_active 0
1141 set ui_status_value {Ready.}
1142 }
1143}
1144
1145proc do_signoff {} {
1146 global ui_comm
1147
1148 catch {
1149 set me [exec git var GIT_COMMITTER_IDENT]
1150 if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} {
1151 set str "Signed-off-by: $name"
1152 if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
1153 $ui_comm insert end "\n"
1154 $ui_comm insert end $str
1155 $ui_comm see end
1156 }
1157 }
1158 }
1159}
1160
e57ca85e
SP
1161proc do_amend_last {} {
1162 load_last_commit
1163}
1164
6e27d826 1165proc do_commit {} {
ec6b424a 1166 commit_tree
6e27d826
SP
1167}
1168
cb07fc2a
SP
1169# shift == 1: left click
1170# 3: right click
1171proc click {w x y shift wx wy} {
131f503b
SP
1172 global ui_index ui_other
1173
cb07fc2a
SP
1174 set pos [split [$w index @$x,$y] .]
1175 set lno [lindex $pos 0]
1176 set col [lindex $pos 1]
1177 set path [$w get $lno.1 $lno.end]
1178 if {$path == {}} return
1179
1180 if {$col > 0 && $shift == 1} {
131f503b
SP
1181 $ui_index tag remove in_diff 0.0 end
1182 $ui_other tag remove in_diff 0.0 end
1183 $w tag add in_diff $lno.0 [expr $lno + 1].0
cb07fc2a
SP
1184 show_diff $path
1185 }
1186}
1187
1188proc unclick {w x y} {
1189 set pos [split [$w index @$x,$y] .]
1190 set lno [lindex $pos 0]
1191 set col [lindex $pos 1]
1192 set path [$w get $lno.1 $lno.end]
1193 if {$path == {}} return
1194
e210e674 1195 if {$col == 0} {
cb07fc2a
SP
1196 toggle_mode $path
1197 }
1198}
1199
1200######################################################################
1201##
1202## ui init
1203
1204set mainfont {Helvetica 10}
1205set difffont {Courier 10}
1206set maincursor [. cget -cursor]
1207
66144892
SP
1208switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" {
1209windows,* {set M1B Control; set M1T Ctrl}
1210unix,Darwin {set M1B M1; set M1T Cmd}
1211default {set M1B M1; set M1T M1}
e210e674
SP
1212}
1213
cb07fc2a
SP
1214# -- Menu Bar
1215menu .mbar -tearoff 0
1216.mbar add cascade -label Project -menu .mbar.project
1217.mbar add cascade -label Commit -menu .mbar.commit
1218.mbar add cascade -label Fetch -menu .mbar.fetch
1219.mbar add cascade -label Pull -menu .mbar.pull
8c0ce436 1220.mbar add cascade -label Push -menu .mbar.push
cb07fc2a
SP
1221. configure -menu .mbar
1222
1223# -- Project Menu
1224menu .mbar.project
6f6eed28 1225.mbar.project add command -label Visualize \
cb07fc2a
SP
1226 -command do_gitk \
1227 -font $mainfont
1228.mbar.project add command -label Quit \
1229 -command do_quit \
e210e674 1230 -accelerator $M1T-Q \
cb07fc2a
SP
1231 -font $mainfont
1232
1233# -- Commit Menu
1234menu .mbar.commit
1235.mbar.commit add command -label Rescan \
1236 -command do_rescan \
e210e674 1237 -accelerator F5 \
cb07fc2a 1238 -font $mainfont
e210e674
SP
1239lappend disable_on_lock \
1240 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e57ca85e
SP
1241.mbar.commit add command -label {Amend Last Commit} \
1242 -command do_amend_last \
1243 -font $mainfont
1244lappend disable_on_lock \
1245 [list .mbar.commit entryconf [.mbar.commit index last] -state]
131f503b
SP
1246.mbar.commit add command -label {Check-in All Files} \
1247 -command do_checkin_all \
e210e674 1248 -accelerator $M1T-U \
131f503b 1249 -font $mainfont
e210e674
SP
1250lappend disable_on_lock \
1251 [list .mbar.commit entryconf [.mbar.commit index last] -state]
131f503b
SP
1252.mbar.commit add command -label {Sign Off} \
1253 -command do_signoff \
e210e674 1254 -accelerator $M1T-S \
131f503b
SP
1255 -font $mainfont
1256.mbar.commit add command -label Commit \
1257 -command do_commit \
e210e674 1258 -accelerator $M1T-Return \
131f503b 1259 -font $mainfont
e210e674
SP
1260lappend disable_on_lock \
1261 [list .mbar.commit entryconf [.mbar.commit index last] -state]
cb07fc2a
SP
1262
1263# -- Fetch Menu
1264menu .mbar.fetch
1265
1266# -- Pull Menu
1267menu .mbar.pull
1268
8c0ce436
SP
1269# -- Push Menu
1270menu .mbar.push
1271
cb07fc2a
SP
1272# -- Main Window Layout
1273panedwindow .vpane -orient vertical
1274panedwindow .vpane.files -orient horizontal
6f6eed28 1275.vpane add .vpane.files -sticky nsew -height 100 -width 400
cb07fc2a
SP
1276pack .vpane -anchor n -side top -fill both -expand 1
1277
1278# -- Index File List
1279set ui_index .vpane.files.index.list
1280frame .vpane.files.index -height 100 -width 400
1281label .vpane.files.index.title -text {Modified Files} \
1282 -background green \
1283 -font $mainfont
1284text $ui_index -background white -borderwidth 0 \
1285 -width 40 -height 10 \
1286 -font $mainfont \
1287 -yscrollcommand {.vpane.files.index.sb set} \
1288 -cursor $maincursor \
1289 -state disabled
1290scrollbar .vpane.files.index.sb -command [list $ui_index yview]
1291pack .vpane.files.index.title -side top -fill x
1292pack .vpane.files.index.sb -side right -fill y
1293pack $ui_index -side left -fill both -expand 1
1294.vpane.files add .vpane.files.index -sticky nsew
1295
1296# -- Other (Add) File List
1297set ui_other .vpane.files.other.list
1298frame .vpane.files.other -height 100 -width 100
1299label .vpane.files.other.title -text {Untracked Files} \
1300 -background red \
1301 -font $mainfont
1302text $ui_other -background white -borderwidth 0 \
1303 -width 40 -height 10 \
1304 -font $mainfont \
1305 -yscrollcommand {.vpane.files.other.sb set} \
1306 -cursor $maincursor \
1307 -state disabled
1308scrollbar .vpane.files.other.sb -command [list $ui_other yview]
1309pack .vpane.files.other.title -side top -fill x
1310pack .vpane.files.other.sb -side right -fill y
1311pack $ui_other -side left -fill both -expand 1
1312.vpane.files add .vpane.files.other -sticky nsew
1313
131f503b
SP
1314$ui_index tag conf in_diff -font [concat $mainfont bold]
1315$ui_other tag conf in_diff -font [concat $mainfont bold]
1316
cb07fc2a
SP
1317# -- Diff Header
1318set ui_fname_value {}
1319set ui_fstatus_value {}
6f6eed28 1320frame .vpane.diff -height 200 -width 400
cb07fc2a
SP
1321frame .vpane.diff.header
1322label .vpane.diff.header.l1 -text {File:} -font $mainfont
1323label .vpane.diff.header.l2 -textvariable ui_fname_value \
1324 -anchor w \
1325 -justify left \
1326 -font $mainfont
1327label .vpane.diff.header.l3 -text {Status:} -font $mainfont
1328label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
131f503b 1329 -width $max_status_desc \
cb07fc2a
SP
1330 -anchor w \
1331 -justify left \
1332 -font $mainfont
1333pack .vpane.diff.header.l1 -side left
1334pack .vpane.diff.header.l2 -side left -fill x
1335pack .vpane.diff.header.l4 -side right
1336pack .vpane.diff.header.l3 -side right
1337
1338# -- Diff Body
1339frame .vpane.diff.body
1340set ui_diff .vpane.diff.body.t
1341text $ui_diff -background white -borderwidth 0 \
6f6eed28 1342 -width 80 -height 15 -wrap none \
cb07fc2a
SP
1343 -font $difffont \
1344 -xscrollcommand {.vpane.diff.body.sbx set} \
1345 -yscrollcommand {.vpane.diff.body.sby set} \
1346 -cursor $maincursor \
1347 -state disabled
1348scrollbar .vpane.diff.body.sbx -orient horizontal \
1349 -command [list $ui_diff xview]
1350scrollbar .vpane.diff.body.sby -orient vertical \
1351 -command [list $ui_diff yview]
1352pack .vpane.diff.body.sbx -side bottom -fill x
1353pack .vpane.diff.body.sby -side right -fill y
1354pack $ui_diff -side left -fill both -expand 1
1355pack .vpane.diff.header -side top -fill x
1356pack .vpane.diff.body -side bottom -fill both -expand 1
1357.vpane add .vpane.diff -stick nsew
1358
1359$ui_diff tag conf dm -foreground red
1360$ui_diff tag conf dp -foreground blue
1361$ui_diff tag conf da -font [concat $difffont bold]
1362$ui_diff tag conf di -foreground "#00a000"
1363$ui_diff tag conf dni -foreground "#a000a0"
1364$ui_diff tag conf bold -font [concat $difffont bold]
1365
1366# -- Commit Area
e57ca85e 1367frame .vpane.commarea -height 170
cb07fc2a
SP
1368.vpane add .vpane.commarea -stick nsew
1369
1370# -- Commit Area Buttons
1371frame .vpane.commarea.buttons
1372label .vpane.commarea.buttons.l -text {} \
1373 -anchor w \
1374 -justify left \
1375 -font $mainfont
1376pack .vpane.commarea.buttons.l -side top -fill x
131f503b
SP
1377pack .vpane.commarea.buttons -side left -fill y
1378
cb07fc2a
SP
1379button .vpane.commarea.buttons.rescan -text {Rescan} \
1380 -command do_rescan \
1381 -font $mainfont
1382pack .vpane.commarea.buttons.rescan -side top -fill x
e210e674 1383lappend disable_on_lock {.vpane.commarea.buttons.rescan conf -state}
131f503b 1384
e57ca85e
SP
1385button .vpane.commarea.buttons.amend -text {Amend Last} \
1386 -command do_amend_last \
1387 -font $mainfont
1388pack .vpane.commarea.buttons.amend -side top -fill x
1389lappend disable_on_lock {.vpane.commarea.buttons.amend conf -state}
1390
cb07fc2a
SP
1391button .vpane.commarea.buttons.ciall -text {Check-in All} \
1392 -command do_checkin_all \
1393 -font $mainfont
1394pack .vpane.commarea.buttons.ciall -side top -fill x
e210e674 1395lappend disable_on_lock {.vpane.commarea.buttons.ciall conf -state}
131f503b
SP
1396
1397button .vpane.commarea.buttons.signoff -text {Sign Off} \
1398 -command do_signoff \
1399 -font $mainfont
1400pack .vpane.commarea.buttons.signoff -side top -fill x
1401
cb07fc2a
SP
1402button .vpane.commarea.buttons.commit -text {Commit} \
1403 -command do_commit \
1404 -font $mainfont
1405pack .vpane.commarea.buttons.commit -side top -fill x
e210e674 1406lappend disable_on_lock {.vpane.commarea.buttons.commit conf -state}
cb07fc2a
SP
1407
1408# -- Commit Message Buffer
1409frame .vpane.commarea.buffer
1410set ui_comm .vpane.commarea.buffer.t
bd1e2b40
SP
1411set ui_coml .vpane.commarea.buffer.l
1412label $ui_coml -text {Commit Message:} \
cb07fc2a
SP
1413 -anchor w \
1414 -justify left \
1415 -font $mainfont
bd1e2b40
SP
1416trace add variable commit_type write {uplevel #0 {
1417 switch -glob $commit_type \
1418 initial {$ui_coml conf -text {Initial Commit Message:}} \
1419 amend {$ui_coml conf -text {Amended Commit Message:}} \
1420 merge {$ui_coml conf -text {Merge Commit Message:}} \
1421 * {$ui_coml conf -text {Commit Message:}}
1422}}
cb07fc2a
SP
1423text $ui_comm -background white -borderwidth 1 \
1424 -relief sunken \
1425 -width 75 -height 10 -wrap none \
1426 -font $difffont \
1427 -yscrollcommand {.vpane.commarea.buffer.sby set} \
1428 -cursor $maincursor
1429scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
bd1e2b40 1430pack $ui_coml -side top -fill x
cb07fc2a
SP
1431pack .vpane.commarea.buffer.sby -side right -fill y
1432pack $ui_comm -side left -fill y
1433pack .vpane.commarea.buffer -side left -fill y
1434
1435# -- Status Bar
1436set ui_status_value {Initializing...}
1437label .status -textvariable ui_status_value \
1438 -anchor w \
1439 -justify left \
1440 -borderwidth 1 \
1441 -relief sunken \
1442 -font $mainfont
1443pack .status -anchor w -side bottom -fill x
1444
1445# -- Key Bindings
ec6b424a 1446bind $ui_comm <$M1B-Key-Return> {do_commit;break}
cb07fc2a
SP
1447bind . <Destroy> do_quit
1448bind . <Key-F5> do_rescan
e210e674
SP
1449bind . <$M1B-Key-r> do_rescan
1450bind . <$M1B-Key-R> do_rescan
1451bind . <$M1B-Key-s> do_signoff
1452bind . <$M1B-Key-S> do_signoff
1453bind . <$M1B-Key-u> do_checkin_all
1454bind . <$M1B-Key-U> do_checkin_all
1455bind . <$M1B-Key-Return> do_commit
1456bind . <$M1B-Key-q> do_quit
1457bind . <$M1B-Key-Q> do_quit
cb07fc2a
SP
1458foreach i [list $ui_index $ui_other] {
1459 bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
1460 bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
1461 bind $i <ButtonRelease-1> {unclick %W %x %y; break}
1462}
e210e674 1463unset i M1B M1T
cb07fc2a
SP
1464
1465######################################################################
1466##
1467## main
1468
b8ce6f0e
SP
1469set appname [lindex [file split $argv0] end]
1470set gitdir {}
1471
1472if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} {
cb07fc2a
SP
1473 show_msg {} . "Cannot find the git directory: $err"
1474 exit 1
1475}
bd1e2b40
SP
1476if {$cdup != ""} {
1477 cd $cdup
1478}
1479unset cdup
cb07fc2a 1480
b8ce6f0e
SP
1481if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
1482 show_msg {} . "Cannot find the git directory: $err"
1483 exit 1
1484}
1485
ec6b424a
SP
1486if {$appname == {git-citool}} {
1487 set single_commit 1
1488}
1489
1490wm title . "$appname ([file normalize [file dirname $gitdir]])"
cb07fc2a 1491focus -force $ui_comm
8c0ce436
SP
1492load_all_remotes
1493populate_remote_menu .mbar.fetch From fetch_from
1494populate_remote_menu .mbar.push To push_to
cb07fc2a 1495update_status