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