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