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