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