Commits

Anonymous committed f8db788 Merge

Merge branch 'master' of git://git.kernel.org/pub/scm/gitk/gitk

* 'master' of git://git.kernel.org/pub/scm/gitk/gitk:
gitk: Improve handling of -- and ambiguous arguments
gitk: Use git log and add support for --left-right
gitk: Fix bug causing "can't read commitrow(0,n)" error
[PATCH] gitk: Fix for tree view ending in nested directories
gitk: Remove the unused stopfindproc function
gitk: Fix bug in the anc_or_desc routine
gitk: Fix the find and highlight functions

Comments (0)

Files changed (1)

 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
-    set args $viewargs($view)
-    if {$viewfiles($view) ne {}} {
-	set args [concat $args "--" $viewfiles($view)]
-    }
     set order "--topo-order"
     if {$datemode} {
 	set order "--date-order"
     }
     if {[catch {
-	set fd [open [concat | git rev-list --header $order \
-			  --parents --boundary --default HEAD $args] r]
+	set fd [open [concat | git log -z --pretty=raw $order --parents \
+			 --boundary $viewargs($view) "--" $viewfiles($view)] r]
     } err]} {
-	puts stderr "Error executing git rev-list: $err"
+	error_popup "Error executing git rev-list: $err"
 	exit 1
     }
     set commfd($view) $fd
 	set j [string first "\n" $cmit]
 	set ok 0
 	set listed 1
-	if {$j >= 0} {
-	    set ids [string range $cmit 0 [expr {$j - 1}]]
-	    if {[string range $ids 0 0] == "-"} {
-		set listed 0
+	if {$j >= 0 && [string match "commit *" $cmit]} {
+	    set ids [string range $cmit 7 [expr {$j - 1}]]
+	    if {[string match {[-<>]*} $ids]} {
+		switch -- [string index $ids 0] {
+		    "-" {set listed 0}
+		    "<" {set listed 2}
+		    ">" {set listed 3}
+		}
 		set ids [string range $ids 1 end]
 	    }
 	    set ok 1
 	    if {[string length $shortcmit] > 80} {
 		set shortcmit "[string range $shortcmit 0 80]..."
 	    }
-	    error_popup "Can't parse git rev-list output: {$shortcmit}"
+	    error_popup "Can't parse git log output: {$shortcmit}"
 	    exit 1
 	}
 	set id [lindex $ids 0]
 	set treeheight($prefix) $ht
 	incr ht [lindex $htstack end]
 	set htstack [lreplace $htstack end end]
+	set prefixend [lindex $prefendstack end]
+	set prefendstack [lreplace $prefendstack end end]
+	set prefix [string range $prefix 0 $prefixend]
     }
     $w conf -state disabled
 }
     global colormap rowtextx commitrow nextcolor canvxmax
     global numcommits rowrangelist commitlisted idrowranges rowchk
     global selectedline currentid canv canvy0
-    global matchinglines treediffs
+    global treediffs
     global pending_select phase
     global commitidx rowlaidout rowoptim
     global commfd
     }
     unselectline
     normalline
-    stopfindproc
     if {$curview >= 0} {
 	set vparentlist($curview) $parentlist
 	set vdisporder($curview) $displayorder
 		[list {} $rowidlist $rowoffsets $rowrangelist]
 	}
     }
-    catch {unset matchinglines}
     catch {unset treediffs}
     clear_display
     if {[info exists hlview] && $hlview == $n} {
 
 proc find_change {name ix op} {
     global nhighlights mainfont boldnamerows
-    global findstring findpattern findtype
+    global findstring findpattern findtype markingmatches
 
     # delete previous highlights, if any
     foreach row $boldnamerows {
     set boldnamerows {}
     catch {unset nhighlights}
     unbolden
+    unmarkmatches
     if {$findtype ne "Regexp"} {
 	set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
 		   $findstring]
 	set findpattern "*$e*"
     }
+    set markingmatches [expr {$findstring ne {}}]
     drawvisible
 }
 
+proc doesmatch {f} {
+    global findtype findstring findpattern
+
+    if {$findtype eq "Regexp"} {
+	return [regexp $findstring $f]
+    } elseif {$findtype eq "IgnCase"} {
+	return [string match -nocase $findpattern $f]
+    } else {
+	return [string match $findpattern $f]
+    }
+}
+
 proc askfindhighlight {row id} {
     global nhighlights commitinfo iddrawn mainfont
-    global findstring findtype findloc findpattern
+    global findloc
+    global markingmatches
 
     if {![info exists commitinfo($id)]} {
 	getcommit $id
     set isbold 0
     set fldtypes {Headline Author Date Committer CDate Comments}
     foreach f $info ty $fldtypes {
-	if {$findloc ne "All fields" && $findloc ne $ty} {
-	    continue
-	}
-	if {$findtype eq "Regexp"} {
-	    set doesmatch [regexp $findstring $f]
-	} elseif {$findtype eq "IgnCase"} {
-	    set doesmatch [string match -nocase $findpattern $f]
-	} else {
-	    set doesmatch [string match $findpattern $f]
-	}
-	if {$doesmatch} {
+	if {($findloc eq "All fields" || $findloc eq $ty) &&
+	    [doesmatch $f]} {
 	    if {$ty eq "Author"} {
 		set isbold 2
-	    } else {
-		set isbold 1
+		break
 	    }
+	    set isbold 1
 	}
     }
-    if {[info exists iddrawn($id)]} {
-	if {$isbold && ![ishighlighted $row]} {
-	    bolden $row [concat $mainfont bold]
+    if {$isbold && [info exists iddrawn($id)]} {
+	set f [concat $mainfont bold]
+	if {![ishighlighted $row]} {
+	    bolden $row $f
+	    if {$isbold > 1} {
+		bolden_name $row $f
+	    }
 	}
-	if {$isbold >= 2} {
-	    bolden_name $row [concat $mainfont bold]
+	if {$markingmatches} {
+	    markrowmatches $row [lindex $info 0] [lindex $info 1]
 	}
     }
     set nhighlights($row) $isbold
 }
 
+proc markrowmatches {row headline author} {
+    global canv canv2 linehtag linentag
+
+    $canv delete match$row
+    $canv2 delete match$row
+    set m [findmatches $headline]
+    if {$m ne {}} {
+	markmatches $canv $row $headline $linehtag($row) $m \
+	    [$canv itemcget $linehtag($row) -font]
+    }
+    set m [findmatches $author]
+    if {$m ne {}} {
+	markmatches $canv2 $row $author $linentag($row) $m \
+	    [$canv2 itemcget $linentag($row) -font]
+    }
+}
+
 proc vrel_change {name ix op} {
     global highlight_related
 
 	set id [lindex $idlist $col]
 	addextraid $id $row
 	unset idinlist($id)
-	lappend idrowranges($id) $row
+	lappend idrowranges($id) $id
 	lappend rowrangelist $idrowranges($id)
 	unset idrowranges($id)
 	incr row
 	lset rowidlist $row [list $id]
 	lset rowoffsets $row 0
 	makeuparrow $id 0 $row 0
-	lappend idrowranges($id) $row
+	lappend idrowranges($id) $id
 	lappend rowrangelist $idrowranges($id)
 	unset idrowranges($id)
 	incr row
 }
 
 proc drawcmittext {id row col} {
-    global linespc canv canv2 canv3 canvy0 fgcolor
+    global linespc canv canv2 canv3 canvy0 fgcolor curview
     global commitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
-    global linehtag linentag linedtag
+    global linehtag linentag linedtag markingmatches
     global mainfont canvxmax boldrows boldnamerows fgcolor nullid
 
+    # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
+    set listed [lindex $commitlisted $row]
     if {$id eq $nullid} {
 	set ofill red
     } else {
-	set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
+	set ofill [expr {$listed != 0? "blue": "white"}]
     }
     set x [xc $row $col]
     set y [yc $row]
     set orad [expr {$linespc / 3}]
-    set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
-	       [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-	       -fill $ofill -outline $fgcolor -width 1 -tags circle]
+    if {$listed <= 1} {
+	set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
+		   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
+		   -fill $ofill -outline $fgcolor -width 1 -tags circle]
+    } elseif {$listed == 2} {
+	# triangle pointing left for left-side commits
+	set t [$canv create polygon \
+		   [expr {$x - $orad}] $y \
+		   [expr {$x + $orad - 1}] [expr {$y - $orad}] \
+		   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
+		   -fill $ofill -outline $fgcolor -width 1 -tags circle]
+    } else {
+	# triangle pointing right for right-side commits
+	set t [$canv create polygon \
+		   [expr {$x + $orad - 1}] $y \
+		   [expr {$x - $orad}] [expr {$y - $orad}] \
+		   [expr {$x - $orad}] [expr {$y + $orad - 1}] \
+		   -fill $ofill -outline $fgcolor -width 1 -tags circle]
+    }
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set rmx [llength [lindex $rowidlist $row]]
     set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
 			    -text $date -font $mainfont -tags text]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
+    if {$markingmatches} {
+	markrowmatches $row $headline $name
+    }
     if {$xr > $canvxmax} {
 	set canvxmax $xr
 	setcanvscroll
     for {} {$r <= $er} {incr r} {
 	set id [lindex $displayorder $r]
 	set wasdrawn [info exists iddrawn($id)]
-	if {!$wasdrawn} {
-	    drawcmitrow $r
-	}
+	drawcmitrow $r
 	if {$r == $er} break
 	set nextid [lindex $displayorder [expr {$r + 1}]]
 	if {$wasdrawn && [info exists iddrawn($nextid)]} {
 }
 
 proc findmatches {f} {
-    global findtype foundstring foundstrlen
+    global findtype findstring
     if {$findtype == "Regexp"} {
-	set matches [regexp -indices -all -inline $foundstring $f]
+	set matches [regexp -indices -all -inline $findstring $f]
     } else {
+	set fs $findstring
 	if {$findtype == "IgnCase"} {
-	    set str [string tolower $f]
-	} else {
-	    set str $f
+	    set f [string tolower $f]
+	    set fs [string tolower $fs]
 	}
 	set matches {}
 	set i 0
-	while {[set j [string first $foundstring $str $i]] >= 0} {
-	    lappend matches [list $j [expr {$j+$foundstrlen-1}]]
-	    set i [expr {$j + $foundstrlen}]
+	set l [string length $fs]
+	while {[set j [string first $fs $f $i]] >= 0} {
+	    lappend matches [list $j [expr {$j+$l-1}]]
+	    set i [expr {$j + $l}]
 	}
     }
     return $matches
 }
 
-proc dofind {} {
-    global findtype findloc findstring markedmatches commitinfo
-    global numcommits displayorder linehtag linentag linedtag
-    global mainfont canv canv2 canv3 selectedline
-    global matchinglines foundstring foundstrlen matchstring
-    global commitdata
+proc dofind {{rev 0}} {
+    global findstring findstartline findcurline selectedline numcommits
 
-    stopfindproc
     unmarkmatches
     cancel_next_highlight
     focus .
-    set matchinglines {}
-    if {$findtype == "IgnCase"} {
-	set foundstring [string tolower $findstring]
+    if {$findstring eq {} || $numcommits == 0} return
+    if {![info exists selectedline]} {
+	set findstartline [lindex [visiblerows] $rev]
     } else {
-	set foundstring $findstring
+	set findstartline $selectedline
     }
-    set foundstrlen [string length $findstring]
-    if {$foundstrlen == 0} return
-    regsub -all {[*?\[\\]} $foundstring {\\&} matchstring
-    set matchstring "*$matchstring*"
-    if {![info exists selectedline]} {
-	set oldsel -1
+    set findcurline $findstartline
+    nowbusy finding
+    if {!$rev} {
+	run findmore
     } else {
-	set oldsel $selectedline
+	set findcurline $findstartline
+	if {$findcurline == 0} {
+	    set findcurline $numcommits
+	}
+	incr findcurline -1
+	run findmorerev
     }
-    set didsel 0
-    set fldtypes {Headline Author Date Committer CDate Comments}
-    set l -1
-    foreach id $displayorder {
-	set d $commitdata($id)
-	incr l
-	if {$findtype == "Regexp"} {
-	    set doesmatch [regexp $foundstring $d]
-	} elseif {$findtype == "IgnCase"} {
-	    set doesmatch [string match -nocase $matchstring $d]
+}
+
+proc findnext {restart} {
+    global findcurline
+    if {![info exists findcurline]} {
+	if {$restart} {
+	    dofind
 	} else {
-	    set doesmatch [string match $matchstring $d]
+	    bell
 	}
-	if {!$doesmatch} continue
+    } else {
+	run findmore
+	nowbusy finding
+    }
+}
+
+proc findprev {} {
+    global findcurline
+    if {![info exists findcurline]} {
+	dofind 1
+    } else {
+	run findmorerev
+	nowbusy finding
+    }
+}
+
+proc findmore {} {
+    global commitdata commitinfo numcommits findstring findpattern findloc
+    global findstartline findcurline markingmatches displayorder
+
+    set fldtypes {Headline Author Date Committer CDate Comments}
+    set l [expr {$findcurline + 1}]
+    if {$l >= $numcommits} {
+	set l 0
+    }
+    if {$l <= $findstartline} {
+	set lim [expr {$findstartline + 1}]
+    } else {
+	set lim $numcommits
+    }
+    if {$lim - $l > 500} {
+	set lim [expr {$l + 500}]
+    }
+    set last 0
+    for {} {$l < $lim} {incr l} {
+	set id [lindex $displayorder $l]
+	if {![doesmatch $commitdata($id)]} continue
 	if {![info exists commitinfo($id)]} {
 	    getcommit $id
 	}
 	set info $commitinfo($id)
-	set doesmatch 0
 	foreach f $info ty $fldtypes {
-	    if {$findloc != "All fields" && $findloc != $ty} {
-		continue
-	    }
-	    set matches [findmatches $f]
-	    if {$matches == {}} continue
-	    set doesmatch 1
-	    if {$ty == "Headline"} {
-		drawcommits $l
-		markmatches $canv $l $f $linehtag($l) $matches $mainfont
-	    } elseif {$ty == "Author"} {
-		drawcommits $l
-		markmatches $canv2 $l $f $linentag($l) $matches $mainfont
-	    } elseif {$ty == "Date"} {
-		drawcommits $l
-		markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
+	    if {($findloc eq "All fields" || $findloc eq $ty) &&
+		[doesmatch $f]} {
+		set markingmatches 1
+		findselectline $l
+		notbusy finding
+		return 0
 	    }
 	}
-	if {$doesmatch} {
-	    lappend matchinglines $l
-	    if {!$didsel && $l > $oldsel} {
+    }
+    if {$l == $findstartline + 1} {
+	bell
+	unset findcurline
+	notbusy finding
+	return 0
+    }
+    set findcurline [expr {$l - 1}]
+    return 1
+}
+
+proc findmorerev {} {
+    global commitdata commitinfo numcommits findstring findpattern findloc
+    global findstartline findcurline markingmatches displayorder
+
+    set fldtypes {Headline Author Date Committer CDate Comments}
+    set l $findcurline
+    if {$l == 0} {
+	set l $numcommits
+    }
+    incr l -1
+    if {$l >= $findstartline} {
+	set lim [expr {$findstartline - 1}]
+    } else {
+	set lim -1
+    }
+    if {$l - $lim > 500} {
+	set lim [expr {$l - 500}]
+    }
+    set last 0
+    for {} {$l > $lim} {incr l -1} {
+	set id [lindex $displayorder $l]
+	if {![doesmatch $commitdata($id)]} continue
+	if {![info exists commitinfo($id)]} {
+	    getcommit $id
+	}
+	set info $commitinfo($id)
+	foreach f $info ty $fldtypes {
+	    if {($findloc eq "All fields" || $findloc eq $ty) &&
+		[doesmatch $f]} {
+		set markingmatches 1
 		findselectline $l
-		set didsel 1
+		notbusy finding
+		return 0
 	    }
 	}
     }
-    if {$matchinglines == {}} {
+    if {$l == -1} {
 	bell
-    } elseif {!$didsel} {
-	findselectline [lindex $matchinglines 0]
+	unset findcurline
+	notbusy finding
+	return 0
     }
+    set findcurline [expr {$l + 1}]
+    return 1
 }
 
 proc findselectline {l} {
     }
 }
 
-proc findnext {restart} {
-    global matchinglines selectedline
-    if {![info exists matchinglines]} {
-	if {$restart} {
-	    dofind
-	}
-	return
-    }
-    if {![info exists selectedline]} return
-    foreach l $matchinglines {
-	if {$l > $selectedline} {
-	    findselectline $l
-	    return
-	}
-    }
-    bell
-}
-
-proc findprev {} {
-    global matchinglines selectedline
-    if {![info exists matchinglines]} {
-	dofind
-	return
-    }
-    if {![info exists selectedline]} return
-    set prev {}
-    foreach l $matchinglines {
-	if {$l >= $selectedline} break
-	set prev $l
-    }
-    if {$prev != {}} {
-	findselectline $prev
-    } else {
-	bell
-    }
-}
-
-proc stopfindproc {{done 0}} {
-    global findprocpid findprocfile findids
-    global ctext findoldcursor phase maincursor textcursor
-    global findinprogress
-
-    catch {unset findids}
-    if {[info exists findprocpid]} {
-	if {!$done} {
-	    catch {exec kill $findprocpid}
-	}
-	catch {close $findprocfile}
-	unset findprocpid
-    }
-    catch {unset findinprogress}
-    notbusy find
-}
-
-# mark a commit as matching by putting a yellow background
-# behind the headline
-proc markheadline {l id} {
-    global canv mainfont linehtag
-
-    drawcommits $l
-    set bbox [$canv bbox $linehtag($l)]
-    set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
-    $canv lower $t
-}
-
-# mark the bits of a headline, author or date that match a find string
+# mark the bits of a headline or author that match a find string
 proc markmatches {canv l str tag matches font} {
     set bbox [$canv bbox $tag]
     set x0 [lindex $bbox 0]
 	set xlen [font measure $font [string range $str 0 [expr {$end}]]]
 	set t [$canv create rect [expr {$x0+$xoff}] $y0 \
 		   [expr {$x0+$xlen+2}] $y1 \
-		   -outline {} -tags matches -fill yellow]
+		   -outline {} -tags [list match$l matches] -fill yellow]
 	$canv lower $t
     }
 }
 
 proc unmarkmatches {} {
-    global matchinglines findids
+    global findids markingmatches findcurline
+
     allcanvs delete matches
-    catch {unset matchinglines}
     catch {unset findids}
+    set markingmatches 0
+    catch {unset findcurline}
 }
 
 proc selcanvline {w x y} {
 	# Both are on the same arc(s); either both are the same BMP,
 	# or if one is not a BMP, the other is also not a BMP or is
 	# the BMP at end of the arc (and it only has 1 incoming arc).
-	if {$a eq $b} {
+	# Or both can be BMPs with no incoming arcs.
+	if {$a eq $b || $arcnos($a) eq {}} {
 	    return 0
 	}
 	# assert {[llength $arcnos($a)] == 1}
 
 font create optionfont -family sans-serif -size -12
 
+# check that we can find a .git directory somewhere...
+set gitdir [gitdir]
+if {![file isdirectory $gitdir]} {
+    show_error {} . "Cannot find the git directory \"$gitdir\"."
+    exit 1
+}
+
 set revtreeargs {}
+set cmdline_files {}
+set i 0
 foreach arg $argv {
     switch -regexp -- $arg {
 	"^$" { }
 	"^-d" { set datemode 1 }
+	"--" {
+	    set cmdline_files [lrange $argv [expr {$i + 1}] end]
+	    break
+	}
 	default {
 	    lappend revtreeargs $arg
 	}
     }
+    incr i
 }
 
-# check that we can find a .git directory somewhere...
-set gitdir [gitdir]
-if {![file isdirectory $gitdir]} {
-    show_error {} . "Cannot find the git directory \"$gitdir\"."
-    exit 1
-}
-
-set cmdline_files {}
-set i [lsearch -exact $revtreeargs "--"]
-if {$i >= 0} {
-    set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end]
-    set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]]
-} elseif {$revtreeargs ne {}} {
+if {$i >= [llength $argv] && $revtreeargs ne {}} {
+    # no -- on command line, but some arguments (other than -d)
     if {[catch {
 	set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
 	set cmdline_files [split $f "\n"]
 	set n [llength $cmdline_files]
 	set revtreeargs [lrange $revtreeargs 0 end-$n]
+	# Unfortunately git rev-parse doesn't produce an error when
+	# something is both a revision and a filename.  To be consistent
+	# with git log and git rev-list, check revtreeargs for filenames.
+	foreach arg $revtreeargs {
+	    if {[file exists $arg]} {
+		show_error {} . "Ambiguous argument '$arg': both revision\
+				 and filename"
+		exit 1
+	    }
+	}
     } err]} {
 	# unfortunately we get both stdout and stderr in $err,
 	# so look for "fatal:".
 set boldrows {}
 set boldnamerows {}
 set diffelide {0 0}
+set markingmatches 0
 
 set optim_delay 16