[PATCH] gitk : External diff viewer.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Right click on patched file list view gives "External diff" popup menu entry,
launching selected external diff tool.
The diff tool is configurable through Edit/Preference/External diff tool.

Signed-off-by: Thomas Arcila <thomas.arcila@xxxxxxxxx>
---
Hi,

Here is a patch to gitk that allows to run an external diff viewer.
It can be configured in Edit/Preferences/External diff tool.

To see the diff between two files:
- select revisions to diff
- right click on a file in the patched files list view
- choose "External diff"

* About the UI : 
This pane is already meant to show which files are different between selected revisions
and it's also used to select the diff displayed in the diff viewer. It only required
few modifications to gitk except for adding the external_diff method and
tweaking the menu.

flist_menu has been split up into flist_menu (contextual menu for patched files list
view) and flist_menu_tree (contextual menu for tree view).

* Diff tool :
It defaults to meld and can be configured through Edit/Preferences/External diff tool

* Diff tool calling conventions :
The choice done here is to consider that any diff tool can take the two
files to diff. No git information should be required - filenames give
information about revisions currently diffed (see Temporay files).
The few ones I tried could worked this way ([g]vimdiff, meld, kdiff3,
beyond compare), others can be warped in scripts as it has to be done
when using GIT_EXTERNAL_DIFF_TOOL.

* Temporary files :
Temporary files are stored at the top level of project directory along with .git.
- As each instance of gitk should be able to diff, temporary directory is name by pid.
- One instance of gitk can run several diffs at the same time so diffs must be numbered.
- Name of a file should be self describing : it should tell the revision it came from
  and its name
- name mangling should keep extension as is as smart diff viewer can do syntactic
  coloration
- diffing with working copy should directly involve file from the working copy (not a copy) 
  so it can be edited (some changes could be discarded from there...)
The naming convention used is
.gitk-tmp.$pid/$diffnum/[$revision|index] $basefile.$ext

* Clean up
Temporary files are removed :
- when diff viewer exits(through [file delete -force $gitktmpdir .gitk-tmp.$pid/$diffnum])
-when gitk exits (through [file delete -force $gitktmpdir .gitk-tmp.$pid])


Any feedback is welcome.
Thomas



 gitk |  121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 117 insertions(+), 4 deletions(-)

diff --git a/gitk b/gitk
index 84ab02e..21d79e3 100755
--- a/gitk
+++ b/gitk
@@ -1088,6 +1088,16 @@ 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}
+
+    global flist_menu_tree
+    set flist_menu_tree .flistctxmenutree
+    menu $flist_menu_tree -tearoff 0
+    $flist_menu_tree add command -label [mc "Highlight this too"] \
+	-command {flist_hl 0}
+    $flist_menu_tree add command -label [mc "Highlight this only"] \
+	-command {flist_hl 1}
 }
 
 # Windows sends all mouse wheel events to the current focused window, not
@@ -1192,7 +1202,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 +1228,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]"
@@ -1755,7 +1766,7 @@ proc sel_flist {w x y} {
 }
 
 proc pop_flist_menu {w X Y x y} {
-    global ctext cflist cmitmode flist_menu flist_menu_file
+    global ctext cflist cmitmode flist_menu flist_menu_tree flist_menu_file
     global treediffs diffids
 
     stopfinding
@@ -1768,7 +1779,11 @@ proc pop_flist_menu {w X Y x y} {
 	set e [lindex $treediffs($diffids) [expr {$l-2}]]
     }
     set flist_menu_file $e
-    tk_popup $flist_menu $X $Y
+    if {$cmitmode eq "tree"} {
+      tk_popup $flist_menu_tree $X $Y
+    } else {
+      tk_popup $flist_menu $X $Y
+    }
 }
 
 proc flist_hl {only} {
@@ -1783,6 +1798,88 @@ proc flist_hl {only} {
     set gdttype [mc "touching paths:"]
 }
 
+proc save_file_from_commit {filename output} {
+    exec git show $filename > $output
+}
+
+proc external_diff_get_one_file {diffid filename diffdir} {
+    global nullid nullid2
+    global gitdir
+    set failure_reason "File has probably been created, deleted, renamed \
+            in a different commit."
+    if {$diffid == $nullid} {
+        set difffile [file join $gitdir ".." $filename]
+    } elseif {$diffid == $nullid2} {
+        set difffile [file join $diffdir "\[index\] [file tail $filename]"]
+        if {[catch {save_file_from_commit :$filename $difffile} err]} {
+            error_popup "\"$filename\" cannot be found in the index. \
+                $failure_reason ($err)"
+            return 0;
+        }
+    } else {
+        set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
+        if {[catch {save_file_from_commit $diffid:$filename $difffile} err]} {
+            error_popup "\"$filename\" cannot be found in revision $diffid. \
+                $failure_reason ($err)"
+            return 0;
+        }
+    }
+    return $difffile
+}
+
+proc external_diff {} {
+    global gitktmpdir nullid nullid2
+    global flist_menu_file
+    global diffids
+    global diffnum
+    global gitdir extdifftool
+
+    set diffidto [lindex $diffids 0]
+
+    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 parent commit
+            global allparents
+            set diffidfrom $allparents($diffidto)
+        }
+    } else {
+        set diffidfrom [lindex $diffids 0]
+        set diffidto [lindex $diffids 1]
+    }
+
+    # make sure that several diffs wont collide
+    if {! [info exists diffnum]} {
+        set diffnum 0
+    } else {
+        set diffnum [expr $diffnum + 1]
+    }
+    set diffdir [file join $gitktmpdir "$diffnum"]
+    file mkdir $diffdir
+
+    # 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 != 0 && $difftofile != 0} {
+        set cmd [concat | $extdifftool [shellarglist [list $difffromfile $difftofile]]]
+        if {[catch {set fl [open $cmd]} err]} {
+            file delete -force [ file join $gitktmpdir $diffnum ]
+            error_popup [mc "$extdifftool command failed: $err"]
+        } else {
+            fconfigure $fl -blocking 0
+            filerun $fl [list file delete -force [file join $gitktmpdir $diffnum]]
+        }
+    }
+}
+
 # Functions for adding and removing shell-type quoting
 
 proc shellquote {str} {
@@ -7858,9 +7955,13 @@ proc showtag {tag isnew} {
 
 proc doquit {} {
     global stopped
+    global gitktmpdir
+
     set stopped 100
     savestuff .
     destroy .
+
+    catch {file delete -force $gitktmpdir}
 }
 
 proc mkfontdisp {font top which} {
@@ -7989,7 +8090,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
@@ -8041,6 +8142,13 @@ proc doprefs {} {
     pack $top.ldiff.b $top.ldiff.l -side left
     grid x $top.ldiff -sticky w
 
+    entry $top.extdifft -textvariable extdifftool
+    button $top.extdiffb -text [mc "External diff tool" ] -font optionfont \
+  -command {set extdifftool \"[tk_getOpenFile -title "External diff tool" \
+  -multiple "false"]\"}
+    grid x $top.extdiffb $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
@@ -8516,6 +8624,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
@@ -8570,6 +8680,9 @@ if {![file isdirectory $gitdir]} {
     exit 1
 }
 
+set gitktmpdir [file normalize [file join $gitdir ".." [format ".gitk-tmp.%s" [pid]]]]
+file mkdir $gitktmpdir
+
 set mergeonly 0
 set revtreeargs {}
 set cmdline_files {}
-- 
1.5.4.3


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux