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