##
## locate our library
-set oguilib {@@GITGUI_LIBDIR@@}
+if { [info exists ::env(GIT_GUI_LIB_DIR) ] } {
+ set oguilib $::env(GIT_GUI_LIB_DIR)
+} else {
+ set oguilib {@@GITGUI_LIBDIR@@}
+}
set oguirel {@@GITGUI_RELATIVE@@}
if {$oguirel eq {1}} {
set oguilib [file dirname [file normalize $argv0]]
return [uplevel 1 real__auto_load $name $args]
}
rename source real__source
- proc source {name} {
- puts stderr "source $name"
- uplevel 1 real__source $name
+ proc source {args} {
+ puts stderr "source $args"
+ uplevel 1 [linsert $args 0 real__source]
}
if {[tk windowingsystem] eq "win32"} { console show }
}
package require msgcat
+# Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
+if {[tk windowingsystem] eq "win32"
+ && [package vcompare [package provide msgcat] 1.4.4] < 0
+} then {
+ proc _mc_update_locale {} {
+ set key {HKEY_CURRENT_USER\Control Panel\Desktop}
+ if {![catch {
+ package require registry
+ set uilocale [registry get $key "PreferredUILanguages"]
+ msgcat::ConvertLocale [string map {- _} [lindex $uilocale 0]]
+ } uilocale]} {
+ if {[string length $uilocale] > 0} {
+ msgcat::mclocale $uilocale
+ }
+ }
+ }
+ _mc_update_locale
+}
+
proc _mc_trim {fmt} {
set cmk [string first @@ $fmt]
if {$cmk > 0} {
::msgcat::mcload $oguimsg
unset oguimsg
+######################################################################
+##
+## On Mac, bring the current Wish process window to front
+
+if {[tk windowingsystem] eq "aqua"} {
+ catch {
+ exec osascript -e [format {
+ tell application "System Events"
+ set frontmost of processes whose unix id is %d to true
+ end tell
+ } [pid]]
+ }
+}
+
######################################################################
##
## read only globals
if {$_trace >= 0} {
set argv [lreplace $argv $_trace $_trace]
set _trace 1
+ if {[tk windowingsystem] eq "win32"} { console show }
} else {
set _trace 0
}
+# variable for the last merged branch (useful for a default when deleting
+# branches).
+set _last_merged_branch {}
+
proc shellpath {} {
global _shellpath env
if {[string match @@* $_shellpath]} {
set _iscygwin 0
} else {
set _iscygwin 1
+ # Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
+ if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
+ set _iscygwin 0
+ }
}
} else {
set _iscygwin 0
global repo_config
if {[catch {set v $repo_config($name)}]} {
return 0
- } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
+ }
+ set v [string tolower $v]
+ if {$v eq {} || $v eq {true} || $v eq {1} || $v eq {yes} || $v eq {on}} {
return 1
} else {
return 0
global repo_config
if {[catch {set v $repo_config($name)}]} {
return 0
- } elseif {$v eq {false} || $v eq {0} || $v eq {no}} {
+ }
+ set v [string tolower $v]
+ if {$v eq {false} || $v eq {0} || $v eq {no} || $v eq {off}} {
return 1
} else {
return 0
return {}
}
+# Test a file for a hashbang to identify executable scripts on Windows.
+proc is_shellscript {filename} {
+ if {![file exists $filename]} {return 0}
+ set f [open $filename r]
+ fconfigure $f -encoding binary
+ set magic [read $f 2]
+ close $f
+ return [expr {$magic eq "#!"}]
+}
+
+# Run a command connected via pipes on stdout.
+# This is for use with textconv filters and uses sh -c "..." to allow it to
+# contain a command with arguments. On windows we must check for shell
+# scripts specifically otherwise just call the filter command.
+proc open_cmd_pipe {cmd path} {
+ global env
+ if {![file executable [shellpath]]} {
+ set exe [auto_execok [lindex $cmd 0]]
+ if {[is_shellscript [lindex $exe 0]]} {
+ set run [linsert [auto_execok sh] end -c "$cmd \"\$0\"" $path]
+ } else {
+ set run [concat $exe [lrange $cmd 1 end] $path]
+ }
+ } else {
+ set run [list [shellpath] -c "$cmd \"\$0\"" $path]
+ }
+ return [open |$run r]
+}
+
proc _lappend_nice {cmd_var} {
global _nice
upvar $cmd_var cmd
}
proc git {args} {
- set opt [list]
-
- while {1} {
- switch -- [lindex $args 0] {
- --nice {
- _lappend_nice opt
- }
-
- default {
- break
- }
-
- }
-
- set args [lrange $args 1 end]
- }
-
- set cmdp [_git_cmd [lindex $args 0]]
- set args [lrange $args 1 end]
-
- _trace_exec [concat $opt $cmdp $args]
- set result [eval exec $opt $cmdp $args]
+ set fd [eval [list git_read] $args]
+ fconfigure $fd -translation binary -encoding utf-8
+ set result [string trimright [read $fd] "\n"]
+ close $fd
if {$::_trace} {
puts stderr "< $result"
}
catch {
if {[is_Windows]} {
- # Use a Cygwin-specific flag to allow killing
- # native Windows processes
- exec kill -f $process
+ exec taskkill /pid $process
} else {
exec kill $process
}
gitlogo put gray26 -to 5 15 11 16
gitlogo redither
- wm iconphoto . -default gitlogo
+ image create photo gitlogo32 -width 32 -height 32
+ gitlogo32 copy gitlogo -zoom 2 2
+
+ wm iconphoto . -default gitlogo gitlogo32
}
}
set default_config(gui.pruneduringfetch) false
set default_config(gui.trustmtime) false
set default_config(gui.fastcopyblame) false
+set default_config(gui.maxrecentrepo) 10
set default_config(gui.copyblamethreshold) 40
set default_config(gui.blamehistoryctx) 7
set default_config(gui.diffcontext) 5
+set default_config(gui.diffopts) {}
set default_config(gui.commitmsgwidth) 75
set default_config(gui.newbranchtemplate) {}
set default_config(gui.spellingdictionary) {}
# TODO: this option should be added to the git-config documentation
set default_config(gui.maxfilesdisplayed) 5000
set default_config(gui.usettk) 1
+set default_config(gui.warndetachedcommit) 1
+set default_config(gui.tabsize) 8
set font_descs {
{fontui font_ui {mc "Main Font"}}
{fontdiff font_diff {mc "Diff/Console Font"}}
}
+set default_config(gui.stageuntracked) ask
+set default_config(gui.displayuntracked) true
######################################################################
##
exit 1
}
+proc get_trimmed_version {s} {
+ set r {}
+ foreach x [split $s -._] {
+ if {[string is integer -strict $x]} {
+ lappend r $x
+ } else {
+ break
+ }
+ }
+ return [join $r .]
+}
set _real_git_version $_git_version
-regsub -- {[\-\.]dirty$} $_git_version {} _git_version
-regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
-regsub {\.GIT$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
+set _git_version [get_trimmed_version $_git_version]
if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
catch {wm withdraw .}
[list git_read config] \
$args \
[list --null --list]]
- fconfigure $fd_rc -translation binary
+ fconfigure $fd_rc -translation binary -encoding utf-8
set buf [read $fd_rc]
close $fd_rc
}
} else {
set arr($name) $value
}
+ } elseif {[regexp {^([^\n]+)$} $line line name]} {
+ # no value given, but interpreting them as
+ # boolean will be handled as true
+ set arr($name) {}
}
}
}
} else {
set arr($name) $value
}
+ } elseif {[regexp {^([^=]+)$} $line line name]} {
+ # no value given, but interpreting them as
+ # boolean will be handled as true
+ set arr($name) {}
}
}
close $fd_rc
# _gitdir exists, so try loading the config
load_config 0
apply_config
-# try to set work tree from environment, falling back to core.worktree
-if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
- set _gitworktree [get_config core.worktree]
- if {$_gitworktree eq ""} {
- set _gitworktree [file dirname [file normalize $_gitdir]]
+
+# v1.7.0 introduced --show-toplevel to return the canonical work-tree
+if {[package vcompare $_git_version 1.7.0] >= 0} {
+ if { [is_Cygwin] } {
+ catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]}
+ } else {
+ set _gitworktree [git rev-parse --show-toplevel]
+ }
+} else {
+ # try to set work tree from environment, core.worktree or use
+ # cdup to obtain a relative path to the top of the worktree. If
+ # run from the top, the ./ prefix ensures normalize expands pwd.
+ if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+ set _gitworktree [get_config core.worktree]
+ if {$_gitworktree eq ""} {
+ set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
+ }
}
}
+
if {$_prefix ne {}} {
if {$_gitworktree eq {}} {
regsub -all {[^/]+/} $_prefix ../ cdup
set PARENT {}
set MERGE_HEAD [list]
set commit_type {}
+set commit_type_is_amend 0
set empty_tree {}
set current_branch {}
set is_detached 0
set is_3way_diff 0
set is_submodule_diff 0
set is_conflict_diff 0
-set selected_commit_type new
set diff_empty_count 0
+set last_revert {}
+set last_revert_enc {}
set nullid "0000000000000000000000000000000000000000"
set nullid2 "0000000000000000000000000000000000000001"
}
proc force_amend {} {
- global selected_commit_type
+ global commit_type_is_amend
global HEAD PARENT MERGE_HEAD commit_type
repository_state newType newHEAD newMERGE_HEAD
set MERGE_HEAD $newMERGE_HEAD
set commit_type $newType
- set selected_commit_type amend
+ set commit_type_is_amend 1
do_select_commit_type
}
(![$ui_comm edit modified]
|| [string trim [$ui_comm get 0.0 end]] eq {})} {
if {[string match amend* $commit_type]} {
- } elseif {[load_message GITGUI_MSG]} {
+ } elseif {[load_message GITGUI_MSG utf-8]} {
} elseif {[run_prepare_commit_msg_hook]} {
} elseif {[load_message MERGE_MSG]} {
} elseif {[load_message SQUASH_MSG]} {
close $fd
}
- set ls_others [list --exclude-per-directory=.gitignore]
- if {[have_info_exclude]} {
- lappend ls_others "--exclude-from=[gitdir info exclude]"
- }
- set user_exclude [get_config core.excludesfile]
- if {$user_exclude ne {} && [file readable $user_exclude]} {
- lappend ls_others "--exclude-from=$user_exclude"
+ if {[package vcompare $::_git_version 1.6.3] >= 0} {
+ set ls_others [list --exclude-standard]
+ } else {
+ set ls_others [list --exclude-per-directory=.gitignore]
+ if {[have_info_exclude]} {
+ lappend ls_others "--exclude-from=[gitdir info exclude]"
+ }
+ set user_exclude [get_config core.excludesfile]
+ if {$user_exclude ne {} && [file readable $user_exclude]} {
+ lappend ls_others "--exclude-from=[file normalize $user_exclude]"
+ }
}
set buf_rdi {}
set buf_rdf {}
set buf_rlo {}
- set rescan_active 3
+ set rescan_active 2
ui_status [mc "Scanning for modified files ..."]
- set fd_di [git_read diff-index --cached -z [PARENT]]
+ if {[git-version >= "1.7.2"]} {
+ set fd_di [git_read diff-index --cached --ignore-submodules=dirty -z [PARENT]]
+ } else {
+ set fd_di [git_read diff-index --cached -z [PARENT]]
+ }
set fd_df [git_read diff-files -z]
- set fd_lo [eval git_read ls-files --others -z $ls_others]
fconfigure $fd_di -blocking 0 -translation binary -encoding binary
fconfigure $fd_df -blocking 0 -translation binary -encoding binary
- fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
+
fileevent $fd_di readable [list read_diff_index $fd_di $after]
fileevent $fd_df readable [list read_diff_files $fd_df $after]
- fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
+
+ if {[is_config_true gui.displayuntracked]} {
+ set fd_lo [eval git_read ls-files --others -z $ls_others]
+ fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
+ fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
+ incr rescan_active
+ }
}
-proc load_message {file} {
+proc load_message {file {encoding {}}} {
global ui_comm
set f [gitdir $file]
return 0
}
fconfigure $fd -eofchar {}
+ if {$encoding ne {}} {
+ fconfigure $fd -encoding $encoding
+ }
set content [string trim [read $fd]]
close $fd
regsub -all -line {[ \r\t]+$} $content {} content
# prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
# it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
- # empty file but existant file.
+ # empty file but existent file.
set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
if {[file isfile [gitdir MERGE_MSG]]} {
set pcm_source "merge"
set fd_mm [open [gitdir MERGE_MSG] r]
+ fconfigure $fd_mm -encoding utf-8
puts -nonewline $fd_pcm [read $fd_mm]
close $fd_mm
} elseif {[file isfile [gitdir SQUASH_MSG]]} {
set pcm_source "squash"
set fd_sm [open [gitdir SQUASH_MSG] r]
+ fconfigure $fd_sm -encoding utf-8
puts -nonewline $fd_pcm [read $fd_sm]
close $fd_sm
} else {
set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
merge_state \
- [encoding convertfrom $p] \
+ [encoding convertfrom utf-8 $p] \
[lindex $i 4]? \
[list [lindex $i 0] [lindex $i 2]] \
[list]
set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
merge_state \
- [encoding convertfrom $p] \
+ [encoding convertfrom utf-8 $p] \
?[lindex $i 4] \
[list] \
[list [lindex $i 0] [lindex $i 2]]
set pck [split $buf_rlo "\0"]
set buf_rlo [lindex $pck end]
foreach p [lrange $pck 0 end-1] {
- set p [encoding convertfrom $p]
+ set p [encoding convertfrom utf-8 $p]
if {[string index $p end] eq {/}} {
set p [string range $p 0 end-1]
}
set to_display [lsort [array names file_states]]
set display_limit [get_config gui.maxfilesdisplayed]
- if {[llength $to_display] > $display_limit} {
- if {!$files_warning} {
- # do not repeatedly warn:
- set files_warning 1
- info_popup [mc "Displaying only %s of %s files." \
- $display_limit [llength $to_display]]
- }
- set to_display [lrange $to_display 0 [expr {$display_limit-1}]]
- }
+ set displayed 0
foreach path $to_display {
set s $file_states($path)
set m [lindex $s 0]
set icon_name [lindex $s 1]
+ if {$displayed > $display_limit && [string index $m 1] eq {O} } {
+ if {!$files_warning} {
+ # do not repeatedly warn:
+ set files_warning 1
+ info_popup [mc "Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files." \
+ $display_limit [llength $to_display]]
+ }
+ continue
+ }
+
set s [string index $m 0]
if {$s ne {U} && $s ne {_}} {
display_all_files_helper $ui_index $path \
if {$s ne {_}} {
display_all_files_helper $ui_workdir $path \
$icon_name $s
+ incr displayed
}
}
} -maskdata $filemask
image create bitmap file_statechange -background white -foreground green -data {
-#define file_merge_width 14
-#define file_merge_height 15
+#define file_statechange_width 14
+#define file_statechange_height 15
static unsigned char file_statechange_bits[] = {
0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
{MD {mc "Staged for commit, missing"}}
{_T {mc "File type changed, not staged"}}
+ {MT {mc "File type changed, old type staged for commit"}}
+ {AT {mc "File type changed, old type staged for commit"}}
{T_ {mc "File type changed, staged"}}
+ {TM {mc "File type change staged, modification not staged"}}
+ {TD {mc "File type change staged, file missing"}}
{_O {mc "Untracked, not staged"}}
{A_ {mc "Staged for commit"}}
&& $msg ne {}} {
catch {
set fd [open $save w]
+ fconfigure $fd -encoding utf-8
puts -nonewline $fd $msg
close $fd
}
}
}
-proc toggle_or_diff {w x y} {
+proc toggle_or_diff {mode w args} {
global file_states file_lists current_diff_path ui_index ui_workdir
- global last_clicked selected_paths
+ global last_clicked selected_paths file_lists_last_clicked
+
+ if {$mode eq "click"} {
+ foreach {x y} $args break
+ set pos [split [$w index @$x,$y] .]
+ foreach {lno col} $pos break
+ } else {
+ if {$last_clicked ne {}} {
+ set lno [lindex $last_clicked 1]
+ } else {
+ set lno [expr {int([lindex [$w tag ranges in_diff] 0])}]
+ }
+ if {$mode eq "toggle"} {
+ set col 0; set y 2
+ } else {
+ incr lno [expr {$mode eq "up" ? -1 : 1}]
+ set col 1
+ }
+ }
- set pos [split [$w index @$x,$y] .]
- set lno [lindex $pos 0]
- set col [lindex $pos 1]
set path [lindex $file_lists($w) [expr {$lno - 1}]]
if {$path eq {}} {
set last_clicked {}
}
set last_clicked [list $w $lno]
+ focus $w
array unset selected_paths
$ui_index tag remove in_sel 0.0 end
$ui_workdir tag remove in_sel 0.0 end
+ set file_lists_last_clicked($w) $path
+
# Determine the state of the file
if {[info exists file_states($path)]} {
set state [lindex $file_states($path) 0]
[concat $after [list ui_ready]]
}
} else {
+ set selected_paths($path) 1
show_diff $path $w $lno
}
}
global file_lists last_clicked selected_paths
if {[lindex $last_clicked 0] ne $w} {
- toggle_or_diff $w $x $y
+ toggle_or_diff click $w $x $y
return
}
}
}
+proc focus_widget {widget} {
+ global file_lists last_clicked selected_paths
+ global file_lists_last_clicked
+
+ if {[llength $file_lists($widget)] > 0} {
+ set path $file_lists_last_clicked($widget)
+ set index [lsearch -sorted -exact $file_lists($widget) $path]
+ if {$index < 0} {
+ set index 0
+ set path [lindex $file_lists($widget) $index]
+ }
+
+ focus $widget
+ set last_clicked [list $widget [expr $index + 1]]
+ array unset selected_paths
+ set selected_paths($path) 1
+ show_diff $path $widget
+ }
+}
+
######################################################################
##
## ui construction
.mbar.repository add command \
-label [mc "Explore Working Copy"] \
-command {do_explore}
+}
+
+if {[is_Windows]} {
+ .mbar.repository add command \
+ -label [mc "Git Bash"] \
+ -command {eval exec [auto_execok start] \
+ [list "Git Bash" bash --login -l &]}
+}
+
+if {[is_Windows] || ![is_bare]} {
.mbar.repository add separator
}
menu .mbar.commit
if {![is_enabled nocommit]} {
- .mbar.commit add radiobutton \
- -label [mc "New Commit"] \
- -command do_select_commit_type \
- -variable selected_commit_type \
- -value new
- lappend disable_on_lock \
- [list .mbar.commit entryconf [.mbar.commit index last] -state]
-
- .mbar.commit add radiobutton \
+ .mbar.commit add checkbutton \
-label [mc "Amend Last Commit"] \
- -command do_select_commit_type \
- -variable selected_commit_type \
- -value amend
+ -variable commit_type_is_amend \
+ -command do_select_commit_type
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
set subcommand_args {}
proc usage {} {
- set s "usage: $::argv0 $::subcommand $::subcommand_args"
+ set s "[mc usage:] $::argv0 $::subcommand $::subcommand_args"
if {[tk windowingsystem] eq "win32"} {
wm withdraw .
tk_messageBox -icon info -message $s \
set jump_spec {}
set is_path 0
foreach a $argv {
- if {$is_path || [file exists $_prefix$a]} {
+ set p [file join $_prefix $a]
+
+ if {$is_path || [file exists $p]} {
if {$path ne {}} usage
- set path [normalize_relpath $_prefix$a]
+ set path [normalize_relpath $p]
break
} elseif {$a eq {--}} {
if {$path ne {}} {
unset is_path
if {$head ne {} && $path eq {}} {
- set path [normalize_relpath $_prefix$head]
- set head {}
+ if {[string index $head 0] eq {/}} {
+ set path [normalize_relpath $head]
+ set head {}
+ } else {
+ set path [normalize_relpath $_prefix$head]
+ set head {}
+ }
}
if {$head eq {}} {
# fall through to setup UI for commits
}
default {
- set err "usage: $argv0 \[{blame|browser|citool}\]"
+ set err "[mc usage:] $argv0 \[{blame|browser|citool}\]"
if {[tk windowingsystem] eq "win32"} {
wm withdraw .
tk_messageBox -icon error -message $err \
}
pack .vpane -anchor n -side top -fill both -expand 1
+# -- Working Directory File List
+
+textframe .vpane.files.workdir -height 100 -width 200
+tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
+ -background lightsalmon -foreground black
+ttext $ui_workdir -background white -foreground black \
+ -borderwidth 0 \
+ -width 20 -height 10 \
+ -wrap none \
+ -takefocus 1 -highlightthickness 1\
+ -cursor $cursor_ptr \
+ -xscrollcommand {.vpane.files.workdir.sx set} \
+ -yscrollcommand {.vpane.files.workdir.sy set} \
+ -state disabled
+${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
+${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
+pack .vpane.files.workdir.title -side top -fill x
+pack .vpane.files.workdir.sx -side bottom -fill x
+pack .vpane.files.workdir.sy -side right -fill y
+pack $ui_workdir -side left -fill both -expand 1
+
# -- Index File List
#
-${NS}::frame .vpane.files.index -height 100 -width 200
+textframe .vpane.files.index -height 100 -width 200
tlabel .vpane.files.index.title \
-text [mc "Staged Changes (Will Commit)"] \
-background lightgreen -foreground black
-text $ui_index -background white -foreground black \
+ttext $ui_index -background white -foreground black \
-borderwidth 0 \
-width 20 -height 10 \
-wrap none \
+ -takefocus 1 -highlightthickness 1\
-cursor $cursor_ptr \
-xscrollcommand {.vpane.files.index.sx set} \
-yscrollcommand {.vpane.files.index.sy set} \
pack .vpane.files.index.sy -side right -fill y
pack $ui_index -side left -fill both -expand 1
-# -- Working Directory File List
+# -- Insert the workdir and index into the panes
#
-${NS}::frame .vpane.files.workdir -height 100 -width 200
-tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
- -background lightsalmon -foreground black
-text $ui_workdir -background white -foreground black \
- -borderwidth 0 \
- -width 20 -height 10 \
- -wrap none \
- -cursor $cursor_ptr \
- -xscrollcommand {.vpane.files.workdir.sx set} \
- -yscrollcommand {.vpane.files.workdir.sy set} \
- -state disabled
-${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
-${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
-pack .vpane.files.workdir.title -side top -fill x
-pack .vpane.files.workdir.sx -side bottom -fill x
-pack .vpane.files.workdir.sy -side right -fill y
-pack $ui_workdir -side left -fill both -expand 1
-
.vpane.files add .vpane.files.workdir
.vpane.files add .vpane.files.index
if {!$use_ttk} {
# -- Diff and Commit Area
#
-${NS}::frame .vpane.lower -height 300 -width 400
-${NS}::frame .vpane.lower.commarea
-${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1
-pack .vpane.lower.diff -fill both -expand 1
-pack .vpane.lower.commarea -side bottom -fill x
-.vpane add .vpane.lower
-if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew}
+if {$have_tk85} {
+ ${NS}::panedwindow .vpane.lower -orient vertical
+ ${NS}::frame .vpane.lower.commarea
+ ${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500
+ .vpane.lower add .vpane.lower.diff
+ .vpane.lower add .vpane.lower.commarea
+ .vpane add .vpane.lower
+ if {$use_ttk} {
+ .vpane.lower pane .vpane.lower.diff -weight 1
+ .vpane.lower pane .vpane.lower.commarea -weight 0
+ } else {
+ .vpane.lower paneconfigure .vpane.lower.diff -stretch always
+ .vpane.lower paneconfigure .vpane.lower.commarea -stretch never
+ }
+} else {
+ frame .vpane.lower -height 300 -width 400
+ frame .vpane.lower.commarea
+ frame .vpane.lower.diff -relief sunken -borderwidth 1
+ pack .vpane.lower.diff -fill both -expand 1
+ pack .vpane.lower.commarea -side bottom -fill x
+ .vpane add .vpane.lower
+ .vpane paneconfigure .vpane.lower -sticky nsew
+}
# -- Commit Area Buttons
#
#
${NS}::frame .vpane.lower.commarea.buffer
${NS}::frame .vpane.lower.commarea.buffer.header
-set ui_comm .vpane.lower.commarea.buffer.t
+set ui_comm .vpane.lower.commarea.buffer.frame.t
set ui_coml .vpane.lower.commarea.buffer.header.l
if {![is_enabled nocommit]} {
- ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
- -text [mc "New Commit"] \
- -command do_select_commit_type \
- -variable selected_commit_type \
- -value new
- lappend disable_on_lock \
- [list .vpane.lower.commarea.buffer.header.new conf -state]
- ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
+ ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \
-text [mc "Amend Last Commit"] \
- -command do_select_commit_type \
- -variable selected_commit_type \
- -value amend
+ -variable commit_type_is_amend \
+ -command do_select_commit_type
lappend disable_on_lock \
[list .vpane.lower.commarea.buffer.header.amend conf -state]
}
if {![is_enabled nocommit]} {
pack .vpane.lower.commarea.buffer.header.amend -side right
- pack .vpane.lower.commarea.buffer.header.new -side right
}
-text $ui_comm -background white -foreground black \
+textframe .vpane.lower.commarea.buffer.frame
+ttext $ui_comm -background white -foreground black \
-borderwidth 1 \
-undo true \
-maxundo 20 \
-autoseparators true \
+ -takefocus 1 \
+ -highlightthickness 1 \
-relief sunken \
-width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
-font font_diff \
- -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
-${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
+ -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \
+ -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set}
+${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \
+ -orient horizontal \
+ -command [list $ui_comm xview]
+${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
+ -orient vertical \
-command [list $ui_comm yview]
-pack .vpane.lower.commarea.buffer.header -side top -fill x
-pack .vpane.lower.commarea.buffer.sby -side right -fill y
+
+pack .vpane.lower.commarea.buffer.frame.sbx -side bottom -fill x
+pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y
pack $ui_comm -side left -fill y
+pack .vpane.lower.commarea.buffer.header -side top -fill x
+pack .vpane.lower.commarea.buffer.frame -side left -fill y
pack .vpane.lower.commarea.buffer -side left -fill y
# -- Commit Message Buffer Context Menu
# -- Diff Body
#
-${NS}::frame .vpane.lower.diff.body
+textframe .vpane.lower.diff.body
set ui_diff .vpane.lower.diff.body.t
-text $ui_diff -background white -foreground black \
+ttext $ui_diff -background white -foreground black \
-borderwidth 0 \
-width 80 -height 5 -wrap none \
-font font_diff \
+ -takefocus 1 -highlightthickness 1 \
-xscrollcommand {.vpane.lower.diff.body.sbx set} \
-yscrollcommand {.vpane.lower.diff.body.sby set} \
-state disabled
+catch {$ui_diff configure -tabstyle wordprocessor}
${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
-command [list $ui_diff xview]
${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
pack .vpane.lower.diff.header -side top -fill x
pack .vpane.lower.diff.body -side bottom -fill both -expand 1
+foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
+ $ui_diff tag configure clr4$n -background $c
+ $ui_diff tag configure clri4$n -foreground $c
+ $ui_diff tag configure clr3$n -foreground $c
+ $ui_diff tag configure clri3$n -background $c
+}
+$ui_diff tag configure clr1 -font font_diffbold
+$ui_diff tag configure clr4 -underline 1
+
+$ui_diff tag conf d_info -foreground blue -font font_diffbold
+
$ui_diff tag conf d_cr -elide true
-$ui_diff tag conf d_@ -foreground blue -font font_diffbold
+$ui_diff tag conf d_@ -font font_diffbold
$ui_diff tag conf d_+ -foreground {#00a000}
$ui_diff tag conf d_- -foreground red
-foreground red \
-background ivory1
-$ui_diff tag conf d<<<<<<< \
+$ui_diff tag conf d< \
-foreground orange \
-font font_diffbold
-$ui_diff tag conf d======= \
+$ui_diff tag conf d= \
-foreground orange \
-font font_diffbold
-$ui_diff tag conf d>>>>>>> \
+$ui_diff tag conf d> \
-foreground orange \
-font font_diffbold
menu $ctxm -tearoff 0
$ctxm add command \
-label [mc "Apply/Reverse Hunk"] \
- -command {apply_hunk $cursorX $cursorY}
+ -command {apply_or_revert_hunk $cursorX $cursorY 0}
set ui_diff_applyhunk [$ctxm index last]
lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
$ctxm add command \
-label [mc "Apply/Reverse Line"] \
- -command {apply_range_or_line $cursorX $cursorY; do_rescan}
+ -command {apply_or_revert_range_or_line $cursorX $cursorY 0; do_rescan}
set ui_diff_applyline [$ctxm index last]
lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
$ctxm add separator
+$ctxm add command \
+ -label [mc "Revert Hunk"] \
+ -command {apply_or_revert_hunk $cursorX $cursorY 1}
+set ui_diff_reverthunk [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_reverthunk -state]
+$ctxm add command \
+ -label [mc "Revert Line"] \
+ -command {apply_or_revert_range_or_line $cursorX $cursorY 1; do_rescan}
+set ui_diff_revertline [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_revertline -state]
+$ctxm add command \
+ -label [mc "Undo Last Revert"] \
+ -command {undo_last_revert; do_rescan}
+set ui_diff_undorevert [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_undorevert -state]
+$ctxm add separator
$ctxm add command \
-label [mc "Show Less Context"] \
-command show_less_context
}
proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
- global current_diff_path file_states
+ global current_diff_path file_states last_revert
set ::cursorX $x
set ::cursorY $y
if {[info exists file_states($current_diff_path)]} {
tk_popup $ctxmsm $X $Y
} else {
set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
+ set u [mc "Undo Last Revert"]
if {$::ui_index eq $::current_diff_side} {
set l [mc "Unstage Hunk From Commit"]
+ set h [mc "Revert Hunk"]
+
if {$has_range} {
set t [mc "Unstage Lines From Commit"]
+ set r [mc "Revert Lines"]
} else {
set t [mc "Unstage Line From Commit"]
+ set r [mc "Revert Line"]
}
} else {
set l [mc "Stage Hunk For Commit"]
+ set h [mc "Revert Hunk"]
+
if {$has_range} {
set t [mc "Stage Lines For Commit"]
+ set r [mc "Revert Lines"]
} else {
set t [mc "Stage Line For Commit"]
+ set r [mc "Revert Line"]
}
}
if {$::is_3way_diff
|| $current_diff_path eq {}
|| {__} eq $state
|| {_O} eq $state
- || {_T} eq $state
- || {T_} eq $state
+ || [string match {?T} $state]
+ || [string match {T?} $state]
|| [has_textconv $current_diff_path]} {
set s disabled
+ set revert_state disabled
} else {
set s normal
+
+ # Only allow reverting changes in the working tree. If
+ # the user wants to revert changes in the index, they
+ # need to unstage those first.
+ if {$::ui_workdir eq $::current_diff_side} {
+ set revert_state normal
+ } else {
+ set revert_state disabled
+ }
+ }
+
+ if {$last_revert eq {}} {
+ set undo_state disabled
+ } else {
+ set undo_state normal
}
+
$ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
$ctxm entryconf $::ui_diff_applyline -state $s -label $t
+ $ctxm entryconf $::ui_diff_revertline -state $revert_state \
+ -label $r
+ $ctxm entryconf $::ui_diff_reverthunk -state $revert_state \
+ -label $h
+ $ctxm entryconf $::ui_diff_undorevert -state $undo_state \
+ -label $u
+
tk_popup $ctxm $X $Y
}
}
bind $ui_diff <$M1B-Key-V> {break}
bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+bind $ui_diff <$M1B-Key-j> {do_revert_selection;break}
+bind $ui_diff <$M1B-Key-J> {do_revert_selection;break}
bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
bind . <$M1B-Key-R> ui_do_rescan
bind . <$M1B-Key-s> do_signoff
bind . <$M1B-Key-S> do_signoff
-bind . <$M1B-Key-t> do_add_selection
-bind . <$M1B-Key-T> do_add_selection
+bind . <$M1B-Key-t> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-T> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-u> { toggle_or_diff toggle %W }
+bind . <$M1B-Key-U> { toggle_or_diff toggle %W }
bind . <$M1B-Key-j> do_revert_selection
bind . <$M1B-Key-J> do_revert_selection
bind . <$M1B-Key-i> do_add_all
bind . <$M1B-Key-KP_Add> {show_more_context;break}
bind . <$M1B-Key-Return> do_commit
foreach i [list $ui_index $ui_workdir] {
- bind $i <Button-1> "toggle_or_diff $i %x %y; break"
- bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
- bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
+ bind $i <Button-1> { toggle_or_diff click %W %x %y; break }
+ bind $i <$M1B-Button-1> { add_one_to_selection %W %x %y; break }
+ bind $i <Shift-Button-1> { add_range_to_selection %W %x %y; break }
+ bind $i <Key-Up> { toggle_or_diff up %W; break }
+ bind $i <Key-Down> { toggle_or_diff down %W; break }
}
unset i
+bind . <Alt-Key-1> {focus_widget $::ui_workdir}
+bind . <Alt-Key-2> {focus_widget $::ui_index}
+bind . <Alt-Key-3> {focus $::ui_diff}
+bind . <Alt-Key-4> {focus $::ui_comm}
+
+set file_lists_last_clicked($ui_index) {}
+set file_lists_last_clicked($ui_workdir) {}
+
set file_lists($ui_index) [list]
set file_lists($ui_workdir) [list]
}
if {[winfo exists $ui_comm]} {
- set GITGUI_BCK_exists [load_message GITGUI_BCK]
+ set GITGUI_BCK_exists [load_message GITGUI_BCK utf-8]
# -- If both our backup and message files exist use the
# newer of the two files to initialize the buffer.
} elseif {$m} {
catch {
set fd [open [gitdir GITGUI_BCK] w]
+ fconfigure $fd -encoding utf-8
puts -nonewline $fd $msg
close $fd
set GITGUI_BCK_exists 1
$ui_comm configure -state disabled -background gray
}
}
-if {[is_enabled multicommit]} {
+if {[is_enabled multicommit] && ![is_config_false gui.gcwarning]} {
after 1000 hint_gc
}
if {[is_enabled retcode]} {