threads / patch / 16355

v2, 5 partsAdd a customizable Tools menu.

Subject: [PATCH (GIT-GUI) v2 0/5] Add a customizable Tools menu.

## tl;dr

7 messages between Nov 16, 2008 and Nov 16, 2008. Diffs are folded; open one to read it.

replies: 6people: 2as markdown or json

Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · lore

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(-)
Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · re: Alexander Gavrilov · lore

[PATCH (GIT-GUI) v2 1/5] git-gui: Implement system-wide configuration handling.

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 <angavrilov@gmail.com>
---
 git-gui.sh     |   12 +++++++++---
 lib/option.tcl |   12 ++++++------
 2 files changed, 15 insertions(+), 9 deletions(-)
Show changes to 2 files +15 −9

git-gui.sh, lib/option.tcl

diff --git a/git-gui.sh b/git-gui.sh
index cf9ef6e..34214b6 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -918,19 +918,25 @@ git-version proc _parse_config {arr_name args} {
 }
 
 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)
 		}
 	}
 }
diff --git a/lib/option.tcl b/lib/option.tcl
index c80c939..1d55b49 100644
--- a/lib/option.tcl
+++ b/lib/option.tcl
@@ -25,7 +25,7 @@ proc config_check_encodings {} {
 
 proc save_config {} {
 	global default_config font_descs
-	global repo_config global_config
+	global repo_config global_config system_config
 	global repo_config_new global_config_new
 	global ui_comm_spell
 
@@ -49,7 +49,7 @@ proc save_config {} {
 	foreach name [array names default_config] {
 		set value $global_config_new($name)
 		if {$value ne $global_config($name)} {
-			if {$value eq $default_config($name)} {
+			if {$value eq $system_config($name)} {
 				catch {git config --global --unset $name}
 			} else {
 				regsub -all "\[{}\]" $value {"} value
@@ -284,17 +284,17 @@ proc do_options {} {
 }
 
 proc do_restore_defaults {} {
-	global font_descs default_config repo_config
+	global font_descs default_config repo_config system_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)
+		set repo_config_new($name) $system_config($name)
+		set global_config_new($name) $system_config($name)
 	}
 
 	foreach option $font_descs {
 		set name [lindex $option 0]
-		set repo_config(gui.$name) $default_config(gui.$name)
+		set repo_config(gui.$name) $system_config(gui.$name)
 	}
 	apply_config
 
-- 
1.6.0.3.15.gb8d36
Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · re: Alexander Gavrilov · lore

[PATCH (GIT-GUI) v2 2/5] git-gui: Fix the after callback execution in rescan.

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 <angavrilov@gmail.com>
---
 git-gui.sh   |   41 ++++++++++++++++++++++++++++-------------
 lib/diff.tcl |    6 +++---
 2 files changed, 31 insertions(+), 16 deletions(-)
Show changes to 2 files +31 −16

git-gui.sh, lib/diff.tcl

diff --git a/git-gui.sh b/git-gui.sh
index 34214b6..2709f6e 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1469,10 +1469,8 @@ proc rescan_done {fd buf after} {
 	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 {} {
@@ -1984,16 +1982,16 @@ proc do_rescan {} {
 }
 
 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} {
@@ -2078,25 +2076,42 @@ proc next_diff_after_action {w path {lno {}} {mmask {}}} {
 	}
 }
 
-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} {
diff --git a/lib/diff.tcl b/lib/diff.tcl
index 94ee38c..bbbf15c 100644
--- a/lib/diff.tcl
+++ b/lib/diff.tcl
@@ -16,7 +16,7 @@ proc clear_diff {} {
 	$ui_workdir tag remove in_diff 0.0 end
 }
 
-proc reshow_diff {} {
+proc reshow_diff {{after {}}} {
 	global file_states file_lists
 	global current_diff_path current_diff_side
 	global ui_diff
@@ -30,13 +30,13 @@ proc reshow_diff {} {
 		|| [lsearch -sorted -exact $file_lists($current_diff_side) $p] == -1} {
 
 		if {[find_next_diff $current_diff_side $p {} {[^O]}]} {
-			next_diff
+			next_diff $after
 		} else {
 			clear_diff
 		}
 	} else {
 		set save_pos [lindex [$ui_diff yview] 0]
-		show_diff $p $current_diff_side {} $save_pos
+		show_diff $p $current_diff_side {} $save_pos $after
 	}
 }
 
-- 
1.6.0.3.15.gb8d36
Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · re: Alexander Gavrilov · lore

[PATCH (GIT-GUI) v2 3/5] git-gui: Add a Tools menu for arbitrary commands.

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 <angavrilov@gmail.com>
---
 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
Show changes to 3 files +359 −0

git-gui.sh, lib/tools.tcl, lib/tools_dlg.tcl

diff --git a/git-gui.sh b/git-gui.sh
index 2709f6e..0751211 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -2267,6 +2267,9 @@ if {[is_enabled transport]} {
 	.mbar add cascade -label [mc Merge] -menu .mbar.merge
 	.mbar add cascade -label [mc Remote] -menu .mbar.remote
 }
+if {[is_enabled multicommit] || [is_enabled singlecommit]} {
+	.mbar add cascade -label [mc Tools] -menu .mbar.tools
+}
 . configure -menu .mbar
 
 # -- Repository Menu
@@ -2541,6 +2544,20 @@ if {[is_MacOSX]} {
 		-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
diff --git a/lib/tools.tcl b/lib/tools.tcl
new file mode 100644
index 0000000..00d46dd
--- /dev/null
+++ b/lib/tools.tcl
@@ -0,0 +1,108 @@
+# git-gui Tools menu implementation
+
+proc tools_list {} {
+	global repo_config
+
+	set names {}
+	foreach item [array names repo_config guitool.*.cmd] {
+		lappend names [string range $item 8 end-4]
+	}
+	return [lsort $names]
+}
+
+proc tools_populate_all {} {
+	global tools_menubar tools_menutbl
+	global tools_tailcnt
+
+	set mbar_end [$tools_menubar index end]
+	set mbar_base [expr {$mbar_end - $tools_tailcnt}]
+	if {$mbar_base >= 0} {
+		$tools_menubar delete 0 $mbar_base
+	}
+
+	array unset tools_menutbl
+
+	foreach fullname [tools_list] {
+		tools_populate_one $fullname
+	}
+}
+
+proc tools_create_item {parent args} {
+	global tools_menubar tools_tailcnt
+	if {$parent eq $tools_menubar} {
+		set pos [expr {[$parent index end]-$tools_tailcnt+1}]
+		eval [list $parent insert $pos] $args
+	} else {
+		eval [list $parent add] $args
+	}
+}
+
+proc tools_populate_one {fullname} {
+	global tools_menubar tools_menutbl tools_id
+
+	if {![info exists tools_id]} {
+		set tools_id 0
+	}
+
+	set names [split $fullname '/']
+	set parent $tools_menubar
+	for {set i 0} {$i < [llength $names]-1} {incr i} {
+		set subname [join [lrange $names 0 $i] '/']
+		if {[info exists tools_menutbl($subname)]} {
+			set parent $tools_menutbl($subname)
+		} else {
+			set subid $parent.t$tools_id
+			tools_create_item $parent cascade \
+					-label [lindex $names $i] -menu $subid
+			menu $subid
+			set tools_menutbl($subname) $subid
+			set parent $subid
+			incr tools_id
+		}
+	}
+
+	tools_create_item $parent command \
+		-label [lindex $names end] \
+		-command [list tools_exec $fullname]
+}
+
+proc tools_exec {fullname} {
+	global repo_config env current_diff_path
+	global current_branch is_detached
+
+	if {[is_config_true "guitool.$fullname.needsfile"]} {
+		if {$current_diff_path eq {}} {
+			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
+		}
+	}
+
+	set env(GIT_GUITOOL) $fullname
+	set env(FILENAME) $current_diff_path
+	if {$is_detached} {
+		set env(CUR_BRANCH) ""
+	} else {
+		set env(CUR_BRANCH) $current_branch
+	}
+
+	set cmdline $repo_config(guitool.$fullname.cmd)
+	if {[is_config_true "guitool.$fullname.noconsole"]} {
+		exec sh -c $cmdline &
+	} else {
+		regsub {/} $fullname { / } title
+		set w [console::new \
+			[mc "Tool: %s" $title] \
+			[mc "Running: %s" $cmdline]]
+		console::exec $w [list sh -c $cmdline]
+	}
+
+	unset env(GIT_GUITOOL)
+	unset env(FILENAME)
+	unset env(CUR_BRANCH)
+}
diff --git a/lib/tools_dlg.tcl b/lib/tools_dlg.tcl
new file mode 100644
index 0000000..c221ba9
--- /dev/null
+++ b/lib/tools_dlg.tcl
@@ -0,0 +1,234 @@
+# git-gui Tools menu dialogs
+
+class tools_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_cmd          ; # new remote location widget
+
+field name         {}; # name of the tool
+field command      {}; # command to execute
+field add_global    0; # add to the --global config
+field no_console    0; # disable using the console
+field needs_file    0; # ensure filename is set
+field confirm       0; # ask for confirmation
+
+constructor dialog {} {
+	global repo_config
+
+	make_toplevel top w
+	wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	label $w.header -text [mc "Add New Tool Command"] -font font_uibold
+	pack $w.header -side top -fill x
+
+	frame $w.buttons
+	checkbutton $w.buttons.global \
+		-text [mc "Add globally"] \
+		-variable @add_global
+	pack $w.buttons.global -side left -padx 5
+	button $w.buttons.create -text [mc Add] \
+		-default active \
+		-command [cb _add]
+	pack $w.buttons.create -side right
+	button $w.buttons.cancel -text [mc Cancel] \
+		-command [list destroy $w]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	labelframe $w.desc -text [mc "Tool Details"]
+
+	label $w.desc.name_cmnt -anchor w\
+		-text [mc "Use '/' separators to create a submenu tree:"]
+	grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2}
+	label $w.desc.name_l -text [mc "Name:"]
+	set w_name $w.desc.name_t
+	entry $w_name \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @name \
+		-validate key \
+		-validatecommand [cb _validate_name %d %S]
+	grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+	label $w.desc.cmd_l -text [mc "Command:"]
+	set w_cmd $w.desc.cmd_t
+	entry $w_cmd \
+		-borderwidth 1 \
+		-relief sunken \
+		-width 40 \
+		-textvariable @command
+	grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3}
+
+	grid columnconfigure $w.desc 1 -weight 1
+	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+	checkbutton $w.confirm \
+		-text [mc "Ask for confirmation before running"] \
+		-variable @confirm
+	pack $w.confirm -anchor w -pady {5 0} -padx 5
+
+	checkbutton $w.noconsole \
+		-text [mc "Don't show the command output window"] \
+		-variable @no_console
+	pack $w.noconsole -anchor w -padx 5
+
+	checkbutton $w.needsfile \
+		-text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \
+		-variable @needs_file
+	pack $w.needsfile -anchor w -padx 5
+
+	bind $w <Visibility> [cb _visible]
+	bind $w <Key-Escape> [list destroy $w]
+	bind $w <Key-Return> [cb _add]\;break
+	tkwait window $w
+}
+
+method _add {} {
+	global repo_config
+
+	if {$name eq {}} {
+		error_popup [mc "Please supply a name for the tool."]
+		focus $w_name
+		return
+	}
+
+	set item "guitool.$name.cmd"
+
+	if {[info exists repo_config($item)]} {
+		error_popup [mc "Tool '%s' already exists." $name]
+		focus $w_name
+		return
+	}
+
+	set cmd [list git config]
+	if {$add_global} { lappend cmd --global }
+	set items {}
+	if {$no_console} { lappend items "guitool.$name.noconsole" }
+	if {$confirm}    { lappend items "guitool.$name.confirm" }
+	if {$needs_file} { lappend items "guitool.$name.needsfile" }
+
+	if {[catch {
+		eval $cmd [list $item $command]
+		foreach citem $items { eval $cmd [list $citem yes] }
+	    } err]} {
+		error_popup [mc "Could not add tool:\n%s" $err]
+	} else {
+		set repo_config($item) $command
+		foreach citem $items { set repo_config($citem) yes }
+
+		tools_populate_all
+	}
+
+	destroy $w
+}
+
+method _validate_name {d S} {
+	if {$d == 1} {
+		if {[regexp {[~?*&\[\0\"\\\{]} $S]} {
+			return 0
+		}
+	}
+	return 1
+}
+
+method _visible {} {
+	grab $w
+	$w_name icursor end
+	focus $w_name
+}
+
+}
+
+class tools_remove {
+
+field w              ; # widget path
+field w_names        ; # name list
+
+constructor dialog {} {
+	global repo_config global_config system_config
+
+	load_config 1
+
+	make_toplevel top w
+	wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	label $w.header -text [mc "Remove Tool Commands"] -font font_uibold
+	pack $w.header -side top -fill x
+
+	frame $w.buttons
+	button $w.buttons.create -text [mc Remove] \
+		-default active \
+		-command [cb _remove]
+	pack $w.buttons.create -side right
+	button $w.buttons.cancel -text [mc Cancel] \
+		-command [list destroy $w]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	frame $w.list
+	set w_names $w.list.l
+	listbox $w_names \
+		-height 10 \
+		-width 30 \
+		-selectmode extended \
+		-exportselection false \
+		-yscrollcommand [list $w.list.sby set]
+	scrollbar $w.list.sby -command [list $w.list.l yview]
+	pack $w.list.sby -side right -fill y
+	pack $w.list.l -side left -fill both -expand 1
+	pack $w.list -fill both -expand 1 -pady 5 -padx 5
+
+	set local_cnt 0
+	foreach fullname [tools_list] {
+		# Cannot delete system tools
+		if {[info exists system_config(guitool.$fullname.cmd)]} continue
+
+		$w_names insert end $fullname
+		if {![info exists global_config(guitool.$fullname.cmd)]} {
+			$w_names itemconfigure end -foreground blue
+			incr local_cnt
+		}
+	}
+
+	if {$local_cnt > 0} {
+		label $w.colorlbl -foreground blue \
+			-text [mc "(Blue denotes repository-local tools)"]
+		pack $w.colorlbl -fill x -pady 5 -padx 5
+	}
+
+	bind $w <Visibility> [cb _visible]
+	bind $w <Key-Escape> [list destroy $w]
+	bind $w <Key-Return> [cb _remove]\;break
+	tkwait window $w
+}
+
+method _remove {} {
+	foreach i [$w_names curselection] {
+		set name [$w_names get $i]
+
+		catch { git config --remove-section guitool.$name }
+		catch { git config --global --remove-section guitool.$name }
+	}
+
+	load_config 0
+	tools_populate_all
+
+	destroy $w
+}
+
+method _visible {} {
+	grab $w
+	focus $w_names
+}
+
+}
-- 
1.6.0.3.15.gb8d36
Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · re: Alexander Gavrilov · lore

[PATCH (GIT-GUI) v2 4/5] git-gui: Allow Tools request arguments from the user.

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 <angavrilov@gmail.com>
---
 lib/tools.tcl     |   13 +++-
 lib/tools_dlg.tcl |  195 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 203 insertions(+), 5 deletions(-)
Show changes to 2 files +203 −5

lib/tools.tcl, lib/tools_dlg.tcl

diff --git a/lib/tools.tcl b/lib/tools.tcl
index 00d46dd..044432e 100644
--- a/lib/tools.tcl
+++ b/lib/tools.tcl
@@ -77,7 +77,16 @@ proc tools_exec {fullname} {
 		}
 	}
 
-	if {[is_config_true "guitool.$fullname.confirm"]} {
+	catch { unset env(ARGS) }
+	catch { unset env(REVISION) }
+
+	if {[get_config "guitool.$fullname.revprompt"] ne {} ||
+	    [get_config "guitool.$fullname.argprompt"] ne {}} {
+		set dlg [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
 		}
@@ -105,4 +114,6 @@ proc tools_exec {fullname} {
 	unset env(GIT_GUITOOL)
 	unset env(FILENAME)
 	unset env(CUR_BRANCH)
+	catch { unset env(ARGS) }
+	catch { unset env(REVISION) }
 }
diff --git a/lib/tools_dlg.tcl b/lib/tools_dlg.tcl
index c221ba9..5f7f08e 100644
--- a/lib/tools_dlg.tcl
+++ b/lib/tools_dlg.tcl
@@ -12,6 +12,8 @@ field add_global    0; # add to the --global config
 field no_console    0; # disable using the console
 field needs_file    0; # ensure filename is set
 field confirm       0; # ask for confirmation
+field ask_branch    0; # ask for a revision
+field ask_args      0; # ask for additional args
 
 constructor dialog {} {
 	global repo_config
@@ -69,9 +71,22 @@ constructor dialog {} {
 	pack $w.desc -anchor nw -fill x -pady 5 -padx 5
 
 	checkbutton $w.confirm \
-		-text [mc "Ask for confirmation before running"] \
-		-variable @confirm
-	pack $w.confirm -anchor w -pady {5 0} -padx 5
+		-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 -state disabled
+	pack $w.dlg.askbranch -anchor w -padx 15
+
+	checkbutton $w.dlg.askargs \
+		-text [mc "Ask the user for additional arguments (sets \$ARGS)"] \
+		-variable @ask_args -state disabled
+	pack $w.dlg.askargs -anchor w -padx 15
+
+	pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5
 
 	checkbutton $w.noconsole \
 		-text [mc "Don't show the command output window"] \
@@ -89,6 +104,16 @@ constructor dialog {} {
 	tkwait window $w
 }
 
+method _check_enable_dlg {} {
+	if {$confirm} {
+		$w.dlg.askbranch configure -state normal
+		$w.dlg.askargs configure -state normal
+	} else {
+		$w.dlg.askbranch configure -state disabled
+		$w.dlg.askargs configure -state disabled
+	}
+}
+
 method _add {} {
 	global repo_config
 
@@ -110,8 +135,14 @@ method _add {} {
 	if {$add_global} { lappend cmd --global }
 	set items {}
 	if {$no_console} { lappend items "guitool.$name.noconsole" }
-	if {$confirm}    { lappend items "guitool.$name.confirm" }
 	if {$needs_file} { lappend items "guitool.$name.needsfile" }
+	if {$confirm} {
+		if {$ask_args}   { lappend items "guitool.$name.argprompt" }
+		if {$ask_branch} { lappend items "guitool.$name.revprompt" }
+		if {!$ask_args && !$ask_branch} {
+			lappend items "guitool.$name.confirm"
+		}
+	}
 
 	if {[catch {
 		eval $cmd [list $item $command]
@@ -232,3 +263,159 @@ method _visible {} {
 }
 
 }
+
+class tools_askdlg {
+
+field w              ; # widget path
+field w_rev        {}; # revision browser
+field w_args       {}; # arguments
+
+field is_ask_args   0; # has arguments field
+field is_ask_revs   0; # has revision browser
+
+field is_ok         0; # ok to start
+field argstr       {}; # arguments
+
+constructor dialog {fullname} {
+	global M1B
+
+	set title [get_config "guitool.$fullname.title"]
+	if {$title eq {}} {
+		regsub {/} $fullname { / } title
+	}
+
+	make_toplevel top w -autodelete 0
+	wm title $top [append "[appname] ([reponame]): " $title]
+	if {$top ne {.}} {
+		wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+		wm transient $top .
+	}
+
+	set prompt [get_config "guitool.$fullname.prompt"]
+	if {$prompt eq {}} {
+		set command [get_config "guitool.$fullname.cmd"]
+		set prompt [mc "Run Command: %s" $command]
+	}
+
+	label $w.header -text $prompt -font font_uibold
+	pack $w.header -side top -fill x
+
+	set argprompt [get_config "guitool.$fullname.argprompt"]
+	set revprompt [get_config "guitool.$fullname.revprompt"]
+
+	set is_ask_args [expr {$argprompt ne {}}]
+	set is_ask_revs [expr {$revprompt ne {}}]
+
+	if {$is_ask_args} {
+		if {$argprompt eq {yes} || $argprompt eq {true} || $argprompt eq {1}} {
+			set argprompt [mc "Arguments"]
+		}
+
+		labelframe $w.arg -text $argprompt
+
+		set w_args $w.arg.txt
+		entry $w_args \
+			-borderwidth 1 \
+			-relief sunken \
+			-width 40 \
+			-textvariable @argstr
+		pack $w_args -padx 5 -pady 5 -fill both
+		pack $w.arg -anchor nw -fill both -pady 5 -padx 5
+	}
+
+	if {$is_ask_revs} {
+		if {$revprompt eq {yes} || $revprompt eq {true} || $revprompt eq {1}} {
+			set revprompt [mc "Revision"]
+		}
+
+		if {[is_config_true "guitool.$fullname.revunmerged"]} {
+			set w_rev [::choose_rev::new_unmerged $w.rev $revprompt]
+		} else {
+			set w_rev [::choose_rev::new $w.rev $revprompt]
+		}
+
+		pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5
+	}
+
+	frame $w.buttons
+	if {$is_ask_revs} {
+		button $w.buttons.visualize \
+			-text [mc Visualize] \
+			-command [cb _visualize]
+		pack $w.buttons.visualize -side left
+	}
+	button $w.buttons.ok \
+		-text [mc OK] \
+		-command [cb _start]
+	pack $w.buttons.ok -side right
+	button $w.buttons.cancel \
+		-text [mc "Cancel"] \
+		-command [cb _cancel]
+	pack $w.buttons.cancel -side right -padx 5
+	pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+	bind $w <$M1B-Key-Return> [cb _start]
+	bind $w <Key-Return> [cb _start]
+	bind $w <Key-Escape> [cb _cancel]
+	wm protocol $w WM_DELETE_WINDOW [cb _cancel]
+
+	bind $w <Visibility> [cb _visible]
+	return $this
+}
+
+method execute {} {
+	tkwait window $w
+	set rv $is_ok
+	delete_this
+	return $rv
+}
+
+method _visible {} {
+	grab $w
+	if {$is_ask_args} {
+		focus $w_args
+	} elseif {$is_ask_revs} {
+		$w_rev focus_filter
+	}
+}
+
+method _cancel {} {
+	wm protocol $w WM_DELETE_WINDOW {}
+	destroy $w
+}
+
+method _rev {} {
+	if {[catch {$w_rev commit_or_die}]} {
+		return {}
+	}
+	return [$w_rev get]
+}
+
+method _visualize {} {
+	global current_branch
+	set rev [_rev $this]
+	if {$rev ne {}} {
+		do_gitk [list --left-right "$current_branch...$rev"]
+	}
+}
+
+method _start {} {
+	global env
+
+	if {$is_ask_revs} {
+		set name [_rev $this]
+		if {$name eq {}} {
+			return
+		}
+		set env(REVISION) $name
+	}
+
+	if {$is_ask_args} {
+		set env(ARGS) $argstr
+	}
+
+	set is_ok 1
+	_cancel $this
+}
+
+}
-- 
1.6.0.3.15.gb8d36
Alexander Gavrilov· Nov 16, 2008, 18:46 UTC · re: Alexander Gavrilov · lore

[PATCH (GIT-GUI) v2 5/5] git-gui: Implement automatic rescan after Tool execution.

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 <angavrilov@gmail.com>
---
 lib/tools.tcl |   44 ++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 42 insertions(+), 2 deletions(-)
Show changes to lib/tools.tcl +42 −2
diff --git a/lib/tools.tcl b/lib/tools.tcl
index 044432e..51366b0 100644
--- a/lib/tools.tcl
+++ b/lib/tools.tcl
@@ -102,13 +102,15 @@ proc tools_exec {fullname} {
 
 	set cmdline $repo_config(guitool.$fullname.cmd)
 	if {[is_config_true "guitool.$fullname.noconsole"]} {
-		exec sh -c $cmdline &
+		tools_run_silent [list sh -c $cmdline] \
+				 [list tools_complete $fullname {}]
 	} else {
 		regsub {/} $fullname { / } title
 		set w [console::new \
 			[mc "Tool: %s" $title] \
 			[mc "Running: %s" $cmdline]]
-		console::exec $w [list sh -c $cmdline]
+		console::exec $w [list sh -c $cmdline] \
+				 [list tools_complete $fullname $w]
 	}
 
 	unset env(GIT_GUITOOL)
@@ -117,3 +119,41 @@ proc tools_exec {fullname} {
 	catch { unset env(ARGS) }
 	catch { unset env(REVISION) }
 }
+
+proc tools_run_silent {cmd after} {
+	lappend cmd 2>@1
+	set fd [_open_stdout_stderr $cmd]
+	
+	fconfigure $fd -blocking 0 -translation binary
+	fileevent $fd readable [list tools_consume_input $fd $after]
+}
+
+proc tools_consume_input {fd after} {
+	read $fd
+	if {[eof $fd]} {
+		fconfigure $fd -blocking 1
+		if {[catch {close $fd}]} {
+			uplevel #0 $after 0
+		} else {
+			uplevel #0 $after 1
+		}
+	}
+}
+
+proc tools_complete {fullname w {ok 1}} {
+	if {$w ne {}} {
+		console::done $w $ok
+	}
+
+	if {$ok} {
+		set msg [mc "Tool completed succesfully: %s" $fullname]
+	} else {
+		set msg [mc "Tool failed: %s" $fullname]
+	}
+
+	if {[is_config_true "guitool.$fullname.norescan"]} {
+		ui_status $msg
+	} else {
+		rescan [list ui_status $msg]
+	}
+}
-- 
1.6.0.3.15.gb8d36
Shawn O. Pearce· Nov 16, 2008, 21:56 UTC · re: Alexander Gavrilov · lore

Re: [PATCH (GIT-GUI) v2 0/5] Add a customizable Tools menu.

Alexander Gavrilov <angavrilov@gmail.com> wrote:
Show 6 quoted lines
> 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.

← back to recent threads