Code

Merge branch 'maint'
[git.git] / git-gui.sh
1 #!/bin/sh
2 # Tcl ignores the next line -*- tcl -*- \
3 exec wish "$0" -- "$@"
5 set appvers {@@GITGUI_VERSION@@}
6 set copyright {
7 Copyright © 2006, 2007 Shawn Pearce, et. al.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA}
23 ######################################################################
24 ##
25 ## Tcl/Tk sanity check
27 if {[catch {package require Tcl 8.4} err]
28  || [catch {package require Tk  8.4} err]
29 } {
30         catch {wm withdraw .}
31         tk_messageBox \
32                 -icon error \
33                 -type ok \
34                 -title "git-gui: fatal error" \
35                 -message $err
36         exit 1
37 }
39 ######################################################################
40 ##
41 ## enable verbose loading?
43 if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
44         unset _verbose
45         rename auto_load real__auto_load
46         proc auto_load {name args} {
47                 puts stderr "auto_load $name"
48                 return [uplevel 1 real__auto_load $name $args]
49         }
50         rename source real__source
51         proc source {name} {
52                 puts stderr "source    $name"
53                 uplevel 1 real__source $name
54         }
55 }
57 ######################################################################
58 ##
59 ## configure our library
61 set oguilib {@@GITGUI_LIBDIR@@}
62 set oguirel {@@GITGUI_RELATIVE@@}
63 if {$oguirel eq {1}} {
64         set oguilib [file dirname [file dirname [file normalize $argv0]]]
65         set oguilib [file join $oguilib share git-gui lib]
66 } elseif {[string match @@* $oguirel]} {
67         set oguilib [file join [file dirname [file normalize $argv0]] lib]
68 }
70 set idx [file join $oguilib tclIndex]
71 if {[catch {set fd [open $idx r]} err]} {
72         catch {wm withdraw .}
73         tk_messageBox \
74                 -icon error \
75                 -type ok \
76                 -title "git-gui: fatal error" \
77                 -message $err
78         exit 1
79 }
80 if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
81         set idx [list]
82         while {[gets $fd n] >= 0} {
83                 if {$n ne {} && ![string match #* $n]} {
84                         lappend idx $n
85                 }
86         }
87 } else {
88         set idx {}
89 }
90 close $fd
92 if {$idx ne {}} {
93         set loaded [list]
94         foreach p $idx {
95                 if {[lsearch -exact $loaded $p] >= 0} continue
96                 source [file join $oguilib $p]
97                 lappend loaded $p
98         }
99         unset loaded p
100 } else {
101         set auto_path [concat [list $oguilib] $auto_path]
103 unset -nocomplain oguirel idx fd
105 ######################################################################
106 ##
107 ## read only globals
109 set _appname [lindex [file split $argv0] end]
110 set _gitdir {}
111 set _gitexec {}
112 set _reponame {}
113 set _iscygwin {}
115 proc appname {} {
116         global _appname
117         return $_appname
120 proc gitdir {args} {
121         global _gitdir
122         if {$args eq {}} {
123                 return $_gitdir
124         }
125         return [eval [concat [list file join $_gitdir] $args]]
128 proc gitexec {args} {
129         global _gitexec
130         if {$_gitexec eq {}} {
131                 if {[catch {set _gitexec [git --exec-path]} err]} {
132                         error "Git not installed?\n\n$err"
133                 }
134         }
135         if {$args eq {}} {
136                 return $_gitexec
137         }
138         return [eval [concat [list file join $_gitexec] $args]]
141 proc reponame {} {
142         global _reponame
143         return $_reponame
146 proc is_MacOSX {} {
147         global tcl_platform tk_library
148         if {[tk windowingsystem] eq {aqua}} {
149                 return 1
150         }
151         return 0
154 proc is_Windows {} {
155         global tcl_platform
156         if {$tcl_platform(platform) eq {windows}} {
157                 return 1
158         }
159         return 0
162 proc is_Cygwin {} {
163         global tcl_platform _iscygwin
164         if {$_iscygwin eq {}} {
165                 if {$tcl_platform(platform) eq {windows}} {
166                         if {[catch {set p [exec cygpath --windir]} err]} {
167                                 set _iscygwin 0
168                         } else {
169                                 set _iscygwin 1
170                         }
171                 } else {
172                         set _iscygwin 0
173                 }
174         }
175         return $_iscygwin
178 proc is_enabled {option} {
179         global enabled_options
180         if {[catch {set on $enabled_options($option)}]} {return 0}
181         return $on
184 proc enable_option {option} {
185         global enabled_options
186         set enabled_options($option) 1
189 proc disable_option {option} {
190         global enabled_options
191         set enabled_options($option) 0
194 ######################################################################
195 ##
196 ## config
198 proc is_many_config {name} {
199         switch -glob -- $name {
200         remote.*.fetch -
201         remote.*.push
202                 {return 1}
203         *
204                 {return 0}
205         }
208 proc is_config_true {name} {
209         global repo_config
210         if {[catch {set v $repo_config($name)}]} {
211                 return 0
212         } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
213                 return 1
214         } else {
215                 return 0
216         }
219 proc get_config {name} {
220         global repo_config
221         if {[catch {set v $repo_config($name)}]} {
222                 return {}
223         } else {
224                 return $v
225         }
228 proc load_config {include_global} {
229         global repo_config global_config default_config
231         array unset global_config
232         if {$include_global} {
233                 catch {
234                         set fd_rc [open "| git config --global --list" r]
235                         while {[gets $fd_rc line] >= 0} {
236                                 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
237                                         if {[is_many_config $name]} {
238                                                 lappend global_config($name) $value
239                                         } else {
240                                                 set global_config($name) $value
241                                         }
242                                 }
243                         }
244                         close $fd_rc
245                 }
246         }
248         array unset repo_config
249         catch {
250                 set fd_rc [open "| git config --list" r]
251                 while {[gets $fd_rc line] >= 0} {
252                         if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
253                                 if {[is_many_config $name]} {
254                                         lappend repo_config($name) $value
255                                 } else {
256                                         set repo_config($name) $value
257                                 }
258                         }
259                 }
260                 close $fd_rc
261         }
263         foreach name [array names default_config] {
264                 if {[catch {set v $global_config($name)}]} {
265                         set global_config($name) $default_config($name)
266                 }
267                 if {[catch {set v $repo_config($name)}]} {
268                         set repo_config($name) $default_config($name)
269                 }
270         }
273 ######################################################################
274 ##
275 ## handy utils
277 proc git {args} {
278         return [eval exec git $args]
281 proc current-branch {} {
282         set ref {}
283         set fd [open [gitdir HEAD] r]
284         if {[gets $fd ref] <16
285          || ![regsub {^ref: refs/heads/} $ref {} ref]} {
286                 set ref {}
287         }
288         close $fd
289         return $ref
292 auto_load tk_optionMenu
293 rename tk_optionMenu real__tkOptionMenu
294 proc tk_optionMenu {w varName args} {
295         set m [eval real__tkOptionMenu $w $varName $args]
296         $m configure -font font_ui
297         $w configure -font font_ui
298         return $m
301 ######################################################################
302 ##
303 ## version check
305 if {{--version} eq $argv || {version} eq $argv} {
306         puts "git-gui version $appvers"
307         exit
310 set req_maj 1
311 set req_min 5
313 if {[catch {set v [git --version]} err]} {
314         catch {wm withdraw .}
315         error_popup "Cannot determine Git version:
317 $err
319 [appname] requires Git $req_maj.$req_min or later."
320         exit 1
322 if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
323         if {$act_maj < $req_maj
324                 || ($act_maj == $req_maj && $act_min < $req_min)} {
325                 catch {wm withdraw .}
326                 error_popup "[appname] requires Git $req_maj.$req_min or later.
328 You are using $v."
329                 exit 1
330         }
331 } else {
332         catch {wm withdraw .}
333         error_popup "Cannot parse Git version string:\n\n$v"
334         exit 1
336 unset -nocomplain v _junk act_maj act_min req_maj req_min
338 ######################################################################
339 ##
340 ## repository setup
342 if {[catch {
343                 set _gitdir $env(GIT_DIR)
344                 set _prefix {}
345                 }]
346         && [catch {
347                 set _gitdir [git rev-parse --git-dir]
348                 set _prefix [git rev-parse --show-prefix]
349         } err]} {
350         catch {wm withdraw .}
351         error_popup "Cannot find the git directory:\n\n$err"
352         exit 1
354 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
355         catch {set _gitdir [exec cygpath --unix $_gitdir]}
357 if {![file isdirectory $_gitdir]} {
358         catch {wm withdraw .}
359         error_popup "Git directory not found:\n\n$_gitdir"
360         exit 1
362 if {[lindex [file split $_gitdir] end] ne {.git}} {
363         catch {wm withdraw .}
364         error_popup "Cannot use funny .git directory:\n\n$_gitdir"
365         exit 1
367 if {[catch {cd [file dirname $_gitdir]} err]} {
368         catch {wm withdraw .}
369         error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
370         exit 1
372 set _reponame [lindex [file split \
373         [file normalize [file dirname $_gitdir]]] \
374         end]
376 ######################################################################
377 ##
378 ## global init
380 set current_diff_path {}
381 set current_diff_side {}
382 set diff_actions [list]
383 set ui_status_value {Initializing...}
385 set HEAD {}
386 set PARENT {}
387 set MERGE_HEAD [list]
388 set commit_type {}
389 set empty_tree {}
390 set current_branch {}
391 set current_diff_path {}
392 set selected_commit_type new
394 ######################################################################
395 ##
396 ## task management
398 set rescan_active 0
399 set diff_active 0
400 set last_clicked {}
402 set disable_on_lock [list]
403 set index_lock_type none
405 proc lock_index {type} {
406         global index_lock_type disable_on_lock
408         if {$index_lock_type eq {none}} {
409                 set index_lock_type $type
410                 foreach w $disable_on_lock {
411                         uplevel #0 $w disabled
412                 }
413                 return 1
414         } elseif {$index_lock_type eq "begin-$type"} {
415                 set index_lock_type $type
416                 return 1
417         }
418         return 0
421 proc unlock_index {} {
422         global index_lock_type disable_on_lock
424         set index_lock_type none
425         foreach w $disable_on_lock {
426                 uplevel #0 $w normal
427         }
430 ######################################################################
431 ##
432 ## status
434 proc repository_state {ctvar hdvar mhvar} {
435         global current_branch
436         upvar $ctvar ct $hdvar hd $mhvar mh
438         set mh [list]
440         set current_branch [current-branch]
441         if {[catch {set hd [git rev-parse --verify HEAD]}]} {
442                 set hd {}
443                 set ct initial
444                 return
445         }
447         set merge_head [gitdir MERGE_HEAD]
448         if {[file exists $merge_head]} {
449                 set ct merge
450                 set fd_mh [open $merge_head r]
451                 while {[gets $fd_mh line] >= 0} {
452                         lappend mh $line
453                 }
454                 close $fd_mh
455                 return
456         }
458         set ct normal
461 proc PARENT {} {
462         global PARENT empty_tree
464         set p [lindex $PARENT 0]
465         if {$p ne {}} {
466                 return $p
467         }
468         if {$empty_tree eq {}} {
469                 set empty_tree [git mktree << {}]
470         }
471         return $empty_tree
474 proc rescan {after {honor_trustmtime 1}} {
475         global HEAD PARENT MERGE_HEAD commit_type
476         global ui_index ui_workdir ui_status_value ui_comm
477         global rescan_active file_states
478         global repo_config
480         if {$rescan_active > 0 || ![lock_index read]} return
482         repository_state newType newHEAD newMERGE_HEAD
483         if {[string match amend* $commit_type]
484                 && $newType eq {normal}
485                 && $newHEAD eq $HEAD} {
486         } else {
487                 set HEAD $newHEAD
488                 set PARENT $newHEAD
489                 set MERGE_HEAD $newMERGE_HEAD
490                 set commit_type $newType
491         }
493         array unset file_states
495         if {![$ui_comm edit modified]
496                 || [string trim [$ui_comm get 0.0 end]] eq {}} {
497                 if {[load_message GITGUI_MSG]} {
498                 } elseif {[load_message MERGE_MSG]} {
499                 } elseif {[load_message SQUASH_MSG]} {
500                 }
501                 $ui_comm edit reset
502                 $ui_comm edit modified false
503         }
505         if {[is_enabled branch]} {
506                 load_all_heads
507                 populate_branch_menu
508         }
510         if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
511                 rescan_stage2 {} $after
512         } else {
513                 set rescan_active 1
514                 set ui_status_value {Refreshing file status...}
515                 set cmd [list git update-index]
516                 lappend cmd -q
517                 lappend cmd --unmerged
518                 lappend cmd --ignore-missing
519                 lappend cmd --refresh
520                 set fd_rf [open "| $cmd" r]
521                 fconfigure $fd_rf -blocking 0 -translation binary
522                 fileevent $fd_rf readable \
523                         [list rescan_stage2 $fd_rf $after]
524         }
527 proc rescan_stage2 {fd after} {
528         global ui_status_value
529         global rescan_active buf_rdi buf_rdf buf_rlo
531         if {$fd ne {}} {
532                 read $fd
533                 if {![eof $fd]} return
534                 close $fd
535         }
537         set ls_others [list | git ls-files --others -z \
538                 --exclude-per-directory=.gitignore]
539         set info_exclude [gitdir info exclude]
540         if {[file readable $info_exclude]} {
541                 lappend ls_others "--exclude-from=$info_exclude"
542         }
544         set buf_rdi {}
545         set buf_rdf {}
546         set buf_rlo {}
548         set rescan_active 3
549         set ui_status_value {Scanning for modified files ...}
550         set fd_di [open "| git diff-index --cached -z [PARENT]" r]
551         set fd_df [open "| git diff-files -z" r]
552         set fd_lo [open $ls_others r]
554         fconfigure $fd_di -blocking 0 -translation binary -encoding binary
555         fconfigure $fd_df -blocking 0 -translation binary -encoding binary
556         fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
557         fileevent $fd_di readable [list read_diff_index $fd_di $after]
558         fileevent $fd_df readable [list read_diff_files $fd_df $after]
559         fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
562 proc load_message {file} {
563         global ui_comm
565         set f [gitdir $file]
566         if {[file isfile $f]} {
567                 if {[catch {set fd [open $f r]}]} {
568                         return 0
569                 }
570                 set content [string trim [read $fd]]
571                 close $fd
572                 regsub -all -line {[ \r\t]+$} $content {} content
573                 $ui_comm delete 0.0 end
574                 $ui_comm insert end $content
575                 return 1
576         }
577         return 0
580 proc read_diff_index {fd after} {
581         global buf_rdi
583         append buf_rdi [read $fd]
584         set c 0
585         set n [string length $buf_rdi]
586         while {$c < $n} {
587                 set z1 [string first "\0" $buf_rdi $c]
588                 if {$z1 == -1} break
589                 incr z1
590                 set z2 [string first "\0" $buf_rdi $z1]
591                 if {$z2 == -1} break
593                 incr c
594                 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
595                 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
596                 merge_state \
597                         [encoding convertfrom $p] \
598                         [lindex $i 4]? \
599                         [list [lindex $i 0] [lindex $i 2]] \
600                         [list]
601                 set c $z2
602                 incr c
603         }
604         if {$c < $n} {
605                 set buf_rdi [string range $buf_rdi $c end]
606         } else {
607                 set buf_rdi {}
608         }
610         rescan_done $fd buf_rdi $after
613 proc read_diff_files {fd after} {
614         global buf_rdf
616         append buf_rdf [read $fd]
617         set c 0
618         set n [string length $buf_rdf]
619         while {$c < $n} {
620                 set z1 [string first "\0" $buf_rdf $c]
621                 if {$z1 == -1} break
622                 incr z1
623                 set z2 [string first "\0" $buf_rdf $z1]
624                 if {$z2 == -1} break
626                 incr c
627                 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
628                 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
629                 merge_state \
630                         [encoding convertfrom $p] \
631                         ?[lindex $i 4] \
632                         [list] \
633                         [list [lindex $i 0] [lindex $i 2]]
634                 set c $z2
635                 incr c
636         }
637         if {$c < $n} {
638                 set buf_rdf [string range $buf_rdf $c end]
639         } else {
640                 set buf_rdf {}
641         }
643         rescan_done $fd buf_rdf $after
646 proc read_ls_others {fd after} {
647         global buf_rlo
649         append buf_rlo [read $fd]
650         set pck [split $buf_rlo "\0"]
651         set buf_rlo [lindex $pck end]
652         foreach p [lrange $pck 0 end-1] {
653                 merge_state [encoding convertfrom $p] ?O
654         }
655         rescan_done $fd buf_rlo $after
658 proc rescan_done {fd buf after} {
659         global rescan_active current_diff_path
660         global file_states repo_config
661         upvar $buf to_clear
663         if {![eof $fd]} return
664         set to_clear {}
665         close $fd
666         if {[incr rescan_active -1] > 0} return
668         prune_selection
669         unlock_index
670         display_all_files
671         if {$current_diff_path ne {}} reshow_diff
672         uplevel #0 $after
675 proc prune_selection {} {
676         global file_states selected_paths
678         foreach path [array names selected_paths] {
679                 if {[catch {set still_here $file_states($path)}]} {
680                         unset selected_paths($path)
681                 }
682         }
685 ######################################################################
686 ##
687 ## ui helpers
689 proc mapicon {w state path} {
690         global all_icons
692         if {[catch {set r $all_icons($state$w)}]} {
693                 puts "error: no icon for $w state={$state} $path"
694                 return file_plain
695         }
696         return $r
699 proc mapdesc {state path} {
700         global all_descs
702         if {[catch {set r $all_descs($state)}]} {
703                 puts "error: no desc for state={$state} $path"
704                 return $state
705         }
706         return $r
709 proc escape_path {path} {
710         regsub -all {\\} $path "\\\\" path
711         regsub -all "\n" $path "\\n" path
712         return $path
715 proc short_path {path} {
716         return [escape_path [lindex [file split $path] end]]
719 set next_icon_id 0
720 set null_sha1 [string repeat 0 40]
722 proc merge_state {path new_state {head_info {}} {index_info {}}} {
723         global file_states next_icon_id null_sha1
725         set s0 [string index $new_state 0]
726         set s1 [string index $new_state 1]
728         if {[catch {set info $file_states($path)}]} {
729                 set state __
730                 set icon n[incr next_icon_id]
731         } else {
732                 set state [lindex $info 0]
733                 set icon [lindex $info 1]
734                 if {$head_info eq {}}  {set head_info  [lindex $info 2]}
735                 if {$index_info eq {}} {set index_info [lindex $info 3]}
736         }
738         if     {$s0 eq {?}} {set s0 [string index $state 0]} \
739         elseif {$s0 eq {_}} {set s0 _}
741         if     {$s1 eq {?}} {set s1 [string index $state 1]} \
742         elseif {$s1 eq {_}} {set s1 _}
744         if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
745                 set head_info [list 0 $null_sha1]
746         } elseif {$s0 ne {_} && [string index $state 0] eq {_}
747                 && $head_info eq {}} {
748                 set head_info $index_info
749         }
751         set file_states($path) [list $s0$s1 $icon \
752                 $head_info $index_info \
753                 ]
754         return $state
757 proc display_file_helper {w path icon_name old_m new_m} {
758         global file_lists
760         if {$new_m eq {_}} {
761                 set lno [lsearch -sorted -exact $file_lists($w) $path]
762                 if {$lno >= 0} {
763                         set file_lists($w) [lreplace $file_lists($w) $lno $lno]
764                         incr lno
765                         $w conf -state normal
766                         $w delete $lno.0 [expr {$lno + 1}].0
767                         $w conf -state disabled
768                 }
769         } elseif {$old_m eq {_} && $new_m ne {_}} {
770                 lappend file_lists($w) $path
771                 set file_lists($w) [lsort -unique $file_lists($w)]
772                 set lno [lsearch -sorted -exact $file_lists($w) $path]
773                 incr lno
774                 $w conf -state normal
775                 $w image create $lno.0 \
776                         -align center -padx 5 -pady 1 \
777                         -name $icon_name \
778                         -image [mapicon $w $new_m $path]
779                 $w insert $lno.1 "[escape_path $path]\n"
780                 $w conf -state disabled
781         } elseif {$old_m ne $new_m} {
782                 $w conf -state normal
783                 $w image conf $icon_name -image [mapicon $w $new_m $path]
784                 $w conf -state disabled
785         }
788 proc display_file {path state} {
789         global file_states selected_paths
790         global ui_index ui_workdir
792         set old_m [merge_state $path $state]
793         set s $file_states($path)
794         set new_m [lindex $s 0]
795         set icon_name [lindex $s 1]
797         set o [string index $old_m 0]
798         set n [string index $new_m 0]
799         if {$o eq {U}} {
800                 set o _
801         }
802         if {$n eq {U}} {
803                 set n _
804         }
805         display_file_helper     $ui_index $path $icon_name $o $n
807         if {[string index $old_m 0] eq {U}} {
808                 set o U
809         } else {
810                 set o [string index $old_m 1]
811         }
812         if {[string index $new_m 0] eq {U}} {
813                 set n U
814         } else {
815                 set n [string index $new_m 1]
816         }
817         display_file_helper     $ui_workdir $path $icon_name $o $n
819         if {$new_m eq {__}} {
820                 unset file_states($path)
821                 catch {unset selected_paths($path)}
822         }
825 proc display_all_files_helper {w path icon_name m} {
826         global file_lists
828         lappend file_lists($w) $path
829         set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
830         $w image create end \
831                 -align center -padx 5 -pady 1 \
832                 -name $icon_name \
833                 -image [mapicon $w $m $path]
834         $w insert end "[escape_path $path]\n"
837 proc display_all_files {} {
838         global ui_index ui_workdir
839         global file_states file_lists
840         global last_clicked
842         $ui_index conf -state normal
843         $ui_workdir conf -state normal
845         $ui_index delete 0.0 end
846         $ui_workdir delete 0.0 end
847         set last_clicked {}
849         set file_lists($ui_index) [list]
850         set file_lists($ui_workdir) [list]
852         foreach path [lsort [array names file_states]] {
853                 set s $file_states($path)
854                 set m [lindex $s 0]
855                 set icon_name [lindex $s 1]
857                 set s [string index $m 0]
858                 if {$s ne {U} && $s ne {_}} {
859                         display_all_files_helper $ui_index $path \
860                                 $icon_name $s
861                 }
863                 if {[string index $m 0] eq {U}} {
864                         set s U
865                 } else {
866                         set s [string index $m 1]
867                 }
868                 if {$s ne {_}} {
869                         display_all_files_helper $ui_workdir $path \
870                                 $icon_name $s
871                 }
872         }
874         $ui_index conf -state disabled
875         $ui_workdir conf -state disabled
878 ######################################################################
879 ##
880 ## icons
882 set filemask {
883 #define mask_width 14
884 #define mask_height 15
885 static unsigned char mask_bits[] = {
886    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
887    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
888    0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
891 image create bitmap file_plain -background white -foreground black -data {
892 #define plain_width 14
893 #define plain_height 15
894 static unsigned char plain_bits[] = {
895    0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
896    0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
897    0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
898 } -maskdata $filemask
900 image create bitmap file_mod -background white -foreground blue -data {
901 #define mod_width 14
902 #define mod_height 15
903 static unsigned char mod_bits[] = {
904    0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
905    0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
906    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
907 } -maskdata $filemask
909 image create bitmap file_fulltick -background white -foreground "#007000" -data {
910 #define file_fulltick_width 14
911 #define file_fulltick_height 15
912 static unsigned char file_fulltick_bits[] = {
913    0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
914    0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
915    0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
916 } -maskdata $filemask
918 image create bitmap file_parttick -background white -foreground "#005050" -data {
919 #define parttick_width 14
920 #define parttick_height 15
921 static unsigned char parttick_bits[] = {
922    0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
923    0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
924    0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
925 } -maskdata $filemask
927 image create bitmap file_question -background white -foreground black -data {
928 #define file_question_width 14
929 #define file_question_height 15
930 static unsigned char file_question_bits[] = {
931    0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
932    0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
933    0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
934 } -maskdata $filemask
936 image create bitmap file_removed -background white -foreground red -data {
937 #define file_removed_width 14
938 #define file_removed_height 15
939 static unsigned char file_removed_bits[] = {
940    0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
941    0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
942    0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
943 } -maskdata $filemask
945 image create bitmap file_merge -background white -foreground blue -data {
946 #define file_merge_width 14
947 #define file_merge_height 15
948 static unsigned char file_merge_bits[] = {
949    0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
950    0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
951    0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
952 } -maskdata $filemask
954 set file_dir_data {
955 #define file_width 18
956 #define file_height 18
957 static unsigned char file_bits[] = {
958   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
959   0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
960   0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
961   0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
962   0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
964 image create bitmap file_dir -background white -foreground blue \
965         -data $file_dir_data -maskdata $file_dir_data
966 unset file_dir_data
968 set file_uplevel_data {
969 #define up_width 15
970 #define up_height 15
971 static unsigned char up_bits[] = {
972   0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
973   0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
974   0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
976 image create bitmap file_uplevel -background white -foreground red \
977         -data $file_uplevel_data -maskdata $file_uplevel_data
978 unset file_uplevel_data
980 set ui_index .vpane.files.index.list
981 set ui_workdir .vpane.files.workdir.list
983 set all_icons(_$ui_index)   file_plain
984 set all_icons(A$ui_index)   file_fulltick
985 set all_icons(M$ui_index)   file_fulltick
986 set all_icons(D$ui_index)   file_removed
987 set all_icons(U$ui_index)   file_merge
989 set all_icons(_$ui_workdir) file_plain
990 set all_icons(M$ui_workdir) file_mod
991 set all_icons(D$ui_workdir) file_question
992 set all_icons(U$ui_workdir) file_merge
993 set all_icons(O$ui_workdir) file_plain
995 set max_status_desc 0
996 foreach i {
997                 {__ "Unmodified"}
999                 {_M "Modified, not staged"}
1000                 {M_ "Staged for commit"}
1001                 {MM "Portions staged for commit"}
1002                 {MD "Staged for commit, missing"}
1004                 {_O "Untracked, not staged"}
1005                 {A_ "Staged for commit"}
1006                 {AM "Portions staged for commit"}
1007                 {AD "Staged for commit, missing"}
1009                 {_D "Missing"}
1010                 {D_ "Staged for removal"}
1011                 {DO "Staged for removal, still present"}
1013                 {U_ "Requires merge resolution"}
1014                 {UU "Requires merge resolution"}
1015                 {UM "Requires merge resolution"}
1016                 {UD "Requires merge resolution"}
1017         } {
1018         if {$max_status_desc < [string length [lindex $i 1]]} {
1019                 set max_status_desc [string length [lindex $i 1]]
1020         }
1021         set all_descs([lindex $i 0]) [lindex $i 1]
1023 unset i
1025 ######################################################################
1026 ##
1027 ## util
1029 proc bind_button3 {w cmd} {
1030         bind $w <Any-Button-3> $cmd
1031         if {[is_MacOSX]} {
1032                 bind $w <Control-Button-1> $cmd
1033         }
1036 proc scrollbar2many {list mode args} {
1037         foreach w $list {eval $w $mode $args}
1040 proc many2scrollbar {list mode sb top bottom} {
1041         $sb set $top $bottom
1042         foreach w $list {$w $mode moveto $top}
1045 proc incr_font_size {font {amt 1}} {
1046         set sz [font configure $font -size]
1047         incr sz $amt
1048         font configure $font -size $sz
1049         font configure ${font}bold -size $sz
1052 ######################################################################
1053 ##
1054 ## ui commands
1056 set starting_gitk_msg {Starting gitk... please wait...}
1058 proc do_gitk {revs} {
1059         global env ui_status_value starting_gitk_msg
1061         # -- Always start gitk through whatever we were loaded with.  This
1062         #    lets us bypass using shell process on Windows systems.
1063         #
1064         set cmd [list [info nameofexecutable]]
1065         lappend cmd [gitexec gitk]
1066         if {$revs ne {}} {
1067                 append cmd { }
1068                 append cmd $revs
1069         }
1071         if {[catch {eval exec $cmd &} err]} {
1072                 error_popup "Failed to start gitk:\n\n$err"
1073         } else {
1074                 set ui_status_value $starting_gitk_msg
1075                 after 10000 {
1076                         if {$ui_status_value eq $starting_gitk_msg} {
1077                                 set ui_status_value {Ready.}
1078                         }
1079                 }
1080         }
1083 set is_quitting 0
1085 proc do_quit {} {
1086         global ui_comm is_quitting repo_config commit_type
1088         if {$is_quitting} return
1089         set is_quitting 1
1091         if {[winfo exists $ui_comm]} {
1092                 # -- Stash our current commit buffer.
1093                 #
1094                 set save [gitdir GITGUI_MSG]
1095                 set msg [string trim [$ui_comm get 0.0 end]]
1096                 regsub -all -line {[ \r\t]+$} $msg {} msg
1097                 if {(![string match amend* $commit_type]
1098                         || [$ui_comm edit modified])
1099                         && $msg ne {}} {
1100                         catch {
1101                                 set fd [open $save w]
1102                                 puts -nonewline $fd $msg
1103                                 close $fd
1104                         }
1105                 } else {
1106                         catch {file delete $save}
1107                 }
1109                 # -- Stash our current window geometry into this repository.
1110                 #
1111                 set cfg_geometry [list]
1112                 lappend cfg_geometry [wm geometry .]
1113                 lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1114                 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1115                 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1116                         set rc_geometry {}
1117                 }
1118                 if {$cfg_geometry ne $rc_geometry} {
1119                         catch {git config gui.geometry $cfg_geometry}
1120                 }
1121         }
1123         destroy .
1126 proc do_rescan {} {
1127         rescan {set ui_status_value {Ready.}}
1130 proc do_commit {} {
1131         commit_tree
1134 proc toggle_or_diff {w x y} {
1135         global file_states file_lists current_diff_path ui_index ui_workdir
1136         global last_clicked selected_paths
1138         set pos [split [$w index @$x,$y] .]
1139         set lno [lindex $pos 0]
1140         set col [lindex $pos 1]
1141         set path [lindex $file_lists($w) [expr {$lno - 1}]]
1142         if {$path eq {}} {
1143                 set last_clicked {}
1144                 return
1145         }
1147         set last_clicked [list $w $lno]
1148         array unset selected_paths
1149         $ui_index tag remove in_sel 0.0 end
1150         $ui_workdir tag remove in_sel 0.0 end
1152         if {$col == 0} {
1153                 if {$current_diff_path eq $path} {
1154                         set after {reshow_diff;}
1155                 } else {
1156                         set after {}
1157                 }
1158                 if {$w eq $ui_index} {
1159                         update_indexinfo \
1160                                 "Unstaging [short_path $path] from commit" \
1161                                 [list $path] \
1162                                 [concat $after {set ui_status_value {Ready.}}]
1163                 } elseif {$w eq $ui_workdir} {
1164                         update_index \
1165                                 "Adding [short_path $path]" \
1166                                 [list $path] \
1167                                 [concat $after {set ui_status_value {Ready.}}]
1168                 }
1169         } else {
1170                 show_diff $path $w $lno
1171         }
1174 proc add_one_to_selection {w x y} {
1175         global file_lists last_clicked selected_paths
1177         set lno [lindex [split [$w index @$x,$y] .] 0]
1178         set path [lindex $file_lists($w) [expr {$lno - 1}]]
1179         if {$path eq {}} {
1180                 set last_clicked {}
1181                 return
1182         }
1184         if {$last_clicked ne {}
1185                 && [lindex $last_clicked 0] ne $w} {
1186                 array unset selected_paths
1187                 [lindex $last_clicked 0] tag remove in_sel 0.0 end
1188         }
1190         set last_clicked [list $w $lno]
1191         if {[catch {set in_sel $selected_paths($path)}]} {
1192                 set in_sel 0
1193         }
1194         if {$in_sel} {
1195                 unset selected_paths($path)
1196                 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
1197         } else {
1198                 set selected_paths($path) 1
1199                 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1200         }
1203 proc add_range_to_selection {w x y} {
1204         global file_lists last_clicked selected_paths
1206         if {[lindex $last_clicked 0] ne $w} {
1207                 toggle_or_diff $w $x $y
1208                 return
1209         }
1211         set lno [lindex [split [$w index @$x,$y] .] 0]
1212         set lc [lindex $last_clicked 1]
1213         if {$lc < $lno} {
1214                 set begin $lc
1215                 set end $lno
1216         } else {
1217                 set begin $lno
1218                 set end $lc
1219         }
1221         foreach path [lrange $file_lists($w) \
1222                 [expr {$begin - 1}] \
1223                 [expr {$end - 1}]] {
1224                 set selected_paths($path) 1
1225         }
1226         $w tag add in_sel $begin.0 [expr {$end + 1}].0
1229 ######################################################################
1230 ##
1231 ## config defaults
1233 set cursor_ptr arrow
1234 font create font_diff -family Courier -size 10
1235 font create font_ui
1236 catch {
1237         label .dummy
1238         eval font configure font_ui [font actual [.dummy cget -font]]
1239         destroy .dummy
1242 font create font_uibold
1243 font create font_diffbold
1245 foreach class {Button Checkbutton Entry Label
1246                 Labelframe Listbox Menu Message
1247                 Radiobutton Spinbox Text} {
1248         option add *$class.font font_ui
1250 unset class
1252 if {[is_Windows] || [is_MacOSX]} {
1253         option add *Menu.tearOff 0
1256 if {[is_MacOSX]} {
1257         set M1B M1
1258         set M1T Cmd
1259 } else {
1260         set M1B Control
1261         set M1T Ctrl
1264 proc apply_config {} {
1265         global repo_config font_descs
1267         foreach option $font_descs {
1268                 set name [lindex $option 0]
1269                 set font [lindex $option 1]
1270                 if {[catch {
1271                         foreach {cn cv} $repo_config(gui.$name) {
1272                                 font configure $font $cn $cv
1273                         }
1274                         } err]} {
1275                         error_popup "Invalid font specified in gui.$name:\n\n$err"
1276                 }
1277                 foreach {cn cv} [font configure $font] {
1278                         font configure ${font}bold $cn $cv
1279                 }
1280                 font configure ${font}bold -weight bold
1281         }
1284 set default_config(merge.diffstat) true
1285 set default_config(merge.summary) false
1286 set default_config(merge.verbosity) 2
1287 set default_config(user.name) {}
1288 set default_config(user.email) {}
1290 set default_config(gui.pruneduringfetch) false
1291 set default_config(gui.trustmtime) false
1292 set default_config(gui.diffcontext) 5
1293 set default_config(gui.newbranchtemplate) {}
1294 set default_config(gui.fontui) [font configure font_ui]
1295 set default_config(gui.fontdiff) [font configure font_diff]
1296 set font_descs {
1297         {fontui   font_ui   {Main Font}}
1298         {fontdiff font_diff {Diff/Console Font}}
1300 load_config 0
1301 apply_config
1303 ######################################################################
1304 ##
1305 ## feature option selection
1307 if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
1308         unset _junk
1309 } else {
1310         set subcommand gui
1312 if {$subcommand eq {gui.sh}} {
1313         set subcommand gui
1315 if {$subcommand eq {gui} && [llength $argv] > 0} {
1316         set subcommand [lindex $argv 0]
1317         set argv [lrange $argv 1 end]
1320 enable_option multicommit
1321 enable_option branch
1322 enable_option transport
1324 switch -- $subcommand {
1325 browser -
1326 blame {
1327         disable_option multicommit
1328         disable_option branch
1329         disable_option transport
1331 citool {
1332         enable_option singlecommit
1334         disable_option multicommit
1335         disable_option branch
1336         disable_option transport
1340 ######################################################################
1341 ##
1342 ## ui construction
1344 set ui_comm {}
1346 # -- Menu Bar
1348 menu .mbar -tearoff 0
1349 .mbar add cascade -label Repository -menu .mbar.repository
1350 .mbar add cascade -label Edit -menu .mbar.edit
1351 if {[is_enabled branch]} {
1352         .mbar add cascade -label Branch -menu .mbar.branch
1354 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1355         .mbar add cascade -label Commit -menu .mbar.commit
1357 if {[is_enabled transport]} {
1358         .mbar add cascade -label Merge -menu .mbar.merge
1359         .mbar add cascade -label Fetch -menu .mbar.fetch
1360         .mbar add cascade -label Push -menu .mbar.push
1362 . configure -menu .mbar
1364 # -- Repository Menu
1366 menu .mbar.repository
1368 .mbar.repository add command \
1369         -label {Browse Current Branch} \
1370         -command {browser::new $current_branch}
1371 trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1372 .mbar.repository add separator
1374 .mbar.repository add command \
1375         -label {Visualize Current Branch} \
1376         -command {do_gitk $current_branch}
1377 trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1378 .mbar.repository add command \
1379         -label {Visualize All Branches} \
1380         -command {do_gitk --all}
1381 .mbar.repository add separator
1383 if {[is_enabled multicommit]} {
1384         .mbar.repository add command -label {Database Statistics} \
1385                 -command do_stats
1387         .mbar.repository add command -label {Compress Database} \
1388                 -command do_gc
1390         .mbar.repository add command -label {Verify Database} \
1391                 -command do_fsck_objects
1393         .mbar.repository add separator
1395         if {[is_Cygwin]} {
1396                 .mbar.repository add command \
1397                         -label {Create Desktop Icon} \
1398                         -command do_cygwin_shortcut
1399         } elseif {[is_Windows]} {
1400                 .mbar.repository add command \
1401                         -label {Create Desktop Icon} \
1402                         -command do_windows_shortcut
1403         } elseif {[is_MacOSX]} {
1404                 .mbar.repository add command \
1405                         -label {Create Desktop Icon} \
1406                         -command do_macosx_app
1407         }
1410 .mbar.repository add command -label Quit \
1411         -command do_quit \
1412         -accelerator $M1T-Q
1414 # -- Edit Menu
1416 menu .mbar.edit
1417 .mbar.edit add command -label Undo \
1418         -command {catch {[focus] edit undo}} \
1419         -accelerator $M1T-Z
1420 .mbar.edit add command -label Redo \
1421         -command {catch {[focus] edit redo}} \
1422         -accelerator $M1T-Y
1423 .mbar.edit add separator
1424 .mbar.edit add command -label Cut \
1425         -command {catch {tk_textCut [focus]}} \
1426         -accelerator $M1T-X
1427 .mbar.edit add command -label Copy \
1428         -command {catch {tk_textCopy [focus]}} \
1429         -accelerator $M1T-C
1430 .mbar.edit add command -label Paste \
1431         -command {catch {tk_textPaste [focus]; [focus] see insert}} \
1432         -accelerator $M1T-V
1433 .mbar.edit add command -label Delete \
1434         -command {catch {[focus] delete sel.first sel.last}} \
1435         -accelerator Del
1436 .mbar.edit add separator
1437 .mbar.edit add command -label {Select All} \
1438         -command {catch {[focus] tag add sel 0.0 end}} \
1439         -accelerator $M1T-A
1441 # -- Branch Menu
1443 if {[is_enabled branch]} {
1444         menu .mbar.branch
1446         .mbar.branch add command -label {Create...} \
1447                 -command do_create_branch \
1448                 -accelerator $M1T-N
1449         lappend disable_on_lock [list .mbar.branch entryconf \
1450                 [.mbar.branch index last] -state]
1452         .mbar.branch add command -label {Rename...} \
1453                 -command branch_rename::dialog
1454         lappend disable_on_lock [list .mbar.branch entryconf \
1455                 [.mbar.branch index last] -state]
1457         .mbar.branch add command -label {Delete...} \
1458                 -command do_delete_branch
1459         lappend disable_on_lock [list .mbar.branch entryconf \
1460                 [.mbar.branch index last] -state]
1462         .mbar.branch add command -label {Reset...} \
1463                 -command merge::reset_hard
1464         lappend disable_on_lock [list .mbar.branch entryconf \
1465                 [.mbar.branch index last] -state]
1468 # -- Commit Menu
1470 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1471         menu .mbar.commit
1473         .mbar.commit add radiobutton \
1474                 -label {New Commit} \
1475                 -command do_select_commit_type \
1476                 -variable selected_commit_type \
1477                 -value new
1478         lappend disable_on_lock \
1479                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1481         .mbar.commit add radiobutton \
1482                 -label {Amend Last Commit} \
1483                 -command do_select_commit_type \
1484                 -variable selected_commit_type \
1485                 -value amend
1486         lappend disable_on_lock \
1487                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1489         .mbar.commit add separator
1491         .mbar.commit add command -label Rescan \
1492                 -command do_rescan \
1493                 -accelerator F5
1494         lappend disable_on_lock \
1495                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1497         .mbar.commit add command -label {Add To Commit} \
1498                 -command do_add_selection
1499         lappend disable_on_lock \
1500                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1502         .mbar.commit add command -label {Add Existing To Commit} \
1503                 -command do_add_all \
1504                 -accelerator $M1T-I
1505         lappend disable_on_lock \
1506                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1508         .mbar.commit add command -label {Unstage From Commit} \
1509                 -command do_unstage_selection
1510         lappend disable_on_lock \
1511                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1513         .mbar.commit add command -label {Revert Changes} \
1514                 -command do_revert_selection
1515         lappend disable_on_lock \
1516                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1518         .mbar.commit add separator
1520         .mbar.commit add command -label {Sign Off} \
1521                 -command do_signoff \
1522                 -accelerator $M1T-S
1524         .mbar.commit add command -label Commit \
1525                 -command do_commit \
1526                 -accelerator $M1T-Return
1527         lappend disable_on_lock \
1528                 [list .mbar.commit entryconf [.mbar.commit index last] -state]
1531 # -- Merge Menu
1533 if {[is_enabled branch]} {
1534         menu .mbar.merge
1535         .mbar.merge add command -label {Local Merge...} \
1536                 -command merge::dialog
1537         lappend disable_on_lock \
1538                 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1539         .mbar.merge add command -label {Abort Merge...} \
1540                 -command merge::reset_hard
1541         lappend disable_on_lock \
1542                 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1546 # -- Transport Menu
1548 if {[is_enabled transport]} {
1549         menu .mbar.fetch
1551         menu .mbar.push
1552         .mbar.push add command -label {Push...} \
1553                 -command do_push_anywhere
1554         .mbar.push add command -label {Delete...} \
1555                 -command remote_branch_delete::dialog
1558 if {[is_MacOSX]} {
1559         # -- Apple Menu (Mac OS X only)
1560         #
1561         .mbar add cascade -label Apple -menu .mbar.apple
1562         menu .mbar.apple
1564         .mbar.apple add command -label "About [appname]" \
1565                 -command do_about
1566         .mbar.apple add command -label "Options..." \
1567                 -command do_options
1568 } else {
1569         # -- Edit Menu
1570         #
1571         .mbar.edit add separator
1572         .mbar.edit add command -label {Options...} \
1573                 -command do_options
1575         # -- Tools Menu
1576         #
1577         if {[file exists /usr/local/miga/lib/gui-miga]
1578                 && [file exists .pvcsrc]} {
1579         proc do_miga {} {
1580                 global ui_status_value
1581                 if {![lock_index update]} return
1582                 set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1583                 set miga_fd [open "|$cmd" r]
1584                 fconfigure $miga_fd -blocking 0
1585                 fileevent $miga_fd readable [list miga_done $miga_fd]
1586                 set ui_status_value {Running miga...}
1587         }
1588         proc miga_done {fd} {
1589                 read $fd 512
1590                 if {[eof $fd]} {
1591                         close $fd
1592                         unlock_index
1593                         rescan [list set ui_status_value {Ready.}]
1594                 }
1595         }
1596         .mbar add cascade -label Tools -menu .mbar.tools
1597         menu .mbar.tools
1598         .mbar.tools add command -label "Migrate" \
1599                 -command do_miga
1600         lappend disable_on_lock \
1601                 [list .mbar.tools entryconf [.mbar.tools index last] -state]
1602         }
1605 # -- Help Menu
1607 .mbar add cascade -label Help -menu .mbar.help
1608 menu .mbar.help
1610 if {![is_MacOSX]} {
1611         .mbar.help add command -label "About [appname]" \
1612                 -command do_about
1615 set browser {}
1616 catch {set browser $repo_config(instaweb.browser)}
1617 set doc_path [file dirname [gitexec]]
1618 set doc_path [file join $doc_path Documentation index.html]
1620 if {[is_Cygwin]} {
1621         set doc_path [exec cygpath --mixed $doc_path]
1624 if {$browser eq {}} {
1625         if {[is_MacOSX]} {
1626                 set browser open
1627         } elseif {[is_Cygwin]} {
1628                 set program_files [file dirname [exec cygpath --windir]]
1629                 set program_files [file join $program_files {Program Files}]
1630                 set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
1631                 set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
1632                 if {[file exists $firefox]} {
1633                         set browser $firefox
1634                 } elseif {[file exists $ie]} {
1635                         set browser $ie
1636                 }
1637                 unset program_files firefox ie
1638         }
1641 if {[file isfile $doc_path]} {
1642         set doc_url "file:$doc_path"
1643 } else {
1644         set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
1647 if {$browser ne {}} {
1648         .mbar.help add command -label {Online Documentation} \
1649                 -command [list exec $browser $doc_url &]
1651 unset browser doc_path doc_url
1653 # -- Standard bindings
1655 bind .   <Destroy> {if {{%W} eq {.}} do_quit}
1656 bind all <$M1B-Key-q> do_quit
1657 bind all <$M1B-Key-Q> do_quit
1658 bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
1659 bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
1661 set subcommand_args {}
1662 proc usage {} {
1663         puts stderr "usage: $::argv0 $::subcommand $::subcommand_args"
1664         exit 1
1667 # -- Not a normal commit type invocation?  Do that instead!
1669 switch -- $subcommand {
1670 browser {
1671         set subcommand_args {rev?}
1672         switch [llength $argv] {
1673         0 { set current_branch [current-branch] }
1674         1 { set current_branch [lindex $argv 0] }
1675         default usage
1676         }
1677         browser::new $current_branch
1678         return
1680 blame {
1681         set subcommand_args {rev? path?}
1682         set head {}
1683         set path {}
1684         set is_path 0
1685         foreach a $argv {
1686                 if {$is_path || [file exists $_prefix$a]} {
1687                         if {$path ne {}} usage
1688                         set path $_prefix$a
1689                         break
1690                 } elseif {$a eq {--}} {
1691                         if {$path ne {}} {
1692                                 if {$head ne {}} usage
1693                                 set head $path
1694                                 set path {}
1695                         }
1696                         set is_path 1
1697                 } elseif {$head eq {}} {
1698                         if {$head ne {}} usage
1699                         set head $a
1700                 } else {
1701                         usage
1702                 }
1703         }
1704         unset is_path
1706         if {$head eq {}} {
1707                 set current_branch [current-branch]
1708         } else {
1709                 set current_branch $head
1710         }
1712         if {$path eq {}} usage
1713         blame::new $head $path
1714         return
1716 citool -
1717 gui {
1718         if {[llength $argv] != 0} {
1719                 puts -nonewline stderr "usage: $argv0"
1720                 if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
1721                         puts -nonewline stderr " $subcommand"
1722                 }
1723                 puts stderr {}
1724                 exit 1
1725         }
1726         # fall through to setup UI for commits
1728 default {
1729         puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
1730         exit 1
1734 # -- Branch Control
1736 frame .branch \
1737         -borderwidth 1 \
1738         -relief sunken
1739 label .branch.l1 \
1740         -text {Current Branch:} \
1741         -anchor w \
1742         -justify left
1743 label .branch.cb \
1744         -textvariable current_branch \
1745         -anchor w \
1746         -justify left
1747 pack .branch.l1 -side left
1748 pack .branch.cb -side left -fill x
1749 pack .branch -side top -fill x
1751 # -- Main Window Layout
1753 panedwindow .vpane -orient vertical
1754 panedwindow .vpane.files -orient horizontal
1755 .vpane add .vpane.files -sticky nsew -height 100 -width 200
1756 pack .vpane -anchor n -side top -fill both -expand 1
1758 # -- Index File List
1760 frame .vpane.files.index -height 100 -width 200
1761 label .vpane.files.index.title -text {Staged Changes (Will Be Committed)} \
1762         -background lightgreen
1763 text $ui_index -background white -borderwidth 0 \
1764         -width 20 -height 10 \
1765         -wrap none \
1766         -cursor $cursor_ptr \
1767         -xscrollcommand {.vpane.files.index.sx set} \
1768         -yscrollcommand {.vpane.files.index.sy set} \
1769         -state disabled
1770 scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
1771 scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
1772 pack .vpane.files.index.title -side top -fill x
1773 pack .vpane.files.index.sx -side bottom -fill x
1774 pack .vpane.files.index.sy -side right -fill y
1775 pack $ui_index -side left -fill both -expand 1
1776 .vpane.files add .vpane.files.index -sticky nsew
1778 # -- Working Directory File List
1780 frame .vpane.files.workdir -height 100 -width 200
1781 label .vpane.files.workdir.title -text {Unstaged Changes (Will Not Be Committed)} \
1782         -background lightsalmon
1783 text $ui_workdir -background white -borderwidth 0 \
1784         -width 20 -height 10 \
1785         -wrap none \
1786         -cursor $cursor_ptr \
1787         -xscrollcommand {.vpane.files.workdir.sx set} \
1788         -yscrollcommand {.vpane.files.workdir.sy set} \
1789         -state disabled
1790 scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
1791 scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
1792 pack .vpane.files.workdir.title -side top -fill x
1793 pack .vpane.files.workdir.sx -side bottom -fill x
1794 pack .vpane.files.workdir.sy -side right -fill y
1795 pack $ui_workdir -side left -fill both -expand 1
1796 .vpane.files add .vpane.files.workdir -sticky nsew
1798 foreach i [list $ui_index $ui_workdir] {
1799         $i tag conf in_diff -background lightgray
1800         $i tag conf in_sel  -background lightgray
1802 unset i
1804 # -- Diff and Commit Area
1806 frame .vpane.lower -height 300 -width 400
1807 frame .vpane.lower.commarea
1808 frame .vpane.lower.diff -relief sunken -borderwidth 1
1809 pack .vpane.lower.commarea -side top -fill x
1810 pack .vpane.lower.diff -side bottom -fill both -expand 1
1811 .vpane add .vpane.lower -sticky nsew
1813 # -- Commit Area Buttons
1815 frame .vpane.lower.commarea.buttons
1816 label .vpane.lower.commarea.buttons.l -text {} \
1817         -anchor w \
1818         -justify left
1819 pack .vpane.lower.commarea.buttons.l -side top -fill x
1820 pack .vpane.lower.commarea.buttons -side left -fill y
1822 button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
1823         -command do_rescan
1824 pack .vpane.lower.commarea.buttons.rescan -side top -fill x
1825 lappend disable_on_lock \
1826         {.vpane.lower.commarea.buttons.rescan conf -state}
1828 button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
1829         -command do_add_all
1830 pack .vpane.lower.commarea.buttons.incall -side top -fill x
1831 lappend disable_on_lock \
1832         {.vpane.lower.commarea.buttons.incall conf -state}
1834 button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
1835         -command do_signoff
1836 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
1838 button .vpane.lower.commarea.buttons.commit -text {Commit} \
1839         -command do_commit
1840 pack .vpane.lower.commarea.buttons.commit -side top -fill x
1841 lappend disable_on_lock \
1842         {.vpane.lower.commarea.buttons.commit conf -state}
1844 # -- Commit Message Buffer
1846 frame .vpane.lower.commarea.buffer
1847 frame .vpane.lower.commarea.buffer.header
1848 set ui_comm .vpane.lower.commarea.buffer.t
1849 set ui_coml .vpane.lower.commarea.buffer.header.l
1850 radiobutton .vpane.lower.commarea.buffer.header.new \
1851         -text {New Commit} \
1852         -command do_select_commit_type \
1853         -variable selected_commit_type \
1854         -value new
1855 lappend disable_on_lock \
1856         [list .vpane.lower.commarea.buffer.header.new conf -state]
1857 radiobutton .vpane.lower.commarea.buffer.header.amend \
1858         -text {Amend Last Commit} \
1859         -command do_select_commit_type \
1860         -variable selected_commit_type \
1861         -value amend
1862 lappend disable_on_lock \
1863         [list .vpane.lower.commarea.buffer.header.amend conf -state]
1864 label $ui_coml \
1865         -anchor w \
1866         -justify left
1867 proc trace_commit_type {varname args} {
1868         global ui_coml commit_type
1869         switch -glob -- $commit_type {
1870         initial       {set txt {Initial Commit Message:}}
1871         amend         {set txt {Amended Commit Message:}}
1872         amend-initial {set txt {Amended Initial Commit Message:}}
1873         amend-merge   {set txt {Amended Merge Commit Message:}}
1874         merge         {set txt {Merge Commit Message:}}
1875         *             {set txt {Commit Message:}}
1876         }
1877         $ui_coml conf -text $txt
1879 trace add variable commit_type write trace_commit_type
1880 pack $ui_coml -side left -fill x
1881 pack .vpane.lower.commarea.buffer.header.amend -side right
1882 pack .vpane.lower.commarea.buffer.header.new -side right
1884 text $ui_comm -background white -borderwidth 1 \
1885         -undo true \
1886         -maxundo 20 \
1887         -autoseparators true \
1888         -relief sunken \
1889         -width 75 -height 9 -wrap none \
1890         -font font_diff \
1891         -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
1892 scrollbar .vpane.lower.commarea.buffer.sby \
1893         -command [list $ui_comm yview]
1894 pack .vpane.lower.commarea.buffer.header -side top -fill x
1895 pack .vpane.lower.commarea.buffer.sby -side right -fill y
1896 pack $ui_comm -side left -fill y
1897 pack .vpane.lower.commarea.buffer -side left -fill y
1899 # -- Commit Message Buffer Context Menu
1901 set ctxm .vpane.lower.commarea.buffer.ctxm
1902 menu $ctxm -tearoff 0
1903 $ctxm add command \
1904         -label {Cut} \
1905         -command {tk_textCut $ui_comm}
1906 $ctxm add command \
1907         -label {Copy} \
1908         -command {tk_textCopy $ui_comm}
1909 $ctxm add command \
1910         -label {Paste} \
1911         -command {tk_textPaste $ui_comm}
1912 $ctxm add command \
1913         -label {Delete} \
1914         -command {$ui_comm delete sel.first sel.last}
1915 $ctxm add separator
1916 $ctxm add command \
1917         -label {Select All} \
1918         -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
1919 $ctxm add command \
1920         -label {Copy All} \
1921         -command {
1922                 $ui_comm tag add sel 0.0 end
1923                 tk_textCopy $ui_comm
1924                 $ui_comm tag remove sel 0.0 end
1925         }
1926 $ctxm add separator
1927 $ctxm add command \
1928         -label {Sign Off} \
1929         -command do_signoff
1930 bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
1932 # -- Diff Header
1934 proc trace_current_diff_path {varname args} {
1935         global current_diff_path diff_actions file_states
1936         if {$current_diff_path eq {}} {
1937                 set s {}
1938                 set f {}
1939                 set p {}
1940                 set o disabled
1941         } else {
1942                 set p $current_diff_path
1943                 set s [mapdesc [lindex $file_states($p) 0] $p]
1944                 set f {File:}
1945                 set p [escape_path $p]
1946                 set o normal
1947         }
1949         .vpane.lower.diff.header.status configure -text $s
1950         .vpane.lower.diff.header.file configure -text $f
1951         .vpane.lower.diff.header.path configure -text $p
1952         foreach w $diff_actions {
1953                 uplevel #0 $w $o
1954         }
1956 trace add variable current_diff_path write trace_current_diff_path
1958 frame .vpane.lower.diff.header -background gold
1959 label .vpane.lower.diff.header.status \
1960         -background gold \
1961         -width $max_status_desc \
1962         -anchor w \
1963         -justify left
1964 label .vpane.lower.diff.header.file \
1965         -background gold \
1966         -anchor w \
1967         -justify left
1968 label .vpane.lower.diff.header.path \
1969         -background gold \
1970         -anchor w \
1971         -justify left
1972 pack .vpane.lower.diff.header.status -side left
1973 pack .vpane.lower.diff.header.file -side left
1974 pack .vpane.lower.diff.header.path -fill x
1975 set ctxm .vpane.lower.diff.header.ctxm
1976 menu $ctxm -tearoff 0
1977 $ctxm add command \
1978         -label {Copy} \
1979         -command {
1980                 clipboard clear
1981                 clipboard append \
1982                         -format STRING \
1983                         -type STRING \
1984                         -- $current_diff_path
1985         }
1986 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1987 bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
1989 # -- Diff Body
1991 frame .vpane.lower.diff.body
1992 set ui_diff .vpane.lower.diff.body.t
1993 text $ui_diff -background white -borderwidth 0 \
1994         -width 80 -height 15 -wrap none \
1995         -font font_diff \
1996         -xscrollcommand {.vpane.lower.diff.body.sbx set} \
1997         -yscrollcommand {.vpane.lower.diff.body.sby set} \
1998         -state disabled
1999 scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
2000         -command [list $ui_diff xview]
2001 scrollbar .vpane.lower.diff.body.sby -orient vertical \
2002         -command [list $ui_diff yview]
2003 pack .vpane.lower.diff.body.sbx -side bottom -fill x
2004 pack .vpane.lower.diff.body.sby -side right -fill y
2005 pack $ui_diff -side left -fill both -expand 1
2006 pack .vpane.lower.diff.header -side top -fill x
2007 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
2009 $ui_diff tag conf d_cr -elide true
2010 $ui_diff tag conf d_@ -foreground blue -font font_diffbold
2011 $ui_diff tag conf d_+ -foreground {#00a000}
2012 $ui_diff tag conf d_- -foreground red
2014 $ui_diff tag conf d_++ -foreground {#00a000}
2015 $ui_diff tag conf d_-- -foreground red
2016 $ui_diff tag conf d_+s \
2017         -foreground {#00a000} \
2018         -background {#e2effa}
2019 $ui_diff tag conf d_-s \
2020         -foreground red \
2021         -background {#e2effa}
2022 $ui_diff tag conf d_s+ \
2023         -foreground {#00a000} \
2024         -background ivory1
2025 $ui_diff tag conf d_s- \
2026         -foreground red \
2027         -background ivory1
2029 $ui_diff tag conf d<<<<<<< \
2030         -foreground orange \
2031         -font font_diffbold
2032 $ui_diff tag conf d======= \
2033         -foreground orange \
2034         -font font_diffbold
2035 $ui_diff tag conf d>>>>>>> \
2036         -foreground orange \
2037         -font font_diffbold
2039 $ui_diff tag raise sel
2041 # -- Diff Body Context Menu
2043 set ctxm .vpane.lower.diff.body.ctxm
2044 menu $ctxm -tearoff 0
2045 $ctxm add command \
2046         -label {Refresh} \
2047         -command reshow_diff
2048 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2049 $ctxm add command \
2050         -label {Copy} \
2051         -command {tk_textCopy $ui_diff}
2052 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2053 $ctxm add command \
2054         -label {Select All} \
2055         -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
2056 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2057 $ctxm add command \
2058         -label {Copy All} \
2059         -command {
2060                 $ui_diff tag add sel 0.0 end
2061                 tk_textCopy $ui_diff
2062                 $ui_diff tag remove sel 0.0 end
2063         }
2064 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2065 $ctxm add separator
2066 $ctxm add command \
2067         -label {Apply/Reverse Hunk} \
2068         -command {apply_hunk $cursorX $cursorY}
2069 set ui_diff_applyhunk [$ctxm index last]
2070 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
2071 $ctxm add separator
2072 $ctxm add command \
2073         -label {Decrease Font Size} \
2074         -command {incr_font_size font_diff -1}
2075 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2076 $ctxm add command \
2077         -label {Increase Font Size} \
2078         -command {incr_font_size font_diff 1}
2079 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2080 $ctxm add separator
2081 $ctxm add command \
2082         -label {Show Less Context} \
2083         -command {if {$repo_config(gui.diffcontext) >= 1} {
2084                 incr repo_config(gui.diffcontext) -1
2085                 reshow_diff
2086         }}
2087 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2088 $ctxm add command \
2089         -label {Show More Context} \
2090         -command {if {$repo_config(gui.diffcontext) < 99} {
2091                 incr repo_config(gui.diffcontext)
2092                 reshow_diff
2093         }}
2094 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2095 $ctxm add separator
2096 $ctxm add command -label {Options...} \
2097         -command do_options
2098 bind_button3 $ui_diff "
2099         set cursorX %x
2100         set cursorY %y
2101         if {\$ui_index eq \$current_diff_side} {
2102                 $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2103         } else {
2104                 $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2105         }
2106         tk_popup $ctxm %X %Y
2108 unset ui_diff_applyhunk
2110 # -- Status Bar
2112 label .status -textvariable ui_status_value \
2113         -anchor w \
2114         -justify left \
2115         -borderwidth 1 \
2116         -relief sunken
2117 pack .status -anchor w -side bottom -fill x
2119 # -- Load geometry
2121 catch {
2122 set gm $repo_config(gui.geometry)
2123 wm geometry . [lindex $gm 0]
2124 .vpane sash place 0 \
2125         [lindex [.vpane sash coord 0] 0] \
2126         [lindex $gm 1]
2127 .vpane.files sash place 0 \
2128         [lindex $gm 2] \
2129         [lindex [.vpane.files sash coord 0] 1]
2130 unset gm
2133 # -- Key Bindings
2135 bind $ui_comm <$M1B-Key-Return> {do_commit;break}
2136 bind $ui_comm <$M1B-Key-i> {do_add_all;break}
2137 bind $ui_comm <$M1B-Key-I> {do_add_all;break}
2138 bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
2139 bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
2140 bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
2141 bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
2142 bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
2143 bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
2144 bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2145 bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2147 bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
2148 bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
2149 bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
2150 bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
2151 bind $ui_diff <$M1B-Key-v> {break}
2152 bind $ui_diff <$M1B-Key-V> {break}
2153 bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2154 bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2155 bind $ui_diff <Key-Up>     {catch {%W yview scroll -1 units};break}
2156 bind $ui_diff <Key-Down>   {catch {%W yview scroll  1 units};break}
2157 bind $ui_diff <Key-Left>   {catch {%W xview scroll -1 units};break}
2158 bind $ui_diff <Key-Right>  {catch {%W xview scroll  1 units};break}
2159 bind $ui_diff <Key-k>         {catch {%W yview scroll -1 units};break}
2160 bind $ui_diff <Key-j>         {catch {%W yview scroll  1 units};break}
2161 bind $ui_diff <Key-h>         {catch {%W xview scroll -1 units};break}
2162 bind $ui_diff <Key-l>         {catch {%W xview scroll  1 units};break}
2163 bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
2164 bind $ui_diff <Control-Key-f> {catch {%W yview scroll  1 pages};break}
2165 bind $ui_diff <Button-1>   {focus %W}
2167 if {[is_enabled branch]} {
2168         bind . <$M1B-Key-n> do_create_branch
2169         bind . <$M1B-Key-N> do_create_branch
2172 bind all <Key-F5> do_rescan
2173 bind all <$M1B-Key-r> do_rescan
2174 bind all <$M1B-Key-R> do_rescan
2175 bind .   <$M1B-Key-s> do_signoff
2176 bind .   <$M1B-Key-S> do_signoff
2177 bind .   <$M1B-Key-i> do_add_all
2178 bind .   <$M1B-Key-I> do_add_all
2179 bind .   <$M1B-Key-Return> do_commit
2180 foreach i [list $ui_index $ui_workdir] {
2181         bind $i <Button-1>       "toggle_or_diff         $i %x %y; break"
2182         bind $i <$M1B-Button-1>  "add_one_to_selection   $i %x %y; break"
2183         bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
2185 unset i
2187 set file_lists($ui_index) [list]
2188 set file_lists($ui_workdir) [list]
2190 wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2191 focus -force $ui_comm
2193 # -- Warn the user about environmental problems.  Cygwin's Tcl
2194 #    does *not* pass its env array onto any processes it spawns.
2195 #    This means that git processes get none of our environment.
2197 if {[is_Cygwin]} {
2198         set ignored_env 0
2199         set suggest_user {}
2200         set msg "Possible environment issues exist.
2202 The following environment variables are probably
2203 going to be ignored by any Git subprocess run
2204 by [appname]:
2207         foreach name [array names env] {
2208                 switch -regexp -- $name {
2209                 {^GIT_INDEX_FILE$} -
2210                 {^GIT_OBJECT_DIRECTORY$} -
2211                 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
2212                 {^GIT_DIFF_OPTS$} -
2213                 {^GIT_EXTERNAL_DIFF$} -
2214                 {^GIT_PAGER$} -
2215                 {^GIT_TRACE$} -
2216                 {^GIT_CONFIG$} -
2217                 {^GIT_CONFIG_LOCAL$} -
2218                 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
2219                         append msg " - $name\n"
2220                         incr ignored_env
2221                 }
2222                 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
2223                         append msg " - $name\n"
2224                         incr ignored_env
2225                         set suggest_user $name
2226                 }
2227                 }
2228         }
2229         if {$ignored_env > 0} {
2230                 append msg "
2231 This is due to a known issue with the
2232 Tcl binary distributed by Cygwin."
2234                 if {$suggest_user ne {}} {
2235                         append msg "
2237 A good replacement for $suggest_user
2238 is placing values for the user.name and
2239 user.email settings into your personal
2240 ~/.gitconfig file.
2242                 }
2243                 warn_popup $msg
2244         }
2245         unset ignored_env msg suggest_user name
2248 # -- Only initialize complex UI if we are going to stay running.
2250 if {[is_enabled transport]} {
2251         load_all_remotes
2252         load_all_heads
2254         populate_branch_menu
2255         populate_fetch_menu
2256         populate_push_menu
2259 # -- Only suggest a gc run if we are going to stay running.
2261 if {[is_enabled multicommit]} {
2262         set object_limit 2000
2263         if {[is_Windows]} {set object_limit 200}
2264         regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
2265         if {$objects_current >= $object_limit} {
2266                 if {[ask_popup \
2267                         "This repository currently has $objects_current loose objects.
2269 To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2271 Compress the database now?"] eq yes} {
2272                         do_gc
2273                 }
2274         }
2275         unset object_limit _junk objects_current
2278 lock_index begin-read
2279 after 1 do_rescan