From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
This series adds a customizable Tools menu, that can
be used to call any external commands from Git Gui.
It reduces the inconvenience of using tools like git-svn
with GUI, by removing the need to jump between the
terminal and the GUI even for simple actions. QGit
already has a similar feature.
UPDATES:
1) Tweaked some of the text strings to make UI look better.
2) Added auto-rescan functionality.
Sorry for sending an incomplete WIP version 3 days ago.
SUMMARY:
git-gui.sh | 70 +++++++--
lib/diff.tcl | 6 +-
lib/option.tcl | 12 +-
lib/tools.tcl | 159 ++++++++++++++++++++
lib/tools_dlg.tcl | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 643 insertions(+), 25 deletions(-)
PATCHES:
git-gui: Implement system-wide configuration handling.
---
git-gui.sh | 12 +++++++++---
lib/option.tcl | 12 ++++++------
2 files changed, 15 insertions(+), 9 deletions(-)
(NEW) git-gui: Fix the after callback execution in rescan.
---
git-gui.sh | 41 ++++++++++++++++++++++++++++-------------
lib/diff.tcl | 6 +++---
2 files changed, 31 insertions(+), 16 deletions(-)
git-gui: Add a Tools menu for arbitrary commands.
---
git-gui.sh | 17 ++++
lib/tools.tcl | 108 ++++++++++++++++++++++++
lib/tools_dlg.tcl | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 359 insertions(+), 0 deletions(-)
create mode 100644 lib/tools.tcl
create mode 100644 lib/tools_dlg.tcl
git-gui: Allow Tools request arguments from the user.
---
lib/tools.tcl | 13 +++-
lib/tools_dlg.tcl | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 203 insertions(+), 5 deletions(-)
(NEW) git-gui: Implement automatic rescan after Tool execution.
---
lib/tools.tcl | 44 ++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 42 insertions(+), 2 deletions(-)
From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
With the old implementation any system-wide options appear
to be set locally in the current repository. This commit
adds explicit handling of system options, essentially
interpreting them as customized default_config.
The difficulty in interpreting system options stems from
the fact that simple 'git config' lists all values, while
'git config --global' only values set in ~/.gitconfig,
excluding both local and system options.
Signed-off-by: Alexander Gavrilov <redacted>
---
git-gui.sh | 12 +++++++++---
lib/option.tcl | 12 ++++++------
2 files changed, 15 insertions(+), 9 deletions(-)
From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
The rescan function receives a callback command
as its parameter, which is supposed to be executed
after the scan finishes. It is generally used to
update status. However, rescan may initiate a
loading of a diff, which always calls ui_ready after
completion. If the after handler is called before
that, ui_ready will override the new status.
This commit ensures that the after callback is
properly threaded through the diff machinery.
Since it uncovered the fact that force_first_diff
actually didn't work due to an undeclared global
variable, and the desired effects appeared only
because of the race condition between the diff
system and the rescan callback, I also reimplement
this function to make it behave as originally
intended.
Signed-off-by: Alexander Gavrilov <redacted>
---
git-gui.sh | 41 ++++++++++++++++++++++++++++-------------
lib/diff.tcl | 6 +++---
2 files changed, 31 insertions(+), 16 deletions(-)
From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
While static commands are already useful, some tools need
additional parameters to reach maximum usability. This
commit adds support for passing them one revision name
parameter, and one arbitrary string. With this addition,
the tools menu becomes flexible enough to implement basic
rebase support:
[core]
editor = kwrite
[guitool "Rebase/Abort"]
cmd = git rebase --abort
confirm = yes
[guitool "Rebase/Continue"]
cmd = git rebase --continue
[guitool "Rebase/Skip Commit"]
cmd = git rebase --skip
confirm = yes
[guitool "Rebase/Start..."]
cmd = git rebase $ARGS $REVISION $CUR_BRANCH
title = Start Rebase
prompt = Rebase Current Branch
argprompt = Flags
revprompt = New Base
revunmerged = yes
Some of the options, like title or prompt, are intentionally
not included in the Add dialog to avoid clutter. Also, the
dialog handles argprompt and revprompt as boolean vars.
Signed-off-by: Alexander Gavrilov <redacted>
---
lib/tools.tcl | 13 +++-
lib/tools_dlg.tcl | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 203 insertions(+), 5 deletions(-)
@@ -77,7 +77,16 @@ proc tools_exec {fullname} {}}-if{[is_config_true"guitool.$fullname.confirm"]}{+catch{unsetenv(ARGS)}+catch{unsetenv(REVISION)}++if{[get_config"guitool.$fullname.revprompt"]ne{}||+[get_config"guitool.$fullname.argprompt"]ne{}}{+setdlg[tools_askdlg::dialog$fullname]+if{![tools_askdlg::execute$dlg]}{+return+}+}elseif{[is_config_true"guitool.$fullname.confirm"]}{if{[ask_popup[mc"Are you sure you want to run %s?"$fullname]]ne{yes}}{return}
@@ -12,6 +12,8 @@ field add_global 0; # add to the --global configfieldno_console0;# disable using the consolefieldneeds_file0;# ensure filename is setfieldconfirm0;# ask for confirmation+fieldask_branch0;# ask for a revision+fieldask_args0;# ask for additional argsconstructordialog{}{globalrepo_config
@@ -69,9 +71,22 @@ constructor dialog {} {pack$w.desc-anchornw-fillx-pady5-padx5checkbutton$w.confirm\--text[mc"Ask for confirmation before running"]\--variable@confirm-pack$w.confirm-anchorw-pady{50}-padx5+-text[mc"Show a dialog before running"]\+-variable@confirm-command[cb_check_enable_dlg]++labelframe$w.dlg-labelwidget$w.confirm++checkbutton$w.dlg.askbranch\+-text[mc"Ask the user to select a revision (sets \$REVISION)"]\+-variable@ask_branch-statedisabled+pack$w.dlg.askbranch-anchorw-padx15++checkbutton$w.dlg.askargs\+-text[mc"Ask the user for additional arguments (sets \$ARGS)"]\+-variable@ask_args-statedisabled+pack$w.dlg.askargs-anchorw-padx15++pack$w.dlg-anchornw-fillx-pady{08}-padx5checkbutton$w.noconsole\-text[mc"Don't show the command output window"]\
From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
Due to the emphasis on scriptability in the git
design, it is impossible to provide 100% complete
GUI. Currently unaccounted areas include git-svn
and other source control system interfaces, TopGit,
all custom scripts.
This problem can be mitigated by providing basic
customization capabilities in Git Gui. This commit
adds a new Tools menu, which can be configured
to contain items invoking arbitrary shell commands.
The interface is powerful enough to allow calling
both batch text programs like git-svn, and GUI editors.
To support the latter use, the commands have access
to the name of the currently selected file through
the environment.
Signed-off-by: Alexander Gavrilov <redacted>
---
git-gui.sh | 17 ++++
lib/tools.tcl | 108 ++++++++++++++++++++++++
lib/tools_dlg.tcl | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 359 insertions(+), 0 deletions(-)
create mode 100644 lib/tools.tcl
create mode 100644 lib/tools_dlg.tcl
@@ -0,0 +1,108 @@+# git-gui Tools menu implementation++proctools_list{}{+globalrepo_config++setnames{}+foreachitem[arraynamesrepo_configguitool.*.cmd]{+lappendnames[stringrange$item8end-4]+}+return[lsort$names]+}++proctools_populate_all{}{+globaltools_menubartools_menutbl+globaltools_tailcnt++setmbar_end[$tools_menubarindexend]+setmbar_base[expr{$mbar_end-$tools_tailcnt}]+if{$mbar_base>=0}{+$tools_menubardelete0$mbar_base+}++arrayunsettools_menutbl++foreachfullname[tools_list]{+tools_populate_one$fullname+}+}++proctools_create_item{parentargs}{+globaltools_menubartools_tailcnt+if{$parenteq$tools_menubar}{+setpos[expr{[$parentindexend]-$tools_tailcnt+1}]+eval[list$parentinsert$pos]$args+}else{+eval[list$parentadd]$args+}+}++proctools_populate_one{fullname}{+globaltools_menubartools_menutbltools_id++if{![infoexiststools_id]}{+settools_id0+}++setnames[split$fullname'/']+setparent$tools_menubar+for{seti0}{$i<[llength$names]-1}{incri}{+setsubname[join[lrange$names0$i]'/']+if{[infoexiststools_menutbl($subname)]}{+setparent$tools_menutbl($subname)+}else{+setsubid$parent.t$tools_id+tools_create_item$parentcascade\+-label[lindex$names$i]-menu$subid+menu$subid+settools_menutbl($subname)$subid+setparent$subid+incrtools_id+}+}++tools_create_item$parentcommand\+-label[lindex$namesend]\+-command[listtools_exec$fullname]+}++proctools_exec{fullname}{+globalrepo_configenvcurrent_diff_path+globalcurrent_branchis_detached++if{[is_config_true"guitool.$fullname.needsfile"]}{+if{$current_diff_patheq{}}{+error_popup[mc"Running %s requires a selected file."$fullname]+return+}+}++if{[is_config_true"guitool.$fullname.confirm"]}{+if{[ask_popup[mc"Are you sure you want to run %s?"$fullname]]ne{yes}}{+return+}+}++setenv(GIT_GUITOOL)$fullname+setenv(FILENAME)$current_diff_path+if{$is_detached}{+setenv(CUR_BRANCH)""+}else{+setenv(CUR_BRANCH)$current_branch+}++setcmdline$repo_config(guitool.$fullname.cmd)+if{[is_config_true"guitool.$fullname.noconsole"]}{+execsh-c$cmdline&+}else{+regsub{/}$fullname{/}title+setw[console::new\+[mc"Tool: %s"$title]\+[mc"Running: %s"$cmdline]]+console::exec$w[listsh-c$cmdline]+}++unsetenv(GIT_GUITOOL)+unsetenv(FILENAME)+unsetenv(CUR_BRANCH)+}
@@ -0,0 +1,234 @@+# git-gui Tools menu dialogs++classtools_add{++fieldw;# widget path+fieldw_name;# new remote name widget+fieldw_cmd;# new remote location widget++fieldname{};# name of the tool+fieldcommand{};# command to execute+fieldadd_global0;# add to the --global config+fieldno_console0;# disable using the console+fieldneeds_file0;# ensure filename is set+fieldconfirm0;# ask for confirmation++constructordialog{}{+globalrepo_config++make_topleveltopw+wmtitle$top[append"[appname] ([reponame]): "[mc"Add Tool"]]+if{$topne{.}}{+wmgeometry$top"+[winfo rootx .]+[winfo rooty .]"+wmtransient$top.+}++label$w.header-text[mc"Add New Tool Command"]-fontfont_uibold+pack$w.header-sidetop-fillx++frame$w.buttons+checkbutton$w.buttons.global\+-text[mc"Add globally"]\+-variable@add_global+pack$w.buttons.global-sideleft-padx5+button$w.buttons.create-text[mcAdd]\+-defaultactive\+-command[cb_add]+pack$w.buttons.create-sideright+button$w.buttons.cancel-text[mcCancel]\+-command[listdestroy$w]+pack$w.buttons.cancel-sideright-padx5+pack$w.buttons-sidebottom-fillx-pady10-padx10++labelframe$w.desc-text[mc"Tool Details"]++label$w.desc.name_cmnt-anchorw\+-text[mc"Use '/' separators to create a submenu tree:"]+gridx$w.desc.name_cmnt-stickywe-padx{05}-pady{02}+label$w.desc.name_l-text[mc"Name:"]+setw_name$w.desc.name_t+entry$w_name\+-borderwidth1\+-reliefsunken\+-width40\+-textvariable@name\+-validatekey\+-validatecommand[cb_validate_name%d%S]+grid$w.desc.name_l$w_name-stickywe-padx{05}++label$w.desc.cmd_l-text[mc"Command:"]+setw_cmd$w.desc.cmd_t+entry$w_cmd\+-borderwidth1\+-reliefsunken\+-width40\+-textvariable@command+grid$w.desc.cmd_l$w_cmd-stickywe-padx{05}-pady{03}++gridcolumnconfigure$w.desc1-weight1+pack$w.desc-anchornw-fillx-pady5-padx5++checkbutton$w.confirm\+-text[mc"Ask for confirmation before running"]\+-variable@confirm+pack$w.confirm-anchorw-pady{50}-padx5++checkbutton$w.noconsole\+-text[mc"Don't show the command output window"]\+-variable@no_console+pack$w.noconsole-anchorw-padx5++checkbutton$w.needsfile\+-text[mc"Run only if a diff is selected (\$FILENAME not empty)"]\+-variable@needs_file+pack$w.needsfile-anchorw-padx5++bind$w<Visibility>[cb_visible]+bind$w<Key-Escape>[listdestroy$w]+bind$w<Key-Return>[cb_add]\;break+tkwaitwindow$w+}++method_add{}{+globalrepo_config++if{$nameeq{}}{+error_popup[mc"Please supply a name for the tool."]+focus$w_name+return+}++setitem"guitool.$name.cmd"++if{[infoexistsrepo_config($item)]}{+error_popup[mc"Tool '%s' already exists."$name]+focus$w_name+return+}++setcmd[listgitconfig]+if{$add_global}{lappendcmd--global}+setitems{}+if{$no_console}{lappenditems"guitool.$name.noconsole"}+if{$confirm}{lappenditems"guitool.$name.confirm"}+if{$needs_file}{lappenditems"guitool.$name.needsfile"}++if{[catch{+eval$cmd[list$item$command]+foreachcitem$items{eval$cmd[list$citemyes]}+}err]}{+error_popup[mc"Could not add tool:\n%s"$err]+}else{+setrepo_config($item)$command+foreachcitem$items{setrepo_config($citem)yes}++tools_populate_all+}++destroy$w+}++method_validate_name{dS}{+if{$d==1}{+if{[regexp{[~?*&\[\0\"\\\{]} $S]} {+return0+}+}+return1+}++method_visible{}{+grab$w+$w_nameicursorend+focus$w_name+}++}++classtools_remove{++fieldw;# widget path+fieldw_names;# name list++constructordialog{}{+globalrepo_configglobal_configsystem_config++load_config1++make_topleveltopw+wmtitle$top[append"[appname] ([reponame]): "[mc"Remove Tool"]]+if{$topne{.}}{+wmgeometry$top"+[winfo rootx .]+[winfo rooty .]"+wmtransient$top.+}++label$w.header-text[mc"Remove Tool Commands"]-fontfont_uibold+pack$w.header-sidetop-fillx++frame$w.buttons+button$w.buttons.create-text[mcRemove]\+-defaultactive\+-command[cb_remove]+pack$w.buttons.create-sideright+button$w.buttons.cancel-text[mcCancel]\+-command[listdestroy$w]+pack$w.buttons.cancel-sideright-padx5+pack$w.buttons-sidebottom-fillx-pady10-padx10++frame$w.list+setw_names$w.list.l+listbox$w_names\+-height10\+-width30\+-selectmodeextended\+-exportselectionfalse\+-yscrollcommand[list$w.list.sbyset]+scrollbar$w.list.sby-command[list$w.list.lyview]+pack$w.list.sby-sideright-filly+pack$w.list.l-sideleft-fillboth-expand1+pack$w.list-fillboth-expand1-pady5-padx5++setlocal_cnt0+foreachfullname[tools_list]{+# Cannot delete system tools+if{[infoexistssystem_config(guitool.$fullname.cmd)]}continue++$w_namesinsertend$fullname+if{![infoexistsglobal_config(guitool.$fullname.cmd)]}{+$w_namesitemconfigureend-foregroundblue+incrlocal_cnt+}+}++if{$local_cnt>0}{+label$w.colorlbl-foregroundblue\+-text[mc"(Blue denotes repository-local tools)"]+pack$w.colorlbl-fillx-pady5-padx5+}++bind$w<Visibility>[cb_visible]+bind$w<Key-Escape>[listdestroy$w]+bind$w<Key-Return>[cb_remove]\;break+tkwaitwindow$w+}++method_remove{}{+foreachi[$w_namescurselection]{+setname[$w_namesget$i]++catch{gitconfig--remove-sectionguitool.$name}+catch{gitconfig--global--remove-sectionguitool.$name}+}++load_config0+tools_populate_all++destroy$w+}++method_visible{}{+grab$w+focus$w_names+}++}
From: Alexander Gavrilov <hidden> Date: 2016-06-15 22:45:39
The Tools menu is generally intended for commands that
affect the working directory or repository state. Thus,
the user would usually want to initiate rescan after
execution of a tool. This commit implements it.
In case somebody would want to avoid rescanning after
certain tools, it also adds an option that controls it,
although it is not made available through the Add dialog.
Signed-off-by: Alexander Gavrilov <redacted>
---
lib/tools.tcl | 44 ++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 42 insertions(+), 2 deletions(-)
From: Shawn O. Pearce <hidden> Date: 2016-06-15 22:45:39
Alexander Gavrilov [off-list ref] wrote:
This series adds a customizable Tools menu, that can
be used to call any external commands from Git Gui.
It reduces the inconvenience of using tools like git-svn
with GUI, by removing the need to jump between the
terminal and the GUI even for simple actions. QGit
already has a similar feature.
Thanks. This is really slick. Its in my tree now.
Given that 1.6.1 is entering rc mode, I'm probably not going to be
accepting any more new features into my master branch until 1.6.1
freezes. Everything after this will wind up in `pu` until I cut
git-gui 0.12.0.
--
Shawn.