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