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