git-gui: Allow the user to disable diff stat summary during pull.
[gitweb.git] / git-gui
diff --git a/git-gui b/git-gui
index 040581e8296ad262ed4abfe383b729a7cfb17830..761ce7551f8250fba693ecc8d516cb7f00303e6e 100755 (executable)
--- a/git-gui
+++ b/git-gui
@@ -14,49 +14,108 @@ set gitdir {}
 ##
 ## config
 
-proc load_repo_config {} {
-       global repo_config
-       global cfg_trust_mtime
+proc is_many_config {name} {
+       switch -glob -- $name {
+       remote.*.fetch -
+       remote.*.push
+               {return 1}
+       *
+               {return 0}
+       }
+}
+
+proc load_config {include_global} {
+       global repo_config global_config default_config
+
+       array unset global_config
+       if {$include_global} {
+               catch {
+                       set fd_rc [open "| git repo-config --global --list" r]
+                       while {[gets $fd_rc line] >= 0} {
+                               if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
+                                       if {[is_many_config $name]} {
+                                               lappend global_config($name) $value
+                                       } else {
+                                               set global_config($name) $value
+                                       }
+                               }
+                       }
+                       close $fd_rc
+               }
+       }
 
        array unset repo_config
        catch {
                set fd_rc [open "| git repo-config --list" r]
                while {[gets $fd_rc line] >= 0} {
                        if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
-                               lappend repo_config($name) $value
+                               if {[is_many_config $name]} {
+                                       lappend repo_config($name) $value
+                               } else {
+                                       set repo_config($name) $value
+                               }
                        }
                }
                close $fd_rc
        }
 
-       if {[catch {set cfg_trust_mtime \
-                       [lindex $repo_config(gui.trustmtime) 0]
-               }]} {
-               set cfg_trust_mtime false
+       foreach name [array names default_config] {
+               if {[catch {set v $global_config($name)}]} {
+                       set global_config($name) $default_config($name)
+               }
+               if {[catch {set v $repo_config($name)}]} {
+                       set repo_config($name) $default_config($name)
+               }
        }
 }
 
-proc save_my_config {} {
-       global repo_config
-       global cfg_trust_mtime
+proc save_config {} {
+       global default_config font_descs
+       global repo_config global_config
+       global repo_config_new global_config_new
 
-       if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
-               set rc_trustMTime [list false]
-       }
-       if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} {
-               exec git repo-config gui.trustMTime $cfg_trust_mtime
-               set repo_config(gui.trustmtime) [list $cfg_trust_mtime]
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set font [lindex $option 1]
+               font configure $font \
+                       -family $global_config_new(gui.$font^^family) \
+                       -size $global_config_new(gui.$font^^size)
+               font configure ${font}bold \
+                       -family $global_config_new(gui.$font^^family) \
+                       -size $global_config_new(gui.$font^^size)
+               set global_config_new(gui.$name) [font configure $font]
+               unset global_config_new(gui.$font^^family)
+               unset global_config_new(gui.$font^^size)
        }
 
