Skip to content

Commit 314f5de

Browse files
tarcilapaulusmack
authored andcommitted
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>
1 parent f4c54b3 commit 314f5de

File tree

1 file changed

+145
-2
lines changed

1 file changed

+145
-2
lines changed

gitk

Lines changed: 145 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,8 @@ proc makewindow {} {
10881088
-command {flist_hl 0}
10891089
$flist_menu add command -label [mc "Highlight this only"] \
10901090
-command {flist_hl 1}
1091+
$flist_menu add command -label [mc "External diff"] \
1092+
-command {external_diff}
10911093
}
10921094

10931095
# Windows sends all mouse wheel events to the current focused window, not
@@ -1192,7 +1194,7 @@ proc savestuff {w} {
11921194
global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
11931195
global cmitmode wrapcomment datetimeformat limitdiffs
11941196
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
1195-
global autoselect
1197+
global autoselect extdifftool
11961198

11971199
if {$stuffsaved} return
11981200
if {![winfo viewable .]} return
@@ -1218,6 +1220,7 @@ proc savestuff {w} {
12181220
puts $f [list set diffcolors $diffcolors]
12191221
puts $f [list set diffcontext $diffcontext]
12201222
puts $f [list set selectbgcolor $selectbgcolor]
1223+
puts $f [list set extdifftool $extdifftool]
12211224

12221225
puts $f "set geometry(main) [wm geometry .]"
12231226
puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -1768,6 +1771,12 @@ proc pop_flist_menu {w X Y x y} {
17681771
set e [lindex $treediffs($diffids) [expr {$l-2}]]
17691772
}
17701773
set flist_menu_file $e
1774+
set xdiffstate "normal"
1775+
if {$cmitmode eq "tree"} {
1776+
set xdiffstate "disabled"
1777+
}
1778+
# Disable "External diff" item in tree mode
1779+
$flist_menu entryconf 2 -state $xdiffstate
17711780
tk_popup $flist_menu $X $Y
17721781
}
17731782

@@ -1783,6 +1792,113 @@ proc flist_hl {only} {
17831792
set gdttype [mc "touching paths:"]
17841793
}
17851794

1795+
proc save_file_from_commit {filename output what} {
1796+
global nullfile
1797+
1798+
if {[catch {exec git show $filename -- > $output} err]} {
1799+
if {[string match "fatal: bad revision *" $err]} {
1800+
return $nullfile
1801+
}
1802+
error_popup "Error getting \"$filename\" from $what: $err"
1803+
return {}
1804+
}
1805+
return $output
1806+
}
1807+
1808+
proc external_diff_get_one_file {diffid filename diffdir} {
1809+
global nullid nullid2 nullfile
1810+
global gitdir
1811+
1812+
if {$diffid == $nullid} {
1813+
set difffile [file join [file dirname $gitdir] $filename]
1814+
if {[file exists $difffile]} {
1815+
return $difffile
1816+
}
1817+
return $nullfile
1818+
}
1819+
if {$diffid == $nullid2} {
1820+
set difffile [file join $diffdir "\[index\] [file tail $filename]"]
1821+
return [save_file_from_commit :$filename $difffile index]
1822+
}
1823+
set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
1824+
return [save_file_from_commit $diffid:$filename $difffile \
1825+
"revision $diffid"]
1826+
}
1827+
1828+
proc external_diff {} {
1829+
global gitktmpdir nullid nullid2
1830+
global flist_menu_file
1831+
global diffids
1832+
global diffnum
1833+
global gitdir extdifftool
1834+
1835+
if {[llength $diffids] == 1} {
1836+
# no reference commit given
1837+
set diffidto [lindex $diffids 0]
1838+
if {$diffidto eq $nullid} {
1839+
# diffing working copy with index
1840+
set diffidfrom $nullid2
1841+
} elseif {$diffidto eq $nullid2} {
1842+
# diffing index with HEAD
1843+
set diffidfrom "HEAD"
1844+
} else {
1845+
# use first parent commit
1846+
global parentlist selectedline
1847+
set diffidfrom [lindex $parentlist $selectedline 0]
1848+
}
1849+
} else {
1850+
set diffidfrom [lindex $diffids 0]
1851+
set diffidto [lindex $diffids 1]
1852+
}
1853+
1854+
# make sure that several diffs wont collide
1855+
if {![info exists gitktmpdir]} {
1856+
set gitktmpdir [file join [file dirname $gitdir] \
1857+
[format ".gitk-tmp.%s" [pid]]]
1858+
if {[catch {file mkdir $gitktmpdir} err]} {
1859+
error_popup "Error creating temporary directory $gitktmpdir: $err"
1860+
unset gitktmpdir
1861+
return
1862+
}
1863+
set diffnum 0
1864+
}
1865+
incr diffnum
1866+
set diffdir [file join $gitktmpdir $diffnum]
1867+
if {[catch {file mkdir $diffdir} err]} {
1868+
error_popup "Error creating temporary directory $diffdir: $err"
1869+
return
1870+
}
1871+
1872+
# gather files to diff
1873+
set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
1874+
set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
1875+
1876+
if {$difffromfile ne {} && $difftofile ne {}} {
1877+
set cmd [concat | [shellsplit $extdifftool] \
1878+
[list $difffromfile $difftofile]]
1879+
if {[catch {set fl [open $cmd r]} err]} {
1880+
file delete -force $diffdir
1881+
error_popup [mc "$extdifftool: command failed: $err"]
1882+
} else {
1883+
fconfigure $fl -blocking 0
1884+
filerun $fl [list delete_at_eof $fl $diffdir]
1885+
}
1886+
}
1887+
}
1888+
1889+
# delete $dir when we see eof on $f (presumably because the child has exited)
1890+
proc delete_at_eof {f dir} {
1891+
while {[gets $f line] >= 0} {}
1892+
if {[eof $f]} {
1893+
if {[catch {close $f} err]} {
1894+
error_popup "External diff viewer failed: $err"
1895+
}
1896+
file delete -force $dir
1897+
return 0
1898+
}
1899+
return 1
1900+
}
1901+
17861902
# Functions for adding and removing shell-type quoting
17871903

17881904
proc shellquote {str} {
@@ -7881,9 +7997,15 @@ proc showtag {tag isnew} {
78817997

78827998
proc doquit {} {
78837999
global stopped
8000+
global gitktmpdir
8001+
78848002
set stopped 100
78858003
savestuff .
78868004
destroy .
8005+
8006+
if {[info exists gitktmpdir]} {
8007+
catch {file delete -force $gitktmpdir}
8008+
}
78878009
}
78888010

78898011
proc mkfontdisp {font top which} {
@@ -8012,7 +8134,7 @@ proc doprefs {} {
80128134
global maxwidth maxgraphpct
80138135
global oldprefs prefstop showneartags showlocalchanges
80148136
global bgcolor fgcolor ctext diffcolors selectbgcolor
8015-
global tabstop limitdiffs autoselect
8137+
global tabstop limitdiffs autoselect extdifftool
80168138

80178139
set top .gitkprefs
80188140
set prefstop $top
@@ -8064,6 +8186,15 @@ proc doprefs {} {
80648186
pack $top.ldiff.b $top.ldiff.l -side left
80658187
grid x $top.ldiff -sticky w
80668188

8189+
entry $top.extdifft -textvariable extdifftool
8190+
frame $top.extdifff
8191+
label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
8192+
-padx 10
8193+
button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
8194+
-command choose_extdiff
8195+
pack $top.extdifff.l $top.extdifff.b -side left
8196+
grid x $top.extdifff $top.extdifft -sticky w
8197+
80678198
label $top.cdisp -text [mc "Colors: press to choose"]
80688199
grid $top.cdisp - -sticky w -pady 10
80698200
label $top.bg -padx 40 -relief sunk -background $bgcolor
@@ -8111,6 +8242,15 @@ proc doprefs {} {
81118242
bind $top <Visibility> "focus $top.buts.ok"
81128243
}
81138244

8245+
proc choose_extdiff {} {
8246+
global extdifftool
8247+
8248+
set prog [tk_getOpenFile -title "External diff tool" -multiple false]
8249+
if {$prog ne {}} {
8250+
set extdifftool $prog
8251+
}
8252+
}
8253+
81148254
proc choosecolor {v vi w x cmd} {
81158255
global $v
81168256

@@ -8539,6 +8679,8 @@ set limitdiffs 1
85398679
set datetimeformat "%Y-%m-%d %H:%M:%S"
85408680
set autoselect 1
85418681

8682+
set extdifftool "meld"
8683+
85428684
set colors {green red blue magenta darkgrey brown orange}
85438685
set bgcolor white
85448686
set fgcolor black
@@ -8685,6 +8827,7 @@ if {$mergeonly} {
86858827

86868828
set nullid "0000000000000000000000000000000000000000"
86878829
set nullid2 "0000000000000000000000000000000000000001"
8830+
set nullfile "/dev/null"
86888831

86898832
set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
86908833

0 commit comments

Comments
 (0)