gitk: Add a command to compare two strings of commits
authorPaul Mackerras <paulus@samba.org>
Thu, 9 Apr 2009 12:10:20 +0000 (22:10 +1000)
committerPaul Mackerras <paulus@samba.org>
Thu, 9 Apr 2009 12:10:20 +0000 (22:10 +1000)
This adds a row context menu command to compare this commit and its
descendants with the marked commit and its descendants.  The results
are shown in the bottom-left pane.  Commits are compared by checking
whether their headlines are the same and their patches have the same
patch ID as generated by git patch-id.

Merges are ignored and skipped over (as long as they have one
descendant).  If two commits have the same patch ID then the process
will continue and compare their descendants, as long as they both have
exactly one descendant.  If either commit has 0 or 2 or more descendants,
the comparison stops there.  There is currently a limit of 100
comparisons.

This can be useful for checking whether one string of commits is just
a rebased version of another string of commits.  Mark the end of one
string (i.e. the oldest commit in the string) and invoke "Compare with
marked commit" on the end of the other string.

As this is implemented, the UI will be unresponsive while the results
are being generated.  This should be fixed.

Signed-off-by: Paul Mackerras <paulus@samba.org>
gitk

diff --git a/gitk b/gitk
index 628f6e5c5bbcf3b074d4c9e52e0b346614ed44f5..bdd12236b86e7fbcd4314afb32c3e603037c648e 100755 (executable)
--- a/gitk
+++ b/gitk
@@ -2362,6 +2362,7 @@ proc makewindow {} {
        {mc "Mark this commit" command markhere}
        {mc "Return to mark" command gotomark}
        {mc "Find descendant of this and mark" command find_common_desc}
+       {mc "Compare with marked commit" command compare_commits}
     }
     $rowctxmenu configure -tearoff 0
 
@@ -8037,9 +8038,11 @@ proc rowmenu {x y id} {
        if {[info exists markedid] && $markedid ne $id} {
            $menu entryconfigure 9 -state normal
            $menu entryconfigure 10 -state normal
+           $menu entryconfigure 11 -state normal
        } else {
            $menu entryconfigure 9 -state disabled
            $menu entryconfigure 10 -state disabled
+           $menu entryconfigure 11 -state disabled
        }
     } else {
        set menu $fakerowmenu
@@ -8103,6 +8106,92 @@ proc find_common_desc {} {
     #puts "took [expr {$t2-$t1}]ms"
 }
 
+proc compare_commits {} {
+    global markedid rowmenuid curview children
+
+    if {![info exists markedid]} return
+    if {![commitinview $markedid $curview]} return
+    addtohistory [list do_cmp_commits $markedid $rowmenuid]
+    do_cmp_commits $markedid $rowmenuid
+}
+
+proc getpatchid {id} {
+    global patchids
+
+    if {![info exists patchids($id)]} {
+       set x [exec git diff-tree -p --root $id | git patch-id]
+       set patchids($id) [lindex $x 0]
+    }
+    return $patchids($id)
+}
+
+proc do_cmp_commits {a b} {
+    global ctext curview parents children patchids commitinfo
+
+    $ctext conf -state normal
+    clear_ctext
+    init_flist {}
+    for {set i 0} {$i < 100} {incr i} {
+       set shorta [string range $a 0 7]
+       set shortb [string range $b 0 7]
+       set skipa 0
+       set skipb 0
+       if {[llength $parents($curview,$a)] > 1} {
+           appendwithlinks [mc "Skipping merge commit %s\n" $shorta] {}
+           set skipa 1
+       } else {
+           set patcha [getpatchid $a]
+       }
+       if {[llength $parents($curview,$b)] > 1} {
+           appendwithlinks [mc "Skipping merge commit %s\n" $shortb] {}
+           set skipb 1
+       } else {
+           set patchb [getpatchid $b]
+       }
+       if {!$skipa && !$skipb} {
+           set heada [lindex $commitinfo($a) 0]
+           set headb [lindex $commitinfo($b) 0]
+           if {$patcha eq $patchb} {
+               if {$heada eq $headb} {
+                   appendwithlinks [mc "Commit %s == %s  %s\n" \
+                                        $shorta $shortb $heada] {}
+               } else {
+                   appendwithlinks [mc "Commit %s  %s\n" $shorta $heada] {}
+                   appendwithlinks [mc " is the same patch as\n"] {}
+                   appendwithlinks [mc "       %s  %s\n" $shortb $headb] {}
+               }
+               set skipa 1
+               set skipb 1
+           } else {
+               $ctext insert end "\n"
+               appendwithlinks [mc "Commit %s  %s\n" $shorta $heada] {}
+               appendwithlinks [mc " differs from\n"] {}
+               appendwithlinks [mc "       %s  %s\n" $shortb $headb] {}
+               appendwithlinks [mc "- stopping\n"]
+               break
+           }
+       }
+       if {$skipa} {
+           if {[llength $children($curview,$a)] != 1} {
+               $ctext insert end "\n"
+               appendwithlinks [mc "Commit %s has %s children - stopping\n" \
+                                   $shorta [llength $children($curview,$a)]] {}
+               break
+           }
+           set a [lindex $children($curview,$a) 0]
+       }
+       if {$skipb} {
+           if {[llength $children($curview,$b)] != 1} {
+               appendwithlinks [mc "Commit %s has %s children - stopping\n" \
+                                   $shortb [llength $children($curview,$b)]] {}
+               break
+           }
+           set b [lindex $children($curview,$b) 0]
+       }
+    }
+    $ctext conf -state disabled
+}
+
 proc diffvssel {dirn} {
     global rowmenuid selectedline