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