]> git.ipfire.org Git - thirdparty/git.git/blame - git-gui.sh
Merge branch 'st/selected-text-colors'
[thirdparty/git.git] / git-gui.sh
CommitLineData
bd11b82d 1#!/bin/sh
cb07fc2a 2# Tcl ignores the next line -*- tcl -*- \
4e817d1a
SP
3 if test "z$*" = zversion \
4 || test "z$*" = z--version; \
5 then \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
7 exit; \
8 fi; \
2f7c9a7f
SP
9 argv0=$0; \
10 exec wish "$argv0" -- "$@"
cb07fc2a 11
7e81d4ee 12set appvers {@@GITGUI_VERSION@@}
2473543c
PT
13set copyright [string map [list (c) \u00a9] {
14Copyright (c) 2006-2010 Shawn Pearce, et. al.
bdc9ea20 15
0499b24a
SP
16This program is free software; you can redistribute it and/or modify
17it under the terms of the GNU General Public License as published by
18the Free Software Foundation; either version 2 of the License, or
19(at your option) any later version.
20
21This program is distributed in the hope that it will be useful,
22but WITHOUT ANY WARRANTY; without even the implied warranty of
23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24GNU General Public License for more details.
25
26You should have received a copy of the GNU General Public License
63100874 27along with this program; if not, see <http://www.gnu.org/licenses/>.}]
cb07fc2a 28
f522c9b5 29######################################################################
cfb07cca
SP
30##
31## Tcl/Tk sanity check
32
8a8efbe4
PY
33if {[catch {package require Tcl 8.5} err]
34 || [catch {package require Tk 8.5} err]
cfb07cca
SP
35} {
36 catch {wm withdraw .}
37 tk_messageBox \
38 -icon error \
39 -type ok \
9cb268c4 40 -title "git-gui: fatal error" \
cfb07cca
SP
41 -message $err
42 exit 1
43}
44
63c4024f 45catch {rename send {}} ; # What an evil concept...
cff93397 46
fc703c20
SP
47######################################################################
48##
49## locate our library
50
a3b3ae35
DT
51if { [info exists ::env(GIT_GUI_LIB_DIR) ] } {
52 set oguilib $::env(GIT_GUI_LIB_DIR)
53} else {
54 set oguilib {@@GITGUI_LIBDIR@@}
55}
fc703c20
SP
56set oguirel {@@GITGUI_RELATIVE@@}
57if {$oguirel eq {1}} {
9534c9fb
JS
58 set oguilib [file dirname [file normalize $argv0]]
59 if {[file tail $oguilib] eq {git-core}} {
60 set oguilib [file dirname $oguilib]
61 }
62 set oguilib [file dirname $oguilib]
fc703c20 63 set oguilib [file join $oguilib share git-gui lib]
d4b0ccd9 64 set oguimsg [file join $oguilib msgs]
fc703c20
SP
65} elseif {[string match @@* $oguirel]} {
66 set oguilib [file join [file dirname [file normalize $argv0]] lib]
d4b0ccd9
SP
67 set oguimsg [file join [file dirname [file normalize $argv0]] po]
68} else {
69 set oguimsg [file join $oguilib msgs]
fc703c20
SP
70}
71unset oguirel
72
cd12901b
SP
73######################################################################
74##
75## enable verbose loading?
76
77if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
78 unset _verbose
79 rename auto_load real__auto_load
80 proc auto_load {name args} {
81 puts stderr "auto_load $name"
82 return [uplevel 1 real__auto_load $name $args]
83 }
84 rename source real__source
cdc6aba8
PT
85 proc source {args} {
86 puts stderr "source $args"
87 uplevel 1 [linsert $args 0 real__source]
cd12901b 88 }
c0d2c38d 89 if {[tk windowingsystem] eq "win32"} { console show }
cd12901b
SP
90}
91
c950c66e 92######################################################################
d4b0ccd9
SP
93##
94## Internationalization (i18n) through msgcat and gettext. See
95## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
96
97package require msgcat
146d73a3 98
35b6f72f
PT
99# Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
100if {[tk windowingsystem] eq "win32"
101 && [package vcompare [package provide msgcat] 1.4.4] < 0
102} then {
103 proc _mc_update_locale {} {
104 set key {HKEY_CURRENT_USER\Control Panel\Desktop}
105 if {![catch {
106 package require registry
107 set uilocale [registry get $key "PreferredUILanguages"]
108 msgcat::ConvertLocale [string map {- _} [lindex $uilocale 0]]
109 } uilocale]} {
110 if {[string length $uilocale] > 0} {
111 msgcat::mclocale $uilocale
112 }
113 }
114 }
115 _mc_update_locale
116}
117
ab0d33c4 118proc _mc_trim {fmt} {
146d73a3
SP
119 set cmk [string first @@ $fmt]
120 if {$cmk > 0} {
ab0d33c4 121 return [string range $fmt 0 [expr {$cmk - 1}]]
146d73a3 122 }
ab0d33c4
SP
123 return $fmt
124}
125
126proc mc {en_fmt args} {
127 set fmt [_mc_trim [::msgcat::mc $en_fmt]]
128 if {[catch {set msg [eval [list format $fmt] $args]} err]} {
129 set msg [eval [list format [_mc_trim $en_fmt]] $args]
130 }
131 return $msg
146d73a3
SP
132}
133
31bb1d1b
SP
134proc strcat {args} {
135 return [join $args {}]
136}
137
d4b0ccd9
SP
138::msgcat::mcload $oguimsg
139unset oguimsg
140
7d2017e7
SH
141######################################################################
142##
143## On Mac, bring the current Wish process window to front
144
145if {[tk windowingsystem] eq "aqua"} {
146 catch {
147 exec osascript -e [format {
148 tell application "System Events"
149 set frontmost of processes whose unix id is %d to true
150 end tell
151 } [pid]]
152 }
153}
154
d4b0ccd9 155######################################################################
c950c66e
SP
156##
157## read only globals
158
0b2bc460 159set _appname {Git Gui}
c950c66e 160set _gitdir {}
21985a11 161set _gitworktree {}
29e5573d 162set _isbare {}
20ddfcaa 163set _gitexec {}
3eb5682b 164set _githtmldir {}
c950c66e 165set _reponame {}
20ddfcaa 166set _iscygwin {}
0b812616 167set _search_path {}
62f9a632 168set _shellpath {@@SHELL_PATH@@}
c950c66e 169
16dd62ac
SP
170set _trace [lsearch -exact $argv --trace]
171if {$_trace >= 0} {
172 set argv [lreplace $argv $_trace $_trace]
173 set _trace 1
c42939d2 174 if {[tk windowingsystem] eq "win32"} { console show }
16dd62ac
SP
175} else {
176 set _trace 0
177}
178
9d04278a
HV
179# variable for the last merged branch (useful for a default when deleting
180# branches).
181set _last_merged_branch {}
182
62f9a632 183proc shellpath {} {
d5257fb3
PT
184 global _shellpath env
185 if {[string match @@* $_shellpath]} {
186 if {[info exists env(SHELL)]} {
187 return $env(SHELL)
188 } else {
189 return /bin/sh
190 }
191 }
62f9a632
MM
192 return $_shellpath
193}
194
c950c66e
SP
195proc appname {} {
196 global _appname
197 return $_appname
198}
199
c2758a17 200proc gitdir {args} {
c950c66e 201 global _gitdir
c2758a17
SP
202 if {$args eq {}} {
203 return $_gitdir
204 }
0b812616 205 return [eval [list file join $_gitdir] $args]
c950c66e
SP
206}
207
20ddfcaa
SP
208proc gitexec {args} {
209 global _gitexec
210 if {$_gitexec eq {}} {
81347223 211 if {[catch {set _gitexec [git --exec-path]} err]} {
20ddfcaa
SP
212 error "Git not installed?\n\n$err"
213 }
0b812616
SP
214 if {[is_Cygwin]} {
215 set _gitexec [exec cygpath \
216 --windows \
217 --absolute \
218 $_gitexec]
219 } else {
220 set _gitexec [file normalize $_gitexec]
221 }
20ddfcaa
SP
222 }
223 if {$args eq {}} {
224 return $_gitexec
225 }
0b812616 226 return [eval [list file join $_gitexec] $args]
20ddfcaa
SP
227}
228
3eb5682b
MH
229proc githtmldir {args} {
230 global _githtmldir
231 if {$_githtmldir eq {}} {
232 if {[catch {set _githtmldir [git --html-path]}]} {
233 # Git not installed or option not yet supported
234 return {}
235 }
236 if {[is_Cygwin]} {
237 set _githtmldir [exec cygpath \
238 --windows \
239 --absolute \
240 $_githtmldir]
241 } else {
242 set _githtmldir [file normalize $_githtmldir]
243 }
244 }
245 if {$args eq {}} {
246 return $_githtmldir
247 }
248 return [eval [list file join $_githtmldir] $args]
249}
250
c950c66e 251proc reponame {} {
d36cd968 252 return $::_reponame
c950c66e 253}
da5239dc 254
20ddfcaa 255proc is_MacOSX {} {
20ddfcaa
SP
256 if {[tk windowingsystem] eq {aqua}} {
257 return 1
258 }
259 return 0
260}
261
262proc is_Windows {} {
d36cd968 263 if {$::tcl_platform(platform) eq {windows}} {
20ddfcaa
SP
264 return 1
265 }
266 return 0
267}
268
269proc is_Cygwin {} {
d36cd968 270 global _iscygwin
20ddfcaa 271 if {$_iscygwin eq {}} {
d36cd968 272 if {$::tcl_platform(platform) eq {windows}} {
20ddfcaa
SP
273 if {[catch {set p [exec cygpath --windir]} err]} {
274 set _iscygwin 0
275 } else {
276 set _iscygwin 1
577c7e8f
PT
277 # Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
278 if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
279 set _iscygwin 0
280 }
20ddfcaa
SP
281 }
282 } else {
283 set _iscygwin 0
284 }
285 }
286 return $_iscygwin
287}
288
cf25ddc8
SP
289proc is_enabled {option} {
290 global enabled_options
291 if {[catch {set on $enabled_options($option)}]} {return 0}
292 return $on
293}
294
295proc enable_option {option} {
296 global enabled_options
297 set enabled_options($option) 1
298}
299
300proc disable_option {option} {
301 global enabled_options
302 set enabled_options($option) 0
303}
304
2d19516d
SP
305######################################################################
306##
307## config
308
51f4d16b
SP
309proc is_many_config {name} {
310 switch -glob -- $name {
24f7c64b 311 gui.recentrepo -
51f4d16b
SP
312 remote.*.fetch -
313 remote.*.push
314 {return 1}
315 *
316 {return 0}
317 }
318}
2d19516d 319
c539449b
SP
320proc is_config_true {name} {
321 global repo_config
322 if {[catch {set v $repo_config($name)}]} {
323 return 0
12b219f7
BW
324 }
325 set v [string tolower $v]
326 if {$v eq {} || $v eq {true} || $v eq {1} || $v eq {yes} || $v eq {on}} {
c539449b
SP
327 return 1
328 } else {
329 return 0
330 }
331}
332
1fbaccad
CP
333proc is_config_false {name} {
334 global repo_config
335 if {[catch {set v $repo_config($name)}]} {
336 return 0
12b219f7
BW
337 }
338 set v [string tolower $v]
339 if {$v eq {false} || $v eq {0} || $v eq {no} || $v eq {off}} {
1fbaccad
CP
340 return 1
341 } else {
342 return 0
343 }
344}
345
61f82ce7
SP
346proc get_config {name} {
347 global repo_config
348 if {[catch {set v $repo_config($name)}]} {
349 return {}
350 } else {
351 return $v
352 }
353}
354
29e5573d
GB
355proc is_bare {} {
356 global _isbare
357 global _gitdir
358 global _gitworktree
359
360 if {$_isbare eq {}} {
361 if {[catch {
362 set _bare [git rev-parse --is-bare-repository]
363 switch -- $_bare {
364 true { set _isbare 1 }
365 false { set _isbare 0}
366 default { throw }
367 }
368 }]} {
369 if {[is_config_true core.bare]
370 || ($_gitworktree eq {}
371 && [lindex [file split $_gitdir] end] ne {.git})} {
372 set _isbare 1
373 } else {
374 set _isbare 0
375 }
376 }
377 }
378 return $_isbare
379}
380
81347223
SP
381######################################################################
382##
383## handy utils
384
16dd62ac
SP
385proc _trace_exec {cmd} {
386 if {!$::_trace} return
387 set d {}
388 foreach v $cmd {
389 if {$d ne {}} {
390 append d { }
391 }
392 if {[regexp {[ \t\r\n'"$?*]} $v]} {
393 set v [sq $v]
394 }
395 append d $v
396 }
397 puts stderr $d
398}
399
2810a58d
PT
400#'" fix poor old emacs font-lock mode
401
0b812616
SP
402proc _git_cmd {name} {
403 global _git_cmd_path
404
405 if {[catch {set v $_git_cmd_path($name)}]} {
406 switch -- $name {
70a7595c 407 version -
0b812616
SP
408 --version -
409 --exec-path { return [list $::_git $name] }
410 }
411
412 set p [gitexec git-$name$::_search_exe]
413 if {[file exists $p]} {
414 set v [list $p]
c136f2b8
SP
415 } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
416 # Try to determine what sort of magic will make
417 # git-$name go and do its thing, because native
418 # Tcl on Windows doesn't know it.
0b812616 419 #
c136f2b8
SP
420 set p [gitexec git-$name]
421 set f [open $p r]
422 set s [gets $f]
423 close $f
424
6e4ba05c 425 switch -glob -- [lindex $s 0] {
c136f2b8
SP
426 #!*sh { set i sh }
427 #!*perl { set i perl }
428 #!*python { set i python }
429 default { error "git-$name is not supported: $s" }
430 }
431
432 upvar #0 _$i interp
433 if {![info exists interp]} {
434 set interp [_which $i]
435 }
436 if {$interp eq {}} {
437 error "git-$name requires $i (not in PATH)"
438 }
6e4ba05c 439 set v [concat [list $interp] [lrange $s 1 end] [list $p]]
0b812616 440 } else {
c6729890
SP
441 # Assume it is builtin to git somehow and we
442 # aren't actually able to see a file for it.
443 #
444 set v [list $::_git $name]
0b812616
SP
445 }
446 set _git_cmd_path($name) $v
447 }
448 return $v
449}
450
79317e5d 451proc _which {what args} {
0b812616
SP
452 global env _search_exe _search_path
453
454 if {$_search_path eq {}} {
299077fb 455 if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
0b812616
SP
456 set _search_path [split [exec cygpath \
457 --windows \
458 --path \
459 --absolute \
460 $env(PATH)] {;}]
461 set _search_exe .exe
462 } elseif {[is_Windows]} {
be700fe3
SP
463 set gitguidir [file dirname [info script]]
464 regsub -all ";" $gitguidir "\\;" gitguidir
465 set env(PATH) "$gitguidir;$env(PATH)"
0b812616
SP
466 set _search_path [split $env(PATH) {;}]
467 set _search_exe .exe
468 } else {
469 set _search_path [split $env(PATH) :]
470 set _search_exe {}
471 }
472 }
473
79317e5d
SP
474 if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
475 set suffix {}
476 } else {
477 set suffix $_search_exe
478 }
479
0b812616 480 foreach p $_search_path {
79317e5d 481 set p [file join $p $what$suffix]
0b812616
SP
482 if {[file exists $p]} {
483 return [file normalize $p]
484 }
485 }
486 return {}
487}
488
7d076d56
PT
489# Test a file for a hashbang to identify executable scripts on Windows.
490proc is_shellscript {filename} {
491 if {![file exists $filename]} {return 0}
492 set f [open $filename r]
493 fconfigure $f -encoding binary
494 set magic [read $f 2]
495 close $f
496 return [expr {$magic eq "#!"}]
497}
498
499# Run a command connected via pipes on stdout.
500# This is for use with textconv filters and uses sh -c "..." to allow it to
501# contain a command with arguments. On windows we must check for shell
502# scripts specifically otherwise just call the filter command.
503proc open_cmd_pipe {cmd path} {
504 global env
505 if {![file executable [shellpath]]} {
506 set exe [auto_execok [lindex $cmd 0]]
507 if {[is_shellscript [lindex $exe 0]]} {
508 set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path]
509 } else {
510 set run [concat $exe [lrange $cmd 1 end] $path]
511 }
512 } else {
513 set run [list [shellpath] -c "$cmd \"\$0\"" $path]
514 }
515 return [open |$run r]
516}
517
6f62b4f7
SP
518proc _lappend_nice {cmd_var} {
519 global _nice
520 upvar $cmd_var cmd
521
522 if {![info exists _nice]} {
523 set _nice [_which nice]
9c898a18
HV
524 if {[catch {exec $_nice git version}]} {
525 set _nice {}
ff9db6c7
SS
526 } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
527 set _nice {}
9c898a18 528 }
6f62b4f7
SP
529 }
530 if {$_nice ne {}} {
531 lappend cmd $_nice
532 }
533}
534
81347223 535proc git {args} {
ae75e1e4
KB
536 set fd [eval [list git_read] $args]
537 fconfigure $fd -translation binary -encoding utf-8
538 set result [string trimright [read $fd] "\n"]
539 close $fd
16dd62ac
SP
540 if {$::_trace} {
541 puts stderr "< $result"
542 }
543 return $result
0b812616
SP
544}
545
74c4763c 546proc _open_stdout_stderr {cmd} {
16dd62ac 547 _trace_exec $cmd
74c4763c 548 if {[catch {
16dd62ac 549 set fd [open [concat [list | ] $cmd] r]
74c4763c
SP
550 } err]} {
551 if { [lindex $cmd end] eq {2>@1}
552 && $err eq {can not find channel named "1"}
553 } {
554 # Older versions of Tcl 8.4 don't have this 2>@1 IO
555 # redirect operator. Fallback to |& cat for those.
556 # The command was not actually started, so its safe
557 # to try to start it a second time.
558 #
559 set fd [open [concat \
16dd62ac 560 [list | ] \
74c4763c
SP
561 [lrange $cmd 0 end-1] \
562 [list |& cat] \
563 ] r]
564 } else {
565 error $err
566 }
567 }
6eb420ef 568 fconfigure $fd -eofchar {}
74c4763c
SP
569 return $fd
570}
571
0b812616 572proc git_read {args} {
16dd62ac 573 set opt [list]
0b812616
SP
574
575 while {1} {
576 switch -- [lindex $args 0] {
577 --nice {
6f62b4f7 578 _lappend_nice opt
0b812616
SP
579 }
580
581 --stderr {
582 lappend args 2>@1
583 }
584
585 default {
586 break
587 }
588
589 }
590
591 set args [lrange $args 1 end]
592 }
593
594 set cmdp [_git_cmd [lindex $args 0]]
595 set args [lrange $args 1 end]
596
74c4763c 597 return [_open_stdout_stderr [concat $opt $cmdp $args]]
0b812616
SP
598}
599
600proc git_write {args} {
16dd62ac 601 set opt [list]
0b812616
SP
602
603 while {1} {
604 switch -- [lindex $args 0] {
605 --nice {
6f62b4f7 606 _lappend_nice opt
0b812616
SP
607 }
608
609 default {
610 break
611 }
612
613 }
614
615 set args [lrange $args 1 end]
616 }
617
618 set cmdp [_git_cmd [lindex $args 0]]
619 set args [lrange $args 1 end]
620
16dd62ac
SP
621 _trace_exec [concat $opt $cmdp $args]
622 return [open [concat [list | ] $opt $cmdp $args] w]
81347223
SP
623}
624
ed76cb70
SP
625proc githook_read {hook_name args} {
626 set pchook [gitdir hooks $hook_name]
627 lappend args 2>@1
628
fbc0e7ac 629 # On Windows [file executable] might lie so we need to ask
ed76cb70
SP
630 # the shell if the hook is executable. Yes that's annoying.
631 #
fbc0e7ac 632 if {[is_Windows]} {
ed76cb70
SP
633 upvar #0 _sh interp
634 if {![info exists interp]} {
635 set interp [_which sh]
636 }
637 if {$interp eq {}} {
638 error "hook execution requires sh (not in PATH)"
639 }
640
641 set scr {if test -x "$1";then exec "$@";fi}
16dd62ac 642 set sh_c [list $interp -c $scr $interp $pchook]
ed76cb70
SP
643 return [_open_stdout_stderr [concat $sh_c $args]]
644 }
645
646 if {[file executable $pchook]} {
16dd62ac 647 return [_open_stdout_stderr [concat [list $pchook] $args]]
ed76cb70
SP
648 }
649
650 return {}
651}
652
e6131d30
AG
653proc kill_file_process {fd} {
654 set process [pid $fd]
655
656 catch {
657 if {[is_Windows]} {
3b422bc8 658 exec taskkill /pid $process
e6131d30
AG
659 } else {
660 exec kill $process
661 }
662 }
663}
664
1ffca60f
SP
665proc gitattr {path attr default} {
666 if {[catch {set r [git check-attr $attr -- $path]}]} {
667 set r unspecified
668 } else {
669 set r [join [lrange [split $r :] 2 end] :]
670 regsub {^ } $r {} r
671 }
672 if {$r eq {unspecified}} {
673 return $default
674 }
675 return $r
676}
677
7eafa2f1
SP
678proc sq {value} {
679 regsub -all ' $value "'\\''" value
680 return "'$value'"
681}
682
d41b43eb
SP
683proc load_current_branch {} {
684 global current_branch is_detached
685
fc4e8da7 686 set fd [open [gitdir HEAD] r]
39acfa3d 687 fconfigure $fd -translation binary -encoding utf-8
311e02a4 688 if {[gets $fd ref] < 1} {
fc4e8da7
SP
689 set ref {}
690 }
691 close $fd
311e02a4
SP
692
693 set pfx {ref: refs/heads/}
694 set len [string length $pfx]
695 if {[string equal -length $len $pfx $ref]} {
696 # We're on a branch. It might not exist. But
697 # HEAD looks good enough to be a branch.
698 #
d41b43eb
SP
699 set current_branch [string range $ref $len end]
700 set is_detached 0
311e02a4
SP
701 } else {
702 # Assume this is a detached head.
703 #
d41b43eb
SP
704 set current_branch HEAD
705 set is_detached 1
311e02a4 706 }
fc4e8da7
SP
707}
708
2739291b
SP
709auto_load tk_optionMenu
710rename tk_optionMenu real__tkOptionMenu
711proc tk_optionMenu {w varName args} {
712 set m [eval real__tkOptionMenu $w $varName $args]
713 $m configure -font font_ui
714 $w configure -font font_ui
715 return $m
716}
717
3849bfba
SP
718proc rmsel_tag {text} {
719 $text tag conf sel \
720 -background [$text cget -background] \
721 -foreground [$text cget -foreground] \
722 -borderwidth 0
c02efc13
ST
723 $text tag conf in_sel\
724 -background $color::select_bg \
725 -foreground $color::select_fg
3849bfba
SP
726 bind $text <Motion> break
727 return $text
728}
729
2810a58d 730wm withdraw .
a4bee597
SP
731set root_exists 0
732bind . <Visibility> {
733 bind . <Visibility> {}
734 set root_exists 1
735}
736
1bdd8a15
SP
737if {[is_Windows]} {
738 wm iconbitmap . -default $oguilib/git-gui.ico
f10d5b06 739 set ::tk::AlwaysShowSelection 1
c0d2c38d 740 bind . <Control-F2> {console show}
8c762125
AG
741
742 # Spoof an X11 display for SSH
743 if {![info exists env(DISPLAY)]} {
744 set env(DISPLAY) :9999
745 }
d1f2b362
GB
746} else {
747 catch {
748 image create photo gitlogo -width 16 -height 16
749
750 gitlogo put #33CC33 -to 7 0 9 2
751 gitlogo put #33CC33 -to 4 2 12 4
752 gitlogo put #33CC33 -to 7 4 9 6
753 gitlogo put #CC3333 -to 4 6 12 8
754 gitlogo put gray26 -to 4 9 6 10
755 gitlogo put gray26 -to 3 10 6 12
756 gitlogo put gray26 -to 8 9 13 11
757 gitlogo put gray26 -to 8 11 10 12
758 gitlogo put gray26 -to 11 11 13 14
759 gitlogo put gray26 -to 3 12 5 14
760 gitlogo put gray26 -to 5 13
761 gitlogo put gray26 -to 10 13
762 gitlogo put gray26 -to 4 14 12 15
763 gitlogo put gray26 -to 5 15 11 16
764 gitlogo redither
765
215d4fdb
SB
766 image create photo gitlogo32 -width 32 -height 32
767 gitlogo32 copy gitlogo -zoom 2 2
768
769 wm iconphoto . -default gitlogo gitlogo32
d1f2b362 770 }
1bdd8a15
SP
771}
772
a4bee597
SP
773######################################################################
774##
775## config defaults
776
777set cursor_ptr arrow
a4bee597 778font create font_ui
c80d7be5
PT
779if {[lsearch -exact [font names] TkDefaultFont] != -1} {
780 eval [linsert [font actual TkDefaultFont] 0 font configure font_ui]
781 eval [linsert [font actual TkFixedFont] 0 font create font_diff]
782} else {
783 font create font_diff -family Courier -size 10
784 catch {
785 label .dummy
786 eval font configure font_ui [font actual [.dummy cget -font]]
787 destroy .dummy
788 }
a4bee597
SP
789}
790
791font create font_uiitalic
792font create font_uibold
793font create font_diffbold
794font create font_diffitalic
795
796foreach class {Button Checkbutton Entry Label
a91be3fc 797 Labelframe Listbox Message
a4bee597
SP
798 Radiobutton Spinbox Text} {
799 option add *$class.font font_ui
800}
a91be3fc
DS
801if {![is_MacOSX]} {
802 option add *Menu.font font_ui
c80d7be5
PT
803 option add *Entry.borderWidth 1 startupFile
804 option add *Entry.relief sunken startupFile
805 option add *RadioButton.anchor w startupFile
a91be3fc 806}
a4bee597
SP
807unset class
808
809if {[is_Windows] || [is_MacOSX]} {
810 option add *Menu.tearOff 0
811}
812
813if {[is_MacOSX]} {
814 set M1B M1
815 set M1T Cmd
816} else {
817 set M1B Control
818 set M1T Ctrl
819}
820
821proc bind_button3 {w cmd} {
822 bind $w <Any-Button-3> $cmd
823 if {[is_MacOSX]} {
824 # Mac OS X sends Button-2 on right click through three-button mouse,
825 # or through trackpad right-clicking (two-finger touch + click).
826 bind $w <Any-Button-2> $cmd
827 bind $w <Control-Button-1> $cmd
828 }
829}
830
831proc apply_config {} {
832 global repo_config font_descs
833
834 foreach option $font_descs {
835 set name [lindex $option 0]
836 set font [lindex $option 1]
837 if {[catch {
48b8d2b3 838 set need_weight 1
a4bee597 839 foreach {cn cv} $repo_config(gui.$name) {
48b8d2b3
SP
840 if {$cn eq {-weight}} {
841 set need_weight 0
842 }
843 font configure $font $cn $cv
844 }
845 if {$need_weight} {
846 font configure $font -weight normal
a4bee597
SP
847 }
848 } err]} {
849 error_popup [strcat [mc "Invalid font specified in %s:" "gui.$name"] "\n\n$err"]
850 }
851 foreach {cn cv} [font configure $font] {
852 font configure ${font}bold $cn $cv
853 font configure ${font}italic $cn $cv
854 }
855 font configure ${font}bold -weight bold
856 font configure ${font}italic -slant italic
857 }
c80d7be5
PT
858
859 global use_ttk NS
860 set use_ttk 0
861 set NS {}
862 if {$repo_config(gui.usettk)} {
863 set use_ttk [package vsatisfies [package provide Tk] 8.5]
864 if {$use_ttk} {
865 set NS ttk
866 bind [winfo class .] <<ThemeChanged>> [list InitTheme]
867 pave_toplevel .
c02efc13 868 color::sync_with_theme
c80d7be5
PT
869 }
870 }
a4bee597
SP
871}
872
fe70225d 873set default_config(branch.autosetupmerge) true
7e30682c 874set default_config(merge.tool) {}
fb25092a 875set default_config(mergetool.keepbackup) true
a4bee597
SP
876set default_config(merge.diffstat) true
877set default_config(merge.summary) false
878set default_config(merge.verbosity) 2
879set default_config(user.name) {}
880set default_config(user.email) {}
881
72e6b002 882set default_config(gui.encoding) [encoding system]
a4bee597 883set default_config(gui.matchtrackingbranch) false
1fbaccad 884set default_config(gui.textconv) true
a4bee597
SP
885set default_config(gui.pruneduringfetch) false
886set default_config(gui.trustmtime) false
57cae87b 887set default_config(gui.fastcopyblame) false
d478056c 888set default_config(gui.maxrecentrepo) 10
57cae87b 889set default_config(gui.copyblamethreshold) 40
a9c80b83 890set default_config(gui.blamehistoryctx) 7
a4bee597 891set default_config(gui.diffcontext) 5
54531e7c 892set default_config(gui.diffopts) {}
11027d54 893set default_config(gui.commitmsgwidth) 75
a4bee597 894set default_config(gui.newbranchtemplate) {}
95b002ee 895set default_config(gui.spellingdictionary) {}
a4bee597
SP
896set default_config(gui.fontui) [font configure font_ui]
897set default_config(gui.fontdiff) [font configure font_diff]
dd6451f9
DZ
898# TODO: this option should be added to the git-config documentation
899set default_config(gui.maxfilesdisplayed) 5000
c80d7be5 900set default_config(gui.usettk) 1
e34789cc 901set default_config(gui.warndetachedcommit) 1
a43c5f51 902set default_config(gui.tabsize) 8
a4bee597
SP
903set font_descs {
904 {fontui font_ui {mc "Main Font"}}
905 {fontdiff font_diff {mc "Diff/Console Font"}}
906}
bb196e26 907set default_config(gui.stageuntracked) ask
e632b3c0 908set default_config(gui.displayuntracked) true
a4bee597 909
0b812616
SP
910######################################################################
911##
912## find git
913
914set _git [_which git]
915if {$_git eq {}} {
916 catch {wm withdraw .}
183a1d14
SP
917 tk_messageBox \
918 -icon error \
919 -type ok \
920 -title [mc "git-gui: fatal error"] \
921 -message [mc "Cannot find git in PATH."]
0b812616
SP
922 exit 1
923}
0b812616 924
54acdd95
SP
925######################################################################
926##
927## version check
928
d6967022 929if {[catch {set _git_version [git --version]} err]} {
54acdd95 930 catch {wm withdraw .}
875b7c93
SP
931 tk_messageBox \
932 -icon error \
933 -type ok \
c8c4854b 934 -title [mc "git-gui: fatal error"] \
875b7c93 935 -message "Cannot determine Git version:
54acdd95
SP
936
937$err
938
d6967022
SP
939[appname] requires Git 1.5.0 or later."
940 exit 1
941}
942if {![regsub {^git version } $_git_version {} _git_version]} {
943 catch {wm withdraw .}
875b7c93
SP
944 tk_messageBox \
945 -icon error \
946 -type ok \
c8c4854b 947 -title [mc "git-gui: fatal error"] \
31bb1d1b 948 -message [strcat [mc "Cannot parse Git version string:"] "\n\n$_git_version"]
54acdd95
SP
949 exit 1
950}
301dfaa9 951
67112c48 952proc get_trimmed_version {s} {
5c1b3913
ST
953 set r {}
954 foreach x [split $s -._] {
955 if {[string is integer -strict $x]} {
956 lappend r $x
957 } else {
958 break
959 }
960 }
961 return [join $r .]
67112c48 962}
301dfaa9 963set _real_git_version $_git_version
67112c48 964set _git_version [get_trimmed_version $_git_version]
d6967022 965
301dfaa9
SP
966if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
967 catch {wm withdraw .}
968 if {[tk_messageBox \
969 -icon warning \
970 -type yesno \
971 -default no \
972 -title "[appname]: warning" \
5c1b3913 973 -message [mc "Git version cannot be determined.
301dfaa9 974
1ac17950 975%s claims it is version '%s'.
301dfaa9 976
1ac17950 977%s requires at least Git 1.5.0 or later.
301dfaa9 978
1ac17950
CS
979Assume '%s' is version 1.5.0?
980" $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} {
301dfaa9
SP
981 set _git_version 1.5.0
982 } else {
983 exit 1
984 }
985}
986unset _real_git_version
987
d6967022
SP
988proc git-version {args} {
989 global _git_version
990
991 switch [llength $args] {
992 0 {
993 return $_git_version
54acdd95 994 }
d6967022
SP
995
996 2 {
997 set op [lindex $args 0]
998 set vr [lindex $args 1]
999 set cm [package vcompare $_git_version $vr]
1000 return [expr $cm $op 0]
1001 }
1002
1003 4 {
1004 set type [lindex $args 0]
1005 set name [lindex $args 1]
1006 set parm [lindex $args 2]
1007 set body [lindex $args 3]
1008
1009 if {($type ne {proc} && $type ne {method})} {
1010 error "Invalid arguments to git-version"
1011 }
1012 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
1013 error "Last arm of $type $name must be default"
1014 }
1015
1016 foreach {op vr cb} [lrange $body 0 end-2] {
1017 if {[git-version $op $vr]} {
1018 return [uplevel [list $type $name $parm $cb]]
1019 }
1020 }
1021
1022 return [uplevel [list $type $name $parm [lindex $body end]]]
1023 }
1024
1025 default {
1026 error "git-version >= x"
1027 }
1028
1029 }
1030}
1031
1032if {[git-version < 1.5]} {
54acdd95 1033 catch {wm withdraw .}
875b7c93
SP
1034 tk_messageBox \
1035 -icon error \
1036 -type ok \
c8c4854b 1037 -title [mc "git-gui: fatal error"] \
875b7c93 1038 -message "[appname] requires Git 1.5.0 or later.
d6967022
SP
1039
1040You are using [git-version]:
1041
1042[git --version]"
54acdd95
SP
1043 exit 1
1044}
54acdd95 1045
875b7c93
SP
1046######################################################################
1047##
1048## configure our library
1049
875b7c93
SP
1050set idx [file join $oguilib tclIndex]
1051if {[catch {set fd [open $idx r]} err]} {
1052 catch {wm withdraw .}
1053 tk_messageBox \
1054 -icon error \
1055 -type ok \
c8c4854b 1056 -title [mc "git-gui: fatal error"] \
875b7c93
SP
1057 -message $err
1058 exit 1
1059}
1060if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
1061 set idx [list]
1062 while {[gets $fd n] >= 0} {
1063 if {$n ne {} && ![string match #* $n]} {
1064 lappend idx $n
1065 }
1066 }
1067} else {
1068 set idx {}
1069}
1070close $fd
1071
1072if {$idx ne {}} {
1073 set loaded [list]
1074 foreach p $idx {
1075 if {[lsearch -exact $loaded $p] >= 0} continue
1076 source [file join $oguilib $p]
1077 lappend loaded $p
1078 }
1079 unset loaded p
1080} else {
1081 set auto_path [concat [list $oguilib] $auto_path]
1082}
fc703c20 1083unset -nocomplain idx fd
875b7c93 1084
ba7cc660 1085######################################################################
69f85ffa
SP
1086##
1087## config file parsing
1088
f00d504a 1089git-version proc _parse_config {arr_name args} {
85f7a94b
SP
1090 >= 1.5.3 {
1091 upvar $arr_name arr
1092 array unset arr
1093 set buf {}
1094 catch {
a5bb31fb
SP
1095 set fd_rc [eval \
1096 [list git_read config] \
1097 $args \
1098 [list --null --list]]
e2039e94 1099 fconfigure $fd_rc -translation binary -encoding utf-8
85f7a94b
SP
1100 set buf [read $fd_rc]
1101 close $fd_rc
1102 }
1103 foreach line [split $buf "\0"] {
1104 if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} {
1105 if {[is_many_config $name]} {
1106 lappend arr($name) $value
1107 } else {
1108 set arr($name) $value
1109 }
12b219f7
BW
1110 } elseif {[regexp {^([^\n]+)$} $line line name]} {
1111 # no value given, but interpreting them as
1112 # boolean will be handled as true
1113 set arr($name) {}
85f7a94b
SP
1114 }
1115 }
1116 }
f00d504a
SP
1117 default {
1118 upvar $arr_name arr
1119 array unset arr
69f85ffa 1120 catch {
f00d504a 1121 set fd_rc [eval [list git_read config --list] $args]
69f85ffa
SP
1122 while {[gets $fd_rc line] >= 0} {
1123 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
1124 if {[is_many_config $name]} {
f00d504a 1125 lappend arr($name) $value
69f85ffa 1126 } else {
f00d504a 1127 set arr($name) $value
69f85ffa 1128 }
12b219f7
BW
1129 } elseif {[regexp {^([^=]+)$} $line line name]} {
1130 # no value given, but interpreting them as
1131 # boolean will be handled as true
1132 set arr($name) {}
69f85ffa
SP
1133 }
1134 }
1135 close $fd_rc
1136 }
1137 }
f00d504a 1138}
69f85ffa 1139
f00d504a 1140proc load_config {include_global} {
153ad78b 1141 global repo_config global_config system_config default_config
f00d504a
SP
1142
1143 if {$include_global} {
153ad78b 1144 _parse_config system_config --system
f00d504a 1145 _parse_config global_config --global
69f85ffa 1146 }
f00d504a 1147 _parse_config repo_config
69f85ffa
SP
1148
1149 foreach name [array names default_config] {
153ad78b
AG
1150 if {[catch {set v $system_config($name)}]} {
1151 set system_config($name) $default_config($name)
1152 }
1153 }
1154 foreach name [array names system_config] {
69f85ffa 1155 if {[catch {set v $global_config($name)}]} {
153ad78b 1156 set global_config($name) $system_config($name)
69f85ffa
SP
1157 }
1158 if {[catch {set v $repo_config($name)}]} {
153ad78b 1159 set repo_config($name) $system_config($name)
69f85ffa
SP
1160 }
1161 }
1162}
1163
1164######################################################################
ba7cc660
SP
1165##
1166## feature option selection
1167
0b2bc460 1168if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
ba7cc660
SP
1169 unset _junk
1170} else {
1171 set subcommand gui
1172}
1173if {$subcommand eq {gui.sh}} {
1174 set subcommand gui
1175}
1176if {$subcommand eq {gui} && [llength $argv] > 0} {
1177 set subcommand [lindex $argv 0]
1178 set argv [lrange $argv 1 end]
1179}
1180
1181enable_option multicommit
1182enable_option branch
1183enable_option transport
c52c9452 1184disable_option bare
ba7cc660
SP
1185
1186switch -- $subcommand {
1187browser -
1188blame {
c52c9452
SP
1189 enable_option bare
1190
ba7cc660
SP
1191 disable_option multicommit
1192 disable_option branch
1193 disable_option transport
1194}
1195citool {
1196 enable_option singlecommit
1e65c622 1197 enable_option retcode
ba7cc660
SP
1198
1199 disable_option multicommit
1200 disable_option branch
1201 disable_option transport
1e65c622
AG
1202
1203 while {[llength $argv] > 0} {
1204 set a [lindex $argv 0]
1205 switch -- $a {
1206 --amend {
1207 enable_option initialamend
1208 }
1209 --nocommit {
1210 enable_option nocommit
1211 enable_option nocommitmsg
1212 }
1213 --commitmsg {
1214 disable_option nocommitmsg
1215 }
1216 default {
1217 break
1218 }
1219 }
1220
1221 set argv [lrange $argv 1 end]
1222 }
ba7cc660
SP
1223}
1224}
1225
e29c0d10
AG
1226######################################################################
1227##
1228## execution environment
1229
1230set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1231
1232# Suggest our implementation of askpass, if none is set
1233if {![info exists env(SSH_ASKPASS)]} {
1234 set env(SSH_ASKPASS) [gitexec git-gui--askpass]
1235}
1236
2d19516d
SP
1237######################################################################
1238##
1239## repository setup
1240
bb4812bc 1241set picked 0
c6127856
SP
1242if {[catch {
1243 set _gitdir $env(GIT_DIR)
1244 set _prefix {}
1245 }]
1246 && [catch {
87cd09f4
GB
1247 # beware that from the .git dir this sets _gitdir to .
1248 # and _prefix to the empty string
c6127856
SP
1249 set _gitdir [git rev-parse --git-dir]
1250 set _prefix [git rev-parse --show-prefix]
1251 } err]} {
ab08b363
SP
1252 load_config 1
1253 apply_config
1254 choose_repository::pick
bb4812bc 1255 set picked 1
2d19516d 1256}
87cd09f4
GB
1257
1258# we expand the _gitdir when it's just a single dot (i.e. when we're being
1259# run from the .git dir itself) lest the routines to find the worktree
1260# get confused
1261if {$_gitdir eq "."} {
1262 set _gitdir [pwd]
1263}
1264
20ddfcaa 1265if {![file isdirectory $_gitdir] && [is_Cygwin]} {
2f7c9a7f 1266 catch {set _gitdir [exec cygpath --windows $_gitdir]}
20ddfcaa 1267}
c950c66e 1268if {![file isdirectory $_gitdir]} {
dbccbbda 1269 catch {wm withdraw .}
31bb1d1b 1270 error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
dbccbbda
SP
1271 exit 1
1272}
21985a11
GB
1273# _gitdir exists, so try loading the config
1274load_config 0
1275apply_config
38ec8d3e
PT
1276
1277# v1.7.0 introduced --show-toplevel to return the canonical work-tree
ce3e848b 1278if {[package vcompare $_git_version 1.7.0] >= 0} {
4394faf6
JM
1279 if { [is_Cygwin] } {
1280 catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]}
1281 } else {
1282 set _gitworktree [git rev-parse --show-toplevel]
1283 }
38ec8d3e
PT
1284} else {
1285 # try to set work tree from environment, core.worktree or use
1286 # cdup to obtain a relative path to the top of the worktree. If
1287 # run from the top, the ./ prefix ensures normalize expands pwd.
1288 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
1289 set _gitworktree [get_config core.worktree]
1290 if {$_gitworktree eq ""} {
1291 set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
1292 }
13a3d637 1293 }
21985a11 1294}
38ec8d3e 1295
c80d25db 1296if {$_prefix ne {}} {
21985a11
GB
1297 if {$_gitworktree eq {}} {
1298 regsub -all {[^/]+/} $_prefix ../ cdup
1299 } else {
1300 set cdup $_gitworktree
1301 }
c80d25db
SP
1302 if {[catch {cd $cdup} err]} {
1303 catch {wm withdraw .}
31bb1d1b 1304 error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
c80d25db
SP
1305 exit 1
1306 }
21985a11 1307 set _gitworktree [pwd]
c80d25db
SP
1308 unset cdup
1309} elseif {![is_enabled bare]} {
29e5573d 1310 if {[is_bare]} {
c52c9452 1311 catch {wm withdraw .}
29e5573d 1312 error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
c52c9452
SP
1313 exit 1
1314 }
21985a11
GB
1315 if {$_gitworktree eq {}} {
1316 set _gitworktree [file dirname $_gitdir]
1317 }
1318 if {[catch {cd $_gitworktree} err]} {
c52c9452 1319 catch {wm withdraw .}
21985a11 1320 error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
c52c9452
SP
1321 exit 1
1322 }
21985a11 1323 set _gitworktree [pwd]
dbccbbda 1324}
c52c9452
SP
1325set _reponame [file split [file normalize $_gitdir]]
1326if {[lindex $_reponame end] eq {.git}} {
1327 set _reponame [lindex $_reponame end-1]
1328} else {
1329 set _reponame [lindex $_reponame end]
2d19516d 1330}
2d19516d 1331
a9fa11fe
GB
1332set env(GIT_DIR) $_gitdir
1333set env(GIT_WORK_TREE) $_gitworktree
1334
372ef954
SP
1335######################################################################
1336##
1337## global init
1338
1339set current_diff_path {}
1340set current_diff_side {}
1341set diff_actions [list]
372ef954
SP
1342
1343set HEAD {}
1344set PARENT {}
1345set MERGE_HEAD [list]
1346set commit_type {}
ba41b5b3 1347set commit_type_is_amend 0
372ef954
SP
1348set empty_tree {}
1349set current_branch {}
d41b43eb 1350set is_detached 0
372ef954 1351set current_diff_path {}
9c9f5fa9 1352set is_3way_diff 0
cd846aa1 1353set is_submodule_diff 0
3e34838c 1354set is_conflict_diff 0
8052e788 1355set diff_empty_count 0
a4fa2f0a
PY
1356set last_revert {}
1357set last_revert_enc {}
372ef954 1358
a9786bb4
AG
1359set nullid "0000000000000000000000000000000000000000"
1360set nullid2 "0000000000000000000000000000000000000001"
1361
cb07fc2a
SP
1362######################################################################
1363##
e210e674 1364## task management
cb07fc2a 1365
8f52548a 1366set rescan_active 0
131f503b 1367set diff_active 0
24263b77 1368set last_clicked {}
131f503b 1369
e210e674
SP
1370set disable_on_lock [list]
1371set index_lock_type none
1372
1373proc lock_index {type} {
1374 global index_lock_type disable_on_lock
131f503b 1375
043f7011 1376 if {$index_lock_type eq {none}} {
e210e674
SP
1377 set index_lock_type $type
1378 foreach w $disable_on_lock {
1379 uplevel #0 $w disabled
1380 }
1381 return 1
53716a7b 1382 } elseif {$index_lock_type eq "begin-$type"} {
e210e674 1383 set index_lock_type $type
131f503b
SP
1384 return 1
1385 }
1386 return 0
1387}
cb07fc2a 1388
e210e674
SP
1389proc unlock_index {} {
1390 global index_lock_type disable_on_lock
1391
1392 set index_lock_type none
1393 foreach w $disable_on_lock {
1394 uplevel #0 $w normal
1395 }
1396}
1397
1398######################################################################
1399##
1400## status
1401
f18e40a1 1402proc repository_state {ctvar hdvar mhvar} {
c950c66e 1403 global current_branch
f18e40a1
SP
1404 upvar $ctvar ct $hdvar hd $mhvar mh
1405
1406 set mh [list]
ec6b424a 1407
d41b43eb 1408 load_current_branch
81347223 1409 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
4539eacd 1410 set hd {}
ec6b424a 1411 set ct initial
f18e40a1
SP
1412 return
1413 }
1414
c2758a17 1415 set merge_head [gitdir MERGE_HEAD]
f18e40a1 1416 if {[file exists $merge_head]} {
ec6b424a 1417 set ct merge
f18e40a1
SP
1418 set fd_mh [open $merge_head r]
1419 while {[gets $fd_mh line] >= 0} {
1420 lappend mh $line
1421 }
1422 close $fd_mh
1423 return
ec6b424a 1424 }
f18e40a1
SP
1425
1426 set ct normal
ec6b424a
SP
1427}
1428
4539eacd
SP
1429proc PARENT {} {
1430 global PARENT empty_tree
1431
f18e40a1
SP
1432 set p [lindex $PARENT 0]
1433 if {$p ne {}} {
1434 return $p
4539eacd
SP
1435 }
1436 if {$empty_tree eq {}} {
81347223 1437 set empty_tree [git mktree << {}]
4539eacd
SP
1438 }
1439 return $empty_tree
1440}
1441
1e65c622 1442proc force_amend {} {
ba41b5b3 1443 global commit_type_is_amend
1e65c622
AG
1444 global HEAD PARENT MERGE_HEAD commit_type
1445
1446 repository_state newType newHEAD newMERGE_HEAD
1447 set HEAD $newHEAD
1448 set PARENT $newHEAD
1449 set MERGE_HEAD $newMERGE_HEAD
1450 set commit_type $newType
1451
ba41b5b3 1452 set commit_type_is_amend 1
1e65c622
AG
1453 do_select_commit_type
1454}
1455
46aaf90b 1456proc rescan {after {honor_trustmtime 1}} {
f18e40a1 1457 global HEAD PARENT MERGE_HEAD commit_type
699d5601 1458 global ui_index ui_workdir ui_comm
8f52548a 1459 global rescan_active file_states
cf25ddc8 1460 global repo_config
cb07fc2a 1461
8f52548a 1462 if {$rescan_active > 0 || ![lock_index read]} return
cb07fc2a 1463
f18e40a1 1464 repository_state newType newHEAD newMERGE_HEAD
4539eacd 1465 if {[string match amend* $commit_type]
f18e40a1
SP
1466 && $newType eq {normal}
1467 && $newHEAD eq $HEAD} {
e57ca85e 1468 } else {
f18e40a1
SP
1469 set HEAD $newHEAD
1470 set PARENT $newHEAD
1471 set MERGE_HEAD $newMERGE_HEAD
1472 set commit_type $newType
e57ca85e
SP
1473 }
1474
cb07fc2a 1475 array unset file_states
cb07fc2a 1476
1e0a92fd
SP
1477 if {!$::GITGUI_BCK_exists &&
1478 (![$ui_comm edit modified]
1479 || [string trim [$ui_comm get 0.0 end]] eq {})} {
b2f3bb1b 1480 if {[string match amend* $commit_type]} {
fda1ba02 1481 } elseif {[load_message GITGUI_MSG utf-8]} {
2cd1fd1f 1482 } elseif {[run_prepare_commit_msg_hook]} {
131f503b
SP
1483 } elseif {[load_message MERGE_MSG]} {
1484 } elseif {[load_message SQUASH_MSG]} {
627c87f8 1485 } elseif {[load_message [get_config commit.template]]} {
131f503b 1486 }
b2c6fcf1 1487 $ui_comm edit reset
21d7744f 1488 $ui_comm edit modified false
131f503b
SP
1489 }
1490
46aaf90b 1491 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
8f52548a 1492 rescan_stage2 {} $after
e534f3a8 1493 } else {
8f52548a 1494 set rescan_active 1
1ac17950 1495 ui_status [mc "Refreshing file status..."]
0b812616
SP
1496 set fd_rf [git_read update-index \
1497 -q \
1498 --unmerged \
1499 --ignore-missing \
1500 --refresh \
1501 ]
e534f3a8 1502 fconfigure $fd_rf -blocking 0 -translation binary
390adaea 1503 fileevent $fd_rf readable \
8f52548a 1504 [list rescan_stage2 $fd_rf $after]
e534f3a8 1505 }
131f503b
SP
1506}
1507
2fe167b6 1508if {[is_Cygwin]} {
2fe167b6
SP
1509 set is_git_info_exclude {}
1510 proc have_info_exclude {} {
7f83aa2d 1511 global is_git_info_exclude
2fe167b6 1512
7f83aa2d
SP
1513 if {$is_git_info_exclude eq {}} {
1514 if {[catch {exec test -f [gitdir info exclude]}]} {
1515 set is_git_info_exclude 0
1516 } else {
1517 set is_git_info_exclude 1
2fe167b6 1518 }
2fe167b6 1519 }
7f83aa2d 1520 return $is_git_info_exclude
2fe167b6
SP
1521 }
1522} else {
1523 proc have_info_exclude {} {
1524 return [file readable [gitdir info exclude]]
1525 }
1526}
1527
8f52548a 1528proc rescan_stage2 {fd after} {
4539eacd 1529 global rescan_active buf_rdi buf_rdf buf_rlo
131f503b 1530
043f7011 1531 if {$fd ne {}} {
e534f3a8
SP
1532 read $fd
1533 if {![eof $fd]} return
1534 close $fd
1535 }
131f503b 1536
ce3e848b 1537 if {[package vcompare $::_git_version 1.6.3] >= 0} {
673eb4a0
SN
1538 set ls_others [list --exclude-standard]
1539 } else {
1540 set ls_others [list --exclude-per-directory=.gitignore]
1541 if {[have_info_exclude]} {
1542 lappend ls_others "--exclude-from=[gitdir info exclude]"
1543 }
1544 set user_exclude [get_config core.excludesfile]
1545 if {$user_exclude ne {} && [file readable $user_exclude]} {
1546 lappend ls_others "--exclude-from=[file normalize $user_exclude]"
1547 }
94a4dd9b 1548 }
cb07fc2a 1549
868c8752
SP
1550 set buf_rdi {}
1551 set buf_rdf {}
1552 set buf_rlo {}
1553
e632b3c0 1554 set rescan_active 2
1ac17950 1555 ui_status [mc "Scanning for modified files ..."]
e0db1dd7
JL
1556 if {[git-version >= "1.7.2"]} {
1557 set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]]
1558 } else {
1559 set fd_di [git_read diff-index --cached -z [PARENT]]
1560 }
0b812616 1561 set fd_df [git_read diff-files -z]
cb07fc2a 1562
51a989ba
SP
1563 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1564 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
e632b3c0 1565
8f52548a
SP
1566 fileevent $fd_di readable [list read_diff_index $fd_di $after]
1567 fileevent $fd_df readable [list read_diff_files $fd_df $after]
e632b3c0
MK
1568
1569 if {[is_config_true gui.displayuntracked]} {
1570 set fd_lo [eval git_read ls-files --others -z $ls_others]
1571 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
1572 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
1573 incr rescan_active
1574 }
cb07fc2a
SP
1575}
1576
fda1ba02 1577proc load_message {file {encoding {}}} {
c950c66e 1578 global ui_comm
131f503b 1579
c2758a17 1580 set f [gitdir $file]
e57ca85e 1581 if {[file isfile $f]} {
131f503b
SP
1582 if {[catch {set fd [open $f r]}]} {
1583 return 0
1584 }
6eb420ef 1585 fconfigure $fd -eofchar {}
fda1ba02
PT
1586 if {$encoding ne {}} {
1587 fconfigure $fd -encoding $encoding
1588 }
e57ca85e 1589 set content [string trim [read $fd]]
131f503b 1590 close $fd
4e55d19a 1591 regsub -all -line {[ \r\t]+$} $content {} content
131f503b
SP
1592 $ui_comm delete 0.0 end
1593 $ui_comm insert end $content
1594 return 1
1595 }
1596 return 0
1597}
1598
2cd1fd1f
JW
1599proc run_prepare_commit_msg_hook {} {
1600 global pch_error
1601
1602 # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
1603 # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
c5c45e1a 1604 # empty file but existent file.
2cd1fd1f
JW
1605
1606 set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
1607
1608 if {[file isfile [gitdir MERGE_MSG]]} {
1609 set pcm_source "merge"
1610 set fd_mm [open [gitdir MERGE_MSG] r]
af465c0c 1611 fconfigure $fd_mm -encoding utf-8
2cd1fd1f
JW
1612 puts -nonewline $fd_pcm [read $fd_mm]
1613 close $fd_mm
1614 } elseif {[file isfile [gitdir SQUASH_MSG]]} {
1615 set pcm_source "squash"
1616 set fd_sm [open [gitdir SQUASH_MSG] r]
af465c0c 1617 fconfigure $fd_sm -encoding utf-8
2cd1fd1f
JW
1618 puts -nonewline $fd_pcm [read $fd_sm]
1619 close $fd_sm
627c87f8
MS
1620 } elseif {[file isfile [get_config commit.template]]} {
1621 set pcm_source "template"
1622 set fd_sm [open [get_config commit.template] r]
1623 fconfigure $fd_sm -encoding utf-8
1624 puts -nonewline $fd_pcm [read $fd_sm]
1625 close $fd_sm
2cd1fd1f
JW
1626 } else {
1627 set pcm_source ""
1628 }
1629
1630 close $fd_pcm
1631
1632 set fd_ph [githook_read prepare-commit-msg \
1633 [gitdir PREPARE_COMMIT_MSG] $pcm_source]
1634 if {$fd_ph eq {}} {
1635 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1636 return 0;
1637 }
1638
1639 ui_status [mc "Calling prepare-commit-msg hook..."]
1640 set pch_error {}
1641
1642 fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
1643 fileevent $fd_ph readable \
1644 [list prepare_commit_msg_hook_wait $fd_ph]
1645
1646 return 1;
1647}
1648
1649proc prepare_commit_msg_hook_wait {fd_ph} {
1650 global pch_error
1651
1652 append pch_error [read $fd_ph]
1653 fconfigure $fd_ph -blocking 1
1654 if {[eof $fd_ph]} {
1655 if {[catch {close $fd_ph}]} {
1656 ui_status [mc "Commit declined by prepare-commit-msg hook."]
1657 hook_failed_popup prepare-commit-msg $pch_error
1658 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1659 exit 1
1660 } else {
1661 load_message PREPARE_COMMIT_MSG
1662 }
1663 set pch_error {}
1664 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1665 return
5c1b3913 1666 }
2cd1fd1f
JW
1667 fconfigure $fd_ph -blocking 0
1668 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1669}
1670
8f52548a 1671proc read_diff_index {fd after} {
cb07fc2a
SP
1672 global buf_rdi
1673
1674 append buf_rdi [read $fd]
868c8752
SP
1675 set c 0
1676 set n [string length $buf_rdi]
1677 while {$c < $n} {
1678 set z1 [string first "\0" $buf_rdi $c]
1679 if {$z1 == -1} break
1680 incr z1
1681 set z2 [string first "\0" $buf_rdi $z1]
1682 if {$z2 == -1} break
1683
868c8752 1684 incr c
86291555 1685 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
51a989ba 1686 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
1461c5f3 1687 merge_state \
e2039e94 1688 [encoding convertfrom utf-8 $p] \
86291555
SP
1689 [lindex $i 4]? \
1690 [list [lindex $i 0] [lindex $i 2]] \
1461c5f3
SP
1691 [list]
1692 set c $z2
86291555 1693 incr c
cb07fc2a 1694 }
868c8752
SP
1695 if {$c < $n} {
1696 set buf_rdi [string range $buf_rdi $c end]
1697 } else {
1698 set buf_rdi {}
1699 }
1700
8f52548a 1701 rescan_done $fd buf_rdi $after
cb07fc2a
SP
1702}
1703
8f52548a 1704proc read_diff_files {fd after} {
cb07fc2a
SP
1705 global buf_rdf
1706
1707 append buf_rdf [read $fd]
868c8752
SP
1708 set c 0
1709 set n [string length $buf_rdf]
1710 while {$c < $n} {
1711 set z1 [string first "\0" $buf_rdf $c]
1712 if {$z1 == -1} break
1713 incr z1
1714 set z2 [string first "\0" $buf_rdf $z1]
1715 if {$z2 == -1} break
1716
868c8752 1717 incr c
86291555 1718 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
51a989ba 1719 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
1461c5f3 1720 merge_state \
e2039e94 1721 [encoding convertfrom utf-8 $p] \
86291555 1722 ?[lindex $i 4] \
1461c5f3 1723 [list] \
86291555 1724 [list [lindex $i 0] [lindex $i 2]]
1461c5f3 1725 set c $z2
86291555 1726 incr c
868c8752
SP
1727 }
1728 if {$c < $n} {
1729 set buf_rdf [string range $buf_rdf $c end]
1730 } else {
1731 set buf_rdf {}
cb07fc2a 1732 }
868c8752 1733
8f52548a 1734 rescan_done $fd buf_rdf $after
cb07fc2a
SP
1735}
1736
8f52548a 1737proc read_ls_others {fd after} {
cb07fc2a
SP
1738 global buf_rlo
1739
1740 append buf_rlo [read $fd]
1741 set pck [split $buf_rlo "\0"]
1742 set buf_rlo [lindex $pck end]
1743 foreach p [lrange $pck 0 end-1] {
e2039e94 1744 set p [encoding convertfrom utf-8 $p]
89384101
SP
1745 if {[string index $p end] eq {/}} {
1746 set p [string range $p 0 end-1]
1747 }
1748 merge_state $p ?O
cb07fc2a 1749 }
8f52548a 1750 rescan_done $fd buf_rlo $after
cb07fc2a
SP
1751}
1752
8f52548a 1753proc rescan_done {fd buf after} {
f522c9b5 1754 global rescan_active current_diff_path
f7f8d322 1755 global file_states repo_config
7f1df79b 1756 upvar $buf to_clear
cb07fc2a 1757
f7f8d322
SP
1758 if {![eof $fd]} return
1759 set to_clear {}
1760 close $fd
8f52548a 1761 if {[incr rescan_active -1] > 0} return
93f654df 1762
24263b77 1763 prune_selection
f7f8d322
SP
1764 unlock_index
1765 display_all_files
7cf4566f
AG
1766 if {$current_diff_path ne {}} { reshow_diff $after }
1767 if {$current_diff_path eq {}} { select_first_diff $after }
cb07fc2a
SP
1768}
1769
24263b77
SP
1770proc prune_selection {} {
1771 global file_states selected_paths
1772
1773 foreach path [array names selected_paths] {
1774 if {[catch {set still_here $file_states($path)}]} {
1775 unset selected_paths($path)
1776 }
1777 }
1778}
1779
cb07fc2a
SP
1780######################################################################
1781##
f522c9b5 1782## ui helpers
cb07fc2a 1783
f522c9b5
SP
1784proc mapicon {w state path} {
1785 global all_icons
1786
1787 if {[catch {set r $all_icons($state$w)}]} {
1788 puts "error: no icon for $w state={$state} $path"
1789 return file_plain
1790 }
1791 return $r
1792}
cb07fc2a 1793
f522c9b5
SP
1794proc mapdesc {state path} {
1795 global all_descs
03e4ec53 1796
f522c9b5
SP
1797 if {[catch {set r $all_descs($state)}]} {
1798 puts "error: no desc for state={$state} $path"
1799 return $state
1800 }
1801 return $r
1802}
03e4ec53 1803
699d5601 1804proc ui_status {msg} {
906ab7f6
SP
1805 global main_status
1806 if {[info exists main_status]} {
1807 $main_status show $msg
1808 }
699d5601
SP
1809}
1810
d9c6469f 1811proc ui_ready {} {
906ab7f6
SP
1812 global main_status
1813 if {[info exists main_status]} {
d9c6469f 1814 $main_status show [mc "Ready."]
906ab7f6 1815 }
699d5601
SP
1816}
1817
f522c9b5
SP
1818proc escape_path {path} {
1819 regsub -all {\\} $path "\\\\" path
1820 regsub -all "\n" $path "\\n" path
1821 return $path
cb07fc2a
SP
1822}
1823
f522c9b5
SP
1824proc short_path {path} {
1825 return [escape_path [lindex [file split $path] end]]
7f1df79b
SP
1826}
1827
f522c9b5
SP
1828set next_icon_id 0
1829set null_sha1 [string repeat 0 40]
16403d0b 1830
f522c9b5
SP
1831proc merge_state {path new_state {head_info {}} {index_info {}}} {
1832 global file_states next_icon_id null_sha1
16403d0b 1833
f522c9b5
SP
1834 set s0 [string index $new_state 0]
1835 set s1 [string index $new_state 1]
16403d0b 1836
f522c9b5
SP
1837 if {[catch {set info $file_states($path)}]} {
1838 set state __
1839 set icon n[incr next_icon_id]
1840 } else {
1841 set state [lindex $info 0]
1842 set icon [lindex $info 1]
1843 if {$head_info eq {}} {set head_info [lindex $info 2]}
1844 if {$index_info eq {}} {set index_info [lindex $info 3]}
1845 }
16403d0b 1846
f522c9b5
SP
1847 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1848 elseif {$s0 eq {_}} {set s0 _}
124355d3 1849
f522c9b5
SP
1850 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1851 elseif {$s1 eq {_}} {set s1 _}
16403d0b 1852
f522c9b5
SP
1853 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1854 set head_info [list 0 $null_sha1]
1855 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1856 && $head_info eq {}} {
1857 set head_info $index_info
7ec2b69f
JL
1858 } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
1859 set index_info $head_info
1860 set head_info {}
f522c9b5 1861 }
16403d0b 1862
f522c9b5
SP
1863 set file_states($path) [list $s0$s1 $icon \
1864 $head_info $index_info \
1865 ]
1866 return $state
1867}
cb07fc2a 1868
f522c9b5
SP
1869proc display_file_helper {w path icon_name old_m new_m} {
1870 global file_lists
cb07fc2a 1871
f522c9b5 1872 if {$new_m eq {_}} {
156b2921 1873 set lno [lsearch -sorted -exact $file_lists($w) $path]
5f8b70b1 1874 if {$lno >= 0} {
f522c9b5 1875 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
5f8b70b1 1876 incr lno
f522c9b5
SP
1877 $w conf -state normal
1878 $w delete $lno.0 [expr {$lno + 1}].0
1879 $w conf -state disabled
03e4ec53 1880 }
f522c9b5
SP
1881 } elseif {$old_m eq {_} && $new_m ne {_}} {
1882 lappend file_lists($w) $path
1883 set file_lists($w) [lsort -unique $file_lists($w)]
1884 set lno [lsearch -sorted -exact $file_lists($w) $path]
1885 incr lno
1886 $w conf -state normal
1887 $w image create $lno.0 \
1888 -align center -padx 5 -pady 1 \
1889 -name $icon_name \
1890 -image [mapicon $w $new_m $path]
1891 $w insert $lno.1 "[escape_path $path]\n"
1892 $w conf -state disabled
1893 } elseif {$old_m ne $new_m} {
1894 $w conf -state normal
1895 $w image conf $icon_name -image [mapicon $w $new_m $path]
1896 $w conf -state disabled
03e4ec53 1897 }
f522c9b5
SP
1898}
1899
1900proc display_file {path state} {
1901 global file_states selected_paths
1902 global ui_index ui_workdir
03e4ec53 1903
f522c9b5 1904 set old_m [merge_state $path $state]
cb07fc2a 1905 set s $file_states($path)
f522c9b5
SP
1906 set new_m [lindex $s 0]
1907 set icon_name [lindex $s 1]
82cb8706 1908
f522c9b5
SP
1909 set o [string index $old_m 0]
1910 set n [string index $new_m 0]
1911 if {$o eq {U}} {
1912 set o _
1913 }
1914 if {$n eq {U}} {
1915 set n _
cb07fc2a 1916 }
f522c9b5 1917 display_file_helper $ui_index $path $icon_name $o $n
cb07fc2a 1918
f522c9b5
SP
1919 if {[string index $old_m 0] eq {U}} {
1920 set o U
1921 } else {
1922 set o [string index $old_m 1]
82cb8706 1923 }
f522c9b5
SP
1924 if {[string index $new_m 0] eq {U}} {
1925 set n U
1926 } else {
1927 set n [string index $new_m 1]
82cb8706 1928 }
f522c9b5
SP
1929 display_file_helper $ui_workdir $path $icon_name $o $n
1930
1931 if {$new_m eq {__}} {
1932 unset file_states($path)
1933 catch {unset selected_paths($path)}
cb07fc2a 1934 }
f522c9b5 1935}
cb07fc2a 1936
f522c9b5
SP
1937proc display_all_files_helper {w path icon_name m} {
1938 global file_lists
1939
1940 lappend file_lists($w) $path
1941 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1942 $w image create end \
1943 -align center -padx 5 -pady 1 \
1944 -name $icon_name \
1945 -image [mapicon $w $m $path]
1946 $w insert end "[escape_path $path]\n"
cb07fc2a
SP
1947}
1948
dd6451f9 1949set files_warning 0
f522c9b5
SP
1950proc display_all_files {} {
1951 global ui_index ui_workdir
1952 global file_states file_lists
1953 global last_clicked
dd6451f9 1954 global files_warning
cb07fc2a 1955
f522c9b5
SP
1956 $ui_index conf -state normal
1957 $ui_workdir conf -state normal
cb07fc2a 1958
f522c9b5
SP
1959 $ui_index delete 0.0 end
1960 $ui_workdir delete 0.0 end
1961 set last_clicked {}
cb07fc2a 1962
f522c9b5
SP
1963 set file_lists($ui_index) [list]
1964 set file_lists($ui_workdir) [list]
16403d0b 1965
dd6451f9
DZ
1966 set to_display [lsort [array names file_states]]
1967 set display_limit [get_config gui.maxfilesdisplayed]
a117fa21 1968 set displayed 0
dd6451f9 1969 foreach path $to_display {
f522c9b5
SP
1970 set s $file_states($path)
1971 set m [lindex $s 0]
1972 set icon_name [lindex $s 1]
1973
a117fa21
CK
1974 if {$displayed > $display_limit && [string index $m 1] eq {O} } {
1975 if {!$files_warning} {
1976 # do not repeatedly warn:
1977 set files_warning 1
1978 info_popup [mc "Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files." \
1979 $display_limit [llength $to_display]]
1980 }
1981 continue
1982 }
1983
f522c9b5
SP
1984 set s [string index $m 0]
1985 if {$s ne {U} && $s ne {_}} {
1986 display_all_files_helper $ui_index $path \
1987 $icon_name $s
16403d0b 1988 }
cb07fc2a 1989
f522c9b5
SP
1990 if {[string index $m 0] eq {U}} {
1991 set s U
1992 } else {
1993 set s [string index $m 1]
a25c5189 1994 }
f522c9b5
SP
1995 if {$s ne {_}} {
1996 display_all_files_helper $ui_workdir $path \
1997 $icon_name $s
a117fa21 1998 incr displayed
a25c5189
SP
1999 }
2000 }
2001
f522c9b5
SP
2002 $ui_index conf -state disabled
2003 $ui_workdir conf -state disabled
a25c5189
SP
2004}
2005
ec6b424a
SP
2006######################################################################
2007##
f522c9b5 2008## icons
ec6b424a 2009
f522c9b5
SP
2010set filemask {
2011#define mask_width 14
2012#define mask_height 15
2013static unsigned char mask_bits[] = {
5c1b3913
ST
2014 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2015 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
2016 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
f522c9b5 2017}
cb07fc2a
SP
2018
2019image create bitmap file_plain -background white -foreground black -data {
2020#define plain_width 14
2021#define plain_height 15
2022static unsigned char plain_bits[] = {
5c1b3913
ST
2023 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2024 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
2025 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2026} -maskdata $filemask
2027
2028image create bitmap file_mod -background white -foreground blue -data {
2029#define mod_width 14
2030#define mod_height 15
2031static unsigned char mod_bits[] = {
5c1b3913
ST
2032 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
2033 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2034 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2035} -maskdata $filemask
2036
131f503b
SP
2037image create bitmap file_fulltick -background white -foreground "#007000" -data {
2038#define file_fulltick_width 14
2039#define file_fulltick_height 15
2040static unsigned char file_fulltick_bits[] = {
5c1b3913
ST
2041 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
2042 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
2043 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2044} -maskdata $filemask
2045
cb07fc2a
SP
2046image create bitmap file_question -background white -foreground black -data {
2047#define file_question_width 14
2048#define file_question_height 15
2049static unsigned char file_question_bits[] = {
5c1b3913
ST
2050 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
2051 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
2052 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2053} -maskdata $filemask
2054
2055image create bitmap file_removed -background white -foreground red -data {
2056#define file_removed_width 14
2057#define file_removed_height 15
2058static unsigned char file_removed_bits[] = {
5c1b3913
ST
2059 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
2060 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
2061 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2062} -maskdata $filemask
2063
2064image create bitmap file_merge -background white -foreground blue -data {
2065#define file_merge_width 14
2066#define file_merge_height 15
2067static unsigned char file_merge_bits[] = {
5c1b3913
ST
2068 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
2069 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
2070 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
cb07fc2a
SP
2071} -maskdata $filemask
2072
e681cb7d 2073image create bitmap file_statechange -background white -foreground green -data {
bf5fe3f3
BW
2074#define file_statechange_width 14
2075#define file_statechange_height 15
e681cb7d 2076static unsigned char file_statechange_bits[] = {
5c1b3913
ST
2077 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
2078 0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
2079 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
e681cb7d
GH
2080} -maskdata $filemask
2081
6b292675 2082set ui_index .vpane.files.index.list
0812665e 2083set ui_workdir .vpane.files.workdir.list
21e409ad
SP
2084
2085set all_icons(_$ui_index) file_plain
0602de48 2086set all_icons(A$ui_index) file_plain
21e409ad
SP
2087set all_icons(M$ui_index) file_fulltick
2088set all_icons(D$ui_index) file_removed
2089set all_icons(U$ui_index) file_merge
e681cb7d 2090set all_icons(T$ui_index) file_statechange
21e409ad
SP
2091
2092set all_icons(_$ui_workdir) file_plain
2093set all_icons(M$ui_workdir) file_mod
2094set all_icons(D$ui_workdir) file_question
3b4db3c1 2095set all_icons(U$ui_workdir) file_merge
21e409ad 2096set all_icons(O$ui_workdir) file_plain
e681cb7d 2097set all_icons(T$ui_workdir) file_statechange
21e409ad 2098
131f503b 2099set max_status_desc 0
cb07fc2a 2100foreach i {
1ac17950
CS
2101 {__ {mc "Unmodified"}}
2102
2103 {_M {mc "Modified, not staged"}}
2104 {M_ {mc "Staged for commit"}}
2105 {MM {mc "Portions staged for commit"}}
2106 {MD {mc "Staged for commit, missing"}}
2107
e681cb7d 2108 {_T {mc "File type changed, not staged"}}
7587f4d3
BW
2109 {MT {mc "File type changed, old type staged for commit"}}
2110 {AT {mc "File type changed, old type staged for commit"}}
e681cb7d 2111 {T_ {mc "File type changed, staged"}}
7587f4d3
BW
2112 {TM {mc "File type change staged, modification not staged"}}
2113 {TD {mc "File type change staged, file missing"}}
e681cb7d 2114
1ac17950
CS
2115 {_O {mc "Untracked, not staged"}}
2116 {A_ {mc "Staged for commit"}}
2117 {AM {mc "Portions staged for commit"}}
2118 {AD {mc "Staged for commit, missing"}}
2119
2120 {_D {mc "Missing"}}
2121 {D_ {mc "Staged for removal"}}
2122 {DO {mc "Staged for removal, still present"}}
2123
ff515d81 2124 {_U {mc "Requires merge resolution"}}
1ac17950
CS
2125 {U_ {mc "Requires merge resolution"}}
2126 {UU {mc "Requires merge resolution"}}
2127 {UM {mc "Requires merge resolution"}}
2128 {UD {mc "Requires merge resolution"}}
ff515d81 2129 {UT {mc "Requires merge resolution"}}
cb07fc2a 2130 } {
1ac17950
CS
2131 set text [eval [lindex $i 1]]
2132 if {$max_status_desc < [string length $text]} {
2133 set max_status_desc [string length $text]
131f503b 2134 }
1ac17950 2135 set all_descs([lindex $i 0]) $text
cb07fc2a 2136}
21e409ad 2137unset i
cb07fc2a
SP
2138
2139######################################################################
2140##
2141## util
2142
35874c16
SP
2143proc scrollbar2many {list mode args} {
2144 foreach w $list {eval $w $mode $args}
2145}
2146
2147proc many2scrollbar {list mode sb top bottom} {
2148 $sb set $top $bottom
2149 foreach w $list {$w $mode moveto $top}
2150}
2151
b4946930
SP
2152proc incr_font_size {font {amt 1}} {
2153 set sz [font configure $font -size]
2154 incr sz $amt
2155 font configure $font -size $sz
2156 font configure ${font}bold -size $sz
debcd0fd 2157 font configure ${font}italic -size $sz
b4946930
SP
2158}
2159
cb07fc2a
SP
2160######################################################################
2161##
2162## ui commands
2163
25476c63
JL
2164proc do_gitk {revs {is_submodule false}} {
2165 global current_diff_path file_states current_diff_side ui_index
a9fa11fe 2166 global _gitdir _gitworktree
25476c63 2167
20ddfcaa
SP
2168 # -- Always start gitk through whatever we were loaded with. This
2169 # lets us bypass using shell process on Windows systems.
2170 #
79317e5d 2171 set exe [_which gitk -script]
02efd48f 2172 set cmd [list [info nameofexecutable] $exe]
15430be5
AMS
2173 if {$exe eq {}} {
2174 error_popup [mc "Couldn't find gitk in PATH"]
cb07fc2a 2175 } else {
501e4c6f
SP
2176 global env
2177
501e4c6f 2178 set pwd [pwd]
501e4c6f 2179
25476c63 2180 if {!$is_submodule} {
29e5573d 2181 if {![is_bare]} {
21985a11
GB
2182 cd $_gitworktree
2183 }
25476c63
JL
2184 } else {
2185 cd $current_diff_path
2186 if {$revs eq {--}} {
2187 set s $file_states($current_diff_path)
2188 set old_sha1 {}
2189 set new_sha1 {}
2190 switch -glob -- [lindex $s 0] {
2191 M_ { set old_sha1 [lindex [lindex $s 2] 1] }
2192 _M { set old_sha1 [lindex [lindex $s 3] 1] }
2193 MM {
2194 if {$current_diff_side eq $ui_index} {
2195 set old_sha1 [lindex [lindex $s 2] 1]
2196 set new_sha1 [lindex [lindex $s 3] 1]
2197 } else {
2198 set old_sha1 [lindex [lindex $s 3] 1]
2199 }
2200 }
2201 }
2202 set revs $old_sha1...$new_sha1
2203 }
a9fa11fe
GB
2204 # GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
2205 # we've been using for the main repository, so unset them.
2206 # TODO we could make life easier (start up faster?) for gitk
2207 # by setting these to the appropriate values to allow gitk
2208 # to skip the heuristics to find their proper value
2209 unset env(GIT_DIR)
2210 unset env(GIT_WORK_TREE)
25476c63 2211 }
e27d106e 2212 eval exec $cmd $revs "--" "--" &
501e4c6f 2213
a9fa11fe
GB
2214 set env(GIT_DIR) $_gitdir
2215 set env(GIT_WORK_TREE) $_gitworktree
25476c63
JL
2216 cd $pwd
2217
5eb9397e
PY
2218 if {[info exists main_status]} {
2219 set status_operation [$::main_status \
2220 start \
2221 [mc "Starting %s... please wait..." "gitk"]]
d9c6469f 2222
5eb9397e
PY
2223 after 3500 [list $status_operation stop]
2224 }
25476c63
JL
2225 }
2226}
2227
2228proc do_git_gui {} {
2229 global current_diff_path
2230
2231 # -- Always start git gui through whatever we were loaded with. This
2232 # lets us bypass using shell process on Windows systems.
2233 #
831cc7eb 2234 set exe [list [_which git]]
25476c63
JL
2235 if {$exe eq {}} {
2236 error_popup [mc "Couldn't find git gui in PATH"]
2237 } else {
2238 global env
a9fa11fe 2239 global _gitdir _gitworktree
25476c63 2240
a9fa11fe
GB
2241 # see note in do_gitk about unsetting these vars when
2242 # running tools in a submodule
2243 unset env(GIT_DIR)
2244 unset env(GIT_WORK_TREE)
25476c63
JL
2245
2246 set pwd [pwd]
2247 cd $current_diff_path
2248
2249 eval exec $exe gui &
2250
a9fa11fe
GB
2251 set env(GIT_DIR) $_gitdir
2252 set env(GIT_WORK_TREE) $_gitworktree
501e4c6f
SP
2253 cd $pwd
2254
d9c6469f
JG
2255 set status_operation [$::main_status \
2256 start \
2257 [mc "Starting %s... please wait..." "git-gui"]]
2258
2259 after 3500 [list $status_operation stop]
cb07fc2a
SP
2260 }
2261}
2262
786f4d24
ZS
2263# Get the system-specific explorer app/command.
2264proc get_explorer {} {
afd54240
PB
2265 if {[is_Cygwin] || [is_Windows]} {
2266 set explorer "explorer.exe"
2267 } elseif {[is_MacOSX]} {
2268 set explorer "open"
2269 } else {
2270 # freedesktop.org-conforming system is our best shot
2271 set explorer "xdg-open"
2272 }
786f4d24
ZS
2273 return $explorer
2274}
2275
2276proc do_explore {} {
2277 global _gitworktree
2278 set explorer [get_explorer]
2e0cda65 2279 eval exec $explorer [list [file nativename $_gitworktree]] &
afd54240
PB
2280}
2281
786f4d24
ZS
2282# Open file relative to the working tree by the default associated app.
2283proc do_file_open {file} {
2284 global _gitworktree
2285 set explorer [get_explorer]
2286 set full_file_path [file join $_gitworktree $file]
2287 exec $explorer [file nativename $full_file_path] &
2288}
2289
b5834d70 2290set is_quitting 0
1e65c622 2291set ret_code 1
c4fe7728 2292
1e65c622
AG
2293proc terminate_me {win} {
2294 global ret_code
2295 if {$win ne {.}} return
2296 exit $ret_code
2297}
2298
2299proc do_quit {{rc {1}}} {
c950c66e 2300 global ui_comm is_quitting repo_config commit_type
4578c5cb 2301 global GITGUI_BCK_exists GITGUI_BCK_i
95b002ee 2302 global ui_comm_spell
c80d7be5 2303 global ret_code use_ttk
c4fe7728 2304
b5834d70
SP
2305 if {$is_quitting} return
2306 set is_quitting 1
131f503b 2307
db7f34d4
SP
2308 if {[winfo exists $ui_comm]} {
2309 # -- Stash our current commit buffer.
2310 #
2311 set save [gitdir GITGUI_MSG]
4578c5cb
SP
2312 if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
2313 file rename -force [gitdir GITGUI_BCK] $save
2314 set GITGUI_BCK_exists 0
ce83ab2b 2315 } elseif {[$ui_comm edit modified]} {
4578c5cb
SP
2316 set msg [string trim [$ui_comm get 0.0 end]]
2317 regsub -all -line {[ \r\t]+$} $msg {} msg
ce83ab2b 2318 if {![string match amend* $commit_type]
4578c5cb
SP
2319 && $msg ne {}} {
2320 catch {
2321 set fd [open $save w]
fda1ba02 2322 fconfigure $fd -encoding utf-8
4578c5cb
SP
2323 puts -nonewline $fd $msg
2324 close $fd
2325 }
2326 } else {
2327 catch {file delete $save}
2328 }
2329 }
2330
95b002ee
SP
2331 # -- Cancel our spellchecker if its running.
2332 #
2333 if {[info exists ui_comm_spell]} {
2334 $ui_comm_spell stop
2335 }
2336
4578c5cb
SP
2337 # -- Remove our editor backup, its not needed.
2338 #
2339 after cancel $GITGUI_BCK_i
2340 if {$GITGUI_BCK_exists} {
2341 catch {file delete [gitdir GITGUI_BCK]}
131f503b 2342 }
131f503b 2343
db7f34d4
SP
2344 # -- Stash our current window geometry into this repository.
2345 #
ed7b6033
AB
2346 set cfg_wmstate [wm state .]
2347 if {[catch {set rc_wmstate $repo_config(gui.wmstate)}]} {
2348 set rc_wmstate {}
2349 }
2350 if {$cfg_wmstate ne $rc_wmstate} {
2351 catch {git config gui.wmstate $cfg_wmstate}
2352 }
2353 if {$cfg_wmstate eq {zoomed}} {
2354 # on Windows wm geometry will lie about window
2355 # position (but not size) when window is zoomed
2356 # restore the window before querying wm geometry
2357 wm state . normal
2358 }
db7f34d4
SP
2359 set cfg_geometry [list]
2360 lappend cfg_geometry [wm geometry .]
c80d7be5
PT
2361 if {$use_ttk} {
2362 lappend cfg_geometry [.vpane sashpos 0]
2363 lappend cfg_geometry [.vpane.files sashpos 0]
2364 } else {
2365 lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
2366 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1]
2367 }
db7f34d4
SP
2368 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
2369 set rc_geometry {}
2370 }
2371 if {$cfg_geometry ne $rc_geometry} {
81347223 2372 catch {git config gui.geometry $cfg_geometry}
db7f34d4 2373 }
51f4d16b
SP
2374 }
2375
1e65c622 2376 set ret_code $rc
60204ddb
JM
2377
2378 # Briefly enable send again, working around Tk bug
2379 # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
2380 tk appname [appname]
2381
cb07fc2a
SP
2382 destroy .
2383}
2384
2385proc do_rescan {} {
699d5601 2386 rescan ui_ready
cb07fc2a
SP
2387}
2388
8056cc4f 2389proc ui_do_rescan {} {
7cf4566f 2390 rescan {force_first_diff ui_ready}
8056cc4f
AG
2391}
2392
6e27d826 2393proc do_commit {} {
ec6b424a 2394 commit_tree
6e27d826
SP
2395}
2396
7cf4566f 2397proc next_diff {{after {}}} {
8a965b8e 2398 global next_diff_p next_diff_w next_diff_i
7cf4566f 2399 show_diff $next_diff_p $next_diff_w {} {} $after
29853b90
AG
2400}
2401
2402proc find_anchor_pos {lst name} {
2403 set lid [lsearch -sorted -exact $lst $name]
2404
2405 if {$lid == -1} {
2406 set lid 0
2407 foreach lname $lst {
2408 if {$lname >= $name} break
2409 incr lid
2410 }
2411 }
2412
2413 return $lid
2414}
2415
2416proc find_file_from {flist idx delta path mmask} {
2417 global file_states
2418
2419 set len [llength $flist]
2420 while {$idx >= 0 && $idx < $len} {
2421 set name [lindex $flist $idx]
2422
2423 if {$name ne $path && [info exists file_states($name)]} {
2424 set state [lindex $file_states($name) 0]
2425
2426 if {$mmask eq {} || [regexp $mmask $state]} {
2427 return $idx
2428 }
2429 }
2430
2431 incr idx $delta
2432 }
2433
2434 return {}
2435}
2436
2437proc find_next_diff {w path {lno {}} {mmask {}}} {
2438 global next_diff_p next_diff_w next_diff_i
2439 global file_lists ui_index ui_workdir
2440
2441 set flist $file_lists($w)
2442 if {$lno eq {}} {
2443 set lno [find_anchor_pos $flist $path]
2444 } else {
2445 incr lno -1
2446 }
2447
2448 if {$mmask ne {} && ![regexp {(^\^)|(\$$)} $mmask]} {
2449 if {$w eq $ui_index} {
2450 set mmask "^$mmask"
2451 } else {
2452 set mmask "$mmask\$"
2453 }
2454 }
2455
2456 set idx [find_file_from $flist $lno 1 $path $mmask]
2457 if {$idx eq {}} {
2458 incr lno -1
2459 set idx [find_file_from $flist $lno -1 $path $mmask]
2460 }
2461
2462 if {$idx ne {}} {
2463 set next_diff_w $w
2464 set next_diff_p [lindex $flist $idx]
2465 set next_diff_i [expr {$idx+1}]
2466 return 1
2467 } else {
2468 return 0
2469 }
2470}
2471
2472proc next_diff_after_action {w path {lno {}} {mmask {}}} {
2473 global current_diff_path
2474
2475 if {$path ne $current_diff_path} {
2476 return {}
2477 } elseif {[find_next_diff $w $path $lno $mmask]} {
2478 return {next_diff;}
2479 } else {
2480 return {reshow_diff;}
2481 }
2482}
2483
7cf4566f 2484proc select_first_diff {after} {
29853b90
AG
2485 global ui_workdir
2486
2487 if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
2488 [find_next_diff $ui_workdir {} 1 {[^O]$}]} {
7cf4566f
AG
2489 next_diff $after
2490 } else {
2491 uplevel #0 $after
29853b90 2492 }
8a965b8e
AMS
2493}
2494
7cf4566f
AG
2495proc force_first_diff {after} {
2496 global ui_workdir current_diff_path file_states
8056cc4f
AG
2497
2498 if {[info exists file_states($current_diff_path)]} {
2499 set state [lindex $file_states($current_diff_path) 0]
7cf4566f
AG
2500 } else {
2501 set state {OO}
2502 }
8056cc4f 2503
7cf4566f
AG
2504 set reselect 0
2505 if {[string first {U} $state] >= 0} {
2506 # Already a conflict, do nothing
2507 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
2508 set reselect 1
2509 } elseif {[string index $state 1] ne {O}} {
2510 # Already a diff & no conflicts, do nothing
2511 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
2512 set reselect 1
8056cc4f
AG
2513 }
2514
7cf4566f
AG
2515 if {$reselect} {
2516 next_diff $after
2517 } else {
2518 uplevel #0 $after
2519 }
8056cc4f
AG
2520}
2521
088ad75d 2522proc toggle_or_diff {mode w args} {
20a53c02 2523 global file_states file_lists current_diff_path ui_index ui_workdir
e07446ed 2524 global last_clicked selected_paths file_lists_last_clicked
131f503b 2525
088ad75d
PT
2526 if {$mode eq "click"} {
2527 foreach {x y} $args break
2528 set pos [split [$w index @$x,$y] .]
2529 foreach {lno col} $pos break
2530 } else {
76756d67
JS
2531 if {$mode eq "toggle"} {
2532 if {$w eq $ui_workdir} {
2533 do_add_selection
2534 set last_clicked {}
2535 return
2536 }
2537 if {$w eq $ui_index} {
2538 do_unstage_selection
2539 set last_clicked {}
2540 return
2541 }
2542 }
2543
088ad75d
PT
2544 if {$last_clicked ne {}} {
2545 set lno [lindex $last_clicked 1]
2546 } else {
2cd9179c
JS
2547 if {![info exists file_lists]
2548 || ![info exists file_lists($w)]
2549 || [llength $file_lists($w)] == 0} {
2365e5b1
JS
2550 set last_clicked {}
2551 return
2552 }
088ad75d
PT
2553 set lno [expr {int([lindex [$w tag ranges in_diff] 0])}]
2554 }
2555 if {$mode eq "toggle"} {
2556 set col 0; set y 2
2557 } else {
2558 incr lno [expr {$mode eq "up" ? -1 : 1}]
2559 set col 1
2560 }
2561 }
2562
2cd9179c
JS
2563 if {![info exists file_lists]
2564 || ![info exists file_lists($w)]
2565 || [llength $file_lists($w)] < $lno - 1} {
2566 set path {}
2567 } else {
2568 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2569 }
24263b77
SP
2570 if {$path eq {}} {
2571 set last_clicked {}
2572 return
2573 }
2574
2575 set last_clicked [list $w $lno]
088ad75d 2576 focus $w
24263b77
SP
2577 array unset selected_paths
2578 $ui_index tag remove in_sel 0.0 end
0812665e 2579 $ui_workdir tag remove in_sel 0.0 end
cb07fc2a 2580
e07446ed
BSP
2581 set file_lists_last_clicked($w) $path
2582
3e34838c 2583 # Determine the state of the file
617ceee6
AG
2584 if {[info exists file_states($path)]} {
2585 set state [lindex $file_states($path) 0]
8056cc4f
AG
2586 } else {
2587 set state {__}
2588 }
2589
8056cc4f 2590 # Restage the file, or simply show the diff
cead78ed 2591 if {$col == 0 && $y > 1} {
3e34838c
AG
2592 # Conflicts need special handling
2593 if {[string first {U} $state] >= 0} {
0aea2842
AG
2594 # $w must always be $ui_workdir, but...
2595 if {$w ne $ui_workdir} { set lno {} }
2596 merge_stage_workdir $path $lno
3e34838c
AG
2597 return
2598 }
2599
8056cc4f
AG
2600 if {[string index $state 1] eq {O}} {
2601 set mmask {}
2602 } else {
2603 set mmask {[^O]}
2604 }
2605
2606 set after [next_diff_after_action $w $path $lno $mmask]
8a965b8e 2607
de5f6d5d 2608 if {$w eq $ui_index} {
74d18d2e 2609 update_indexinfo \
93e912c5 2610 "Unstaging [short_path $path] from commit" \
74d18d2e 2611 [list $path] \
19195fbd 2612 [concat $after {ui_ready;}]
de5f6d5d 2613 } elseif {$w eq $ui_workdir} {
74d18d2e 2614 update_index \
4d583c86 2615 "Adding [short_path $path]" \
74d18d2e 2616 [list $path] \
19195fbd 2617 [concat $after {ui_ready;}]
74d18d2e 2618 }
24263b77 2619 } else {
a8ca7869 2620 set selected_paths($path) 1
03e4ec53 2621 show_diff $path $w $lno
cb07fc2a
SP
2622 }
2623}
2624
24263b77 2625proc add_one_to_selection {w x y} {
833eda73 2626 global file_lists last_clicked selected_paths
7f1df79b 2627
833eda73 2628 set lno [lindex [split [$w index @$x,$y] .] 0]
24263b77
SP
2629 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2630 if {$path eq {}} {
2631 set last_clicked {}
2632 return
2633 }
cb07fc2a 2634
833eda73
SP
2635 if {$last_clicked ne {}
2636 && [lindex $last_clicked 0] ne $w} {
2637 array unset selected_paths
2638 [lindex $last_clicked 0] tag remove in_sel 0.0 end
2639 }
2640
24263b77
SP
2641 set last_clicked [list $w $lno]
2642 if {[catch {set in_sel $selected_paths($path)}]} {
2643 set in_sel 0
2644 }
2645 if {$in_sel} {
2646 unset selected_paths($path)
2647 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2648 } else {
2649 set selected_paths($path) 1
2650 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2651 }
2652}
2653
2654proc add_range_to_selection {w x y} {
833eda73 2655 global file_lists last_clicked selected_paths
24263b77
SP
2656
2657 if {[lindex $last_clicked 0] ne $w} {
088ad75d 2658 toggle_or_diff click $w $x $y
24263b77 2659 return
cb07fc2a 2660 }
24263b77 2661
833eda73 2662 set lno [lindex [split [$w index @$x,$y] .] 0]
24263b77
SP
2663 set lc [lindex $last_clicked 1]
2664 if {$lc < $lno} {
2665 set begin $lc
2666 set end $lno
2667 } else {
2668 set begin $lno
2669 set end $lc
2670 }
2671
2672 foreach path [lrange $file_lists($w) \
2673 [expr {$begin - 1}] \
2674 [expr {$end - 1}]] {
2675 set selected_paths($path) 1
2676 }
2677 $w tag add in_sel $begin.0 [expr {$end + 1}].0
cb07fc2a
SP
2678}
2679
c91ee2bd
JS
2680proc show_more_context {} {
2681 global repo_config
2682 if {$repo_config(gui.diffcontext) < 99} {
2683 incr repo_config(gui.diffcontext)
2684 reshow_diff
2685 }
2686}
2687
2688proc show_less_context {} {
2689 global repo_config
55ba8a34 2690 if {$repo_config(gui.diffcontext) > 1} {
c91ee2bd
JS
2691 incr repo_config(gui.diffcontext) -1
2692 reshow_diff
2693 }
2694}
2695
e07446ed
BSP
2696proc focus_widget {widget} {
2697 global file_lists last_clicked selected_paths
2698 global file_lists_last_clicked
2699
2700 if {[llength $file_lists($widget)] > 0} {
2701 set path $file_lists_last_clicked($widget)
2702 set index [lsearch -sorted -exact $file_lists($widget) $path]
2703 if {$index < 0} {
2704 set index 0
2705 set path [lindex $file_lists($widget) $index]
2706 }
2707
2708 focus $widget
2709 set last_clicked [list $widget [expr $index + 1]]
2710 array unset selected_paths
2711 set selected_paths($path) 1
2712 show_diff $path $widget
2713 }
2714}
2715
ec7424e1
BSP
2716proc toggle_commit_type {} {
2717 global commit_type_is_amend
2718 set commit_type_is_amend [expr !$commit_type_is_amend]
2719 do_select_commit_type
2720}
2721
cb07fc2a
SP
2722######################################################################
2723##
a4bee597 2724## ui construction
db453781 2725
2ebba528
SP
2726set ui_comm {}
2727
cb07fc2a 2728# -- Menu Bar
a49c67d1 2729#
b4946930 2730menu .mbar -tearoff 0
a91be3fc
DS
2731if {[is_MacOSX]} {
2732 # -- Apple Menu (Mac OS X only)
2733 #
2734 .mbar add cascade -label Apple -menu .mbar.apple
2735 menu .mbar.apple
2736}
1ac17950
CS
2737.mbar add cascade -label [mc Repository] -menu .mbar.repository
2738.mbar add cascade -label [mc Edit] -menu .mbar.edit
64a906f8 2739if {[is_enabled branch]} {
1ac17950 2740 .mbar add cascade -label [mc Branch] -menu .mbar.branch
700a65ce 2741}
2ebba528 2742if {[is_enabled multicommit] || [is_enabled singlecommit]} {
a9813cb5 2743 .mbar add cascade -label [mc Commit@@noun] -menu .mbar.commit
2ebba528 2744}
64a906f8 2745if {[is_enabled transport]} {
1ac17950 2746 .mbar add cascade -label [mc Merge] -menu .mbar.merge
6bdf5e5f 2747 .mbar add cascade -label [mc Remote] -menu .mbar.remote
4ccdab02 2748}
0ce76ded
AG
2749if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2750 .mbar add cascade -label [mc Tools] -menu .mbar.tools
2751}
cb07fc2a 2752
a4abfa62 2753# -- Repository Menu
a49c67d1 2754#
a4abfa62 2755menu .mbar.repository
35874c16 2756
29e5573d
GB
2757if {![is_bare]} {
2758 .mbar.repository add command \
2759 -label [mc "Explore Working Copy"] \
2760 -command {do_explore}
224cce8f
PT
2761}
2762
2763if {[is_Windows]} {
6a72d44f
TK
2764 # Use /git-bash.exe if available
2765 set normalized [file normalize $::argv0]
2766 regsub "/mingw../libexec/git-core/git-gui$" \
2767 $normalized "/git-bash.exe" cmdLine
2768 if {$cmdLine != $normalized && [file exists $cmdLine]} {
2769 set cmdLine [list "Git Bash" $cmdLine &]
2770 } else {
2771 set cmdLine [list "Git Bash" bash --login -l &]
2772 }
224cce8f
PT
2773 .mbar.repository add command \
2774 -label [mc "Git Bash"] \
6a72d44f 2775 -command {eval exec [auto_execok start] $cmdLine}
224cce8f
PT
2776}
2777
2778if {[is_Windows] || ![is_bare]} {
29e5573d
GB
2779 .mbar.repository add separator
2780}
afd54240 2781
35874c16 2782.mbar.repository add command \
1ac17950 2783 -label [mc "Browse Current Branch's Files"] \
c74b6c66 2784 -command {browser::new $current_branch}
a8139888 2785set ui_browse_current [.mbar.repository index last]
8e891fac 2786.mbar.repository add command \
1ac17950 2787 -label [mc "Browse Branch Files..."] \
8e891fac 2788 -command browser_open::dialog
35874c16
SP
2789.mbar.repository add separator
2790
d0752429 2791.mbar.repository add command \
1ac17950 2792 -label [mc "Visualize Current Branch's History"] \
7416bbc6 2793 -command {do_gitk $current_branch}
a8139888 2794set ui_visualize_current [.mbar.repository index last]
5753ef1a 2795.mbar.repository add command \
1ac17950 2796 -label [mc "Visualize All Branch History"] \
7416bbc6 2797 -command {do_gitk --all}
d0752429 2798.mbar.repository add separator
75e355d6 2799
a8139888
SP
2800proc current_branch_write {args} {
2801 global current_branch
2802 .mbar.repository entryconf $::ui_browse_current \
1ac17950 2803 -label [mc "Browse %s's Files" $current_branch]
a8139888 2804 .mbar.repository entryconf $::ui_visualize_current \
1ac17950 2805 -label [mc "Visualize %s's History" $current_branch]
a8139888
SP
2806}
2807trace add variable current_branch write current_branch_write
2808
cf25ddc8 2809if {[is_enabled multicommit]} {
1ac17950 2810 .mbar.repository add command -label [mc "Database Statistics"] \
7416bbc6 2811 -command do_stats
0fd49d0a 2812
1ac17950 2813 .mbar.repository add command -label [mc "Compress Database"] \
7416bbc6 2814 -command do_gc
4aca740b 2815
1ac17950 2816 .mbar.repository add command -label [mc "Verify Database"] \
7416bbc6 2817 -command do_fsck_objects
444f92d0 2818
a4abfa62 2819 .mbar.repository add separator
75e355d6 2820
20ddfcaa
SP
2821 if {[is_Cygwin]} {
2822 .mbar.repository add command \
1ac17950 2823 -label [mc "Create Desktop Icon"] \
7416bbc6 2824 -command do_cygwin_shortcut
20ddfcaa 2825 } elseif {[is_Windows]} {
a4abfa62 2826 .mbar.repository add command \
1ac17950 2827 -label [mc "Create Desktop Icon"] \
7416bbc6 2828 -command do_windows_shortcut
06c31115 2829 } elseif {[is_MacOSX]} {
a4abfa62 2830 .mbar.repository add command \
1ac17950 2831 -label [mc "Create Desktop Icon"] \
7416bbc6 2832 -command do_macosx_app
4aca740b 2833 }
4ccdab02 2834}
85ab313e 2835
af894943
SF
2836if {[is_MacOSX]} {
2837 proc ::tk::mac::Quit {args} { do_quit }
2838} else {
2839 .mbar.repository add command -label [mc Quit] \
2840 -command do_quit \
2841 -accelerator $M1T-Q
2842}
cb07fc2a 2843
9861671d
SP
2844# -- Edit Menu
2845#
2846menu .mbar.edit
1ac17950 2847.mbar.edit add command -label [mc Undo] \
9861671d 2848 -command {catch {[focus] edit undo}} \
7416bbc6 2849 -accelerator $M1T-Z
1ac17950 2850.mbar.edit add command -label [mc Redo] \
9861671d 2851 -command {catch {[focus] edit redo}} \
7416bbc6 2852 -accelerator $M1T-Y
9861671d 2853.mbar.edit add separator
1ac17950 2854.mbar.edit add command -label [mc Cut] \
9861671d 2855 -command {catch {tk_textCut [focus]}} \
7416bbc6 2856 -accelerator $M1T-X
1ac17950 2857.mbar.edit add command -label [mc Copy] \
9861671d 2858 -command {catch {tk_textCopy [focus]}} \
7416bbc6 2859 -accelerator $M1T-C
1ac17950 2860.mbar.edit add command -label [mc Paste] \
9861671d 2861 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
7416bbc6 2862 -accelerator $M1T-V
1ac17950 2863.mbar.edit add command -label [mc Delete] \
9861671d 2864 -command {catch {[focus] delete sel.first sel.last}} \
7416bbc6 2865 -accelerator Del
9861671d 2866.mbar.edit add separator
1ac17950 2867.mbar.edit add command -label [mc "Select All"] \
9861671d 2868 -command {catch {[focus] tag add sel 0.0 end}} \
7416bbc6 2869 -accelerator $M1T-A
9861671d 2870
85ab313e
SP
2871# -- Branch Menu
2872#
64a906f8 2873if {[is_enabled branch]} {
700a65ce
SP
2874 menu .mbar.branch
2875
1ac17950 2876 .mbar.branch add command -label [mc "Create..."] \
b1fa2bff 2877 -command branch_create::dialog \
7416bbc6 2878 -accelerator $M1T-N
700a65ce
SP
2879 lappend disable_on_lock [list .mbar.branch entryconf \
2880 [.mbar.branch index last] -state]
2881
1ac17950 2882 .mbar.branch add command -label [mc "Checkout..."] \
d41b43eb
SP
2883 -command branch_checkout::dialog \
2884 -accelerator $M1T-O
2885 lappend disable_on_lock [list .mbar.branch entryconf \
2886 [.mbar.branch index last] -state]
2887
1ac17950 2888 .mbar.branch add command -label [mc "Rename..."] \
61f82ce7
SP
2889 -command branch_rename::dialog
2890 lappend disable_on_lock [list .mbar.branch entryconf \
2891 [.mbar.branch index last] -state]
2892
1ac17950 2893 .mbar.branch add command -label [mc "Delete..."] \
3206c63d 2894 -command branch_delete::dialog
700a65ce
SP
2895 lappend disable_on_lock [list .mbar.branch entryconf \
2896 [.mbar.branch index last] -state]
fd234dfd 2897
1ac17950 2898 .mbar.branch add command -label [mc "Reset..."] \
a6c9b081 2899 -command merge::reset_hard
fd234dfd
SP
2900 lappend disable_on_lock [list .mbar.branch entryconf \
2901 [.mbar.branch index last] -state]
700a65ce
SP
2902}
2903
cb07fc2a 2904# -- Commit Menu
a49c67d1 2905#
1e65c622
AG
2906proc commit_btn_caption {} {
2907 if {[is_enabled nocommit]} {
2908 return [mc "Done"]
2909 } else {
2910 return [mc Commit@@verb]
2911 }
2912}
2913
2ebba528
SP
2914if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2915 menu .mbar.commit
2916
1e02b32e 2917 if {![is_enabled nocommit]} {
ba41b5b3 2918 .mbar.commit add checkbutton \
1e02b32e 2919 -label [mc "Amend Last Commit"] \
ec7424e1 2920 -accelerator $M1T-E \
ba41b5b3
BW
2921 -variable commit_type_is_amend \
2922 -command do_select_commit_type
1e02b32e
SP
2923 lappend disable_on_lock \
2924 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2925
2926 .mbar.commit add separator
2927 }
24ac9b75 2928
1ac17950 2929 .mbar.commit add command -label [mc Rescan] \
8056cc4f 2930 -command ui_do_rescan \
7416bbc6 2931 -accelerator F5
2ebba528
SP
2932 lappend disable_on_lock \
2933 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2934
1ac17950 2935 .mbar.commit add command -label [mc "Stage To Commit"] \
cd16a6c9
SP
2936 -command do_add_selection \
2937 -accelerator $M1T-T
2ebba528
SP
2938 lappend disable_on_lock \
2939 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2940
1ac17950 2941 .mbar.commit add command -label [mc "Stage Changed Files To Commit"] \
2ebba528 2942 -command do_add_all \
7416bbc6 2943 -accelerator $M1T-I
2ebba528
SP
2944 lappend disable_on_lock \
2945 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2946
1ac17950 2947 .mbar.commit add command -label [mc "Unstage From Commit"] \
b677c66e
VS
2948 -command do_unstage_selection \
2949 -accelerator $M1T-U
2ebba528
SP
2950 lappend disable_on_lock \
2951 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e734817d 2952
1ac17950 2953 .mbar.commit add command -label [mc "Revert Changes"] \
b677c66e
VS
2954 -command do_revert_selection \
2955 -accelerator $M1T-J
2ebba528
SP
2956 lappend disable_on_lock \
2957 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e734817d 2958
2ebba528 2959 .mbar.commit add separator
1461c5f3 2960
c91ee2bd
JS
2961 .mbar.commit add command -label [mc "Show Less Context"] \
2962 -command show_less_context \
729ffa50 2963 -accelerator $M1T-\-
c91ee2bd
JS
2964
2965 .mbar.commit add command -label [mc "Show More Context"] \
2966 -command show_more_context \
729ffa50 2967 -accelerator $M1T-=
c91ee2bd
JS
2968
2969 .mbar.commit add separator
2970
ed70e4d7 2971 if {![is_enabled nocommitmsg]} {
1e02b32e
SP
2972 .mbar.commit add command -label [mc "Sign Off"] \
2973 -command do_signoff \
2974 -accelerator $M1T-S
2975 }
24ac9b75 2976
1e65c622 2977 .mbar.commit add command -label [commit_btn_caption] \
2ebba528 2978 -command do_commit \
7416bbc6 2979 -accelerator $M1T-Return
2ebba528
SP
2980 lappend disable_on_lock \
2981 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2982}
cb07fc2a 2983
9b28a8b9
SP
2984# -- Merge Menu
2985#
2986if {[is_enabled branch]} {
2987 menu .mbar.merge
1ac17950 2988 .mbar.merge add command -label [mc "Local Merge..."] \
a870ddc0
SP
2989 -command merge::dialog \
2990 -accelerator $M1T-M
9b28a8b9
SP
2991 lappend disable_on_lock \
2992 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1ac17950 2993 .mbar.merge add command -label [mc "Abort Merge..."] \
a6c9b081 2994 -command merge::reset_hard
9b28a8b9
SP
2995 lappend disable_on_lock \
2996 [list .mbar.merge entryconf [.mbar.merge index last] -state]
9b28a8b9
SP
2997}
2998
2999# -- Transport Menu
3000#
3001if {[is_enabled transport]} {
6bdf5e5f 3002 menu .mbar.remote
9b28a8b9 3003
ba6485e0
PB
3004 .mbar.remote add command \
3005 -label [mc "Add..."] \
3006 -command remote_add::dialog \
3007 -accelerator $M1T-A
6bdf5e5f
SP
3008 .mbar.remote add command \
3009 -label [mc "Push..."] \
840bcfa7
SP
3010 -command do_push_anywhere \
3011 -accelerator $M1T-P
6bdf5e5f 3012 .mbar.remote add command \
3c1c2a00 3013 -label [mc "Delete Branch..."] \
aa252f19 3014 -command remote_branch_delete::dialog
9b28a8b9
SP
3015}
3016
0c8d7839 3017if {[is_MacOSX]} {
a91be3fc 3018 proc ::tk::mac::ShowPreferences {} {do_options}
0c8d7839
SP
3019} else {
3020 # -- Edit Menu
3021 #
3022 .mbar.edit add separator
1ac17950 3023 .mbar.edit add command -label [mc "Options..."] \
7416bbc6 3024 -command do_options
273984fc 3025}
557afe82 3026
0ce76ded
AG
3027# -- Tools Menu
3028#
3029if {[is_enabled multicommit] || [is_enabled singlecommit]} {
3030 set tools_menubar .mbar.tools
3031 menu $tools_menubar
3032 $tools_menubar add separator
3033 $tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
3034 $tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
3035 set tools_tailcnt 3
3036 if {[array names repo_config guitool.*.cmd] ne {}} {
3037 tools_populate_all
3038 }
3039}
3040
273984fc
SP
3041# -- Help Menu
3042#
1ac17950 3043.mbar add cascade -label [mc Help] -menu .mbar.help
273984fc 3044menu .mbar.help
0c8d7839 3045
a91be3fc
DS
3046if {[is_MacOSX]} {
3047 .mbar.apple add command -label [mc "About %s" [appname]] \
3048 -command do_about
3049 .mbar.apple add separator
3050} else {
1ac17950 3051 .mbar.help add command -label [mc "About %s" [appname]] \
7416bbc6 3052 -command do_about
0c8d7839 3053}
a91be3fc 3054. configure -menu .mbar
2db21e70 3055
3eb5682b
MH
3056set doc_path [githtmldir]
3057if {$doc_path ne {}} {
3058 set doc_path [file join $doc_path index.html]
273984fc 3059
3eb5682b
MH
3060 if {[is_Cygwin]} {
3061 set doc_path [exec cygpath --mixed $doc_path]
3062 }
273984fc
SP
3063}
3064
273984fc
SP
3065if {[file isfile $doc_path]} {
3066 set doc_url "file:$doc_path"
3067} else {
3068 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
3069}
3070
2db21e70
PB
3071proc start_browser {url} {
3072 git "web--browse" $url
273984fc 3073}
2db21e70
PB
3074
3075.mbar.help add command -label [mc "Online Documentation"] \
3076 -command [list start_browser $doc_url]
98a6846b
AG
3077
3078.mbar.help add command -label [mc "Show SSH Key"] \
3079 -command do_ssh_key
3080
2db21e70 3081unset doc_path doc_url
82aa2354 3082
2ebba528
SP
3083# -- Standard bindings
3084#
39fa2a98 3085wm protocol . WM_DELETE_WINDOW do_quit
2ebba528
SP
3086bind all <$M1B-Key-q> do_quit
3087bind all <$M1B-Key-Q> do_quit
5440eb0e
PY
3088
3089set m1b_w_script {
3090 set toplvl_win [winfo toplevel %W]
3091
3092 # If we are destroying the main window, we should call do_quit to take
3093 # care of cleanup before exiting the program.
3094 if {$toplvl_win eq "."} {
3095 do_quit
3096 } else {
3097 destroy $toplvl_win
3098 }
3099}
3100
3101bind all <$M1B-Key-w> $m1b_w_script
3102bind all <$M1B-Key-W> $m1b_w_script
3103
3104unset m1b_w_script
2ebba528 3105
3e45ee1e
SP
3106set subcommand_args {}
3107proc usage {} {
43c65a85 3108 set s "[mc usage:] $::argv0 $::subcommand $::subcommand_args"
ea47503d
PT
3109 if {[tk windowingsystem] eq "win32"} {
3110 wm withdraw .
7ae1e727
PT
3111 tk_messageBox -icon info -message $s \
3112 -title [mc "Usage"]
ea47503d
PT
3113 } else {
3114 puts stderr $s
3115 }
3e45ee1e
SP
3116 exit 1
3117}
3118
95e706b5
AG
3119proc normalize_relpath {path} {
3120 set elements {}
3121 foreach item [file split $path] {
3122 if {$item eq {.}} continue
3123 if {$item eq {..} && [llength $elements] > 0
3124 && [lindex $elements end] ne {..}} {
3125 set elements [lrange $elements 0 end-1]
3126 continue
3127 }
3128 lappend elements $item
3129 }
3130 return [eval file join $elements]
3131}
3132
2ebba528
SP
3133# -- Not a normal commit type invocation? Do that instead!
3134#
258871d3 3135switch -- $subcommand {
85d2d597 3136browser -
2ebba528 3137blame {
f7078b40
AG
3138 if {$subcommand eq "blame"} {
3139 set subcommand_args {[--line=<num>] rev? path}
3140 } else {
3141 set subcommand_args {rev? path}
3142 }
c52c9452 3143 if {$argv eq {}} usage
a0db0d61 3144 set head {}
3e45ee1e 3145 set path {}
f7078b40 3146 set jump_spec {}
3e45ee1e
SP
3147 set is_path 0
3148 foreach a $argv {
2f38dd03 3149 set p [file join $_prefix $a]
e3d06ca9 3150
2f38dd03 3151 if {$is_path || [file exists $p]} {
e3d06ca9 3152 if {$path ne {}} usage
2f38dd03 3153 set path [normalize_relpath $p]
e3d06ca9 3154 break
3e45ee1e
SP
3155 } elseif {$a eq {--}} {
3156 if {$path ne {}} {
a0db0d61
SP
3157 if {$head ne {}} usage
3158 set head $path
3e45ee1e
SP
3159 set path {}
3160 }
3161 set is_path 1
f7078b40
AG
3162 } elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
3163 if {$jump_spec ne {} || $head ne {}} usage
3164 set jump_spec [list $lnum]
a0db0d61
SP
3165 } elseif {$head eq {}} {
3166 if {$head ne {}} usage
3167 set head $a
c52c9452 3168 set is_path 1
3e45ee1e
SP
3169 } else {
3170 usage
3171 }
3172 }
3173 unset is_path
3174
c52c9452 3175 if {$head ne {} && $path eq {}} {
df46eda3
AW
3176 if {[string index $head 0] eq {/}} {
3177 set path [normalize_relpath $head]
3178 set head {}
3179 } else {
3180 set path [normalize_relpath $_prefix$head]
3181 set head {}
3182 }
c52c9452
SP
3183 }
3184
a0db0d61 3185 if {$head eq {}} {
d41b43eb 3186 load_current_branch
a0db0d61 3187 } else {
02087abc
SP
3188 if {[regexp {^[0-9a-f]{1,39}$} $head]} {
3189 if {[catch {
3190 set head [git rev-parse --verify $head]
3191 } err]} {
7ae1e727
PT
3192 if {[tk windowingsystem] eq "win32"} {
3193 tk_messageBox -icon error -title [mc Error] -message $err
3194 } else {
3195 puts stderr $err
3196 }
02087abc
SP
3197 exit 1
3198 }
3199 }
a0db0d61 3200 set current_branch $head
2ebba528 3201 }
a0db0d61 3202
2810a58d 3203 wm deiconify .
85d2d597
SP
3204 switch -- $subcommand {
3205 browser {
f7078b40 3206 if {$jump_spec ne {}} usage
85d2d597
SP
3207 if {$head eq {}} {
3208 if {$path ne {} && [file isdirectory $path]} {
3209 set head $current_branch
3210 } else {
3211 set head $path
3212 set path {}
3213 }
3214 }
3215 browser::new $head $path
3216 }
3217 blame {
3218 if {$head eq {} && ![file exists $path]} {
78077772
PT
3219 catch {wm withdraw .}
3220 tk_messageBox \
3221 -icon error \
3222 -type ok \
3223 -title [mc "git-gui: fatal error"] \
3224 -message [mc "fatal: cannot stat path %s: No such file or directory" $path]
85d2d597
SP
3225 exit 1
3226 }
f7078b40 3227 blame::new $head $path $jump_spec
85d2d597 3228 }
c52c9452 3229 }
258871d3
SP
3230 return
3231}
3232citool -
3233gui {
3234 if {[llength $argv] != 0} {
7ae1e727 3235 usage
258871d3
SP
3236 }
3237 # fall through to setup UI for commits
2ebba528 3238}
2ebba528 3239default {
43c65a85 3240 set err "[mc usage:] $argv0 \[{blame|browser|citool}\]"
7ae1e727
PT
3241 if {[tk windowingsystem] eq "win32"} {
3242 wm withdraw .
3243 tk_messageBox -icon error -message $err \
3244 -title [mc "Usage"]
3245 } else {
3246 puts stderr $err
3247 }
2ebba528
SP
3248 exit 1
3249}
3250}
3251
8553b772
SP
3252# -- Branch Control
3253#
c80d7be5
PT
3254${NS}::frame .branch
3255if {!$use_ttk} {.branch configure -borderwidth 1 -relief sunken}
3256${NS}::label .branch.l1 \
1ac17950 3257 -text [mc "Current Branch:"] \
8553b772 3258 -anchor w \
7416bbc6 3259 -justify left
c80d7be5 3260${NS}::label .branch.cb \
8553b772
SP
3261 -textvariable current_branch \
3262 -anchor w \
7416bbc6 3263 -justify left
8553b772
SP
3264pack .branch.l1 -side left
3265pack .branch.cb -side left -fill x
3266pack .branch -side top -fill x
3267
cb07fc2a 3268# -- Main Window Layout
a49c67d1 3269#
c80d7be5
PT
3270${NS}::panedwindow .vpane -orient horizontal
3271${NS}::panedwindow .vpane.files -orient vertical
3272if {$use_ttk} {
3273 .vpane add .vpane.files
3274} else {
3275 .vpane add .vpane.files -sticky nsew -height 100 -width 200
3276}
cb07fc2a
SP
3277pack .vpane -anchor n -side top -fill both -expand 1
3278
30508bc4
PT
3279# -- Working Directory File List
3280
3281textframe .vpane.files.workdir -height 100 -width 200
3282tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
3283 -background lightsalmon -foreground black
c02efc13 3284ttext $ui_workdir \
30508bc4
PT
3285 -borderwidth 0 \
3286 -width 20 -height 10 \
3287 -wrap none \
3288 -takefocus 1 -highlightthickness 1\
3289 -cursor $cursor_ptr \
3290 -xscrollcommand {.vpane.files.workdir.sx set} \
3291 -yscrollcommand {.vpane.files.workdir.sy set} \
3292 -state disabled
3293${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
3294${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
3295pack .vpane.files.workdir.title -side top -fill x
3296pack .vpane.files.workdir.sx -side bottom -fill x
3297pack .vpane.files.workdir.sy -side right -fill y
3298pack $ui_workdir -side left -fill both -expand 1
3299
cb07fc2a 3300# -- Index File List
a49c67d1 3301#
30508bc4 3302textframe .vpane.files.index -height 100 -width 200
c80d7be5
PT
3303tlabel .vpane.files.index.title \
3304 -text [mc "Staged Changes (Will Commit)"] \
c382fdd7 3305 -background lightgreen -foreground black
c02efc13 3306ttext $ui_index \
c382fdd7 3307 -borderwidth 0 \
c5a1eb88 3308 -width 20 -height 10 \
3c236977 3309 -wrap none \
088ad75d 3310 -takefocus 1 -highlightthickness 1\
6c6dd01a 3311 -cursor $cursor_ptr \
3c236977
SP
3312 -xscrollcommand {.vpane.files.index.sx set} \
3313 -yscrollcommand {.vpane.files.index.sy set} \
cb07fc2a 3314 -state disabled
c80d7be5
PT
3315${NS}::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
3316${NS}::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
cb07fc2a 3317pack .vpane.files.index.title -side top -fill x
3c236977
SP
3318pack .vpane.files.index.sx -side bottom -fill x
3319pack .vpane.files.index.sy -side right -fill y
cb07fc2a 3320pack $ui_index -side left -fill both -expand 1
cb07fc2a 3321
30508bc4 3322# -- Insert the workdir and index into the panes
a49c67d1 3323#
c80d7be5
PT
3324.vpane.files add .vpane.files.workdir
3325.vpane.files add .vpane.files.index
3326if {!$use_ttk} {
3327 .vpane.files paneconfigure .vpane.files.workdir -sticky news
3328 .vpane.files paneconfigure .vpane.files.index -sticky news
3329}
cb07fc2a 3330
0812665e 3331foreach i [list $ui_index $ui_workdir] {
3849bfba 3332 rmsel_tag $i
c02efc13
ST
3333 $i tag conf in_diff \
3334 -background $color::select_bg \
3335 -foreground $color::select_fg
24263b77
SP
3336}
3337unset i
131f503b 3338
0fb8f9ce 3339# -- Diff and Commit Area
a49c67d1 3340#
02f6cfbd
MK
3341if {$have_tk85} {
3342 ${NS}::panedwindow .vpane.lower -orient vertical
3343 ${NS}::frame .vpane.lower.commarea
3344 ${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500
3345 .vpane.lower add .vpane.lower.diff
3346 .vpane.lower add .vpane.lower.commarea
3347 .vpane add .vpane.lower
3348 if {$use_ttk} {
3349 .vpane.lower pane .vpane.lower.diff -weight 1
3350 .vpane.lower pane .vpane.lower.commarea -weight 0
3351 } else {
3352 .vpane.lower paneconfigure .vpane.lower.diff -stretch always
3353 .vpane.lower paneconfigure .vpane.lower.commarea -stretch never
3354 }
918dbf58 3355} else {
02f6cfbd
MK
3356 frame .vpane.lower -height 300 -width 400
3357 frame .vpane.lower.commarea
3358 frame .vpane.lower.diff -relief sunken -borderwidth 1
3359 pack .vpane.lower.diff -fill both -expand 1
3360 pack .vpane.lower.commarea -side bottom -fill x
3361 .vpane add .vpane.lower
3362 .vpane paneconfigure .vpane.lower -sticky nsew
918dbf58 3363}
cb07fc2a
SP
3364
3365# -- Commit Area Buttons
a49c67d1 3366#
c80d7be5
PT
3367${NS}::frame .vpane.lower.commarea.buttons
3368${NS}::label .vpane.lower.commarea.buttons.l -text {} \
cb07fc2a 3369 -anchor w \
7416bbc6 3370 -justify left
0fb8f9ce
SP
3371pack .vpane.lower.commarea.buttons.l -side top -fill x
3372pack .vpane.lower.commarea.buttons -side left -fill y
131f503b 3373
c80d7be5 3374${NS}::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
8056cc4f 3375 -command ui_do_rescan
0fb8f9ce 3376pack .vpane.lower.commarea.buttons.rescan -side top -fill x
390adaea
SP
3377lappend disable_on_lock \
3378 {.vpane.lower.commarea.buttons.rescan conf -state}
131f503b 3379
c80d7be5 3380${NS}::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \
7416bbc6 3381 -command do_add_all
7fe7e733 3382pack .vpane.lower.commarea.buttons.incall -side top -fill x
390adaea
SP
3383lappend disable_on_lock \
3384 {.vpane.lower.commarea.buttons.incall conf -state}
131f503b 3385
ed70e4d7 3386if {![is_enabled nocommitmsg]} {
c80d7be5 3387 ${NS}::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
1e02b32e
SP
3388 -command do_signoff
3389 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
3390}
131f503b 3391
c80d7be5 3392${NS}::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
7416bbc6 3393 -command do_commit
0fb8f9ce 3394pack .vpane.lower.commarea.buttons.commit -side top -fill x
390adaea
SP
3395lappend disable_on_lock \
3396 {.vpane.lower.commarea.buttons.commit conf -state}
cb07fc2a 3397
1e02b32e 3398if {![is_enabled nocommit]} {
c80d7be5 3399 ${NS}::button .vpane.lower.commarea.buttons.push -text [mc Push] \
1e02b32e
SP
3400 -command do_push_anywhere
3401 pack .vpane.lower.commarea.buttons.push -side top -fill x
1e65c622
AG
3402}
3403
cb07fc2a 3404# -- Commit Message Buffer
a49c67d1 3405#
c80d7be5
PT
3406${NS}::frame .vpane.lower.commarea.buffer
3407${NS}::frame .vpane.lower.commarea.buffer.header
30508bc4 3408set ui_comm .vpane.lower.commarea.buffer.frame.t
24ac9b75 3409set ui_coml .vpane.lower.commarea.buffer.header.l
1e02b32e
SP
3410
3411if {![is_enabled nocommit]} {
ba41b5b3 3412 ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \
1e02b32e 3413 -text [mc "Amend Last Commit"] \
ba41b5b3
BW
3414 -variable commit_type_is_amend \
3415 -command do_select_commit_type
1e02b32e
SP
3416 lappend disable_on_lock \
3417 [list .vpane.lower.commarea.buffer.header.amend conf -state]
3418}
3419
c80d7be5 3420${NS}::label $ui_coml \
cb07fc2a 3421 -anchor w \
7416bbc6 3422 -justify left
4539eacd
SP
3423proc trace_commit_type {varname args} {
3424 global ui_coml commit_type
3425 switch -glob -- $commit_type {
1ac17950
CS
3426 initial {set txt [mc "Initial Commit Message:"]}
3427 amend {set txt [mc "Amended Commit Message:"]}
3428 amend-initial {set txt [mc "Amended Initial Commit Message:"]}
3429 amend-merge {set txt [mc "Amended Merge Commit Message:"]}
3430 merge {set txt [mc "Merge Commit Message:"]}
3431 * {set txt [mc "Commit Message:"]}
4539eacd
SP
3432 }
3433 $ui_coml conf -text $txt
3434}
3435trace add variable commit_type write trace_commit_type
24ac9b75 3436pack $ui_coml -side left -fill x
1e02b32e
SP
3437
3438if {![is_enabled nocommit]} {
3439 pack .vpane.lower.commarea.buffer.header.amend -side right
1e02b32e 3440}
24ac9b75 3441
30508bc4 3442textframe .vpane.lower.commarea.buffer.frame
c02efc13 3443ttext $ui_comm \
c382fdd7 3444 -borderwidth 1 \
9861671d 3445 -undo true \
b2c6fcf1 3446 -maxundo 20 \
9861671d 3447 -autoseparators true \
30508bc4
PT
3448 -takefocus 1 \
3449 -highlightthickness 1 \
cb07fc2a 3450 -relief sunken \
11027d54 3451 -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
b4946930 3452 -font font_diff \
da08d559 3453 -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \
30508bc4 3454 -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set}
da08d559
BW
3455${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \
3456 -orient horizontal \
3457 -command [list $ui_comm xview]
30508bc4 3458${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
da08d559 3459 -orient vertical \
390adaea 3460 -command [list $ui_comm yview]
30508bc4 3461
da08d559 3462pack .vpane.lower.commarea.buffer.frame.sbx -side bottom -fill x
30508bc4 3463pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y
cb07fc2a 3464pack $ui_comm -side left -fill y
30508bc4
PT
3465pack .vpane.lower.commarea.buffer.header -side top -fill x
3466pack .vpane.lower.commarea.buffer.frame -side left -fill y
0fb8f9ce
SP
3467pack .vpane.lower.commarea.buffer -side left -fill y
3468
0e794311
SP
3469# -- Commit Message Buffer Context Menu
3470#
e8ab6446
SP
3471set ctxm .vpane.lower.commarea.buffer.ctxm
3472menu $ctxm -tearoff 0
3473$ctxm add command \
1ac17950 3474 -label [mc Cut] \
e8ab6446
SP
3475 -command {tk_textCut $ui_comm}
3476$ctxm add command \
1ac17950 3477 -label [mc Copy] \
e8ab6446
SP
3478 -command {tk_textCopy $ui_comm}
3479$ctxm add command \
1ac17950 3480 -label [mc Paste] \
e8ab6446
SP
3481 -command {tk_textPaste $ui_comm}
3482$ctxm add command \
1ac17950 3483 -label [mc Delete] \
bf516eca 3484 -command {catch {$ui_comm delete sel.first sel.last}}
e8ab6446
SP
3485$ctxm add separator
3486$ctxm add command \
1ac17950 3487 -label [mc "Select All"] \
75e78c8a 3488 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
e8ab6446 3489$ctxm add command \
1ac17950 3490 -label [mc "Copy All"] \
e8ab6446 3491 -command {
0e794311
SP
3492 $ui_comm tag add sel 0.0 end
3493 tk_textCopy $ui_comm
3494 $ui_comm tag remove sel 0.0 end
e8ab6446
SP
3495 }
3496$ctxm add separator
3497$ctxm add command \
1ac17950 3498 -label [mc "Sign Off"] \
0e794311 3499 -command do_signoff
95b002ee 3500set ui_comm_ctxm $ctxm
0e794311 3501
0fb8f9ce 3502# -- Diff Header
a49c67d1 3503#
20a53c02
SP
3504proc trace_current_diff_path {varname args} {
3505 global current_diff_path diff_actions file_states
3506 if {$current_diff_path eq {}} {
e8ab6446
SP
3507 set s {}
3508 set f {}
3509 set p {}
3510 set o disabled
3511 } else {
20a53c02 3512 set p $current_diff_path
e8ab6446 3513 set s [mapdesc [lindex $file_states($p) 0] $p]
1ac17950 3514 set f [mc "File:"]
e8ab6446
SP
3515 set p [escape_path $p]
3516 set o normal
3517 }
3518
3519 .vpane.lower.diff.header.status configure -text $s
3520 .vpane.lower.diff.header.file configure -text $f
3521 .vpane.lower.diff.header.path configure -text $p
3522 foreach w $diff_actions {
3523 uplevel #0 $w $o
3524 }
3525}
20a53c02 3526trace add variable current_diff_path write trace_current_diff_path
e8ab6446 3527
c80d7be5
PT
3528gold_frame .vpane.lower.diff.header
3529tlabel .vpane.lower.diff.header.status \
9adccb05 3530 -background gold \
c382fdd7 3531 -foreground black \
3e7b0e1d
SP
3532 -width $max_status_desc \
3533 -anchor w \
7416bbc6 3534 -justify left
c80d7be5 3535tlabel .vpane.lower.diff.header.file \
9adccb05 3536 -background gold \
c382fdd7 3537 -foreground black \
e8ab6446 3538 -anchor w \
7416bbc6 3539 -justify left
c80d7be5 3540tlabel .vpane.lower.diff.header.path \
9adccb05 3541 -background gold \
786f4d24 3542 -foreground blue \
fce89e46 3543 -anchor w \
786f4d24
ZS
3544 -justify left \
3545 -font [eval font create [font configure font_ui] -underline 1] \
3546 -cursor hand2
e8ab6446
SP
3547pack .vpane.lower.diff.header.status -side left
3548pack .vpane.lower.diff.header.file -side left
3549pack .vpane.lower.diff.header.path -fill x
3550set ctxm .vpane.lower.diff.header.ctxm
3551menu $ctxm -tearoff 0
3552$ctxm add command \
1ac17950 3553 -label [mc Copy] \
fce89e46
SP
3554 -command {
3555 clipboard clear
3556 clipboard append \
3557 -format STRING \
3558 -type STRING \
20a53c02 3559 -- $current_diff_path
fce89e46 3560 }
786f4d24
ZS
3561$ctxm add command \
3562 -label [mc Open] \
3563 -command {do_file_open $current_diff_path}
e8ab6446
SP
3564lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3565bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
786f4d24 3566bind .vpane.lower.diff.header.path <Button-1> {do_file_open $current_diff_path}
0fb8f9ce
SP
3567
3568# -- Diff Body
a49c67d1 3569#
30508bc4 3570textframe .vpane.lower.diff.body
0fb8f9ce 3571set ui_diff .vpane.lower.diff.body.t
c02efc13 3572ttext $ui_diff \
c382fdd7 3573 -borderwidth 0 \
acb9108c 3574 -width 80 -height 5 -wrap none \
b4946930 3575 -font font_diff \
30508bc4 3576 -takefocus 1 -highlightthickness 1 \
0fb8f9ce
SP
3577 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
3578 -yscrollcommand {.vpane.lower.diff.body.sby set} \
0fb8f9ce 3579 -state disabled
c7440869 3580catch {$ui_diff configure -tabstyle wordprocessor}
c80d7be5 3581${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
0fb8f9ce 3582 -command [list $ui_diff xview]
c80d7be5 3583${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
0fb8f9ce
SP
3584 -command [list $ui_diff yview]
3585pack .vpane.lower.diff.body.sbx -side bottom -fill x
3586pack .vpane.lower.diff.body.sby -side right -fill y
3587pack $ui_diff -side left -fill both -expand 1
3588pack .vpane.lower.diff.header -side top -fill x
3589pack .vpane.lower.diff.body -side bottom -fill both -expand 1
3590
8f85599a
PT
3591foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
3592 $ui_diff tag configure clr4$n -background $c
3593 $ui_diff tag configure clri4$n -foreground $c
3594 $ui_diff tag configure clr3$n -foreground $c
3595 $ui_diff tag configure clri3$n -background $c
3596}
3597$ui_diff tag configure clr1 -font font_diffbold
9af6413b 3598$ui_diff tag configure clr4 -underline 1
8f85599a 3599
88b21c2a
BW
3600$ui_diff tag conf d_info -foreground blue -font font_diffbold
3601
30b14ed3 3602$ui_diff tag conf d_cr -elide true
8f85599a 3603$ui_diff tag conf d_@ -font font_diffbold
ca521566 3604$ui_diff tag conf d_+ -foreground {#00a000}
fec4a785
SP
3605$ui_diff tag conf d_- -foreground red
3606
ca521566 3607$ui_diff tag conf d_++ -foreground {#00a000}
fec4a785
SP
3608$ui_diff tag conf d_-- -foreground red
3609$ui_diff tag conf d_+s \
ca521566
SP
3610 -foreground {#00a000} \
3611 -background {#e2effa}
fec4a785
SP
3612$ui_diff tag conf d_-s \
3613 -foreground red \
ca521566 3614 -background {#e2effa}
fec4a785 3615$ui_diff tag conf d_s+ \
ca521566
SP
3616 -foreground {#00a000} \
3617 -background ivory1
fec4a785
SP
3618$ui_diff tag conf d_s- \
3619 -foreground red \
ca521566 3620 -background ivory1
fec4a785 3621
4590307a 3622$ui_diff tag conf d< \
fec4a785
SP
3623 -foreground orange \
3624 -font font_diffbold
b436825b
BW
3625$ui_diff tag conf d| \
3626 -foreground orange \
3627 -font font_diffbold
4590307a 3628$ui_diff tag conf d= \
fec4a785
SP
3629 -foreground orange \
3630 -font font_diffbold
4590307a 3631$ui_diff tag conf d> \
fec4a785
SP
3632 -foreground orange \
3633 -font font_diffbold
cb07fc2a 3634
ca521566
SP
3635$ui_diff tag raise sel
3636
0e794311
SP
3637# -- Diff Body Context Menu
3638#
042c2325
AG
3639
3640proc create_common_diff_popup {ctxm} {
042c2325
AG
3641 $ctxm add command \
3642 -label [mc Refresh] \
3643 -command reshow_diff
3644 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3645 $ctxm add command \
3646 -label [mc Copy] \
3647 -command {tk_textCopy $ui_diff}
3648 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3649 $ctxm add command \
3650 -label [mc "Select All"] \
3651 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
3652 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3653 $ctxm add command \
3654 -label [mc "Copy All"] \
3655 -command {
3656 $ui_diff tag add sel 0.0 end
3657 tk_textCopy $ui_diff
3658 $ui_diff tag remove sel 0.0 end
3659 }
3660 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3661 $ctxm add separator
3662 $ctxm add command \
3663 -label [mc "Decrease Font Size"] \
3664 -command {incr_font_size font_diff -1}
3665 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3666 $ctxm add command \
3667 -label [mc "Increase Font Size"] \
3668 -command {incr_font_size font_diff 1}
3669 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3670 $ctxm add separator
3fe01623
AG
3671 set emenu $ctxm.enc
3672 menu $emenu
3673 build_encoding_menu $emenu [list force_diff_encoding]
3674 $ctxm add cascade \
3675 -label [mc "Encoding"] \
3676 -menu $emenu
3677 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3678 $ctxm add separator
042c2325
AG
3679 $ctxm add command -label [mc "Options..."] \
3680 -command do_options
3681}
3682
e8ab6446
SP
3683set ctxm .vpane.lower.diff.body.ctxm
3684menu $ctxm -tearoff 0
fba6072e
JS
3685$ctxm add command \
3686 -label [mc "Apply/Reverse Hunk"] \
62bd9993 3687 -command {apply_or_revert_hunk $cursorX $cursorY 0}
fba6072e
JS
3688set ui_diff_applyhunk [$ctxm index last]
3689lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
5821988f
JS
3690$ctxm add command \
3691 -label [mc "Apply/Reverse Line"] \
5f0a516d 3692 -command {apply_or_revert_range_or_line $cursorX $cursorY 0; do_rescan}
5821988f
JS
3693set ui_diff_applyline [$ctxm index last]
3694lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
fba6072e 3695$ctxm add separator
62bd9993
PY
3696$ctxm add command \
3697 -label [mc "Revert Hunk"] \
3698 -command {apply_or_revert_hunk $cursorX $cursorY 1}
3699set ui_diff_reverthunk [$ctxm index last]
3700lappend diff_actions [list $ctxm entryconf $ui_diff_reverthunk -state]
5f0a516d
PY
3701$ctxm add command \
3702 -label [mc "Revert Line"] \
3703 -command {apply_or_revert_range_or_line $cursorX $cursorY 1; do_rescan}
3704set ui_diff_revertline [$ctxm index last]
3705lappend diff_actions [list $ctxm entryconf $ui_diff_revertline -state]
a4fa2f0a
PY
3706$ctxm add command \
3707 -label [mc "Undo Last Revert"] \
3708 -command {undo_last_revert; do_rescan}
3709set ui_diff_undorevert [$ctxm index last]
3710lappend diff_actions [list $ctxm entryconf $ui_diff_undorevert -state]
5f0a516d 3711$ctxm add separator
25476c63
JL
3712$ctxm add command \
3713 -label [mc "Show Less Context"] \
3714 -command show_less_context
3715lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3716$ctxm add command \
3717 -label [mc "Show More Context"] \
3718 -command show_more_context
3719lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3720$ctxm add separator
042c2325
AG
3721create_common_diff_popup $ctxm
3722
3723set ctxmmg .vpane.lower.diff.body.ctxmmg
3724menu $ctxmmg -tearoff 0
7e30682c
AG
3725$ctxmmg add command \
3726 -label [mc "Run Merge Tool"] \
3727 -command {merge_resolve_tool}
3728lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3729$ctxmmg add separator
042c2325
AG
3730$ctxmmg add command \
3731 -label [mc "Use Remote Version"] \
3732 -command {merge_resolve_one 3}
3733lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3734$ctxmmg add command \
3735 -label [mc "Use Local Version"] \
3736 -command {merge_resolve_one 2}
3737lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3738$ctxmmg add command \
3739 -label [mc "Revert To Base"] \
3740 -command {merge_resolve_one 1}
3741lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3742$ctxmmg add separator
25476c63
JL
3743$ctxmmg add command \
3744 -label [mc "Show Less Context"] \
3745 -command show_less_context
3746lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3747$ctxmmg add command \
3748 -label [mc "Show More Context"] \
3749 -command show_more_context
3750lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3751$ctxmmg add separator
042c2325
AG
3752create_common_diff_popup $ctxmmg
3753
25476c63
JL
3754set ctxmsm .vpane.lower.diff.body.ctxmsm
3755menu $ctxmsm -tearoff 0
3756$ctxmsm add command \
3757 -label [mc "Visualize These Changes In The Submodule"] \
3758 -command {do_gitk -- true}
3759lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3760$ctxmsm add command \
3761 -label [mc "Visualize Current Branch History In The Submodule"] \
3762 -command {do_gitk {} true}
3763lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3764$ctxmsm add command \
3765 -label [mc "Visualize All Branch History In The Submodule"] \
3766 -command {do_gitk --all true}
3767lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3768$ctxmsm add separator
3769$ctxmsm add command \
3770 -label [mc "Start git gui In The Submodule"] \
3771 -command {do_git_gui}
3772lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3773$ctxmsm add separator
3774create_common_diff_popup $ctxmsm
3775
1fbaccad
CP
3776proc has_textconv {path} {
3777 if {[is_config_false gui.textconv]} {
3778 return 0
3779 }
3780 set filter [gitattr $path diff set]
3781 set textconv [get_config [join [list diff $filter textconv] .]]
3782 if {$filter ne {set} && $textconv ne {}} {
3783 return 1
3784 } else {
3785 return 0
3786 }
3787}
3788
25476c63 3789proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
a4fa2f0a 3790 global current_diff_path file_states last_revert
83751fc1
SP
3791 set ::cursorX $x
3792 set ::cursorY $y
042c2325
AG
3793 if {[info exists file_states($current_diff_path)]} {
3794 set state [lindex $file_states($current_diff_path) 0]
a25c5189 3795 } else {
042c2325
AG
3796 set state {__}
3797 }
ff515d81 3798 if {[string first {U} $state] >= 0} {
042c2325 3799 tk_popup $ctxmmg $X $Y
25476c63
JL
3800 } elseif {$::is_submodule_diff} {
3801 tk_popup $ctxmsm $X $Y
047d94d5 3802 } else {
ff07c3b6 3803 set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
a4fa2f0a 3804 set u [mc "Undo Last Revert"]
042c2325
AG
3805 if {$::ui_index eq $::current_diff_side} {
3806 set l [mc "Unstage Hunk From Commit"]
62bd9993
PY
3807 set h [mc "Revert Hunk"]
3808
ff07c3b6
JE
3809 if {$has_range} {
3810 set t [mc "Unstage Lines From Commit"]
5f0a516d 3811 set r [mc "Revert Lines"]
ff07c3b6
JE
3812 } else {
3813 set t [mc "Unstage Line From Commit"]
5f0a516d 3814 set r [mc "Revert Line"]
ff07c3b6 3815 }
042c2325
AG
3816 } else {
3817 set l [mc "Stage Hunk For Commit"]
62bd9993
PY
3818 set h [mc "Revert Hunk"]
3819
ff07c3b6
JE
3820 if {$has_range} {
3821 set t [mc "Stage Lines For Commit"]
5f0a516d 3822 set r [mc "Revert Lines"]
ff07c3b6
JE
3823 } else {
3824 set t [mc "Stage Line For Commit"]
5f0a516d 3825 set r [mc "Revert Line"]
ff07c3b6 3826 }
042c2325 3827 }
25476c63 3828 if {$::is_3way_diff
042c2325
AG
3829 || $current_diff_path eq {}
3830 || {__} eq $state
3831 || {_O} eq $state
7587f4d3
BW
3832 || [string match {?T} $state]
3833 || [string match {T?} $state]
1fbaccad 3834 || [has_textconv $current_diff_path]} {
042c2325 3835 set s disabled
5f0a516d 3836 set revert_state disabled
042c2325
AG
3837 } else {
3838 set s normal
5f0a516d
PY
3839
3840 # Only allow reverting changes in the working tree. If
3841 # the user wants to revert changes in the index, they
3842 # need to unstage those first.
3843 if {$::ui_workdir eq $::current_diff_side} {
3844 set revert_state normal
3845 } else {
3846 set revert_state disabled
3847 }
042c2325 3848 }
5f0a516d 3849
a4fa2f0a
PY
3850 if {$last_revert eq {}} {
3851 set undo_state disabled
3852 } else {
3853 set undo_state normal
3854 }
3855
042c2325
AG
3856 $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
3857 $ctxm entryconf $::ui_diff_applyline -state $s -label $t
5f0a516d
PY
3858 $ctxm entryconf $::ui_diff_revertline -state $revert_state \
3859 -label $r
62bd9993
PY
3860 $ctxm entryconf $::ui_diff_reverthunk -state $revert_state \
3861 -label $h
a4fa2f0a
PY
3862 $ctxm entryconf $::ui_diff_undorevert -state $undo_state \
3863 -label $u
62bd9993 3864
042c2325 3865 tk_popup $ctxm $X $Y
9c9f5fa9 3866 }
83751fc1 3867}
25476c63 3868bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg $ctxmsm %x %y %X %Y]
0e794311 3869
cb07fc2a 3870# -- Status Bar
e8ab6446 3871#
51530d17 3872set main_status [::status_bar::new .status]
cb07fc2a 3873pack .status -anchor w -side bottom -fill x
1ac17950 3874$main_status show [mc "Initializing..."]
cb07fc2a 3875
2d19516d 3876# -- Load geometry
e8ab6446 3877#
2810a58d
PT
3878proc on_ttk_pane_mapped {w pane pos} {
3879 bind $w <Map> {}
3880 after 0 [list after idle [list $w sashpos $pane $pos]]
3881}
3882proc on_tk_pane_mapped {w pane x y} {
3883 bind $w <Map> {}
3884 after 0 [list after idle [list $w sash place $pane $x $y]]
3885}
3886proc on_application_mapped {} {
3887 global repo_config use_ttk
3888 bind . <Map> {}
3889 set gm $repo_config(gui.geometry)
3890 if {$use_ttk} {
3891 bind .vpane <Map> \
5c1b3913 3892 [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
2810a58d 3893 bind .vpane.files <Map> \
5c1b3913 3894 [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
2810a58d
PT
3895 } else {
3896 bind .vpane <Map> \
5c1b3913
ST
3897 [list on_tk_pane_mapped %W 0 \
3898 [lindex $gm 1] \
3899 [lindex [.vpane sash coord 0] 1]]
2810a58d 3900 bind .vpane.files <Map> \
5c1b3913
ST
3901 [list on_tk_pane_mapped %W 0 \
3902 [lindex [.vpane.files sash coord 0] 0] \
3903 [lindex $gm 2]]
2810a58d
PT
3904 }
3905 wm geometry . [lindex $gm 0]
c80d7be5 3906}
2810a58d
PT
3907if {[info exists repo_config(gui.geometry)]} {
3908 bind . <Map> [list on_application_mapped]
3909 wm geometry . [lindex $repo_config(gui.geometry) 0]
390adaea 3910}
2d19516d 3911
ed7b6033
AB
3912# -- Load window state
3913#
2810a58d
PT
3914if {[info exists repo_config(gui.wmstate)]} {
3915 catch {wm state . $repo_config(gui.wmstate)}
ed7b6033
AB
3916}
3917
cb07fc2a 3918# -- Key Bindings
e8ab6446 3919#
ec6b424a 3920bind $ui_comm <$M1B-Key-Return> {do_commit;break}
cd16a6c9
SP
3921bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
3922bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
b677c66e
VS
3923bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
3924bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
3925bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
3926bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
93e912c5
SP
3927bind $ui_comm <$M1B-Key-i> {do_add_all;break}
3928bind $ui_comm <$M1B-Key-I> {do_add_all;break}
9861671d
SP
3929bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
3930bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
3931bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
3932bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
3933bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
3934bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
3935bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3936bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
729ffa50
MB
3937bind $ui_comm <$M1B-Key-minus> {show_less_context;break}
3938bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
3939bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
3940bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
3941bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
e5894146
IL
3942bind $ui_comm <$M1B-Key-BackSpace> {event generate %W <Meta-Delete>;break}
3943bind $ui_comm <$M1B-Key-Delete> {event generate %W <Meta-d>;break}
9861671d
SP
3944
3945bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
3946bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
3947bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
3948bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
3949bind $ui_diff <$M1B-Key-v> {break}
3950bind $ui_diff <$M1B-Key-V> {break}
3951bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3952bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
44e88ce0
VS
3953bind $ui_diff <$M1B-Key-j> {do_revert_selection;break}
3954bind $ui_diff <$M1B-Key-J> {do_revert_selection;break}
b2c6fcf1
SP
3955bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
3956bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
3957bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
3958bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
60aa065f
SP
3959bind $ui_diff <Key-k> {catch {%W yview scroll -1 units};break}
3960bind $ui_diff <Key-j> {catch {%W yview scroll 1 units};break}
3961bind $ui_diff <Key-h> {catch {%W xview scroll -1 units};break}
3962bind $ui_diff <Key-l> {catch {%W xview scroll 1 units};break}
3963bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
3964bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
23effa79 3965bind $ui_diff <Button-1> {focus %W}
49b86f01 3966
64a906f8 3967if {[is_enabled branch]} {
b1fa2bff
SP
3968 bind . <$M1B-Key-n> branch_create::dialog
3969 bind . <$M1B-Key-N> branch_create::dialog
d41b43eb
SP
3970 bind . <$M1B-Key-o> branch_checkout::dialog
3971 bind . <$M1B-Key-O> branch_checkout::dialog
a870ddc0
SP
3972 bind . <$M1B-Key-m> merge::dialog
3973 bind . <$M1B-Key-M> merge::dialog
bd29ebc3 3974}
840bcfa7
SP
3975if {[is_enabled transport]} {
3976 bind . <$M1B-Key-p> do_push_anywhere
3977 bind . <$M1B-Key-P> do_push_anywhere
3978}
bd29ebc3 3979
8056cc4f
AG
3980bind . <Key-F5> ui_do_rescan
3981bind . <$M1B-Key-r> ui_do_rescan
3982bind . <$M1B-Key-R> ui_do_rescan
07123f40
SP
3983bind . <$M1B-Key-s> do_signoff
3984bind . <$M1B-Key-S> do_signoff
088ad75d
PT
3985bind . <$M1B-Key-t> { toggle_or_diff toggle %W }
3986bind . <$M1B-Key-T> { toggle_or_diff toggle %W }
3987bind . <$M1B-Key-u> { toggle_or_diff toggle %W }
3988bind . <$M1B-Key-U> { toggle_or_diff toggle %W }
d6db1bbe
HV
3989bind . <$M1B-Key-j> do_revert_selection
3990bind . <$M1B-Key-J> do_revert_selection
93e912c5
SP
3991bind . <$M1B-Key-i> do_add_all
3992bind . <$M1B-Key-I> do_add_all
ec7424e1
BSP
3993bind . <$M1B-Key-e> toggle_commit_type
3994bind . <$M1B-Key-E> toggle_commit_type
729ffa50
MB
3995bind . <$M1B-Key-minus> {show_less_context;break}
3996bind . <$M1B-Key-KP_Subtract> {show_less_context;break}
3997bind . <$M1B-Key-equal> {show_more_context;break}
3998bind . <$M1B-Key-plus> {show_more_context;break}
3999bind . <$M1B-Key-KP_Add> {show_more_context;break}
07123f40 4000bind . <$M1B-Key-Return> do_commit
146a6f10 4001bind . <$M1B-Key-KP_Enter> do_commit
0812665e 4002foreach i [list $ui_index $ui_workdir] {
088ad75d
PT
4003 bind $i <Button-1> { toggle_or_diff click %W %x %y; break }
4004 bind $i <$M1B-Button-1> { add_one_to_selection %W %x %y; break }
4005 bind $i <Shift-Button-1> { add_range_to_selection %W %x %y; break }
4006 bind $i <Key-Up> { toggle_or_diff up %W; break }
4007 bind $i <Key-Down> { toggle_or_diff down %W; break }
cb07fc2a 4008}
62aac80b
SP
4009unset i
4010
e07446ed
BSP
4011bind . <Alt-Key-1> {focus_widget $::ui_workdir}
4012bind . <Alt-Key-2> {focus_widget $::ui_index}
4013bind . <Alt-Key-3> {focus $::ui_diff}
4014bind . <Alt-Key-4> {focus $::ui_comm}
4015
4016set file_lists_last_clicked($ui_index) {}
4017set file_lists_last_clicked($ui_workdir) {}
4018
62aac80b 4019set file_lists($ui_index) [list]
0812665e 4020set file_lists($ui_workdir) [list]
a49c67d1 4021
21985a11 4022wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
cb07fc2a 4023focus -force $ui_comm
1d8b3cbf 4024
85ab313e
SP
4025# -- Warn the user about environmental problems. Cygwin's Tcl
4026# does *not* pass its env array onto any processes it spawns.
4027# This means that git processes get none of our environment.
1d8b3cbf 4028#
20ddfcaa 4029if {[is_Cygwin]} {
1d8b3cbf
SP
4030 set ignored_env 0
4031 set suggest_user {}
c8c4854b 4032 set msg [mc "Possible environment issues exist.
1d8b3cbf
SP
4033
4034The following environment variables are probably
4035going to be ignored by any Git subprocess run
c8c4854b 4036by %s:
1d8b3cbf 4037
c8c4854b 4038" [appname]]
1d8b3cbf
SP
4039 foreach name [array names env] {
4040 switch -regexp -- $name {
4041 {^GIT_INDEX_FILE$} -
4042 {^GIT_OBJECT_DIRECTORY$} -
4043 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
4044 {^GIT_DIFF_OPTS$} -
4045 {^GIT_EXTERNAL_DIFF$} -
4046 {^GIT_PAGER$} -
4047 {^GIT_TRACE$} -
4048 {^GIT_CONFIG$} -
1d8b3cbf
SP
4049 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
4050 append msg " - $name\n"
4051 incr ignored_env
4052 }
4053 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
4054 append msg " - $name\n"
4055 incr ignored_env
4056 set suggest_user $name
4057 }
4058 }
4059 }
4060 if {$ignored_env > 0} {
c8c4854b 4061 append msg [mc "
1d8b3cbf 4062This is due to a known issue with the
c8c4854b 4063Tcl binary distributed by Cygwin."]
1d8b3cbf
SP
4064
4065 if {$suggest_user ne {}} {
c8c4854b 4066 append msg [mc "
1d8b3cbf 4067
c8c4854b 4068A good replacement for %s
1d8b3cbf
SP
4069is placing values for the user.name and
4070user.email settings into your personal
4071~/.gitconfig file.
c8c4854b 4072" $suggest_user]
1d8b3cbf
SP
4073 }
4074 warn_popup $msg
4075 }
4076 unset ignored_env msg suggest_user name
4077}
4078
85ab313e
SP
4079# -- Only initialize complex UI if we are going to stay running.
4080#
64a906f8 4081if {[is_enabled transport]} {
4ccdab02 4082 load_all_remotes
85ab313e 4083
6bdf5e5f 4084 set n [.mbar.remote index end]
8329bd07 4085 populate_remotes_menu
6bdf5e5f
SP
4086 set n [expr {[.mbar.remote index end] - $n}]
4087 if {$n > 0} {
7e09b153 4088 if {[.mbar.remote type 0] eq "tearoff"} { incr n }
6bdf5e5f
SP
4089 .mbar.remote insert $n separator
4090 }
4091 unset n
4ccdab02 4092}
85ab313e 4093
4578c5cb 4094if {[winfo exists $ui_comm]} {
fda1ba02 4095 set GITGUI_BCK_exists [load_message GITGUI_BCK utf-8]
4578c5cb
SP
4096
4097 # -- If both our backup and message files exist use the
4098 # newer of the two files to initialize the buffer.
4099 #
4100 if {$GITGUI_BCK_exists} {
4101 set m [gitdir GITGUI_MSG]
4102 if {[file isfile $m]} {
4103 if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
4104 catch {file delete [gitdir GITGUI_MSG]}
4105 } else {
4106 $ui_comm delete 0.0 end
4107 $ui_comm edit reset
4108 $ui_comm edit modified false
4109 catch {file delete [gitdir GITGUI_BCK]}
4110 set GITGUI_BCK_exists 0
4111 }
4112 }
4113 unset m
4114 }
4115
4116 proc backup_commit_buffer {} {
4117 global ui_comm GITGUI_BCK_exists
4118
4119 set m [$ui_comm edit modified]
4120 if {$m || $GITGUI_BCK_exists} {
4121 set msg [string trim [$ui_comm get 0.0 end]]
4122 regsub -all -line {[ \r\t]+$} $msg {} msg
4123
4124 if {$msg eq {}} {
4125 if {$GITGUI_BCK_exists} {
4126 catch {file delete [gitdir GITGUI_BCK]}
4127 set GITGUI_BCK_exists 0
4128 }
4129 } elseif {$m} {
4130 catch {
4131 set fd [open [gitdir GITGUI_BCK] w]
fda1ba02 4132 fconfigure $fd -encoding utf-8
4578c5cb
SP
4133 puts -nonewline $fd $msg
4134 close $fd
4135 set GITGUI_BCK_exists 1
4136 }
4137 }
4138
4139 $ui_comm edit modified false
4140 }
4141
4142 set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
4143 }
4144
4145 backup_commit_buffer
95b002ee
SP
4146
4147 # -- If the user has aspell available we can drive it
4148 # in pipe mode to spellcheck the commit message.
4149 #
4150 set spell_cmd [list |]
4151 set spell_dict [get_config gui.spellingdictionary]
4152 lappend spell_cmd aspell
4153 if {$spell_dict ne {}} {
4154 lappend spell_cmd --master=$spell_dict
4155 }
4156 lappend spell_cmd --mode=none
4157 lappend spell_cmd --encoding=utf-8
4158 lappend spell_cmd pipe
4159 if {$spell_dict eq {none}
4160 || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
4161 bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
4162 } else {
4163 set ui_comm_spell [spellcheck::init \
4164 $spell_fd \
4165 $ui_comm \
4166 $ui_comm_ctxm \
4167 ]
4168 }
4169 unset -nocomplain spell_cmd spell_fd spell_err spell_dict
4578c5cb
SP
4170}
4171
53716a7b 4172lock_index begin-read
301dfaa9
SP
4173if {![winfo ismapped .]} {
4174 wm deiconify .
4175}
1e65c622
AG
4176after 1 {
4177 if {[is_enabled initialamend]} {
4178 force_amend
4179 } else {
4180 do_rescan
4181 }
4182
4183 if {[is_enabled nocommitmsg]} {
4184 $ui_comm configure -state disabled -background gray
4185 }
4186}
af867683 4187if {[is_enabled multicommit] && ![is_config_false gui.gcwarning]} {
3972b987
SP
4188 after 1000 hint_gc
4189}
1e65c622
AG
4190if {[is_enabled retcode]} {
4191 bind . <Destroy> {+terminate_me %W}
4192}
bb4812bc
PB
4193if {$picked && [is_config_true gui.autoexplore]} {
4194 do_explore
4195}
c80d7be5 4196
d9c6469f
JG
4197# Clear "Initializing..." status
4198after 500 {$main_status show ""}
4199
c80d7be5
PT
4200# Local variables:
4201# mode: tcl
4202# indent-tabs-mode: t
4203# tab-width: 4
4204# End: