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