]> git.ipfire.org Git - thirdparty/git.git/blame - git-gui
git-gui: Misc. bug fixes for mouse click crashes.
[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
da5239dc
SP
10set appname [lindex [file split $argv0] end]
11set gitdir {}
12
2d19516d
SP
13######################################################################
14##
15## config
16
17proc load_repo_config {} {
18 global repo_config
19 global cfg_trust_mtime
20
21 array unset repo_config
22 catch {
23 set fd_rc [open "| git repo-config --list" r]
24 while {[gets $fd_rc line] >= 0} {
25 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
26 lappend repo_config($name) $value
27 }
28 }
29 close $fd_rc
30 }
31
32 if {[catch {set cfg_trust_mtime \
33 [lindex $repo_config(gui.trustmtime) 0]
34 }]} {
35 set cfg_trust_mtime false
36 }
37}
38
39proc save_my_config {} {
40 global repo_config
41 global cfg_trust_mtime
42
43 if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
44 set rc_trustMTime [list false]
45 }
46 if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} {
47 exec git repo-config gui.trustMTime $cfg_trust_mtime
48 set repo_config(gui.trustmtime) [list $cfg_trust_mtime]
49 }
50
c4fe7728
SP
51 set cfg_geometry [wm geometry .]
52 append cfg_geometry " [lindex [.vpane sash coord 0] 1]"
53 append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]"
2d19516d
SP
54 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
55 set rc_geometry [list [list]]
56 }
57 if {$cfg_geometry != [lindex $rc_geometry 0]} {
58 exec git repo-config gui.geometry $cfg_geometry
59 set repo_config(gui.geometry) [list $cfg_geometry]
60 }
61}
62
da5239dc
SP
63proc error_popup {msg} {
64 global gitdir appname
65
66 set title $appname
67 if {$gitdir != {}} {
68 append title { (}
69 append title [lindex \
70 [file split [file normalize [file dirname $gitdir]]] \
71 end]
72 append title {)}
73 }
44be340e
SP
74 tk_messageBox \
75 -parent . \
da5239dc
SP
76 -icon error \
77 -type ok \
78 -title "$title: error" \
79 -message $msg
80}
81
2d19516d
SP
82######################################################################
83##
84## repository setup
85
44be340e
SP
86if { [catch {set cdup [exec git rev-parse --show-cdup]} err]
87 || [catch {set gitdir [exec git rev-parse --git-dir]} err]} {
88 catch {wm withdraw .}
89 error_popup "Cannot find the git directory:\n\n$err"
2d19516d
SP
90 exit 1
91}
92if {$cdup != ""} {
93 cd $cdup
94}
95unset cdup
96
2d19516d
SP
97if {$appname == {git-citool}} {
98 set single_commit 1
99}
100
101load_repo_config
102
cb07fc2a
SP
103######################################################################
104##
e210e674 105## task management
cb07fc2a 106
ec6b424a 107set single_commit 0
cb07fc2a 108set status_active 0
131f503b 109set diff_active 0
7fe7e733 110set update_active 0
ec6b424a 111set commit_active 0
131f503b
SP
112set update_index_fd {}
113
e210e674
SP
114set disable_on_lock [list]
115set index_lock_type none
116
e57ca85e
SP
117set HEAD {}
118set PARENT {}
119set commit_type {}
120
e210e674
SP
121proc lock_index {type} {
122 global index_lock_type disable_on_lock
131f503b 123
e210e674
SP
124 if {$index_lock_type == {none}} {
125 set index_lock_type $type
126 foreach w $disable_on_lock {
127 uplevel #0 $w disabled
128 }
129 return 1
130 } elseif {$index_lock_type == {begin-update} && $type == {update}} {
131 set index_lock_type $type
131f503b
SP
132 return 1
133 }
134 return 0
135}
cb07fc2a 136
e210e674
SP
137proc unlock_index {} {
138 global index_lock_type disable_on_lock
139
140 set index_lock_type none
141 foreach w $disable_on_lock {
142 uplevel #0 $w normal
143 }
144}
145
146######################################################################
147##
148## status
149
ec6b424a
SP
150proc repository_state {hdvar ctvar} {
151 global gitdir
152 upvar $hdvar hd $ctvar ct
153
154 if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
155 set ct initial
156 } elseif {[file exists [file join $gitdir MERGE_HEAD]]} {
157 set ct merge
158 } else {
159 set ct normal
160 }
161}
162
e57ca85e
SP
163proc update_status {{final Ready.}} {
164 global HEAD PARENT commit_type
131f503b 165 global ui_index ui_other ui_status_value ui_comm
7f1df79b 166 global status_active file_states
e534f3a8 167 global cfg_trust_mtime
cb07fc2a 168
e210e674 169 if {$status_active || ![lock_index read]} return
cb07fc2a 170
e57ca85e
SP
171 repository_state new_HEAD new_type
172 if {$commit_type == {amend}
173 && $new_type == {normal}
174 && $new_HEAD == $HEAD} {
175 } else {
176 set HEAD $new_HEAD
177 set PARENT $new_HEAD
178 set commit_type $new_type
179 }
180
cb07fc2a 181 array unset file_states
cb07fc2a 182
131f503b 183 if {![$ui_comm edit modified]
cc4b1c02 184 || [string trim [$ui_comm get 0.0 end]] == {}} {
131f503b
SP
185 if {[load_message GITGUI_MSG]} {
186 } elseif {[load_message MERGE_MSG]} {
187 } elseif {[load_message SQUASH_MSG]} {
188 }
189 $ui_comm edit modified false
b2c6fcf1 190 $ui_comm edit reset
131f503b
SP
191 }
192
e534f3a8
SP
193 if {$cfg_trust_mtime == {true}} {
194 update_status_stage2 {} $final
195 } else {
196 set status_active 1
197 set ui_status_value {Refreshing file status...}
198 set fd_rf [open "| git update-index -q --unmerged --refresh" r]
199 fconfigure $fd_rf -blocking 0 -translation binary
390adaea
SP
200 fileevent $fd_rf readable \
201 [list update_status_stage2 $fd_rf $final]
e534f3a8 202 }
131f503b
SP
203}
204
e534f3a8 205proc update_status_stage2 {fd final} {
e57ca85e 206 global gitdir PARENT commit_type
131f503b 207 global ui_index ui_other ui_status_value ui_comm
03e4ec53 208 global status_active
868c8752 209 global buf_rdi buf_rdf buf_rlo
131f503b 210
e534f3a8
SP
211 if {$fd != {}} {
212 read $fd
213 if {![eof $fd]} return
214 close $fd
215 }
131f503b 216
cb07fc2a
SP
217 set ls_others [list | git ls-files --others -z \
218 --exclude-per-directory=.gitignore]
219 set info_exclude [file join $gitdir info exclude]
220 if {[file readable $info_exclude]} {
221 lappend ls_others "--exclude-from=$info_exclude"
222 }
223
868c8752
SP
224 set buf_rdi {}
225 set buf_rdf {}
226 set buf_rlo {}
227
131f503b
SP
228 set status_active 3
229 set ui_status_value {Scanning for modified files ...}
e57ca85e 230 set fd_di [open "| git diff-index --cached -z $PARENT" r]
cb07fc2a
SP
231 set fd_df [open "| git diff-files -z" r]
232 set fd_lo [open $ls_others r]
cb07fc2a
SP
233
234 fconfigure $fd_di -blocking 0 -translation binary
235 fconfigure $fd_df -blocking 0 -translation binary
236 fconfigure $fd_lo -blocking 0 -translation binary
e57ca85e
SP
237 fileevent $fd_di readable [list read_diff_index $fd_di $final]
238 fileevent $fd_df readable [list read_diff_files $fd_df $final]
239 fileevent $fd_lo readable [list read_ls_others $fd_lo $final]
cb07fc2a
SP
240}
241
131f503b
SP
242proc load_message {file} {
243 global gitdir ui_comm
244
245 set f [file join $gitdir $file]
e57ca85e 246 if {[file isfile $f]} {
131f503b
SP
247 if {[catch {set fd [open $f r]}]} {
248 return 0
249 }
e57ca85e 250 set content [string trim [read $fd]]
131f503b
SP
251 close $fd
252 $ui_comm delete 0.0 end
253 $ui_comm insert end $content
254 return 1
255 }
256 return 0
257}
258
e57ca85e 259proc read_diff_index {fd final} {
cb07fc2a
SP
260 global buf_rdi
261
262 append buf_rdi [read $fd]
868c8752
SP
263 set c 0
264 set n [string length $buf_rdi]
265 while {$c < $n} {
266 set z1 [string first "\0" $buf_rdi $c]
267 if {$z1 == -1} break
268 incr z1
269 set z2 [string first "\0" $buf_rdi $z1]
270 if {$z2 == -1} break
271
272 set c $z2
273 incr z2 -1
274 display_file \
275 [string range $buf_rdi $z1 $z2] \
276 [string index $buf_rdi [expr $z1 - 2]]_
277 incr c
cb07fc2a 278 }
868c8752
SP
279 if {$c < $n} {
280 set buf_rdi [string range $buf_rdi $c end]
281 } else {
282 set buf_rdi {}
283 }
284
e57ca85e 285 status_eof $fd buf_rdi $final
cb07fc2a
SP
286}
287
e57ca85e 288proc read_diff_files {fd final} {
cb07fc2a
SP
289 global buf_rdf
290
291 append buf_rdf [read $fd]
868c8752
SP
292 set c 0
293 set n [string length $buf_rdf]
294 while {$c < $n} {
295 set z1 [string first "\0" $buf_rdf $c]
296 if {$z1 == -1} break
297 incr z1
298 set z2 [string first "\0" $buf_rdf $z1]
299 if {$z2 == -1} break
300
301 set c $z2
302 incr z2 -1
303 display_file \
304 [string range $buf_rdf $z1 $z2] \
305 _[string index $buf_rdf [expr $z1 - 2]]
306 incr c
307 }
308 if {$c < $n} {
309 set buf_rdf [string range $buf_rdf $c end]
310 } else {
311 set buf_rdf {}
cb07fc2a 312 }
868c8752 313
e57ca85e 314 status_eof $fd buf_rdf $final
cb07fc2a
SP
315}
316
e57ca85e 317proc read_ls_others {fd final} {
cb07fc2a
SP
318 global buf_rlo
319
320 append buf_rlo [read $fd]
321 set pck [split $buf_rlo "\0"]
322 set buf_rlo [lindex $pck end]
323 foreach p [lrange $pck 0 end-1] {
324 display_file $p _O
325 }
e57ca85e 326 status_eof $fd buf_rlo $final
cb07fc2a
SP
327}
328
e57ca85e 329proc status_eof {fd buf final} {
7f1df79b
SP
330 global status_active ui_status_value
331 upvar $buf to_clear
cb07fc2a
SP
332
333 if {[eof $fd]} {
7f1df79b 334 set to_clear {}
cb07fc2a 335 close $fd
93f654df 336
cb07fc2a 337 if {[incr status_active -1] == 0} {
93f654df 338 display_all_files
7f1df79b
SP
339 unlock_index
340 reshow_diff
6b292675 341 set ui_status_value $final
cb07fc2a
SP
342 }
343 }
344}
345
346######################################################################
347##
348## diff
349
cb07fc2a 350proc clear_diff {} {
03e4ec53 351 global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other
cb07fc2a
SP
352
353 $ui_diff conf -state normal
354 $ui_diff delete 0.0 end
355 $ui_diff conf -state disabled
03e4ec53 356
cb07fc2a
SP
357 set ui_fname_value {}
358 set ui_fstatus_value {}
03e4ec53
SP
359
360 $ui_index tag remove in_diff 0.0 end
361 $ui_other tag remove in_diff 0.0 end
cb07fc2a
SP
362}
363
7f1df79b
SP
364proc reshow_diff {} {
365 global ui_fname_value ui_status_value file_states
366
73ad179b
SP
367 if {$ui_fname_value == {}
368 || [catch {set s $file_states($ui_fname_value)}]} {
7f1df79b 369 clear_diff
73ad179b
SP
370 } else {
371 show_diff $ui_fname_value
7f1df79b
SP
372 }
373}
374
03e4ec53
SP
375proc show_diff {path {w {}} {lno {}}} {
376 global file_states file_lists
377 global PARENT diff_3way diff_active
cb07fc2a
SP
378 global ui_diff ui_fname_value ui_fstatus_value ui_status_value
379
e210e674 380 if {$diff_active || ![lock_index read]} return
cb07fc2a
SP
381
382 clear_diff
03e4ec53
SP
383 if {$w == {} || $lno == {}} {
384 foreach w [array names file_lists] {
385 set lno [lsearch -sorted $file_lists($w) $path]
386 if {$lno >= 0} {
387 incr lno
388 break
389 }
390 }
391 }
392 if {$w != {} && $lno >= 1} {
393 $w tag add in_diff $lno.0 [expr $lno + 1].0
394 }
395
cb07fc2a
SP
396 set s $file_states($path)
397 set m [lindex $s 0]
398 set diff_3way 0
399 set diff_active 1
68e009de 400 set ui_fname_value [escape_path $path]
cb07fc2a 401 set ui_fstatus_value [mapdesc $m $path]
68e009de 402 set ui_status_value "Loading diff of [escape_path $path]..."
cb07fc2a 403
e57ca85e 404 set cmd [list | git diff-index -p $PARENT -- $path]
cb07fc2a 405 switch $m {
cb07fc2a 406 MM {
e57ca85e 407 set cmd [list | git diff-index -p -c $PARENT $path]
cb07fc2a
SP
408 }
409 _O {
410 if {[catch {
411 set fd [open $path r]
412 set content [read $fd]
413 close $fd
414 } err ]} {
131f503b 415 set diff_active 0
e210e674 416 unlock_index
68e009de 417 set ui_status_value "Unable to display [escape_path $path]"
44be340e 418 error_popup "Error loading file:\n\n$err"
cb07fc2a
SP
419 return
420 }
421 $ui_diff conf -state normal
422 $ui_diff insert end $content
423 $ui_diff conf -state disabled
bd1e2b40
SP
424 set diff_active 0
425 unlock_index
426 set ui_status_value {Ready.}
cb07fc2a
SP
427 return
428 }
429 }
430
431 if {[catch {set fd [open $cmd r]} err]} {
131f503b 432 set diff_active 0
e210e674 433 unlock_index
68e009de 434 set ui_status_value "Unable to display [escape_path $path]"
44be340e 435 error_popup "Error loading diff:\n\n$err"
cb07fc2a
SP
436 return
437 }
438
6f6eed28 439 fconfigure $fd -blocking 0 -translation auto
cb07fc2a
SP
440 fileevent $fd readable [list read_diff $fd]
441}
442
443proc read_diff {fd} {
444 global ui_diff ui_status_value diff_3way diff_active
445
446 while {[gets $fd line] >= 0} {
6f6eed28
SP
447 if {[string match {diff --git *} $line]} continue
448 if {[string match {diff --combined *} $line]} continue
449 if {[string match {--- *} $line]} continue
450 if {[string match {+++ *} $line]} continue
cb07fc2a
SP
451 if {[string match index* $line]} {
452 if {[string first , $line] >= 0} {
453 set diff_3way 1
454 }
455 }
456
457 $ui_diff conf -state normal
458 if {!$diff_3way} {
459 set x [string index $line 0]
460 switch -- $x {
461 "@" {set tags da}
462 "+" {set tags dp}
463 "-" {set tags dm}
464 default {set tags {}}
465 }
466 } else {
467 set x [string range $line 0 1]
468 switch -- $x {
469 default {set tags {}}
470 "@@" {set tags da}
471 "++" {set tags dp; set x " +"}
472 " +" {set tags {di bold}; set x "++"}
473 "+ " {set tags dni; set x "-+"}
474 "--" {set tags dm; set x " -"}
475 " -" {set tags {dm bold}; set x "--"}
476 "- " {set tags di; set x "+-"}
477 default {set tags {}}
478 }
479 set line [string replace $line 0 1 $x]
480 }
481 $ui_diff insert end $line $tags
482 $ui_diff insert end "\n"
483 $ui_diff conf -state disabled
484 }
485
486 if {[eof $fd]} {
487 close $fd
488 set diff_active 0
e210e674 489 unlock_index
cb07fc2a
SP
490 set ui_status_value {Ready.}
491 }
492}
493
ec6b424a
SP
494######################################################################
495##
496## commit
497
e57ca85e
SP
498proc load_last_commit {} {
499 global HEAD PARENT commit_type ui_comm
500
501 if {$commit_type == {amend}} return
502 if {$commit_type != {normal}} {
503 error_popup "Can't amend a $commit_type commit."
504 return
505 }
506
507 set msg {}
508 set parent {}
509 set parent_count 0
510 if {[catch {
511 set fd [open "| git cat-file commit $HEAD" r]
512 while {[gets $fd line] > 0} {
513 if {[string match {parent *} $line]} {
514 set parent [string range $line 7 end]
515 incr parent_count
516 }
517 }
518 set msg [string trim [read $fd]]
519 close $fd
520 } err]} {
44be340e 521 error_popup "Error loading commit data for amend:\n\n$err"
e57ca85e
SP
522 return
523 }
524
525 if {$parent_count == 0} {
526 set commit_type amend
527 set HEAD {}
528 set PARENT {}
529 update_status
530 } elseif {$parent_count == 1} {
531 set commit_type amend
532 set PARENT $parent
533 $ui_comm delete 0.0 end
534 $ui_comm insert end $msg
535 $ui_comm edit modified false
b2c6fcf1 536 $ui_comm edit reset
e57ca85e
SP
537 update_status
538 } else {
539 error_popup {You can't amend a merge commit.}
540 return
541 }
542}
543
ec6b424a
SP
544proc commit_tree {} {
545 global tcl_platform HEAD gitdir commit_type file_states
546 global commit_active ui_status_value
547 global ui_comm
548
549 if {$commit_active || ![lock_index update]} return
550
551 # -- Our in memory state should match the repository.
552 #
553 repository_state curHEAD cur_type
e57ca85e
SP
554 if {$commit_type == {amend}
555 && $cur_type == {normal}
556 && $curHEAD == $HEAD} {
557 } elseif {$commit_type != $cur_type || $HEAD != $curHEAD} {
ec6b424a
SP
558 error_popup {Last scanned state does not match repository state.
559
560Its highly likely that another Git program modified the
561repository since our last scan. A rescan is required
562before committing.
563}
564 unlock_index
565 update_status
566 return
567 }
568
569 # -- At least one file should differ in the index.
570 #
571 set files_ready 0
572 foreach path [array names file_states] {
573 set s $file_states($path)
574 switch -glob -- [lindex $s 0] {
7f1df79b
SP
575 _? {continue}
576 A? -
577 D? -
578 M? {set files_ready 1; break}
579 U? {
ec6b424a
SP
580 error_popup "Unmerged files cannot be committed.
581
68e009de 582File [escape_path $path] has merge conflicts.
7fe7e733 583You must resolve them and include the file before committing.
ec6b424a
SP
584"
585 unlock_index
586 return
587 }
588 default {
589 error_popup "Unknown file state [lindex $s 0] detected.
590
68e009de 591File [escape_path $path] cannot be committed by this program.
ec6b424a
SP
592"
593 }
594 }
595 }
596 if {!$files_ready} {
7fe7e733 597 error_popup {No included files to commit.
ec6b424a 598
7fe7e733 599You must include at least 1 file before you can commit.
ec6b424a
SP
600}
601 unlock_index
602 return
603 }
604
605 # -- A message is required.
606 #
607 set msg [string trim [$ui_comm get 1.0 end]]
608 if {$msg == {}} {
609 error_popup {Please supply a commit message.
610
611A good commit message has the following format:
612
613- First line: Describe in one sentance what you did.
614- Second line: Blank
615- Remaining lines: Describe why this change is good.
616}
617 unlock_index
618 return
619 }
620
621 # -- Ask the pre-commit hook for the go-ahead.
622 #
623 set pchook [file join $gitdir hooks pre-commit]
e57ca85e 624 if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} {
ec6b424a
SP
625 set pchook [list sh -c \
626 "if test -x \"$pchook\"; then exec \"$pchook\"; fi"]
627 } elseif {[file executable $pchook]} {
628 set pchook [list $pchook]
629 } else {
630 set pchook {}
631 }
632 if {$pchook != {} && [catch {eval exec $pchook} err]} {
633 hook_failed_popup pre-commit $err
634 unlock_index
635 return
636 }
637
638 # -- Write the tree in the background.
639 #
640 set commit_active 1
641 set ui_status_value {Committing changes...}
642
643 set fd_wt [open "| git write-tree" r]
bd1e2b40 644 fileevent $fd_wt readable [list commit_stage2 $fd_wt $curHEAD $msg]
ec6b424a
SP
645}
646
647proc commit_stage2 {fd_wt curHEAD msg} {
7f1df79b 648 global single_commit gitdir HEAD PARENT commit_type
e57ca85e 649 global commit_active ui_status_value ui_comm
7f1df79b 650 global file_states
ec6b424a
SP
651
652 gets $fd_wt tree_id
44be340e
SP
653 if {$tree_id == {} || [catch {close $fd_wt} err]} {
654 error_popup "write-tree failed:\n\n$err"
ec6b424a
SP
655 set commit_active 0
656 set ui_status_value {Commit failed.}
657 unlock_index
658 return
659 }
660
661 # -- Create the commit.
662 #
663 set cmd [list git commit-tree $tree_id]
e57ca85e
SP
664 if {$PARENT != {}} {
665 lappend cmd -p $PARENT
ec6b424a
SP
666 }
667 if {$commit_type == {merge}} {
668 if {[catch {
669 set fd_mh [open [file join $gitdir MERGE_HEAD] r]
bd1e2b40
SP
670 while {[gets $fd_mh merge_head] >= 0} {
671 lappend cmd -p $merge_head
ec6b424a
SP
672 }
673 close $fd_mh
674 } err]} {
44be340e 675 error_popup "Loading MERGE_HEAD failed:\n\n$err"
ec6b424a
SP
676 set commit_active 0
677 set ui_status_value {Commit failed.}
678 unlock_index
679 return
680 }
681 }
e57ca85e 682 if {$PARENT == {}} {
ec6b424a
SP
683 # git commit-tree writes to stderr during initial commit.
684 lappend cmd 2>/dev/null
685 }
686 lappend cmd << $msg
687 if {[catch {set cmt_id [eval exec $cmd]} err]} {
44be340e 688 error_popup "commit-tree failed:\n\n$err"
ec6b424a
SP
689 set commit_active 0
690 set ui_status_value {Commit failed.}
691 unlock_index
692 return
693 }
694
695 # -- Update the HEAD ref.
696 #
697 set reflogm commit
698 if {$commit_type != {normal}} {
699 append reflogm " ($commit_type)"
700 }
701 set i [string first "\n" $msg]
702 if {$i >= 0} {
703 append reflogm {: } [string range $msg 0 [expr $i - 1]]
704 } else {
705 append reflogm {: } $msg
706 }
e57ca85e 707 set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD]
ec6b424a 708 if {[catch {eval exec $cmd} err]} {
44be340e 709 error_popup "update-ref failed:\n\n$err"
ec6b424a
SP
710 set commit_active 0
711 set ui_status_value {Commit failed.}
712 unlock_index
713 return
714 }
715
716 # -- Cleanup after ourselves.
717 #
718 catch {file delete [file join $gitdir MERGE_HEAD]}
719 catch {file delete [file join $gitdir MERGE_MSG]}
720 catch {file delete [file join $gitdir SQUASH_MSG]}
721 catch {file delete [file join $gitdir GITGUI_MSG]}
722
723 # -- Let rerere do its thing.
724 #
725 if {[file isdirectory [file join $gitdir rr-cache]]} {
726 catch {exec git rerere}
727 }
728
e57ca85e
SP
729 $ui_comm delete 0.0 end
730 $ui_comm edit modified false
b2c6fcf1 731 $ui_comm edit reset
ec6b424a
SP
732
733 if {$single_commit} do_quit
734
7f1df79b
SP
735 # -- Update status without invoking any git commands.
736 #
ec6b424a 737 set commit_active 0
7f1df79b 738 set commit_type normal
bd1e2b40
SP
739 set HEAD $cmt_id
740 set PARENT $cmt_id
7f1df79b
SP
741
742 foreach path [array names file_states] {
743 set s $file_states($path)
744 set m [lindex $s 0]
745 switch -glob -- $m {
746 A? -
747 M? -
748 D? {set m _[string index $m 1]}
749 }
750
751 if {$m == {__}} {
752 unset file_states($path)
753 } else {
754 lset file_states($path) 0 $m
755 }
756 }
757
758 display_all_files
ec6b424a 759 unlock_index
7f1df79b
SP
760 reshow_diff
761 set ui_status_value \
762 "Changes committed as [string range $cmt_id 0 7]."
ec6b424a
SP
763}
764
8c0ce436
SP
765######################################################################
766##
767## fetch pull push
768
769proc fetch_from {remote} {
770 set w [new_console "fetch $remote" \
771 "Fetching new changes from $remote"]
cc4b1c02 772 set cmd [list git fetch]
8c0ce436 773 lappend cmd $remote
cc4b1c02 774 console_exec $w $cmd
8c0ce436
SP
775}
776
d33ba5fa 777proc pull_remote {remote branch} {
ec39d83a
SP
778 global HEAD commit_type
779 global file_states
780
988b8a7d 781 if {![lock_index update]} return
ec39d83a
SP
782
783 # -- Our in memory state should match the repository.
784 #
785 repository_state curHEAD cur_type
786 if {$commit_type != $cur_type || $HEAD != $curHEAD} {
787 error_popup {Last scanned state does not match repository state.
788
789Its highly likely that another Git program modified the
790repository since our last scan. A rescan is required
791before a pull can be started.
792}
793 unlock_index
794 update_status
795 return
796 }
797
798 # -- No differences should exist before a pull.
799 #
800 if {[array size file_states] != 0} {
801 error_popup {Uncommitted but modified files are present.
802
803You should not perform a pull with unmodified files in your working
804directory as Git would be unable to recover from an incorrect merge.
805
806Commit or throw away all changes before starting a pull operation.
807}
808 unlock_index
809 return
810 }
811
d33ba5fa
SP
812 set w [new_console "pull $remote $branch" \
813 "Pulling new changes from branch $branch in $remote"]
814 set cmd [list git pull]
815 lappend cmd $remote
816 lappend cmd $branch
817 console_exec $w $cmd [list post_pull_remote $remote $branch]
818}
819
820proc post_pull_remote {remote branch success} {
ec39d83a
SP
821 global HEAD PARENT commit_type
822 global ui_status_value
823
988b8a7d 824 unlock_index
d33ba5fa 825 if {$success} {
ec39d83a
SP
826 repository_state HEAD commit_type
827 set PARENT $HEAD
828 set $ui_status_value {Ready.}
d33ba5fa 829 } else {
390adaea
SP
830 update_status \
831 "Conflicts detected while pulling $branch from $remote."
d33ba5fa
SP
832 }
833}
834
8c0ce436
SP
835proc push_to {remote} {
836 set w [new_console "push $remote" \
837 "Pushing changes to $remote"]
cc4b1c02 838 set cmd [list git push]
8c0ce436 839 lappend cmd $remote
cc4b1c02 840 console_exec $w $cmd
8c0ce436
SP
841}
842
cb07fc2a
SP
843######################################################################
844##
845## ui helpers
846
847proc mapcol {state path} {
6b292675 848 global all_cols ui_other
cb07fc2a
SP
849
850 if {[catch {set r $all_cols($state)}]} {
851 puts "error: no column for state={$state} $path"
6b292675 852 return $ui_other
cb07fc2a
SP
853 }
854 return $r
855}
856
857proc mapicon {state path} {
858 global all_icons
859
860 if {[catch {set r $all_icons($state)}]} {
861 puts "error: no icon for state={$state} $path"
862 return file_plain
863 }
864 return $r
865}
866
867proc mapdesc {state path} {
868 global all_descs
869
870 if {[catch {set r $all_descs($state)}]} {
871 puts "error: no desc for state={$state} $path"
872 return $state
873 }
874 return $r
875}
876
68e009de
SP
877proc escape_path {path} {
878 regsub -all "\n" $path "\\n" path
879 return $path
880}
881
93f654df
SP
882set next_icon_id 0
883
6b292675 884proc merge_state {path new_state} {
93f654df 885 global file_states next_icon_id
cb07fc2a 886
6b292675
SP
887 set s0 [string index $new_state 0]
888 set s1 [string index $new_state 1]
889
890 if {[catch {set info $file_states($path)}]} {
891 set state __
892 set icon n[incr next_icon_id]
cb07fc2a 893 } else {
6b292675
SP
894 set state [lindex $info 0]
895 set icon [lindex $info 1]
cb07fc2a
SP
896 }
897
6b292675
SP
898 if {$s0 == {_}} {
899 set s0 [string index $state 0]
900 } elseif {$s0 == {*}} {
901 set s0 _
cb07fc2a
SP
902 }
903
6b292675
SP
904 if {$s1 == {_}} {
905 set s1 [string index $state 1]
906 } elseif {$s1 == {*}} {
907 set s1 _
cb07fc2a
SP
908 }
909
6b292675
SP
910 set file_states($path) [list $s0$s1 $icon]
911 return $state
cb07fc2a
SP
912}
913
914proc display_file {path state} {
03e4ec53
SP
915 global ui_index ui_other
916 global file_states file_lists status_active
cb07fc2a
SP
917
918 set old_m [merge_state $path $state]
93f654df
SP
919 if {$status_active} return
920
cb07fc2a 921 set s $file_states($path)
93f654df 922 set new_m [lindex $s 0]
0fb8f9ce
SP
923 set new_w [mapcol $new_m $path]
924 set old_w [mapcol $old_m $path]
925 set new_icon [mapicon $new_m $path]
cb07fc2a 926
6b292675 927 if {$new_w != $old_w} {
03e4ec53 928 set lno [lsearch -sorted $file_lists($old_w) $path]
cb07fc2a
SP
929 if {$lno >= 0} {
930 incr lno
93f654df
SP
931 $old_w conf -state normal
932 $old_w delete $lno.0 [expr $lno + 1].0
933 $old_w conf -state disabled
cb07fc2a 934 }
93f654df 935
03e4ec53
SP
936 lappend file_lists($new_w) $path
937 set file_lists($new_w) [lsort $file_lists($new_w)]
938 set lno [lsearch -sorted $file_lists($new_w) $path]
939 incr lno
93f654df
SP
940 $new_w conf -state normal
941 $new_w image create $lno.0 \
942 -align center -padx 5 -pady 1 \
943 -name [lindex $s 1] \
e4ee9af4 944 -image $new_icon
68e009de 945 $new_w insert $lno.1 "[escape_path $path]\n"
93f654df
SP
946 $new_w conf -state disabled
947 } elseif {$new_icon != [mapicon $old_m $path]} {
948 $new_w conf -state normal
949 $new_w image conf [lindex $s 1] -image $new_icon
950 $new_w conf -state disabled
cb07fc2a 951 }
93f654df 952}
cb07fc2a 953
93f654df 954proc display_all_files {} {
03e4ec53 955 global ui_index ui_other file_states file_lists
93f654df
SP
956
957 $ui_index conf -state normal
958 $ui_other conf -state normal
959
7f1df79b
SP
960 $ui_index delete 0.0 end
961 $ui_other delete 0.0 end
962
62aac80b
SP
963 set file_lists($ui_index) [list]
964 set file_lists($ui_other) [list]
965
93f654df
SP
966 foreach path [lsort [array names file_states]] {
967 set s $file_states($path)
968 set m [lindex $s 0]
6b292675 969 set w [mapcol $m $path]
03e4ec53 970 lappend file_lists($w) $path
6b292675 971 $w image create end \
cb07fc2a 972 -align center -padx 5 -pady 1 \
93f654df
SP
973 -name [lindex $s 1] \
974 -image [mapicon $m $path]
68e009de 975 $w insert end "[escape_path $path]\n"
cb07fc2a 976 }
93f654df
SP
977
978 $ui_index conf -state disabled
979 $ui_other conf -state disabled
cb07fc2a
SP
980}
981
131f503b
SP
982proc with_update_index {body} {
983 global update_index_fd
984
985 if {$update_index_fd == {}} {
e210e674 986 if {![lock_index update]} return
131f503b
SP
987 set update_index_fd [open \
988 "| git update-index --add --remove -z --stdin" \
989 w]
990 fconfigure $update_index_fd -translation binary
991 uplevel 1 $body
992 close $update_index_fd
993 set update_index_fd {}
e210e674 994 unlock_index
131f503b
SP
995 } else {
996 uplevel 1 $body
997 }
998}
999
1000proc update_index {path} {
1001 global update_index_fd
1002
1003 if {$update_index_fd == {}} {
1004 error {not in with_update_index}
1005 } else {
1006 puts -nonewline $update_index_fd "$path\0"
1007 }
1008}
1009
cb07fc2a 1010proc toggle_mode {path} {
bd1e2b40 1011 global file_states ui_fname_value
cb07fc2a
SP
1012
1013 set s $file_states($path)
1014 set m [lindex $s 0]
1015
1016 switch -- $m {
1017 AM -
131f503b
SP
1018 _O {set new A*}
1019 _M -
1020 MM {set new M*}
bd1e2b40 1021 AD -
131f503b
SP
1022 _D {set new D*}
1023 default {return}
cb07fc2a
SP
1024 }
1025
131f503b 1026 with_update_index {update_index $path}
cb07fc2a 1027 display_file $path $new
bd1e2b40
SP
1028 if {$ui_fname_value == $path} {
1029 show_diff $path
1030 }
cb07fc2a
SP
1031}
1032
8c0ce436
SP
1033######################################################################
1034##
2d19516d 1035## remote management
0d4f3eb5 1036
8c0ce436 1037proc load_all_remotes {} {
0d4f3eb5 1038 global gitdir all_remotes repo_config
8c0ce436
SP
1039
1040 set all_remotes [list]
1041 set rm_dir [file join $gitdir remotes]
1042 if {[file isdirectory $rm_dir]} {
d47ae541
SP
1043 set all_remotes [concat $all_remotes [glob \
1044 -types f \
1045 -tails \
1046 -nocomplain \
1047 -directory $rm_dir *]]
8c0ce436
SP
1048 }
1049
0d4f3eb5
SP
1050 foreach line [array names repo_config remote.*.url] {
1051 if {[regexp ^remote\.(.*)\.url\$ $line line name]} {
8c0ce436
SP
1052 lappend all_remotes $name
1053 }
1054 }
8c0ce436
SP
1055
1056 set all_remotes [lsort -unique $all_remotes]
1057}
1058
1059proc populate_remote_menu {m pfx op} {
3963678d 1060 global all_remotes font_ui
8c0ce436
SP
1061
1062 foreach remote $all_remotes {
1063 $m add command -label "$pfx $remote..." \
1064 -command [list $op $remote] \
3963678d 1065 -font $font_ui
8c0ce436
SP
1066 }
1067}
1068
d33ba5fa 1069proc populate_pull_menu {m} {
3963678d 1070 global gitdir repo_config all_remotes font_ui disable_on_lock
d33ba5fa
SP
1071
1072 foreach remote $all_remotes {
1073 set rb {}
1074 if {[array get repo_config remote.$remote.url] != {}} {
1075 if {[array get repo_config remote.$remote.fetch] != {}} {
1076 regexp {^([^:]+):} \
1077 [lindex $repo_config(remote.$remote.fetch) 0] \
1078 line rb
1079 }
1080 } else {
1081 catch {
1082 set fd [open [file join $gitdir remotes $remote] r]
1083 while {[gets $fd line] >= 0} {
1084 if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} {
1085 break
1086 }
1087 }
1088 close $fd
1089 }
1090 }
1091
1092 set rb_short $rb
1093 regsub ^refs/heads/ $rb {} rb_short
1094 if {$rb_short != {}} {
1095 $m add command \
1096 -label "Branch $rb_short from $remote..." \
1097 -command [list pull_remote $remote $rb] \
3963678d 1098 -font $font_ui
0a462d67
SP
1099 lappend disable_on_lock \
1100 [list $m entryconf [$m index last] -state]
d33ba5fa
SP
1101 }
1102 }
1103}
1104
cb07fc2a
SP
1105######################################################################
1106##
1107## icons
1108
1109set filemask {
1110#define mask_width 14
1111#define mask_height 15
1112static unsigned char mask_bits[] = {
1113 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1114 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1115 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1116}
1117
1118image create bitmap file_plain -background white -foreground black -data {
1119#define plain_width 14
1120#define plain_height 15
1121static unsigned char plain_bits[] = {
1122 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1123 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1124 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1125} -maskdata $filemask
1126
1127image create bitmap file_mod -background white -foreground blue -data {
1128#define mod_width 14
1129#define mod_height 15
1130static unsigned char mod_bits[] = {
1131 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1132 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1133 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1134} -maskdata $filemask
1135
131f503b
SP
1136image create bitmap file_fulltick -background white -foreground "#007000" -data {
1137#define file_fulltick_width 14
1138#define file_fulltick_height 15
1139static unsigned char file_fulltick_bits[] = {
cb07fc2a
SP
1140 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1141 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1142 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1143} -maskdata $filemask
1144
1145image create bitmap file_parttick -background white -foreground "#005050" -data {
1146#define parttick_width 14
1147#define parttick_height 15
1148static unsigned char parttick_bits[] = {
1149 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1150 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1151 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1152} -maskdata $filemask
1153
1154image create bitmap file_question -background white -foreground black -data {
1155#define file_question_width 14
1156#define file_question_height 15
1157static unsigned char file_question_bits[] = {
1158 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1159 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1160 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1161} -maskdata $filemask
1162
1163image create bitmap file_removed -background white -foreground red -data {
1164#define file_removed_width 14
1165#define file_removed_height 15
1166static unsigned char file_removed_bits[] = {
1167 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1168 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1169 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1170} -maskdata $filemask
1171
1172image create bitmap file_merge -background white -foreground blue -data {
1173#define file_merge_width 14
1174#define file_merge_height 15
1175static unsigned char file_merge_bits[] = {
1176 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1177 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1178 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1179} -maskdata $filemask
1180
6b292675
SP
1181set ui_index .vpane.files.index.list
1182set ui_other .vpane.files.other.list
131f503b 1183set max_status_desc 0
cb07fc2a 1184foreach i {
131f503b
SP
1185 {__ i plain "Unmodified"}
1186 {_M i mod "Modified"}
1187 {M_ i fulltick "Checked in"}
7fe7e733 1188 {MM i parttick "Partially included"}
131f503b
SP
1189
1190 {_O o plain "Untracked"}
1191 {A_ o fulltick "Added"}
1192 {AM o parttick "Partially added"}
6b292675 1193 {AD o question "Added (but now gone)"}
131f503b
SP
1194
1195 {_D i question "Missing"}
1196 {D_ i removed "Removed"}
1197 {DD i removed "Removed"}
1198 {DO i removed "Removed (still exists)"}
1199
1200 {UM i merge "Merge conflicts"}
1201 {U_ i merge "Merge conflicts"}
cb07fc2a 1202 } {
131f503b
SP
1203 if {$max_status_desc < [string length [lindex $i 3]]} {
1204 set max_status_desc [string length [lindex $i 3]]
1205 }
6b292675
SP
1206 if {[lindex $i 1] == {i}} {
1207 set all_cols([lindex $i 0]) $ui_index
1208 } else {
1209 set all_cols([lindex $i 0]) $ui_other
1210 }
131f503b
SP
1211 set all_icons([lindex $i 0]) file_[lindex $i 2]
1212 set all_descs([lindex $i 0]) [lindex $i 3]
cb07fc2a
SP
1213}
1214unset filemask i
1215
1216######################################################################
1217##
1218## util
1219
6e27d826 1220proc hook_failed_popup {hook msg} {
3963678d 1221 global gitdir font_ui font_diff appname
6e27d826
SP
1222
1223 set w .hookfail
1224 toplevel $w
1225 wm transient $w .
1226
1227 frame $w.m
1228 label $w.m.l1 -text "$hook hook failed:" \
1229 -anchor w \
1230 -justify left \
3963678d 1231 -font [concat $font_ui bold]
6e27d826
SP
1232 text $w.m.t \
1233 -background white -borderwidth 1 \
1234 -relief sunken \
1235 -width 80 -height 10 \
3963678d 1236 -font $font_diff \
6e27d826
SP
1237 -yscrollcommand [list $w.m.sby set]
1238 label $w.m.l2 \
1239 -text {You must correct the above errors before committing.} \
1240 -anchor w \
1241 -justify left \
3963678d 1242 -font [concat $font_ui bold]
6e27d826
SP
1243 scrollbar $w.m.sby -command [list $w.m.t yview]
1244 pack $w.m.l1 -side top -fill x
1245 pack $w.m.l2 -side bottom -fill x
1246 pack $w.m.sby -side right -fill y
1247 pack $w.m.t -side left -fill both -expand 1
1248 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1249
1250 $w.m.t insert 1.0 $msg
1251 $w.m.t conf -state disabled
1252
1253 button $w.ok -text OK \
1254 -width 15 \
3963678d 1255 -font $font_ui \
6e27d826
SP
1256 -command "destroy $w"
1257 pack $w.ok -side bottom
1258
1259 bind $w <Visibility> "grab $w; focus $w"
1260 bind $w <Key-Return> "destroy $w"
d33ba5fa
SP
1261 wm title $w "$appname ([lindex [file split \
1262 [file normalize [file dirname $gitdir]]] \
1263 end]): error"
6e27d826
SP
1264 tkwait window $w
1265}
1266
8c0ce436
SP
1267set next_console_id 0
1268
1269proc new_console {short_title long_title} {
37af79d1
SP
1270 global next_console_id console_data
1271 set w .console[incr next_console_id]
1272 set console_data($w) [list $short_title $long_title]
1273 return [console_init $w]
1274}
1275
1276proc console_init {w} {
1277 global console_cr console_data
62aac80b 1278 global gitdir appname font_ui font_diff M1B
8c0ce436 1279
ee3dc935 1280 set console_cr($w) 1.0
8c0ce436
SP
1281 toplevel $w
1282 frame $w.m
37af79d1 1283 label $w.m.l1 -text "[lindex $console_data($w) 1]:" \
8c0ce436
SP
1284 -anchor w \
1285 -justify left \
3963678d 1286 -font [concat $font_ui bold]
8c0ce436
SP
1287 text $w.m.t \
1288 -background white -borderwidth 1 \
1289 -relief sunken \
1290 -width 80 -height 10 \
3963678d 1291 -font $font_diff \
8c0ce436
SP
1292 -state disabled \
1293 -yscrollcommand [list $w.m.sby set]
07123f40
SP
1294 label $w.m.s -anchor w \
1295 -justify left \
3963678d 1296 -font [concat $font_ui bold]
8c0ce436
SP
1297 scrollbar $w.m.sby -command [list $w.m.t yview]
1298 pack $w.m.l1 -side top -fill x
07123f40 1299 pack $w.m.s -side bottom -fill x
8c0ce436
SP
1300 pack $w.m.sby -side right -fill y
1301 pack $w.m.t -side left -fill both -expand 1
1302 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1303
ee3dc935 1304 button $w.ok -text {Running...} \
8c0ce436 1305 -width 15 \
3963678d 1306 -font $font_ui \
8c0ce436
SP
1307 -state disabled \
1308 -command "destroy $w"
1309 pack $w.ok -side bottom
1310
62aac80b
SP
1311 bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
1312 bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
8c0ce436 1313 bind $w <Visibility> "focus $w"
d33ba5fa
SP
1314 wm title $w "$appname ([lindex [file split \
1315 [file normalize [file dirname $gitdir]]] \
1316 end]): [lindex $console_data($w) 0]"
8c0ce436
SP
1317 return $w
1318}
1319
d33ba5fa 1320proc console_exec {w cmd {after {}}} {
cc4b1c02
SP
1321 global tcl_platform
1322
1323 # -- Windows tosses the enviroment when we exec our child.
1324 # But most users need that so we have to relogin. :-(
1325 #
1326 if {$tcl_platform(platform) == {windows}} {
1327 set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"]
1328 }
1329
1330 # -- Tcl won't let us redirect both stdout and stderr to
1331 # the same pipe. So pass it through cat...
1332 #
1333 set cmd [concat | $cmd |& cat]
1334
1335 set fd_f [open $cmd r]
ee3dc935 1336 fconfigure $fd_f -blocking 0 -translation binary
d33ba5fa 1337 fileevent $fd_f readable [list console_read $w $fd_f $after]
cc4b1c02
SP
1338}
1339
d33ba5fa 1340proc console_read {w fd after} {
37af79d1 1341 global console_cr console_data
ee3dc935 1342
ee3dc935 1343 set buf [read $fd]
37af79d1
SP
1344 if {$buf != {}} {
1345 if {![winfo exists $w]} {console_init $w}
1346 $w.m.t conf -state normal
1347 set c 0
1348 set n [string length $buf]
1349 while {$c < $n} {
1350 set cr [string first "\r" $buf $c]
1351 set lf [string first "\n" $buf $c]
1352 if {$cr < 0} {set cr [expr $n + 1]}
1353 if {$lf < 0} {set lf [expr $n + 1]}
1354
1355 if {$lf < $cr} {
1356 $w.m.t insert end [string range $buf $c $lf]
1357 set console_cr($w) [$w.m.t index {end -1c}]
1358 set c $lf
1359 incr c
1360 } else {
1361 $w.m.t delete $console_cr($w) end
1362 $w.m.t insert end "\n"
1363 $w.m.t insert end [string range $buf $c $cr]
1364 set c $cr
1365 incr c
1366 }
ee3dc935 1367 }
37af79d1
SP
1368 $w.m.t conf -state disabled
1369 $w.m.t see end
8c0ce436 1370 }
8c0ce436 1371
07123f40 1372 fconfigure $fd -blocking 1
8c0ce436 1373 if {[eof $fd]} {
07123f40 1374 if {[catch {close $fd}]} {
37af79d1 1375 if {![winfo exists $w]} {console_init $w}
07123f40 1376 $w.m.s conf -background red -text {Error: Command Failed}
37af79d1
SP
1377 $w.ok conf -text Close
1378 $w.ok conf -state normal
d33ba5fa 1379 set ok 0
37af79d1 1380 } elseif {[winfo exists $w]} {
07123f40 1381 $w.m.s conf -background green -text {Success}
37af79d1
SP
1382 $w.ok conf -text Close
1383 $w.ok conf -state normal
d33ba5fa 1384 set ok 1
07123f40 1385 }
ee3dc935 1386 array unset console_cr $w
37af79d1 1387 array unset console_data $w
d33ba5fa
SP
1388 if {$after != {}} {
1389 uplevel #0 $after $ok
1390 }
07123f40 1391 return
8c0ce436 1392 }
07123f40 1393 fconfigure $fd -blocking 0
8c0ce436
SP
1394}
1395
cb07fc2a
SP
1396######################################################################
1397##
1398## ui commands
1399
e210e674 1400set starting_gitk_msg {Please wait... Starting gitk...}
cc4b1c02 1401
cb07fc2a 1402proc do_gitk {} {
e210e674
SP
1403 global tcl_platform ui_status_value starting_gitk_msg
1404
1405 set ui_status_value $starting_gitk_msg
e57ca85e 1406 after 10000 {
e210e674
SP
1407 if {$ui_status_value == $starting_gitk_msg} {
1408 set ui_status_value {Ready.}
1409 }
1410 }
cb07fc2a 1411
cc4b1c02 1412 if {$tcl_platform(platform) == {windows}} {
cb07fc2a
SP
1413 exec sh -c gitk &
1414 } else {
1415 exec gitk &
1416 }
1417}
1418
d1536c48
SP
1419proc do_repack {} {
1420 set w [new_console "repack" "Repacking the object database"]
1421 set cmd [list git repack]
1422 lappend cmd -a
1423 lappend cmd -d
1424 console_exec $w $cmd
1425}
1426
c4fe7728
SP
1427set quitting 0
1428
cb07fc2a 1429proc do_quit {} {
c4fe7728
SP
1430 global gitdir ui_comm quitting
1431
1432 if {$quitting} return
1433 set quitting 1
131f503b
SP
1434
1435 set save [file join $gitdir GITGUI_MSG]
ec6b424a
SP
1436 set msg [string trim [$ui_comm get 0.0 end]]
1437 if {[$ui_comm edit modified] && $msg != {}} {
131f503b
SP
1438 catch {
1439 set fd [open $save w]
1440 puts $fd [string trim [$ui_comm get 0.0 end]]
1441 close $fd
1442 }
ec6b424a 1443 } elseif {$msg == {} && [file exists $save]} {
131f503b
SP
1444 file delete $save
1445 }
1446
e534f3a8 1447 save_my_config
cb07fc2a
SP
1448 destroy .
1449}
1450
1451proc do_rescan {} {
1452 update_status
1453}
1454
7fe7e733
SP
1455proc do_include_all {} {
1456 global update_active ui_status_value
131f503b 1457
7fe7e733 1458 if {$update_active || ![lock_index begin-update]} return
131f503b 1459
7fe7e733
SP
1460 set update_active 1
1461 set ui_status_value {Including all modified files...}
131f503b
SP
1462 after 1 {
1463 with_update_index {
1464 foreach path [array names file_states] {
1465 set s $file_states($path)
1466 set m [lindex $s 0]
1467 switch -- $m {
1468 AM -
1469 MM -
1470 _M -
1471 _D {toggle_mode $path}
1472 }
1473 }
1474 }
7fe7e733 1475 set update_active 0
131f503b
SP
1476 set ui_status_value {Ready.}
1477 }
1478}
1479
da5239dc
SP
1480set GIT_COMMITTER_IDENT {}
1481
131f503b 1482proc do_signoff {} {
97bf01c4 1483 global ui_comm GIT_COMMITTER_IDENT
131f503b 1484
97bf01c4
SP
1485 if {$GIT_COMMITTER_IDENT == {}} {
1486 if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} {
44be340e 1487 error_popup "Unable to obtain your identity:\n\n$err"
97bf01c4 1488 return
131f503b 1489 }
97bf01c4
SP
1490 if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \
1491 $me me GIT_COMMITTER_IDENT]} {
44be340e 1492 error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me"
97bf01c4
SP
1493 return
1494 }
1495 }
1496
1497 set str "Signed-off-by: $GIT_COMMITTER_IDENT"
1498 if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
b2c6fcf1
SP
1499 $ui_comm edit separator
1500 $ui_comm insert end "\n$str"
1501 $ui_comm edit separator
97bf01c4 1502 $ui_comm see end
131f503b
SP
1503 }
1504}
1505
e57ca85e
SP
1506proc do_amend_last {} {
1507 load_last_commit
1508}
1509
6e27d826 1510proc do_commit {} {
ec6b424a 1511 commit_tree
6e27d826
SP
1512}
1513
cb07fc2a
SP
1514# shift == 1: left click
1515# 3: right click
1516proc click {w x y shift wx wy} {
03e4ec53 1517 global ui_index ui_other file_lists
131f503b 1518
cb07fc2a
SP
1519 set pos [split [$w index @$x,$y] .]
1520 set lno [lindex $pos 0]
1521 set col [lindex $pos 1]
03e4ec53 1522 set path [lindex $file_lists($w) [expr $lno - 1]]
cb07fc2a
SP
1523 if {$path == {}} return
1524
1525 if {$col > 0 && $shift == 1} {
03e4ec53 1526 show_diff $path $w $lno
cb07fc2a
SP
1527 }
1528}
1529
1530proc unclick {w x y} {
7f1df79b
SP
1531 global file_lists
1532
cb07fc2a
SP
1533 set pos [split [$w index @$x,$y] .]
1534 set lno [lindex $pos 0]
1535 set col [lindex $pos 1]
7f1df79b 1536 set path [lindex $file_lists($w) [expr $lno - 1]]
cb07fc2a
SP
1537 if {$path == {}} return
1538
e210e674 1539 if {$col == 0} {
cb07fc2a
SP
1540 toggle_mode $path
1541 }
1542}
1543
1544######################################################################
1545##
1546## ui init
1547
3963678d
SP
1548set font_ui {Helvetica 10}
1549set font_diff {Courier 10}
cb07fc2a
SP
1550set maincursor [. cget -cursor]
1551
66144892
SP
1552switch -glob -- "$tcl_platform(platform),$tcl_platform(os)" {
1553windows,* {set M1B Control; set M1T Ctrl}
1554unix,Darwin {set M1B M1; set M1T Cmd}
390adaea 1555* {set M1B M1; set M1T M1}
e210e674
SP
1556}
1557
cb07fc2a
SP
1558# -- Menu Bar
1559menu .mbar -tearoff 0
1560.mbar add cascade -label Project -menu .mbar.project
9861671d 1561.mbar add cascade -label Edit -menu .mbar.edit
cb07fc2a
SP
1562.mbar add cascade -label Commit -menu .mbar.commit
1563.mbar add cascade -label Fetch -menu .mbar.fetch
1564.mbar add cascade -label Pull -menu .mbar.pull
8c0ce436 1565.mbar add cascade -label Push -menu .mbar.push
e534f3a8 1566.mbar add cascade -label Options -menu .mbar.options
cb07fc2a
SP
1567. configure -menu .mbar
1568
1569# -- Project Menu
1570menu .mbar.project
6f6eed28 1571.mbar.project add command -label Visualize \
cb07fc2a 1572 -command do_gitk \
3963678d 1573 -font $font_ui
d1536c48
SP
1574.mbar.project add command -label {Repack Database} \
1575 -command do_repack \
3963678d 1576 -font $font_ui
cb07fc2a
SP
1577.mbar.project add command -label Quit \
1578 -command do_quit \
e210e674 1579 -accelerator $M1T-Q \
3963678d 1580 -font $font_ui
cb07fc2a 1581
9861671d
SP
1582# -- Edit Menu
1583#
1584menu .mbar.edit
1585.mbar.edit add command -label Undo \
1586 -command {catch {[focus] edit undo}} \
1587 -accelerator $M1T-Z \
3963678d 1588 -font $font_ui
9861671d
SP
1589.mbar.edit add command -label Redo \
1590 -command {catch {[focus] edit redo}} \
1591 -accelerator $M1T-Y \
3963678d 1592 -font $font_ui
9861671d
SP
1593.mbar.edit add separator
1594.mbar.edit add command -label Cut \
1595 -command {catch {tk_textCut [focus]}} \
1596 -accelerator $M1T-X \
3963678d 1597 -font $font_ui
9861671d
SP
1598.mbar.edit add command -label Copy \
1599 -command {catch {tk_textCopy [focus]}} \
1600 -accelerator $M1T-C \
3963678d 1601 -font $font_ui
9861671d
SP
1602.mbar.edit add command -label Paste \
1603 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
1604 -accelerator $M1T-V \
3963678d 1605 -font $font_ui
9861671d
SP
1606.mbar.edit add command -label Delete \
1607 -command {catch {[focus] delete sel.first sel.last}} \
1608 -accelerator Del \
3963678d 1609 -font $font_ui
9861671d
SP
1610.mbar.edit add separator
1611.mbar.edit add command -label {Select All} \
1612 -command {catch {[focus] tag add sel 0.0 end}} \
1613 -accelerator $M1T-A \
3963678d 1614 -font $font_ui
9861671d 1615
cb07fc2a
SP
1616# -- Commit Menu
1617menu .mbar.commit
1618.mbar.commit add command -label Rescan \
1619 -command do_rescan \
e210e674 1620 -accelerator F5 \
3963678d 1621 -font $font_ui
e210e674
SP
1622lappend disable_on_lock \
1623 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e57ca85e
SP
1624.mbar.commit add command -label {Amend Last Commit} \
1625 -command do_amend_last \
3963678d 1626 -font $font_ui
e57ca85e
SP
1627lappend disable_on_lock \
1628 [list .mbar.commit entryconf [.mbar.commit index last] -state]
7fe7e733
SP
1629.mbar.commit add command -label {Include All Files} \
1630 -command do_include_all \
49b86f01 1631 -accelerator $M1T-I \
3963678d 1632 -font $font_ui
e210e674
SP
1633lappend disable_on_lock \
1634 [list .mbar.commit entryconf [.mbar.commit index last] -state]
131f503b
SP
1635.mbar.commit add command -label {Sign Off} \
1636 -command do_signoff \
e210e674 1637 -accelerator $M1T-S \
3963678d 1638 -font $font_ui
131f503b
SP
1639.mbar.commit add command -label Commit \
1640 -command do_commit \
e210e674 1641 -accelerator $M1T-Return \
3963678d 1642 -font $font_ui
e210e674
SP
1643lappend disable_on_lock \
1644 [list .mbar.commit entryconf [.mbar.commit index last] -state]
cb07fc2a
SP
1645
1646# -- Fetch Menu
1647menu .mbar.fetch
1648
1649# -- Pull Menu
1650menu .mbar.pull
1651
8c0ce436
SP
1652# -- Push Menu
1653menu .mbar.push
1654
e534f3a8
SP
1655# -- Options Menu
1656menu .mbar.options
9861671d
SP
1657.mbar.options add checkbutton \
1658 -label {Trust File Modification Timestamps} \
e534f3a8
SP
1659 -offvalue false \
1660 -onvalue true \
1661 -variable cfg_trust_mtime
1662
cb07fc2a
SP
1663# -- Main Window Layout
1664panedwindow .vpane -orient vertical
1665panedwindow .vpane.files -orient horizontal
6f6eed28 1666.vpane add .vpane.files -sticky nsew -height 100 -width 400
cb07fc2a
SP
1667pack .vpane -anchor n -side top -fill both -expand 1
1668
1669# -- Index File List
cb07fc2a
SP
1670frame .vpane.files.index -height 100 -width 400
1671label .vpane.files.index.title -text {Modified Files} \
1672 -background green \
3963678d 1673 -font $font_ui
cb07fc2a
SP
1674text $ui_index -background white -borderwidth 0 \
1675 -width 40 -height 10 \
3963678d 1676 -font $font_ui \
cb07fc2a
SP
1677 -yscrollcommand {.vpane.files.index.sb set} \
1678 -cursor $maincursor \
1679 -state disabled
1680scrollbar .vpane.files.index.sb -command [list $ui_index yview]
1681pack .vpane.files.index.title -side top -fill x
1682pack .vpane.files.index.sb -side right -fill y
1683pack $ui_index -side left -fill both -expand 1
1684.vpane.files add .vpane.files.index -sticky nsew
1685
1686# -- Other (Add) File List
cb07fc2a
SP
1687frame .vpane.files.other -height 100 -width 100
1688label .vpane.files.other.title -text {Untracked Files} \
1689 -background red \
3963678d 1690 -font $font_ui
cb07fc2a
SP
1691text $ui_other -background white -borderwidth 0 \
1692 -width 40 -height 10 \
3963678d 1693 -font $font_ui \
cb07fc2a
SP
1694 -yscrollcommand {.vpane.files.other.sb set} \
1695 -cursor $maincursor \
1696 -state disabled
1697scrollbar .vpane.files.other.sb -command [list $ui_other yview]
1698pack .vpane.files.other.title -side top -fill x
1699pack .vpane.files.other.sb -side right -fill y
1700pack $ui_other -side left -fill both -expand 1
1701.vpane.files add .vpane.files.other -sticky nsew
1702
3963678d
SP
1703$ui_index tag conf in_diff -font [concat $font_ui bold]
1704$ui_other tag conf in_diff -font [concat $font_ui bold]
131f503b 1705
0fb8f9ce
SP
1706# -- Diff and Commit Area
1707frame .vpane.lower -height 400 -width 400
1708frame .vpane.lower.commarea
1709frame .vpane.lower.diff -relief sunken -borderwidth 1
1710pack .vpane.lower.commarea -side top -fill x
1711pack .vpane.lower.diff -side bottom -fill both -expand 1
1712.vpane add .vpane.lower -stick nsew
cb07fc2a
SP
1713
1714# -- Commit Area Buttons
0fb8f9ce
SP
1715frame .vpane.lower.commarea.buttons
1716label .vpane.lower.commarea.buttons.l -text {} \
cb07fc2a
SP
1717 -anchor w \
1718 -justify left \
3963678d 1719 -font $font_ui
0fb8f9ce
SP
1720pack .vpane.lower.commarea.buttons.l -side top -fill x
1721pack .vpane.lower.commarea.buttons -side left -fill y
131f503b 1722
0fb8f9ce 1723button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
cb07fc2a 1724 -command do_rescan \
3963678d 1725 -font $font_ui
0fb8f9ce 1726pack .vpane.lower.commarea.buttons.rescan -side top -fill x
390adaea
SP
1727lappend disable_on_lock \
1728 {.vpane.lower.commarea.buttons.rescan conf -state}
131f503b 1729
0fb8f9ce 1730button .vpane.lower.commarea.buttons.amend -text {Amend Last} \
e57ca85e 1731 -command do_amend_last \
3963678d 1732 -font $font_ui
0fb8f9ce 1733pack .vpane.lower.commarea.buttons.amend -side top -fill x
390adaea
SP
1734lappend disable_on_lock \
1735 {.vpane.lower.commarea.buttons.amend conf -state}
e57ca85e 1736
7fe7e733
SP
1737button .vpane.lower.commarea.buttons.incall -text {Include All} \
1738 -command do_include_all \
3963678d 1739 -font $font_ui
7fe7e733 1740pack .vpane.lower.commarea.buttons.incall -side top -fill x
390adaea
SP
1741lappend disable_on_lock \
1742 {.vpane.lower.commarea.buttons.incall conf -state}
131f503b 1743
0fb8f9ce 1744button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
131f503b 1745 -command do_signoff \
3963678d 1746 -font $font_ui
0fb8f9ce 1747pack .vpane.lower.commarea.buttons.signoff -side top -fill x
131f503b 1748
0fb8f9ce 1749button .vpane.lower.commarea.buttons.commit -text {Commit} \
cb07fc2a 1750 -command do_commit \
3963678d 1751 -font $font_ui
0fb8f9ce 1752pack .vpane.lower.commarea.buttons.commit -side top -fill x
390adaea
SP
1753lappend disable_on_lock \
1754 {.vpane.lower.commarea.buttons.commit conf -state}
cb07fc2a
SP
1755
1756# -- Commit Message Buffer
0fb8f9ce
SP
1757frame .vpane.lower.commarea.buffer
1758set ui_comm .vpane.lower.commarea.buffer.t
1759set ui_coml .vpane.lower.commarea.buffer.l
bd1e2b40 1760label $ui_coml -text {Commit Message:} \
cb07fc2a
SP
1761 -anchor w \
1762 -justify left \
3963678d 1763 -font $font_ui
bd1e2b40
SP
1764trace add variable commit_type write {uplevel #0 {
1765 switch -glob $commit_type \
1766 initial {$ui_coml conf -text {Initial Commit Message:}} \
1767 amend {$ui_coml conf -text {Amended Commit Message:}} \
1768 merge {$ui_coml conf -text {Merge Commit Message:}} \
1769 * {$ui_coml conf -text {Commit Message:}}
1770}}
cb07fc2a 1771text $ui_comm -background white -borderwidth 1 \
9861671d 1772 -undo true \
b2c6fcf1 1773 -maxundo 20 \
9861671d 1774 -autoseparators true \
cb07fc2a 1775 -relief sunken \
0fb8f9ce 1776 -width 75 -height 9 -wrap none \
3963678d 1777 -font $font_diff \
0fb8f9ce 1778 -yscrollcommand {.vpane.lower.commarea.buffer.sby set} \
cb07fc2a 1779 -cursor $maincursor
390adaea
SP
1780scrollbar .vpane.lower.commarea.buffer.sby \
1781 -command [list $ui_comm yview]
bd1e2b40 1782pack $ui_coml -side top -fill x
0fb8f9ce 1783pack .vpane.lower.commarea.buffer.sby -side right -fill y
cb07fc2a 1784pack $ui_comm -side left -fill y
0fb8f9ce
SP
1785pack .vpane.lower.commarea.buffer -side left -fill y
1786
1787# -- Diff Header
1788set ui_fname_value {}
1789set ui_fstatus_value {}
1790frame .vpane.lower.diff.header -background orange
1791label .vpane.lower.diff.header.l1 -text {File:} \
1792 -background orange \
3963678d 1793 -font $font_ui
0fb8f9ce
SP
1794label .vpane.lower.diff.header.l2 -textvariable ui_fname_value \
1795 -background orange \
1796 -anchor w \
1797 -justify left \
3963678d 1798 -font $font_ui
0fb8f9ce
SP
1799label .vpane.lower.diff.header.l3 -text {Status:} \
1800 -background orange \
3963678d 1801 -font $font_ui
0fb8f9ce
SP
1802label .vpane.lower.diff.header.l4 -textvariable ui_fstatus_value \
1803 -background orange \
1804 -width $max_status_desc \
1805 -anchor w \
1806 -justify left \
3963678d 1807 -font $font_ui
0fb8f9ce
SP
1808pack .vpane.lower.diff.header.l1 -side left
1809pack .vpane.lower.diff.header.l2 -side left -fill x
1810pack .vpane.lower.diff.header.l4 -side right
1811pack .vpane.lower.diff.header.l3 -side right
1812
1813# -- Diff Body
1814frame .vpane.lower.diff.body
1815set ui_diff .vpane.lower.diff.body.t
1816text $ui_diff -background white -borderwidth 0 \
1817 -width 80 -height 15 -wrap none \
3963678d 1818 -font $font_diff \
0fb8f9ce
SP
1819 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
1820 -yscrollcommand {.vpane.lower.diff.body.sby set} \
1821 -cursor $maincursor \
1822 -state disabled
1823scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
1824 -command [list $ui_diff xview]
1825scrollbar .vpane.lower.diff.body.sby -orient vertical \
1826 -command [list $ui_diff yview]
1827pack .vpane.lower.diff.body.sbx -side bottom -fill x
1828pack .vpane.lower.diff.body.sby -side right -fill y
1829pack $ui_diff -side left -fill both -expand 1
1830pack .vpane.lower.diff.header -side top -fill x
1831pack .vpane.lower.diff.body -side bottom -fill both -expand 1
1832
1833$ui_diff tag conf dm -foreground red
1834$ui_diff tag conf dp -foreground blue
3963678d 1835$ui_diff tag conf da -font [concat $font_diff bold]
0fb8f9ce
SP
1836$ui_diff tag conf di -foreground "#00a000"
1837$ui_diff tag conf dni -foreground "#a000a0"
3963678d 1838$ui_diff tag conf bold -font [concat $font_diff bold]
cb07fc2a
SP
1839
1840# -- Status Bar
1841set ui_status_value {Initializing...}
1842label .status -textvariable ui_status_value \
1843 -anchor w \
1844 -justify left \
1845 -borderwidth 1 \
1846 -relief sunken \
3963678d 1847 -font $font_ui
cb07fc2a
SP
1848pack .status -anchor w -side bottom -fill x
1849
2d19516d
SP
1850# -- Load geometry
1851catch {
c4fe7728
SP
1852set gm [lindex $repo_config(gui.geometry) 0]
1853wm geometry . [lindex $gm 0]
1854.vpane sash place 0 \
1855 [lindex [.vpane sash coord 0] 0] \
1856 [lindex $gm 1]
1857.vpane.files sash place 0 \
1858 [lindex $gm 2] \
1859 [lindex [.vpane.files sash coord 0] 1]
c4fe7728 1860unset gm
390adaea 1861}
2d19516d 1862
cb07fc2a 1863# -- Key Bindings
ec6b424a 1864bind $ui_comm <$M1B-Key-Return> {do_commit;break}
49b86f01
SP
1865bind $ui_comm <$M1B-Key-i> {do_include_all;break}
1866bind $ui_comm <$M1B-Key-I> {do_include_all;break}
9861671d
SP
1867bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
1868bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
1869bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
1870bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
1871bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
1872bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
1873bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
1874bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
1875
1876bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
1877bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
1878bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
1879bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
1880bind $ui_diff <$M1B-Key-v> {break}
1881bind $ui_diff <$M1B-Key-V> {break}
1882bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
1883bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
b2c6fcf1
SP
1884bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
1885bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
1886bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
1887bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
49b86f01 1888
07123f40
SP
1889bind . <Destroy> do_quit
1890bind all <Key-F5> do_rescan
1891bind all <$M1B-Key-r> do_rescan
1892bind all <$M1B-Key-R> do_rescan
1893bind . <$M1B-Key-s> do_signoff
1894bind . <$M1B-Key-S> do_signoff
49b86f01
SP
1895bind . <$M1B-Key-i> do_include_all
1896bind . <$M1B-Key-I> do_include_all
07123f40
SP
1897bind . <$M1B-Key-Return> do_commit
1898bind all <$M1B-Key-q> do_quit
1899bind all <$M1B-Key-Q> do_quit
1900bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
1901bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
cb07fc2a
SP
1902foreach i [list $ui_index $ui_other] {
1903 bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
1904 bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
1905 bind $i <ButtonRelease-1> {unclick %W %x %y; break}
1906}
62aac80b
SP
1907unset i
1908
1909set file_lists($ui_index) [list]
1910set file_lists($ui_other) [list]
cb07fc2a 1911
ec6b424a 1912wm title . "$appname ([file normalize [file dirname $gitdir]])"
cb07fc2a 1913focus -force $ui_comm
8c0ce436
SP
1914load_all_remotes
1915populate_remote_menu .mbar.fetch From fetch_from
1916populate_remote_menu .mbar.push To push_to
d33ba5fa 1917populate_pull_menu .mbar.pull
cb07fc2a 1918update_status