-       set cfg_geometry [wm geometry .]
-       append cfg_geometry " [lindex [.vpane sash coord 0] 1]"
-       append cfg_geometry " [lindex [.vpane.files sash coord 0] 0]"
-       if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
-               set rc_geometry [list [list]]
+       foreach name [array names default_config] {
+               set value $global_config_new($name)
+               if {$value != $global_config($name)} {
+                       if {$value == $default_config($name)} {
+                               catch {exec git repo-config --global --unset $name}
+                       } else {
+                               regsub -all "\[{}\]" $value {"} value
+                               exec git repo-config --global $name $value
+                       }
+                       set global_config($name) $value
+                       if {$value == $repo_config($name)} {
+                               catch {exec git repo-config --unset $name}
+                               set repo_config($name) $value
+                       }
+               }
        }
-       if {$cfg_geometry != [lindex $rc_geometry 0]} {
-               exec git repo-config gui.geometry $cfg_geometry
-               set repo_config(gui.geometry) [list $cfg_geometry]
+
+       foreach name [array names default_config] {
+               set value $repo_config_new($name)
+               if {$value != $repo_config($name)} {
+                       if {$value == $global_config($name)} {
+                               catch {exec git repo-config --unset $name}
+                       } else {
+                               regsub -all "\[{}\]" $value {"} value
+                               exec git repo-config $name $value
+                       }
+                       set repo_config($name) $value
+               }
        }
 }
 
@@ -113,22 +172,18 @@ if {$cdup != ""} {
 }
 unset cdup
 
+set single_commit 0
 if {$appname == {git-citool}} {
        set single_commit 1
 }
 
-load_repo_config
-
 ######################################################################
 ##
 ## task management
 
-set single_commit 0
 set status_active 0
 set diff_active 0
-set update_active 0
 set commit_active 0
-set update_index_fd {}
 
 set disable_on_lock [list]
 set index_lock_type none
@@ -183,7 +238,7 @@ proc update_status {{final Ready.}} {
        global HEAD PARENT commit_type
        global ui_index ui_other ui_status_value ui_comm
        global status_active file_states
-       global cfg_trust_mtime
+       global repo_config
 
        if {$status_active || ![lock_index read]} return
 
@@ -209,7 +264,7 @@ proc update_status {{final Ready.}} {
                $ui_comm edit reset
        }
 
-       if {$cfg_trust_mtime == {true}} {
+       if {$repo_config(gui.trustmtime) == {true}} {
                update_status_stage2 {} $final
        } else {
                set status_active 1
@@ -409,7 +464,7 @@ proc handle_empty_diff {} {
 
 The modification date of this file was updated by another
 application and you currently have the Trust File Modification
-Timestamps feature enabled, so Git did not automatically detect
+Timestamps option enabled, so Git did not automatically detect
 that there are no content differences in this file.
 
 This file will now be removed from the modified files list, to
@@ -502,7 +557,7 @@ proc show_diff {path {w {}} {lno {}}} {
 
 proc read_diff {fd} {
        global ui_diff ui_status_value diff_3way diff_active
-       global cfg_trust_mtime
+       global repo_config
 
        while {[gets $fd line] >= 0} {
                if {[string match {diff --git *} $line]} continue
@@ -550,7 +605,8 @@ proc read_diff {fd} {
                unlock_index
                set ui_status_value {Ready.}
 
-               if {$cfg_trust_mtime && [$ui_diff index end] == {2.0}} {
+               if {$repo_config(gui.trustmtime) == {true}
+                       && [$ui_diff index end] == {2.0}} {
                        handle_empty_diff
                }
        }
@@ -840,8 +896,7 @@ proc fetch_from {remote} {
 }
 
 proc pull_remote {remote branch} {
-       global HEAD commit_type
-       global file_states
+       global HEAD commit_type file_states repo_config
 
        if {![lock_index update]} return
 
@@ -877,6 +932,9 @@ Commit or throw away all changes before starting a pull operation.
        set w [new_console "pull $remote $branch" \
                "Pulling new changes from branch $branch in $remote"]
        set cmd [list git pull]
+       if {$repo_config(gui.pullsummary) == {false}} {
+               lappend cmd --no-summary
+       }
        lappend cmd $remote
        lappend cmd $branch
        console_exec $w $cmd [list post_pull_remote $remote $branch]
@@ -1047,55 +1105,73 @@ proc display_all_files {} {
        $ui_other conf -state disabled
 }
 
-proc with_update_index {body} {
-       global update_index_fd
+proc update_index {pathList} {
+       global update_index_cp ui_status_value
 
-       if {$update_index_fd == {}} {
-               if {![lock_index update]} return
-               set update_index_fd [open \
-                       "| git update-index --add --remove -z --stdin" \
-                       w]
-               fconfigure $update_index_fd -translation binary
-               uplevel 1 $body
-               close $update_index_fd
-               set update_index_fd {}
-               unlock_index
-       } else {
-               uplevel 1 $body
-       }
-}
+       if {![lock_index update]} return
 
-proc update_index {path} {
-       global update_index_fd
+       set update_index_cp 0
+       set totalCnt [llength $pathList]
+       set batch [expr {int($totalCnt * .01) + 1}]
+       if {$batch > 25} {set batch 25}
+
+       set ui_status_value "Including files ... 0/$totalCnt 0%"
+       set ui_status_value [format \
+               "Including files ... %i/%i files (%.2f%%)" \
+               $update_index_cp \
+               $totalCnt \
+               0.0]
+       set fd [open "| git update-index --add --remove -z --stdin" w]
+       fconfigure $fd -blocking 0 -translation binary
+       fileevent $fd writable [list \
+               write_update_index \
+               $fd \
+               $pathList \
+               $totalCnt \
+               $batch \
+               ]
+}
+
+proc write_update_index {fd pathList totalCnt batch} {
+       global update_index_cp ui_status_value
+       global file_states ui_fname_value
 
-       if {$update_index_fd == {}} {
-               error {not in with_update_index}
-       } else {
-               puts -nonewline $update_index_fd "$path\0"
+       if {$update_index_cp >= $totalCnt} {
+               close $fd
+               unlock_index
+               set ui_status_value {Ready.}
+               return
        }
-}
-
-proc toggle_mode {path} {
-       global file_states ui_fname_value
 
-       set s $file_states($path)
-       set m [lindex $s 0]
+       for {set i $batch} \
+               {$update_index_cp < $totalCnt && $i > 0} \
+               {incr i -1} {
+               set path [lindex $pathList $update_index_cp]
+               incr update_index_cp
+
+               switch -- [lindex $file_states($path) 0] {
+               AM -
+               _O {set new A*}
+               _M -
+               MM {set new M*}
+               AD -
+               _D {set new D*}
+               default {continue}
+               }
 
-       switch -- $m {
-       AM -
-       _O {set new A*}
-       _M -
-       MM {set new M*}
-       AD -
-       _D {set new D*}
-       default {return}
+               puts -nonewline $fd $path
+               puts -nonewline $fd "\0"
+               display_file $path $new
+               if {$ui_fname_value == $path} {
+                       show_diff $path
+               }
        }
 
-       with_update_index {update_index $path}
-       display_file $path $new
-       if {$ui_fname_value == $path} {
-               show_diff $path
-       }
+       set ui_status_value [format \
+               "Including files ... %i/%i files (%.2f%%)" \
+               $update_index_cp \
+               $totalCnt \
+               [expr {100.0 * $update_index_cp / $totalCnt}]]
 }
 
 ######################################################################
@@ -1314,7 +1390,6 @@ proc hook_failed_popup {hook msg} {
 
        set w .hookfail
        toplevel $w
-       wm transient $w .
 
        frame $w.m
        label $w.m.l1 -text "$hook hook failed:" \
@@ -1532,14 +1607,16 @@ proc do_repack {} {
        console_exec $w $cmd
 }
 
-set quitting 0
+set is_quitting 0
 
 proc do_quit {} {
-       global gitdir ui_comm quitting
+       global gitdir ui_comm is_quitting repo_config
 
-       if {$quitting} return
-       set quitting 1
+       if {$is_quitting} return
+       set is_quitting 1
 
+       # -- Stash our current commit buffer.
+       #
        set save [file join $gitdir GITGUI_MSG]
        set msg [string trim [$ui_comm get 0.0 end]]
        if {[$ui_comm edit modified] && $msg != {}} {
@@ -1552,7 +1629,19 @@ proc do_quit {} {
                file delete $save
        }
 
-       save_my_config
+       # -- Stash our current window geometry into this repository.
+       #
+       set cfg_geometry [list]
+       lappend cfg_geometry [wm geometry .]
+       lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
+       lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
+       if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
+               set rc_geometry {}
+       }
+       if {$cfg_geometry != $rc_geometry} {
+               catch {exec git repo-config gui.geometry $cfg_geometry}
+       }
+
        destroy .
 }
 
@@ -1561,27 +1650,25 @@ proc do_rescan {} {
 }
 
 proc do_include_all {} {
-       global update_active ui_status_value
-
-       if {$update_active || ![lock_index begin-update]} return
-
-       set update_active 1
-       set ui_status_value {Including all modified files...}
-       after 1 {
-               with_update_index {
-                       foreach path [array names file_states] {
-                               set s $file_states($path)
-                               set m [lindex $s 0]
-                               switch -- $m {
-                               AM -
-                               MM -
-                               _M -
-                               _D {toggle_mode $path}
-                               }
-                       }
+       global file_states
+
+       if {![lock_index begin-update]} return
+
+       set pathList [list]
+       foreach path [array names file_states] {
+               set s $file_states($path)
+               set m [lindex $s 0]
+               switch -- $m {
+               AM -
+               MM -
+               _M -
+               _D {lappend pathList $path}
                }
-               set update_active 0
-               set ui_status_value {Ready.}
+       }
+       if {$pathList == {}} {
+               unlock_index
+       } else {
+               update_index $pathList
        }
 }
 
@@ -1624,6 +1711,137 @@ proc do_commit {} {
        commit_tree
 }
 
+proc do_options {} {
+       global appname gitdir font_descs
+       global repo_config global_config
+       global repo_config_new global_config_new
+
+       load_config 1
+       array unset repo_config_new
+       array unset global_config_new
+       foreach name [array names repo_config] {
+               set repo_config_new($name) $repo_config($name)
+       }
+       foreach name [array names global_config] {
+               set global_config_new($name) $global_config($name)
+       }
+       set reponame [lindex [file split \
+               [file normalize [file dirname $gitdir]]] \
+               end]
+
+       set w .options_editor
+       toplevel $w
+       wm geometry $w "+[winfo rootx .]+[winfo rooty .]"
+
+       label $w.header -text "$appname Options" \
+               -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.restore -text {Restore Defaults} \
+               -font font_ui \
+               -command do_restore_defaults
+       pack $w.buttons.restore -side left
+       button $w.buttons.save -text Save \
+               -font font_ui \
+               -command [list do_save_config $w]
+       pack $w.buttons.save -side right
+       button $w.buttons.cancel -text {Cancel} \
+               -font font_ui \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       labelframe $w.repo -text "$reponame Repository" \
+               -font font_ui \
+               -relief raised -borderwidth 2
+       labelframe $w.global -text {Global (All Repositories)} \
+               -font font_ui \
+               -relief raised -borderwidth 2
+       pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5
+       pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5
+
+       foreach option {
+               {pullsummary {Show Pull Summary}}
+               {trustmtime {Trust File Modification Timestamps}}
+               } {
+               set name [lindex $option 0]
+               set text [lindex $option 1]
+               foreach f {repo global} {
+                       checkbutton $w.$f.$name -text $text \
+                               -variable ${f}_config_new(gui.$name) \
+                               -onvalue true \
+                               -offvalue false \
+                               -font font_ui
+                       pack $w.$f.$name -side top -anchor w
+               }
+       }
+
+       set all_fonts [lsort [font families]]
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set font [lindex $option 1]
+               set text [lindex $option 2]
+
+               set global_config_new(gui.$font^^family) \
+                       [font configure $font -family]
+               set global_config_new(gui.$font^^size) \
+                       [font configure $font -size]
+
+               frame $w.global.$name
+               label $w.global.$name.l -text "$text:" -font font_ui
+               pack $w.global.$name.l -side left -anchor w -fill x
+               eval tk_optionMenu $w.global.$name.family \
+                       global_config_new(gui.$font^^family) \
+                       $all_fonts
+               spinbox $w.global.$name.size \
+                       -textvariable global_config_new(gui.$font^^size) \
+                       -from 2 -to 80 -increment 1 \
+                       -width 3 \
+                       -font font_ui
+               pack $w.global.$name.size -side right -anchor e
+               pack $w.global.$name.family -side right -anchor e
+               pack $w.global.$name -side top -anchor w -fill x
+       }
+
+       bind $w <Visibility> "grab $w; focus $w"
+       bind $w <Key-Escape> "destroy $w"
+       wm title $w "$appname ($reponame): Options"
+       tkwait window $w
+}
+
+proc do_restore_defaults {} {
+       global font_descs default_config repo_config
+       global repo_config_new global_config_new
+
+       foreach name [array names default_config] {
+               set repo_config_new($name) $default_config($name)
+               set global_config_new($name) $default_config($name)
+       }
+
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set repo_config(gui.$name) $default_config(gui.$name)
+       }
+       apply_config
+
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set font [lindex $option 1]
+               set global_config_new(gui.$font^^family) \
+                       [font configure $font -family]
+               set global_config_new(gui.$font^^size) \
+                       [font configure $font -size]
+       }
+}
+
+proc do_save_config {w} {
+       if {[catch {save_config} err]} {
+               error_popup "Failed to completely save options:\n\n$err"
+       }
+       destroy $w
+}
+
 # shift == 1: left click
 #          3: right click  
 proc click {w x y shift wx wy} {
@@ -1650,15 +1868,15 @@ proc unclick {w x y} {
        if {$path == {}} return
 
        if {$col == 0} {
-               toggle_mode $path
+               update_index [list $path]
        }
 }
 
 ######################################################################
 ##
-## ui init
+## config defaults
 
-set cursor_ptr left_ptr
+set cursor_ptr arrow
 font create font_diff -family Courier -size 10
 font create font_ui
 catch {
@@ -1667,10 +1885,8 @@ catch {
        destroy .dummy
 }
 
-eval font create font_uibold [font configure font_ui]
-font configure font_uibold -weight bold
-eval font create font_diffbold [font configure font_diff]
-font configure font_diffbold -weight bold
+font create font_uibold
+font create font_diffbold
 
 set M1B M1
 set M1T M1
@@ -1682,15 +1898,51 @@ if {$tcl_platform(platform) == {windows}} {
        set M1T Cmd
 }
 
+proc apply_config {} {
+       global repo_config font_descs
+
+       foreach option $font_descs {
+               set name [lindex $option 0]
+               set font [lindex $option 1]
+               if {[catch {
+                       foreach {cn cv} $repo_config(gui.$name) {
+                               font configure $font $cn $cv
+                       }
+                       } err]} {
+                       error_popup "Invalid font specified in gui.$name:\n\n$err"
+               }
+               foreach {cn cv} [font configure $font] {
+                       font configure ${font}bold $cn $cv
+               }
+               font configure ${font}bold -weight bold
+       }
+}
+
+set default_config(gui.trustmtime) false
+set default_config(gui.pullsummary) true
+set default_config(gui.fontui) [font configure font_ui]
+set default_config(gui.fontdiff) [font configure font_diff]
+set font_descs {
+       {fontui   font_ui   {Main Font}}
+       {fontdiff font_diff {Diff/Console Font}}
+}
+load_config 0
+apply_config
+
+######################################################################
+##
+## ui construction
+
 # -- Menu Bar
 menu .mbar -tearoff 0
 .mbar add cascade -label Project -menu .mbar.project
 .mbar add cascade -label Edit -menu .mbar.edit
 .mbar add cascade -label Commit -menu .mbar.commit
-.mbar add cascade -label Fetch -menu .mbar.fetch
-.mbar add cascade -label Pull -menu .mbar.pull
-.mbar add cascade -label Push -menu .mbar.push
-.mbar add cascade -label Options -menu .mbar.options
+if {!$single_commit} {
+       .mbar add cascade -label Fetch -menu .mbar.fetch
+       .mbar add cascade -label Pull -menu .mbar.pull
+       .mbar add cascade -label Push -menu .mbar.push
+}
 . configure -menu .mbar
 
 # -- Project Menu
@@ -1698,9 +1950,11 @@ menu .mbar.project
 .mbar.project add command -label Visualize \
        -command do_gitk \
        -font font_ui
-.mbar.project add command -label {Repack Database} \
-       -command do_repack \
-       -font font_ui
+if {!$single_commit} {
+       .mbar.project add command -label {Repack Database} \
+               -command do_repack \
+               -font font_ui
+}
 .mbar.project add command -label Quit \
        -command do_quit \
        -accelerator $M1T-Q \
@@ -1739,6 +1993,10 @@ menu .mbar.edit
        -command {catch {[focus] tag add sel 0.0 end}} \
        -accelerator $M1T-A \
        -font font_ui
+.mbar.edit add separator
+.mbar.edit add command -label {Options...} \
+       -command do_options \
+       -font font_ui
 
 # -- Commit Menu
 menu .mbar.commit
@@ -1770,23 +2028,16 @@ lappend disable_on_lock \
 lappend disable_on_lock \
        [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-# -- Fetch Menu
-menu .mbar.fetch
-
-# -- Pull Menu
-menu .mbar.pull
+if {!$single_commit} {
+       # -- Fetch Menu
+       menu .mbar.fetch
 
-# -- Push Menu
-menu .mbar.push
+       # -- Pull Menu
+       menu .mbar.pull
 
-# -- Options Menu
-menu .mbar.options
-.mbar.options add checkbutton \
-       -label {Trust File Modification Timestamps} \
-       -font font_ui \
-       -offvalue false \
-       -onvalue true \
-       -variable cfg_trust_mtime
+       # -- Push Menu
+       menu .mbar.push
+}
 
 # -- Main Window Layout
 panedwindow .vpane -orient vertical
@@ -1832,7 +2083,7 @@ $ui_index tag conf in_diff -font font_uibold
 $ui_other tag conf in_diff -font font_uibold
 
 # -- Diff and Commit Area
-frame .vpane.lower -height 400 -width 400
+frame .vpane.lower -height 300 -width 400
 frame .vpane.lower.commarea
 frame .vpane.lower.diff -relief sunken -borderwidth 1
 pack .vpane.lower.commarea -side top -fill x
@@ -2018,6 +2269,9 @@ $ui_diff.ctxm add command -label "Decrease Font Size" \
 $ui_diff.ctxm add command -label "Increase Font Size" \
        -font font_ui \
        -command {incr_font_size font_diff 1}
+$ui_diff.ctxm add command -label {Options...} \
+       -font font_ui \
+       -command do_options
 bind_button3 $ui_diff "tk_popup $ui_diff.ctxm %X %Y"
 
 # -- Status Bar
@@ -2032,7 +2286,7 @@ pack .status -anchor w -side bottom -fill x
 
 # -- Load geometry
 catch {
-set gm [lindex $repo_config(gui.geometry) 0]
+set gm $repo_config(gui.geometry)
 wm geometry . [lindex $gm 0]
 .vpane sash place 0 \
        [lindex [.vpane sash coord 0] 0] \
@@ -2094,8 +2348,10 @@ set file_lists($ui_other) [list]
 
 wm title . "$appname ([file normalize [file dirname $gitdir]])"
 focus -force $ui_comm
-load_all_remotes
-populate_remote_menu .mbar.fetch From fetch_from
-populate_remote_menu .mbar.push To push_to
-populate_pull_menu .mbar.pull
-update_status
+if {!$single_commit} {
+       load_all_remotes
+       populate_remote_menu .mbar.fetch From fetch_from
+       populate_remote_menu .mbar.push To push_to
+       populate_pull_menu .mbar.pull
+}
+after 1 update_status