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