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