git-gui: Optionally save commit buffer on exit.
[gitweb.git] / git-gui.sh
index 4116821d8d41d3b357f0acbdcef4a51b94def76b..335b5f8b67dead27b82488266698f842199dc494 100755 (executable)
@@ -93,6 +93,22 @@ proc is_Cygwin {} {
        return $_iscygwin
 }
 
+proc is_enabled {option} {
+       global enabled_options
+       if {[catch {set on $enabled_options($option)}]} {return 0}
+       return $on
+}
+
+proc enable_option {option} {
+       global enabled_options
+       set enabled_options($option) 1
+}
+
+proc disable_option {option} {
+       global enabled_options
+       set enabled_options($option) 0
+}
+
 ######################################################################
 ##
 ## config
@@ -303,9 +319,14 @@ set _reponame [lindex [file split \
        [file normalize [file dirname $_gitdir]]] \
        end]
 
-set single_commit 0
+enable_option multicommit
+enable_option branch
+enable_option transport
+
 if {[appname] eq {git-citool}} {
-       set single_commit 1
+       disable_option multicommit
+       disable_option branch
+       disable_option transport
 }
 
 ######################################################################
@@ -400,7 +421,7 @@ proc rescan {after {honor_trustmtime 1}} {
        global HEAD PARENT MERGE_HEAD commit_type
        global ui_index ui_workdir ui_status_value ui_comm
        global rescan_active file_states
-       global repo_config single_commit
+       global repo_config
 
        if {$rescan_active > 0 || ![lock_index read]} return
 
@@ -427,7 +448,7 @@ proc rescan {after {honor_trustmtime 1}} {
                $ui_comm edit modified false
        }
 
-       if {!$single_commit} {
+       if {[is_enabled branch]} {
                load_all_heads
                populate_branch_menu
        }
@@ -1181,7 +1202,7 @@ proc commit_writetree {curHEAD msg} {
 
 proc commit_committree {fd_wt curHEAD msg} {
        global HEAD PARENT MERGE_HEAD commit_type
-       global single_commit all_heads current_branch
+       global all_heads current_branch
        global ui_status_value ui_comm selected_commit_type
        global file_states selected_paths rescan_active
        global repo_config
@@ -1286,7 +1307,7 @@ proc commit_committree {fd_wt curHEAD msg} {
        $ui_comm edit reset
        $ui_comm edit modified false
 
-       if {$single_commit} do_quit
+       if {![is_enabled multicommit]} do_quit
 
        # -- Update in memory status
        #
@@ -1381,6 +1402,7 @@ proc mapdesc {state path} {
 }
 
 proc escape_path {path} {
+       regsub -all {\\} $path "\\\\" path
        regsub -all "\n" $path "\\n" path
        return $path
 }
@@ -2957,7 +2979,7 @@ proc reset_hard_wait {fd} {
 set next_browser_id 0
 
 proc new_browser {commit} {
-       global next_browser_id cursor_ptr
+       global next_browser_id cursor_ptr M1B
        global browser_commit browser_status browser_stack browser_path browser_busy
 
        set w .browser[incr next_browser_id]
@@ -3007,6 +3029,16 @@ proc new_browser {commit} {
 
        bind $w_list <Button-1>        "browser_click 0 $w_list @%x,%y;break"
        bind $w_list <Double-Button-1> "browser_click 1 $w_list @%x,%y;break"
+       bind $w_list <$M1B-Up>         "browser_parent $w_list;break"
+       bind $w_list <$M1B-Left>       "browser_parent $w_list;break"
+       bind $w_list <Up>              "browser_move -1 $w_list;break"
+       bind $w_list <Down>            "browser_move 1 $w_list;break"
+       bind $w_list <$M1B-Right>      "browser_enter $w_list;break"
+       bind $w_list <Return>          "browser_enter $w_list;break"
+       bind $w_list <Prior>           "browser_page -1 $w_list;break"
+       bind $w_list <Next>            "browser_page 1 $w_list;break"
+       bind $w_list <Left>            break
+       bind $w_list <Right>           break
 
        bind $w <Visibility> "focus $w"
        bind $w <Destroy> "
@@ -3022,52 +3054,100 @@ proc new_browser {commit} {
        ls_tree $w_list $browser_commit($w_list) {}
 }
 
-proc browser_click {was_double_click w pos} {
+proc browser_move {dir w} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       set lno [lindex [split [$w index in_sel.first] .] 0]
+       incr lno $dir
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
+               $w tag add in_sel $lno.0 [expr {$lno + 1}].0
+               $w see $lno.0
+       }
+}
+
+proc browser_page {dir w} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       $w yview scroll $dir pages
+       set lno [expr {int(
+                 [lindex [$w yview] 0]
+               * [llength $browser_files($w)]
+               + 1)}]
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
+               $w tag add in_sel $lno.0 [expr {$lno + 1}].0
+               $w see $lno.0
+       }
+}
+
+proc browser_parent {w} {
+       global browser_files browser_status browser_path
+       global browser_stack browser_busy
+
+       if {$browser_busy($w)} return
+       set info [lindex $browser_files($w) 0]
+       if {[lindex $info 0] eq {parent}} {
+               set parent [lindex $browser_stack($w) end-1]
+               set browser_stack($w) [lrange $browser_stack($w) 0 end-2]
+               if {$browser_stack($w) eq {}} {
+                       regsub {:.*$} $browser_path($w) {:} browser_path($w)
+               } else {
+                       regsub {/[^/]+$} $browser_path($w) {} browser_path($w)
+               }
+               set browser_status($w) "Loading $browser_path($w)..."
+               ls_tree $w [lindex $parent 0] [lindex $parent 1]
+       }
+}
+
+proc browser_enter {w} {
        global browser_files browser_status browser_path
        global browser_commit browser_stack browser_busy
 
        if {$browser_busy($w)} return
-       set lno [lindex [split [$w index $pos] .] 0]
+       set lno [lindex [split [$w index in_sel.first] .] 0]
        set info [lindex $browser_files($w) [expr {$lno - 1}]]
-
-       $w conf -state normal
-       $w tag remove sel 0.0 end
-       $w tag remove in_sel 0.0 end
        if {$info ne {}} {
+               switch -- [lindex $info 0] {
+               parent {
+                       browser_parent $w
+               }
+               tree {
+                       set name [lindex $info 2]
+                       set escn [escape_path $name]
+                       set browser_status($w) "Loading $escn..."
+                       append browser_path($w) $escn
+                       ls_tree $w [lindex $info 1] $name
+               }
+               blob {
+                       set name [lindex $info 2]
+                       set p {}
+                       foreach n $browser_stack($w) {
+                               append p [lindex $n 1]
+                       }
+                       append p $name
+                       show_blame $browser_commit($w) $p
+               }
+               }
+       }
+}
+
+proc browser_click {was_double_click w pos} {
+       global browser_files browser_busy
+
+       if {$browser_busy($w)} return
+       set lno [lindex [split [$w index $pos] .] 0]
+       focus $w
+
+       if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+               $w tag remove in_sel 0.0 end
                $w tag add in_sel $lno.0 [expr {$lno + 1}].0
                if {$was_double_click} {
-                       switch -- [lindex $info 0] {
-                       parent {
-                               set parent [lindex $browser_stack($w) end-1]
-                               set browser_stack($w) [lrange $browser_stack($w) 0 end-2]
-                               if {$browser_stack($w) eq {}} {
-                                       regsub {:.*$} $browser_path($w) {:} browser_path($w)
-                               } else {
-                                       regsub {/[^/]+$} $browser_path($w) {} browser_path($w)
-                               }
-                               set browser_status($w) "Loading $browser_path($w)..."
-                               ls_tree $w [lindex $parent 0] [lindex $parent 1]
-                       }
-                       tree {
-                               set name [lindex $info 2]
-                               set escn [escape_path $name]
-                               set browser_status($w) "Loading $escn..."
-                               append browser_path($w) $escn
-                               ls_tree $w [lindex $info 1] $name
-                       }
-                       blob {
-                               set name [lindex $info 2]
-                               set p {}
-                               foreach n $browser_stack($w) {
-                                       append p [lindex $n 1]
-                               }
-                               append p $name
-                               show_blame $browser_commit($w) $p
-                       }
-                       }
+                       browser_enter $w
                }
        }
-       $w conf -state disabled
 }
 
 proc ls_tree {w tree_id name} {
@@ -3079,7 +3159,6 @@ proc ls_tree {w tree_id name} {
 
        $w conf -state normal
        $w tag remove in_sel 0.0 end
-       $w tag remove sel 0.0 end
        $w delete 0.0 end
        if {$browser_stack($w) ne {}} {
                $w image create end \
@@ -3147,6 +3226,10 @@ proc read_ls_tree {fd w} {
                set browser_status($w) Ready.
                set browser_busy($w) 0
                array unset browser_buffer $w
+               if {$n > 0} {
+                       $w tag add in_sel 1.0 2.0
+                       focus -force $w
+               }
        }
 }
 
@@ -4043,34 +4126,36 @@ proc do_quit {} {
        if {$is_quitting} return
        set is_quitting 1
 
-       # -- Stash our current commit buffer.
-       #
-       set save [gitdir GITGUI_MSG]
-       set msg [string trim [$ui_comm get 0.0 end]]
-       regsub -all -line {[ \r\t]+$} $msg {} msg
-       if {(![string match amend* $commit_type]
-               || [$ui_comm edit modified])
-               && $msg ne {}} {
-               catch {
-                       set fd [open $save w]
-                       puts -nonewline $fd $msg
-                       close $fd
+       if {[winfo exists $ui_comm]} {
+               # -- Stash our current commit buffer.
+               #
+               set save [gitdir GITGUI_MSG]
+               set msg [string trim [$ui_comm get 0.0 end]]
+               regsub -all -line {[ \r\t]+$} $msg {} msg
+               if {(![string match amend* $commit_type]
+                       || [$ui_comm edit modified])
+                       && $msg ne {}} {
+                       catch {
+                               set fd [open $save w]
+                               puts -nonewline $fd $msg
+                               close $fd
+                       }
+               } else {
+                       catch {file delete $save}
                }
-       } else {
-               catch {file delete $save}
-       }
 
-       # -- 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 ne $rc_geometry} {
-               catch {exec git repo-config gui.geometry $cfg_geometry}
+               # -- 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 ne $rc_geometry} {
+                       catch {exec git repo-config gui.geometry $cfg_geometry}
+               }
        }
 
        destroy .
@@ -4840,11 +4925,11 @@ apply_config
 menu .mbar -tearoff 0
 .mbar add cascade -label Repository -menu .mbar.repository
 .mbar add cascade -label Edit -menu .mbar.edit
-if {!$single_commit} {
+if {[is_enabled branch]} {
        .mbar add cascade -label Branch -menu .mbar.branch
 }
 .mbar add cascade -label Commit -menu .mbar.commit
-if {!$single_commit} {
+if {[is_enabled transport]} {
        .mbar add cascade -label Merge -menu .mbar.merge
        .mbar add cascade -label Fetch -menu .mbar.fetch
        .mbar add cascade -label Push -menu .mbar.push
@@ -4871,7 +4956,7 @@ menu .mbar.repository
        -font font_ui
 .mbar.repository add separator
 
-if {!$single_commit} {
+if {[is_enabled multicommit]} {
        .mbar.repository add command -label {Database Statistics} \
                -command do_stats \
                -font font_ui
@@ -4945,7 +5030,7 @@ menu .mbar.edit
 
 # -- Branch Menu
 #
-if {!$single_commit} {
+if {[is_enabled branch]} {
        menu .mbar.branch
 
        .mbar.branch add command -label {Create...} \
@@ -5152,7 +5237,7 @@ pack .branch.l1 -side left
 pack .branch.cb -side left -fill x
 pack .branch -side top -fill x
 
-if {!$single_commit} {
+if {[is_enabled branch]} {
        menu .mbar.merge
        .mbar.merge add command -label {Local Merge...} \
                -command do_local_merge \
@@ -5624,7 +5709,7 @@ bind $ui_diff <Key-Left>   {catch {%W xview scroll -1 units};break}
 bind $ui_diff <Key-Right>  {catch {%W xview scroll  1 units};break}
 bind $ui_diff <Button-1>   {focus %W}
 
-if {!$single_commit} {
+if {[is_enabled branch]} {
        bind . <$M1B-Key-n> do_create_branch
        bind . <$M1B-Key-N> do_create_branch
 }
@@ -5721,7 +5806,7 @@ user.email settings into your personal
 
 # -- Only initialize complex UI if we are going to stay running.
 #
-if {!$single_commit} {
+if {[is_enabled transport]} {
        load_all_remotes
        load_all_heads
 
@@ -5732,7 +5817,7 @@ if {!$single_commit} {
 
 # -- Only suggest a gc run if we are going to stay running.
 #
-if {!$single_commit} {
+if {[is_enabled multicommit]} {
        set object_limit 2000
        if {[is_Windows]} {set object_limit 200}
        regexp {^([0-9]+) objects,} [exec git count-objects] _junk objects_current