aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Arcila <thomas.arcila@gmail.com>2008-03-24 12:55:36 +0100
committerPaul Mackerras <paulus@samba.org>2008-05-11 10:50:05 +1000
commit314f5de1a0b91d27cbbb8a400337dd41cf84d511 (patch)
tree13a22a5ff81b7358b27d9128a0d0c57b9bb34296
parentf4c54b3cc4b1bbde34fe957ac03f9862ec970de3 (diff)
downloadgit-314f5de1a0b91d27cbbb8a400337dd41cf84d511.tar.gz
git-314f5de1a0b91d27cbbb8a400337dd41cf84d511.tar.xz
gitk: Allow users to view diffs in external diff viewer
This allows gitk to run an external diff viewer such as meld. Right-click on a file in the file list view gives "External diff" popup menu entry, which launches the selected external diff tool. The menu entry is only active in "Patch" mode, not in "Tree" mode. The program to run to display the diff is configurable through Edit/Preference/External diff tool. The program is run with two arguments, being the names of files containing the two versions to diff. Gitk will create temporary directories called .gitk-tmp.<pid>/<n> to place these files in, and remove them when it's finished. If the file doesn't exist in one or other revision, gitk will supply /dev/null as the name of the file on that side of the diff. This may need to be adjusted for Windows or MacOS. [paulus@samba.org - cleaned up and rewrote some parts of the patch.] Signed-off-by: Thomas Arcila <thomas.arcila@gmail.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rwxr-xr-xgitk147
1 files changed, 145 insertions, 2 deletions
diff --git a/gitk b/gitk
index 9ff25deb7..a1eccfc6d 100755
--- a/gitk
+++ b/gitk
@@ -1088,6 +1088,8 @@ proc makewindow {} {
-command {flist_hl 0}
$flist_menu add command -label [mc "Highlight this only"] \
-command {flist_hl 1}
+ $flist_menu add command -label [mc "External diff"] \
+ -command {external_diff}
}
# Windows sends all mouse wheel events to the current focused window, not
@@ -1192,7 +1194,7 @@ proc savestuff {w} {
global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
global cmitmode wrapcomment datetimeformat limitdiffs
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
- global autoselect
+ global autoselect extdifftool
if {$stuffsaved} return
if {![winfo viewable .]} return
@@ -1218,6 +1220,7 @@ proc savestuff {w} {
puts $f [list set diffcolors $diffcolors]
puts $f [list set diffcontext $diffcontext]
puts $f [list set selectbgcolor $selectbgcolor]
+ puts $f [list set extdifftool $extdifftool]
puts $f "set geometry(main) [wm geometry .]"
puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -1768,6 +1771,12 @@ proc pop_flist_menu {w X Y x y} {
set e [lindex $treediffs($diffids) [expr {$l-2}]]
}
set flist_menu_file $e
+ set xdiffstate "normal"
+ if {$cmitmode eq "tree"} {
+ set xdiffstate "disabled"
+ }
+ # Disable "External diff" item in tree mode
+ $flist_menu entryconf 2 -state $xdiffstate
tk_popup $flist_menu $X $Y
}
@@ -1783,6 +1792,113 @@ proc flist_hl {only} {
set gdttype [mc "touching paths:"]
}
+proc save_file_from_commit {filename output what} {
+ global nullfile
+
+ if {[catch {exec git show $filename -- > $output} err]} {
+ if {[string match "fatal: bad revision *" $err]} {
+ return $nullfile
+ }
+ error_popup "Error getting \"$filename\" from $what: $err"
+ return {}
+ }
+ return $output
+}
+
+proc external_diff_get_one_file {diffid filename diffdir} {
+ global nullid nullid2 nullfile
+ global gitdir
+
+ if {$diffid == $nullid} {
+ set difffile [file join [file dirname $gitdir] $filename]
+ if {[file exists $difffile]} {
+ return $difffile
+ }
+ return $nullfile
+ }
+ if {$diffid == $nullid2} {
+ set difffile [file join $diffdir "\[index\] [file tail $filename]"]
+ return [save_file_from_commit :$filename $difffile index]
+ }
+ set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
+ return [save_file_from_commit $diffid:$filename $difffile \
+ "revision $diffid"]
+}
+
+proc external_diff {} {
+ global gitktmpdir nullid nullid2
+ global flist_menu_file
+ global diffids
+ global diffnum
+ global gitdir extdifftool
+
+ if {[llength $diffids] == 1} {
+ # no reference commit given
+ set diffidto [lindex $diffids 0]
+ if {$diffidto eq $nullid} {
+ # diffing working copy with index
+ set diffidfrom $nullid2
+ } elseif {$diffidto eq $nullid2} {
+ # diffing index with HEAD
+ set diffidfrom "HEAD"
+ } else {
+ # use first parent commit
+ global parentlist selectedline
+ set diffidfrom [lindex $parentlist $selectedline 0]
+ }
+ } else {
+ set diffidfrom [lindex $diffids 0]
+ set diffidto [lindex $diffids 1]
+ }
+
+ # make sure that several diffs wont collide
+ if {![info exists gitktmpdir]} {
+ set gitktmpdir [file join [file dirname $gitdir] \
+ [format ".gitk-tmp.%s" [pid]]]
+ if {[catch {file mkdir $gitktmpdir} err]} {
+ error_popup "Error creating temporary directory $gitktmpdir: $err"
+ unset gitktmpdir
+ return
+ }
+ set diffnum 0
+ }
+ incr diffnum
+ set diffdir [file join $gitktmpdir $diffnum]
+ if {[catch {file mkdir $diffdir} err]} {
+ error_popup "Error creating temporary directory $diffdir: $err"
+ return
+ }
+
+ # gather files to diff
+ set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
+ set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
+
+ if {$difffromfile ne {} && $difftofile ne {}} {
+ set cmd [concat | [shellsplit $extdifftool] \
+ [list $difffromfile $difftofile]]
+ if {[catch {set fl [open $cmd r]} err]} {
+ file delete -force $diffdir
+ error_popup [mc "$extdifftool: command failed: $err"]
+ } else {
+ fconfigure $fl -blocking 0
+ filerun $fl [list delete_at_eof $fl $diffdir]
+ }
+ }
+}
+
+# delete $dir when we see eof on $f (presumably because the child has exited)
+proc delete_at_eof {f dir} {
+ while {[gets $f line] >= 0} {}
+ if {[eof $f]} {
+ if {[catch {close $f} err]} {
+ error_popup "External diff viewer failed: $err"
+ }
+ file delete -force $dir
+ return 0
+ }
+ return 1
+}
+
# Functions for adding and removing shell-type quoting
proc shellquote {str} {
@@ -7881,9 +7997,15 @@ proc showtag {tag isnew} {
proc doquit {} {
global stopped
+ global gitktmpdir
+
set stopped 100
savestuff .
destroy .
+
+ if {[info exists gitktmpdir]} {
+ catch {file delete -force $gitktmpdir}
+ }
}
proc mkfontdisp {font top which} {
@@ -8012,7 +8134,7 @@ proc doprefs {} {
global maxwidth maxgraphpct
global oldprefs prefstop showneartags showlocalchanges
global bgcolor fgcolor ctext diffcolors selectbgcolor
- global tabstop limitdiffs autoselect
+ global tabstop limitdiffs autoselect extdifftool
set top .gitkprefs
set prefstop $top
@@ -8064,6 +8186,15 @@ proc doprefs {} {
pack $top.ldiff.b $top.ldiff.l -side left
grid x $top.ldiff -sticky w
+ entry $top.extdifft -textvariable extdifftool
+ frame $top.extdifff
+ label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
+ -padx 10
+ button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
+ -command choose_extdiff
+ pack $top.extdifff.l $top.extdifff.b -side left
+ grid x $top.extdifff $top.extdifft -sticky w
+
label $top.cdisp -text [mc "Colors: press to choose"]
grid $top.cdisp - -sticky w -pady 10
label $top.bg -padx 40 -relief sunk -background $bgcolor
@@ -8111,6 +8242,15 @@ proc doprefs {} {
bind $top <Visibility> "focus $top.buts.ok"
}
+proc choose_extdiff {} {
+ global extdifftool
+
+ set prog [tk_getOpenFile -title "External diff tool" -multiple false]
+ if {$prog ne {}} {
+ set extdifftool $prog
+ }
+}
+
proc choosecolor {v vi w x cmd} {
global $v
@@ -8539,6 +8679,8 @@ set limitdiffs 1
set datetimeformat "%Y-%m-%d %H:%M:%S"
set autoselect 1
+set extdifftool "meld"
+
set colors {green red blue magenta darkgrey brown orange}
set bgcolor white
set fgcolor black
@@ -8685,6 +8827,7 @@ if {$mergeonly} {
set nullid "0000000000000000000000000000000000000000"
set nullid2 "0000000000000000000000000000000000000001"
+set nullfile "/dev/null"
set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]