##
## config
-set default_config(gui.trustmtime) false
-
proc is_many_config {name} {
switch -glob -- $name {
remote.*.fetch -
}
}
-proc load_config {} {
+proc load_config {include_global} {
global repo_config global_config default_config
array unset global_config
- array unset repo_config
- 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
+ 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
}
- close $fd_rc
}
+
+ array unset repo_config
catch {
set fd_rc [open "| git repo-config --list" r]
while {[gets $fd_rc line] >= 0} {
}
proc save_config {} {
- global repo_config global_config default_config
+ global default_config font_descs
+ global repo_config global_config
global repo_config_new global_config_new
- foreach name [array names global_config_new] {
+ 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)
+ }
+
+ 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 {
- catch {exec git repo-config --global $name $value}
+ regsub -all "\[{}\]" $value {"} value
+ exec git repo-config --global $name $value
}
set global_config($name) $value
if {$value == $repo_config($name)} {
}
}
- foreach name [array names repo_config_new] {
+ 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 {
- catch {exec git repo-config $name $value}
+ regsub -all "\[{}\]" $value {"} value
+ exec git repo-config $name $value
}
set repo_config($name) $value
}
}
unset cdup
+set single_commit 0
if {$appname == {git-citool}} {
set single_commit 1
}
-load_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
proc commit_tree {} {
global tcl_platform HEAD gitdir commit_type file_states
- global commit_active ui_status_value
- global ui_comm
+ global commit_active pch_error
+ global ui_status_value ui_comm
if {$commit_active || ![lock_index update]} return
return
}
+ set commit_active 1
+
# -- Ask the pre-commit hook for the go-ahead.
#
set pchook [file join $gitdir hooks pre-commit]
if {$tcl_platform(platform) == {windows} && [file isfile $pchook]} {
- set pchook [list sh -c \
- "if test -x \"$pchook\"; then exec \"$pchook\"; fi"]
+ set pchook [list sh -c [concat \
+ "if test -x \"$pchook\";" \
+ "then exec \"$pchook\" 2>&1;" \
+ "fi"]]
} elseif {[file executable $pchook]} {
- set pchook [list $pchook]
+ set pchook [list $pchook |& cat]
} else {
set pchook {}
}
- if {$pchook != {} && [catch {eval exec $pchook} err]} {
- hook_failed_popup pre-commit $err
- unlock_index
+ if {$pchook != {}} {
+ set ui_status_value {Calling pre-commit hook...}
+ set pch_error {}
+ set fd_ph [open "| $pchook" r]
+ fconfigure $fd_ph -blocking 0 -translation binary
+ fileevent $fd_ph readable \
+ [list commit_stage1 $fd_ph $curHEAD $msg]
+ } else {
+ commit_stage2 $curHEAD $msg
+ }
+}
+
+proc commit_stage1 {fd_ph curHEAD msg} {
+ global commit_active pch_error ui_status_value
+
+ append pch_error [read $fd_ph]
+ fconfigure $fd_ph -blocking 1
+ if {[eof $fd_ph]} {
+ if {[catch {close $fd_ph}]} {
+ set ui_status_value {Commit declined by pre-commit hook.}
+ hook_failed_popup pre-commit $pch_error
+ unlock_index
+ set commit_active 0
+ set pch_error {}
+ return
+ }
+ commit_stage2 $curHEAD $msg
return
}
+ fconfigure $fd_ph -blocking 0
+}
+
+proc commit_stage2 {curHEAD msg} {
+ global ui_status_value
# -- Write the tree in the background.
#
- set commit_active 1
set ui_status_value {Committing changes...}
-
set fd_wt [open "| git write-tree" r]
- fileevent $fd_wt readable [list commit_stage2 $fd_wt $curHEAD $msg]
+ fileevent $fd_wt readable [list commit_stage3 $fd_wt $curHEAD $msg]
}
-proc commit_stage2 {fd_wt curHEAD msg} {
+proc commit_stage3 {fd_wt curHEAD msg} {
global single_commit gitdir HEAD PARENT commit_type
global commit_active ui_status_value ui_comm
global file_states
}
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
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]
$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}]]
}
######################################################################
}
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
}
}
}
proc do_options {} {
- global appname gitdir
+ global appname gitdir font_descs
global repo_config global_config
global repo_config_new global_config_new
- load_config
+ load_config 1
array unset repo_config_new
array unset global_config_new
foreach name [array names repo_config] {
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 "save_config; destroy $w"
+ -command [list do_save_config $w]
pack $w.buttons.save -side right
button $w.buttons.cancel -text {Cancel} \
-font font_ui \
- -command "destroy $w"
+ -command [list destroy $w]
pack $w.buttons.cancel -side right
- pack $w.buttons -side bottom -anchor e -pady 10 -padx 10
+ pack $w.buttons -side bottom -fill x -pady 10 -padx 10
- labelframe $w.repo -text {This Repository} \
+ 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 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 ([lindex [file split \
- [file normalize [file dirname $gitdir]]] \
- end]): Options"
+ 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} {
if {$path == {}} return
if {$col == 0} {
- toggle_mode $path
+ update_index [list $path]
}
}
######################################################################
##
-## ui init
+## config defaults
set cursor_ptr arrow
font create font_diff -family Courier -size 10
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
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
+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
.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 \
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
-# -- Fetch Menu
-menu .mbar.fetch
+if {!$single_commit} {
+ # -- Fetch Menu
+ menu .mbar.fetch
-# -- Pull Menu
-menu .mbar.pull
+ # -- Pull Menu
+ menu .mbar.pull
-# -- Push Menu
-menu .mbar.push
+ # -- Push Menu
+ menu .mbar.push
+}
# -- Main Window Layout
panedwindow .vpane -orient vertical
$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
$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
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