set _appname {Git Gui}
set _gitdir {}
set _gitexec {}
+set _githtmldir {}
set _reponame {}
set _iscygwin {}
set _search_path {}
return [eval [list file join $_gitexec] $args]
}
+proc githtmldir {args} {
+ global _githtmldir
+ if {$_githtmldir eq {}} {
+ if {[catch {set _githtmldir [git --html-path]}]} {
+ # Git not installed or option not yet supported
+ return {}
+ }
+ if {[is_Cygwin]} {
+ set _githtmldir [exec cygpath \
+ --windows \
+ --absolute \
+ $_githtmldir]
+ } else {
+ set _githtmldir [file normalize $_githtmldir]
+ }
+ }
+ if {$args eq {}} {
+ return $_githtmldir
+ }
+ return [eval [list file join $_githtmldir] $args]
+}
+
proc reponame {} {
return $::_reponame
}
if {![info exists _nice]} {
set _nice [_which nice]
+ if {[catch {exec $_nice git version}]} {
+ set _nice {}
+ }
}
if {$_nice ne {}} {
lappend cmd $_nice
font create font_diffitalic
foreach class {Button Checkbutton Entry Label
- Labelframe Listbox Menu Message
+ Labelframe Listbox Message
Radiobutton Spinbox Text} {
option add *$class.font font_ui
}
+if {![is_MacOSX]} {
+ option add *Menu.font font_ui
+}
unset class
if {[is_Windows] || [is_MacOSX]} {
set default_config(branch.autosetupmerge) true
set default_config(merge.tool) {}
-set default_config(merge.keepbackup) true
+set default_config(mergetool.keepbackup) true
set default_config(merge.diffstat) true
set default_config(merge.summary) false
set default_config(merge.verbosity) 2
set default_config(gui.spellingdictionary) {}
set default_config(gui.fontui) [font configure font_ui]
set default_config(gui.fontdiff) [font configure font_diff]
+# TODO: this option should be added to the git-config documentation
+set default_config(gui.maxfilesdisplayed) 5000
set font_descs {
{fontui font_ui {mc "Main Font"}}
{fontdiff font_diff {mc "Diff/Console Font"}}
set _real_git_version $_git_version
regsub -- {[\-\.]dirty$} $_git_version {} _git_version
regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.rc[0-9]+$} $_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
+regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
catch {wm withdraw .}
}
proc load_config {include_global} {
- global repo_config global_config default_config
+ global repo_config global_config system_config default_config
if {$include_global} {
+ _parse_config system_config --system
_parse_config global_config --global
}
_parse_config repo_config
foreach name [array names default_config] {
+ if {[catch {set v $system_config($name)}]} {
+ set system_config($name) $default_config($name)
+ }
+ }
+ foreach name [array names system_config] {
if {[catch {set v $global_config($name)}]} {
- set global_config($name) $default_config($name)
+ set global_config($name) $system_config($name)
}
if {[catch {set v $repo_config($name)}]} {
- set repo_config($name) $default_config($name)
+ set repo_config($name) $system_config($name)
}
}
}
set _prefix {}
}]
&& [catch {
+ # beware that from the .git dir this sets _gitdir to .
+ # and _prefix to the empty string
set _gitdir [git rev-parse --git-dir]
set _prefix [git rev-parse --show-prefix]
} err]} {
choose_repository::pick
set picked 1
}
+
+# we expand the _gitdir when it's just a single dot (i.e. when we're being
+# run from the .git dir itself) lest the routines to find the worktree
+# get confused
+if {$_gitdir eq "."} {
+ set _gitdir [pwd]
+}
+
if {![file isdirectory $_gitdir] && [is_Cygwin]} {
catch {set _gitdir [exec cygpath --windows $_gitdir]}
}
set is_detached 0
set current_diff_path {}
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 nullid "0000000000000000000000000000000000000000"
set nullid2 "0000000000000000000000000000000000000001"
prune_selection
unlock_index
display_all_files
- if {$current_diff_path ne {}} reshow_diff
- if {$current_diff_path eq {}} select_first_diff
-
- uplevel #0 $after
+ if {$current_diff_path ne {}} { reshow_diff $after }
+ if {$current_diff_path eq {}} { select_first_diff $after }
}
proc prune_selection {} {
} elseif {$s0 ne {_} && [string index $state 0] eq {_}
&& $head_info eq {}} {
set head_info $index_info
+ } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
+ set index_info $head_info
+ set head_info {}
}
set file_states($path) [list $s0$s1 $icon \
$w insert end "[escape_path $path]\n"
}
+set files_warning 0
proc display_all_files {} {
global ui_index ui_workdir
global file_states file_lists
global last_clicked
+ global files_warning
$ui_index conf -state normal
$ui_workdir conf -state normal
set file_lists($ui_index) [list]
set file_lists($ui_workdir) [list]
- foreach path [lsort [array names file_states]] {
+ 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}]]
+ }
+ foreach path $to_display {
set s $file_states($path)
set m [lindex $s 0]
set icon_name [lindex $s 1]
cd [file dirname [gitdir]]
set env(GIT_DIR) [file tail [gitdir]]
- eval exec $cmd $revs &
+ eval exec $cmd $revs "--" "--" &
if {$old_GIT_DIR eq {}} {
unset env(GIT_DIR)
# freedesktop.org-conforming system is our best shot
set explorer "xdg-open"
}
- eval exec $explorer [file dirname [gitdir]] &
+ eval exec $explorer [list [file nativename [file dirname [gitdir]]]] &
}
set is_quitting 0
# -- Stash our current window geometry into this repository.
#
+ set cfg_wmstate [wm state .]
+ if {[catch {set rc_wmstate $repo_config(gui.wmstate)}]} {
+ set rc_wmstate {}
+ }
+ if {$cfg_wmstate ne $rc_wmstate} {
+ catch {git config gui.wmstate $cfg_wmstate}
+ }
+ if {$cfg_wmstate eq {zoomed}} {
+ # on Windows wm geometry will lie about window
+ # position (but not size) when window is zoomed
+ # restore the window before querying wm geometry
+ wm state . normal
+ }
set cfg_geometry [list]
lappend cfg_geometry [wm geometry .]
lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
}
set ret_code $rc
+
+ # Briefly enable send again, working around Tk bug
+ # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
+ tk appname [appname]
+
destroy .
}
}
proc ui_do_rescan {} {
- rescan {force_first_diff; ui_ready}
+ rescan {force_first_diff ui_ready}
}
proc do_commit {} {
commit_tree
}
-proc next_diff {} {
+proc next_diff {{after {}}} {
global next_diff_p next_diff_w next_diff_i
- show_diff $next_diff_p $next_diff_w {}
+ show_diff $next_diff_p $next_diff_w {} {} $after
}
proc find_anchor_pos {lst name} {
}
}
-proc select_first_diff {} {
+proc select_first_diff {after} {
global ui_workdir
if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
[find_next_diff $ui_workdir {} 1 {[^O]$}]} {
- next_diff
+ next_diff $after
+ } else {
+ uplevel #0 $after
}
}
-proc force_first_diff {} {
- global current_diff_path
+proc force_first_diff {after} {
+ global ui_workdir current_diff_path file_states
if {[info exists file_states($current_diff_path)]} {
set state [lindex $file_states($current_diff_path) 0]
+ } else {
+ set state {OO}
+ }
- if {[string index $state 1] ne {O}} return
+ set reselect 0
+ if {[string first {U} $state] >= 0} {
+ # Already a conflict, do nothing
+ } elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
+ set reselect 1
+ } elseif {[string index $state 1] ne {O}} {
+ # Already a diff & no conflicts, do nothing
+ } elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
+ set reselect 1
}
- select_first_diff
+ if {$reselect} {
+ next_diff $after
+ } else {
+ uplevel #0 $after
+ }
}
proc toggle_or_diff {w x y} {
# -- Menu Bar
#
menu .mbar -tearoff 0
+if {[is_MacOSX]} {
+ # -- Apple Menu (Mac OS X only)
+ #
+ .mbar add cascade -label Apple -menu .mbar.apple
+ menu .mbar.apple
+}
.mbar add cascade -label [mc Repository] -menu .mbar.repository
.mbar add cascade -label [mc Edit] -menu .mbar.edit
if {[is_enabled branch]} {
.mbar add cascade -label [mc Merge] -menu .mbar.merge
.mbar add cascade -label [mc Remote] -menu .mbar.remote
}
-. configure -menu .mbar
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+ .mbar add cascade -label [mc Tools] -menu .mbar.tools
+}
# -- Repository Menu
#
[list .mbar.commit entryconf [.mbar.commit index last] -state]
.mbar.commit add command -label [mc "Unstage From Commit"] \
- -command do_unstage_selection
+ -command do_unstage_selection \
+ -accelerator $M1T-U
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
.mbar.commit add command -label [mc "Revert Changes"] \
- -command do_revert_selection
+ -command do_revert_selection \
+ -accelerator $M1T-J
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
}
if {[is_MacOSX]} {
- # -- Apple Menu (Mac OS X only)
- #
- .mbar add cascade -label Apple -menu .mbar.apple
- menu .mbar.apple
-
- .mbar.apple add command -label [mc "About %s" [appname]] \
- -command do_about
- .mbar.apple add separator
- .mbar.apple add command \
- -label [mc "Preferences..."] \
- -command do_options \
- -accelerator $M1T-,
- bind . <$M1B-,> do_options
+ proc ::tk::mac::ShowPreferences {} {do_options}
} else {
# -- Edit Menu
#
-command do_options
}
+# -- Tools Menu
+#
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+ set tools_menubar .mbar.tools
+ menu $tools_menubar
+ $tools_menubar add separator
+ $tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
+ $tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
+ set tools_tailcnt 3
+ if {[array names repo_config guitool.*.cmd] ne {}} {
+ tools_populate_all
+ }
+}
+
# -- Help Menu
#
.mbar add cascade -label [mc Help] -menu .mbar.help
menu .mbar.help
-if {![is_MacOSX]} {
+if {[is_MacOSX]} {
+ .mbar.apple add command -label [mc "About %s" [appname]] \
+ -command do_about
+ .mbar.apple add separator
+} else {
.mbar.help add command -label [mc "About %s" [appname]] \
-command do_about
}
+. configure -menu .mbar
+set doc_path [githtmldir]
+if {$doc_path ne {}} {
+ set doc_path [file join $doc_path index.html]
-set doc_path [file dirname [gitexec]]
-set doc_path [file join $doc_path Documentation index.html]
-
-if {[is_Cygwin]} {
- set doc_path [exec cygpath --mixed $doc_path]
+ if {[is_Cygwin]} {
+ set doc_path [exec cygpath --mixed $doc_path]
+ }
}
if {[file isfile $doc_path]} {
exit 1
}
+proc normalize_relpath {path} {
+ set elements {}
+ foreach item [file split $path] {
+ if {$item eq {.}} continue
+ if {$item eq {..} && [llength $elements] > 0
+ && [lindex $elements end] ne {..}} {
+ set elements [lrange $elements 0 end-1]
+ continue
+ }
+ lappend elements $item
+ }
+ return [eval file join $elements]
+}
+
# -- Not a normal commit type invocation? Do that instead!
#
switch -- $subcommand {
foreach a $argv {
if {$is_path || [file exists $_prefix$a]} {
if {$path ne {}} usage
- set path $_prefix$a
+ set path [normalize_relpath $_prefix$a]
break
} elseif {$a eq {--}} {
if {$path ne {}} {
unset is_path
if {$head ne {} && $path eq {}} {
- set path $_prefix$head
+ set path [normalize_relpath $_prefix$head]
set head {}
}
-command {tk_textPaste $ui_comm}
$ctxm add command \
-label [mc Delete] \
- -command {$ui_comm delete sel.first sel.last}
+ -command {catch {$ui_comm delete sel.first sel.last}}
$ctxm add separator
$ctxm add command \
-label [mc "Select All"] \
set ui_diff .vpane.lower.diff.body.t
text $ui_diff -background white -foreground black \
-borderwidth 0 \
- -width 80 -height 15 -wrap none \
+ -width 80 -height 5 -wrap none \
-font font_diff \
-xscrollcommand {.vpane.lower.diff.body.sbx set} \
-yscrollcommand {.vpane.lower.diff.body.sby set} \
set l [mc "Stage Hunk For Commit"]
set t [mc "Stage Line For Commit"]
}
- if {$::is_3way_diff
+ if {$::is_3way_diff || $::is_submodule_diff
|| $current_diff_path eq {}
|| {__} eq $state
|| {_O} eq $state
unset gm
}
+# -- Load window state
+#
+catch {
+set gws $repo_config(gui.wmstate)
+wm state . $gws
+unset gws
+}
+
# -- Key Bindings
#
bind $ui_comm <$M1B-Key-Return> {do_commit;break}
bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
+bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
+bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
+bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
bind $ui_comm <$M1B-Key-i> {do_add_all;break}
bind $ui_comm <$M1B-Key-I> {do_add_all;break}
bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
bind . <$M1B-Key-S> do_signoff
bind . <$M1B-Key-t> do_add_selection
bind . <$M1B-Key-T> do_add_selection
+bind . <$M1B-Key-j> do_revert_selection
+bind . <$M1B-Key-J> do_revert_selection
bind . <$M1B-Key-i> do_add_all
bind . <$M1B-Key-I> do_add_all
bind . <$M1B-Key-minus> {show_less_context;break}
{^GIT_PAGER$} -
{^GIT_TRACE$} -
{^GIT_CONFIG$} -
- {^GIT_CONFIG_LOCAL$} -
{^GIT_(AUTHOR|COMMITTER)_DATE$} {
append msg " - $name\n"
incr ignored_env