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