git-gui: Automatically update-index all included files before commit.
[git/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
51f4d16b
SP
17proc is_many_config {name} {
18 switch -glob -- $name {
19 remote.*.fetch -
20 remote.*.push
21 {return 1}
22 *
23 {return 0}
24 }
25}
2d19516d 26
6bbd1cb9 27proc load_config {include_global} {
51f4d16b
SP
28 global repo_config global_config default_config
29
30 array unset global_config
6bbd1cb9
SP
31 if {$include_global} {
32 catch {
33 set fd_rc [open "| git repo-config --global --list" r]
34 while {[gets $fd_rc line] >= 0} {
35 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
36 if {[is_many_config $name]} {
37 lappend global_config($name) $value
38 } else {
39 set global_config($name) $value
40 }
51f4d16b
SP
41 }
42 }
6bbd1cb9 43 close $fd_rc
51f4d16b 44 }
51f4d16b 45 }
6bbd1cb9
SP
46
47 array unset repo_config
2d19516d
SP
48 catch {
49 set fd_rc [open "| git repo-config --list" r]
50 while {[gets $fd_rc line] >= 0} {
51 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
51f4d16b
SP
52 if {[is_many_config $name]} {
53 lappend repo_config($name) $value
54 } else {
55 set repo_config($name) $value
56 }
2d19516d
SP
57 }
58 }
59 close $fd_rc
60 }
61
51f4d16b
SP
62 foreach name [array names default_config] {
63 if {[catch {set v $global_config($name)}]} {
64 set global_config($name) $default_config($name)
65 }
66 if {[catch {set v $repo_config($name)}]} {
67 set repo_config($name) $default_config($name)
68 }
2d19516d
SP
69 }
70}
71
51f4d16b 72proc save_config {} {
92148d80
SP
73 global default_config font_descs
74 global repo_config global_config
51f4d16b 75 global repo_config_new global_config_new
2d19516d 76
92148d80
SP
77 foreach option $font_descs {
78 set name [lindex $option 0]
79 set font [lindex $option 1]
80 font configure $font \
81 -family $global_config_new(gui.$font^^family) \
82 -size $global_config_new(gui.$font^^size)
83 font configure ${font}bold \
84 -family $global_config_new(gui.$font^^family) \
85 -size $global_config_new(gui.$font^^size)
86 set global_config_new(gui.$name) [font configure $font]
87 unset global_config_new(gui.$font^^family)
88 unset global_config_new(gui.$font^^size)
89 }
90
91 foreach name [array names default_config] {
51f4d16b 92 set value $global_config_new($name)
043f7011
SP
93 if {$value ne $global_config($name)} {
94 if {$value eq $default_config($name)} {
51f4d16b
SP
95 catch {exec git repo-config --global --unset $name}
96 } else {
7b64d0b7
SP
97 regsub -all "\[{}\]" $value {"} value
98 exec git repo-config --global $name $value
51f4d16b
SP
99 }
100 set global_config($name) $value
043f7011 101 if {$value eq $repo_config($name)} {
51f4d16b
SP
102 catch {exec git repo-config --unset $name}
103 set repo_config($name) $value
104 }
105 }
2d19516d
SP
106 }
107
92148d80 108 foreach name [array names default_config] {
51f4d16b 109 set value $repo_config_new($name)
043f7011
SP
110 if {$value ne $repo_config($name)} {
111 if {$value eq $global_config($name)} {
51f4d16b
SP
112 catch {exec git repo-config --unset $name}
113 } else {
7b64d0b7
SP
114 regsub -all "\[{}\]" $value {"} value
115 exec git repo-config $name $value
51f4d16b
SP
116 }
117 set repo_config($name) $value
118 }
2d19516d
SP
119 }
120}
121
da5239dc
SP
122proc error_popup {msg} {
123 global gitdir appname
124
125 set title $appname
043f7011 126 if {$gitdir ne {}} {
da5239dc
SP
127 append title { (}
128 append title [lindex \
129 [file split [file normalize [file dirname $gitdir]]] \
130 end]
131 append title {)}
132 }
44be340e
SP
133 tk_messageBox \
134 -parent . \
da5239dc
SP
135 -icon error \
136 -type ok \
137 -title "$title: error" \
138 -message $msg
139}
140
16403d0b
SP
141proc info_popup {msg} {
142 global gitdir appname
143
144 set title $appname
043f7011 145 if {$gitdir ne {}} {
16403d0b
SP
146 append title { (}
147 append title [lindex \
148 [file split [file normalize [file dirname $gitdir]]] \
149 end]
150 append title {)}
151 }
152 tk_messageBox \
153 -parent . \
154 -icon error \
155 -type ok \
156 -title $title \
157 -message $msg
158}
159
2d19516d
SP
160######################################################################
161##
162## repository setup
163
44be340e
SP
164if { [catch {set cdup [exec git rev-parse --show-cdup]} err]
165 || [catch {set gitdir [exec git rev-parse --git-dir]} err]} {
166 catch {wm withdraw .}
167 error_popup "Cannot find the git directory:\n\n$err"
2d19516d
SP
168 exit 1
169}
043f7011 170if {$cdup ne ""} {
2d19516d
SP
171 cd $cdup
172}
173unset cdup
174
4ccdab02 175set single_commit 0
043f7011 176if {$appname eq {git-citool}} {
2d19516d
SP
177 set single_commit 1
178}
179
cb07fc2a
SP
180######################################################################
181##
e210e674 182## task management
cb07fc2a 183
8f52548a 184set rescan_active 0
131f503b 185set diff_active 0
24263b77 186set last_clicked {}
131f503b 187
e210e674
SP
188set disable_on_lock [list]
189set index_lock_type none
190
e57ca85e
SP
191set HEAD {}
192set PARENT {}
193set commit_type {}
194
e210e674
SP
195proc lock_index {type} {
196 global index_lock_type disable_on_lock
131f503b 197
043f7011 198 if {$index_lock_type eq {none}} {
e210e674
SP
199 set index_lock_type $type
200 foreach w $disable_on_lock {
201 uplevel #0 $w disabled
202 }
203 return 1
043f7011 204 } elseif {$index_lock_type eq {begin-update} && $type eq {update}} {
e210e674 205 set index_lock_type $type
131f503b
SP
206 return 1
207 }
208 return 0
209}
cb07fc2a 210
e210e674
SP
211proc unlock_index {} {
212 global index_lock_type disable_on_lock
213
214 set index_lock_type none
215 foreach w $disable_on_lock {
216 uplevel #0 $w normal
217 }
218}
219
220######################################################################
221##
222## status
223
ec6b424a
SP
224proc repository_state {hdvar ctvar} {
225 global gitdir
226 upvar $hdvar hd $ctvar ct
227
228 if {[catch {set hd [exec git rev-parse --verify HEAD]}]} {
229 set ct initial
230 } elseif {[file exists [file join $gitdir MERGE_HEAD]]} {
231 set ct merge
232 } else {
233 set ct normal
234 }
235}
236
8f52548a 237proc rescan {after} {
e57ca85e 238 global HEAD PARENT commit_type
131f503b 239 global ui_index ui_other ui_status_value ui_comm
8f52548a 240 global rescan_active file_states
51f4d16b 241 global repo_config
cb07fc2a 242
8f52548a 243 if {$rescan_active > 0 || ![lock_index read]} return
cb07fc2a 244
e57ca85e 245 repository_state new_HEAD new_type
043f7011
SP
246 if {$commit_type eq {amend}
247 && $new_type eq {normal}
248 && $new_HEAD eq $HEAD} {
e57ca85e
SP
249 } else {
250 set HEAD $new_HEAD
251 set PARENT $new_HEAD
252 set commit_type $new_type
253 }
254
cb07fc2a 255 array unset file_states
cb07fc2a 256
131f503b 257 if {![$ui_comm edit modified]
043f7011 258 || [string trim [$ui_comm get 0.0 end]] eq {}} {
131f503b
SP
259 if {[load_message GITGUI_MSG]} {
260 } elseif {[load_message MERGE_MSG]} {
261 } elseif {[load_message SQUASH_MSG]} {
262 }
263 $ui_comm edit modified false
b2c6fcf1 264 $ui_comm edit reset
131f503b
SP
265 }
266
043f7011 267 if {$repo_config(gui.trustmtime) eq {true}} {
8f52548a 268 rescan_stage2 {} $after
e534f3a8 269 } else {
8f52548a 270 set rescan_active 1
e534f3a8 271 set ui_status_value {Refreshing file status...}
16403d0b
SP
272 set cmd [list git update-index]
273 lappend cmd -q
274 lappend cmd --unmerged
275 lappend cmd --ignore-missing
276 lappend cmd --refresh
277 set fd_rf [open "| $cmd" r]
e534f3a8 278 fconfigure $fd_rf -blocking 0 -translation binary
390adaea 279 fileevent $fd_rf readable \
8f52548a 280 [list rescan_stage2 $fd_rf $after]
e534f3a8 281 }
131f503b
SP
282}
283
8f52548a 284proc rescan_stage2 {fd after} {
e57ca85e 285 global gitdir PARENT commit_type
131f503b 286 global ui_index ui_other ui_status_value ui_comm
8f52548a 287 global rescan_active
868c8752 288 global buf_rdi buf_rdf buf_rlo
131f503b 289
043f7011 290 if {$fd ne {}} {
e534f3a8
SP
291 read $fd
292 if {![eof $fd]} return
293 close $fd
294 }
131f503b 295
cb07fc2a
SP
296 set ls_others [list | git ls-files --others -z \
297 --exclude-per-directory=.gitignore]
298 set info_exclude [file join $gitdir info exclude]
299 if {[file readable $info_exclude]} {
300 lappend ls_others "--exclude-from=$info_exclude"
301 }
302
868c8752
SP
303 set buf_rdi {}
304 set buf_rdf {}
305 set buf_rlo {}
306
8f52548a 307 set rescan_active 3
131f503b 308 set ui_status_value {Scanning for modified files ...}
e57ca85e 309 set fd_di [open "| git diff-index --cached -z $PARENT" r]
cb07fc2a
SP
310 set fd_df [open "| git diff-files -z" r]
311 set fd_lo [open $ls_others r]
cb07fc2a
SP
312
313 fconfigure $fd_di -blocking 0 -translation binary
314 fconfigure $fd_df -blocking 0 -translation binary
315 fconfigure $fd_lo -blocking 0 -translation binary
8f52548a
SP
316 fileevent $fd_di readable [list read_diff_index $fd_di $after]
317 fileevent $fd_df readable [list read_diff_files $fd_df $after]
318 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
cb07fc2a
SP
319}
320
131f503b
SP
321proc load_message {file} {
322 global gitdir ui_comm
323
324 set f [file join $gitdir $file]
e57ca85e 325 if {[file isfile $f]} {
131f503b
SP
326 if {[catch {set fd [open $f r]}]} {
327 return 0
328 }
e57ca85e 329 set content [string trim [read $fd]]
131f503b
SP
330 close $fd
331 $ui_comm delete 0.0 end
332 $ui_comm insert end $content
333 return 1
334 }
335 return 0
336}
337
8f52548a 338proc read_diff_index {fd after} {
cb07fc2a
SP
339 global buf_rdi
340
341 append buf_rdi [read $fd]
868c8752
SP
342 set c 0
343 set n [string length $buf_rdi]
344 while {$c < $n} {
345 set z1 [string first "\0" $buf_rdi $c]
346 if {$z1 == -1} break
347 incr z1
348 set z2 [string first "\0" $buf_rdi $z1]
349 if {$z2 == -1} break
350
351 set c $z2
352 incr z2 -1
353 display_file \
354 [string range $buf_rdi $z1 $z2] \
24263b77 355 [string index $buf_rdi [expr {$z1 - 2}]]_
868c8752 356 incr c
cb07fc2a 357 }
868c8752
SP
358 if {$c < $n} {
359 set buf_rdi [string range $buf_rdi $c end]
360 } else {
361 set buf_rdi {}
362 }
363
8f52548a 364 rescan_done $fd buf_rdi $after
cb07fc2a
SP
365}
366
8f52548a 367proc read_diff_files {fd after} {
cb07fc2a
SP
368 global buf_rdf
369
370 append buf_rdf [read $fd]
868c8752
SP
371 set c 0
372 set n [string length $buf_rdf]
373 while {$c < $n} {
374 set z1 [string first "\0" $buf_rdf $c]
375 if {$z1 == -1} break
376 incr z1
377 set z2 [string first "\0" $buf_rdf $z1]
378 if {$z2 == -1} break
379
380 set c $z2
381 incr z2 -1
382 display_file \
383 [string range $buf_rdf $z1 $z2] \
24263b77 384 _[string index $buf_rdf [expr {$z1 - 2}]]
868c8752
SP
385 incr c
386 }
387 if {$c < $n} {
388 set buf_rdf [string range $buf_rdf $c end]
389 } else {
390 set buf_rdf {}
cb07fc2a 391 }
868c8752 392
8f52548a 393 rescan_done $fd buf_rdf $after
cb07fc2a
SP
394}
395
8f52548a 396proc read_ls_others {fd after} {
cb07fc2a
SP
397 global buf_rlo
398
399 append buf_rlo [read $fd]
400 set pck [split $buf_rlo "\0"]
401 set buf_rlo [lindex $pck end]
402 foreach p [lrange $pck 0 end-1] {
403 display_file $p _O
404 }
8f52548a 405 rescan_done $fd buf_rlo $after
cb07fc2a
SP
406}
407
8f52548a
SP
408proc rescan_done {fd buf after} {
409 global rescan_active
f7f8d322 410 global file_states repo_config
7f1df79b 411 upvar $buf to_clear
cb07fc2a 412
f7f8d322
SP
413 if {![eof $fd]} return
414 set to_clear {}
415 close $fd
8f52548a 416 if {[incr rescan_active -1] > 0} return
93f654df 417
24263b77 418 prune_selection
f7f8d322
SP
419 unlock_index
420 display_all_files
421
422 if {$repo_config(gui.partialinclude) ne {true}} {
bbe3b3b9 423 set pathList [list]
f7f8d322
SP
424 foreach path [array names file_states] {
425 switch -- [lindex $file_states($path) 0] {
426 AM -
427 MM {lappend pathList $path}
428 }
429 }
430 if {$pathList ne {}} {
04b39382
SP
431 update_index \
432 "Updating included files" \
433 $pathList \
434 [concat {reshow_diff;} $after]
f7f8d322 435 return
cb07fc2a
SP
436 }
437 }
f7f8d322
SP
438
439 reshow_diff
8f52548a 440 uplevel #0 $after
cb07fc2a
SP
441}
442
24263b77
SP
443proc prune_selection {} {
444 global file_states selected_paths
445
446 foreach path [array names selected_paths] {
447 if {[catch {set still_here $file_states($path)}]} {
448 unset selected_paths($path)
449 }
450 }
451}
452
cb07fc2a
SP
453######################################################################
454##
455## diff
456
cb07fc2a 457proc clear_diff {} {
03e4ec53 458 global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other
cb07fc2a
SP
459
460 $ui_diff conf -state normal
461 $ui_diff delete 0.0 end
462 $ui_diff conf -state disabled
03e4ec53 463
cb07fc2a
SP
464 set ui_fname_value {}
465 set ui_fstatus_value {}
03e4ec53
SP
466
467 $ui_index tag remove in_diff 0.0 end
468 $ui_other tag remove in_diff 0.0 end
cb07fc2a
SP
469}
470
7f1df79b
SP
471proc reshow_diff {} {
472 global ui_fname_value ui_status_value file_states
473
043f7011 474 if {$ui_fname_value eq {}
73ad179b 475 || [catch {set s $file_states($ui_fname_value)}]} {
7f1df79b 476 clear_diff
73ad179b
SP
477 } else {
478 show_diff $ui_fname_value
7f1df79b
SP
479 }
480}
481
16403d0b
SP
482proc handle_empty_diff {} {
483 global ui_fname_value file_states file_lists
484
485 set path $ui_fname_value
486 set s $file_states($path)
043f7011 487 if {[lindex $s 0] ne {_M}} return
16403d0b
SP
488
489 info_popup "No differences detected.
490
491[short_path $path] has no changes.
492
a37eee44
SP
493The modification date of this file was updated
494by another application and you currently have
495the Trust File Modification Timestamps option
496enabled, so Git did not automatically detect
497that there are no content differences in this
498file.
499
500This file will now be removed from the modified
501files list, to prevent possible confusion.
16403d0b
SP
502"
503 if {[catch {exec git update-index -- $path} err]} {
504 error_popup "Failed to refresh index:\n\n$err"
505 }
506
507 clear_diff
508 set old_w [mapcol [lindex $file_states($path) 0] $path]
509 set lno [lsearch -sorted $file_lists($old_w) $path]
510 if {$lno >= 0} {
511 set file_lists($old_w) \
512 [lreplace $file_lists($old_w) $lno $lno]
513 incr lno
514 $old_w conf -state normal
24263b77 515 $old_w delete $lno.0 [expr {$lno + 1}].0
16403d0b
SP
516 $old_w conf -state disabled
517 }
518}
519
03e4ec53
SP
520proc show_diff {path {w {}} {lno {}}} {
521 global file_states file_lists
fd2656fd 522 global PARENT diff_3way diff_active repo_config
cb07fc2a
SP
523 global ui_diff ui_fname_value ui_fstatus_value ui_status_value
524
e210e674 525 if {$diff_active || ![lock_index read]} return
cb07fc2a
SP
526
527 clear_diff
043f7011 528 if {$w eq {} || $lno == {}} {
03e4ec53
SP
529 foreach w [array names file_lists] {
530 set lno [lsearch -sorted $file_lists($w) $path]
531 if {$lno >= 0} {
532 incr lno
533 break
534 }
535 }
536 }
043f7011 537 if {$w ne {} && $lno >= 1} {
24263b77 538 $w tag add in_diff $lno.0 [expr {$lno + 1}].0
03e4ec53
SP
539 }
540
cb07fc2a
SP
541 set s $file_states($path)
542 set m [lindex $s 0]
543 set diff_3way 0
544 set diff_active 1
c11b5f20 545 set ui_fname_value $path
cb07fc2a 546 set ui_fstatus_value [mapdesc $m $path]
68e009de 547 set ui_status_value "Loading diff of [escape_path $path]..."
cb07fc2a 548
fd2656fd
SP
549 set cmd [list | git diff-index]
550 lappend cmd --no-color
358d8de8
SP
551 if {$repo_config(gui.diffcontext) > 0} {
552 lappend cmd "-U$repo_config(gui.diffcontext)"
553 }
fd2656fd
SP
554 lappend cmd -p
555
cb07fc2a 556 switch $m {
cb07fc2a 557 MM {
fd2656fd 558 lappend cmd -c
cb07fc2a
SP
559 }
560 _O {
561 if {[catch {
562 set fd [open $path r]
563 set content [read $fd]
564 close $fd
565 } err ]} {
131f503b 566 set diff_active 0
e210e674 567 unlock_index
68e009de 568 set ui_status_value "Unable to display [escape_path $path]"
44be340e 569 error_popup "Error loading file:\n\n$err"
cb07fc2a
SP
570 return
571 }
572 $ui_diff conf -state normal
573 $ui_diff insert end $content
574 $ui_diff conf -state disabled
bd1e2b40
SP
575 set diff_active 0
576 unlock_index
577 set ui_status_value {Ready.}
cb07fc2a
SP
578 return
579 }
580 }
581
fd2656fd
SP
582 lappend cmd $PARENT
583 lappend cmd --
584 lappend cmd $path
585
cb07fc2a 586 if {[catch {set fd [open $cmd r]} err]} {
131f503b 587 set diff_active 0
e210e674 588 unlock_index
68e009de 589 set ui_status_value "Unable to display [escape_path $path]"
44be340e 590 error_popup "Error loading diff:\n\n$err"
cb07fc2a
SP
591 return
592 }
593
6f6eed28 594 fconfigure $fd -blocking 0 -translation auto
cb07fc2a
SP
595 fileevent $fd readable [list read_diff $fd]
596}
597
598proc read_diff {fd} {
599 global ui_diff ui_status_value diff_3way diff_active
51f4d16b 600 global repo_config
cb07fc2a
SP
601
602 while {[gets $fd line] >= 0} {
6f6eed28
SP
603 if {[string match {diff --git *} $line]} continue
604 if {[string match {diff --combined *} $line]} continue
605 if {[string match {--- *} $line]} continue
606 if {[string match {+++ *} $line]} continue
cb07fc2a
SP
607 if {[string match index* $line]} {
608 if {[string first , $line] >= 0} {
609 set diff_3way 1
610 }
611 }
612
613 $ui_diff conf -state normal
614 if {!$diff_3way} {
615 set x [string index $line 0]
616 switch -- $x {
617 "@" {set tags da}
618 "+" {set tags dp}
619 "-" {set tags dm}
620 default {set tags {}}
621 }
622 } else {
623 set x [string range $line 0 1]
624 switch -- $x {
625 default {set tags {}}
626 "@@" {set tags da}
627 "++" {set tags dp; set x " +"}
628 " +" {set tags {di bold}; set x "++"}
629 "+ " {set tags dni; set x "-+"}
630 "--" {set tags dm; set x " -"}
631 " -" {set tags {dm bold}; set x "--"}
632 "- " {set tags di; set x "+-"}
633 default {set tags {}}
634 }
635 set line [string replace $line 0 1 $x]
636 }
637 $ui_diff insert end $line $tags
638 $ui_diff insert end "\n"
639 $ui_diff conf -state disabled
640 }
641
642 if {[eof $fd]} {
643 close $fd
644 set diff_active 0
e210e674 645 unlock_index
cb07fc2a 646 set ui_status_value {Ready.}
16403d0b 647
043f7011
SP
648 if {$repo_config(gui.trustmtime) eq {true}
649 && [$ui_diff index end] eq {2.0}} {
16403d0b
SP
650 handle_empty_diff
651 }
cb07fc2a
SP
652 }
653}
654
ec6b424a
SP
655######################################################################
656##
657## commit
658
e57ca85e
SP
659proc load_last_commit {} {
660 global HEAD PARENT commit_type ui_comm
661
043f7011
SP
662 if {$commit_type eq {amend}} return
663 if {$commit_type ne {normal}} {
e57ca85e
SP
664 error_popup "Can't amend a $commit_type commit."
665 return
666 }
667
668 set msg {}
669 set parent {}
670 set parent_count 0
671 if {[catch {
672 set fd [open "| git cat-file commit $HEAD" r]
673 while {[gets $fd line] > 0} {
674 if {[string match {parent *} $line]} {
675 set parent [string range $line 7 end]
676 incr parent_count
677 }
678 }
679 set msg [string trim [read $fd]]
680 close $fd
681 } err]} {
44be340e 682 error_popup "Error loading commit data for amend:\n\n$err"
e57ca85e
SP
683 return
684 }
685
686 if {$parent_count == 0} {
687 set commit_type amend
688 set HEAD {}
689 set PARENT {}
8f52548a 690 rescan {set ui_status_value {Ready.}}
e57ca85e
SP
691 } elseif {$parent_count == 1} {
692 set commit_type amend
693 set PARENT $parent
694 $ui_comm delete 0.0 end
695 $ui_comm insert end $msg
696 $ui_comm edit modified false
b2c6fcf1 697 $ui_comm edit reset
8f52548a 698 rescan {set ui_status_value {Ready.}}
e57ca85e
SP
699 } else {
700 error_popup {You can't amend a merge commit.}
701 return
702 }
703}
704
ec6b424a 705proc commit_tree {} {
bbe3b3b9 706 global HEAD commit_type file_states ui_comm repo_config
ec6b424a 707
333b0c74 708 if {![lock_index update]} return
ec6b424a
SP
709
710 # -- Our in memory state should match the repository.
711 #
712 repository_state curHEAD cur_type
043f7011
SP
713 if {$commit_type eq {amend}
714 && $cur_type eq {normal}
715 && $curHEAD eq $HEAD} {
716 } elseif {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
ec6b424a
SP
717 error_popup {Last scanned state does not match repository state.
718
719Its highly likely that another Git program modified the
bbe3b3b9 720repository since the last scan. A rescan is required
ec6b424a 721before committing.
bbe3b3b9
SP
722
723A rescan will be automatically started now.
ec6b424a
SP
724}
725 unlock_index
8f52548a 726 rescan {set ui_status_value {Ready.}}
ec6b424a
SP
727 return
728 }
729
730 # -- At least one file should differ in the index.
731 #
732 set files_ready 0
733 foreach path [array names file_states] {
bbe3b3b9 734 switch -glob -- [lindex $file_states($path) 0] {
7f1df79b
SP
735 _? {continue}
736 A? -
737 D? -
738 M? {set files_ready 1; break}
739 U? {
ec6b424a
SP
740 error_popup "Unmerged files cannot be committed.
741
16403d0b 742File [short_path $path] has merge conflicts.
7fe7e733 743You must resolve them and include the file before committing.
ec6b424a
SP
744"
745 unlock_index
746 return
747 }
748 default {
749 error_popup "Unknown file state [lindex $s 0] detected.
750
16403d0b 751File [short_path $path] cannot be committed by this program.
ec6b424a
SP
752"
753 }
754 }
755 }
756 if {!$files_ready} {
7fe7e733 757 error_popup {No included files to commit.
ec6b424a 758
7fe7e733 759You must include at least 1 file before you can commit.
ec6b424a
SP
760}
761 unlock_index
762 return
763 }
764
765 # -- A message is required.
766 #
767 set msg [string trim [$ui_comm get 1.0 end]]
043f7011 768 if {$msg eq {}} {
ec6b424a
SP
769 error_popup {Please supply a commit message.
770
771A good commit message has the following format:
772
773- First line: Describe in one sentance what you did.
774- Second line: Blank
775- Remaining lines: Describe why this change is good.
776}
777 unlock_index
778 return
779 }
780
bbe3b3b9 781 # -- Update included files if partialincludes are off.
ec6b424a 782 #
bbe3b3b9
SP
783 if {$repo_config(gui.partialinclude) ne {true}} {
784 set pathList [list]
785 foreach path [array names file_states] {
786 switch -glob -- [lindex $file_states($path) 0] {
787 A? -
788 M? {lappend pathList $path}
789 }
790 }
791 if {$pathList ne {}} {
792 unlock_index
793 update_index \
794 "Updating included files" \
795 $pathList \
796 [concat {lock_index update;} \
797 [list commit_prehook $curHEAD $msg]]
798 return
799 }
800 }
801
802 commit_prehook $curHEAD $msg
803}
804
805proc commit_prehook {curHEAD msg} {
806 global tcl_platform gitdir ui_status_value pch_error
807
808 # On Cygwin [file executable] might lie so we need to ask
809 # the shell if the hook is executable. Yes that's annoying.
810
ec6b424a 811 set pchook [file join $gitdir hooks pre-commit]
bbe3b3b9
SP
812 if {$tcl_platform(platform) eq {windows}
813 && [file isfile $pchook]} {
4658b56f
SP
814 set pchook [list sh -c [concat \
815 "if test -x \"$pchook\";" \
816 "then exec \"$pchook\" 2>&1;" \
817 "fi"]]
ec6b424a 818 } elseif {[file executable $pchook]} {
4658b56f 819 set pchook [list $pchook |& cat]
ec6b424a 820 } else {
bbe3b3b9
SP
821 commit_writetree $curHEAD $msg
822 return
4658b56f 823 }
bbe3b3b9
SP
824
825 set ui_status_value {Calling pre-commit hook...}
826 set pch_error {}
827 set fd_ph [open "| $pchook" r]
828 fconfigure $fd_ph -blocking 0 -translation binary
829 fileevent $fd_ph readable \
830 [list commit_prehook_wait $fd_ph $curHEAD $msg]
4658b56f
SP
831}
832
bbe3b3b9 833proc commit_prehook_wait {fd_ph curHEAD msg} {
333b0c74 834 global pch_error ui_status_value
4658b56f
SP
835
836 append pch_error [read $fd_ph]
837 fconfigure $fd_ph -blocking 1
838 if {[eof $fd_ph]} {
839 if {[catch {close $fd_ph}]} {
840 set ui_status_value {Commit declined by pre-commit hook.}
841 hook_failed_popup pre-commit $pch_error
842 unlock_index
333b0c74 843 } else {
bbe3b3b9 844 commit_writetree $curHEAD $msg
4658b56f 845 }
333b0c74 846 set pch_error {}
bbe3b3b9 847 return
ec6b424a 848 }
bbe3b3b9 849 fconfigure $fd_ph -blocking 0
4658b56f
SP
850}
851
bbe3b3b9 852proc commit_writetree {curHEAD msg} {
4658b56f 853 global ui_status_value
ec6b424a 854
ec6b424a 855 set ui_status_value {Committing changes...}
ec6b424a 856 set fd_wt [open "| git write-tree" r]
bbe3b3b9
SP
857 fileevent $fd_wt readable \
858 [list commit_committree $fd_wt $curHEAD $msg]
ec6b424a
SP
859}
860
bbe3b3b9 861proc commit_committree {fd_wt curHEAD msg} {
c8ebafd8 862 global single_commit gitdir HEAD PARENT commit_type tcl_platform
333b0c74 863 global ui_status_value ui_comm
24263b77 864 global file_states selected_paths
ec6b424a
SP
865
866 gets $fd_wt tree_id
043f7011 867 if {$tree_id eq {} || [catch {close $fd_wt} err]} {
44be340e 868 error_popup "write-tree failed:\n\n$err"
ec6b424a
SP
869 set ui_status_value {Commit failed.}
870 unlock_index
871 return
872 }
873
874 # -- Create the commit.
875 #
876 set cmd [list git commit-tree $tree_id]
043f7011 877 if {$PARENT ne {}} {
e57ca85e 878 lappend cmd -p $PARENT
ec6b424a 879 }
043f7011 880 if {$commit_type eq {merge}} {
ec6b424a
SP
881 if {[catch {
882 set fd_mh [open [file join $gitdir MERGE_HEAD] r]
bd1e2b40
SP
883 while {[gets $fd_mh merge_head] >= 0} {
884 lappend cmd -p $merge_head
ec6b424a
SP
885 }
886 close $fd_mh
887 } err]} {
44be340e 888 error_popup "Loading MERGE_HEAD failed:\n\n$err"
ec6b424a
SP
889 set ui_status_value {Commit failed.}
890 unlock_index
891 return
892 }
893 }
043f7011 894 if {$PARENT eq {}} {
ec6b424a
SP
895 # git commit-tree writes to stderr during initial commit.
896 lappend cmd 2>/dev/null
897 }
898 lappend cmd << $msg
899 if {[catch {set cmt_id [eval exec $cmd]} err]} {
44be340e 900 error_popup "commit-tree failed:\n\n$err"
ec6b424a
SP
901 set ui_status_value {Commit failed.}
902 unlock_index
903 return
904 }
905
906 # -- Update the HEAD ref.
907 #
908 set reflogm commit
043f7011 909 if {$commit_type ne {normal}} {
ec6b424a
SP
910 append reflogm " ($commit_type)"
911 }
912 set i [string first "\n" $msg]
913 if {$i >= 0} {
24263b77 914 append reflogm {: } [string range $msg 0 [expr {$i - 1}]]
ec6b424a
SP
915 } else {
916 append reflogm {: } $msg
917 }
e57ca85e 918 set cmd [list git update-ref -m $reflogm HEAD $cmt_id $curHEAD]
ec6b424a 919 if {[catch {eval exec $cmd} err]} {
44be340e 920 error_popup "update-ref failed:\n\n$err"
ec6b424a
SP
921 set ui_status_value {Commit failed.}
922 unlock_index
923 return
924 }
925
926 # -- Cleanup after ourselves.
927 #
928 catch {file delete [file join $gitdir MERGE_HEAD]}
929 catch {file delete [file join $gitdir MERGE_MSG]}
930 catch {file delete [file join $gitdir SQUASH_MSG]}
931 catch {file delete [file join $gitdir GITGUI_MSG]}
932
933 # -- Let rerere do its thing.
934 #
935 if {[file isdirectory [file join $gitdir rr-cache]]} {
936 catch {exec git rerere}
937 }
938
c8ebafd8
SP
939 # -- Run the post-commit hook.
940 #
941 set pchook [file join $gitdir hooks post-commit]
043f7011 942 if {$tcl_platform(platform) eq {windows} && [file isfile $pchook]} {
c8ebafd8
SP
943 set pchook [list sh -c [concat \
944 "if test -x \"$pchook\";" \
945 "then exec \"$pchook\";" \
946 "fi"]]
947 } elseif {![file executable $pchook]} {
948 set pchook {}
949 }
043f7011 950 if {$pchook ne {}} {
c8ebafd8
SP
951 catch {exec $pchook &}
952 }
953
e57ca85e
SP
954 $ui_comm delete 0.0 end
955 $ui_comm edit modified false
b2c6fcf1 956 $ui_comm edit reset
ec6b424a
SP
957
958 if {$single_commit} do_quit
959
7f1df79b
SP
960 # -- Update status without invoking any git commands.
961 #
7f1df79b 962 set commit_type normal
bd1e2b40
SP
963 set HEAD $cmt_id
964 set PARENT $cmt_id
7f1df79b
SP
965
966 foreach path [array names file_states] {
967 set s $file_states($path)
968 set m [lindex $s 0]
969 switch -glob -- $m {
970 A? -
971 M? -
972 D? {set m _[string index $m 1]}
973 }
974
043f7011 975 if {$m eq {__}} {
7f1df79b 976 unset file_states($path)
24263b77 977 catch {unset selected_paths($path)}
7f1df79b
SP
978 } else {
979 lset file_states($path) 0 $m
980 }
981 }
982
983 display_all_files
ec6b424a 984 unlock_index
7f1df79b
SP
985 reshow_diff
986 set ui_status_value \
987 "Changes committed as [string range $cmt_id 0 7]."
ec6b424a
SP
988}
989
8c0ce436
SP
990######################################################################
991##
992## fetch pull push
993
994proc fetch_from {remote} {
995 set w [new_console "fetch $remote" \
996 "Fetching new changes from $remote"]
cc4b1c02 997 set cmd [list git fetch]
8c0ce436 998 lappend cmd $remote
cc4b1c02 999 console_exec $w $cmd
8c0ce436
SP
1000}
1001
d33ba5fa 1002proc pull_remote {remote branch} {
ebf336b9 1003 global HEAD commit_type file_states repo_config
ec39d83a 1004
988b8a7d 1005 if {![lock_index update]} return
ec39d83a
SP
1006
1007 # -- Our in memory state should match the repository.
1008 #
1009 repository_state curHEAD cur_type
043f7011 1010 if {$commit_type ne $cur_type || $HEAD ne $curHEAD} {
ec39d83a
SP
1011 error_popup {Last scanned state does not match repository state.
1012
1013Its highly likely that another Git program modified the
1014repository since our last scan. A rescan is required
1015before a pull can be started.
1016}
1017 unlock_index
8f52548a 1018 rescan {set ui_status_value {Ready.}}
ec39d83a
SP
1019 return
1020 }
1021
1022 # -- No differences should exist before a pull.
1023 #
1024 if {[array size file_states] != 0} {
1025 error_popup {Uncommitted but modified files are present.
1026
1027You should not perform a pull with unmodified files in your working
1028directory as Git would be unable to recover from an incorrect merge.
1029
1030Commit or throw away all changes before starting a pull operation.
1031}
1032 unlock_index
1033 return
1034 }
1035
d33ba5fa
SP
1036 set w [new_console "pull $remote $branch" \
1037 "Pulling new changes from branch $branch in $remote"]
1038 set cmd [list git pull]
043f7011 1039 if {$repo_config(gui.pullsummary) eq {false}} {
ebf336b9
SP
1040 lappend cmd --no-summary
1041 }
d33ba5fa
SP
1042 lappend cmd $remote
1043 lappend cmd $branch
1044 console_exec $w $cmd [list post_pull_remote $remote $branch]
1045}
1046
1047proc post_pull_remote {remote branch success} {
ec39d83a
SP
1048 global HEAD PARENT commit_type
1049 global ui_status_value
1050
988b8a7d 1051 unlock_index
d33ba5fa 1052 if {$success} {
ec39d83a
SP
1053 repository_state HEAD commit_type
1054 set PARENT $HEAD
8f52548a 1055 set $ui_status_value "Pulling $branch from $remote complete."
d33ba5fa 1056 } else {
8f52548a
SP
1057 set m "Conflicts detected while pulling $branch from $remote."
1058 rescan "set ui_status_value {$m}"
d33ba5fa
SP
1059 }
1060}
1061
8c0ce436
SP
1062proc push_to {remote} {
1063 set w [new_console "push $remote" \
1064 "Pushing changes to $remote"]
cc4b1c02 1065 set cmd [list git push]
8c0ce436 1066 lappend cmd $remote
cc4b1c02 1067 console_exec $w $cmd
8c0ce436
SP
1068}
1069
cb07fc2a
SP
1070######################################################################
1071##
1072## ui helpers
1073
1074proc mapcol {state path} {
6b292675 1075 global all_cols ui_other
cb07fc2a
SP
1076
1077 if {[catch {set r $all_cols($state)}]} {
1078 puts "error: no column for state={$state} $path"
6b292675 1079 return $ui_other
cb07fc2a
SP
1080 }
1081 return $r
1082}
1083
1084proc mapicon {state path} {
1085 global all_icons
1086
1087 if {[catch {set r $all_icons($state)}]} {
1088 puts "error: no icon for state={$state} $path"
1089 return file_plain
1090 }
1091 return $r
1092}
1093
1094proc mapdesc {state path} {
1095 global all_descs
1096
1097 if {[catch {set r $all_descs($state)}]} {
1098 puts "error: no desc for state={$state} $path"
1099 return $state
1100 }
1101 return $r
1102}
1103
68e009de
SP
1104proc escape_path {path} {
1105 regsub -all "\n" $path "\\n" path
1106 return $path
1107}
1108
16403d0b
SP
1109proc short_path {path} {
1110 return [escape_path [lindex [file split $path] end]]
1111}
1112
93f654df
SP
1113set next_icon_id 0
1114
6b292675 1115proc merge_state {path new_state} {
93f654df 1116 global file_states next_icon_id
cb07fc2a 1117
6b292675
SP
1118 set s0 [string index $new_state 0]
1119 set s1 [string index $new_state 1]
1120
1121 if {[catch {set info $file_states($path)}]} {
1122 set state __
1123 set icon n[incr next_icon_id]
cb07fc2a 1124 } else {
6b292675
SP
1125 set state [lindex $info 0]
1126 set icon [lindex $info 1]
cb07fc2a
SP
1127 }
1128
043f7011 1129 if {$s0 eq {_}} {
6b292675 1130 set s0 [string index $state 0]
043f7011 1131 } elseif {$s0 eq {*}} {
6b292675 1132 set s0 _
cb07fc2a
SP
1133 }
1134
043f7011 1135 if {$s1 eq {_}} {
6b292675 1136 set s1 [string index $state 1]
043f7011 1137 } elseif {$s1 eq {*}} {
6b292675 1138 set s1 _
cb07fc2a
SP
1139 }
1140
6b292675
SP
1141 set file_states($path) [list $s0$s1 $icon]
1142 return $state
cb07fc2a
SP
1143}
1144
1145proc display_file {path state} {
8f52548a 1146 global file_states file_lists selected_paths rescan_active
cb07fc2a
SP
1147
1148 set old_m [merge_state $path $state]
8f52548a 1149 if {$rescan_active > 0} return
93f654df 1150
cb07fc2a 1151 set s $file_states($path)
93f654df 1152 set new_m [lindex $s 0]
0fb8f9ce
SP
1153 set new_w [mapcol $new_m $path]
1154 set old_w [mapcol $old_m $path]
1155 set new_icon [mapicon $new_m $path]
cb07fc2a 1156
043f7011 1157 if {$new_w ne $old_w} {
03e4ec53 1158 set lno [lsearch -sorted $file_lists($old_w) $path]
cb07fc2a
SP
1159 if {$lno >= 0} {
1160 incr lno
93f654df 1161 $old_w conf -state normal
24263b77 1162 $old_w delete $lno.0 [expr {$lno + 1}].0
93f654df 1163 $old_w conf -state disabled
cb07fc2a 1164 }
93f654df 1165
03e4ec53
SP
1166 lappend file_lists($new_w) $path
1167 set file_lists($new_w) [lsort $file_lists($new_w)]
1168 set lno [lsearch -sorted $file_lists($new_w) $path]
1169 incr lno
93f654df
SP
1170 $new_w conf -state normal
1171 $new_w image create $lno.0 \
1172 -align center -padx 5 -pady 1 \
1173 -name [lindex $s 1] \
e4ee9af4 1174 -image $new_icon
68e009de 1175 $new_w insert $lno.1 "[escape_path $path]\n"
24263b77
SP
1176 if {[catch {set in_sel $selected_paths($path)}]} {
1177 set in_sel 0
1178 }
1179 if {$in_sel} {
1180 $new_w tag add in_sel $lno.0 [expr {$lno + 1}].0
1181 }
93f654df 1182 $new_w conf -state disabled
043f7011 1183 } elseif {$new_icon ne [mapicon $old_m $path]} {
93f654df
SP
1184 $new_w conf -state normal
1185 $new_w image conf [lindex $s 1] -image $new_icon
1186 $new_w conf -state disabled
cb07fc2a 1187 }
93f654df 1188}
cb07fc2a 1189
93f654df 1190proc display_all_files {} {
24263b77
SP
1191 global ui_index ui_other
1192 global file_states file_lists
1193 global last_clicked selected_paths
93f654df
SP
1194
1195 $ui_index conf -state normal
1196 $ui_other conf -state normal
1197
7f1df79b
SP
1198 $ui_index delete 0.0 end
1199 $ui_other delete 0.0 end
24263b77 1200 set last_clicked {}
7f1df79b 1201
62aac80b
SP
1202 set file_lists($ui_index) [list]
1203 set file_lists($ui_other) [list]
1204
93f654df
SP
1205 foreach path [lsort [array names file_states]] {
1206 set s $file_states($path)
1207 set m [lindex $s 0]
6b292675 1208 set w [mapcol $m $path]
03e4ec53 1209 lappend file_lists($w) $path
24263b77 1210 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
6b292675 1211 $w image create end \
cb07fc2a 1212 -align center -padx 5 -pady 1 \
93f654df
SP
1213 -name [lindex $s 1] \
1214 -image [mapicon $m $path]
68e009de 1215 $w insert end "[escape_path $path]\n"
24263b77
SP
1216 if {[catch {set in_sel $selected_paths($path)}]} {
1217 set in_sel 0
1218 }
1219 if {$in_sel} {
1220 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1221 }
cb07fc2a 1222 }
93f654df
SP
1223
1224 $ui_index conf -state disabled
1225 $ui_other conf -state disabled
cb07fc2a
SP
1226}
1227
04b39382 1228proc update_index {msg pathList after} {
2cbe5577 1229 global update_index_cp update_index_rsd ui_status_value
131f503b 1230
74e6b12f 1231 if {![lock_index update]} return
131f503b 1232
74e6b12f 1233 set update_index_cp 0
2cbe5577 1234 set update_index_rsd 0
aaf1085a 1235 set pathList [lsort $pathList]
74e6b12f
SP
1236 set totalCnt [llength $pathList]
1237 set batch [expr {int($totalCnt * .01) + 1}]
1238 if {$batch > 25} {set batch 25}
1239
74e6b12f 1240 set ui_status_value [format \
04b39382 1241 "$msg... %i/%i files (%.2f%%)" \
74e6b12f
SP
1242 $update_index_cp \
1243 $totalCnt \
1244 0.0]
1245 set fd [open "| git update-index --add --remove -z --stdin" w]
7f09cfaf
SP
1246 fconfigure $fd \
1247 -blocking 0 \
1248 -buffering full \
1249 -buffersize 512 \
1250 -translation binary
74e6b12f
SP
1251 fileevent $fd writable [list \
1252 write_update_index \
1253 $fd \
1254 $pathList \
1255 $totalCnt \
1256 $batch \
04b39382
SP
1257 $msg \
1258 $after \
74e6b12f
SP
1259 ]
1260}
1261
04b39382 1262proc write_update_index {fd pathList totalCnt batch msg after} {
2cbe5577 1263 global update_index_cp update_index_rsd ui_status_value
74e6b12f 1264 global file_states ui_fname_value
131f503b 1265
74e6b12f
SP
1266 if {$update_index_cp >= $totalCnt} {
1267 close $fd
1268 unlock_index
04b39382
SP
1269 if {$update_index_rsd} reshow_diff
1270 uplevel #0 $after
74e6b12f 1271 return
131f503b 1272 }
131f503b 1273
74e6b12f
SP
1274 for {set i $batch} \
1275 {$update_index_cp < $totalCnt && $i > 0} \
1276 {incr i -1} {
1277 set path [lindex $pathList $update_index_cp]
1278 incr update_index_cp
1279
bbe3b3b9 1280 switch -glob -- [lindex $file_states($path) 0] {
74e6b12f 1281 AD -
bbe3b3b9 1282 MD -
74e6b12f 1283 _D {set new D*}
bbe3b3b9
SP
1284
1285 _M -
1286 MM -
1287 M_ {set new M*}
1288
1289 _O -
1290 AM -
1291 A_ {set new A*}
1292
1293 ?? {continue}
74e6b12f 1294 }
cb07fc2a 1295
74e6b12f
SP
1296 puts -nonewline $fd $path
1297 puts -nonewline $fd "\0"
1298 display_file $path $new
043f7011 1299 if {$ui_fname_value eq $path} {
2cbe5577 1300 set update_index_rsd 1
74e6b12f 1301 }
cb07fc2a
SP
1302 }
1303
74e6b12f 1304 set ui_status_value [format \
04b39382 1305 "$msg... %i/%i files (%.2f%%)" \
74e6b12f
SP
1306 $update_index_cp \
1307 $totalCnt \
1308 [expr {100.0 * $update_index_cp / $totalCnt}]]
cb07fc2a
SP
1309}
1310
8c0ce436
SP
1311######################################################################
1312##
2d19516d 1313## remote management
0d4f3eb5 1314
8c0ce436 1315proc load_all_remotes {} {
0d4f3eb5 1316 global gitdir all_remotes repo_config
8c0ce436
SP
1317
1318 set all_remotes [list]
1319 set rm_dir [file join $gitdir remotes]
1320 if {[file isdirectory $rm_dir]} {
d47ae541
SP
1321 set all_remotes [concat $all_remotes [glob \
1322 -types f \
1323 -tails \
1324 -nocomplain \
1325 -directory $rm_dir *]]
8c0ce436
SP
1326 }
1327
0d4f3eb5
SP
1328 foreach line [array names repo_config remote.*.url] {
1329 if {[regexp ^remote\.(.*)\.url\$ $line line name]} {
8c0ce436
SP
1330 lappend all_remotes $name
1331 }
1332 }
8c0ce436
SP
1333
1334 set all_remotes [lsort -unique $all_remotes]
1335}
1336
1337proc populate_remote_menu {m pfx op} {
b4946930 1338 global all_remotes
8c0ce436
SP
1339
1340 foreach remote $all_remotes {
1341 $m add command -label "$pfx $remote..." \
1342 -command [list $op $remote] \
b4946930 1343 -font font_ui
8c0ce436
SP
1344 }
1345}
1346
d33ba5fa 1347proc populate_pull_menu {m} {
b4946930 1348 global gitdir repo_config all_remotes disable_on_lock
d33ba5fa
SP
1349
1350 foreach remote $all_remotes {
1351 set rb {}
043f7011
SP
1352 if {[array get repo_config remote.$remote.url] ne {}} {
1353 if {[array get repo_config remote.$remote.fetch] ne {}} {
d33ba5fa
SP
1354 regexp {^([^:]+):} \
1355 [lindex $repo_config(remote.$remote.fetch) 0] \
1356 line rb
1357 }
1358 } else {
1359 catch {
1360 set fd [open [file join $gitdir remotes $remote] r]
1361 while {[gets $fd line] >= 0} {
1362 if {[regexp {^Pull:[ \t]*([^:]+):} $line line rb]} {
1363 break
1364 }
1365 }
1366 close $fd
1367 }
1368 }
1369
1370 set rb_short $rb
1371 regsub ^refs/heads/ $rb {} rb_short
043f7011 1372 if {$rb_short ne {}} {
d33ba5fa
SP
1373 $m add command \
1374 -label "Branch $rb_short from $remote..." \
1375 -command [list pull_remote $remote $rb] \
b4946930 1376 -font font_ui
0a462d67
SP
1377 lappend disable_on_lock \
1378 [list $m entryconf [$m index last] -state]
d33ba5fa
SP
1379 }
1380 }
1381}
1382
cb07fc2a
SP
1383######################################################################
1384##
1385## icons
1386
1387set filemask {
1388#define mask_width 14
1389#define mask_height 15
1390static unsigned char mask_bits[] = {
1391 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1392 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1393 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1394}
1395
1396image create bitmap file_plain -background white -foreground black -data {
1397#define plain_width 14
1398#define plain_height 15
1399static unsigned char plain_bits[] = {
1400 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1401 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1402 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1403} -maskdata $filemask
1404
1405image create bitmap file_mod -background white -foreground blue -data {
1406#define mod_width 14
1407#define mod_height 15
1408static unsigned char mod_bits[] = {
1409 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1410 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1411 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1412} -maskdata $filemask
1413
131f503b
SP
1414image create bitmap file_fulltick -background white -foreground "#007000" -data {
1415#define file_fulltick_width 14
1416#define file_fulltick_height 15
1417static unsigned char file_fulltick_bits[] = {
cb07fc2a
SP
1418 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1419 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1420 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1421} -maskdata $filemask
1422
1423image create bitmap file_parttick -background white -foreground "#005050" -data {
1424#define parttick_width 14
1425#define parttick_height 15
1426static unsigned char parttick_bits[] = {
1427 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1428 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
1429 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1430} -maskdata $filemask
1431
1432image create bitmap file_question -background white -foreground black -data {
1433#define file_question_width 14
1434#define file_question_height 15
1435static unsigned char file_question_bits[] = {
1436 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1437 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1438 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1439} -maskdata $filemask
1440
1441image create bitmap file_removed -background white -foreground red -data {
1442#define file_removed_width 14
1443#define file_removed_height 15
1444static unsigned char file_removed_bits[] = {
1445 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1446 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1447 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1448} -maskdata $filemask
1449
1450image create bitmap file_merge -background white -foreground blue -data {
1451#define file_merge_width 14
1452#define file_merge_height 15
1453static unsigned char file_merge_bits[] = {
1454 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1455 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1456 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1457} -maskdata $filemask
1458
6b292675
SP
1459set ui_index .vpane.files.index.list
1460set ui_other .vpane.files.other.list
131f503b 1461set max_status_desc 0
cb07fc2a 1462foreach i {
131f503b
SP
1463 {__ i plain "Unmodified"}
1464 {_M i mod "Modified"}
135f76ed 1465 {M_ i fulltick "Included in commit"}
7fe7e733 1466 {MM i parttick "Partially included"}
131f503b
SP
1467
1468 {_O o plain "Untracked"}
135f76ed 1469 {A_ o fulltick "Added by commit"}
131f503b 1470 {AM o parttick "Partially added"}
6b292675 1471 {AD o question "Added (but now gone)"}
131f503b
SP
1472
1473 {_D i question "Missing"}
135f76ed
SP
1474 {D_ i removed "Removed by commit"}
1475 {DD i removed "Removed by commit"}
131f503b
SP
1476 {DO i removed "Removed (still exists)"}
1477
1478 {UM i merge "Merge conflicts"}
1479 {U_ i merge "Merge conflicts"}
cb07fc2a 1480 } {
131f503b
SP
1481 if {$max_status_desc < [string length [lindex $i 3]]} {
1482 set max_status_desc [string length [lindex $i 3]]
1483 }
043f7011 1484 if {[lindex $i 1] eq {i}} {
6b292675
SP
1485 set all_cols([lindex $i 0]) $ui_index
1486 } else {
1487 set all_cols([lindex $i 0]) $ui_other
1488 }
131f503b
SP
1489 set all_icons([lindex $i 0]) file_[lindex $i 2]
1490 set all_descs([lindex $i 0]) [lindex $i 3]
cb07fc2a
SP
1491}
1492unset filemask i
1493
1494######################################################################
1495##
1496## util
1497
16fccd7a
SP
1498proc is_MacOSX {} {
1499 global tcl_platform tk_library
043f7011
SP
1500 if {$tcl_platform(platform) eq {unix}
1501 && $tcl_platform(os) eq {Darwin}
16fccd7a
SP
1502 && [string match /Library/Frameworks/* $tk_library]} {
1503 return 1
1504 }
1505 return 0
1506}
1507
1508proc bind_button3 {w cmd} {
1509 bind $w <Any-Button-3> $cmd
1510 if {[is_MacOSX]} {
1511 bind $w <Control-Button-1> $cmd
1512 }
1513}
1514
b4946930
SP
1515proc incr_font_size {font {amt 1}} {
1516 set sz [font configure $font -size]
1517 incr sz $amt
1518 font configure $font -size $sz
1519 font configure ${font}bold -size $sz
1520}
1521
6e27d826 1522proc hook_failed_popup {hook msg} {
b4946930 1523 global gitdir appname
6e27d826
SP
1524
1525 set w .hookfail
1526 toplevel $w
6e27d826
SP
1527
1528 frame $w.m
1529 label $w.m.l1 -text "$hook hook failed:" \
1530 -anchor w \
1531 -justify left \
b4946930 1532 -font font_uibold
6e27d826
SP
1533 text $w.m.t \
1534 -background white -borderwidth 1 \
1535 -relief sunken \
1536 -width 80 -height 10 \
b4946930 1537 -font font_diff \
6e27d826
SP
1538 -yscrollcommand [list $w.m.sby set]
1539 label $w.m.l2 \
1540 -text {You must correct the above errors before committing.} \
1541 -anchor w \
1542 -justify left \
b4946930 1543 -font font_uibold
6e27d826
SP
1544 scrollbar $w.m.sby -command [list $w.m.t yview]
1545 pack $w.m.l1 -side top -fill x
1546 pack $w.m.l2 -side bottom -fill x
1547 pack $w.m.sby -side right -fill y
1548 pack $w.m.t -side left -fill both -expand 1
1549 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1550
1551 $w.m.t insert 1.0 $msg
1552 $w.m.t conf -state disabled
1553
1554 button $w.ok -text OK \
1555 -width 15 \
b4946930 1556 -font font_ui \
6e27d826 1557 -command "destroy $w"
1e5c18fb 1558 pack $w.ok -side bottom -anchor e -pady 10 -padx 10
6e27d826
SP
1559
1560 bind $w <Visibility> "grab $w; focus $w"
1561 bind $w <Key-Return> "destroy $w"
d33ba5fa
SP
1562 wm title $w "$appname ([lindex [file split \
1563 [file normalize [file dirname $gitdir]]] \
1564 end]): error"
6e27d826
SP
1565 tkwait window $w
1566}
1567
8c0ce436
SP
1568set next_console_id 0
1569
1570proc new_console {short_title long_title} {
37af79d1
SP
1571 global next_console_id console_data
1572 set w .console[incr next_console_id]
1573 set console_data($w) [list $short_title $long_title]
1574 return [console_init $w]
1575}
1576
1577proc console_init {w} {
1578 global console_cr console_data
b4946930 1579 global gitdir appname M1B
8c0ce436 1580
ee3dc935 1581 set console_cr($w) 1.0
8c0ce436
SP
1582 toplevel $w
1583 frame $w.m
37af79d1 1584 label $w.m.l1 -text "[lindex $console_data($w) 1]:" \
8c0ce436
SP
1585 -anchor w \
1586 -justify left \
b4946930 1587 -font font_uibold
8c0ce436
SP
1588 text $w.m.t \
1589 -background white -borderwidth 1 \
1590 -relief sunken \
1591 -width 80 -height 10 \
b4946930 1592 -font font_diff \
8c0ce436
SP
1593 -state disabled \
1594 -yscrollcommand [list $w.m.sby set]
1e5c18fb
SP
1595 label $w.m.s -text {Working... please wait...} \
1596 -anchor w \
07123f40 1597 -justify left \
b4946930 1598 -font font_uibold
8c0ce436
SP
1599 scrollbar $w.m.sby -command [list $w.m.t yview]
1600 pack $w.m.l1 -side top -fill x
07123f40 1601 pack $w.m.s -side bottom -fill x
8c0ce436
SP
1602 pack $w.m.sby -side right -fill y
1603 pack $w.m.t -side left -fill both -expand 1
1604 pack $w.m -side top -fill both -expand 1 -padx 5 -pady 10
1605
0e794311
SP
1606 menu $w.ctxm -tearoff 0
1607 $w.ctxm add command -label "Copy" \
b4946930 1608 -font font_ui \
0e794311
SP
1609 -command "tk_textCopy $w.m.t"
1610 $w.ctxm add command -label "Select All" \
b4946930 1611 -font font_ui \
0e794311
SP
1612 -command "$w.m.t tag add sel 0.0 end"
1613 $w.ctxm add command -label "Copy All" \
b4946930 1614 -font font_ui \
0e794311
SP
1615 -command "
1616 $w.m.t tag add sel 0.0 end
1617 tk_textCopy $w.m.t
1618 $w.m.t tag remove sel 0.0 end
1619 "
1620
1e5c18fb 1621 button $w.ok -text {Close} \
b4946930 1622 -font font_ui \
8c0ce436
SP
1623 -state disabled \
1624 -command "destroy $w"
1e5c18fb 1625 pack $w.ok -side bottom -anchor e -pady 10 -padx 10
8c0ce436 1626
16fccd7a 1627 bind_button3 $w.m.t "tk_popup $w.ctxm %X %Y"
62aac80b
SP
1628 bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
1629 bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
8c0ce436 1630 bind $w <Visibility> "focus $w"
d33ba5fa
SP
1631 wm title $w "$appname ([lindex [file split \
1632 [file normalize [file dirname $gitdir]]] \
1633 end]): [lindex $console_data($w) 0]"
8c0ce436
SP
1634 return $w
1635}
1636
d33ba5fa 1637proc console_exec {w cmd {after {}}} {
cc4b1c02
SP
1638 global tcl_platform
1639
1640 # -- Windows tosses the enviroment when we exec our child.
1641 # But most users need that so we have to relogin. :-(
1642 #
043f7011 1643 if {$tcl_platform(platform) eq {windows}} {
cc4b1c02
SP
1644 set cmd [list sh --login -c "cd \"[pwd]\" && [join $cmd { }]"]
1645 }
1646
1647 # -- Tcl won't let us redirect both stdout and stderr to
1648 # the same pipe. So pass it through cat...
1649 #
1650 set cmd [concat | $cmd |& cat]
1651
1652 set fd_f [open $cmd r]
ee3dc935 1653 fconfigure $fd_f -blocking 0 -translation binary
d33ba5fa 1654 fileevent $fd_f readable [list console_read $w $fd_f $after]
cc4b1c02
SP
1655}
1656
d33ba5fa 1657proc console_read {w fd after} {
37af79d1 1658 global console_cr console_data
ee3dc935 1659
ee3dc935 1660 set buf [read $fd]
043f7011 1661 if {$buf ne {}} {
37af79d1
SP
1662 if {![winfo exists $w]} {console_init $w}
1663 $w.m.t conf -state normal
1664 set c 0
1665 set n [string length $buf]
1666 while {$c < $n} {
1667 set cr [string first "\r" $buf $c]
1668 set lf [string first "\n" $buf $c]
24263b77
SP
1669 if {$cr < 0} {set cr [expr {$n + 1}]}
1670 if {$lf < 0} {set lf [expr {$n + 1}]}
37af79d1
SP
1671
1672 if {$lf < $cr} {
1673 $w.m.t insert end [string range $buf $c $lf]
1674 set console_cr($w) [$w.m.t index {end -1c}]
1675 set c $lf
1676 incr c
1677 } else {
1678 $w.m.t delete $console_cr($w) end
1679 $w.m.t insert end "\n"
1680 $w.m.t insert end [string range $buf $c $cr]
1681 set c $cr
1682 incr c
1683 }
ee3dc935 1684 }
37af79d1
SP
1685 $w.m.t conf -state disabled
1686 $w.m.t see end
8c0ce436 1687 }
8c0ce436 1688
07123f40 1689 fconfigure $fd -blocking 1
8c0ce436 1690 if {[eof $fd]} {
07123f40 1691 if {[catch {close $fd}]} {
37af79d1 1692 if {![winfo exists $w]} {console_init $w}
07123f40 1693 $w.m.s conf -background red -text {Error: Command Failed}
37af79d1 1694 $w.ok conf -state normal
d33ba5fa 1695 set ok 0
37af79d1 1696 } elseif {[winfo exists $w]} {
07123f40 1697 $w.m.s conf -background green -text {Success}
37af79d1 1698 $w.ok conf -state normal
d33ba5fa 1699 set ok 1
07123f40 1700 }
ee3dc935 1701 array unset console_cr $w
37af79d1 1702 array unset console_data $w
043f7011 1703 if {$after ne {}} {
d33ba5fa
SP
1704 uplevel #0 $after $ok
1705 }
07123f40 1706 return
8c0ce436 1707 }
07123f40 1708 fconfigure $fd -blocking 0
8c0ce436
SP
1709}
1710
cb07fc2a
SP
1711######################################################################
1712##
1713## ui commands
1714
e210e674 1715set starting_gitk_msg {Please wait... Starting gitk...}
cc4b1c02 1716
cb07fc2a 1717proc do_gitk {} {
e210e674
SP
1718 global tcl_platform ui_status_value starting_gitk_msg
1719
1720 set ui_status_value $starting_gitk_msg
e57ca85e 1721 after 10000 {
043f7011 1722 if {$ui_status_value eq $starting_gitk_msg} {
e210e674
SP
1723 set ui_status_value {Ready.}
1724 }
1725 }
cb07fc2a 1726
043f7011 1727 if {$tcl_platform(platform) eq {windows}} {
cb07fc2a
SP
1728 exec sh -c gitk &
1729 } else {
1730 exec gitk &
1731 }
1732}
1733
d1536c48
SP
1734proc do_repack {} {
1735 set w [new_console "repack" "Repacking the object database"]
1736 set cmd [list git repack]
1737 lappend cmd -a
1738 lappend cmd -d
1739 console_exec $w $cmd
1740}
1741
b5834d70 1742set is_quitting 0
c4fe7728 1743
cb07fc2a 1744proc do_quit {} {
51f4d16b 1745 global gitdir ui_comm is_quitting repo_config
c4fe7728 1746
b5834d70
SP
1747 if {$is_quitting} return
1748 set is_quitting 1
131f503b 1749
51f4d16b
SP
1750 # -- Stash our current commit buffer.
1751 #
131f503b 1752 set save [file join $gitdir GITGUI_MSG]
ec6b424a 1753 set msg [string trim [$ui_comm get 0.0 end]]
043f7011 1754 if {[$ui_comm edit modified] && $msg ne {}} {
131f503b
SP
1755 catch {
1756 set fd [open $save w]
1757 puts $fd [string trim [$ui_comm get 0.0 end]]
1758 close $fd
1759 }
043f7011 1760 } elseif {$msg eq {} && [file exists $save]} {
131f503b
SP
1761 file delete $save
1762 }
1763
51f4d16b
SP
1764 # -- Stash our current window geometry into this repository.
1765 #
1766 set cfg_geometry [list]
1767 lappend cfg_geometry [wm geometry .]
1768 lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1769 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1770 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1771 set rc_geometry {}
1772 }
043f7011 1773 if {$cfg_geometry ne $rc_geometry} {
51f4d16b
SP
1774 catch {exec git repo-config gui.geometry $cfg_geometry}
1775 }
1776
cb07fc2a
SP
1777 destroy .
1778}
1779
1780proc do_rescan {} {
8f52548a 1781 rescan {set ui_status_value {Ready.}}
cb07fc2a
SP
1782}
1783
7fe7e733 1784proc do_include_all {} {
74e6b12f
SP
1785 global file_states
1786
1787 if {![lock_index begin-update]} return
1788
1789 set pathList [list]
1790 foreach path [array names file_states] {
1791 set s $file_states($path)
1792 set m [lindex $s 0]
1793 switch -- $m {
1794 AM -
1795 MM -
1796 _M -
1797 _D {lappend pathList $path}
131f503b 1798 }
74e6b12f 1799 }
043f7011 1800 if {$pathList eq {}} {
74e6b12f
SP
1801 unlock_index
1802 } else {
04b39382
SP
1803 update_index \
1804 "Including all modified files" \
1805 $pathList \
1806 {set ui_status_value {Ready to commit.}}
131f503b
SP
1807 }
1808}
1809
da5239dc
SP
1810set GIT_COMMITTER_IDENT {}
1811
131f503b 1812proc do_signoff {} {
97bf01c4 1813 global ui_comm GIT_COMMITTER_IDENT
131f503b 1814
043f7011 1815 if {$GIT_COMMITTER_IDENT eq {}} {
97bf01c4 1816 if {[catch {set me [exec git var GIT_COMMITTER_IDENT]} err]} {
44be340e 1817 error_popup "Unable to obtain your identity:\n\n$err"
97bf01c4 1818 return
131f503b 1819 }
97bf01c4
SP
1820 if {![regexp {^(.*) [0-9]+ [-+0-9]+$} \
1821 $me me GIT_COMMITTER_IDENT]} {
44be340e 1822 error_popup "Invalid GIT_COMMITTER_IDENT:\n\n$me"
97bf01c4
SP
1823 return
1824 }
1825 }
1826
1daf1d0c
SP
1827 set sob "Signed-off-by: $GIT_COMMITTER_IDENT"
1828 set last [$ui_comm get {end -1c linestart} {end -1c}]
043f7011 1829 if {$last ne $sob} {
b2c6fcf1 1830 $ui_comm edit separator
043f7011 1831 if {$last ne {}
1daf1d0c
SP
1832 && ![regexp {^[A-Z][A-Za-z]*-[A-Za-z-]+: *} $last]} {
1833 $ui_comm insert end "\n"
1834 }
1835 $ui_comm insert end "\n$sob"
b2c6fcf1 1836 $ui_comm edit separator
97bf01c4 1837 $ui_comm see end
131f503b
SP
1838 }
1839}
1840
e57ca85e
SP
1841proc do_amend_last {} {
1842 load_last_commit
1843}
1844
6e27d826 1845proc do_commit {} {
ec6b424a 1846 commit_tree
6e27d826
SP
1847}
1848
51f4d16b 1849proc do_options {} {
92148d80 1850 global appname gitdir font_descs
51f4d16b
SP
1851 global repo_config global_config
1852 global repo_config_new global_config_new
1853
51f4d16b
SP
1854 array unset repo_config_new
1855 array unset global_config_new
1856 foreach name [array names repo_config] {
1857 set repo_config_new($name) $repo_config($name)
1858 }
358d8de8
SP
1859 load_config 1
1860 foreach name [array names repo_config] {
1861 switch -- $name {
1862 gui.diffcontext {continue}
1863 }
1864 set repo_config_new($name) $repo_config($name)
1865 }
51f4d16b
SP
1866 foreach name [array names global_config] {
1867 set global_config_new($name) $global_config($name)
1868 }
e01b4221
SP
1869 set reponame [lindex [file split \
1870 [file normalize [file dirname $gitdir]]] \
1871 end]
51f4d16b
SP
1872
1873 set w .options_editor
1874 toplevel $w
e01b4221 1875 wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
51f4d16b
SP
1876
1877 label $w.header -text "$appname Options" \
1878 -font font_uibold
1879 pack $w.header -side top -fill x
1880
1881 frame $w.buttons
92148d80
SP
1882 button $w.buttons.restore -text {Restore Defaults} \
1883 -font font_ui \
1884 -command do_restore_defaults
1885 pack $w.buttons.restore -side left
51f4d16b
SP
1886 button $w.buttons.save -text Save \
1887 -font font_ui \
92148d80 1888 -command [list do_save_config $w]
51f4d16b
SP
1889 pack $w.buttons.save -side right
1890 button $w.buttons.cancel -text {Cancel} \
1891 -font font_ui \
92148d80 1892 -command [list destroy $w]
51f4d16b 1893 pack $w.buttons.cancel -side right
92148d80 1894 pack $w.buttons -side bottom -fill x -pady 10 -padx 10
51f4d16b 1895
e01b4221 1896 labelframe $w.repo -text "$reponame Repository" \
92148d80 1897 -font font_ui \
51f4d16b
SP
1898 -relief raised -borderwidth 2
1899 labelframe $w.global -text {Global (All Repositories)} \
92148d80 1900 -font font_ui \
51f4d16b
SP
1901 -relief raised -borderwidth 2
1902 pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5
1903 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5
1904
1905 foreach option {
f7f8d322 1906 {b partialinclude {Allow Partially Included Files}}
358d8de8
SP
1907 {b pullsummary {Show Pull Summary}}
1908 {b trustmtime {Trust File Modification Timestamps}}
1909 {i diffcontext {Number of Diff Context Lines}}
51f4d16b 1910 } {
358d8de8
SP
1911 set type [lindex $option 0]
1912 set name [lindex $option 1]
1913 set text [lindex $option 2]
51f4d16b 1914 foreach f {repo global} {
358d8de8
SP
1915 switch $type {
1916 b {
1917 checkbutton $w.$f.$name -text $text \
1918 -variable ${f}_config_new(gui.$name) \
1919 -onvalue true \
1920 -offvalue false \
1921 -font font_ui
1922 pack $w.$f.$name -side top -anchor w
1923 }
1924 i {
1925 frame $w.$f.$name
1926 label $w.$f.$name.l -text "$text:" -font font_ui
1927 pack $w.$f.$name.l -side left -anchor w -fill x
1928 spinbox $w.$f.$name.v \
1929 -textvariable ${f}_config_new(gui.$name) \
1930 -from 1 -to 99 -increment 1 \
1931 -width 3 \
1932 -font font_ui
1933 pack $w.$f.$name.v -side right -anchor e
1934 pack $w.$f.$name -side top -anchor w -fill x
1935 }
1936 }
51f4d16b
SP
1937 }
1938 }
1939
92148d80
SP
1940 set all_fonts [lsort [font families]]
1941 foreach option $font_descs {
1942 set name [lindex $option 0]
1943 set font [lindex $option 1]
1944 set text [lindex $option 2]
1945
1946 set global_config_new(gui.$font^^family) \
1947 [font configure $font -family]
1948 set global_config_new(gui.$font^^size) \
1949 [font configure $font -size]
1950
1951 frame $w.global.$name
1952 label $w.global.$name.l -text "$text:" -font font_ui
1953 pack $w.global.$name.l -side left -anchor w -fill x
1954 eval tk_optionMenu $w.global.$name.family \
1955 global_config_new(gui.$font^^family) \
1956 $all_fonts
1957 spinbox $w.global.$name.size \
1958 -textvariable global_config_new(gui.$font^^size) \
1959 -from 2 -to 80 -increment 1 \
1960 -width 3 \
1961 -font font_ui
1962 pack $w.global.$name.size -side right -anchor e
1963 pack $w.global.$name.family -side right -anchor e
1964 pack $w.global.$name -side top -anchor w -fill x
1965 }
1966
51f4d16b
SP
1967 bind $w <Visibility> "grab $w; focus $w"
1968 bind $w <Key-Escape> "destroy $w"
e01b4221 1969 wm title $w "$appname ($reponame): Options"
51f4d16b
SP
1970 tkwait window $w
1971}
1972
92148d80 1973proc do_restore_defaults {} {
7b64d0b7 1974 global font_descs default_config repo_config
92148d80
SP
1975 global repo_config_new global_config_new
1976
1977 foreach name [array names default_config] {
1978 set repo_config_new($name) $default_config($name)
1979 set global_config_new($name) $default_config($name)
1980 }
1981
1982 foreach option $font_descs {
1983 set name [lindex $option 0]
7b64d0b7 1984 set repo_config(gui.$name) $default_config(gui.$name)
92148d80
SP
1985 }
1986 apply_config
1987
1988 foreach option $font_descs {
1989 set name [lindex $option 0]
1990 set font [lindex $option 1]
1991 set global_config_new(gui.$font^^family) \
1992 [font configure $font -family]
1993 set global_config_new(gui.$font^^size) \
1994 [font configure $font -size]
1995 }
1996}
1997
1998proc do_save_config {w} {
1999 if {[catch {save_config} err]} {
2000 error_popup "Failed to completely save options:\n\n$err"
2001 }
358d8de8 2002 reshow_diff
92148d80
SP
2003 destroy $w
2004}
2005
24263b77
SP
2006proc toggle_or_diff {w x y} {
2007 global file_lists ui_index ui_other
2008 global last_clicked selected_paths
131f503b 2009
cb07fc2a
SP
2010 set pos [split [$w index @$x,$y] .]
2011 set lno [lindex $pos 0]
2012 set col [lindex $pos 1]
24263b77
SP
2013 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2014 if {$path eq {}} {
2015 set last_clicked {}
2016 return
2017 }
2018
2019 set last_clicked [list $w $lno]
2020 array unset selected_paths
2021 $ui_index tag remove in_sel 0.0 end
2022 $ui_other tag remove in_sel 0.0 end
cb07fc2a 2023
24263b77 2024 if {$col == 0} {
04b39382
SP
2025 update_index \
2026 "Including [short_path $path]" \
2027 [list $path] \
2028 {set ui_status_value {Ready.}}
24263b77 2029 } else {
03e4ec53 2030 show_diff $path $w $lno
cb07fc2a
SP
2031 }
2032}
2033
24263b77 2034proc add_one_to_selection {w x y} {
7f1df79b 2035 global file_lists
24263b77 2036 global last_clicked selected_paths
7f1df79b 2037
cb07fc2a
SP
2038 set pos [split [$w index @$x,$y] .]
2039 set lno [lindex $pos 0]
2040 set col [lindex $pos 1]
24263b77
SP
2041 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2042 if {$path eq {}} {
2043 set last_clicked {}
2044 return
2045 }
cb07fc2a 2046
24263b77
SP
2047 set last_clicked [list $w $lno]
2048 if {[catch {set in_sel $selected_paths($path)}]} {
2049 set in_sel 0
2050 }
2051 if {$in_sel} {
2052 unset selected_paths($path)
2053 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2054 } else {
2055 set selected_paths($path) 1
2056 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2057 }
2058}
2059
2060proc add_range_to_selection {w x y} {
2061 global file_lists
2062 global last_clicked selected_paths
2063
2064 if {[lindex $last_clicked 0] ne $w} {
2065 toggle_or_diff $w $x $y
2066 return
cb07fc2a 2067 }
24263b77
SP
2068
2069 set pos [split [$w index @$x,$y] .]
2070 set lno [lindex $pos 0]
2071 set lc [lindex $last_clicked 1]
2072 if {$lc < $lno} {
2073 set begin $lc
2074 set end $lno
2075 } else {
2076 set begin $lno
2077 set end $lc
2078 }
2079
2080 foreach path [lrange $file_lists($w) \
2081 [expr {$begin - 1}] \
2082 [expr {$end - 1}]] {
2083 set selected_paths($path) 1
2084 }
2085 $w tag add in_sel $begin.0 [expr {$end + 1}].0
cb07fc2a
SP
2086}
2087
2088######################################################################
2089##
92148d80 2090## config defaults
cb07fc2a 2091
00f949fb 2092set cursor_ptr arrow
b4946930
SP
2093font create font_diff -family Courier -size 10
2094font create font_ui
2095catch {
2096 label .dummy
2097 eval font configure font_ui [font actual [.dummy cget -font]]
2098 destroy .dummy
2099}
2100
92148d80
SP
2101font create font_uibold
2102font create font_diffbold
cb07fc2a 2103
16fccd7a
SP
2104set M1B M1
2105set M1T M1
043f7011 2106if {$tcl_platform(platform) eq {windows}} {
16fccd7a
SP
2107 set M1B Control
2108 set M1T Ctrl
2109} elseif {[is_MacOSX]} {
2110 set M1B M1
2111 set M1T Cmd
e210e674
SP
2112}
2113
92148d80
SP
2114proc apply_config {} {
2115 global repo_config font_descs
2116
2117 foreach option $font_descs {
2118 set name [lindex $option 0]
2119 set font [lindex $option 1]
2120 if {[catch {
2121 foreach {cn cv} $repo_config(gui.$name) {
2122 font configure $font $cn $cv
2123 }
2124 } err]} {
2125 error_popup "Invalid font specified in gui.$name:\n\n$err"
2126 }
2127 foreach {cn cv} [font configure $font] {
2128 font configure ${font}bold $cn $cv
2129 }
2130 font configure ${font}bold -weight bold
2131 }
2132}
2133
2134set default_config(gui.trustmtime) false
ebf336b9 2135set default_config(gui.pullsummary) true
f7f8d322 2136set default_config(gui.partialinclude) false
358d8de8 2137set default_config(gui.diffcontext) 5
92148d80
SP
2138set default_config(gui.fontui) [font configure font_ui]
2139set default_config(gui.fontdiff) [font configure font_diff]
2140set font_descs {
2141 {fontui font_ui {Main Font}}
2142 {fontdiff font_diff {Diff/Console Font}}
2143}
6bbd1cb9 2144load_config 0
92148d80
SP
2145apply_config
2146
2147######################################################################
2148##
2149## ui construction
2150
cb07fc2a 2151# -- Menu Bar
b4946930 2152menu .mbar -tearoff 0
cb07fc2a 2153.mbar add cascade -label Project -menu .mbar.project
9861671d 2154.mbar add cascade -label Edit -menu .mbar.edit
cb07fc2a 2155.mbar add cascade -label Commit -menu .mbar.commit
4ccdab02
SP
2156if {!$single_commit} {
2157 .mbar add cascade -label Fetch -menu .mbar.fetch
2158 .mbar add cascade -label Pull -menu .mbar.pull
2159 .mbar add cascade -label Push -menu .mbar.push
2160}
cb07fc2a
SP
2161. configure -menu .mbar
2162
2163# -- Project Menu
2164menu .mbar.project
6f6eed28 2165.mbar.project add command -label Visualize \
cb07fc2a 2166 -command do_gitk \
b4946930 2167 -font font_ui
4ccdab02
SP
2168if {!$single_commit} {
2169 .mbar.project add command -label {Repack Database} \
2170 -command do_repack \
2171 -font font_ui
2172}
cb07fc2a
SP
2173.mbar.project add command -label Quit \
2174 -command do_quit \
e210e674 2175 -accelerator $M1T-Q \
b4946930 2176 -font font_ui
cb07fc2a 2177
9861671d
SP
2178# -- Edit Menu
2179#
2180menu .mbar.edit
2181.mbar.edit add command -label Undo \
2182 -command {catch {[focus] edit undo}} \
2183 -accelerator $M1T-Z \
b4946930 2184 -font font_ui
9861671d
SP
2185.mbar.edit add command -label Redo \
2186 -command {catch {[focus] edit redo}} \
2187 -accelerator $M1T-Y \
b4946930 2188 -font font_ui
9861671d
SP
2189.mbar.edit add separator
2190.mbar.edit add command -label Cut \
2191 -command {catch {tk_textCut [focus]}} \
2192 -accelerator $M1T-X \
b4946930 2193 -font font_ui
9861671d
SP
2194.mbar.edit add command -label Copy \
2195 -command {catch {tk_textCopy [focus]}} \
2196 -accelerator $M1T-C \
b4946930 2197 -font font_ui
9861671d
SP
2198.mbar.edit add command -label Paste \
2199 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
2200 -accelerator $M1T-V \
b4946930 2201 -font font_ui
9861671d
SP
2202.mbar.edit add command -label Delete \
2203 -command {catch {[focus] delete sel.first sel.last}} \
2204 -accelerator Del \
b4946930 2205 -font font_ui
9861671d
SP
2206.mbar.edit add separator
2207.mbar.edit add command -label {Select All} \
2208 -command {catch {[focus] tag add sel 0.0 end}} \
2209 -accelerator $M1T-A \
b4946930 2210 -font font_ui
51f4d16b
SP
2211.mbar.edit add separator
2212.mbar.edit add command -label {Options...} \
2213 -command do_options \
2214 -font font_ui
9861671d 2215
cb07fc2a
SP
2216# -- Commit Menu
2217menu .mbar.commit
2218.mbar.commit add command -label Rescan \
2219 -command do_rescan \
e210e674 2220 -accelerator F5 \
b4946930 2221 -font font_ui
e210e674
SP
2222lappend disable_on_lock \
2223 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e57ca85e
SP
2224.mbar.commit add command -label {Amend Last Commit} \
2225 -command do_amend_last \
b4946930 2226 -font font_ui
e57ca85e
SP
2227lappend disable_on_lock \
2228 [list .mbar.commit entryconf [.mbar.commit index last] -state]
7fe7e733
SP
2229.mbar.commit add command -label {Include All Files} \
2230 -command do_include_all \
49b86f01 2231 -accelerator $M1T-I \
b4946930 2232 -font font_ui
e210e674
SP
2233lappend disable_on_lock \
2234 [list .mbar.commit entryconf [.mbar.commit index last] -state]
131f503b
SP
2235.mbar.commit add command -label {Sign Off} \
2236 -command do_signoff \
e210e674 2237 -accelerator $M1T-S \
b4946930 2238 -font font_ui
131f503b
SP
2239.mbar.commit add command -label Commit \
2240 -command do_commit \
e210e674 2241 -accelerator $M1T-Return \
b4946930 2242 -font font_ui
e210e674
SP
2243lappend disable_on_lock \
2244 [list .mbar.commit entryconf [.mbar.commit index last] -state]
cb07fc2a 2245
4ccdab02
SP
2246if {!$single_commit} {
2247 # -- Fetch Menu
2248 menu .mbar.fetch
cb07fc2a 2249
4ccdab02
SP
2250 # -- Pull Menu
2251 menu .mbar.pull
cb07fc2a 2252
4ccdab02
SP
2253 # -- Push Menu
2254 menu .mbar.push
2255}
8c0ce436 2256
cb07fc2a
SP
2257# -- Main Window Layout
2258panedwindow .vpane -orient vertical
2259panedwindow .vpane.files -orient horizontal
6f6eed28 2260.vpane add .vpane.files -sticky nsew -height 100 -width 400
cb07fc2a
SP
2261pack .vpane -anchor n -side top -fill both -expand 1
2262
2263# -- Index File List
cb07fc2a
SP
2264frame .vpane.files.index -height 100 -width 400
2265label .vpane.files.index.title -text {Modified Files} \
2266 -background green \
b4946930 2267 -font font_ui
cb07fc2a
SP
2268text $ui_index -background white -borderwidth 0 \
2269 -width 40 -height 10 \
b4946930 2270 -font font_ui \
6c6dd01a 2271 -cursor $cursor_ptr \
cb07fc2a 2272 -yscrollcommand {.vpane.files.index.sb set} \
cb07fc2a
SP
2273 -state disabled
2274scrollbar .vpane.files.index.sb -command [list $ui_index yview]
2275pack .vpane.files.index.title -side top -fill x
2276pack .vpane.files.index.sb -side right -fill y
2277pack $ui_index -side left -fill both -expand 1
2278.vpane.files add .vpane.files.index -sticky nsew
2279
2280# -- Other (Add) File List
cb07fc2a
SP
2281frame .vpane.files.other -height 100 -width 100
2282label .vpane.files.other.title -text {Untracked Files} \
2283 -background red \
b4946930 2284 -font font_ui
cb07fc2a
SP
2285text $ui_other -background white -borderwidth 0 \
2286 -width 40 -height 10 \
b4946930 2287 -font font_ui \
6c6dd01a 2288 -cursor $cursor_ptr \
cb07fc2a 2289 -yscrollcommand {.vpane.files.other.sb set} \
cb07fc2a
SP
2290 -state disabled
2291scrollbar .vpane.files.other.sb -command [list $ui_other yview]
2292pack .vpane.files.other.title -side top -fill x
2293pack .vpane.files.other.sb -side right -fill y
2294pack $ui_other -side left -fill both -expand 1
2295.vpane.files add .vpane.files.other -sticky nsew
2296
24263b77
SP
2297foreach i [list $ui_index $ui_other] {
2298 $i tag conf in_diff -font font_uibold
2299 $i tag conf in_sel \
2300 -background [$i cget -foreground] \
2301 -foreground [$i cget -background]
2302}
2303unset i
131f503b 2304
0fb8f9ce 2305# -- Diff and Commit Area
8009dcdc 2306frame .vpane.lower -height 300 -width 400
0fb8f9ce
SP
2307frame .vpane.lower.commarea
2308frame .vpane.lower.diff -relief sunken -borderwidth 1
2309pack .vpane.lower.commarea -side top -fill x
2310pack .vpane.lower.diff -side bottom -fill both -expand 1
2311.vpane add .vpane.lower -stick nsew
cb07fc2a
SP
2312
2313# -- Commit Area Buttons
0fb8f9ce
SP
2314frame .vpane.lower.commarea.buttons
2315label .vpane.lower.commarea.buttons.l -text {} \
cb07fc2a
SP
2316 -anchor w \
2317 -justify left \
b4946930 2318 -font font_ui
0fb8f9ce
SP
2319pack .vpane.lower.commarea.buttons.l -side top -fill x
2320pack .vpane.lower.commarea.buttons -side left -fill y
131f503b 2321
0fb8f9ce 2322button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
cb07fc2a 2323 -command do_rescan \
b4946930 2324 -font font_ui
0fb8f9ce 2325pack .vpane.lower.commarea.buttons.rescan -side top -fill x
390adaea
SP
2326lappend disable_on_lock \
2327 {.vpane.lower.commarea.buttons.rescan conf -state}
131f503b 2328
0fb8f9ce 2329button .vpane.lower.commarea.buttons.amend -text {Amend Last} \
e57ca85e 2330 -command do_amend_last \
b4946930 2331 -font font_ui
0fb8f9ce 2332pack .vpane.lower.commarea.buttons.amend -side top -fill x
390adaea
SP
2333lappend disable_on_lock \
2334 {.vpane.lower.commarea.buttons.amend conf -state}
e57ca85e 2335
7fe7e733
SP
2336button .vpane.lower.commarea.buttons.incall -text {Include All} \
2337 -command do_include_all \
b4946930 2338 -font font_ui
7fe7e733 2339pack .vpane.lower.commarea.buttons.incall -side top -fill x
390adaea
SP
2340lappend disable_on_lock \
2341 {.vpane.lower.commarea.buttons.incall conf -state}
131f503b 2342
0fb8f9ce 2343button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
131f503b 2344 -command do_signoff \
b4946930 2345 -font font_ui
0fb8f9ce 2346pack .vpane.lower.commarea.buttons.signoff -side top -fill x
131f503b 2347
0fb8f9ce 2348button .vpane.lower.commarea.buttons.commit -text {Commit} \
cb07fc2a 2349 -command do_commit \
b4946930 2350 -font font_ui
0fb8f9ce 2351pack .vpane.lower.commarea.buttons.commit -side top -fill x
390adaea
SP
2352lappend disable_on_lock \
2353 {.vpane.lower.commarea.buttons.commit conf -state}
cb07fc2a
SP
2354
2355# -- Commit Message Buffer
0fb8f9ce
SP
2356frame .vpane.lower.commarea.buffer
2357set ui_comm .vpane.lower.commarea.buffer.t
2358set ui_coml .vpane.lower.commarea.buffer.l
bd1e2b40 2359label $ui_coml -text {Commit Message:} \
cb07fc2a
SP
2360 -anchor w \
2361 -justify left \
b4946930 2362 -font font_ui
bd1e2b40
SP
2363trace add variable commit_type write {uplevel #0 {
2364 switch -glob $commit_type \
2365 initial {$ui_coml conf -text {Initial Commit Message:}} \
2366 amend {$ui_coml conf -text {Amended Commit Message:}} \
2367 merge {$ui_coml conf -text {Merge Commit Message:}} \
2368 * {$ui_coml conf -text {Commit Message:}}
2369}}
cb07fc2a 2370text $ui_comm -background white -borderwidth 1 \
9861671d 2371 -undo true \
b2c6fcf1 2372 -maxundo 20 \
9861671d 2373 -autoseparators true \
cb07fc2a 2374 -relief sunken \
0fb8f9ce 2375 -width 75 -height 9 -wrap none \
b4946930 2376 -font font_diff \
6c6dd01a 2377 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
390adaea
SP
2378scrollbar .vpane.lower.commarea.buffer.sby \
2379 -command [list $ui_comm yview]
bd1e2b40 2380pack $ui_coml -side top -fill x
0fb8f9ce 2381pack .vpane.lower.commarea.buffer.sby -side right -fill y
cb07fc2a 2382pack $ui_comm -side left -fill y
0fb8f9ce
SP
2383pack .vpane.lower.commarea.buffer -side left -fill y
2384
0e794311
SP
2385# -- Commit Message Buffer Context Menu
2386#
2387menu $ui_comm.ctxm -tearoff 0
2388$ui_comm.ctxm add command -label "Cut" \
b4946930 2389 -font font_ui \
0e794311
SP
2390 -command "tk_textCut $ui_comm"
2391$ui_comm.ctxm add command -label "Copy" \
b4946930 2392 -font font_ui \
0e794311
SP
2393 -command "tk_textCopy $ui_comm"
2394$ui_comm.ctxm add command -label "Paste" \
b4946930 2395 -font font_ui \
0e794311
SP
2396 -command "tk_textPaste $ui_comm"
2397$ui_comm.ctxm add command -label "Delete" \
b4946930 2398 -font font_ui \
0e794311
SP
2399 -command "$ui_comm delete sel.first sel.last"
2400$ui_comm.ctxm add separator
2401$ui_comm.ctxm add command -label "Select All" \
b4946930 2402 -font font_ui \
0e794311
SP
2403 -command "$ui_comm tag add sel 0.0 end"
2404$ui_comm.ctxm add command -label "Copy All" \
b4946930 2405 -font font_ui \
0e794311
SP
2406 -command "
2407 $ui_comm tag add sel 0.0 end
2408 tk_textCopy $ui_comm
2409 $ui_comm tag remove sel 0.0 end
2410 "
2411$ui_comm.ctxm add separator
2412$ui_comm.ctxm add command -label "Sign Off" \
b4946930 2413 -font font_ui \
0e794311 2414 -command do_signoff
16fccd7a 2415bind_button3 $ui_comm "tk_popup $ui_comm.ctxm %X %Y"
0e794311 2416
0fb8f9ce
SP
2417# -- Diff Header
2418set ui_fname_value {}
2419set ui_fstatus_value {}
2420frame .vpane.lower.diff.header -background orange
3e7b0e1d
SP
2421label .vpane.lower.diff.header.l4 \
2422 -textvariable ui_fstatus_value \
2423 -background orange \
2424 -width $max_status_desc \
2425 -anchor w \
2426 -justify left \
2427 -font font_ui
0fb8f9ce
SP
2428label .vpane.lower.diff.header.l1 -text {File:} \
2429 -background orange \
b4946930 2430 -font font_ui
c11b5f20 2431set ui_fname .vpane.lower.diff.header.l2
fce89e46
SP
2432label $ui_fname \
2433 -textvariable ui_fname_value \
0fb8f9ce 2434 -background orange \
fce89e46
SP
2435 -anchor w \
2436 -justify left \
b4946930 2437 -font font_ui
c11b5f20 2438menu $ui_fname.ctxm -tearoff 0
fce89e46 2439$ui_fname.ctxm add command -label "Copy" \
c11b5f20 2440 -font font_ui \
fce89e46
SP
2441 -command {
2442 clipboard clear
2443 clipboard append \
2444 -format STRING \
2445 -type STRING \
2446 -- $ui_fname_value
2447 }
c11b5f20 2448bind_button3 $ui_fname "tk_popup $ui_fname.ctxm %X %Y"
3e7b0e1d 2449pack .vpane.lower.diff.header.l4 -side left
0fb8f9ce 2450pack .vpane.lower.diff.header.l1 -side left
3e7b0e1d 2451pack $ui_fname -fill x
0fb8f9ce
SP
2452
2453# -- Diff Body
2454frame .vpane.lower.diff.body
2455set ui_diff .vpane.lower.diff.body.t
2456text $ui_diff -background white -borderwidth 0 \
2457 -width 80 -height 15 -wrap none \
b4946930 2458 -font font_diff \
0fb8f9ce
SP
2459 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
2460 -yscrollcommand {.vpane.lower.diff.body.sby set} \
0fb8f9ce
SP
2461 -state disabled
2462scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
2463 -command [list $ui_diff xview]
2464scrollbar .vpane.lower.diff.body.sby -orient vertical \
2465 -command [list $ui_diff yview]
2466pack .vpane.lower.diff.body.sbx -side bottom -fill x
2467pack .vpane.lower.diff.body.sby -side right -fill y
2468pack $ui_diff -side left -fill both -expand 1
2469pack .vpane.lower.diff.header -side top -fill x
2470pack .vpane.lower.diff.body -side bottom -fill both -expand 1
2471
2472$ui_diff tag conf dm -foreground red
2473$ui_diff tag conf dp -foreground blue
b4946930
SP
2474$ui_diff tag conf di -foreground {#00a000}
2475$ui_diff tag conf dni -foreground {#a000a0}
2476$ui_diff tag conf da -font font_diffbold
2477$ui_diff tag conf bold -font font_diffbold
cb07fc2a 2478
0e794311
SP
2479# -- Diff Body Context Menu
2480#
2481menu $ui_diff.ctxm -tearoff 0
2482$ui_diff.ctxm add command -label "Copy" \
b4946930 2483 -font font_ui \
0e794311
SP
2484 -command "tk_textCopy $ui_diff"
2485$ui_diff.ctxm add command -label "Select All" \
b4946930 2486 -font font_ui \
0e794311
SP
2487 -command "$ui_diff tag add sel 0.0 end"
2488$ui_diff.ctxm add command -label "Copy All" \
b4946930 2489 -font font_ui \
0e794311
SP
2490 -command "
2491 $ui_diff tag add sel 0.0 end
2492 tk_textCopy $ui_diff
2493 $ui_diff tag remove sel 0.0 end
2494 "
2c26e6f5
SP
2495$ui_diff.ctxm add separator
2496$ui_diff.ctxm add command -label "Decrease Font Size" \
b4946930
SP
2497 -font font_ui \
2498 -command {incr_font_size font_diff -1}
2c26e6f5 2499$ui_diff.ctxm add command -label "Increase Font Size" \
b4946930
SP
2500 -font font_ui \
2501 -command {incr_font_size font_diff 1}
358d8de8
SP
2502$ui_diff.ctxm add separator
2503$ui_diff.ctxm add command -label "Show Less Context" \
2504 -font font_ui \
2505 -command {if {$ui_fname_value ne {}
2506 && $repo_config(gui.diffcontext) >= 2} {
2507 incr repo_config(gui.diffcontext) -1
2508 reshow_diff
2509 }}
2510$ui_diff.ctxm add command -label "Show More Context" \
2511 -font font_ui \
2512 -command {if {$ui_fname_value ne {}} {
2513 incr repo_config(gui.diffcontext)
2514 reshow_diff
2515 }}
2516$ui_diff.ctxm add separator
8009dcdc
SP
2517$ui_diff.ctxm add command -label {Options...} \
2518 -font font_ui \
2519 -command do_options
16fccd7a 2520bind_button3 $ui_diff "tk_popup $ui_diff.ctxm %X %Y"
0e794311 2521
cb07fc2a
SP
2522# -- Status Bar
2523set ui_status_value {Initializing...}
2524label .status -textvariable ui_status_value \
2525 -anchor w \
2526 -justify left \
2527 -borderwidth 1 \
2528 -relief sunken \
b4946930 2529 -font font_ui
cb07fc2a
SP
2530pack .status -anchor w -side bottom -fill x
2531
2d19516d
SP
2532# -- Load geometry
2533catch {
51f4d16b 2534set gm $repo_config(gui.geometry)
c4fe7728
SP
2535wm geometry . [lindex $gm 0]
2536.vpane sash place 0 \
2537 [lindex [.vpane sash coord 0] 0] \
2538 [lindex $gm 1]
2539.vpane.files sash place 0 \
2540 [lindex $gm 2] \
2541 [lindex [.vpane.files sash coord 0] 1]
c4fe7728 2542unset gm
390adaea 2543}
2d19516d 2544
cb07fc2a 2545# -- Key Bindings
ec6b424a 2546bind $ui_comm <$M1B-Key-Return> {do_commit;break}
49b86f01
SP
2547bind $ui_comm <$M1B-Key-i> {do_include_all;break}
2548bind $ui_comm <$M1B-Key-I> {do_include_all;break}
9861671d
SP
2549bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
2550bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
2551bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
2552bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
2553bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
2554bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
2555bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2556bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2557
2558bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
2559bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
2560bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
2561bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
2562bind $ui_diff <$M1B-Key-v> {break}
2563bind $ui_diff <$M1B-Key-V> {break}
2564bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2565bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
b2c6fcf1
SP
2566bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
2567bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
2568bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
2569bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
49b86f01 2570
07123f40
SP
2571bind . <Destroy> do_quit
2572bind all <Key-F5> do_rescan
2573bind all <$M1B-Key-r> do_rescan
2574bind all <$M1B-Key-R> do_rescan
2575bind . <$M1B-Key-s> do_signoff
2576bind . <$M1B-Key-S> do_signoff
49b86f01
SP
2577bind . <$M1B-Key-i> do_include_all
2578bind . <$M1B-Key-I> do_include_all
07123f40
SP
2579bind . <$M1B-Key-Return> do_commit
2580bind all <$M1B-Key-q> do_quit
2581bind all <$M1B-Key-Q> do_quit
2582bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
2583bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
cb07fc2a 2584foreach i [list $ui_index $ui_other] {
24263b77
SP
2585 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
2586 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
2587 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
cb07fc2a 2588}
62aac80b
SP
2589unset i
2590
2591set file_lists($ui_index) [list]
2592set file_lists($ui_other) [list]
cb07fc2a 2593
ec6b424a 2594wm title . "$appname ([file normalize [file dirname $gitdir]])"
cb07fc2a 2595focus -force $ui_comm
4ccdab02
SP
2596if {!$single_commit} {
2597 load_all_remotes
2598 populate_remote_menu .mbar.fetch From fetch_from
2599 populate_remote_menu .mbar.push To push_to
2600 populate_pull_menu .mbar.pull
2601}
8f52548a 2602after 1 do_rescan