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