From 6e8c87070306a757c4d7fd2c55cca3a90fe140c7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 31 Jul 2007 21:03:06 +1000 Subject: [PATCH 001/123] gitk: Establish and use global left-to-right ordering for commits This creates an "ordering token" for each commit which establishes a total ordering for commits and is used to order the commits from left to right on a row. The ordering token is assigned when a commit is first encountered or when it is first listed as a parent of some other commit, whichever comes first. The ordering token is a string of variable length. Parents that don't already have an ordering token are assigned one by appending to the child's token; the first parent gets a "0" on the end, the second "1" and so on. As an optimization, the "0" isn't appended if the child only has one parent. When inserting a new commit into an element of rowidlist, it is inserted in the position which makes the ordering tokens increase from left to right. This also simplifies the layout code by getting rid of the rowoffsets variable, and terminates lines with an arrow after 5 rows if the line would be longer than about 110 rows (rather than letting them go on and terminating them later with an arrow if the graph gets too wide). The effect of having the total ordering, and terminating the lines early, is that it will be possible to lay out only a part of the graph rather than having to do the whole thing top to bottom. Signed-off-by: Paul Mackerras --- gitk | 351 +++++++++++++++++++++++++---------------------------------- 1 file changed, 148 insertions(+), 203 deletions(-) diff --git a/gitk b/gitk index 6c2be3b727..40e5d31749 100755 --- a/gitk +++ b/gitk @@ -82,11 +82,12 @@ proc dorunq {} { proc start_rev_list {view} { global startmsecs global commfd leftover tclencoding datemode - global viewargs viewfiles commitidx + global viewargs viewfiles commitidx vnextroot global lookingforhead showlocalchanges set startmsecs [clock clicks -milliseconds] set commitidx($view) 0 + set vnextroot($view) 0 set order "--topo-order" if {$datemode} { set order "--date-order" @@ -131,12 +132,26 @@ proc getcommits {} { show_status "Reading commits..." } +# This makes a string representation of a positive integer which +# sorts as a string in numerical order +proc strrep {n} { + if {$n < 16} { + return [format "%x" $n] + } elseif {$n < 256} { + return [format "x%.2x" $n] + } elseif {$n < 65536} { + return [format "y%.4x" $n] + } + return [format "z%.8x" $n] +} + proc getcommitlines {fd view} { global commitlisted global leftover commfd global displayorder commitidx commitrow commitdata global parentlist children curview hlview global vparentlist vdisporder vcmitlisted + global ordertok vnextroot set stuff [read $fd 500000] # git log doesn't terminate the last commit with a null... @@ -221,14 +236,32 @@ proc getcommitlines {fd view} { exit 1 } set id [lindex $ids 0] + if {![info exists ordertok($view,$id)]} { + set otok "o[strrep $vnextroot($view)]" + incr vnextroot($view) + set ordertok($view,$id) $otok + } else { + set otok $ordertok($view,$id) + } if {$listed} { set olds [lrange $ids 1 end] - set i 0 - foreach p $olds { - if {$i == 0 || [lsearch -exact $olds $p] >= $i} { - lappend children($view,$p) $id + if {[llength $olds] == 1} { + set p [lindex $olds 0] + lappend children($view,$p) $id + if {![info exists ordertok($view,$p)]} { + set ordertok($view,$p) $ordertok($view,$id) + } + } else { + set i 0 + foreach p $olds { + if {$i == 0 || [lsearch -exact $olds $p] >= $i} { + lappend children($view,$p) $id + } + if {![info exists ordertok($view,$p)]} { + set ordertok($view,$p) "$otok[strrep $i]]" + } + incr i } - incr i } } else { set olds {} @@ -1821,7 +1854,7 @@ proc unflatten {var l} { proc showview {n} { global curview viewdata viewfiles - global displayorder parentlist rowidlist rowoffsets + global displayorder parentlist rowidlist global colormap rowtextx commitrow nextcolor canvxmax global numcommits rowrangelist commitlisted idrowranges rowchk global selectedline currentid canv canvy0 @@ -1859,13 +1892,13 @@ proc showview {n} { set vcmitlisted($curview) $commitlisted if {$phase ne {}} { set viewdata($curview) \ - [list $phase $rowidlist $rowoffsets $rowrangelist \ + [list $phase $rowidlist {} $rowrangelist \ [flatten idrowranges] [flatten idinlist] \ $rowlaidout $rowoptim $numcommits] } elseif {![info exists viewdata($curview)] || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list {} $rowidlist $rowoffsets $rowrangelist] + [list {} $rowidlist {} $rowrangelist] } } catch {unset treediffs} @@ -1894,7 +1927,6 @@ proc showview {n} { set parentlist $vparentlist($n) set commitlisted $vcmitlisted($n) set rowidlist [lindex $v 1] - set rowoffsets [lindex $v 2] set rowrangelist [lindex $v 3] if {$phase eq {}} { set numcommits [llength $displayorder] @@ -2542,67 +2574,43 @@ proc usedinrange {id l1 l2} { return 0 } -proc sanity {row {full 0}} { - global rowidlist rowoffsets +# Work out where id should go in idlist so that order-token +# values increase from left to right +proc idcol {idlist id {i 0}} { + global ordertok curview - set col -1 - set ids [lindex $rowidlist $row] - foreach id $ids { - incr col - if {$id eq {}} continue - if {$col < [llength $ids] - 1 && - [lsearch -exact -start [expr {$col+1}] $ids $id] >= 0} { - puts "oops: [shortids $id] repeated in row $row col $col: {[shortids [lindex $rowidlist $row]]}" + set t $ordertok($curview,$id) + if {$i >= [llength $idlist] || + $t < $ordertok($curview,[lindex $idlist $i])} { + if {$i > [llength $idlist]} { + set i [llength $idlist] } - set o [lindex $rowoffsets $row $col] - set y $row - set x $col - while {$o ne {}} { - incr y -1 - incr x $o - if {[lindex $rowidlist $y $x] != $id} { - puts "oops: rowoffsets wrong at row [expr {$y+1}] col [expr {$x-$o}]" - puts " id=[shortids $id] check started at row $row" - for {set i $row} {$i >= $y} {incr i -1} { - puts " row $i ids={[shortids [lindex $rowidlist $i]]} offs={[lindex $rowoffsets $i]}" - } - break - } - if {!$full} break - set o [lindex $rowoffsets $y $x] + while {[incr i -1] >= 0 && + $t < $ordertok($curview,[lindex $idlist $i])} {} + incr i + } else { + if {$t > $ordertok($curview,[lindex $idlist $i])} { + while {[incr i] < [llength $idlist] && + $t >= $ordertok($curview,[lindex $idlist $i])} {} } } + return $i } -proc makeuparrow {oid x y z} { - global rowidlist rowoffsets uparrowlen idrowranges displayorder +proc makeuparrow {oid y x} { + global rowidlist uparrowlen idrowranges displayorder for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} { incr y -1 - incr x $z - set off0 [lindex $rowoffsets $y] - for {set x0 $x} {1} {incr x0} { - if {$x0 >= [llength $off0]} { - set x0 [llength [lindex $rowoffsets [expr {$y-1}]]] - break - } - set z [lindex $off0 $x0] - if {$z ne {}} { - incr x0 $z - break - } - } - set z [expr {$x0 - $x}] - lset rowidlist $y [linsert [lindex $rowidlist $y] $x $oid] - lset rowoffsets $y [linsert [lindex $rowoffsets $y] $x $z] + set idl [lindex $rowidlist $y] + set x [idcol $idl $oid $x] + lset rowidlist $y [linsert $idl $x $oid] } - set tmp [lreplace [lindex $rowoffsets $y] $x $x {}] - lset rowoffsets $y [incrange $tmp [expr {$x+1}] -1] lappend idrowranges($oid) [lindex $displayorder $y] } proc initlayout {} { - global rowidlist rowoffsets displayorder commitlisted + global rowidlist displayorder commitlisted global rowlaidout rowoptim global idinlist rowchk rowrangelist idrowranges global numcommits canvxmax canv @@ -2618,7 +2626,6 @@ proc initlayout {} { set rowrangelist {} set nextcolor 0 set rowidlist {{}} - set rowoffsets {{}} catch {unset idinlist} catch {unset rowchk} set rowlaidout 0 @@ -2679,8 +2686,8 @@ proc layoutmore {tmax allread} { set nr [expr {$commitidx($curview) - $rowlaidout}] # may need to increase this threshold if uparrowlen or # mingaplen are increased... - if {$nr > 150} { - set nr 150 + if {$nr > 200} { + set nr 200 } set row $rowlaidout set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread] @@ -2861,7 +2868,7 @@ proc readdifffiles {fd serial} { } proc layoutrows {row endrow last} { - global rowidlist rowoffsets displayorder + global rowidlist displayorder global uparrowlen downarrowlen maxwidth mingaplen global children parentlist global idrowranges @@ -2869,12 +2876,12 @@ proc layoutrows {row endrow last} { global idinlist rowchk rowrangelist set idlist [lindex $rowidlist $row] - set offs [lindex $rowoffsets $row] while {$row < $endrow} { set id [lindex $displayorder $row] set oldolds {} set newolds {} - foreach p [lindex $parentlist $row] { + set olds [lindex $parentlist $row] + foreach p $olds { if {![info exists idinlist($p)]} { lappend newolds $p } elseif {!$idinlist($p)} { @@ -2883,7 +2890,7 @@ proc layoutrows {row endrow last} { } set nev [expr {[llength $idlist] + [llength $newolds] + [llength $oldolds] - $maxwidth + 1}] - if {$nev > 0} { + if {1 || $nev > 0} { if {!$last && $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break for {set x [llength $idlist]} {[incr x -1] >= 0} {} { @@ -2893,34 +2900,25 @@ proc layoutrows {row endrow last} { [expr {$row + $uparrowlen + $mingaplen}]] if {$r == 0} { set idlist [lreplace $idlist $x $x] - set offs [lreplace $offs $x $x] - set offs [incrange $offs $x 1] set idinlist($i) 0 set rm1 [expr {$row - 1}] lappend idrowranges($i) [lindex $displayorder $rm1] - if {[incr nev -1] <= 0} break + #if {[incr nev -1] <= 0} break continue } set rowchk($id) [expr {$row + $r}] } } lset rowidlist $row $idlist - lset rowoffsets $row $offs } set col [lsearch -exact $idlist $id] if {$col < 0} { - set col [llength $idlist] - lappend idlist $id + set col [idcol $idlist $id] + set idlist [linsert $idlist $col $id] lset rowidlist $row $idlist - set z {} if {$children($curview,$id) ne {}} { - set z [expr {[llength [lindex $rowidlist [expr {$row-1}]]] - $col}] unset idinlist($id) - } - lappend offs $z - lset rowoffsets $row $offs - if {$z ne {}} { - makeuparrow $id $col $row $z + makeuparrow $id $row $col } } else { unset idinlist($id) @@ -2933,38 +2931,21 @@ proc layoutrows {row endrow last} { } lappend rowrangelist $ranges incr row - set offs [ntimes [llength $idlist] 0] - set l [llength $newolds] - set idlist [eval lreplace \$idlist $col $col $newolds] - set o 0 - if {$l != 1} { - set offs [lrange $offs 0 [expr {$col - 1}]] - foreach x $newolds { - lappend offs {} - incr o -1 - } - incr o - set tmp [expr {[llength $idlist] - [llength $offs]}] - if {$tmp > 0} { - set offs [concat $offs [ntimes $tmp $o]] - } - } else { - lset offs $col {} - } + set idlist [lreplace $idlist $col $col] + set x $col foreach i $newolds { + set x [idcol $idlist $i $x] + set idlist [linsert $idlist $x $i] set idinlist($i) 1 set idrowranges($i) $id } - incr col $l foreach oid $oldolds { set idinlist($oid) 1 - set idlist [linsert $idlist $col $oid] - set offs [linsert $offs $col $o] - makeuparrow $oid $col $row $o - incr col + set x [idcol $idlist $oid $x] + set idlist [linsert $idlist $x $oid] + makeuparrow $oid $row $x } lappend rowidlist $idlist - lappend rowoffsets $offs } return $row } @@ -2989,7 +2970,7 @@ proc addextraid {id row} { } proc layouttail {} { - global rowidlist rowoffsets idinlist commitidx curview + global rowidlist idinlist commitidx curview global idrowranges rowrangelist set row $commitidx($curview) @@ -3003,56 +2984,70 @@ proc layouttail {} { lappend rowrangelist $idrowranges($id) unset idrowranges($id) incr row - set offs [ntimes $col 0] set idlist [lreplace $idlist $col $col] lappend rowidlist $idlist - lappend rowoffsets $offs } foreach id [array names idinlist] { unset idinlist($id) addextraid $id $row lset rowidlist $row [list $id] - lset rowoffsets $row 0 - makeuparrow $id 0 $row 0 + makeuparrow $id $row 0 lappend idrowranges($id) $id lappend rowrangelist $idrowranges($id) unset idrowranges($id) incr row lappend rowidlist {} - lappend rowoffsets {} } } proc insert_pad {row col npad} { - global rowidlist rowoffsets + global rowidlist set pad [ntimes $npad {}] lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad] - set tmp [eval linsert [list [lindex $rowoffsets $row]] $col $pad] - lset rowoffsets $row [incrange $tmp [expr {$col + $npad}] [expr {-$npad}]] } proc optimize_rows {row col endrow} { - global rowidlist rowoffsets displayorder + global rowidlist displayorder + if {$row < 1} { + set row 1 + } + set idlist [lindex $rowidlist [expr {$row - 1}]] + if {$row >= 2} { + set previdlist [lindex $rowidlist [expr {$row - 2}]] + } else { + set previdlist {} + } for {} {$row < $endrow} {incr row} { + set pprevidlist $previdlist + set previdlist $idlist set idlist [lindex $rowidlist $row] - set offs [lindex $rowoffsets $row] set haspad 0 - for {} {$col < [llength $offs]} {incr col} { - if {[lindex $idlist $col] eq {}} { + set y0 [expr {$row - 1}] + set ym [expr {$row - 2}] + set x0 -1 + set xm -1 + for {} {$col < [llength $idlist]} {incr col} { + set id [lindex $idlist $col] + if {[lindex $previdlist $col] eq $id} continue + if {$id eq {}} { set haspad 1 continue } - set z [lindex $offs $col] - if {$z eq {}} continue + set x0 [lsearch -exact $previdlist $id] + if {$x0 < 0} continue + set z [expr {$x0 - $col}] set isarrow 0 - set x0 [expr {$col + $z}] - set y0 [expr {$row - 1}] - set z0 [lindex $rowoffsets $y0 $x0] + set z0 {} + if {$ym >= 0} { + set xm [lsearch -exact $pprevidlist $id] + if {$xm >= 0} { + set z0 [expr {$xm - $x0}] + } + } if {$z0 eq {}} { - set id [lindex $idlist $col] set ranges [rowranges $id] if {$ranges ne {} && $y0 > [lindex $ranges 0]} { set isarrow 1 @@ -3066,43 +3061,32 @@ proc optimize_rows {row col endrow} { # Line currently goes left too much; # insert pads in the previous row, then optimize it set npad [expr {-1 - $z + $isarrow}] - set offs [incrange $offs $col $npad] insert_pad $y0 $x0 $npad if {$y0 > 0} { optimize_rows $y0 $x0 $row } - set z [lindex $offs $col] - set x0 [expr {$col + $z}] - set z0 [lindex $rowoffsets $y0 $x0] + set previdlist [lindex $rowidlist $y0] + set x0 [lsearch -exact $previdlist $id] + set z [expr {$x0 - $col}] + if {$z0 ne {}} { + set pprevidlist [lindex $rowidlist $ym] + set xm [lsearch -exact $pprevidlist $id] + set z0 [expr {$xm - $x0}] + } } elseif {$z > 1 || ($z > 0 && $isarrow)} { # Line currently goes right too much; - # insert pads in this line and adjust the next's rowoffsets + # insert pads in this line set npad [expr {$z - 1 + $isarrow}] - set y1 [expr {$row + 1}] - set offs2 [lindex $rowoffsets $y1] - set x1 -1 - foreach z $offs2 { - incr x1 - if {$z eq {} || $x1 + $z < $col} continue - if {$x1 + $z > $col} { - incr npad - } - lset rowoffsets $y1 [incrange $offs2 $x1 $npad] - break - } set pad [ntimes $npad {}] set idlist [eval linsert \$idlist $col $pad] - set tmp [eval linsert \$offs $col $pad] incr col $npad - set offs [incrange $tmp $col [expr {-$npad}]] - set z [lindex $offs $col] + set z [expr {$x0 - $col}] set haspad 1 } - if {$z0 eq {} && !$isarrow} { + if {$z0 eq {} && !$isarrow && $ym >= 0} { # this line links to its first child on row $row-2 - set rm2 [expr {$row - 2}] - set id [lindex $displayorder $rm2] - set xc [lsearch -exact [lindex $rowidlist $rm2] $id] + set id [lindex $displayorder $ym] + set xc [lsearch -exact $pprevidlist $id] if {$xc >= 0} { set z0 [expr {$xc - $x0}] } @@ -3110,51 +3094,36 @@ proc optimize_rows {row col endrow} { # avoid lines jigging left then immediately right if {$z0 ne {} && $z < 0 && $z0 > 0} { insert_pad $y0 $x0 1 - set offs [incrange $offs $col 1] - optimize_rows $y0 [expr {$x0 + 1}] $row + incr x0 + optimize_rows $y0 $x0 $row + set previdlist [lindex $rowidlist $y0] + set pprevidlist [lindex $rowidlist $ym] } } if {!$haspad} { - set o {} # Find the first column that doesn't have a line going right for {set col [llength $idlist]} {[incr col -1] >= 0} {} { - set o [lindex $offs $col] - if {$o eq {}} { + set id [lindex $idlist $col] + if {$id eq {}} break + set x0 [lsearch -exact $previdlist $id] + if {$x0 < 0} { # check if this is the link to the first child - set id [lindex $idlist $col] set ranges [rowranges $id] if {$ranges ne {} && $row == [lindex $ranges 0]} { # it is, work out offset to child - set y0 [expr {$row - 1}] set id [lindex $displayorder $y0] - set x0 [lsearch -exact [lindex $rowidlist $y0] $id] - if {$x0 >= 0} { - set o [expr {$x0 - $col}] - } + set x0 [lsearch -exact $previdlist $id] } } - if {$o eq {} || $o <= 0} break + if {$x0 <= $col} break } # Insert a pad at that column as long as it has a line and - # isn't the last column, and adjust the next row' offsets - if {$o ne {} && [incr col] < [llength $idlist]} { - set y1 [expr {$row + 1}] - set offs2 [lindex $rowoffsets $y1] - set x1 -1 - foreach z $offs2 { - incr x1 - if {$z eq {} || $x1 + $z < $col} continue - lset rowoffsets $y1 [incrange $offs2 $x1 1] - break - } + # isn't the last column + if {$x0 >= 0 && [incr col] < [llength $idlist]} { set idlist [linsert $idlist $col {}] - set tmp [linsert $offs $col {}] - incr col - set offs [incrange $tmp $col -1] } } lset rowidlist $row $idlist - lset rowoffsets $row $offs set col 0 } } @@ -3669,7 +3638,7 @@ proc clear_display {} { } proc findcrossings {id} { - global rowidlist parentlist numcommits rowoffsets displayorder + global rowidlist parentlist numcommits displayorder set cross {} set ccross {} @@ -3678,12 +3647,9 @@ proc findcrossings {id} { set e [expr {$numcommits - 1}] } if {$e <= $s} continue - set x [lsearch -exact [lindex $rowidlist $e] $id] - if {$x < 0} { - puts "findcrossings: oops, no [shortids $id] in row $e" - continue - } for {set row $e} {[incr row -1] >= $s} {} { + set x [lsearch -exact [lindex $rowidlist $row] $id] + if {$x < 0} break set olds [lindex $parentlist $row] set kid [lindex $displayorder $row] set kidx [lsearch -exact [lindex $rowidlist $row] $kid] @@ -3701,9 +3667,6 @@ proc findcrossings {id} { } } } - set inc [lindex $rowoffsets $row $x] - if {$inc eq {}} break - incr x $inc } } return [concat $ccross {{}} $cross] @@ -3893,7 +3856,7 @@ proc show_status {msg} { # on that row and below will move down one row. proc insertrow {row newcmit} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist rowoffsets numcommits + global commitrow curview rowidlist numcommits global rowrangelist rowlaidout rowoptim numcommits global selectedline rowchk commitidx @@ -3917,26 +3880,14 @@ proc insertrow {row newcmit} { incr commitidx($curview) set idlist [lindex $rowidlist $row] - set offs [lindex $rowoffsets $row] - set newoffs {} - foreach x $idlist { - if {$x eq {} || ($x eq $p && [llength $kids] == 1)} { - lappend newoffs {} - } else { - lappend newoffs 0 - } - } if {[llength $kids] == 1} { set col [lsearch -exact $idlist $p] lset idlist $col $newcmit } else { set col [llength $idlist] lappend idlist $newcmit - lappend offs {} - lset rowoffsets $row $offs } set rowidlist [linsert $rowidlist $row $idlist] - set rowoffsets [linsert $rowoffsets [expr {$row+1}] $newoffs] set rowrangelist [linsert $rowrangelist $row {}] if {[llength $kids] > 1} { @@ -3965,7 +3916,7 @@ proc insertrow {row newcmit} { # Remove a commit that was inserted with insertrow on row $row. proc removerow {row} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist rowoffsets numcommits + global commitrow curview rowidlist numcommits global rowrangelist idrowranges rowlaidout rowoptim numcommits global linesegends selectedline rowchk commitidx @@ -3993,12 +3944,6 @@ proc removerow {row} { incr commitidx($curview) -1 set rowidlist [lreplace $rowidlist $row $row] - set rowoffsets [lreplace $rowoffsets $rp1 $rp1] - if {$kids ne {}} { - set offs [lindex $rowoffsets $row] - set offs [lreplace $offs end end] - lset rowoffsets $row $offs - } set rowrangelist [lreplace $rowrangelist $row $row] if {[llength $kids] > 0} { @@ -7590,9 +7535,9 @@ set maxgraphpct 50 set maxwidth 16 set revlistorder 0 set fastdate 0 -set uparrowlen 7 -set downarrowlen 7 -set mingaplen 30 +set uparrowlen 5 +set downarrowlen 5 +set mingaplen 100 set cmitmode "patch" set wrapcomment "none" set showneartags 1 From 513a54dc212044596d932dcc9468e0774c1ee2c1 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 1 Aug 2007 22:27:57 +1000 Subject: [PATCH 002/123] gitk: Improve the drawing of links to parent lines The way gitk used to draw the lines joining a commit to the lines representing its parents was sometimes visually ambiguous, especially when the line to the parent had a corner that coincided with a corner on another line. This improves things by using a smaller slanting section on the line joining a commit to a parent line if the parent line is vertical where it joins on. It also optimizes the drawing a little in the case where the parent line slants towards this commit already. Signed-off-by: Paul Mackerras --- gitk | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/gitk b/gitk index 40e5d31749..bc3022e69f 100755 --- a/gitk +++ b/gitk @@ -3363,7 +3363,7 @@ proc drawlineseg {id row endrow arrowlow} { proc drawparentlinks {id row} { global rowidlist canv colormap curview parentlist - global idpos + global idpos linespc set rowids [lindex $rowidlist $row] set col [lsearch -exact $rowids $id] @@ -3373,6 +3373,8 @@ proc drawparentlinks {id row} { set x [xc $row $col] set y [yc $row] set y2 [yc $row2] + set d [expr {int(0.4 * $linespc)}] + set ymid [expr {$y + $d}] set ids [lindex $rowidlist $row2] # rmx = right-most X coord used set rmx 0 @@ -3386,19 +3388,37 @@ proc drawparentlinks {id row} { if {$x2 > $rmx} { set rmx $x2 } - if {[lsearch -exact $rowids $p] < 0} { + set j [lsearch -exact $rowids $p] + if {$j < 0} { # drawlineseg will do this one for us continue } assigncolor $p # should handle duplicated parents here... set coords [list $x $y] - if {$i < $col - 1} { - lappend coords [xc $row [expr {$i + 1}]] $y - } elseif {$i > $col + 1} { - lappend coords [xc $row [expr {$i - 1}]] $y + if {$i != $col} { + # if attaching to a vertical segment, draw a smaller + # slant for visual distinctness + if {$i == $j} { + if {$i < $col} { + lappend coords [expr {$x2 + $d}] $y $x2 $ymid + } else { + lappend coords [expr {$x2 - $d}] $y $x2 $ymid + } + } elseif {$i < $col && $i < $j} { + # segment slants towards us already + lappend coords [xc $row $j] $y + } else { + if {$i < $col - 1} { + lappend coords [expr {$x2 + $linespc}] $y + } elseif {$i > $col + 1} { + lappend coords [expr {$x2 - $linespc}] $y + } + lappend coords $x2 $y2 + } + } else { + lappend coords $x2 $y2 } - lappend coords $x2 $y2 set t [$canv create line $coords -width [linewidth $p] \ -fill $colormap($p) -tags lines.$p] $canv lower $t From e341c06d8140b689001ddc183ec3476c1ede264a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 12 Aug 2007 12:42:57 +1000 Subject: [PATCH 003/123] gitk: Eliminate diagonal arrows This changes the optimizer to insert pads to straighten downward pointing arrows so they point straight down. When drawing the parent link to the first child in drawlineseg, this draws it with 3 segments like other parent links if it is only one row high with an arrow. These two things mean we can dispense with the workarounds for arrows on diagonal segments. This also fixes a couple of other minor bugs. Signed-off-by: Paul Mackerras --- gitk | 87 +++++++++++++++++++++++++----------------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) diff --git a/gitk b/gitk index bc3022e69f..7b62e98ec1 100755 --- a/gitk +++ b/gitk @@ -2600,7 +2600,7 @@ proc idcol {idlist id {i 0}} { proc makeuparrow {oid y x} { global rowidlist uparrowlen idrowranges displayorder - for {set i 1} {$i < $uparrowlen && $y > 1} {incr i} { + for {set i 0} {$i < $uparrowlen && $y > 1} {incr i} { incr y -1 set idl [lindex $rowidlist $y] set x [idcol $idl $oid $x] @@ -3005,7 +3005,14 @@ proc insert_pad {row col npad} { global rowidlist set pad [ntimes $npad {}] - lset rowidlist $row [eval linsert [list [lindex $rowidlist $row]] $col $pad] + set idlist [lindex $rowidlist $row] + set bef [lrange $idlist 0 [expr {$col - 1}]] + set aft [lrange $idlist $col end] + set i [lsearch -exact $aft {}] + if {$i > 0} { + set aft [lreplace $aft $i $i] + } + lset rowidlist $row [concat $bef $pad $aft] } proc optimize_rows {row col endrow} { @@ -3053,6 +3060,10 @@ proc optimize_rows {row col endrow} { set isarrow 1 } } + if {!$isarrow && $id ne [lindex $displayorder $row] && + [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} { + set isarrow 1 + } # Looking at lines from this row to the previous row, # make them go straight up if they end in an arrow on # the previous row; otherwise make them go straight up @@ -3077,8 +3088,8 @@ proc optimize_rows {row col endrow} { # Line currently goes right too much; # insert pads in this line set npad [expr {$z - 1 + $isarrow}] - set pad [ntimes $npad {}] - set idlist [eval linsert \$idlist $col $pad] + insert_pad $row $col $npad + set idlist [lindex $rowidlist $row] incr col $npad set z [expr {$x0 - $col}] set haspad 1 @@ -3169,31 +3180,9 @@ proc rowranges {id} { return $linenos } -# work around tk8.4 refusal to draw arrows on diagonal segments -proc adjarrowhigh {coords} { - global linespc - - set x0 [lindex $coords 0] - set x1 [lindex $coords 2] - if {$x0 != $x1} { - set y0 [lindex $coords 1] - set y1 [lindex $coords 3] - if {$y0 - $y1 <= 2 * $linespc && $x1 == [lindex $coords 4]} { - # we have a nearby vertical segment, just trim off the diag bit - set coords [lrange $coords 2 end] - } else { - set slope [expr {($x0 - $x1) / ($y0 - $y1)}] - set xi [expr {$x0 - $slope * $linespc / 2}] - set yi [expr {$y0 - $linespc / 2}] - set coords [lreplace $coords 0 1 $xi $y0 $xi $yi] - } - } - return $coords -} - proc drawlineseg {id row endrow arrowlow} { global rowidlist displayorder iddrawn linesegs - global canv colormap linespc curview maxlinelen + global canv colormap linespc curview maxlinelen parentlist set cols [list [lsearch -exact [lindex $rowidlist $row] $id]] set le [expr {$row + 1}] @@ -3268,9 +3257,11 @@ proc drawlineseg {id row endrow arrowlow} { set itl [lindex $lines [expr {$i-1}] 2] set al [$canv itemcget $itl -arrow] set arrowlow [expr {$al eq "last" || $al eq "both"}] - } elseif {$arrowlow && - [lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0} { - set arrowlow 0 + } elseif {$arrowlow} { + if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 || + [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} { + set arrowlow 0 + } } set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]] for {set y $le} {[incr y -1] > $row} {} { @@ -3289,8 +3280,19 @@ proc drawlineseg {id row endrow arrowlow} { set xc [lsearch -exact [lindex $rowidlist $row] $ch] if {$xc < 0} { puts "oops: drawlineseg: child $ch not on row $row" - } else { - if {$xc < $x - 1} { + } elseif {$xc != $x} { + if {($arrowhigh && $le == $row + 1) || $dir == 0} { + set d [expr {int(0.5 * $linespc)}] + set x1 [xc $row $x] + if {$xc < $x} { + set x2 [expr {$x1 - $d}] + } else { + set x2 [expr {$x1 + $d}] + } + set y2 [yc $row] + set y1 [expr {$y2 + $d}] + lappend coords $x1 $y1 $x2 $y2 + } elseif {$xc < $x - 1} { lappend coords [xc $row [expr {$x-1}]] [yc $row] } elseif {$xc > $x + 1} { lappend coords [xc $row [expr {$x+1}]] [yc $row] @@ -3301,23 +3303,9 @@ proc drawlineseg {id row endrow arrowlow} { } else { set xn [xc $row $xp] set yn [yc $row] - # work around tk8.4 refusal to draw arrows on diagonal segments - if {$arrowlow && $xn != [lindex $coords end-1]} { - if {[llength $coords] < 4 || - [lindex $coords end-3] != [lindex $coords end-1] || - [lindex $coords end] - $yn > 2 * $linespc} { - set xn [xc $row [expr {$xp - 0.5 * $dir}]] - set yo [yc [expr {$row + 0.5}]] - lappend coords $xn $yo $xn $yn - } - } else { - lappend coords $xn $yn - } + lappend coords $xn $yn } if {!$joinhigh} { - if {$arrowhigh} { - set coords [adjarrowhigh $coords] - } assigncolor $id set t [$canv create line $coords -width [linewidth $id] \ -fill $colormap($id) -tags lines.$id -arrow $arrow] @@ -3341,9 +3329,6 @@ proc drawlineseg {id row endrow arrowlow} { set coords [concat $coords $clow] if {!$joinhigh} { lset lines [expr {$i-1}] 1 $le - if {$arrowhigh} { - set coords [adjarrowhigh $coords] - } } else { # coalesce two pieces $canv delete $ith @@ -3373,7 +3358,7 @@ proc drawparentlinks {id row} { set x [xc $row $col] set y [yc $row] set y2 [yc $row2] - set d [expr {int(0.4 * $linespc)}] + set d [expr {int(0.5 * $linespc)}] set ymid [expr {$y + $d}] set ids [lindex $rowidlist $row2] # rmx = right-most X coord used From 92ed666fa761554c67c8f883863517870a65376d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 22 Aug 2007 22:35:28 +1000 Subject: [PATCH 004/123] gitk: Get rid of idrowranges and rowrangelist Instead make the rowranges procedure compute its result by looking in the rowidlist entries for the rows around the children of the id and the id itself. This turns out not to take too long, and not having to maintain idrowranges and rowrangelist speeds up the layout. This also makes optimize_rows not use rowranges, since all it really needed was a way to work out if one id is the first child of another, so it can just look at the children list. Signed-off-by: Paul Mackerras --- gitk | 142 +++++++++++++++++++++++++++-------------------------------- 1 file changed, 66 insertions(+), 76 deletions(-) diff --git a/gitk b/gitk index d2f5eeeaaf..a29c793830 100755 --- a/gitk +++ b/gitk @@ -1927,7 +1927,7 @@ proc showview {n} { global curview viewdata viewfiles global displayorder parentlist rowidlist global colormap rowtextx commitrow nextcolor canvxmax - global numcommits rowrangelist commitlisted idrowranges rowchk + global numcommits commitlisted rowchk global selectedline currentid canv canvy0 global treediffs global pending_select phase @@ -1963,13 +1963,13 @@ proc showview {n} { set vcmitlisted($curview) $commitlisted if {$phase ne {}} { set viewdata($curview) \ - [list $phase $rowidlist {} $rowrangelist \ - [flatten idrowranges] [flatten idinlist] \ + [list $phase $rowidlist {} {} \ + {} [flatten idinlist] \ $rowlaidout $rowoptim $numcommits] } elseif {![info exists viewdata($curview)] || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list {} $rowidlist {} $rowrangelist] + [list {} $rowidlist {} {}] } } catch {unset treediffs} @@ -1998,12 +1998,9 @@ proc showview {n} { set parentlist $vparentlist($n) set commitlisted $vcmitlisted($n) set rowidlist [lindex $v 1] - set rowrangelist [lindex $v 3] if {$phase eq {}} { set numcommits [llength $displayorder] - catch {unset idrowranges} } else { - unflatten idrowranges [lindex $v 4] unflatten idinlist [lindex $v 5] set rowlaidout [lindex $v 6] set rowoptim [lindex $v 7] @@ -2670,7 +2667,7 @@ proc idcol {idlist id {i 0}} { } proc makeuparrow {oid y x} { - global rowidlist uparrowlen idrowranges displayorder + global rowidlist uparrowlen displayorder for {set i 0} {$i < $uparrowlen && $y > 1} {incr i} { incr y -1 @@ -2678,13 +2675,12 @@ proc makeuparrow {oid y x} { set x [idcol $idl $oid $x] lset rowidlist $y [linsert $idl $x $oid] } - lappend idrowranges($oid) [lindex $displayorder $y] } proc initlayout {} { global rowidlist displayorder commitlisted global rowlaidout rowoptim - global idinlist rowchk rowrangelist idrowranges + global idinlist rowchk global numcommits canvxmax canv global nextcolor global parentlist @@ -2695,7 +2691,6 @@ proc initlayout {} { set displayorder {} set commitlisted {} set parentlist {} - set rowrangelist {} set nextcolor 0 set rowidlist {{}} catch {unset idinlist} @@ -2705,7 +2700,6 @@ proc initlayout {} { set canvxmax [$canv cget -width] catch {unset colormap} catch {unset rowtextx} - catch {unset idrowranges} set selectfirst 1 } @@ -2952,9 +2946,8 @@ proc layoutrows {row endrow last} { global rowidlist displayorder global uparrowlen downarrowlen maxwidth mingaplen global children parentlist - global idrowranges global commitidx curview - global idinlist rowchk rowrangelist + global idinlist rowchk set idlist [lindex $rowidlist $row] while {$row < $endrow} { @@ -2970,8 +2963,6 @@ proc layoutrows {row endrow last} { if {$r == 0} { set idlist [lreplace $idlist $x $x] set idinlist($i) 0 - set rm1 [expr {$row - 1}] - lappend idrowranges($i) [lindex $displayorder $rm1] continue } set rowchk($i) [expr {$row + $r}] @@ -3001,20 +2992,12 @@ proc layoutrows {row endrow last} { } else { unset idinlist($id) } - set ranges {} - if {[info exists idrowranges($id)]} { - set ranges $idrowranges($id) - lappend ranges $id - unset idrowranges($id) - } - lappend rowrangelist $ranges incr row set idlist [lreplace $idlist $col $col] set x $col foreach i $newolds { set x [idcol $idlist $i $x] set idlist [linsert $idlist $x $i] - set idrowranges($i) $id } foreach oid $oldolds { set x [idcol $idlist $oid $x] @@ -3047,7 +3030,6 @@ proc addextraid {id row} { proc layouttail {} { global rowidlist idinlist commitidx curview - global idrowranges rowrangelist set row $commitidx($curview) set idlist [lindex $rowidlist $row] @@ -3056,9 +3038,6 @@ proc layouttail {} { set id [lindex $idlist $col] addextraid $id $row catch {unset idinlist($id)} - lappend idrowranges($id) $id - lappend rowrangelist $idrowranges($id) - unset idrowranges($id) incr row set idlist [lreplace $idlist $col $col] lappend rowidlist $idlist @@ -3069,9 +3048,6 @@ proc layouttail {} { addextraid $id $row lset rowidlist $row [list $id] makeuparrow $id $row 0 - lappend idrowranges($id) $id - lappend rowrangelist $idrowranges($id) - unset idrowranges($id) incr row lappend rowidlist {} } @@ -3092,7 +3068,7 @@ proc insert_pad {row col npad} { } proc optimize_rows {row col endrow} { - global rowidlist displayorder + global rowidlist displayorder curview children if {$row < 1} { set row 1 @@ -3131,8 +3107,9 @@ proc optimize_rows {row col endrow} { } } if {$z0 eq {}} { - set ranges [rowranges $id] - if {$ranges ne {} && $y0 > [lindex $ranges 0]} { + # if row y0 is the first child of $id then it's not an arrow + if {[lindex $children($curview,$id) 0] ne + [lindex $displayorder $y0]} { set isarrow 1 } } @@ -3195,11 +3172,10 @@ proc optimize_rows {row col endrow} { set x0 [lsearch -exact $previdlist $id] if {$x0 < 0} { # check if this is the link to the first child - set ranges [rowranges $id] - if {$ranges ne {} && $row == [lindex $ranges 0]} { + set kid [lindex $displayorder $y0] + if {[lindex $children($curview,$id) 0] eq $kid} { # it is, work out offset to child - set id [lindex $displayorder $y0] - set x0 [lsearch -exact $previdlist $id] + set x0 [lsearch -exact $previdlist $kid] } } if {$x0 <= $col} break @@ -3236,24 +3212,59 @@ proc linewidth {id} { } proc rowranges {id} { - global phase idrowranges commitrow rowlaidout rowrangelist curview + global commitrow curview children uparrowlen downarrowlen + global rowidlist - set ranges {} - if {$phase eq {} || - ([info exists commitrow($curview,$id)] - && $commitrow($curview,$id) < $rowlaidout)} { - set ranges [lindex $rowrangelist $commitrow($curview,$id)] - } elseif {[info exists idrowranges($id)]} { - set ranges $idrowranges($id) + set kids $children($curview,$id) + if {$kids eq {}} { + return {} } - set linenos {} - foreach rid $ranges { - lappend linenos $commitrow($curview,$rid) + set ret {} + lappend kids $id + foreach child $kids { + if {![info exists commitrow($curview,$child)]} break + set row $commitrow($curview,$child) + if {![info exists prev]} { + lappend ret [expr {$row + 1}] + } else { + if {$row <= $prevrow} { + puts "oops children out of order [shortids $id] $row < [shortids $prev] $prevrow" + } + # see if the line extends the whole way from prevrow to row + if {$row > $prevrow + $uparrowlen + $downarrowlen && + [lsearch -exact [lindex $rowidlist \ + [expr {int(($row + $prevrow) / 2)}]] $id] < 0} { + # it doesn't, see where it ends + set r [expr {$prevrow + $downarrowlen}] + if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} { + while {[incr r -1] > $prevrow && + [lsearch -exact [lindex $rowidlist $r] $id] < 0} {} + } else { + while {[incr r] <= $row && + [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {} + incr r -1 + } + lappend ret $r + # see where it starts up again + set r [expr {$row - $uparrowlen}] + if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} { + while {[incr r] < $row && + [lsearch -exact [lindex $rowidlist $r] $id] < 0} {} + } else { + while {[incr r -1] >= $prevrow && + [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {} + incr r + } + lappend ret $r + } + } + if {$child eq $id} { + lappend ret $row + } + set prev $id + set prevrow $row } - if {$linenos ne {}} { - lset linenos 0 [expr {[lindex $linenos 0] + 1}] - } - return $linenos + return $ret } proc drawlineseg {id row endrow arrowlow} { @@ -3938,7 +3949,7 @@ proc show_status {msg} { proc insertrow {row newcmit} { global displayorder parentlist commitlisted children global commitrow curview rowidlist numcommits - global rowrangelist rowlaidout rowoptim numcommits + global rowlaidout rowoptim numcommits global selectedline rowchk commitidx if {$row >= $numcommits} { @@ -3970,18 +3981,6 @@ proc insertrow {row newcmit} { } set rowidlist [linsert $rowidlist $row $idlist] - set rowrangelist [linsert $rowrangelist $row {}] - if {[llength $kids] > 1} { - set rp1 [expr {$row + 1}] - set ranges [lindex $rowrangelist $rp1] - if {$ranges eq {}} { - set ranges [list $newcmit $p] - } elseif {[lindex $ranges end-1] eq $p} { - lset ranges end-1 $newcmit - } - lset rowrangelist $rp1 $ranges - } - catch {unset rowchk} incr rowlaidout @@ -3998,7 +3997,7 @@ proc insertrow {row newcmit} { proc removerow {row} { global displayorder parentlist commitlisted children global commitrow curview rowidlist numcommits - global rowrangelist idrowranges rowlaidout rowoptim numcommits + global rowlaidout rowoptim numcommits global linesegends selectedline rowchk commitidx if {$row >= $numcommits} { @@ -4026,15 +4025,6 @@ proc removerow {row} { set rowidlist [lreplace $rowidlist $row $row] - set rowrangelist [lreplace $rowrangelist $row $row] - if {[llength $kids] > 0} { - set ranges [lindex $rowrangelist $row] - if {[lindex $ranges end-1] eq $id} { - set ranges [lreplace $ranges end-1 end] - lset rowrangelist $row $ranges - } - } - catch {unset rowchk} incr rowlaidout -1 From b0cdca996a3717552ee30e8cc2bd157bb32fd213 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 23 Aug 2007 19:35:51 +1000 Subject: [PATCH 005/123] gitk: Get rid of idinlist array This changes layoutrows to use information from rowidlist and children to work out which parent ids are appearing for the first time or need an up arrow, instead of using idinlist. To detect the situation where git log doesn't give us all the commits it references, this adds an idpending array that is updated and used by getcommitlines. This also fixes a bug where we weren't resetting the ordertok array when updating the list of commits; this fixes that too, and a bug where we could try to access an undefined element of commitrow if the user did an update before gitk had finished reading in the graph. Signed-off-by: Paul Mackerras --- gitk | 84 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/gitk b/gitk index a29c793830..7b0b4cfade 100755 --- a/gitk +++ b/gitk @@ -151,7 +151,7 @@ proc getcommitlines {fd view} { global displayorder commitidx commitrow commitdata global parentlist children curview hlview global vparentlist vdisporder vcmitlisted - global ordertok vnextroot + global ordertok vnextroot idpending set stuff [read $fd 500000] # git log doesn't terminate the last commit with a null... @@ -162,6 +162,23 @@ proc getcommitlines {fd view} { if {![eof $fd]} { return 1 } + # Check if we have seen any ids listed as parents that haven't + # appeared in the list + foreach vid [array names idpending "$view,*"] { + # should only get here if git log is buggy + set id [lindex [split $vid ","] 1] + set commitrow($vid) $commitidx($view) + incr commitidx($view) + if {$view == $curview} { + lappend parentlist {} + lappend displayorder $id + lappend commitlisted 0 + } else { + lappend vparentlist($view) {} + lappend vdisporder($view) $id + lappend vcmitlisted($view) 0 + } + } global viewname unset commfd($view) notbusy $view @@ -242,6 +259,7 @@ proc getcommitlines {fd view} { set ordertok($view,$id) $otok } else { set otok $ordertok($view,$id) + unset idpending($view,$id) } if {$listed} { set olds [lrange $ids 1 end] @@ -250,6 +268,7 @@ proc getcommitlines {fd view} { lappend children($view,$p) $id if {![info exists ordertok($view,$p)]} { set ordertok($view,$p) $ordertok($view,$id) + set idpending($view,$p) 1 } } else { set i 0 @@ -259,6 +278,7 @@ proc getcommitlines {fd view} { } if {![info exists ordertok($view,$p)]} { set ordertok($view,$p) "$otok[strrep $i]]" + set idpending($view,$p) 1 } incr i } @@ -328,7 +348,7 @@ proc readcommit {id} { } proc updatecommits {} { - global viewdata curview phase displayorder + global viewdata curview phase displayorder ordertok idpending global children commitrow selectedline thickerline showneartags if {$phase ne {}} { @@ -339,6 +359,10 @@ proc updatecommits {} { foreach id $displayorder { catch {unset children($n,$id)} catch {unset commitrow($n,$id)} + catch {unset ordertok($n,$id)} + } + foreach vid [array names idpending "$n,*"] { + unset idpending($vid) } set curview -1 catch {unset selectedline} @@ -1963,13 +1987,11 @@ proc showview {n} { set vcmitlisted($curview) $commitlisted if {$phase ne {}} { set viewdata($curview) \ - [list $phase $rowidlist {} {} \ - {} [flatten idinlist] \ - $rowlaidout $rowoptim $numcommits] + [list $phase $rowidlist $rowlaidout $rowoptim $numcommits] } elseif {![info exists viewdata($curview)] || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list {} $rowidlist {} {}] + [list {} $rowidlist] } } catch {unset treediffs} @@ -2001,10 +2023,9 @@ proc showview {n} { if {$phase eq {}} { set numcommits [llength $displayorder] } else { - unflatten idinlist [lindex $v 5] - set rowlaidout [lindex $v 6] - set rowoptim [lindex $v 7] - set numcommits [lindex $v 8] + set rowlaidout [lindex $v 2] + set rowoptim [lindex $v 3] + set numcommits [lindex $v 4] catch {unset rowchk} } @@ -2123,7 +2144,7 @@ proc addvhighlight {n} { } set hlview $n if {$n != $curview && ![info exists viewdata($n)]} { - set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}] + set viewdata($n) [list getcommits {{}} 0 0 0] set vparentlist($n) {} set vdisporder($n) {} set vcmitlisted($n) {} @@ -2635,9 +2656,11 @@ proc usedinrange {id l1 l2} { } set kids $children($curview,$id) foreach c $kids { - set r $commitrow($curview,$c) - if {$l1 <= $r && $r <= $l2} { - return [expr {$r - $l1 + 1}] + if {[info exists commitrow($curview,$c)]} { + set r $commitrow($curview,$c) + if {$l1 <= $r && $r <= $l2} { + return [expr {$r - $l1 + 1}] + } } } return 0 @@ -2680,7 +2703,7 @@ proc makeuparrow {oid y x} { proc initlayout {} { global rowidlist displayorder commitlisted global rowlaidout rowoptim - global idinlist rowchk + global rowchk global numcommits canvxmax canv global nextcolor global parentlist @@ -2693,7 +2716,6 @@ proc initlayout {} { set parentlist {} set nextcolor 0 set rowidlist {{}} - catch {unset idinlist} catch {unset rowchk} set rowlaidout 0 set rowoptim 0 @@ -2733,7 +2755,7 @@ proc visiblerows {} { proc layoutmore {tmax allread} { global rowlaidout rowoptim commitidx numcommits optim_delay - global uparrowlen curview rowidlist idinlist + global uparrowlen curview rowidlist set showlast 0 set showdelay $optim_delay @@ -2763,8 +2785,7 @@ proc layoutmore {tmax allread} { } elseif {$allread} { set optdelay 0 set nrows $commitidx($curview) - if {[lindex $rowidlist $nrows] ne {} || - [array names idinlist] ne {}} { + if {[lindex $rowidlist $nrows] ne {}} { layouttail set rowlaidout $commitidx($curview) } elseif {$rowoptim == $nrows} { @@ -2947,7 +2968,7 @@ proc layoutrows {row endrow last} { global uparrowlen downarrowlen maxwidth mingaplen global children parentlist global commitidx curview - global idinlist rowchk + global rowchk set idlist [lindex $rowidlist $row] while {$row < $endrow} { @@ -2962,7 +2983,6 @@ proc layoutrows {row endrow last} { [expr {$row + $uparrowlen + $mingaplen}]] if {$r == 0} { set idlist [lreplace $idlist $x $x] - set idinlist($i) 0 continue } set rowchk($i) [expr {$row + $r}] @@ -2973,12 +2993,12 @@ proc layoutrows {row endrow last} { set oldolds {} set newolds {} foreach p [lindex $parentlist $row] { - if {![info exists idinlist($p)]} { + # is id the first child of this parent? + if {$id eq [lindex $children($curview,$p) 0]} { lappend newolds $p - } elseif {!$idinlist($p)} { + } elseif {[lsearch -exact $idlist $p] < 0} { lappend oldolds $p } - set idinlist($p) 1 } set col [lsearch -exact $idlist $id] if {$col < 0} { @@ -2986,11 +3006,8 @@ proc layoutrows {row endrow last} { set idlist [linsert $idlist $col $id] lset rowidlist $row $idlist if {$children($curview,$id) ne {}} { - unset idinlist($id) makeuparrow $id $row $col } - } else { - unset idinlist($id) } incr row set idlist [lreplace $idlist $col $col] @@ -3029,7 +3046,7 @@ proc addextraid {id row} { } proc layouttail {} { - global rowidlist idinlist commitidx curview + global rowidlist commitidx curview set row $commitidx($curview) set idlist [lindex $rowidlist $row] @@ -3037,20 +3054,10 @@ proc layouttail {} { set col [expr {[llength $idlist] - 1}] set id [lindex $idlist $col] addextraid $id $row - catch {unset idinlist($id)} incr row set idlist [lreplace $idlist $col $col] lappend rowidlist $idlist } - - foreach id [array names idinlist] { - unset idinlist($id) - addextraid $id $row - lset rowidlist $row [list $id] - makeuparrow $id $row 0 - incr row - lappend rowidlist {} - } } proc insert_pad {row col npad} { @@ -4205,6 +4212,7 @@ proc findmorerev {} { set last 0 for {} {$l > $lim} {incr l -1} { set id [lindex $displayorder $l] + if {![info exists commitdata($id)]} continue if {![doesmatch $commitdata($id)]} continue if {![info exists commitinfo($id)]} { getcommit $id From 97645683bff498e369c1c24ce10e78b51cdaf468 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 23 Aug 2007 22:24:38 +1000 Subject: [PATCH 006/123] gitk: Fix some problems with the display of ids as links First, this fixes the problem where a SHA1 id wouldn't be displayed as a link if it wasn't in the part of the graph that had been laid out at the time the details pane was filled in, even if that commit later became part of the graph. This arranges for us to turn the SHA1 id into a link when we get to that id in laying out the graph. Secondly, there was a problem where the cursor wouldn't always turn to a hand when over a link, because the areas for two links could overlap slightly. This fixes that by using a counter rather than always reverting to a counter when we leave the region of a link (which can happen just after we've entered a different link). Signed-off-by: Paul Mackerras --- gitk | 87 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/gitk b/gitk index 7b0b4cfade..c795e9838e 100755 --- a/gitk +++ b/gitk @@ -1959,7 +1959,7 @@ proc showview {n} { global commfd global selectedview selectfirst global vparentlist vdisporder vcmitlisted - global hlview selectedhlview + global hlview selectedhlview commitinterest if {$n == $curview} return set selid {} @@ -2000,6 +2000,7 @@ proc showview {n} { unset hlview set selectedhlview None } + catch {unset commitinterest} set curview $n set selectedview $n @@ -4322,7 +4323,7 @@ proc commit_descriptor {p} { # append some text to the ctext widget, and make any SHA1 ID # that we know about be a clickable link. proc appendwithlinks {text tags} { - global ctext commitrow linknum curview + global ctext commitrow linknum curview pendinglinks set start [$ctext index "end - 1c"] $ctext insert end $text $tags @@ -4331,17 +4332,48 @@ proc appendwithlinks {text tags} { set s [lindex $l 0] set e [lindex $l 1] set linkid [string range $text $s $e] - if {![info exists commitrow($curview,$linkid)]} continue incr e - $ctext tag add link "$start + $s c" "$start + $e c" $ctext tag add link$linknum "$start + $s c" "$start + $e c" - $ctext tag bind link$linknum <1> \ - [list selectline $commitrow($curview,$linkid) 1] + setlink $linkid link$linknum incr linknum } - $ctext tag conf link -foreground blue -underline 1 - $ctext tag bind link { %W configure -cursor hand2 } - $ctext tag bind link { %W configure -cursor $curtextcursor } +} + +proc setlink {id lk} { + global curview commitrow ctext pendinglinks commitinterest + + if {[info exists commitrow($curview,$id)]} { + $ctext tag conf $lk -foreground blue -underline 1 + $ctext tag bind $lk <1> [list selectline $commitrow($curview,$id) 1] + $ctext tag bind $lk {linkcursor %W 1} + $ctext tag bind $lk {linkcursor %W -1} + } else { + lappend pendinglinks($id) $lk + lappend commitinterest($id) {makelink %I} + } +} + +proc makelink {id} { + global pendinglinks + + if {![info exists pendinglinks($id)]} return + foreach lk $pendinglinks($id) { + setlink $id $lk + } + unset pendinglinks($id) +} + +proc linkcursor {w inc} { + global linkentercount curtextcursor + + if {[incr linkentercount $inc] > 0} { + $w configure -cursor hand2 + } else { + $w configure -cursor $curtextcursor + if {$linkentercount < 0} { + set linkentercount 0 + } + } } proc viewnextline {dir} { @@ -4388,15 +4420,7 @@ proc appendrefs {pos ids var} { $ctext tag delete $lk $ctext insert $pos $sep $ctext insert $pos [lindex $ti 0] $lk - if {[info exists commitrow($curview,$id)]} { - $ctext tag conf $lk -foreground blue - $ctext tag bind $lk <1> \ - [list selectline $commitrow($curview,$id) 1] - $ctext tag conf $lk -underline 1 - $ctext tag bind $lk { %W configure -cursor hand2 } - $ctext tag bind $lk \ - { %W configure -cursor $curtextcursor } - } + setlink $id $lk set sep ", " } } @@ -5237,6 +5261,7 @@ proc nextfile {} { proc clear_ctext {{first 1.0}} { global ctext smarktop smarkbot + global pendinglinks set l [lindex [split $first .] 0] if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} { @@ -5246,6 +5271,9 @@ proc clear_ctext {{first 1.0}} { set smarkbot $l } $ctext delete $first end + if {$first eq "1.0"} { + catch {unset pendinglinks} + } } proc incrsearch {name ix op} { @@ -5609,12 +5637,9 @@ proc lineclick {x y id isnew} { # fill the details pane with info about this line $ctext conf -state normal clear_ctext - $ctext tag conf link -foreground blue -underline 1 - $ctext tag bind link { %W configure -cursor hand2 } - $ctext tag bind link { %W configure -cursor $curtextcursor } $ctext insert end "Parent:\t" - $ctext insert end $id [list link link0] - $ctext tag bind link0 <1> [list selbyid $id] + $ctext insert end $id link0 + setlink $id link0 set info $commitinfo($id) $ctext insert end "\n\t[lindex $info 0]\n" $ctext insert end "\tAuthor:\t[lindex $info 1]\n" @@ -5629,8 +5654,8 @@ proc lineclick {x y id isnew} { if {![info exists commitinfo($child)] && ![getcommit $child]} continue set info $commitinfo($child) $ctext insert end "\n\t" - $ctext insert end $child [list link link$i] - $ctext tag bind link$i <1> [list selbyid $child] + $ctext insert end $child link$i + setlink $child link$i $ctext insert end "\n\t[lindex $info 0]" $ctext insert end "\n\tAuthor:\t[lindex $info 1]" set date [formatdate [lindex $info 2]] @@ -5711,16 +5736,13 @@ proc doseldiff {oldid newid} { clear_ctext init_flist "Top" $ctext insert end "From " - $ctext tag conf link -foreground blue -underline 1 - $ctext tag bind link { %W configure -cursor hand2 } - $ctext tag bind link { %W configure -cursor $curtextcursor } - $ctext tag bind link0 <1> [list selbyid $oldid] - $ctext insert end $oldid [list link link0] + $ctext insert end $oldid link0 + setlink $oldid link0 $ctext insert end "\n " $ctext insert end [lindex $commitinfo($oldid) 0] $ctext insert end "\n\nTo " - $ctext tag bind link1 <1> [list selbyid $newid] - $ctext insert end $newid [list link link1] + $ctext insert end $newid link1 + setlink $newid link1 $ctext insert end "\n " $ctext insert end [lindex $commitinfo($newid) 0] $ctext insert end "\n" @@ -7892,6 +7914,7 @@ set boldrows {} set boldnamerows {} set diffelide {0 0} set markingmatches 0 +set linkentercount 0 set optim_delay 16 From 8f0bc7e95e41673a853a53e17708c6f4f46e6420 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 24 Aug 2007 22:16:42 +1000 Subject: [PATCH 007/123] gitk: Get rid of the rowchk array Instead, when looking for lines that should be terminated with a down arrow, we look at the parents of the commit $downarrowlen + 1 rows before. This gets rid of one more place where we are assuming that all the rows are laid out in order from top to bottom. Signed-off-by: Paul Mackerras --- gitk | 55 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/gitk b/gitk index c795e9838e..7726c311c5 100755 --- a/gitk +++ b/gitk @@ -1951,7 +1951,7 @@ proc showview {n} { global curview viewdata viewfiles global displayorder parentlist rowidlist global colormap rowtextx commitrow nextcolor canvxmax - global numcommits commitlisted rowchk + global numcommits commitlisted global selectedline currentid canv canvy0 global treediffs global pending_select phase @@ -2027,7 +2027,6 @@ proc showview {n} { set rowlaidout [lindex $v 2] set rowoptim [lindex $v 3] set numcommits [lindex $v 4] - catch {unset rowchk} } catch {unset colormap} @@ -2704,7 +2703,6 @@ proc makeuparrow {oid y x} { proc initlayout {} { global rowidlist displayorder commitlisted global rowlaidout rowoptim - global rowchk global numcommits canvxmax canv global nextcolor global parentlist @@ -2717,7 +2715,6 @@ proc initlayout {} { set parentlist {} set nextcolor 0 set rowidlist {{}} - catch {unset rowchk} set rowlaidout 0 set rowoptim 0 set canvxmax [$canv cget -width] @@ -2964,29 +2961,43 @@ proc readdifffiles {fd serial} { return 0 } +proc nextuse {id row} { + global commitrow curview children + + if {[info exists children($curview,$id)]} { + foreach kid $children($curview,$id) { + if {[info exists commitrow($curview,$kid)] && + $commitrow($curview,$kid) > $row} { + return $commitrow($curview,$kid) + } + } + } + if {[info exists commitrow($curview,$id)]} { + return $commitrow($curview,$id) + } + return -1 +} + proc layoutrows {row endrow last} { global rowidlist displayorder global uparrowlen downarrowlen maxwidth mingaplen global children parentlist global commitidx curview - global rowchk set idlist [lindex $rowidlist $row] + if {!$last && $endrow + $uparrowlen + $mingaplen > $commitidx($curview)} { + set endrow [expr {$commitidx($curview) - $uparrowlen - $mingaplen}] + } while {$row < $endrow} { set id [lindex $displayorder $row] - if {1} { - if {!$last && - $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break - for {set x [llength $idlist]} {[incr x -1] >= 0} {} { - set i [lindex $idlist $x] - if {![info exists rowchk($i)] || $row >= $rowchk($i)} { - set r [usedinrange $i [expr {$row - $downarrowlen}] \ - [expr {$row + $uparrowlen + $mingaplen}]] - if {$r == 0} { - set idlist [lreplace $idlist $x $x] - continue - } - set rowchk($i) [expr {$row + $r}] + if {$row > $downarrowlen} { + set termrow [expr {$row - $downarrowlen - 1}] + foreach p [lindex $parentlist $termrow] { + set i [lsearch -exact $idlist $p] + if {$i < 0} continue + set nr [nextuse $p $termrow] + if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} { + set idlist [lreplace $idlist $i $i] } } lset rowidlist $row $idlist @@ -3958,7 +3969,7 @@ proc insertrow {row newcmit} { global displayorder parentlist commitlisted children global commitrow curview rowidlist numcommits global rowlaidout rowoptim numcommits - global selectedline rowchk commitidx + global selectedline commitidx if {$row >= $numcommits} { puts "oops, inserting new row $row but only have $numcommits rows" @@ -3989,8 +4000,6 @@ proc insertrow {row newcmit} { } set rowidlist [linsert $rowidlist $row $idlist] - catch {unset rowchk} - incr rowlaidout incr rowoptim incr numcommits @@ -4006,7 +4015,7 @@ proc removerow {row} { global displayorder parentlist commitlisted children global commitrow curview rowidlist numcommits global rowlaidout rowoptim numcommits - global linesegends selectedline rowchk commitidx + global linesegends selectedline commitidx if {$row >= $numcommits} { puts "oops, removing row $row but only have $numcommits rows" @@ -4033,8 +4042,6 @@ proc removerow {row} { set rowidlist [lreplace $rowidlist $row $row] - catch {unset rowchk} - incr rowlaidout -1 incr rowoptim -1 incr numcommits -1 From 0380081c65c3e8a46caad9aebe8e97ff65510453 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 29 Aug 2007 21:45:21 +1000 Subject: [PATCH 008/123] gitk: Do only the parts of the layout that are needed This changes layoutrows and optimize_rows to make it possible to lay out only a little bit more of the graph than is visible, rather than having to lay out the whole graph from top to bottom. To lay out some of the graph without starting at the top, we use the new make_idlist procedure for the first row, then lay it out proceeding downwards as before. Empty list elements in rowidlist are used to denote rows that haven't been laid out yet. Optimizing happens much as before except that we don't try to optimize unless we have three consecutive rows laid out (or the top 2 rows). We have a new list, rowisopt, to record which rows have been optimized. If we change a row that has already been drawn, we set a flag which causes drawcommits to throw away everything drawn on the canvas and redraw the visible rows. Signed-off-by: Paul Mackerras --- gitk | 492 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 265 insertions(+), 227 deletions(-) diff --git a/gitk b/gitk index 7726c311c5..060c4c0cb2 100755 --- a/gitk +++ b/gitk @@ -1949,13 +1949,13 @@ proc unflatten {var l} { proc showview {n} { global curview viewdata viewfiles - global displayorder parentlist rowidlist + global displayorder parentlist rowidlist rowisopt global colormap rowtextx commitrow nextcolor canvxmax global numcommits commitlisted global selectedline currentid canv canvy0 global treediffs global pending_select phase - global commitidx rowlaidout rowoptim + global commitidx global commfd global selectedview selectfirst global vparentlist vdisporder vcmitlisted @@ -1987,11 +1987,11 @@ proc showview {n} { set vcmitlisted($curview) $commitlisted if {$phase ne {}} { set viewdata($curview) \ - [list $phase $rowidlist $rowlaidout $rowoptim $numcommits] + [list $phase $rowidlist $rowisopt $numcommits] } elseif {![info exists viewdata($curview)] || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list {} $rowidlist] + [list {} $rowidlist $rowisopt] } } catch {unset treediffs} @@ -2021,12 +2021,11 @@ proc showview {n} { set parentlist $vparentlist($n) set commitlisted $vcmitlisted($n) set rowidlist [lindex $v 1] + set rowisopt [lindex $v 2] if {$phase eq {}} { set numcommits [llength $displayorder] } else { - set rowlaidout [lindex $v 2] - set rowoptim [lindex $v 3] - set numcommits [lindex $v 4] + set numcommits [lindex $v 3] } catch {unset colormap} @@ -2625,47 +2624,18 @@ proc shortids {ids} { return $res } -proc incrange {l x o} { - set n [llength $l] - while {$x < $n} { - set e [lindex $l $x] - if {$e ne {}} { - lset l $x [expr {$e + $o}] - } - incr x - } - return $l -} - proc ntimes {n o} { set ret {} - for {} {$n > 0} {incr n -1} { - lappend ret $o + set o [list $o] + for {set mask 1} {$mask <= $n} {incr mask $mask} { + if {($n & $mask) != 0} { + set ret [concat $ret $o] + } + set o [concat $o $o] } return $ret } -proc usedinrange {id l1 l2} { - global children commitrow curview - - if {[info exists commitrow($curview,$id)]} { - set r $commitrow($curview,$id) - if {$l1 <= $r && $r <= $l2} { - return [expr {$r - $l1 + 1}] - } - } - set kids $children($curview,$id) - foreach c $kids { - if {[info exists commitrow($curview,$c)]} { - set r $commitrow($curview,$c) - if {$l1 <= $r && $r <= $l2} { - return [expr {$r - $l1 + 1}] - } - } - } - return 0 -} - # Work out where id should go in idlist so that order-token # values increase from left to right proc idcol {idlist id {i 0}} { @@ -2689,20 +2659,8 @@ proc idcol {idlist id {i 0}} { return $i } -proc makeuparrow {oid y x} { - global rowidlist uparrowlen displayorder - - for {set i 0} {$i < $uparrowlen && $y > 1} {incr i} { - incr y -1 - set idl [lindex $rowidlist $y] - set x [idcol $idl $oid $x] - lset rowidlist $y [linsert $idl $x $oid] - } -} - proc initlayout {} { - global rowidlist displayorder commitlisted - global rowlaidout rowoptim + global rowidlist rowisopt displayorder commitlisted global numcommits canvxmax canv global nextcolor global parentlist @@ -2714,9 +2672,8 @@ proc initlayout {} { set commitlisted {} set parentlist {} set nextcolor 0 - set rowidlist {{}} - set rowlaidout 0 - set rowoptim 0 + set rowidlist {} + set rowisopt {} set canvxmax [$canv cget -width] catch {unset colormap} catch {unset rowtextx} @@ -2752,54 +2709,18 @@ proc visiblerows {} { } proc layoutmore {tmax allread} { - global rowlaidout rowoptim commitidx numcommits optim_delay - global uparrowlen curview rowidlist + global commitidx numcommits + global uparrowlen downarrowlen mingaplen curview - set showlast 0 - set showdelay $optim_delay - set optdelay [expr {$uparrowlen + 1}] - while {1} { - if {$rowoptim - $showdelay > $numcommits} { - showstuff [expr {$rowoptim - $showdelay}] $showlast - } elseif {$rowlaidout - $optdelay > $rowoptim} { - set nr [expr {$rowlaidout - $optdelay - $rowoptim}] - if {$nr > 100} { - set nr 100 - } - optimize_rows $rowoptim 0 [expr {$rowoptim + $nr}] - incr rowoptim $nr - } elseif {$commitidx($curview) > $rowlaidout} { - set nr [expr {$commitidx($curview) - $rowlaidout}] - # may need to increase this threshold if uparrowlen or - # mingaplen are increased... - if {$nr > 200} { - set nr 200 - } - set row $rowlaidout - set rowlaidout [layoutrows $row [expr {$row + $nr}] $allread] - if {$rowlaidout == $row} { - return 0 - } - } elseif {$allread} { - set optdelay 0 - set nrows $commitidx($curview) - if {[lindex $rowidlist $nrows] ne {}} { - layouttail - set rowlaidout $commitidx($curview) - } elseif {$rowoptim == $nrows} { - set showdelay 0 - set showlast 1 - if {$numcommits == $nrows} { - return 0 - } - } - } else { - return 0 - } - if {$tmax ne {} && [clock clicks -milliseconds] >= $tmax} { - return 1 - } + set show $commitidx($curview) + if {!$allread} { + set delay [expr {$uparrowlen + $mingaplen + $downarrowlen + 3}] + set show [expr {$show - $delay}] } + if {$show > $numcommits} { + showstuff $show $allread + } + return 0 } proc showstuff {canshow last} { @@ -2966,8 +2887,10 @@ proc nextuse {id row} { if {[info exists children($curview,$id)]} { foreach kid $children($curview,$id) { - if {[info exists commitrow($curview,$kid)] && - $commitrow($curview,$kid) > $row} { + if {![info exists commitrow($curview,$kid)]} { + return -1 + } + if {$commitrow($curview,$kid) > $row} { return $commitrow($curview,$kid) } } @@ -2978,97 +2901,171 @@ proc nextuse {id row} { return -1 } -proc layoutrows {row endrow last} { - global rowidlist displayorder +proc make_idlist {row} { + global displayorder parentlist uparrowlen downarrowlen mingaplen + global commitidx curview ordertok children commitrow + + set r [expr {$row - $mingaplen - $downarrowlen - 1}] + if {$r < 0} { + set r 0 + } + set ra [expr {$row - $downarrowlen}] + if {$ra < 0} { + set ra 0 + } + set rb [expr {$row + $uparrowlen}] + if {$rb > $commitidx($curview)} { + set rb $commitidx($curview) + } + set ids {} + for {} {$r < $ra} {incr r} { + set nextid [lindex $displayorder [expr {$r + 1}]] + foreach p [lindex $parentlist $r] { + if {$p eq $nextid} continue + set rn [nextuse $p $r] + if {$rn >= $row && + $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} { + lappend ids [list $ordertok($curview,$p) $p] + } + } + } + for {} {$r < $row} {incr r} { + set nextid [lindex $displayorder [expr {$r + 1}]] + foreach p [lindex $parentlist $r] { + if {$p eq $nextid} continue + set rn [nextuse $p $r] + if {$rn < 0 || $rn >= $row} { + lappend ids [list $ordertok($curview,$p) $p] + } + } + } + set id [lindex $displayorder $row] + lappend ids [list $ordertok($curview,$id) $id] + while {$r < $rb} { + foreach p [lindex $parentlist $r] { + set firstkid [lindex $children($curview,$p) 0] + if {$commitrow($curview,$firstkid) < $row} { + lappend ids [list $ordertok($curview,$p) $p] + } + } + incr r + set id [lindex $displayorder $r] + if {$id ne {}} { + set firstkid [lindex $children($curview,$id) 0] + if {$firstkid ne {} && $commitrow($curview,$firstkid) < $row} { + lappend ids [list $ordertok($curview,$id) $id] + } + } + } + set idlist {} + foreach idx [lsort -unique $ids] { + lappend idlist [lindex $idx 1] + } + return $idlist +} + +proc layoutrows {row endrow} { + global rowidlist rowisopt displayorder global uparrowlen downarrowlen maxwidth mingaplen global children parentlist - global commitidx curview + global commitidx curview commitrow - set idlist [lindex $rowidlist $row] - if {!$last && $endrow + $uparrowlen + $mingaplen > $commitidx($curview)} { - set endrow [expr {$commitidx($curview) - $uparrowlen - $mingaplen}] + set idlist {} + if {$row > 0} { + foreach id [lindex $rowidlist [expr {$row - 1}]] { + if {$id ne {}} { + lappend idlist $id + } + } } - while {$row < $endrow} { - set id [lindex $displayorder $row] - if {$row > $downarrowlen} { - set termrow [expr {$row - $downarrowlen - 1}] - foreach p [lindex $parentlist $termrow] { - set i [lsearch -exact $idlist $p] - if {$i < 0} continue - set nr [nextuse $p $termrow] - if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} { - set idlist [lreplace $idlist $i $i] + for {} {$row < $endrow} {incr row} { + set rm1 [expr {$row - 1}] + if {$rm1 < 0 || [lindex $rowidlist $rm1] eq {}} { + set idlist [make_idlist $row] + } else { + set id [lindex $displayorder $rm1] + set col [lsearch -exact $idlist $id] + set idlist [lreplace $idlist $col $col] + foreach p [lindex $parentlist $rm1] { + if {[lsearch -exact $idlist $p] < 0} { + set col [idcol $idlist $p $col] + set idlist [linsert $idlist $col $p] } } - lset rowidlist $row $idlist - } - set oldolds {} - set newolds {} - foreach p [lindex $parentlist $row] { - # is id the first child of this parent? - if {$id eq [lindex $children($curview,$p) 0]} { - lappend newolds $p - } elseif {[lsearch -exact $idlist $p] < 0} { - lappend oldolds $p + set id [lindex $displayorder $row] + if {$row > $downarrowlen} { + set termrow [expr {$row - $downarrowlen - 1}] + foreach p [lindex $parentlist $termrow] { + set i [lsearch -exact $idlist $p] + if {$i < 0} continue + set nr [nextuse $p $termrow] + if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} { + set idlist [lreplace $idlist $i $i] + } + } + } + set col [lsearch -exact $idlist $id] + if {$col < 0} { + set col [idcol $idlist $id] + set idlist [linsert $idlist $col $id] + } + set r [expr {$row + $uparrowlen - 1}] + if {$r < $commitidx($curview)} { + set x $col + foreach p [lindex $parentlist $r] { + if {[lsearch -exact $idlist $p] >= 0} continue + set fk [lindex $children($curview,$p) 0] + if {$commitrow($curview,$fk) < $row} { + set x [idcol $idlist $p $x] + set idlist [linsert $idlist $x $p] + } + } + if {[incr r] < $commitidx($curview)} { + set p [lindex $displayorder $r] + if {[lsearch -exact $idlist $p] < 0} { + set fk [lindex $children($curview,$p) 0] + if {$fk ne {} && $commitrow($curview,$fk) < $row} { + set x [idcol $idlist $p $x] + set idlist [linsert $idlist $x $p] + } + } + } } } - set col [lsearch -exact $idlist $id] - if {$col < 0} { - set col [idcol $idlist $id] - set idlist [linsert $idlist $col $id] - lset rowidlist $row $idlist - if {$children($curview,$id) ne {}} { - makeuparrow $id $row $col + set l [llength $rowidlist] + if {$row == $l} { + lappend rowidlist $idlist + lappend rowisopt 0 + } elseif {$row < $l} { + if {$idlist ne [lindex $rowidlist $row]} { + lset rowidlist $row $idlist + changedrow $row } + } else { + set rowidlist [concat $rowidlist [ntimes [expr {$row - $l}] {}]] + lappend rowidlist $idlist + set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]] } - incr row - set idlist [lreplace $idlist $col $col] - set x $col - foreach i $newolds { - set x [idcol $idlist $i $x] - set idlist [linsert $idlist $x $i] - } - foreach oid $oldolds { - set x [idcol $idlist $oid $x] - set idlist [linsert $idlist $x $oid] - makeuparrow $oid $row $x - } - lappend rowidlist $idlist } return $row } -proc addextraid {id row} { - global displayorder commitrow commitinfo - global commitidx commitlisted - global parentlist children curview +proc changedrow {row} { + global displayorder iddrawn rowisopt need_redisplay - incr commitidx($curview) - lappend displayorder $id - lappend commitlisted 0 - lappend parentlist {} - set commitrow($curview,$id) $row - readcommit $id - if {![info exists commitinfo($id)]} { - set commitinfo($id) {"No commit information available"} + set l [llength $rowisopt] + if {$row < $l} { + lset rowisopt $row 0 + if {$row + 1 < $l} { + lset rowisopt [expr {$row + 1}] 0 + if {$row + 2 < $l} { + lset rowisopt [expr {$row + 2}] 0 + } + } } - if {![info exists children($curview,$id)]} { - set children($curview,$id) {} - } -} - -proc layouttail {} { - global rowidlist commitidx curview - - set row $commitidx($curview) - set idlist [lindex $rowidlist $row] - while {$idlist ne {}} { - set col [expr {[llength $idlist] - 1}] - set id [lindex $idlist $col] - addextraid $id $row - incr row - set idlist [lreplace $idlist $col $col] - lappend rowidlist $idlist + set id [lindex $displayorder $row] + if {[info exists iddrawn($id)]} { + set need_redisplay 1 } } @@ -3084,27 +3081,29 @@ proc insert_pad {row col npad} { set aft [lreplace $aft $i $i] } lset rowidlist $row [concat $bef $pad $aft] + changedrow $row } proc optimize_rows {row col endrow} { - global rowidlist displayorder curview children + global rowidlist rowisopt displayorder curview children if {$row < 1} { set row 1 } - set idlist [lindex $rowidlist [expr {$row - 1}]] - if {$row >= 2} { - set previdlist [lindex $rowidlist [expr {$row - 2}]] - } else { - set previdlist {} - } - for {} {$row < $endrow} {incr row} { - set pprevidlist $previdlist - set previdlist $idlist - set idlist [lindex $rowidlist $row] + for {} {$row < $endrow} {incr row; set col 0} { + if {[lindex $rowisopt $row]} continue set haspad 0 set y0 [expr {$row - 1}] set ym [expr {$row - 2}] + set idlist [lindex $rowidlist $row] + set previdlist [lindex $rowidlist $y0] + if {$idlist eq {} || $previdlist eq {}} continue + if {$ym >= 0} { + set pprevidlist [lindex $rowidlist $ym] + if {$pprevidlist eq {}} continue + } else { + set pprevidlist {} + } set x0 -1 set xm -1 for {} {$col < [llength $idlist]} {incr col} { @@ -3180,7 +3179,6 @@ proc optimize_rows {row col endrow} { incr x0 optimize_rows $y0 $x0 $row set previdlist [lindex $rowidlist $y0] - set pprevidlist [lindex $rowidlist $ym] } } if {!$haspad} { @@ -3203,10 +3201,10 @@ proc optimize_rows {row col endrow} { # isn't the last column if {$x0 >= 0 && [incr col] < [llength $idlist]} { set idlist [linsert $idlist $col {}] + lset rowidlist $row $idlist + changedrow $row } } - lset rowidlist $row $idlist - set col 0 } } @@ -3531,7 +3529,7 @@ proc drawcmittext {id row col} { 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 selectedline global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2 # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right @@ -3607,6 +3605,9 @@ proc drawcmittext {id row col} { -text $name -font $nfont -tags text] set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \ -text $date -font $mainfont -tags text] + if {[info exists selectedline] && $selectedline == $row} { + make_secsel $row + } set xr [expr {$xt + [font measure $mainfont $headline]}] if {$xr > $canvxmax} { set canvxmax $xr @@ -3615,7 +3616,7 @@ proc drawcmittext {id row col} { } proc drawcmitrow {row} { - global displayorder rowidlist + global displayorder rowidlist nrows_drawn global iddrawn markingmatches global commitinfo parentlist numcommits global filehighlight fhighlights findstring nhighlights @@ -3649,6 +3650,7 @@ proc drawcmitrow {row} { assigncolor $id drawcmittext $id $row $col set iddrawn($id) 1 + incr nrows_drawn } if {$markingmatches} { markrowmatches $row $id @@ -3656,8 +3658,8 @@ proc drawcmitrow {row} { } proc drawcommits {row {endrow {}}} { - global numcommits iddrawn displayorder curview - global parentlist rowidlist + global numcommits iddrawn displayorder curview need_redisplay + global parentlist rowidlist uparrowlen downarrowlen nrows_drawn if {$row < 0} { set row 0 @@ -3669,6 +3671,35 @@ proc drawcommits {row {endrow {}}} { set endrow [expr {$numcommits - 1}] } + set rl1 [expr {$row - $downarrowlen - 3}] + if {$rl1 < 0} { + set rl1 0 + } + set ro1 [expr {$row - 3}] + if {$ro1 < 0} { + set ro1 0 + } + set r2 [expr {$endrow + $uparrowlen + 3}] + if {$r2 > $numcommits} { + set r2 $numcommits + } + for {set r $rl1} {$r < $r2} {incr r} { + if {[lindex $rowidlist $r] ne {}} { + if {$rl1 < $r} { + layoutrows $rl1 $r + } + set rl1 [expr {$r + 1}] + } + } + if {$rl1 < $r} { + layoutrows $rl1 $r + } + optimize_rows $ro1 0 $r2 + if {$need_redisplay || $nrows_drawn > 2000} { + clear_display + drawvisible + } + # make the lines join to already-drawn rows either side set r [expr {$row - 1}] if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} { @@ -3736,7 +3767,7 @@ proc drawvisible {} { } proc clear_display {} { - global iddrawn linesegs + global iddrawn linesegs need_redisplay nrows_drawn global vhighlights fhighlights nhighlights rhighlights allcanvs delete all @@ -3746,6 +3777,8 @@ proc clear_display {} { catch {unset fhighlights} catch {unset nhighlights} catch {unset rhighlights} + set need_redisplay 0 + set nrows_drawn 0 } proc findcrossings {id} { @@ -3967,9 +4000,9 @@ proc show_status {msg} { # on that row and below will move down one row. proc insertrow {row newcmit} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist numcommits - global rowlaidout rowoptim numcommits - global selectedline commitidx + global commitrow curview rowidlist rowisopt numcommits + global numcommits + global selectedline commitidx ordertok if {$row >= $numcommits} { puts "oops, inserting new row $row but only have $numcommits rows" @@ -3989,6 +4022,7 @@ proc insertrow {row newcmit} { set commitrow($curview,$id) $r } incr commitidx($curview) + set ordertok($curview,$newcmit) $ordertok($curview,$p) set idlist [lindex $rowidlist $row] if {[llength $kids] == 1} { @@ -3999,9 +4033,8 @@ proc insertrow {row newcmit} { lappend idlist $newcmit } set rowidlist [linsert $rowidlist $row $idlist] + set rowisopt [linsert $rowisopt $row 0] - incr rowlaidout - incr rowoptim incr numcommits if {[info exists selectedline] && $selectedline >= $row} { @@ -4013,8 +4046,8 @@ proc insertrow {row newcmit} { # Remove a commit that was inserted with insertrow on row $row. proc removerow {row} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist numcommits - global rowlaidout rowoptim numcommits + global commitrow curview rowidlist rowisopt numcommits + global numcommits global linesegends selectedline commitidx if {$row >= $numcommits} { @@ -4041,9 +4074,8 @@ proc removerow {row} { incr commitidx($curview) -1 set rowidlist [lreplace $rowidlist $row $row] + set rowisopt [lreplace $rowisopt $row $row] - incr rowlaidout -1 - incr rowoptim -1 incr numcommits -1 if {[info exists selectedline] && $selectedline > $row} { @@ -4485,9 +4517,27 @@ proc dispnexttag {} { } } +proc make_secsel {l} { + global linehtag linentag linedtag canv canv2 canv3 + + if {![info exists linehtag($l)]} return + $canv delete secsel + set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \ + -tags secsel -fill [$canv cget -selectbackground]] + $canv lower $t + $canv2 delete secsel + set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \ + -tags secsel -fill [$canv2 cget -selectbackground]] + $canv2 lower $t + $canv3 delete secsel + set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \ + -tags secsel -fill [$canv3 cget -selectbackground]] + $canv3 lower $t +} + proc selectline {l isnew} { - global canv canv2 canv3 ctext commitinfo selectedline - global displayorder linehtag linentag linedtag + global canv ctext commitinfo selectedline + global displayorder global canvy0 linespc parentlist children curview global currentid sha1entry global commentend idtags linknum @@ -4536,19 +4586,7 @@ proc selectline {l isnew} { drawvisible } - if {![info exists linehtag($l)]} return - $canv delete secsel - set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \ - -tags secsel -fill [$canv cget -selectbackground]] - $canv lower $t - $canv2 delete secsel - set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \ - -tags secsel -fill [$canv2 cget -selectbackground]] - $canv2 lower $t - $canv3 delete secsel - set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \ - -tags secsel -fill [$canv3 cget -selectbackground]] - $canv3 lower $t + make_secsel $l if {$isnew} { addtohistory [list selectline $l 0] @@ -5616,7 +5654,7 @@ proc arrowjump {id n y} { } proc lineclick {x y id isnew} { - global ctext commitinfo children canv thickerline curview + global ctext commitinfo children canv thickerline curview commitrow if {![info exists commitinfo($id)] && ![getcommit $id]} return unmarkmatches @@ -7922,8 +7960,8 @@ set boldnamerows {} set diffelide {0 0} set markingmatches 0 set linkentercount 0 - -set optim_delay 16 +set need_redisplay 0 +set nrows_drawn 0 set nextviewnum 1 set curview 0 From df904497ecc15382199045bb257250c857f04eca Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 29 Aug 2007 22:03:07 +1000 Subject: [PATCH 009/123] gitk: Fix bug causing incorrect ref list contents when switching view If the view we're switching to hadn't been read in, we hit an early return in showview which meant we didn't update the ref list window. Signed-off-by: Paul Mackerras --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index 060c4c0cb2..0125f17fcd 100755 --- a/gitk +++ b/gitk @@ -2007,6 +2007,7 @@ proc showview {n} { .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}] .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}] + run refill_reflist if {![info exists viewdata($n)]} { if {$selid ne {}} { set pending_select $selid @@ -2070,7 +2071,6 @@ proc showview {n} { } elseif {$numcommits == 0} { show_status "No commits selected" } - run refill_reflist } # Stuff relating to the highlighting facility From 6eaaccd12846c5957c3433c773ad60b8a4196045 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 29 Aug 2007 22:41:34 +1000 Subject: [PATCH 010/123] gitk: Fix bug causing undefined variable error when cherry-picking When "Show nearby tags" is turned off and the user did a cherry-pick, we were trying to access variables relating to the descendent/ancestor tag & head computations in addnewchild though they hadn't been set. This makes sure we don't do that. Reported by Johannes Sixt. Signed-off-by: Paul Mackerras --- gitk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index 0125f17fcd..22a6318421 100755 --- a/gitk +++ b/gitk @@ -6648,8 +6648,9 @@ proc splitarc {p} { proc addnewchild {id p} { global allids allparents allchildren idtags nextarc nbmp global arcnos arcids arctags arcout arcend arcstart archeads growing - global seeds + global seeds allcommits + if {![info exists allcommits]} return lappend allids $id set allparents($id) [list $p] set allchildren($id) {} From 5cd15b6b7f87dc61f729ad31a682ffc394560273 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 30 Aug 2007 21:54:17 +1000 Subject: [PATCH 011/123] gitk: Add a cache for the topology info This adds code to write out the topology information used to determine precedes/follows and branch information into a cache file (~3.5MB for the kernel tree). At startup we read the cache file and then do a git rev-list to update it, which is fast because we exclude all commits in the cache that have no children and commits reachable from them (which amounts to everything in the cache). If one of those commits without children no longer exists, then git rev-list will give an error, whereupon we throw away the cache and read in the whole tree again. This gives a significant speedup in the startup time for gitk. Signed-off-by: Paul Mackerras --- gitk | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 237 insertions(+), 22 deletions(-) diff --git a/gitk b/gitk index 22a6318421..251e9242b3 100755 --- a/gitk +++ b/gitk @@ -6445,25 +6445,59 @@ proc refill_reflist {} { # Stuff for finding nearby tags proc getallcommits {} { - global allcommits allids nbmp nextarc seeds + global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate + global idheads idtags idotherrefs allparents tagobjid if {![info exists allcommits]} { - set allids {} - set nbmp 0 set nextarc 0 set allcommits 0 set seeds {} + set allcwait 0 + set cachedarcs 0 + set allccache [file join [gitdir] "gitk.cache"] + if {![catch { + set f [open $allccache r] + set allcwait 1 + getcache $f + }]} return } - set cmd [concat | git rev-list --all --parents] - foreach id $seeds { - lappend cmd "^$id" + if {$allcwait} { + return + } + set cmd [list | git rev-list --parents] + set allcupdate [expr {$seeds ne {}}] + if {!$allcupdate} { + set ids "--all" + } else { + set refs [concat [array names idheads] [array names idtags] \ + [array names idotherrefs]] + set ids {} + set tagobjs {} + foreach name [array names tagobjid] { + lappend tagobjs $tagobjid($name) + } + foreach id [lsort -unique $refs] { + if {![info exists allparents($id)] && + [lsearch -exact $tagobjs $id] < 0} { + lappend ids $id + } + } + if {$ids ne {}} { + foreach id $seeds { + lappend ids "^$id" + } + } + } + if {$ids ne {}} { + set fd [open [concat $cmd $ids] r] + fconfigure $fd -blocking 0 + incr allcommits + nowbusy allcommits + filerun $fd [list getallclines $fd] + } else { + dispneartags 0 } - set fd [open $cmd r] - fconfigure $fd -blocking 0 - incr allcommits - nowbusy allcommits - filerun $fd [list getallclines $fd] } # Since most commits have 1 parent and 1 child, we group strings of @@ -6482,10 +6516,10 @@ proc getallcommits {} { # coming from descendents, and "outgoing" means going towards ancestors. proc getallclines {fd} { - global allids allparents allchildren idtags idheads nextarc nbmp + global allparents allchildren idtags idheads nextarc global arcnos arcids arctags arcout arcend arcstart archeads growing - global seeds allcommits - + global seeds allcommits cachedarcs allcupdate + set nid 0 while {[incr nid] <= 1000 && [gets $fd line] >= 0} { set id [lindex $line 0] @@ -6493,7 +6527,7 @@ proc getallclines {fd} { # seen it already continue } - lappend allids $id + set cachedarcs 0 set olds [lrange $line 1 end] set allparents($id) $olds if {![info exists allchildren($id)]} { @@ -6524,7 +6558,6 @@ proc getallclines {fd} { continue } } - incr nbmp foreach a $arcnos($id) { lappend arcids($a) $id set arcend($a) $id @@ -6564,9 +6597,28 @@ proc getallclines {fd} { if {![eof $fd]} { return [expr {$nid >= 1000? 2: 1}] } - close $fd + set cacheok 1 + if {[catch { + fconfigure $fd -blocking 1 + close $fd + } err]} { + # got an error reading the list of commits + # if we were updating, try rereading the whole thing again + if {$allcupdate} { + incr allcommits -1 + dropcache $err + return + } + error_popup "Error reading commit topology information;\ + branch and preceding/following tag information\ + will be incomplete.\n($err)" + set cacheok 0 + } if {[incr allcommits -1] == 0} { notbusy allcommits + if {$cacheok} { + run savecache + } } dispneartags 0 return 0 @@ -6590,7 +6642,7 @@ proc recalcarc {a} { } proc splitarc {p} { - global arcnos arcids nextarc nbmp arctags archeads idtags idheads + global arcnos arcids nextarc arctags archeads idtags idheads global arcstart arcend arcout allparents growing set a $arcnos($p) @@ -6622,7 +6674,6 @@ proc splitarc {p} { set growing($na) 1 unset growing($a) } - incr nbmp foreach id $tail { if {[llength $arcnos($id)] == 1} { @@ -6646,17 +6697,15 @@ proc splitarc {p} { # Update things for a new commit added that is a child of one # existing commit. Used when cherry-picking. proc addnewchild {id p} { - global allids allparents allchildren idtags nextarc nbmp + global allparents allchildren idtags nextarc global arcnos arcids arctags arcout arcend arcstart archeads growing global seeds allcommits if {![info exists allcommits]} return - lappend allids $id set allparents($id) [list $p] set allchildren($id) {} set arcnos($id) {} lappend seeds $id - incr nbmp lappend allchildren($p) $id set a [incr nextarc] set arcstart($a) $id @@ -6671,6 +6720,172 @@ proc addnewchild {id p} { set arcout($id) [list $a] } +# This implements a cache for the topology information. +# The cache saves, for each arc, the start and end of the arc, +# the ids on the arc, and the outgoing arcs from the end. +proc readcache {f} { + global arcnos arcids arcout arcstart arcend arctags archeads nextarc + global idtags idheads allparents cachedarcs possible_seeds seeds growing + global allcwait + + set a $nextarc + set lim $cachedarcs + if {$lim - $a > 500} { + set lim [expr {$a + 500}] + } + if {[catch { + if {$a == $lim} { + # finish reading the cache and setting up arctags, etc. + set line [gets $f] + if {$line ne "1"} {error "bad final version"} + close $f + foreach id [array names idtags] { + if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 && + [llength $allparents($id)] == 1} { + set a [lindex $arcnos($id) 0] + if {$arctags($a) eq {}} { + recalcarc $a + } + } + } + foreach id [array names idheads] { + if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 && + [llength $allparents($id)] == 1} { + set a [lindex $arcnos($id) 0] + if {$archeads($a) eq {}} { + recalcarc $a + } + } + } + foreach id [lsort -unique $possible_seeds] { + if {$arcnos($id) eq {}} { + lappend seeds $id + } + } + set allcwait 0 + } else { + while {[incr a] <= $lim} { + set line [gets $f] + if {[llength $line] != 3} {error "bad line"} + set s [lindex $line 0] + set arcstart($a) $s + lappend arcout($s) $a + if {![info exists arcnos($s)]} { + lappend possible_seeds $s + set arcnos($s) {} + } + set e [lindex $line 1] + if {$e eq {}} { + set growing($a) 1 + } else { + set arcend($a) $e + if {![info exists arcout($e)]} { + set arcout($e) {} + } + } + set arcids($a) [lindex $line 2] + foreach id $arcids($a) { + lappend allparents($s) $id + set s $id + lappend arcnos($id) $a + } + if {![info exists allparents($s)]} { + set allparents($s) {} + } + set arctags($a) {} + set archeads($a) {} + } + set nextarc [expr {$a - 1}] + } + } err]} { + dropcache $err + return 0 + } + if {!$allcwait} { + getallcommits + } + return $allcwait +} + +proc getcache {f} { + global nextarc cachedarcs possible_seeds + + if {[catch { + set line [gets $f] + if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"} + # make sure it's an integer + set cachedarcs [expr {int([lindex $line 1])}] + if {$cachedarcs < 0} {error "bad number of arcs"} + set nextarc 0 + set possible_seeds {} + run readcache $f + } err]} { + dropcache $err + } + return 0 +} + +proc dropcache {err} { + global allcwait nextarc cachedarcs seeds + + #puts "dropping cache ($err)" + foreach v {arcnos arcout arcids arcstart arcend growing \ + arctags archeads allparents allchildren} { + global $v + catch {unset $v} + } + set allcwait 0 + set nextarc 0 + set cachedarcs 0 + set seeds {} + getallcommits +} + +proc writecache {f} { + global cachearc cachedarcs allccache + global arcstart arcend arcnos arcids arcout + + set a $cachearc + set lim $cachedarcs + if {$lim - $a > 1000} { + set lim [expr {$a + 1000}] + } + if {[catch { + while {[incr a] <= $lim} { + if {[info exists arcend($a)]} { + puts $f [list $arcstart($a) $arcend($a) $arcids($a)] + } else { + puts $f [list $arcstart($a) {} $arcids($a)] + } + } + } err]} { + catch {close $f} + catch {file delete $allccache} + #puts "writing cache failed ($err)" + return 0 + } + set cachearc [expr {$a - 1}] + if {$a > $cachedarcs} { + puts $f "1" + close $f + return 0 + } + return 1 +} + +proc savecache {} { + global nextarc cachedarcs cachearc allccache + + if {$nextarc == $cachedarcs} return + set cachearc 0 + set cachedarcs $nextarc + catch { + set f [open $allccache w] + puts $f [list 1 $cachedarcs] + run writecache $f + } +} + # Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a, # or 0 if neither is true. proc anc_or_desc {a b} { From f5f3c2e29f51a38261daa91073a3f227d4532325 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 5 Sep 2007 02:19:56 +1000 Subject: [PATCH 012/123] gitk: Make it possible to lay out all the rows we have received so far This arranges things so that we can do the layout all the way up to the last commit that we have received from git log. If we get more commits we re-lay and redisplay (if necessary) the visible rows. Signed-off-by: Paul Mackerras --- gitk | 127 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 36 deletions(-) diff --git a/gitk b/gitk index 251e9242b3..a042efe260 100755 --- a/gitk +++ b/gitk @@ -82,11 +82,12 @@ proc dorunq {} { proc start_rev_list {view} { global startmsecs global commfd leftover tclencoding datemode - global viewargs viewfiles commitidx vnextroot + global viewargs viewfiles commitidx viewcomplete vnextroot global lookingforhead showlocalchanges set startmsecs [clock clicks -milliseconds] set commitidx($view) 0 + set viewcomplete($view) 0 set vnextroot($view) 0 set order "--topo-order" if {$datemode} { @@ -148,7 +149,7 @@ proc strrep {n} { proc getcommitlines {fd view} { global commitlisted global leftover commfd - global displayorder commitidx commitrow commitdata + global displayorder commitidx viewcomplete commitrow commitdata global parentlist children curview hlview global vparentlist vdisporder vcmitlisted global ordertok vnextroot idpending @@ -179,6 +180,7 @@ proc getcommitlines {fd view} { lappend vcmitlisted($view) 0 } } + set viewcomplete($view) 1 global viewname unset commfd($view) notbusy $view @@ -310,15 +312,12 @@ proc getcommitlines {fd view} { } proc chewcommits {view} { - global curview hlview commfd + global curview hlview viewcomplete global selectedline pending_select - set more 0 if {$view == $curview} { - set allread [expr {![info exists commfd($view)]}] - set tlimit [expr {[clock clicks -milliseconds] + 50}] - set more [layoutmore $tlimit $allread] - if {$allread && !$more} { + layoutmore + if {$viewcomplete($view)} { global displayorder commitidx phase global numcommits startmsecs @@ -339,7 +338,7 @@ proc chewcommits {view} { if {[info exists hlview] && $view == $hlview} { vhighlightmore } - return $more + return 0 } proc readcommit {id} { @@ -1949,7 +1948,7 @@ proc unflatten {var l} { proc showview {n} { global curview viewdata viewfiles - global displayorder parentlist rowidlist rowisopt + global displayorder parentlist rowidlist rowisopt rowfinal global colormap rowtextx commitrow nextcolor canvxmax global numcommits commitlisted global selectedline currentid canv canvy0 @@ -1985,13 +1984,11 @@ proc showview {n} { set vparentlist($curview) $parentlist set vdisporder($curview) $displayorder set vcmitlisted($curview) $commitlisted - if {$phase ne {}} { + if {$phase ne {} || + ![info exists viewdata($curview)] || + [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list $phase $rowidlist $rowisopt $numcommits] - } elseif {![info exists viewdata($curview)] - || [lindex $viewdata($curview) 0] ne {}} { - set viewdata($curview) \ - [list {} $rowidlist $rowisopt] + [list $phase $rowidlist $rowisopt $rowfinal] } } catch {unset treediffs} @@ -2023,11 +2020,8 @@ proc showview {n} { set commitlisted $vcmitlisted($n) set rowidlist [lindex $v 1] set rowisopt [lindex $v 2] - if {$phase eq {}} { - set numcommits [llength $displayorder] - } else { - set numcommits [lindex $v 3] - } + set rowfinal [lindex $v 3] + set numcommits $commitidx($n) catch {unset colormap} catch {unset rowtextx} @@ -2660,7 +2654,7 @@ proc idcol {idlist id {i 0}} { } proc initlayout {} { - global rowidlist rowisopt displayorder commitlisted + global rowidlist rowisopt rowfinal displayorder commitlisted global numcommits canvxmax canv global nextcolor global parentlist @@ -2674,6 +2668,7 @@ proc initlayout {} { set nextcolor 0 set rowidlist {} set rowisopt {} + set rowfinal {} set canvxmax [$canv cget -width] catch {unset colormap} catch {unset rowtextx} @@ -2708,19 +2703,14 @@ proc visiblerows {} { return [list $r0 $r1] } -proc layoutmore {tmax allread} { - global commitidx numcommits +proc layoutmore {} { + global commitidx viewcomplete numcommits global uparrowlen downarrowlen mingaplen curview set show $commitidx($curview) - if {!$allread} { - set delay [expr {$uparrowlen + $mingaplen + $downarrowlen + 3}] - set show [expr {$show - $delay}] - } if {$show > $numcommits} { - showstuff $show $allread + showstuff $show $viewcomplete($curview) } - return 0 } proc showstuff {canshow last} { @@ -2901,6 +2891,21 @@ proc nextuse {id row} { return -1 } +proc prevuse {id row} { + global commitrow curview children + + set ret -1 + if {[info exists children($curview,$id)]} { + foreach kid $children($curview,$id) { + if {![info exists commitrow($curview,$kid)]} break + if {$commitrow($curview,$kid) < $row} { + set ret $commitrow($curview,$kid) + } + } + } + return $ret +} + proc make_idlist {row} { global displayorder parentlist uparrowlen downarrowlen mingaplen global commitidx curview ordertok children commitrow @@ -2964,11 +2969,42 @@ proc make_idlist {row} { return $idlist } +proc rowsequal {a b} { + while {[set i [lsearch -exact $a {}]] >= 0} { + set a [lreplace $a $i $i] + } + while {[set i [lsearch -exact $b {}]] >= 0} { + set b [lreplace $b $i $i] + } + return [expr {$a eq $b}] +} + +proc makeupline {id row rend col} { + global rowidlist uparrowlen downarrowlen mingaplen + + for {set r $rend} {1} {set r $rstart} { + set rstart [prevuse $id $r] + if {$rstart < 0} return + if {$rstart < $row} break + } + if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} { + set rstart [expr {$rend - $uparrowlen - 1}] + } + for {set r $rstart} {[incr r] <= $row} {} { + set idlist [lindex $rowidlist $r] + if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} { + set col [idcol $idlist $id $col] + lset rowidlist $r [linsert $idlist $col $id] + changedrow $r + } + } +} + proc layoutrows {row endrow} { - global rowidlist rowisopt displayorder + global rowidlist rowisopt rowfinal displayorder global uparrowlen downarrowlen maxwidth mingaplen global children parentlist - global commitidx curview commitrow + global commitidx viewcomplete curview commitrow set idlist {} if {$row > 0} { @@ -2982,14 +3018,20 @@ proc layoutrows {row endrow} { set rm1 [expr {$row - 1}] if {$rm1 < 0 || [lindex $rowidlist $rm1] eq {}} { set idlist [make_idlist $row] + set final 1 } else { set id [lindex $displayorder $rm1] + set final [lindex $rowfinal $rm1] set col [lsearch -exact $idlist $id] set idlist [lreplace $idlist $col $col] foreach p [lindex $parentlist $rm1] { if {[lsearch -exact $idlist $p] < 0} { set col [idcol $idlist $p $col] set idlist [linsert $idlist $col $p] + # if not the first child, we have to insert a line going up + if {$id ne [lindex $children($curview,$p) 0]} { + makeupline $p $rm1 $row $col + } } } set id [lindex $displayorder $row] @@ -3008,6 +3050,9 @@ proc layoutrows {row endrow} { if {$col < 0} { set col [idcol $idlist $id] set idlist [linsert $idlist $col $id] + if {$children($curview,$id) ne {}} { + makeupline $id $rm1 $row $col + } } set r [expr {$row + $uparrowlen - 1}] if {$r < $commitidx($curview)} { @@ -3032,18 +3077,28 @@ proc layoutrows {row endrow} { } } } + if {$final && !$viewcomplete($curview) && + $row + $uparrowlen + $mingaplen + $downarrowlen + >= $commitidx($curview)} { + set final 0 + } set l [llength $rowidlist] if {$row == $l} { lappend rowidlist $idlist lappend rowisopt 0 + lappend rowfinal $final } elseif {$row < $l} { - if {$idlist ne [lindex $rowidlist $row]} { + if {![rowsequal $idlist [lindex $rowidlist $row]]} { lset rowidlist $row $idlist + lset rowfinal $row $final changedrow $row } } else { - set rowidlist [concat $rowidlist [ntimes [expr {$row - $l}] {}]] + set pad [ntimes [expr {$row - $l}] {}] + set rowidlist [concat $rowidlist $pad] lappend rowidlist $idlist + set rowfinal [concat $rowfinal $pad] + lappend rowfinal $final set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]] } } @@ -3659,7 +3714,7 @@ proc drawcmitrow {row} { proc drawcommits {row {endrow {}}} { global numcommits iddrawn displayorder curview need_redisplay - global parentlist rowidlist uparrowlen downarrowlen nrows_drawn + global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn if {$row < 0} { set row 0 @@ -3684,7 +3739,7 @@ proc drawcommits {row {endrow {}}} { set r2 $numcommits } for {set r $rl1} {$r < $r2} {incr r} { - if {[lindex $rowidlist $r] ne {}} { + if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} { if {$rl1 < $r} { layoutrows $rl1 $r } From f56782aef4b3d7339461d8f12ff15f6258d9871d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 15 Sep 2007 09:04:11 +1000 Subject: [PATCH 013/123] gitk: Fix bugs in setting rowfinal We weren't updating the rowfinal list in insertrow and removerow, so it was getting out of sync with rowidlist, which resulted in Tcl errors. This also optimizes the setting of rowfinal in layoutrows a bit. Signed-off-by: Paul Mackerras --- gitk | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/gitk b/gitk index a042efe260..fd6bbab3ed 100755 --- a/gitk +++ b/gitk @@ -3008,20 +3008,21 @@ proc layoutrows {row endrow} { set idlist {} if {$row > 0} { - foreach id [lindex $rowidlist [expr {$row - 1}]] { + set rm1 [expr {$row - 1}] + foreach id [lindex $rowidlist $rm1] { if {$id ne {}} { lappend idlist $id } } + set final [lindex $rowfinal $rm1] } for {} {$row < $endrow} {incr row} { set rm1 [expr {$row - 1}] - if {$rm1 < 0 || [lindex $rowidlist $rm1] eq {}} { + if {$rm1 < 0 || $idlist eq {}} { set idlist [make_idlist $row] set final 1 } else { set id [lindex $displayorder $rm1] - set final [lindex $rowfinal $rm1] set col [lsearch -exact $idlist $id] set idlist [lreplace $idlist $col $col] foreach p [lindex $parentlist $rm1] { @@ -3090,9 +3091,9 @@ proc layoutrows {row endrow} { } elseif {$row < $l} { if {![rowsequal $idlist [lindex $rowidlist $row]]} { lset rowidlist $row $idlist - lset rowfinal $row $final changedrow $row } + lset rowfinal $row $final } else { set pad [ntimes [expr {$row - $l}] {}] set rowidlist [concat $rowidlist $pad] @@ -4055,7 +4056,7 @@ proc show_status {msg} { # on that row and below will move down one row. proc insertrow {row newcmit} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist rowisopt numcommits + global commitrow curview rowidlist rowisopt rowfinal numcommits global numcommits global selectedline commitidx ordertok @@ -4089,6 +4090,7 @@ proc insertrow {row newcmit} { } set rowidlist [linsert $rowidlist $row $idlist] set rowisopt [linsert $rowisopt $row 0] + set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]] incr numcommits @@ -4101,7 +4103,7 @@ proc insertrow {row newcmit} { # Remove a commit that was inserted with insertrow on row $row. proc removerow {row} { global displayorder parentlist commitlisted children - global commitrow curview rowidlist rowisopt numcommits + global commitrow curview rowidlist rowisopt rowfinal numcommits global numcommits global linesegends selectedline commitidx @@ -4130,6 +4132,7 @@ proc removerow {row} { set rowidlist [lreplace $rowidlist $row $row] set rowisopt [lreplace $rowisopt $row $row] + set rowfinal [lreplace $rowfinal $row $row] incr numcommits -1 From 3e6b893f33476e7969c7bd5b8914e8bcc62385e3 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 15 Sep 2007 09:33:39 +1000 Subject: [PATCH 014/123] gitk: Get rid of lookingforhead, use commitinterest instead Now that we have a general-purpose way of taking some action when a commit ID of interest is encountered, use that for triggering the git diff-index process when we find the currently checked-out head, rather than the special-purpose lookingforhead variable. Also do the commitinterest processing in getcommitlines rather than in showstuff. Signed-off-by: Paul Mackerras --- gitk | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/gitk b/gitk index fd6bbab3ed..85d33abf4a 100755 --- a/gitk +++ b/gitk @@ -83,7 +83,7 @@ proc start_rev_list {view} { global startmsecs global commfd leftover tclencoding datemode global viewargs viewfiles commitidx viewcomplete vnextroot - global lookingforhead showlocalchanges + global showlocalchanges commitinterest mainheadid set startmsecs [clock clicks -milliseconds] set commitidx($view) 0 @@ -102,7 +102,9 @@ proc start_rev_list {view} { } set commfd($view) $fd set leftover($view) {} - set lookingforhead $showlocalchanges + if {$showlocalchanges} { + lappend commitinterest($mainheadid) {dodiffindex} + } fconfigure $fd -blocking 0 -translation lf -eofchar {} if {$tclencoding != {}} { fconfigure $fd -encoding $tclencoding @@ -147,7 +149,7 @@ proc strrep {n} { } proc getcommitlines {fd view} { - global commitlisted + global commitlisted commitinterest global leftover commfd global displayorder commitidx viewcomplete commitrow commitdata global parentlist children curview hlview @@ -303,6 +305,12 @@ proc getcommitlines {fd view} { lappend vdisporder($view) $id lappend vcmitlisted($view) $listed } + if {[info exists commitinterest($id)]} { + foreach script $commitinterest($id) { + eval [string map [list "%I" $id] $script] + } + unset commitinterest($id) + } set gotsome 1 } if {$gotsome} { @@ -2715,7 +2723,7 @@ proc layoutmore {} { proc showstuff {canshow last} { global numcommits commitrow pending_select selectedline curview - global lookingforhead mainheadid displayorder selectfirst + global mainheadid displayorder selectfirst global lastscrollset commitinterest if {$numcommits == 0} { @@ -2723,15 +2731,6 @@ proc showstuff {canshow last} { set phase "incrdraw" allcanvs delete all } - for {set l $numcommits} {$l < $canshow} {incr l} { - set id [lindex $displayorder $l] - if {[info exists commitinterest($id)]} { - foreach script $commitinterest($id) { - eval [string map [list "%I" $id] $script] - } - unset commitinterest($id) - } - } set r0 $numcommits set prev $numcommits set numcommits $canshow @@ -2762,28 +2761,22 @@ proc showstuff {canshow last} { set selectfirst 0 } } - if {$lookingforhead && [info exists commitrow($curview,$mainheadid)] - && ($last || $commitrow($curview,$mainheadid) < $numcommits - 1)} { - set lookingforhead 0 - dodiffindex - } } proc doshowlocalchanges {} { - global lookingforhead curview mainheadid phase commitrow + global curview mainheadid phase commitrow if {[info exists commitrow($curview,$mainheadid)] && ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} { dodiffindex } elseif {$phase ne {}} { - set lookingforhead 1 + lappend commitinterest($mainheadid) {} } } proc dohidelocalchanges {} { - global lookingforhead localfrow localirow lserial + global localfrow localirow lserial - set lookingforhead 0 if {$localfrow >= 0} { removerow $localfrow set localfrow -1 @@ -2800,8 +2793,9 @@ proc dohidelocalchanges {} { # spawn off a process to do git diff-index --cached HEAD proc dodiffindex {} { - global localirow localfrow lserial + global localirow localfrow lserial showlocalchanges + if {!$showlocalchanges} return incr lserial set localfrow -1 set localirow -1 @@ -6188,7 +6182,6 @@ proc cherrypick {} { proc resethead {} { global mainheadid mainhead rowmenuid confirm_ok resettype - global showlocalchanges set confirm_ok 0 set w ".confirmreset" @@ -8249,7 +8242,6 @@ set cmdlineok 0 set stopped 0 set stuffsaved 0 set patchnum 0 -set lookingforhead 0 set localirow -1 set localfrow -1 set lserial 0 From d372e21613b36d94d595f6627ec603ed11e2fd65 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 15 Sep 2007 12:08:38 +1000 Subject: [PATCH 015/123] gitk: Fix bug in generating patches Commit 8f4893639129acfc866c71583317090aa2a46eab changed mkpatchgo to use diffcmd rather than constructing the diff command itself. Unfortunately diffcmd returns the command with a "|" as the first element (ready for use with open), but exec won't accept the "|". Thus we need to remove the "|". Signed-off-by: Paul Mackerras --- gitk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gitk b/gitk index 85d33abf4a..d5db836528 100755 --- a/gitk +++ b/gitk @@ -5920,6 +5920,8 @@ proc mkpatchgo {} { set newid [$patchtop.tosha1 get] set fname [$patchtop.fname get] set cmd [diffcmd [list $oldid $newid] -p] + # trim off the initial "|" + set cmd [lrange $cmd 1 end] lappend cmd >$fname & if {[catch {eval exec $cmd} err]} { error_popup "Error creating patch: $err" From f26a0012262106e0c7e92d8d07f00611112d3a8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:42 -0400 Subject: [PATCH 016/123] Enable wt-status output to a given FILE pointer. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still defaults to stdout, but you can now override wt_status.fp after calling wt_status_prepare(). Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- color.c | 18 ++++++------ color.h | 4 +-- wt-status.c | 85 +++++++++++++++++++++++++++-------------------------- wt-status.h | 3 ++ 4 files changed, 58 insertions(+), 52 deletions(-) diff --git a/color.c b/color.c index 09d82eec3d..124ba331c7 100644 --- a/color.c +++ b/color.c @@ -135,39 +135,39 @@ int git_config_colorbool(const char *var, const char *value) return git_config_bool(var, value); } -static int color_vprintf(const char *color, const char *fmt, +static int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args, const char *trail) { int r = 0; if (*color) - r += printf("%s", color); - r += vprintf(fmt, args); + r += fprintf(fp, "%s", color); + r += vfprintf(fp, fmt, args); if (*color) - r += printf("%s", COLOR_RESET); + r += fprintf(fp, "%s", COLOR_RESET); if (trail) - r += printf("%s", trail); + r += fprintf(fp, "%s", trail); return r; } -int color_printf(const char *color, const char *fmt, ...) +int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) { va_list args; int r; va_start(args, fmt); - r = color_vprintf(color, fmt, args, NULL); + r = color_vfprintf(fp, color, fmt, args, NULL); va_end(args); return r; } -int color_printf_ln(const char *color, const char *fmt, ...) +int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...) { va_list args; int r; va_start(args, fmt); - r = color_vprintf(color, fmt, args, "\n"); + r = color_vfprintf(fp, color, fmt, args, "\n"); va_end(args); return r; } diff --git a/color.h b/color.h index 88bb8ff1bd..68098006ed 100644 --- a/color.h +++ b/color.h @@ -6,7 +6,7 @@ int git_config_colorbool(const char *var, const char *value); void color_parse(const char *var, const char *value, char *dst); -int color_printf(const char *color, const char *fmt, ...); -int color_printf_ln(const char *color, const char *fmt, ...); +int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); +int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); #endif /* COLOR_H */ diff --git a/wt-status.c b/wt-status.c index 10ce6eedc7..eeb16915c2 100644 --- a/wt-status.c +++ b/wt-status.c @@ -52,31 +52,33 @@ void wt_status_prepare(struct wt_status *s) head = resolve_ref("HEAD", sha1, 0, NULL); s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; + s->fp = stdout; } -static void wt_status_print_cached_header(const char *reference) +static void wt_status_print_cached_header(struct wt_status *s) { const char *c = color(WT_STATUS_HEADER); - color_printf_ln(c, "# Changes to be committed:"); - if (reference) { - color_printf_ln(c, "# (use \"git reset %s ...\" to unstage)", reference); + color_fprintf_ln(s->fp, c, "# Changes to be committed:"); + if (s->reference) { + color_fprintf_ln(s->fp, c, "# (use \"git reset %s ...\" to unstage)", s->reference); } else { - color_printf_ln(c, "# (use \"git rm --cached ...\" to unstage)"); + color_fprintf_ln(s->fp, c, "# (use \"git rm --cached ...\" to unstage)"); } - color_printf_ln(c, "#"); + color_fprintf_ln(s->fp, c, "#"); } -static void wt_status_print_header(const char *main, const char *sub) +static void wt_status_print_header(struct wt_status *s, + const char *main, const char *sub) { const char *c = color(WT_STATUS_HEADER); - color_printf_ln(c, "# %s:", main); - color_printf_ln(c, "# (%s)", sub); - color_printf_ln(c, "#"); + color_fprintf_ln(s->fp, c, "# %s:", main); + color_fprintf_ln(s->fp, c, "# (%s)", sub); + color_fprintf_ln(s->fp, c, "#"); } -static void wt_status_print_trailer(void) +static void wt_status_print_trailer(struct wt_status *s) { - color_printf_ln(color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); } static const char *quote_crlf(const char *in, char *buf, size_t sz) @@ -108,7 +110,8 @@ static const char *quote_crlf(const char *in, char *buf, size_t sz) return ret; } -static void wt_status_print_filepair(int t, struct diff_filepair *p) +static void wt_status_print_filepair(struct wt_status *s, + int t, struct diff_filepair *p) { const char *c = color(t); const char *one, *two; @@ -117,36 +120,36 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p) one = quote_crlf(p->one->path, onebuf, sizeof(onebuf)); two = quote_crlf(p->two->path, twobuf, sizeof(twobuf)); - color_printf(color(WT_STATUS_HEADER), "#\t"); + color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); switch (p->status) { case DIFF_STATUS_ADDED: - color_printf(c, "new file: %s", one); + color_fprintf(s->fp, c, "new file: %s", one); break; case DIFF_STATUS_COPIED: - color_printf(c, "copied: %s -> %s", one, two); + color_fprintf(s->fp, c, "copied: %s -> %s", one, two); break; case DIFF_STATUS_DELETED: - color_printf(c, "deleted: %s", one); + color_fprintf(s->fp, c, "deleted: %s", one); break; case DIFF_STATUS_MODIFIED: - color_printf(c, "modified: %s", one); + color_fprintf(s->fp, c, "modified: %s", one); break; case DIFF_STATUS_RENAMED: - color_printf(c, "renamed: %s -> %s", one, two); + color_fprintf(s->fp, c, "renamed: %s -> %s", one, two); break; case DIFF_STATUS_TYPE_CHANGED: - color_printf(c, "typechange: %s", one); + color_fprintf(s->fp, c, "typechange: %s", one); break; case DIFF_STATUS_UNKNOWN: - color_printf(c, "unknown: %s", one); + color_fprintf(s->fp, c, "unknown: %s", one); break; case DIFF_STATUS_UNMERGED: - color_printf(c, "unmerged: %s", one); + color_fprintf(s->fp, c, "unmerged: %s", one); break; default: die("bug: unhandled diff status %c", p->status); } - printf("\n"); + fprintf(s->fp, "\n"); } static void wt_status_print_updated_cb(struct diff_queue_struct *q, @@ -160,14 +163,14 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q, if (q->queue[i]->status == 'U') continue; if (!shown_header) { - wt_status_print_cached_header(s->reference); + wt_status_print_cached_header(s); s->commitable = 1; shown_header = 1; } - wt_status_print_filepair(WT_STATUS_UPDATED, q->queue[i]); + wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]); } if (shown_header) - wt_status_print_trailer(); + wt_status_print_trailer(s); } static void wt_status_print_changed_cb(struct diff_queue_struct *q, @@ -184,12 +187,12 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q, msg = use_add_rm_msg; break; } - wt_status_print_header("Changed but not updated", msg); + wt_status_print_header(s, "Changed but not updated", msg); } for (i = 0; i < q->nr; i++) - wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]); + wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]); if (q->nr) - wt_status_print_trailer(); + wt_status_print_trailer(s); } static void wt_read_cache(struct wt_status *s) @@ -206,16 +209,16 @@ static void wt_status_print_initial(struct wt_status *s) wt_read_cache(s); if (active_nr) { s->commitable = 1; - wt_status_print_cached_header(NULL); + wt_status_print_cached_header(s); } for (i = 0; i < active_nr; i++) { - color_printf(color(WT_STATUS_HEADER), "#\t"); - color_printf_ln(color(WT_STATUS_UPDATED), "new file: %s", + color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); + color_fprintf_ln(s->fp, color(WT_STATUS_UPDATED), "new file: %s", quote_crlf(active_cache[i]->name, buf, sizeof(buf))); } if (active_nr) - wt_status_print_trailer(); + wt_status_print_trailer(s); } static void wt_status_print_updated(struct wt_status *s) @@ -282,12 +285,12 @@ static void wt_status_print_untracked(struct wt_status *s) } if (!shown_header) { s->workdir_untracked = 1; - wt_status_print_header("Untracked files", + wt_status_print_header(s, "Untracked files", use_add_to_include_msg); shown_header = 1; } - color_printf(color(WT_STATUS_HEADER), "#\t"); - color_printf_ln(color(WT_STATUS_UNTRACKED), "%.*s", + color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t"); + color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%.*s", ent->len, ent->name); } } @@ -317,14 +320,14 @@ void wt_status_print(struct wt_status *s) branch_name = ""; on_what = "Not currently on any branch."; } - color_printf_ln(color(WT_STATUS_HEADER), + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# %s%s", on_what, branch_name); } if (s->is_initial) { - color_printf_ln(color(WT_STATUS_HEADER), "#"); - color_printf_ln(color(WT_STATUS_HEADER), "# Initial commit"); - color_printf_ln(color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit"); + color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#"); wt_status_print_initial(s); } else { @@ -338,7 +341,7 @@ void wt_status_print(struct wt_status *s) wt_status_print_verbose(s); if (!s->commitable) { if (s->amend) - printf("# No changes\n"); + fprintf(s->fp, "# No changes\n"); else if (s->workdir_dirty) printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); else if (s->workdir_untracked) diff --git a/wt-status.h b/wt-status.h index cfea4ae688..4f3a615c32 100644 --- a/wt-status.h +++ b/wt-status.h @@ -1,6 +1,8 @@ #ifndef STATUS_H #define STATUS_H +#include + enum color_wt_status { WT_STATUS_HEADER, WT_STATUS_UPDATED, @@ -19,6 +21,7 @@ struct wt_status { int commitable; int workdir_dirty; int workdir_untracked; + FILE *fp; }; int git_status_config(const char *var, const char *value); From 0f729f21348c43a1c80f48faed2e753b1c923e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:43 -0400 Subject: [PATCH 017/123] Enable wt-status to run against non-standard index file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We still default to get_index_file(), but this can be overridden by setting wt_status.index_file after calling wt_status_prepare(). Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- wt-status.c | 3 ++- wt-status.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/wt-status.c b/wt-status.c index eeb16915c2..03b5ec4488 100644 --- a/wt-status.c +++ b/wt-status.c @@ -53,6 +53,7 @@ void wt_status_prepare(struct wt_status *s) s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; s->fp = stdout; + s->index_file = get_index_file(); } static void wt_status_print_cached_header(struct wt_status *s) @@ -198,7 +199,7 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q, static void wt_read_cache(struct wt_status *s) { discard_cache(); - read_cache(); + read_cache_from(s->index_file); } static void wt_status_print_initial(struct wt_status *s) diff --git a/wt-status.h b/wt-status.h index 4f3a615c32..77449326db 100644 --- a/wt-status.h +++ b/wt-status.h @@ -21,6 +21,7 @@ struct wt_status { int commitable; int workdir_dirty; int workdir_untracked; + const char *index_file; FILE *fp; }; From 687c8765ec996225a01cadc7d91354ae3cfbdf8a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 22 Sep 2007 12:49:33 +1000 Subject: [PATCH 018/123] gitk: Simplify highlighting interface and combine with Find function This effectively coaelesces the highlighting function and the search function. Instead of separate highlight and find controls, there is now one set of interface elements that controls both. The main selector is a drop-down menu that controls whether commits are highlighted and searched for on the basis of text in the commit (i.e. the commit object), files affected by the commit or strings added/removed by the commit. The functions to highlight by membership of a view or by ancestor/ descendent relation to the selected commit are gone, as is the move to next/previous highlighted commit (shift-up/down) function. Signed-off-by: Paul Mackerras --- gitk | 430 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 225 insertions(+), 205 deletions(-) diff --git a/gitk b/gitk index d5db836528..a5d0d66e6c 100755 --- a/gitk +++ b/gitk @@ -706,62 +706,43 @@ proc makewindow {} { -state disabled -width 26 pack .tf.bar.rightbut -side left -fill y - button .tf.bar.findbut -text "Find" -command dofind -font $uifont - pack .tf.bar.findbut -side left - set findstring {} - set fstring .tf.bar.findstring - lappend entries $fstring - entry $fstring -width 30 -font $textfont -textvariable findstring - trace add variable findstring write find_change - pack $fstring -side left -expand 1 -fill x -in .tf.bar - set findtype Exact - set findtypemenu [tk_optionMenu .tf.bar.findtype \ - findtype Exact IgnCase Regexp] - trace add variable findtype write find_change - .tf.bar.findtype configure -font $uifont - .tf.bar.findtype.menu configure -font $uifont - set findloc "All fields" - tk_optionMenu .tf.bar.findloc findloc "All fields" Headline \ - Comments Author Committer - trace add variable findloc write find_change - .tf.bar.findloc configure -font $uifont - .tf.bar.findloc.menu configure -font $uifont - pack .tf.bar.findloc -side right - pack .tf.bar.findtype -side right - # build up the bottom bar of upper window - label .tf.lbar.flabel -text "Highlight: Commits " \ - -font $uifont - pack .tf.lbar.flabel -side left -fill y - set gdttype "touching paths:" - set gm [tk_optionMenu .tf.lbar.gdttype gdttype "touching paths:" \ - "adding/removing string:"] - trace add variable gdttype write hfiles_change + label .tf.lbar.flabel -text "Find " -font $uifont + button .tf.lbar.fnext -text "next" -command dofind -font $uifont + button .tf.lbar.fprev -text "prev" -command {dofind 1} -font $uifont + label .tf.lbar.flab2 -text " commit " -font $uifont + pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \ + -side left -fill y + set gdttype "containing:" + set gm [tk_optionMenu .tf.lbar.gdttype gdttype \ + "containing:" \ + "touching paths:" \ + "adding/removing string:"] + trace add variable gdttype write gdttype_change $gm conf -font $uifont .tf.lbar.gdttype conf -font $uifont pack .tf.lbar.gdttype -side left -fill y - entry .tf.lbar.fent -width 25 -font $textfont \ - -textvariable highlight_files - trace add variable highlight_files write hfiles_change - lappend entries .tf.lbar.fent - pack .tf.lbar.fent -side left -fill x -expand 1 - label .tf.lbar.vlabel -text " OR in view" -font $uifont - pack .tf.lbar.vlabel -side left -fill y - global viewhlmenu selectedhlview - set viewhlmenu [tk_optionMenu .tf.lbar.vhl selectedhlview None] - $viewhlmenu entryconf None -command delvhighlight - $viewhlmenu conf -font $uifont - .tf.lbar.vhl conf -font $uifont - pack .tf.lbar.vhl -side left -fill y - label .tf.lbar.rlabel -text " OR " -font $uifont - pack .tf.lbar.rlabel -side left -fill y - global highlight_related - set m [tk_optionMenu .tf.lbar.relm highlight_related None \ - "Descendent" "Not descendent" "Ancestor" "Not ancestor"] - $m conf -font $uifont - .tf.lbar.relm conf -font $uifont - trace add variable highlight_related write vrel_change - pack .tf.lbar.relm -side left -fill y + + set findstring {} + set fstring .tf.lbar.findstring + lappend entries $fstring + entry $fstring -width 30 -font $textfont -textvariable findstring + trace add variable findstring write find_change + set findtype Exact + set findtypemenu [tk_optionMenu .tf.lbar.findtype \ + findtype Exact IgnCase Regexp] + trace add variable findtype write findcom_change + .tf.lbar.findtype configure -font $uifont + .tf.lbar.findtype.menu configure -font $uifont + set findloc "All fields" + tk_optionMenu .tf.lbar.findloc findloc "All fields" Headline \ + Comments Author Committer + trace add variable findloc write find_change + .tf.lbar.findloc configure -font $uifont + .tf.lbar.findloc.menu configure -font $uifont + pack .tf.lbar.findloc -side right + pack .tf.lbar.findtype -side right + pack $fstring -side left -expand 1 -fill x # Finish putting the upper half of the viewer together pack .tf.lbar -in .tf -side bottom -fill x @@ -914,8 +895,6 @@ proc makewindow {} { bindkey sellastline bind . "selnextline -1" bind . "selnextline 1" - bind . "next_highlight -1" - bind . "next_highlight 1" bindkey "goforw" bindkey "goback" bind . "selnextpage -1" @@ -1852,10 +1831,10 @@ proc doviewmenu {m first cmd op argv} { } proc allviewmenus {n op args} { - global viewhlmenu + # global viewhlmenu doviewmenu .bar.view 5 [list showview $n] $op $args - doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args + # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args } proc newviewok {top n} { @@ -1898,8 +1877,8 @@ proc newviewok {top n} { set viewname($n) $newviewname($n) doviewmenu .bar.view 5 [list showview $n] \ entryconf [list -label $viewname($n)] - doviewmenu $viewhlmenu 1 [list addvhighlight $n] \ - entryconf [list -label $viewname($n) -value $viewname($n)] + # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \ + # entryconf [list -label $viewname($n) -value $viewname($n)] } if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} { set viewfiles($n) $files @@ -1931,8 +1910,8 @@ proc addviewmenu {n} { .bar.view add radiobutton -label $viewname($n) \ -command [list showview $n] -variable selectedview -value $n - $viewhlmenu add radiobutton -label $viewname($n) \ - -command [list addvhighlight $n] -variable selectedhlview + #$viewhlmenu add radiobutton -label $viewname($n) \ + # -command [list addvhighlight $n] -variable selectedhlview } proc flatten {var} { @@ -2208,9 +2187,9 @@ proc askvhighlight {row id} { } } -proc hfiles_change {name ix op} { +proc hfiles_change {} { global highlight_files filehighlight fhighlights fh_serial - global mainfont highlight_paths + global mainfont highlight_paths gdttype if {[info exists filehighlight]} { # delete previous highlights @@ -2228,6 +2207,66 @@ proc hfiles_change {name ix op} { } } +proc gdttype_change {name ix op} { + global gdttype highlight_files findstring findpattern + + if {$findstring ne {}} { + if {$gdttype eq "containing:"} { + if {$highlight_files ne {}} { + set highlight_files {} + hfiles_change + } + findcom_change + } else { + if {$findpattern ne {}} { + set findpattern {} + findcom_change + } + set highlight_files $findstring + hfiles_change + } + drawvisible + } + # enable/disable findtype/findloc menus too +} + +proc find_change {name ix op} { + global gdttype findstring highlight_files + + if {$gdttype eq "containing:"} { + findcom_change + } else { + if {$highlight_files ne $findstring} { + set highlight_files $findstring + hfiles_change + } + } + drawvisible +} + +proc findcom_change {} { + global nhighlights mainfont boldnamerows + global findpattern findtype findstring gdttype + + # delete previous highlights, if any + foreach row $boldnamerows { + bolden_name $row $mainfont + } + set boldnamerows {} + catch {unset nhighlights} + unbolden + unmarkmatches + if {$gdttype ne "containing:" || $findstring eq {}} { + set findpattern {} + } elseif {$findtype eq "Regexp"} { + set findpattern $findstring + } else { + set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \ + $findstring] + set findpattern "*$e*" + } +} + proc makepatterns {l} { set ret {} foreach e $l { @@ -2250,8 +2289,11 @@ proc do_file_hl {serial} { set highlight_paths [makepatterns $paths] highlight_filelist set gdtargs [concat -- $paths] - } else { + } elseif {$gdttype eq "adding/removing string:"} { set gdtargs [list "-S$highlight_files"] + } else { + # must be "containing:", i.e. we're searching commit info + return } set cmd [concat | git diff-tree -r -s --stdin $gdtargs] set filehighlight [open $cmd r+] @@ -2282,7 +2324,7 @@ proc askfilehighlight {row id} { proc readfhighlight {} { global filehighlight fhighlights commitrow curview mainfont iddrawn - global fhl_list + global fhl_list find_dirn if {![info exists filehighlight]} { return 0 @@ -2314,35 +2356,21 @@ proc readfhighlight {} { unset filehighlight return 0 } - next_hlcont + if {[info exists find_dirn]} { + if {$find_dirn > 0} { + run findmore + } else { + run findmorerev + } + } return 1 } -proc find_change {name ix op} { - global nhighlights mainfont boldnamerows - global findstring findpattern findtype - - # delete previous highlights, if any - foreach row $boldnamerows { - bolden_name $row $mainfont - } - set boldnamerows {} - catch {unset nhighlights} - unbolden - unmarkmatches - if {$findtype ne "Regexp"} { - set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \ - $findstring] - set findpattern "*$e*" - } - drawvisible -} - proc doesmatch {f} { - global findtype findstring findpattern + global findtype findpattern if {$findtype eq "Regexp"} { - return [regexp $findstring $f] + return [regexp $findpattern $f] } elseif {$findtype eq "IgnCase"} { return [string match -nocase $findpattern $f] } else { @@ -2535,81 +2563,6 @@ proc askrelhighlight {row id} { set rhighlights($row) $isbold } -proc next_hlcont {} { - global fhl_row fhl_dirn displayorder numcommits - global vhighlights fhighlights nhighlights rhighlights - global hlview filehighlight findstring highlight_related - - if {![info exists fhl_dirn] || $fhl_dirn == 0} return - set row $fhl_row - while {1} { - if {$row < 0 || $row >= $numcommits} { - bell - set fhl_dirn 0 - return - } - set id [lindex $displayorder $row] - if {[info exists hlview]} { - if {![info exists vhighlights($row)]} { - askvhighlight $row $id - } - if {$vhighlights($row) > 0} break - } - if {$findstring ne {}} { - if {![info exists nhighlights($row)]} { - askfindhighlight $row $id - } - if {$nhighlights($row) > 0} break - } - if {$highlight_related ne "None"} { - if {![info exists rhighlights($row)]} { - askrelhighlight $row $id - } - if {$rhighlights($row) > 0} break - } - if {[info exists filehighlight]} { - if {![info exists fhighlights($row)]} { - # ask for a few more while we're at it... - set r $row - for {set n 0} {$n < 100} {incr n} { - if {![info exists fhighlights($r)]} { - askfilehighlight $r [lindex $displayorder $r] - } - incr r $fhl_dirn - if {$r < 0 || $r >= $numcommits} break - } - flushhighlights - } - if {$fhighlights($row) < 0} { - set fhl_row $row - return - } - if {$fhighlights($row) > 0} break - } - incr row $fhl_dirn - } - set fhl_dirn 0 - selectline $row 1 -} - -proc next_highlight {dirn} { - global selectedline fhl_row fhl_dirn - global hlview filehighlight findstring highlight_related - - if {![info exists selectedline]} return - if {!([info exists hlview] || $findstring ne {} || - $highlight_related ne "None" || [info exists filehighlight])} return - set fhl_row [expr {$selectedline + $dirn}] - set fhl_dirn $dirn - next_hlcont -} - -proc cancel_next_highlight {} { - global fhl_dirn - - set fhl_dirn 0 -} - # Graph layout functions proc shortids {ids} { @@ -3669,7 +3622,7 @@ proc drawcmitrow {row} { global displayorder rowidlist nrows_drawn global iddrawn markingmatches global commitinfo parentlist numcommits - global filehighlight fhighlights findstring nhighlights + global filehighlight fhighlights findpattern nhighlights global hlview vhighlights global highlight_related rhighlights @@ -3682,7 +3635,7 @@ proc drawcmitrow {row} { if {[info exists filehighlight] && ![info exists fhighlights($row)]} { askfilehighlight $row $id } - if {$findstring ne {} && ![info exists nhighlights($row)]} { + if {$findpattern ne {} && ![info exists nhighlights($row)]} { askfindhighlight $row $id } if {$highlight_related ne "None" && ![info exists rhighlights($row)]} { @@ -4190,9 +4143,9 @@ proc findmatches {f} { proc dofind {{rev 0}} { global findstring findstartline findcurline selectedline numcommits + global gdttype filehighlight fh_serial find_dirn unmarkmatches - cancel_next_highlight focus . if {$findstring eq {} || $numcommits == 0} return if {![info exists selectedline]} { @@ -4202,19 +4155,24 @@ proc dofind {{rev 0}} { } set findcurline $findstartline nowbusy finding + if {$gdttype ne "containing:" && ![info exists filehighlight]} { + after cancel do_file_hl $fh_serial + do_file_hl $fh_serial + } if {!$rev} { + set find_dirn 1 run findmore } else { - if {$findcurline == 0} { - set findcurline $numcommits - } - incr findcurline -1 + set find_dirn -1 run findmorerev } } proc findnext {restart} { - global findcurline + global findcurline find_dirn + + if {[info exists find_dirn]} return + set find_dirn 1 if {![info exists findcurline]} { if {$restart} { dofind @@ -4228,7 +4186,10 @@ proc findnext {restart} { } proc findprev {} { - global findcurline + global findcurline find_dirn + + if {[info exists find_dirn]} return + set find_dirn -1 if {![info exists findcurline]} { dofind 1 } else { @@ -4238,8 +4199,9 @@ proc findprev {} { } proc findmore {} { - global commitdata commitinfo numcommits findstring findpattern findloc + global commitdata commitinfo numcommits findpattern findloc global findstartline findcurline displayorder + global find_dirn gdttype fhighlights set fldtypes {Headline Author Date Committer CDate Comments} set l [expr {$findcurline + 1}] @@ -4254,28 +4216,56 @@ proc findmore {} { if {$lim - $l > 500} { set lim [expr {$l + 500}] } - set last 0 - for {} {$l < $lim} {incr l} { - set id [lindex $displayorder $l] - # shouldn't happen unless git log doesn't give all the commits... - if {![info exists commitdata($id)]} continue - if {![doesmatch $commitdata($id)]} continue - if {![info exists commitinfo($id)]} { - getcommit $id + set found 0 + set domore 1 + if {$gdttype eq "containing:"} { + for {} {$l < $lim} {incr l} { + set id [lindex $displayorder $l] + # shouldn't happen unless git log doesn't give all the commits... + if {![info exists commitdata($id)]} continue + 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 found 1 + break + } + } + if {$found} break } - set info $commitinfo($id) - foreach f $info ty $fldtypes { - if {($findloc eq "All fields" || $findloc eq $ty) && - [doesmatch $f]} { - findselectline $l - notbusy finding - return 0 + } else { + for {} {$l < $lim} {incr l} { + set id [lindex $displayorder $l] + if {![info exists fhighlights($l)]} { + askfilehighlight $l $id + if {$domore} { + set domore 0 + set findcurline [expr {$l - 1}] + } + } elseif {$fhighlights($l)} { + set found $domore + break } } } + if {$found} { + unset find_dirn + findselectline $l + notbusy finding + return 0 + } + if {!$domore} { + flushhighlights + return 0 + } if {$l == $findstartline + 1} { bell unset findcurline + unset find_dirn notbusy finding return 0 } @@ -4284,8 +4274,9 @@ proc findmore {} { } proc findmorerev {} { - global commitdata commitinfo numcommits findstring findpattern findloc + global commitdata commitinfo numcommits findpattern findloc global findstartline findcurline displayorder + global find_dirn gdttype fhighlights set fldtypes {Headline Author Date Committer CDate Comments} set l $findcurline @@ -4301,27 +4292,55 @@ proc findmorerev {} { if {$l - $lim > 500} { set lim [expr {$l - 500}] } - set last 0 - for {} {$l > $lim} {incr l -1} { - set id [lindex $displayorder $l] - if {![info exists commitdata($id)]} continue - if {![doesmatch $commitdata($id)]} continue - if {![info exists commitinfo($id)]} { - getcommit $id + set found 0 + set domore 1 + if {$gdttype eq "containing:"} { + for {} {$l > $lim} {incr l -1} { + set id [lindex $displayorder $l] + if {![info exists commitdata($id)]} continue + 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 found 1 + break + } + } + if {$found} break } - set info $commitinfo($id) - foreach f $info ty $fldtypes { - if {($findloc eq "All fields" || $findloc eq $ty) && - [doesmatch $f]} { - findselectline $l - notbusy finding - return 0 + } else { + for {} {$l > $lim} {incr l -1} { + set id [lindex $displayorder $l] + if {![info exists fhighlights($l)]} { + askfilehighlight $l $id + if {$domore} { + set domore 0 + set findcurline [expr {$l + 1}] + } + } elseif {$fhighlights($l)} { + set found $domore + break } } } + if {$found} { + unset find_dirn + findselectline $l + notbusy finding + return 0 + } + if {!$domore} { + flushhighlights + return 0 + } if {$l == -1} { bell unset findcurline + unset find_dirn notbusy finding return 0 } @@ -4330,7 +4349,7 @@ proc findmorerev {} { } proc findselectline {l} { - global findloc commentend ctext findcurline markingmatches + global findloc commentend ctext findcurline markingmatches gdttype set markingmatches 1 set findcurline $l @@ -4599,7 +4618,6 @@ proc selectline {l isnew} { catch {unset pending_select} $canv delete hover normalline - cancel_next_highlight unsel_reflist if {$l < 0 || $l >= $numcommits} return set y [expr {$canvy0 + $l * $linespc}] @@ -4781,7 +4799,6 @@ proc unselectline {} { catch {unset currentid} allcanvs delete secsel rhighlight_none - cancel_next_highlight } proc reselectline {} { @@ -8223,6 +8240,7 @@ set historyindex 0 set fh_serial 0 set nhl_names {} set highlight_paths {} +set findpattern {} set searchdirn -forwards set boldrows {} set boldnamerows {} @@ -8236,6 +8254,8 @@ set nextviewnum 1 set curview 0 set selectedview 0 set selectedhlview None +set highlight_related None +set highlight_files {} set viewfiles(0) {} set viewperm(0) 0 set viewargs(0) {} From c73adce219ce52a662d90af1e1762c77ea5c4cb0 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 27 Sep 2007 10:35:05 +1000 Subject: [PATCH 019/123] gitk: Fix a couple of bugs insertrow and removerow were trying to adjust rowidlist, rowisopt and rowfinal even if the row where we're inserting/deleting stuff hasn't been laid out yet, which resulted in Tcl errors. This fixes that. Also we weren't deleting the link$linknum tag in appendwithlinks, which resulted in SHA1 IDs in the body of a commit message sometimes getting shown in blue with underlining when they shouldn't. Signed-off-by: Paul Mackerras --- gitk | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/gitk b/gitk index a5d0d66e6c..34fe33771f 100755 --- a/gitk +++ b/gitk @@ -4027,17 +4027,21 @@ proc insertrow {row newcmit} { incr commitidx($curview) set ordertok($curview,$newcmit) $ordertok($curview,$p) - set idlist [lindex $rowidlist $row] - if {[llength $kids] == 1} { - set col [lsearch -exact $idlist $p] - lset idlist $col $newcmit - } else { - set col [llength $idlist] - lappend idlist $newcmit + if {$row < [llength $rowidlist]} { + set idlist [lindex $rowidlist $row] + if {$idlist ne {}} { + if {[llength $kids] == 1} { + set col [lsearch -exact $idlist $p] + lset idlist $col $newcmit + } else { + set col [llength $idlist] + lappend idlist $newcmit + } + } + set rowidlist [linsert $rowidlist $row $idlist] + set rowisopt [linsert $rowisopt $row 0] + set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]] } - set rowidlist [linsert $rowidlist $row $idlist] - set rowisopt [linsert $rowisopt $row 0] - set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]] incr numcommits @@ -4077,9 +4081,11 @@ proc removerow {row} { } incr commitidx($curview) -1 - set rowidlist [lreplace $rowidlist $row $row] - set rowisopt [lreplace $rowisopt $row $row] - set rowfinal [lreplace $rowfinal $row $row] + if {$row < [llength $rowidlist]} { + set rowidlist [lreplace $rowidlist $row $row] + set rowisopt [lreplace $rowisopt $row $row] + set rowfinal [lreplace $rowfinal $row $row] + } incr numcommits -1 @@ -4443,6 +4449,7 @@ proc appendwithlinks {text tags} { set e [lindex $l 1] set linkid [string range $text $s $e] incr e + $ctext tag delete link$linknum $ctext tag add link$linknum "$start + $s c" "$start + $e c" setlink $linkid link$linknum incr linknum From bb3edc8b0473192da11bf7f9e961ea0fcc444c63 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 27 Sep 2007 11:00:25 +1000 Subject: [PATCH 020/123] gitk: Add progress bars for reading in stuff and for finding This uses the space formerly occupied by the find string entry field to make a status label (unused for now) and a canvas to display a couple of progress bars. The bar for reading in commits is a short green bar that oscillates back and forth as commits come in. The bar for showing the progress of a Find operation is yellow and advances from left to right. This also arranges to stop a Find operation if the user selects another commit or pops up a context menu, and fixes the "highlight this" popup menu items in the file list window. Signed-off-by: Paul Mackerras --- gitk | 186 ++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 153 insertions(+), 33 deletions(-) diff --git a/gitk b/gitk index 34fe33771f..4e168e98a0 100755 --- a/gitk +++ b/gitk @@ -84,6 +84,7 @@ proc start_rev_list {view} { global commfd leftover tclencoding datemode global viewargs viewfiles commitidx viewcomplete vnextroot global showlocalchanges commitinterest mainheadid + global progressdirn progresscoords proglastnc curview set startmsecs [clock clicks -milliseconds] set commitidx($view) 0 @@ -111,6 +112,11 @@ proc start_rev_list {view} { } filerun $fd [list getcommitlines $fd $view] nowbusy $view + if {$view == $curview} { + set progressdirn 1 + set progresscoords {0 0} + set proglastnc 0 + } } proc stop_rev_list {} { @@ -183,9 +189,11 @@ proc getcommitlines {fd view} { } } set viewcomplete($view) 1 - global viewname + global viewname progresscoords unset commfd($view) notbusy $view + set progresscoords {0 0} + adjustprogress # set it blocking so we wait for the process to terminate fconfigure $fd -blocking 1 if {[catch {close $fd} err]} { @@ -315,6 +323,33 @@ proc getcommitlines {fd view} { } if {$gotsome} { run chewcommits $view + if {$view == $curview} { + # update progress bar + global progressdirn progresscoords proglastnc + set inc [expr {($commitidx($view) - $proglastnc) * 0.0002}] + set proglastnc $commitidx($view) + set l [lindex $progresscoords 0] + set r [lindex $progresscoords 1] + if {$progressdirn} { + set r [expr {$r + $inc}] + if {$r >= 1.0} { + set r 1.0 + set progressdirn 0 + } + if {$r > 0.2} { + set l [expr {$r - 0.2}] + } + } else { + set l [expr {$l - $inc}] + if {$l <= 0.0} { + set l 0.0 + set progressdirn 1 + } + set r [expr {$l + 0.2}] + } + set progresscoords [list $l $r] + adjustprogress + } } return 2 } @@ -589,7 +624,8 @@ proc makewindow {} { global highlight_files gdttype global searchstring sstring global bgcolor fgcolor bglist fglist diffcolors selectbgcolor - global headctxmenu + global headctxmenu progresscanv progressitem progresscoords statusw + global fprogitem fprogcoord lastprogupdate progupdatepending menu .bar .bar add cascade -label "File" -menu .bar.file @@ -706,6 +742,22 @@ proc makewindow {} { -state disabled -width 26 pack .tf.bar.rightbut -side left -fill y + # Status label and progress bar + set statusw .tf.bar.status + label $statusw -width 15 -relief sunken -font $uifont + pack $statusw -side left -padx 5 + set h [expr {[font metrics $uifont -linespace] + 2}] + set progresscanv .tf.bar.progress + canvas $progresscanv -relief sunken -height $h -borderwidth 2 + set progressitem [$progresscanv create rect -1 0 0 $h -fill green] + set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow] + pack $progresscanv -side right -expand 1 -fill x + set progresscoords {0 0} + set fprogcoord 0 + bind $progresscanv adjustprogress + set lastprogupdate [clock clicks -milliseconds] + set progupdatepending 0 + # build up the bottom bar of upper window label .tf.lbar.flabel -text "Find " -font $uifont button .tf.lbar.fnext -text "next" -command dofind -font $uifont @@ -1051,6 +1103,37 @@ proc click {w} { focus . } +# Adjust the progress bar for a change in requested extent or canvas size +proc adjustprogress {} { + global progresscanv progressitem progresscoords + global fprogitem fprogcoord lastprogupdate progupdatepending + + set w [expr {[winfo width $progresscanv] - 4}] + set x0 [expr {$w * [lindex $progresscoords 0]}] + set x1 [expr {$w * [lindex $progresscoords 1]}] + set h [winfo height $progresscanv] + $progresscanv coords $progressitem $x0 0 $x1 $h + $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h + set now [clock clicks -milliseconds] + if {$now >= $lastprogupdate + 100} { + set progupdatepending 0 + update + } elseif {!$progupdatepending} { + set progupdatepending 1 + after [expr {$lastprogupdate + 100 - $now}] doprogupdate + } +} + +proc doprogupdate {} { + global lastprogupdate progupdatepending + + if {$progupdatepending} { + set progupdatepending 0 + set lastprogupdate [clock clicks -milliseconds] + update + } +} + proc savestuff {w} { global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop global stuffsaved findmergefiles maxgraphpct @@ -1626,6 +1709,7 @@ proc pop_flist_menu {w X Y x y} { global ctext cflist cmitmode flist_menu flist_menu_file global treediffs diffids + stopfinding set l [lindex [split [$w index "@$x,$y"] "."] 0] if {$l <= 1} return if {$cmitmode eq "tree"} { @@ -1639,14 +1723,15 @@ proc pop_flist_menu {w X Y x y} { } proc flist_hl {only} { - global flist_menu_file highlight_files + global flist_menu_file findstring gdttype set x [shellquote $flist_menu_file] - if {$only || $highlight_files eq {}} { - set highlight_files $x + if {$only || $findstring eq {} || $gdttype ne "touching paths:"} { + set findstring $x } else { - append highlight_files " " $x + append findstring " " $x } + set gdttype "touching paths:" } # Functions for adding and removing shell-type quoting @@ -2210,6 +2295,7 @@ proc hfiles_change {} { proc gdttype_change {name ix op} { global gdttype highlight_files findstring findpattern + stopfinding if {$findstring ne {}} { if {$gdttype eq "containing:"} { if {$highlight_files ne {}} { @@ -2233,6 +2319,7 @@ proc gdttype_change {name ix op} { proc find_change {name ix op} { global gdttype findstring highlight_files + stopfinding if {$gdttype eq "containing:"} { findcom_change } else { @@ -2248,6 +2335,7 @@ proc findcom_change {} { global nhighlights mainfont boldnamerows global findpattern findtype findstring gdttype + stopfinding # delete previous highlights, if any foreach row $boldnamerows { bolden_name $row $mainfont @@ -4174,6 +4262,18 @@ proc dofind {{rev 0}} { } } +proc stopfinding {} { + global find_dirn findcurline fprogcoord + + if {[info exists find_dirn]} { + unset find_dirn + unset findcurline + notbusy finding + set fprogcoord 0 + adjustprogress + } +} + proc findnext {restart} { global findcurline find_dirn @@ -4207,8 +4307,11 @@ proc findprev {} { proc findmore {} { global commitdata commitinfo numcommits findpattern findloc global findstartline findcurline displayorder - global find_dirn gdttype fhighlights + global find_dirn gdttype fhighlights fprogcoord + if {![info exists find_dirn]} { + return 0 + } set fldtypes {Headline Author Date Committer CDate Comments} set l [expr {$findcurline + 1}] if {$l >= $numcommits} { @@ -4258,32 +4361,41 @@ proc findmore {} { } } } - if {$found} { + if {$found || ($domore && $l == $findstartline + 1)} { + unset findcurline unset find_dirn - findselectline $l notbusy finding + set fprogcoord 0 + adjustprogress + if {$found} { + findselectline $l + } else { + bell + } return 0 } if {!$domore} { flushhighlights - return 0 + } else { + set findcurline [expr {$l - 1}] } - if {$l == $findstartline + 1} { - bell - unset findcurline - unset find_dirn - notbusy finding - return 0 + set n [expr {$findcurline - ($findstartline + 1)}] + if {$n < 0} { + incr n $numcommits } - set findcurline [expr {$l - 1}] - return 1 + set fprogcoord [expr {$n * 1.0 / $numcommits}] + adjustprogress + return $domore } proc findmorerev {} { global commitdata commitinfo numcommits findpattern findloc global findstartline findcurline displayorder - global find_dirn gdttype fhighlights + global find_dirn gdttype fhighlights fprogcoord + if {![info exists find_dirn]} { + return 0 + } set fldtypes {Headline Author Date Committer CDate Comments} set l $findcurline if {$l == 0} { @@ -4333,25 +4445,31 @@ proc findmorerev {} { } } } - if {$found} { + if {$found || ($domore && $l == $findstartline - 1)} { + unset findcurline unset find_dirn - findselectline $l notbusy finding + set fprogcoord 0 + adjustprogress + if {$found} { + findselectline $l + } else { + bell + } return 0 } if {!$domore} { flushhighlights - return 0 + } else { + set findcurline [expr {$l + 1}] } - if {$l == -1} { - bell - unset findcurline - unset find_dirn - notbusy finding - return 0 + set n [expr {($findstartline - 1) - $findcurline}] + if {$n < 0} { + incr n $numcommits } - set findcurline [expr {$l + 1}] - return 1 + set fprogcoord [expr {$n * 1.0 / $numcommits}] + adjustprogress + return $domore } proc findselectline {l} { @@ -4398,12 +4516,11 @@ proc markmatches {canv l str tag matches font row} { } proc unmarkmatches {} { - global findids markingmatches findcurline + global markingmatches allcanvs delete matches - catch {unset findids} set markingmatches 0 - catch {unset findcurline} + stopfinding } proc selcanvline {w x y} { @@ -4626,6 +4743,7 @@ proc selectline {l isnew} { $canv delete hover normalline unsel_reflist + stopfinding if {$l < 0 || $l >= $numcommits} return set y [expr {$canvy0 + $l * $linespc}] set ymax [lindex [$canv cget -scrollregion] 3] @@ -5815,6 +5933,7 @@ proc rowmenu {x y id} { global rowctxmenu commitrow selectedline rowmenuid curview global nullid nullid2 fakerowmenu mainhead + stopfinding set rowmenuid $id if {![info exists selectedline] || $commitrow($curview,$id) eq $selectedline} { @@ -6293,6 +6412,7 @@ proc readresetstat {fd w} { proc headmenu {x y id head} { global headmenuid headmenuhead headctxmenu mainhead + stopfinding set headmenuid $id set headmenuhead $head set state normal From 586801650824f0f7b62c34a206b6a60ffbcd8be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:44 -0400 Subject: [PATCH 021/123] Introduce entry point add_interactive and add_files_to_cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This refactors builtin-add.c a little to provide a unique entry point for launching git add --interactive, which will be used by builtin-commit too. If we later want to make add --interactive a builtin or change how it is launched, we just start from this function. It also exports the private function update() which is used to add all modified paths to the index as add_files_to_cache(). Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- builtin-add.c | 23 ++++++++++++++--------- commit.h | 4 ++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/builtin-add.c b/builtin-add.c index f9a65803d8..966e145038 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -12,6 +12,7 @@ #include "diffcore.h" #include "commit.h" #include "revision.h" +#include "run-command.h" static const char builtin_add_usage[] = "git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] ..."; @@ -106,7 +107,7 @@ static void update_callback(struct diff_queue_struct *q, } } -static void update(int verbose, const char *prefix, const char **files) +void add_files_to_cache(int verbose, const char *prefix, const char **files) { struct rev_info rev; init_revisions(&rev, prefix); @@ -115,8 +116,6 @@ static void update(int verbose, const char *prefix, const char **files) rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; rev.diffopt.format_callback_data = &verbose; - if (read_cache() < 0) - die("index file corrupt"); run_diff_files(&rev, 0); } @@ -149,6 +148,13 @@ static int git_add_config(const char *var, const char *value) return git_default_config(var, value); } +int interactive_add(void) +{ + const char *argv[2] = { "add--interactive", NULL }; + + return run_command_v_opt(argv, RUN_GIT_CMD); +} + static struct lock_file lock_file; static const char ignore_error[] = @@ -168,12 +174,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) add_interactive++; } if (add_interactive) { - const char *args[] = { "add--interactive", NULL }; - - if (add_interactive != 1 || argc != 2) + if (argc != 2) die("add --interactive does not take any parameters"); - execv_git_cmd(args); - exit(1); + exit(interactive_add()); } git_config(git_add_config); @@ -213,7 +216,9 @@ int cmd_add(int argc, const char **argv, const char *prefix) } if (take_worktree_changes) { - update(verbose, prefix, argv + i); + if (read_cache() < 0) + die("index file corrupt"); + add_files_to_cache(verbose, prefix, argv + i); goto finish; } diff --git a/commit.h b/commit.h index b779de8cbc..b50c30e255 100644 --- a/commit.h +++ b/commit.h @@ -128,4 +128,8 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag); int in_merge_bases(struct commit *, struct commit **, int); + +extern int interactive_add(void); +extern void add_files_to_cache(int verbose, const char *prefix, const char **files); + #endif /* COMMIT_H */ From d8b7db0abfd0c74478964f5a9b08ad141f4f7f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Mon, 17 Sep 2007 20:06:47 -0400 Subject: [PATCH 022/123] Export rerere() and launch_editor(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- builtin-rerere.c | 31 ++++++++++++++++++++++++++----- commit.h | 1 + 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/builtin-rerere.c b/builtin-rerere.c index b8206744c1..74493237c9 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -389,18 +389,39 @@ static int is_rerere_enabled(void) return 1; } -int cmd_rerere(int argc, const char **argv, const char *prefix) +static int setup_rerere(struct path_list *merge_rr) { - struct path_list merge_rr = { NULL, 0, 0, 1 }; - int i, fd = -1; + int fd; git_config(git_rerere_config); if (!is_rerere_enabled()) - return 0; + return -1; merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR")); fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1); - read_rr(&merge_rr); + read_rr(merge_rr); + return fd; +} + +int rerere(void) +{ + struct path_list merge_rr = { NULL, 0, 0, 1 }; + int fd; + + fd = setup_rerere(&merge_rr); + if (fd < 0) + return 0; + return do_plain_rerere(&merge_rr, fd); +} + +int cmd_rerere(int argc, const char **argv, const char *prefix) +{ + struct path_list merge_rr = { NULL, 0, 0, 1 }; + int i, fd; + + fd = setup_rerere(&merge_rr); + if (fd < 0) + return 0; if (argc < 2) return do_plain_rerere(&merge_rr, fd); diff --git a/commit.h b/commit.h index b50c30e255..b661503972 100644 --- a/commit.h +++ b/commit.h @@ -131,5 +131,6 @@ int in_merge_bases(struct commit *, struct commit **, int); extern int interactive_add(void); extern void add_files_to_cache(int verbose, const char *prefix, const char **files); +extern int rerere(void); #endif /* COMMIT_H */ From 32f1b3e4a4baa3fe3e1acbb75f8134d822a09d58 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 28 Sep 2007 21:27:39 +1000 Subject: [PATCH 023/123] gitk: Fix the tab setting in the diff display window This fixes the bug where we were using the wrong font to calculate the width of the tab stops in the diff display window. If we're running on Tk 8.5 we also use the new -tabstyle wordprocessor option that makes tabs work as expected, i.e. a tab moves the cursor to the right until the next tab stop is reached. On Tk 8.5 we also get fancy and set the first tab stop at column 1 for a normal diff or column N for a merge diff with N parents. On Tk8.4 we can't do that because the tabs work in the "tabular" style, i.e. the nth tab character moves to the location of the nth tab position, *unless* you ask for the default tab setting, which gives 8-column tabs that work in the "wordprocessor" mode. So on Tk8.4 if the tab setting is 8 we ask for default tabs. This means that a tab setting of 7 or 9 can look quite different to 8 in some cases. Signed-off-by: Paul Mackerras --- gitk | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/gitk b/gitk index 4e168e98a0..01f5926916 100755 --- a/gitk +++ b/gitk @@ -626,6 +626,7 @@ proc makewindow {} { global bgcolor fgcolor bglist fglist diffcolors selectbgcolor global headctxmenu progresscanv progressitem progresscoords statusw global fprogitem fprogcoord lastprogupdate progupdatepending + global have_tk85 menu .bar .bar add cascade -label "File" -menu .bar.file @@ -845,9 +846,11 @@ proc makewindow {} { pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left set ctext .bleft.ctext text $ctext -background $bgcolor -foreground $fgcolor \ - -tabs "[expr {$tabstop * $charspc}]" \ -state disabled -font $textfont \ -yscrollcommand scrolltext -wrap none + if {$have_tk85} { + $ctext conf -tabstyle wordprocessor + } scrollbar .bleft.sb -command "$ctext yview" pack .bleft.top -side top -fill x pack .bleft.mid -side top -fill x @@ -1135,7 +1138,7 @@ proc doprogupdate {} { } proc savestuff {w} { - global canv canv2 canv3 ctext cflist mainfont textfont uifont tabstop + global canv canv2 canv3 mainfont textfont uifont tabstop global stuffsaved findmergefiles maxgraphpct global maxwidth showneartags showlocalchanges global viewname viewfiles viewargs viewperm nextviewnum @@ -5092,6 +5095,7 @@ proc showfile {f} { $ctext insert end "$f\n" filesep $ctext config -state disabled $ctext yview $commentend + settabs 0 } proc getblobline {bf id} { @@ -5133,6 +5137,7 @@ proc mergediff {id l} { fconfigure $mdf -blocking 0 set mdifffd($id) $mdf set np [llength [lindex $parentlist $l]] + settabs $np filerun $mdf [list getmergediffline $mdf $id $np] } @@ -5210,6 +5215,7 @@ proc getmergediffline {mdf id np} { proc startdiff {ids} { global treediffs diffids treepending diffmergeid nullid nullid2 + settabs 1 set diffids $ids catch {unset diffmergeid} if {![info exists treediffs($ids)] || @@ -5515,6 +5521,23 @@ proc clear_ctext {{first 1.0}} { } } +proc settabs {{firstab {}}} { + global firsttabstop tabstop textfont ctext have_tk85 + + if {$firstab ne {} && $have_tk85} { + set firsttabstop $firstab + } + set w [font measure $textfont "0"] + if {$firsttabstop != 0} { + $ctext conf -tabs [list [expr {$firsttabstop * $w}] \ + [expr {($firsttabstop + $tabstop) * $w}]] + } elseif {$have_tk85 || $tabstop != 8} { + $ctext conf -tabs [expr {$tabstop * $w}] + } else { + $ctext conf -tabs {} + } +} + proc incrsearch {name ix op} { global ctext searchstring searchdirn @@ -5666,13 +5689,12 @@ proc redisplay {} { proc incrfont {inc} { global mainfont textfont ctext canv phase cflist showrefstop - global charspc tabstop global stopped entries unmarkmatches set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]] set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]] setcoords - $ctext conf -font $textfont -tabs "[expr {$tabstop * $charspc}]" + settabs $cflist conf -font $textfont $ctext tag conf filesep -font [concat $textfont bold] foreach e $entries { @@ -5876,6 +5898,7 @@ proc lineclick {x y id isnew} { # fill the details pane with info about this line $ctext conf -state normal clear_ctext + settabs 0 $ctext insert end "Parent:\t" $ctext insert end $id link0 setlink $id link0 @@ -7780,6 +7803,7 @@ proc showtag {tag isnew} { } $ctext conf -state normal clear_ctext + settabs 0 set linknum 0 if {![info exists tagcontents($tag)]} { catch { @@ -7951,11 +7975,10 @@ proc prefscan {} { proc prefsok {} { global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges - global charspc ctext tabstop catch {destroy $prefstop} unset prefstop - $ctext configure -tabs "[expr {$tabstop * $charspc}]" + settabs if {$showlocalchanges != $oldprefs(showlocalchanges)} { if {$showlocalchanges} { doshowlocalchanges @@ -8360,6 +8383,7 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} { set nullid "0000000000000000000000000000000000000000" set nullid2 "0000000000000000000000000000000000000001" +set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] set runq {} set history {} @@ -8376,6 +8400,7 @@ set markingmatches 0 set linkentercount 0 set need_redisplay 0 set nrows_drawn 0 +set firsttabstop 0 set nextviewnum 1 set curview 0 From 64b5f146fd2252646d23eac925c49ce9cb526de9 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 4 Oct 2007 22:19:24 +1000 Subject: [PATCH 024/123] gitk: Fix bug causing Tcl error when changing find match type When changing the selector for Exact/IgnCase/Regexp, we were getting a Tcl error. This fixes it. It also adds a workaround for a bug in alpha versions of Tk8.5 where wordprocessor-style tabs don't seem to work properly around column 1. Signed-off-by: Paul Mackerras --- gitk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index 01f5926916..35920abc7b 100755 --- a/gitk +++ b/gitk @@ -2334,7 +2334,7 @@ proc find_change {name ix op} { drawvisible } -proc findcom_change {} { +proc findcom_change args { global nhighlights mainfont boldnamerows global findpattern findtype findstring gdttype @@ -5529,8 +5529,8 @@ proc settabs {{firstab {}}} { } set w [font measure $textfont "0"] if {$firsttabstop != 0} { - $ctext conf -tabs [list [expr {$firsttabstop * $w}] \ - [expr {($firsttabstop + $tabstop) * $w}]] + $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \ + [expr {($firsttabstop + 2 * $tabstop) * $w}]] } elseif {$have_tk85 || $tabstop != 8} { $ctext conf -tabs [expr {$tabstop * $w}] } else { From 9c311b3208f25ce70edf0fdbe0f440ecd8e0bda7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 4 Oct 2007 22:27:13 +1000 Subject: [PATCH 025/123] gitk: Use named fonts instead of the font specification This replaces the use of $mainfont, $textfont and $uifont with named fonts called mainfont, textfont and uifont. We also have variants called mainfontbold and textfontbold. This makes it much easier to make sure font size changes are reflected everywhere they should be, since configuring a named font automatically changes all the widgets that are using that font. Signed-off-by: Paul Mackerras --- gitk | 257 +++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 142 insertions(+), 115 deletions(-) diff --git a/gitk b/gitk index 35920abc7b..c257bb57ac 100755 --- a/gitk +++ b/gitk @@ -133,7 +133,7 @@ proc stop_rev_list {} { } proc getcommits {} { - global phase canv mainfont curview + global phase canv curview set phase getcommits initlayout @@ -615,7 +615,7 @@ proc confirm_popup msg { proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist - global textfont mainfont uifont tabstop + global tabstop global findtype findtypemenu findloc findstring fstring geometry global entries sha1entry sha1string sha1but global diffcontextstring diffcontext @@ -630,19 +630,19 @@ proc makewindow {} { menu .bar .bar add cascade -label "File" -menu .bar.file - .bar configure -font $uifont + .bar configure -font uifont menu .bar.file .bar.file add command -label "Update" -command updatecommits .bar.file add command -label "Reread references" -command rereadrefs .bar.file add command -label "List references" -command showrefs .bar.file add command -label "Quit" -command doquit - .bar.file configure -font $uifont + .bar.file configure -font uifont menu .bar.edit .bar add cascade -label "Edit" -menu .bar.edit .bar.edit add command -label "Preferences" -command doprefs - .bar.edit configure -font $uifont + .bar.edit configure -font uifont - menu .bar.view -font $uifont + menu .bar.view -font uifont .bar add cascade -label "View" -menu .bar.view .bar.view add command -label "New view..." -command {newview 0} .bar.view add command -label "Edit view..." -command editview \ @@ -656,7 +656,7 @@ proc makewindow {} { .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About gitk" -command about .bar.help add command -label "Key bindings" -command keys - .bar.help configure -font $uifont + .bar.help configure -font uifont . configure -menu .bar # the gui has upper and lower half, parts of a paned window. @@ -713,10 +713,10 @@ proc makewindow {} { set entries $sha1entry set sha1but .tf.bar.sha1label button $sha1but -text "SHA1 ID: " -state disabled -relief flat \ - -command gotocommit -width 8 -font $uifont + -command gotocommit -width 8 -font uifont $sha1but conf -disabledforeground [$sha1but cget -foreground] pack .tf.bar.sha1label -side left - entry $sha1entry -width 40 -font $textfont -textvariable sha1string + entry $sha1entry -width 40 -font textfont -textvariable sha1string trace add variable sha1string write sha1change pack $sha1entry -side left -pady 2 @@ -745,9 +745,9 @@ proc makewindow {} { # Status label and progress bar set statusw .tf.bar.status - label $statusw -width 15 -relief sunken -font $uifont + label $statusw -width 15 -relief sunken -font uifont pack $statusw -side left -padx 5 - set h [expr {[font metrics $uifont -linespace] + 2}] + set h [expr {[font metrics uifont -linespace] + 2}] set progresscanv .tf.bar.progress canvas $progresscanv -relief sunken -height $h -borderwidth 2 set progressitem [$progresscanv create rect -1 0 0 $h -fill green] @@ -760,10 +760,10 @@ proc makewindow {} { set progupdatepending 0 # build up the bottom bar of upper window - label .tf.lbar.flabel -text "Find " -font $uifont - button .tf.lbar.fnext -text "next" -command dofind -font $uifont - button .tf.lbar.fprev -text "prev" -command {dofind 1} -font $uifont - label .tf.lbar.flab2 -text " commit " -font $uifont + label .tf.lbar.flabel -text "Find " -font uifont + button .tf.lbar.fnext -text "next" -command dofind -font uifont + button .tf.lbar.fprev -text "prev" -command {dofind 1} -font uifont + label .tf.lbar.flab2 -text " commit " -font uifont pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \ -side left -fill y set gdttype "containing:" @@ -772,27 +772,27 @@ proc makewindow {} { "touching paths:" \ "adding/removing string:"] trace add variable gdttype write gdttype_change - $gm conf -font $uifont - .tf.lbar.gdttype conf -font $uifont + $gm conf -font uifont + .tf.lbar.gdttype conf -font uifont pack .tf.lbar.gdttype -side left -fill y set findstring {} set fstring .tf.lbar.findstring lappend entries $fstring - entry $fstring -width 30 -font $textfont -textvariable findstring + entry $fstring -width 30 -font textfont -textvariable findstring trace add variable findstring write find_change set findtype Exact set findtypemenu [tk_optionMenu .tf.lbar.findtype \ findtype Exact IgnCase Regexp] trace add variable findtype write findcom_change - .tf.lbar.findtype configure -font $uifont - .tf.lbar.findtype.menu configure -font $uifont + .tf.lbar.findtype configure -font uifont + .tf.lbar.findtype.menu configure -font uifont set findloc "All fields" tk_optionMenu .tf.lbar.findloc findloc "All fields" Headline \ Comments Author Committer trace add variable findloc write find_change - .tf.lbar.findloc configure -font $uifont - .tf.lbar.findloc.menu configure -font $uifont + .tf.lbar.findloc configure -font uifont + .tf.lbar.findloc.menu configure -font uifont pack .tf.lbar.findloc -side right pack .tf.lbar.findtype -side right pack $fstring -side left -expand 1 -fill x @@ -820,10 +820,10 @@ proc makewindow {} { frame .bleft.mid button .bleft.top.search -text "Search" -command dosearch \ - -font $uifont + -font uifont pack .bleft.top.search -side left -padx 5 set sstring .bleft.top.sstring - entry $sstring -width 20 -font $textfont -textvariable searchstring + entry $sstring -width 20 -font textfont -textvariable searchstring lappend entries $sstring trace add variable searchstring write incrsearch pack $sstring -side left -expand 1 -fill x @@ -834,9 +834,9 @@ proc makewindow {} { radiobutton .bleft.mid.new -text "New version" \ -command changediffdisp -variable diffelide -value {1 0} label .bleft.mid.labeldiffcontext -text " Lines of context: " \ - -font $uifont + -font uifont pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left - spinbox .bleft.mid.diffcontext -width 5 -font $textfont \ + spinbox .bleft.mid.diffcontext -width 5 -font textfont \ -from 1 -increment 1 -to 10000000 \ -validate all -validatecommand "diffcontextvalidate %P" \ -textvariable diffcontextstring @@ -846,7 +846,7 @@ proc makewindow {} { pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left set ctext .bleft.ctext text $ctext -background $bgcolor -foreground $fgcolor \ - -state disabled -font $textfont \ + -state disabled -font textfont \ -yscrollcommand scrolltext -wrap none if {$have_tk85} { $ctext conf -tabstyle wordprocessor @@ -860,7 +860,7 @@ proc makewindow {} { lappend fglist $ctext $ctext tag conf comment -wrap $wrapcomment - $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa" + $ctext tag conf filesep -font textfontbold -back "#aaaaaa" $ctext tag conf hunksep -fore [lindex $diffcolors 2] $ctext tag conf d0 -fore [lindex $diffcolors 0] $ctext tag conf d1 -fore [lindex $diffcolors 1] @@ -882,8 +882,8 @@ proc makewindow {} { $ctext tag conf m15 -fore "#ff70b0" $ctext tag conf mmax -fore darkgrey set mergemax 16 - $ctext tag conf mresult -font [concat $textfont bold] - $ctext tag conf msep -font [concat $textfont bold] + $ctext tag conf mresult -font textfontbold + $ctext tag conf msep -font textfontbold $ctext tag conf found -back yellow .pwbottom add .bleft @@ -894,18 +894,18 @@ proc makewindow {} { frame .bright.mode radiobutton .bright.mode.patch -text "Patch" \ -command reselectline -variable cmitmode -value "patch" - .bright.mode.patch configure -font $uifont + .bright.mode.patch configure -font uifont radiobutton .bright.mode.tree -text "Tree" \ -command reselectline -variable cmitmode -value "tree" - .bright.mode.tree configure -font $uifont + .bright.mode.tree configure -font uifont grid .bright.mode.patch .bright.mode.tree -sticky ew pack .bright.mode -side top -fill x set cflist .bright.cfiles - set indent [font measure $mainfont "nn"] + set indent [font measure mainfont "nn"] text $cflist \ -selectbackground $selectbgcolor \ -background $bgcolor -foreground $fgcolor \ - -font $mainfont \ + -font mainfont \ -tabs [list $indent [expr {2 * $indent}]] \ -yscrollcommand ".bright.sb set" \ -cursor [. cget -cursor] \ @@ -917,7 +917,7 @@ proc makewindow {} { pack $cflist -side left -fill both -expand 1 $cflist tag configure highlight \ -background [$cflist cget -selectbackground] - $cflist tag configure bold -font [concat $mainfont bold] + $cflist tag configure bold -font mainfontbold .pwbottom add .bright .ctop add .pwbottom @@ -1272,10 +1272,10 @@ Copyright Use and redistribute under the terms of the GNU General Public License} \ -justify center -aspect 400 -border 2 -bg white -relief groove pack $w.m -side top -fill x -padx 2 -pady 2 - $w.m configure -font $uifont + $w.m configure -font uifont button $w.ok -text Close -command "destroy $w" -default active pack $w.ok -side bottom - $w.ok configure -font $uifont + $w.ok configure -font uifont bind $w "focus $w.ok" bind $w "destroy $w" bind $w "destroy $w" @@ -1336,10 +1336,10 @@ f Scroll diff view to next file " \ -justify left -bg white -border 2 -relief groove pack $w.m -side top -fill both -padx 2 -pady 2 - $w.m configure -font $uifont + $w.m configure -font uifont button $w.ok -text Close -command "destroy $w" -default active pack $w.ok -side bottom - $w.ok configure -font $uifont + $w.ok configure -font uifont bind $w "focus $w.ok" bind $w "destroy $w" bind $w "destroy $w" @@ -1871,22 +1871,22 @@ proc vieweditor {top n title} { toplevel $top wm title $top $title - label $top.nl -text "Name" -font $uifont - entry $top.name -width 20 -textvariable newviewname($n) -font $uifont + label $top.nl -text "Name" -font uifont + entry $top.name -width 20 -textvariable newviewname($n) -font uifont grid $top.nl $top.name -sticky w -pady 5 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \ - -font $uifont + -font uifont grid $top.perm - -pady 5 -sticky w - message $top.al -aspect 1000 -font $uifont \ + message $top.al -aspect 1000 -font uifont \ -text "Commits to include (arguments to git rev-list):" grid $top.al - -sticky w -pady 5 entry $top.args -width 50 -textvariable newviewargs($n) \ - -background white -font $uifont + -background white -font uifont grid $top.args - -sticky ew -padx 5 - message $top.l -aspect 1000 -font $uifont \ + message $top.l -aspect 1000 -font uifont \ -text "Enter files and directories to include, one per line:" grid $top.l - -sticky w - text $top.t -width 40 -height 10 -background white -font $uifont + text $top.t -width 40 -height 10 -background white -font uifont if {[info exists viewfiles($n)]} { foreach f $viewfiles($n) { $top.t insert end $f @@ -1898,9 +1898,9 @@ proc vieweditor {top n title} { grid $top.t - -sticky ew -padx 5 frame $top.buts button $top.buts.ok -text "OK" -command [list newviewok $top $n] \ - -font $uifont + -font uifont button $top.buts.can -text "Cancel" -command [list destroy $top] \ - -font $uifont + -font uifont grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -2191,12 +2191,12 @@ proc bolden_name {row font} { } proc unbolden {} { - global mainfont boldrows + global boldrows set stillbold {} foreach row $boldrows { if {![ishighlighted $row]} { - bolden $row $mainfont + bolden $row mainfont } else { lappend stillbold $row } @@ -2235,9 +2235,8 @@ proc delvhighlight {} { proc vhighlightmore {} { global hlview vhl_done commitidx vhighlights - global displayorder vdisporder curview mainfont + global displayorder vdisporder curview - set font [concat $mainfont bold] set max $commitidx($hlview) if {$hlview == $curview} { set disp $displayorder @@ -2253,7 +2252,7 @@ proc vhighlightmore {} { set row $commitrow($curview,$id) if {$r0 <= $row && $row <= $r1} { if {![highlighted $row]} { - bolden $row $font + bolden $row mainfontbold } set vhighlights($row) 1 } @@ -2263,11 +2262,11 @@ proc vhighlightmore {} { } proc askvhighlight {row id} { - global hlview vhighlights commitrow iddrawn mainfont + global hlview vhighlights commitrow iddrawn if {[info exists commitrow($hlview,$id)]} { if {[info exists iddrawn($id)] && ![ishighlighted $row]} { - bolden $row [concat $mainfont bold] + bolden $row mainfontbold } set vhighlights($row) 1 } else { @@ -2277,7 +2276,7 @@ proc askvhighlight {row id} { proc hfiles_change {} { global highlight_files filehighlight fhighlights fh_serial - global mainfont highlight_paths gdttype + global highlight_paths gdttype if {[info exists filehighlight]} { # delete previous highlights @@ -2335,13 +2334,13 @@ proc find_change {name ix op} { } proc findcom_change args { - global nhighlights mainfont boldnamerows + global nhighlights boldnamerows global findpattern findtype findstring gdttype stopfinding # delete previous highlights, if any foreach row $boldnamerows { - bolden_name $row $mainfont + bolden_name $row mainfont } set boldnamerows {} catch {unset nhighlights} @@ -2414,7 +2413,7 @@ proc askfilehighlight {row id} { } proc readfhighlight {} { - global filehighlight fhighlights commitrow curview mainfont iddrawn + global filehighlight fhighlights commitrow curview iddrawn global fhl_list find_dirn if {![info exists filehighlight]} { @@ -2436,7 +2435,7 @@ proc readfhighlight {} { if {![info exists commitrow($curview,$line)]} continue set row $commitrow($curview,$line) if {[info exists iddrawn($line)] && ![ishighlighted $row]} { - bolden $row [concat $mainfont bold] + bolden $row mainfontbold } set fhighlights($row) 1 } @@ -2470,7 +2469,7 @@ proc doesmatch {f} { } proc askfindhighlight {row id} { - global nhighlights commitinfo iddrawn mainfont + global nhighlights commitinfo iddrawn global findloc global markingmatches @@ -2491,11 +2490,10 @@ proc askfindhighlight {row id} { } } if {$isbold && [info exists iddrawn($id)]} { - set f [concat $mainfont bold] if {![ishighlighted $row]} { - bolden $row $f + bolden $row mainfontbold if {$isbold > 1} { - bolden_name $row $f + bolden_name $row mainfontbold } } if {$markingmatches} { @@ -2624,7 +2622,7 @@ proc is_ancestor {a} { } proc askrelhighlight {row id} { - global descendent highlight_related iddrawn mainfont rhighlights + global descendent highlight_related iddrawn rhighlights global selectedline ancestor if {![info exists selectedline]} return @@ -2648,7 +2646,7 @@ proc askrelhighlight {row id} { } if {[info exists iddrawn($id)]} { if {$isbold && ![ishighlighted $row]} { - bolden $row [concat $mainfont bold] + bolden $row mainfontbold } } set rhighlights($row) $isbold @@ -3624,7 +3622,7 @@ proc drawcmittext {id row col} { global commitlisted commitinfo rowidlist parentlist global rowtextx idpos idtags idheads idotherrefs global linehtag linentag linedtag selectedline - global mainfont canvxmax boldrows boldnamerows fgcolor nullid nullid2 + global canvxmax boldrows boldnamerows fgcolor nullid nullid2 # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right set listed [lindex $commitlisted $row] @@ -3681,15 +3679,15 @@ proc drawcmittext {id row col} { set name [lindex $commitinfo($id) 1] set date [lindex $commitinfo($id) 2] set date [formatdate $date] - set font $mainfont - set nfont $mainfont + set font mainfont + set nfont mainfont set isbold [ishighlighted $row] if {$isbold > 0} { lappend boldrows $row - lappend font bold + set font mainfontbold if {$isbold > 1} { lappend boldnamerows $row - lappend nfont bold + set nfont mainfontbold } } set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \ @@ -3698,11 +3696,11 @@ proc drawcmittext {id row col} { set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \ -text $name -font $nfont -tags text] set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \ - -text $date -font $mainfont -tags text] + -text $date -font mainfont -tags text] if {[info exists selectedline] && $selectedline == $row} { make_secsel $row } - set xr [expr {$xt + [font measure $mainfont $headline]}] + set xr [expr {$xt + [font measure $font $headline]}] if {$xr > $canvxmax} { set canvxmax $xr setcanvscroll @@ -3985,7 +3983,7 @@ proc bindline {t id} { proc drawtags {id x xt y1} { global idtags idheads idotherrefs mainhead global linespc lthickness - global canv mainfont commitrow rowtextx curview fgcolor bgcolor + global canv commitrow rowtextx curview fgcolor bgcolor set marks {} set ntags 0 @@ -4014,9 +4012,9 @@ proc drawtags {id x xt y1} { foreach tag $marks { incr i if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} { - set wid [font measure [concat $mainfont bold] $tag] + set wid [font measure mainfontbold $tag] } else { - set wid [font measure $mainfont $tag] + set wid [font measure mainfont $tag] } lappend xvals $xt lappend wvals $wid @@ -4028,7 +4026,7 @@ proc drawtags {id x xt y1} { foreach tag $marks x $xvals wid $wvals { set xl [expr {$x + $delta}] set xr [expr {$x + $delta + $wid + $lthickness}] - set font $mainfont + set font mainfont if {[incr ntags -1] >= 0} { # draw a tag set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \ @@ -4041,7 +4039,7 @@ proc drawtags {id x xt y1} { if {[incr nheads -1] >= 0} { set col green if {$tag eq $mainhead} { - lappend font bold + set font mainfontbold } } else { set col "#ddddff" @@ -4050,7 +4048,7 @@ proc drawtags {id x xt y1} { $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ -width 1 -outline black -fill $col -tags tag.$id if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} { - set rwid [font measure $mainfont $remoteprefix] + set rwid [font measure mainfont $remoteprefix] set xi [expr {$x + 1}] set yti [expr {$yt + 1}] set xri [expr {$x + $rwid}] @@ -4082,10 +4080,10 @@ proc xcoord {i level ln} { } proc show_status {msg} { - global canv mainfont fgcolor + global canv fgcolor clear_display - $canv create text 3 3 -anchor nw -text $msg -font $mainfont \ + $canv create text 3 3 -anchor nw -text $msg -font mainfont \ -tags text -fill $fgcolor } @@ -5522,12 +5520,12 @@ proc clear_ctext {{first 1.0}} { } proc settabs {{firstab {}}} { - global firsttabstop tabstop textfont ctext have_tk85 + global firsttabstop tabstop ctext have_tk85 if {$firstab ne {} && $have_tk85} { set firsttabstop $firstab } - set w [font measure $textfont "0"] + set w [font measure textfont "0"] if {$firsttabstop != 0} { $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \ [expr {($firsttabstop + 2 * $tabstop) * $w}]] @@ -5658,11 +5656,11 @@ proc scrolltext {f0 f1} { } proc setcoords {} { - global linespc charspc canvx0 canvy0 mainfont + global linespc charspc canvx0 canvy0 global xspc1 xspc2 lthickness - set linespc [font metrics $mainfont -linespace] - set charspc [font measure $mainfont "m"] + set linespc [font metrics mainfont -linespace] + set charspc [font measure mainfont "m"] set canvy0 [expr {int(3 + 0.5 * $linespc)}] set canvx0 [expr {int(3 + 0.5 * $linespc)}] set lthickness [expr {int($linespc / 9) + 1}] @@ -5687,25 +5685,45 @@ proc redisplay {} { } } +proc fontdescr {f} { + set d [list [font actual $f -family] [font actual $f -size]] + if {[font actual $f -weight] eq "bold"} { + lappend d "bold" + } + if {[font actual $f -slant] eq "italic"} { + lappend d "italic" + } + if {[font actual $f -underline]} { + lappend d "underline" + } + if {[font actual $f -overstrike]} { + lappend d "overstrike" + } + return $d +} + proc incrfont {inc} { global mainfont textfont ctext canv phase cflist showrefstop global stopped entries unmarkmatches - set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]] - set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]] + set s [font actual mainfont -size] + incr s $inc + if {$s < 1} { + set s 1 + } + font config mainfont -size $s + font config mainfontbold -size $s + set mainfont [fontdescr mainfont] + set s [font actual textfont -size] + incr s $inc + if {$s < 1} { + set s 1 + } + font config textfont -size $s + font config textfontbold -size $s + set textfont [fontdescr textfont] setcoords settabs - $cflist conf -font $textfont - $ctext tag conf filesep -font [concat $textfont bold] - foreach e $entries { - $e conf -font $mainfont - } - if {$phase eq "getcommits"} { - $canv itemconf textitems -font $mainfont - } - if {[info exists showrefstop] && [winfo exists $showrefstop]} { - $showrefstop.list conf -font $mainfont - } redisplay } @@ -5816,7 +5834,7 @@ proc lineleave {id} { proc linehover {} { global hoverx hovery hoverid hovertimer global canv linespc lthickness - global commitinfo mainfont + global commitinfo set text [lindex $commitinfo($hoverid) 0] set ymax [lindex [$canv cget -scrollregion] 3] @@ -5826,13 +5844,13 @@ proc linehover {} { set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}] set x0 [expr {$x - 2 * $lthickness}] set y0 [expr {$y - 2 * $lthickness}] - set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}] + set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}] set y1 [expr {$y + $linespc + 2 * $lthickness}] set t [$canv create rectangle $x0 $y0 $x1 $y1 \ -fill \#ffff80 -outline black -width 1 -tags hover] $canv raise $t set t [$canv create text $x $y -anchor nw -text $text -tags hover \ - -font $mainfont] + -font mainfont] $canv raise $t } @@ -6168,7 +6186,7 @@ proc domktag {} { proc redrawtags {id} { global canv linehtag commitrow idpos selectedline curview - global mainfont canvxmax iddrawn + global canvxmax iddrawn if {![info exists commitrow($curview,$id)]} return if {![info exists iddrawn($id)]} return @@ -6177,7 +6195,7 @@ proc redrawtags {id} { set xt [eval drawtags $id $idpos($id)] $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2] set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text] - set xr [expr {$xt + [font measure $mainfont $text]}] + set xr [expr {$xt + [font measure mainfont $text]}] if {$xr > $canvxmax} { set canvxmax $xr setcanvscroll @@ -6509,8 +6527,8 @@ proc rmbranch {} { # Display a list of tags and heads proc showrefs {} { - global showrefstop bgcolor fgcolor selectbgcolor mainfont - global bglist fglist uifont reflistfilter reflist maincursor + global showrefstop bgcolor fgcolor selectbgcolor + global bglist fglist reflistfilter reflist maincursor set top .showrefs set showrefstop $top @@ -6522,7 +6540,7 @@ proc showrefs {} { toplevel $top wm title $top "Tags and heads: [file tail [pwd]]" text $top.list -background $bgcolor -foreground $fgcolor \ - -selectbackground $selectbgcolor -font $mainfont \ + -selectbackground $selectbgcolor -font mainfont \ -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \ -width 30 -height 20 -cursor $maincursor \ -spacing1 1 -spacing3 1 -state disabled @@ -6534,15 +6552,15 @@ proc showrefs {} { grid $top.list $top.ysb -sticky nsew grid $top.xsb x -sticky ew frame $top.f - label $top.f.l -text "Filter: " -font $uifont - entry $top.f.e -width 20 -textvariable reflistfilter -font $uifont + label $top.f.l -text "Filter: " -font uifont + entry $top.f.e -width 20 -textvariable reflistfilter -font uifont set reflistfilter "*" trace add variable reflistfilter write reflistfilter_change pack $top.f.e -side right -fill x -expand 1 pack $top.f.l -side left grid $top.f - -sticky ew -pady 2 button $top.close -command [list destroy $top] -text "Close" \ - -font $uifont + -font uifont grid $top.close - grid columnconfigure $top 0 -weight 1 grid rowconfigure $top 0 -weight 1 @@ -7845,7 +7863,7 @@ proc doprefs {} { toplevel $top wm title $top "Gitk preferences" label $top.ldisp -text "Commit list display options" - $top.ldisp configure -font $uifont + $top.ldisp configure -font uifont grid $top.ldisp - -sticky w -pady 10 label $top.spacer -text " " label $top.maxwidthl -text "Maximum graph width (lines)" \ @@ -7863,7 +7881,7 @@ proc doprefs {} { grid x $top.showlocal -sticky w label $top.ddisp -text "Diff display options" - $top.ddisp configure -font $uifont + $top.ddisp configure -font uifont grid $top.ddisp - -sticky w -pady 10 label $top.diffoptl -text "Options for diff program" \ -font optionfont @@ -7879,7 +7897,7 @@ proc doprefs {} { grid x $top.tabstopl $top.tabstop -sticky w label $top.cdisp -text "Colors: press to choose" - $top.cdisp configure -font $uifont + $top.cdisp configure -font uifont grid $top.cdisp - -sticky w -pady 10 label $top.bg -padx 40 -relief sunk -background $bgcolor button $top.bgbut -text "Background" -font optionfont \ @@ -7912,9 +7930,9 @@ proc doprefs {} { frame $top.buts button $top.buts.ok -text "OK" -command prefsok -default active - $top.buts.ok configure -font $uifont + $top.buts.ok configure -font uifont button $top.buts.can -text "Cancel" -command prefscan -default normal - $top.buts.can configure -font $uifont + $top.buts.can configure -font uifont grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -8322,6 +8340,15 @@ set selectbgcolor gray85 catch {source ~/.gitk} font create optionfont -family sans-serif -size -12 +font create mainfont +catch {eval font config mainfont [font actual $mainfont]} +eval font create mainfontbold [font actual mainfont] -weight bold +font create textfont +catch {eval font config textfont [font actual $textfont]} +eval font create textfontbold [font actual textfont] +font config textfontbold -weight bold +font create uifont +catch {eval font config uifont [font actual $uifont]} # check that we can find a .git directory somewhere... if {[catch {set gitdir [gitdir]}]} { From 0ed1dd3c77e606156f0f5d1baa59a47f33711787 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 6 Oct 2007 18:27:37 +1000 Subject: [PATCH 026/123] gitk: Keep track of font attributes ourselves instead of using font actual Unfortunately there seems to be a bug in Tk8.5 where font actual -size sometimes gives the wrong answer (e.g. 12 for Bitstream Vera Sans 9), even though the font is actually displayed at the right size. This works around it by parsing and storing the family, size, weight and slant of the mainfont, textfont and uifont explicitly. Signed-off-by: Paul Mackerras --- gitk | 82 ++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/gitk b/gitk index c257bb57ac..69b31f037e 100755 --- a/gitk +++ b/gitk @@ -5685,43 +5685,73 @@ proc redisplay {} { } } -proc fontdescr {f} { - set d [list [font actual $f -family] [font actual $f -size]] - if {[font actual $f -weight] eq "bold"} { - lappend d "bold" +proc parsefont {f n} { + global fontattr + + set fontattr($f,family) [lindex $n 0] + set s [lindex $n 1] + if {$s eq {} || $s == 0} { + set s 10 + } elseif {$s < 0} { + set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}] } - if {[font actual $f -slant] eq "italic"} { - lappend d "italic" + set fontattr($f,size) $s + set fontattr($f,weight) normal + set fontattr($f,slant) roman + foreach style [lrange $n 2 end] { + switch -- $style { + "normal" - + "bold" {set fontattr($f,weight) $style} + "roman" - + "italic" {set fontattr($f,slant) $style} + } } - if {[font actual $f -underline]} { - lappend d "underline" +} + +proc fontflags {f {isbold 0}} { + global fontattr + + return [list -family $fontattr($f,family) -size $fontattr($f,size) \ + -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \ + -slant $fontattr($f,slant)] +} + +proc fontname {f} { + global fontattr + + set n [list $fontattr($f,family) $fontattr($f,size)] + if {$fontattr($f,weight) eq "bold"} { + lappend n "bold" } - if {[font actual $f -overstrike]} { - lappend d "overstrike" + if {$fontattr($f,slant) eq "italic"} { + lappend n "italic" } - return $d + return $n } proc incrfont {inc} { global mainfont textfont ctext canv phase cflist showrefstop - global stopped entries + global stopped entries fontattr + unmarkmatches - set s [font actual mainfont -size] + set s $fontattr(mainfont,size) incr s $inc if {$s < 1} { set s 1 } + set fontattr(mainfont,size) $s font config mainfont -size $s font config mainfontbold -size $s - set mainfont [fontdescr mainfont] - set s [font actual textfont -size] + set mainfont [fontname mainfont] + set s $fontattr(textfont,size) incr s $inc if {$s < 1} { set s 1 } + set fontattr(textfont,size) $s font config textfont -size $s font config textfontbold -size $s - set textfont [fontdescr textfont] + set textfont [fontname textfont] setcoords settabs redisplay @@ -8340,15 +8370,17 @@ set selectbgcolor gray85 catch {source ~/.gitk} font create optionfont -family sans-serif -size -12 -font create mainfont -catch {eval font config mainfont [font actual $mainfont]} -eval font create mainfontbold [font actual mainfont] -weight bold -font create textfont -catch {eval font config textfont [font actual $textfont]} -eval font create textfontbold [font actual textfont] -font config textfontbold -weight bold -font create uifont -catch {eval font config uifont [font actual $uifont]} + +parsefont mainfont $mainfont +eval font create mainfont [fontflags mainfont] +eval font create mainfontbold [fontflags mainfont 1] + +parsefont textfont $textfont +eval font create textfont [fontflags textfont] +eval font create textfontbold [fontflags textfont 1] + +parsefont uifont $uifont +eval font create uifont [fontflags uifont] # check that we can find a .git directory somewhere... if {[catch {set gitdir [gitdir]}]} { From 9a7558f348772ab3c2fb3d4beda3a3a7af1e843a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 6 Oct 2007 20:16:06 +1000 Subject: [PATCH 027/123] gitk: Add a font chooser This adds buttons to the edit preferences window to allow the user to choose the main font, the text font (used for the diff display window) and the UI font. Pressing those buttons pops up a font chooser window that lets the user pick the font family, size, weight (bold/normal) and slant (roman/italic). Signed-off-by: Paul Mackerras --- gitk | 156 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index 69b31f037e..6f0af37342 100755 --- a/gitk +++ b/gitk @@ -7875,6 +7875,130 @@ proc doquit {} { destroy . } +proc mkfontdisp {font top which} { + global fontattr fontpref $font + + set fontpref($font) [set $font] + button $top.${font}but -text $which -font optionfont \ + -command [list choosefont $font $which] + label $top.$font -relief flat -font $font \ + -text $fontattr($font,family) -justify left + grid x $top.${font}but $top.$font -sticky w +} + +proc choosefont {font which} { + global fontparam fontlist fonttop fontattr + + set fontparam(which) $which + set fontparam(font) $font + set fontparam(family) [font actual $font -family] + set fontparam(size) $fontattr($font,size) + set fontparam(weight) $fontattr($font,weight) + set fontparam(slant) $fontattr($font,slant) + set top .gitkfont + set fonttop $top + if {![winfo exists $top]} { + font create sample + eval font config sample [font actual $font] + toplevel $top + wm title $top "Gitk font chooser" + label $top.l -textvariable fontparam(which) -font uifont + pack $top.l -side top + set fontlist [lsort [font families]] + frame $top.f + listbox $top.f.fam -listvariable fontlist \ + -yscrollcommand [list $top.f.sb set] + bind $top.f.fam <> selfontfam + scrollbar $top.f.sb -command [list $top.f.fam yview] + pack $top.f.sb -side right -fill y + pack $top.f.fam -side left -fill both -expand 1 + pack $top.f -side top -fill both -expand 1 + frame $top.g + spinbox $top.g.size -from 4 -to 40 -width 4 \ + -textvariable fontparam(size) \ + -validatecommand {string is integer -strict %s} + checkbutton $top.g.bold -padx 5 \ + -font {{Times New Roman} 12 bold} -text "B" -indicatoron 0 \ + -variable fontparam(weight) -onvalue bold -offvalue normal + checkbutton $top.g.ital -padx 5 \ + -font {{Times New Roman} 12 italic} -text "I" -indicatoron 0 \ + -variable fontparam(slant) -onvalue italic -offvalue roman + pack $top.g.size $top.g.bold $top.g.ital -side left + pack $top.g -side top + canvas $top.c -width 150 -height 50 -border 2 -relief sunk \ + -background white + $top.c create text 100 25 -anchor center -text $which -font sample \ + -fill black -tags text + bind $top.c [list centertext $top.c] + pack $top.c -side top -fill x + frame $top.buts + button $top.buts.ok -text "OK" -command fontok -default active \ + -font uifont + button $top.buts.can -text "Cancel" -command fontcan -default normal \ + -font uifont + grid $top.buts.ok $top.buts.can + grid columnconfigure $top.buts 0 -weight 1 -uniform a + grid columnconfigure $top.buts 1 -weight 1 -uniform a + pack $top.buts -side bottom -fill x + trace add variable fontparam write chg_fontparam + } else { + raise $top + $top.c itemconf text -text $which + } + set i [lsearch -exact $fontlist $fontparam(family)] + if {$i >= 0} { + $top.f.fam selection set $i + $top.f.fam see $i + } +} + +proc centertext {w} { + $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}] +} + +proc fontok {} { + global fontparam fontpref prefstop + + set f $fontparam(font) + set fontpref($f) [list $fontparam(family) $fontparam(size)] + if {$fontparam(weight) eq "bold"} { + lappend fontpref($f) "bold" + } + if {$fontparam(slant) eq "italic"} { + lappend fontpref($f) "italic" + } + set w $prefstop.$f + $w conf -text $fontparam(family) -font $fontpref($f) + + fontcan +} + +proc fontcan {} { + global fonttop fontparam + + if {[info exists fonttop]} { + catch {destroy $fonttop} + catch {font delete sample} + unset fonttop + unset fontparam + } +} + +proc selfontfam {} { + global fonttop fontparam + + set i [$fonttop.f.fam curselection] + if {$i ne {}} { + set fontparam(family) [$fonttop.f.fam get $i] + } +} + +proc chg_fontparam {v sub op} { + global fontparam + + font config sample -$sub $fontparam($sub) +} + proc doprefs {} { global maxwidth maxgraphpct diffopts global oldprefs prefstop showneartags showlocalchanges @@ -7958,6 +8082,13 @@ proc doprefs {} { -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg] grid x $top.selbgbut $top.selbgsep -sticky w + label $top.cfont -text "Fonts: press to choose" + $top.cfont configure -font uifont + grid $top.cfont - -sticky w -pady 10 + mkfontdisp mainfont $top "Main font" + mkfontdisp textfont $top "Diff display font" + mkfontdisp uifont $top "User interface font" + frame $top.buts button $top.buts.ok -text "OK" -command prefsok -default active $top.buts.ok configure -font uifont @@ -8018,14 +8149,37 @@ proc prefscan {} { } catch {destroy $prefstop} unset prefstop + fontcan } proc prefsok {} { global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges + global fontpref mainfont textfont uifont catch {destroy $prefstop} unset prefstop + fontcan + set fontchanged 0 + if {$mainfont ne $fontpref(mainfont)} { + set mainfont $fontpref(mainfont) + parsefont mainfont $mainfont + eval font configure mainfont [fontflags mainfont] + eval font configure mainfontbold [fontflags mainfont 1] + setcoords + set fontchanged 1 + } + if {$textfont ne $fontpref(textfont)} { + set textfont $fontpref(textfont) + parsefont textfont $textfont + eval font configure textfont [fontflags textfont] + eval font configure textfontbold [fontflags textfont 1] + } + if {$uifont ne $fontpref(uifont)} { + set uifont $fontpref(uifont) + parsefont uifont $uifont + eval font configure uifont [fontflags uifont] + } settabs if {$showlocalchanges != $oldprefs(showlocalchanges)} { if {$showlocalchanges} { @@ -8034,7 +8188,7 @@ proc prefsok {} { dohidelocalchanges } } - if {$maxwidth != $oldprefs(maxwidth) + if {$fontchanged || $maxwidth != $oldprefs(maxwidth) || $maxgraphpct != $oldprefs(maxgraphpct)} { redisplay } elseif {$showneartags != $oldprefs(showneartags)} { From 308ff3d59df853a21d4e218473974311fb7b3320 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 6 Oct 2007 20:17:59 +1000 Subject: [PATCH 028/123] gitk: Fix bug where the last few commits would sometimes not be visible We weren't calling showstuff for the last few commits under some circumstances, causing the scrolling region not to be extended right to the end of the graph. This fixes it. Signed-off-by: Paul Mackerras --- gitk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitk b/gitk index 6f0af37342..3f7f77777d 100755 --- a/gitk +++ b/gitk @@ -2758,7 +2758,7 @@ proc layoutmore {} { global uparrowlen downarrowlen mingaplen curview set show $commitidx($curview) - if {$show > $numcommits} { + if {$show > $numcommits || $viewcomplete($curview)} { showstuff $show $viewcomplete($curview) } } From 8d73b242a53da9ea36800a8ff0f9993e5100ea24 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 6 Oct 2007 20:22:00 +1000 Subject: [PATCH 029/123] gitk: Get rid of the diffopts variable The only thing that could be specified with diffopts was the number of lines of context, but there is already a spinbox for that. So this gets rid of it. Signed-off-by: Paul Mackerras --- gitk | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/gitk b/gitk index 3f7f77777d..290deff7b2 100755 --- a/gitk +++ b/gitk @@ -5119,14 +5119,13 @@ proc getblobline {bf id} { } proc mergediff {id l} { - global diffmergeid diffopts mdifffd + global diffmergeid mdifffd global diffids global parentlist set diffmergeid $id set diffids $id # this doesn't seem to actually affect anything... - set env(GIT_DIFF_OPTS) $diffopts set cmd [concat | git diff-tree --no-commit-id --cc $id] if {[catch {set mdf [open $cmd r]} err]} { error_popup "Error getting merge diffs: $err" @@ -5333,11 +5332,10 @@ proc diffcontextchange {n1 n2 op} { } proc getblobdiffs {ids} { - global diffopts blobdifffd diffids env + global blobdifffd diffids env global diffinhdr treediffs global diffcontext - set env(GIT_DIFF_OPTS) $diffopts if {[catch {set bdf [open [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] r]} err]} { puts "error getting diffs: $err" return @@ -8000,7 +7998,7 @@ proc chg_fontparam {v sub op} { } proc doprefs {} { - global maxwidth maxgraphpct diffopts + global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges global bgcolor fgcolor ctext diffcolors selectbgcolor global uifont tabstop @@ -8011,7 +8009,7 @@ proc doprefs {} { raise $top return } - foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} { + foreach v {maxwidth maxgraphpct showneartags showlocalchanges} { set oldprefs($v) [set $v] } toplevel $top @@ -8037,10 +8035,6 @@ proc doprefs {} { label $top.ddisp -text "Diff display options" $top.ddisp configure -font uifont grid $top.ddisp - -sticky w -pady 10 - label $top.diffoptl -text "Options for diff program" \ - -font optionfont - entry $top.diffopt -width 20 -textvariable diffopts - grid x $top.diffoptl $top.diffopt -sticky w frame $top.ntag label $top.ntag.l -text "Display nearby tags" -font optionfont checkbutton $top.ntag.b -variable showneartags @@ -8141,10 +8135,10 @@ proc setfg {c} { } proc prefscan {} { - global maxwidth maxgraphpct diffopts + global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges - foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} { + foreach v {maxwidth maxgraphpct showneartags showlocalchanges} { set $v $oldprefs($v) } catch {destroy $prefstop} @@ -8479,7 +8473,6 @@ proc tcl_encoding {enc} { # defaults... set datemode 0 -set diffopts "-U 5 -p" set wrcomcmd "git diff-tree --stdin -p --pretty" set gitencoding {} From 334f4831e5a779d42e521b770a26eae1ecb27e86 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 18 Oct 2007 02:19:15 -0400 Subject: [PATCH 030/123] send-pack: don't update tracking refs on error Previously, we updated the tracking refs (which match refs we are pushing) while generating the list of refs to send. However, at that point we don't know whether the refs were accepted. Instead, we now wait until we get a response code from the server. If an error was indicated, we don't update any local tracking refs. Technically some refs could have been updated on the remote, but since the local ref update is just an optimization to avoid an extra fetch, we are better off erring on the side of correctness. The user-visible message is now generated much later in the program, and has been tweaked to make more sense. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- send-pack.c | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/send-pack.c b/send-pack.c index c1807f0794..1a7397f3a6 100644 --- a/send-pack.c +++ b/send-pack.c @@ -178,6 +178,35 @@ static int receive_status(int in) return ret; } +static void update_tracking_ref(struct remote *remote, struct ref *ref) +{ + struct refspec rs; + int will_delete_ref; + + rs.src = ref->name; + rs.dst = NULL; + + if (!ref->peer_ref) + return; + + will_delete_ref = is_null_sha1(ref->peer_ref->new_sha1); + + if (!will_delete_ref && + !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) + return; + + if (!remote_find_tracking(remote, &rs)) { + fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); + if (is_null_sha1(ref->peer_ref->new_sha1)) { + if (delete_ref(rs.dst, NULL)) + error("Failed to delete"); + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); + free(rs.dst); + } +} + static int send_pack(int in, int out, struct remote *remote, int nr_refspec, char **refspec) { struct ref *ref; @@ -306,22 +335,6 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha fprintf(stderr, "\n from %s\n to %s\n", old_hex, new_hex); } - if (remote && !dry_run) { - struct refspec rs; - rs.src = ref->name; - rs.dst = NULL; - if (!remote_find_tracking(remote, &rs)) { - fprintf(stderr, " Also local %s\n", rs.dst); - if (will_delete_ref) { - if (delete_ref(rs.dst, NULL)) { - error("Failed to delete"); - } - } else - update_ref("update by push", rs.dst, - ref->new_sha1, NULL, 0, 0); - free(rs.dst); - } - } } packet_flush(out); @@ -334,6 +347,11 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha ret = -4; } + if (!dry_run && remote && ret == 0) { + for (ref = remote_refs; ref; ref = ref->next) + update_tracking_ref(remote, ref); + } + if (!new_refs && ret == 0) fprintf(stderr, "Everything up-to-date\n"); return ret; From 09fba7a59d38d1cafaf33eadaf1d409c4113b30c Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 18 Oct 2007 02:17:46 -0400 Subject: [PATCH 031/123] t5516: test update of local refs on push The first test (updating local refs) should succeed without the prior commit, but the second one (not updating on error) used to fail before the prior commit was written. Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- t/t5516-fetch-push.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4fbd5b1f47..86f9b5346a 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -254,4 +254,32 @@ test_expect_success 'push with dry-run' ' check_push_result $old_commit heads/master ' +test_expect_success 'push updates local refs' ' + + rm -rf parent child && + mkdir parent && cd parent && git init && + echo one >foo && git add foo && git commit -m one && + cd .. && + git clone parent child && cd child && + echo two >foo && git commit -a -m two && + git push && + test $(git rev-parse master) = $(git rev-parse remotes/origin/master) + +' + +test_expect_success 'push does not update local refs on failure' ' + + rm -rf parent child && + mkdir parent && cd parent && git init && + echo one >foo && git add foo && git commit -m one && + echo exit 1 >.git/hooks/pre-receive && + chmod +x .git/hooks/pre-receive && + cd .. && + git clone parent child && cd child && + echo two >foo && git commit -a -m two || exit 1 + git push && exit 1 + test $(git rev-parse master) != $(git rev-parse remotes/origin/master) + +' + test_done From 889a50e909dba5f4416049afc5eeae601fe133bc Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Wed, 17 Oct 2007 10:31:35 +0100 Subject: [PATCH 032/123] Fixing path quoting in git-rebase git-rebase used to fail when run from a path containing a space. Signed-off-by: Jonathan del Strother Signed-off-by: Shawn O. Pearce --- git-rebase.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/git-rebase.sh b/git-rebase.sh index 1583402a06..224cca98ee 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -59,7 +59,7 @@ continue_merge () { die "$RESOLVEMSG" fi - cmt=`cat $dotest/current` + cmt=`cat "$dotest/current"` if ! git diff-index --quiet HEAD then if ! git-commit -C "$cmt" @@ -84,14 +84,14 @@ continue_merge () { } call_merge () { - cmt="$(cat $dotest/cmt.$1)" + cmt="$(cat "$dotest/cmt.$1")" echo "$cmt" > "$dotest/current" hd=$(git rev-parse --verify HEAD) cmt_name=$(git symbolic-ref HEAD) - msgnum=$(cat $dotest/msgnum) - end=$(cat $dotest/end) + msgnum=$(cat "$dotest/msgnum") + end=$(cat "$dotest/end") eval GITHEAD_$cmt='"${cmt_name##refs/heads/}~$(($end - $msgnum))"' - eval GITHEAD_$hd='"$(cat $dotest/onto_name)"' + eval GITHEAD_$hd='$(cat "$dotest/onto_name")' export GITHEAD_$cmt GITHEAD_$hd git-merge-$strategy "$cmt^" -- "$hd" "$cmt" rv=$? @@ -140,10 +140,10 @@ do } if test -d "$dotest" then - prev_head="`cat $dotest/prev_head`" - end="`cat $dotest/end`" - msgnum="`cat $dotest/msgnum`" - onto="`cat $dotest/onto`" + prev_head=$(cat "$dotest/prev_head") + end=$(cat "$dotest/end") + msgnum=$(cat "$dotest/msgnum") + onto=$(cat "$dotest/onto") continue_merge while test "$msgnum" -le "$end" do @@ -160,11 +160,11 @@ do if test -d "$dotest" then git rerere clear - prev_head="`cat $dotest/prev_head`" - end="`cat $dotest/end`" - msgnum="`cat $dotest/msgnum`" + prev_head=$(cat "$dotest/prev_head") + end=$(cat "$dotest/end") + msgnum=$(cat "$dotest/msgnum") msgnum=$(($msgnum + 1)) - onto="`cat $dotest/onto`" + onto=$(cat "$dotest/onto") while test "$msgnum" -le "$end" do call_merge "$msgnum" From e3fa2c761fdc490494e8e0855bcee4d7e58ada6a Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Wed, 17 Oct 2007 19:16:11 +0200 Subject: [PATCH 033/123] mergetool: use path to mergetool in config var mergetool..path This commit adds a mechanism to provide absolute paths to the external programs called by 'git mergetool'. A path can be specified in the configuation variable mergetool..path. The configuration variable is similar to how we name branches and remotes. It is extensible if we need to specify more details about a tool. The mechanism is especially useful on Windows, where external programs are unlikely to be in PATH. [sp: Fixed a few minor issues prior to applying] Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- Documentation/git-mergetool.txt | 6 ++ git-mergetool.sh | 97 ++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 43 deletions(-) diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index 6c32c6d18e..78b2f433b0 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -31,6 +31,12 @@ If a merge resolution program is not specified, 'git mergetool' will use the configuration variable merge.tool. If the configuration variable merge.tool is not set, 'git mergetool' will pick a suitable default. ++ +You can explicitly provide a full path to the tool by setting the +configuration variable mergetool..path. For example, you +can configure the absolute path to kdiff3 by setting +mergetool.kdiff3.path. Otherwise, 'git mergetool' assumes the tool +is available in PATH. Author ------ diff --git a/git-mergetool.sh b/git-mergetool.sh index 9f4f3134b6..4f89cbe8e6 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -192,10 +192,10 @@ merge_file () { case "$merge_tool" in kdiff3) if base_present ; then - (kdiff3 --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \ + ("$merge_tool_path" --auto --L1 "$path (Base)" --L2 "$path (Local)" --L3 "$path (Remote)" \ -o "$path" -- "$BASE" "$LOCAL" "$REMOTE" > /dev/null 2>&1) else - (kdiff3 --auto --L1 "$path (Local)" --L2 "$path (Remote)" \ + ("$merge_tool_path" --auto --L1 "$path (Local)" --L2 "$path (Remote)" \ -o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1) fi status=$? @@ -203,35 +203,35 @@ merge_file () { ;; tkdiff) if base_present ; then - tkdiff -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE" + "$merge_tool_path" -a "$BASE" -o "$path" -- "$LOCAL" "$REMOTE" else - tkdiff -o "$path" -- "$LOCAL" "$REMOTE" + "$merge_tool_path" -o "$path" -- "$LOCAL" "$REMOTE" fi status=$? save_backup ;; meld|vimdiff) touch "$BACKUP" - $merge_tool -- "$LOCAL" "$path" "$REMOTE" + "$merge_tool_path" -- "$LOCAL" "$path" "$REMOTE" check_unchanged save_backup ;; gvimdiff) touch "$BACKUP" - gvimdiff -f -- "$LOCAL" "$path" "$REMOTE" + "$merge_tool_path" -f -- "$LOCAL" "$path" "$REMOTE" check_unchanged save_backup ;; xxdiff) touch "$BACKUP" if base_present ; then - xxdiff -X --show-merged-pane \ + "$merge_tool_path" -X --show-merged-pane \ -R 'Accel.SaveAsMerged: "Ctrl-S"' \ -R 'Accel.Search: "Ctrl+F"' \ -R 'Accel.SearchForward: "Ctrl-G"' \ --merged-file "$path" -- "$LOCAL" "$BASE" "$REMOTE" else - xxdiff -X --show-merged-pane \ + "$merge_tool_path" -X --show-merged-pane \ -R 'Accel.SaveAsMerged: "Ctrl-S"' \ -R 'Accel.Search: "Ctrl+F"' \ -R 'Accel.SearchForward: "Ctrl-G"' \ @@ -243,18 +243,18 @@ merge_file () { opendiff) touch "$BACKUP" if base_present; then - opendiff "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat + "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat else - opendiff "$LOCAL" "$REMOTE" -merge "$path" | cat + "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$path" | cat fi check_unchanged save_backup ;; emerge) if base_present ; then - emacs -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")" + "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")" else - emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")" + "$merge_tool_path" -f emerge-files-command "$LOCAL" "$REMOTE" "$(basename "$path")" fi status=$? save_backup @@ -297,17 +297,38 @@ do shift done +valid_tool() { + case "$1" in + kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff) + ;; # happy + *) + return 1 + ;; + esac +} + +init_merge_tool_path() { + merge_tool_path=`git config mergetool.$1.path` + if test -z "$merge_tool_path" ; then + case "$1" in + emerge) + merge_tool_path=emacs + ;; + *) + merge_tool_path=$1 + ;; + esac + fi +} + + if test -z "$merge_tool"; then merge_tool=`git config merge.tool` - case "$merge_tool" in - kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | "") - ;; # happy - *) + if ! valid_tool "$merge_tool"; then echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool - ;; - esac + fi fi if test -z "$merge_tool" ; then @@ -329,40 +350,30 @@ if test -z "$merge_tool" ; then merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" echo "merge tool candidates: $merge_tool_candidates" for i in $merge_tool_candidates; do - if test $i = emerge ; then - cmd=emacs - else - cmd=$i - fi - if type $cmd > /dev/null 2>&1; then + init_merge_tool_path $i + if type "$merge_tool_path" > /dev/null 2>&1; then merge_tool=$i break fi done if test -z "$merge_tool" ; then - echo "No available merge resolution programs available." + echo "No known merge resolution program available." exit 1 fi +else + if ! valid_tool "$merge_tool"; then + echo >&2 "Unknown merge_tool $merge_tool" + exit 1 + fi + + init_merge_tool_path "$merge_tool" + + if ! type "$merge_tool_path" > /dev/null 2>&1; then + echo "The merge tool $merge_tool is not available as '$merge_tool_path'" + exit 1 + fi fi -case "$merge_tool" in - kdiff3|tkdiff|meld|xxdiff|vimdiff|gvimdiff|opendiff) - if ! type "$merge_tool" > /dev/null 2>&1; then - echo "The merge tool $merge_tool is not available" - exit 1 - fi - ;; - emerge) - if ! type "emacs" > /dev/null 2>&1; then - echo "Emacs is not available" - exit 1 - fi - ;; - *) - echo "Unknown merge tool: $merge_tool" - exit 1 - ;; -esac if test $# -eq 0 ; then files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u` From ca8e6b7a55bfeccbd08182205102d44874f787b6 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Wed, 17 Oct 2007 19:16:12 +0200 Subject: [PATCH 034/123] mergetool: add support for ECMerge Add support to mergetool for ECMerge available from http://www.elliecomputing.com/Products/merge_overview.asp Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- Documentation/git-mergetool.txt | 2 +- git-mergetool.sh | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index 78b2f433b0..a26c260162 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -25,7 +25,7 @@ OPTIONS -t or --tool=:: Use the merge resolution program specified by . Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, and opendiff + kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git mergetool' will use the configuration variable merge.tool. If the diff --git a/git-mergetool.sh b/git-mergetool.sh index 4f89cbe8e6..94511f9d1a 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -250,6 +250,16 @@ merge_file () { check_unchanged save_backup ;; + ecmerge) + touch "$BACKUP" + if base_present; then + "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$path" + else + "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$path" + fi + check_unchanged + save_backup + ;; emerge) if base_present ; then "$merge_tool_path" -f emerge-files-with-ancestor-command "$LOCAL" "$REMOTE" "$BASE" "$(basename "$path")" @@ -299,7 +309,7 @@ done valid_tool() { case "$1" in - kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff) + kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) ;; # happy *) return 1 From d279fc1a76bf9bfeedded1a1c8ebcfc8779f5b10 Mon Sep 17 00:00:00 2001 From: Steffen Prohaska Date: Thu, 18 Oct 2007 12:25:34 +0200 Subject: [PATCH 035/123] mergetool: avoid misleading message "Resetting to default..." If no mergetool is configured in the configuration variable merge.tool the resetting message should not be printed. This is fixed. The message is only printed if a tool is configured but the entry in merge.tool is invalid. Signed-off-by: Steffen Prohaska Signed-off-by: Shawn O. Pearce --- git-mergetool.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-mergetool.sh b/git-mergetool.sh index 94511f9d1a..a68b40386b 100755 --- a/git-mergetool.sh +++ b/git-mergetool.sh @@ -334,7 +334,7 @@ init_merge_tool_path() { if test -z "$merge_tool"; then merge_tool=`git config merge.tool` - if ! valid_tool "$merge_tool"; then + if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool From eb33a67f218b612f6fb1456e19b40a1f97ff02c0 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 19 Oct 2007 19:09:43 +1000 Subject: [PATCH 036/123] gitk: Fix Tcl error: can't unset findcurline The logic in stopfinding assumes that findcurline will be set if find_dirn is, but findnext and findprev can set find_dirn without setting findcurline. This makes sure we only set find_dirn in those places if findcurline is already set. Signed-off-by: Paul Mackerras --- gitk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitk b/gitk index 290deff7b2..3b3cc4bd93 100755 --- a/gitk +++ b/gitk @@ -4279,7 +4279,6 @@ proc findnext {restart} { global findcurline find_dirn if {[info exists find_dirn]} return - set find_dirn 1 if {![info exists findcurline]} { if {$restart} { dofind @@ -4287,6 +4286,7 @@ proc findnext {restart} { bell } } else { + set find_dirn 1 run findmore nowbusy finding } @@ -4296,10 +4296,10 @@ proc findprev {} { global findcurline find_dirn if {[info exists find_dirn]} return - set find_dirn -1 if {![info exists findcurline]} { dofind 1 } else { + set find_dirn -1 run findmorerev nowbusy finding } From 5be507fc955bd14643cac1162cfaa592d0e236ba Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 21 Oct 2007 00:51:09 -0400 Subject: [PATCH 037/123] Use PRIuMAX instead of 'unsigned long long' in show-index Elsewhere in Git we already use PRIuMAX and cast to uintmax_t when we need to display a value that is 'very big' and we're not exactly sure what the largest display size is for this platform. This particular fix is needed so we can do the incredibly crazy temporary hack of: diff --git a/cache.h b/cache.h index e0abcd6..6637fd8 100644 --- a/cache.h +++ b/cache.h @@ -6,6 +6,7 @@ #include SHA1_HEADER #include +#define long long long #if ZLIB_VERNUM < 0x1200 #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11) allowing us to more easily look for locations where we are passing a pointer to an 8 byte value to a function that expects a 4 byte value. This can occur on some platforms where sizeof(long) == 8 and sizeof(size_t) == 4. Signed-off-by: Shawn O. Pearce --- show-index.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/show-index.c b/show-index.c index 57ed9e87b7..7253991fff 100644 --- a/show-index.c +++ b/show-index.c @@ -68,7 +68,7 @@ int main(int argc, char **argv) ntohl(off64[1]); off64_nr++; } - printf("%llu %s (%08x)\n", (unsigned long long) offset, + printf("%" PRIuMAX " %s (%08x)\n", (uintmax_t) offset, sha1_to_hex(entries[i].sha1), ntohl(entries[i].crc)); } From c32f749fec69f92ce3b076128e6322f8130bd881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 21 Oct 2007 11:23:49 +0200 Subject: [PATCH 038/123] Correct some sizeof(size_t) != sizeof(unsigned long) typing errors Fix size_t vs. unsigned long pointer mismatch warnings introduced with the addition of strbuf_detach(). Signed-off-by: Rene Scharfe Signed-off-by: Shawn O. Pearce --- builtin-apply.c | 2 +- builtin-archive.c | 4 +++- diff.c | 8 ++++++-- entry.c | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 05c6bc3592..8411b38c79 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -152,7 +152,7 @@ struct patch { unsigned int is_rename:1; struct fragment *fragments; char *result; - unsigned long resultsize; + size_t resultsize; char old_sha1_prefix[41]; char new_sha1_prefix[41]; struct patch *next; diff --git a/builtin-archive.c b/builtin-archive.c index 04385dea05..6f29c2f40a 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -148,12 +148,14 @@ void *sha1_file_to_archive(const char *path, const unsigned char *sha1, buffer = read_sha1_file(sha1, type, sizep); if (buffer && S_ISREG(mode)) { struct strbuf buf; + size_t size = 0; strbuf_init(&buf, 0); strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(path, buf.buf, buf.len, &buf); convert_to_archive(path, buf.buf, buf.len, &buf, commit); - buffer = strbuf_detach(&buf, sizep); + buffer = strbuf_detach(&buf, &size); + *sizep = size; } return buffer; diff --git a/diff.c b/diff.c index 6648e01521..dfb8595b70 100644 --- a/diff.c +++ b/diff.c @@ -1512,6 +1512,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { struct strbuf buf; + size_t size = 0; strbuf_init(&buf, 0); if (strbuf_read(&buf, 0, 0) < 0) @@ -1519,7 +1520,8 @@ static int populate_from_stdin(struct diff_filespec *s) strerror(errno)); s->should_munmap = 0; - s->data = strbuf_detach(&buf, &s->size); + s->data = strbuf_detach(&buf, &size); + s->size = size; s->should_free = 1; return 0; } @@ -1609,9 +1611,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) */ strbuf_init(&buf, 0); if (convert_to_git(s->path, s->data, s->size, &buf)) { + size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; - s->data = strbuf_detach(&buf, &s->size); + s->data = strbuf_detach(&buf, &size); + s->size = size; s->should_free = 1; } } diff --git a/entry.c b/entry.c index 98f5f6d4ec..cfadc6a292 100644 --- a/entry.c +++ b/entry.c @@ -119,8 +119,10 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout */ strbuf_init(&buf, 0); if (convert_to_working_tree(ce->name, new, size, &buf)) { + size_t newsize = 0; free(new); - new = strbuf_detach(&buf, &size); + new = strbuf_detach(&buf, &newsize); + size = newsize; } if (to_tempfile) { From dd8175f83c725619d72d6bbaacf8eaafc158edea Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Sun, 21 Oct 2007 11:36:19 +0200 Subject: [PATCH 039/123] git-cherry-pick: improve description of -x. Reword the first sentence of the description of -x, in order to make it easier to read and understand. Signed-off-by: Ralf Wildenhues Signed-off-by: Shawn O. Pearce --- Documentation/git-cherry-pick.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 47b1e8c2fc..76a2edfd9b 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -27,11 +27,12 @@ OPTIONS message prior committing. -x:: - Cause the command to append which commit was - cherry-picked after the original commit message when - making a commit. Do not use this option if you are - cherry-picking from your private branch because the - information is useless to the recipient. If on the + When recording the commit, append to the original commit + message a note that indicates which commit this change + was cherry-picked from. Append the note only for cherry + picks without conflicts. Do not use this option if + you are cherry-picking from your private branch because + the information is useless to the recipient. If on the other hand you are cherry-picking between two publicly visible branches (e.g. backporting a fix to a maintenance branch for an older release from a From e076a0e71f25430205d0437c177fd12a7018e5ad Mon Sep 17 00:00:00 2001 From: David Symonds Date: Mon, 22 Oct 2007 10:28:03 +1000 Subject: [PATCH 040/123] gitweb: Provide title attributes for abbreviated author names. Signed-off-by: David Symonds Signed-off-by: Shawn O. Pearce --- gitweb/gitweb.perl | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 48e21dad6c..0d45769bc3 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3437,9 +3437,15 @@ sub git_shortlog_body { print "\n"; } $alternate ^= 1; + my $author = chop_str($co{'author_name'}, 10); + if ($author ne $co{'author_name'}) { + $author = "" . esc_html($author) . ""; + } else { + $author = esc_html($author); + } # git_summary() used print "$co{'age_string'}\n" . print "$co{'age_string_date'}\n" . - "" . esc_html(chop_str($co{'author_name'}, 10)) . "\n" . + "" . $author . "\n" . ""; print format_subject_html($co{'title'}, $co{'title_short'}, href(action=>"commit", hash=>$commit), $ref); @@ -3487,9 +3493,15 @@ sub git_history_body { print "\n"; } $alternate ^= 1; + # shortlog uses chop_str($co{'author_name'}, 10) + my $author = chop_str($co{'author_name'}, 15, 3); + if ($author ne $co{'author_name'}) { + "" . esc_html($author) . ""; + } else { + $author = esc_html($author); + } print "$co{'age_string_date'}\n" . - # shortlog uses chop_str($co{'author_name'}, 10) - "" . esc_html(chop_str($co{'author_name'}, 15, 3)) . "\n" . + "" . $author . "\n" . ""; # originally git_history used chop_str($co{'title'}, 50) print format_subject_html($co{'title'}, $co{'title_short'}, @@ -3643,8 +3655,14 @@ sub git_search_grep_body { print "\n"; } $alternate ^= 1; + my $author = chop_str($co{'author_name'}, 15, 5); + if ($author ne $co{'author_name'}) { + $author = "" . esc_html($author) . ""; + } else { + $author = esc_html($author); + } print "$co{'age_string_date'}\n" . - "" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "\n" . + "" . $author . "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, esc_html(chop_str($co{'title'}, 50)) . "
"); @@ -5157,8 +5175,14 @@ sub git_search { print "\n"; } $alternate ^= 1; + my $author = chop_str($co{'author_name'}, 15, 5); + if ($author ne $co{'author_name'}) { + $author = "" . esc_html($author) . ""; + } else { + $author = esc_html($author); + } print "$co{'age_string_date'}\n" . - "" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "\n" . + "" . $author . "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, From b5d21a4b68aa69c7a2b4d2239aaa9ee5a51058a7 Mon Sep 17 00:00:00 2001 From: Jari Aalto Date: Sun, 21 Oct 2007 01:41:41 +0300 Subject: [PATCH 041/123] On error, do not list all commands, but point to --help option - Remove out call to list_common_cmds_help() - Send error message to stderr, not stdout. Signed-off-by: Jari Aalto Signed-off-by: Shawn O. Pearce --- help.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/help.c b/help.c index 1cd33ece6b..814a8cd02a 100644 --- a/help.c +++ b/help.c @@ -185,8 +185,7 @@ static void show_man_page(const char *git_cmd) void help_unknown_cmd(const char *cmd) { - printf("git: '%s' is not a git-command\n\n", cmd); - list_common_cmds_help(); + fprintf(stderr, "git: '%s' is not a git-command. See --help\n\n", cmd); exit(1); } From 7a39a17a873b818e3a4d121b3a43baf10f68cf61 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 23 Oct 2007 10:15:11 +1000 Subject: [PATCH 042/123] gitk: Limit diff display to listed paths by default When the user has specified a list of paths, either on the command line or when creating a view, gitk currently displays the diffs for all files that a commit has modified, not just the ones that match the path list. This is different from other git commands such as git log. This change makes gitk behave the same as these other git commands by default, that is, gitk only displays the diffs for files that match the path list. There is now a checkbox labelled "Limit diffs to listed paths" in the Edit/Preferences pane. If that is unchecked, gitk will display the diffs for all files as before. When gitk is run with the --merge flag, it will get the list of unmerged files at startup, intersect that with the paths listed on the command line (if any), and use that as the list of paths. Signed-off-by: Paul Mackerras --- gitk | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/gitk b/gitk index 41a1c69e19..248f5fbd04 100755 --- a/gitk +++ b/gitk @@ -1019,7 +1019,7 @@ proc savestuff {w} { global stuffsaved findmergefiles maxgraphpct global maxwidth showneartags showlocalchanges global viewname viewfiles viewargs viewperm nextviewnum - global cmitmode wrapcomment datetimeformat + global cmitmode wrapcomment datetimeformat limitdiffs global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor if {$stuffsaved} return @@ -1038,6 +1038,7 @@ proc savestuff {w} { puts $f [list set showneartags $showneartags] puts $f [list set showlocalchanges $showlocalchanges] puts $f [list set datetimeformat $datetimeformat] + puts $f [list set limitdiffs $limitdiffs] puts $f [list set bgcolor $bgcolor] puts $f [list set fgcolor $fgcolor] puts $f [list set colors $colors] @@ -5015,9 +5016,31 @@ proc startdiff {ids} { } } +proc path_filter {filter name} { + foreach p $filter { + set l [string length $p] + if {[string compare -length $l $p $name] == 0 && + ([string length $name] == $l || [string index $name $l] eq "/")} { + return 1 + } + } + return 0 +} + proc addtocflist {ids} { - global treediffs cflist - add_flist $treediffs($ids) + global treediffs cflist viewfiles curview limitdiffs + + if {$limitdiffs && $viewfiles($curview) ne {}} { + set flist {} + foreach f $treediffs($ids) { + if {[path_filter $viewfiles($curview) $f]} { + lappend flist $f + } + } + } else { + set flist $treediffs($ids) + } + add_flist $flist getblobdiffs $ids } @@ -5124,9 +5147,14 @@ proc getblobdiffs {ids} { global diffopts blobdifffd diffids env global diffinhdr treediffs global diffcontext + global limitdiffs viewfiles curview set env(GIT_DIFF_OPTS) $diffopts - if {[catch {set bdf [open [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] r]} err]} { + set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] + if {$limitdiffs && $viewfiles($curview) ne {}} { + set cmd [concat $cmd $viewfiles($curview)] + } + if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" return } @@ -7382,7 +7410,7 @@ proc doprefs {} { global maxwidth maxgraphpct diffopts global oldprefs prefstop showneartags showlocalchanges global bgcolor fgcolor ctext diffcolors selectbgcolor - global uifont tabstop + global uifont tabstop limitdiffs set top .gitkprefs set prefstop $top @@ -7390,7 +7418,8 @@ proc doprefs {} { raise $top return } - foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} { + foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges \ + limitdiffs} { set oldprefs($v) [set $v] } toplevel $top @@ -7428,6 +7457,11 @@ proc doprefs {} { label $top.tabstopl -text "tabstop" -font optionfont spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop grid x $top.tabstopl $top.tabstop -sticky w + frame $top.ldiff + label $top.ldiff.l -text "Limit diffs to listed paths" -font optionfont + checkbutton $top.ldiff.b -variable limitdiffs + pack $top.ldiff.b $top.ldiff.l -side left + grid x $top.ldiff -sticky w label $top.cdisp -text "Colors: press to choose" $top.cdisp configure -font $uifont @@ -7514,9 +7548,10 @@ proc setfg {c} { proc prefscan {} { global maxwidth maxgraphpct diffopts - global oldprefs prefstop showneartags showlocalchanges + global oldprefs prefstop showneartags showlocalchanges limitdiffs - foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges} { + foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges \ + limitdiffs} { set $v $oldprefs($v) } catch {destroy $prefstop} @@ -7526,7 +7561,7 @@ proc prefscan {} { proc prefsok {} { global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges - global charspc ctext tabstop + global charspc ctext tabstop limitdiffs catch {destroy $prefstop} unset prefstop @@ -7541,7 +7576,8 @@ proc prefsok {} { if {$maxwidth != $oldprefs(maxwidth) || $maxgraphpct != $oldprefs(maxgraphpct)} { redisplay - } elseif {$showneartags != $oldprefs(showneartags)} { + } elseif {$showneartags != $oldprefs(showneartags) || + $limitdiffs != $oldprefs(limitdiffs)} { reselectline } } @@ -7869,6 +7905,7 @@ set showneartags 1 set maxrefs 20 set maxlinelen 200 set showlocalchanges 1 +set limitdiffs 1 set datetimeformat "%Y-%m-%d %H:%M:%S" set colors {green red blue magenta darkgrey brown orange} @@ -7892,6 +7929,7 @@ if {![file isdirectory $gitdir]} { exit 1 } +set mergeonly 0 set revtreeargs {} set cmdline_files {} set i 0 @@ -7899,6 +7937,10 @@ foreach arg $argv { switch -- $arg { "" { } "-d" { set datemode 1 } + "--merge" { + set mergeonly 1 + lappend revtreeargs $arg + } "--" { set cmdline_files [lrange $argv [expr {$i + 1}] end] break @@ -7939,6 +7981,40 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} { } } +if {$mergeonly} { + # find the list of unmerged files + set mlist {} + set nr_unmerged 0 + if {[catch { + set fd [open "| git ls-files -u" r] + } err]} { + show_error {} . "Couldn't get list of unmerged files: $err" + exit 1 + } + while {[gets $fd line] >= 0} { + set i [string first "\t" $line] + if {$i < 0} continue + set fname [string range $line [expr {$i+1}] end] + if {[lsearch -exact $mlist $fname] >= 0} continue + incr nr_unmerged + if {$cmdline_files eq {} || [path_filter $cmdline_files $fname]} { + lappend mlist $fname + } + } + catch {close $fd} + if {$mlist eq {}} { + if {$nr_unmerged == 0} { + show_error {} . "No files selected: --merge specified but\ + no files are unmerged." + } else { + show_error {} . "No files selected: --merge specified but\ + no unmerged files are within file limit." + } + exit 1 + } + set cmdline_files $mlist +} + set nullid "0000000000000000000000000000000000000000" set nullid2 "0000000000000000000000000000000000000001" From 94503918e480123d0d4cf03b03153e4d83cdfd4e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 23 Oct 2007 10:33:38 +1000 Subject: [PATCH 043/123] gitk: Ensure tabstop setting gets restored by Cancel button We weren't restoring the tabstop setting if the user pressed the Cancel button in the Edit/Preferences window. Also improved the label for the checkbox (made it "Tab spacing" rather than the laconic "tabstop") and moved it above the "Display nearby tags" checkbox. Signed-off-by: Paul Mackerras --- gitk | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gitk b/gitk index 248f5fbd04..0d3705c43c 100755 --- a/gitk +++ b/gitk @@ -7419,7 +7419,7 @@ proc doprefs {} { return } foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges \ - limitdiffs} { + limitdiffs tabstop} { set oldprefs($v) [set $v] } toplevel $top @@ -7449,14 +7449,14 @@ proc doprefs {} { -font optionfont entry $top.diffopt -width 20 -textvariable diffopts grid x $top.diffoptl $top.diffopt -sticky w + label $top.tabstopl -text "Tab spacing" -font optionfont + spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop + grid x $top.tabstopl $top.tabstop -sticky w frame $top.ntag label $top.ntag.l -text "Display nearby tags" -font optionfont checkbutton $top.ntag.b -variable showneartags pack $top.ntag.b $top.ntag.l -side left grid x $top.ntag -sticky w - label $top.tabstopl -text "tabstop" -font optionfont - spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop - grid x $top.tabstopl $top.tabstop -sticky w frame $top.ldiff label $top.ldiff.l -text "Limit diffs to listed paths" -font optionfont checkbutton $top.ldiff.b -variable limitdiffs @@ -7547,11 +7547,11 @@ proc setfg {c} { } proc prefscan {} { - global maxwidth maxgraphpct diffopts - global oldprefs prefstop showneartags showlocalchanges limitdiffs + global oldprefs prefstop foreach v {maxwidth maxgraphpct diffopts showneartags showlocalchanges \ - limitdiffs} { + limitdiffs tabstop} { + global $v set $v $oldprefs($v) } catch {destroy $prefstop} From a137a90f49e30fdcb24da0f9ff5c21b28d9cb227 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 23 Oct 2007 21:12:49 +1000 Subject: [PATCH 044/123] gitk: Integrate the reset progress bar in the main frame This makes the reset function use a progress bar in the same location as the progress bars for reading in commits and for finding commits, instead of a progress bar in a separate detached window. The progress bar for resetting is red. This also puts "Resetting" in the status window while the reset is in progress. The setting of the status window is done through an extension of the interface used for setting the watch cursor. Signed-off-by: Paul Mackerras --- gitk | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/gitk b/gitk index 3b3cc4bd93..722e47869b 100755 --- a/gitk +++ b/gitk @@ -626,6 +626,7 @@ proc makewindow {} { global bgcolor fgcolor bglist fglist diffcolors selectbgcolor global headctxmenu progresscanv progressitem progresscoords statusw global fprogitem fprogcoord lastprogupdate progupdatepending + global rprogitem rprogcoord global have_tk85 menu .bar @@ -752,9 +753,11 @@ proc makewindow {} { canvas $progresscanv -relief sunken -height $h -borderwidth 2 set progressitem [$progresscanv create rect -1 0 0 $h -fill green] set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow] + set rprogitem [$progresscanv create rect -1 0 0 $h -fill red] pack $progresscanv -side right -expand 1 -fill x set progresscoords {0 0} set fprogcoord 0 + set rprogcoord 0 bind $progresscanv adjustprogress set lastprogupdate [clock clicks -milliseconds] set progupdatepending 0 @@ -1110,6 +1113,7 @@ proc click {w} { proc adjustprogress {} { global progresscanv progressitem progresscoords global fprogitem fprogcoord lastprogupdate progupdatepending + global rprogitem rprogcoord set w [expr {[winfo width $progresscanv] - 4}] set x0 [expr {$w * [lindex $progresscoords 0]}] @@ -1117,6 +1121,7 @@ proc adjustprogress {} { set h [winfo height $progresscanv] $progresscanv coords $progressitem $x0 0 $x1 $h $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h + $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h set now [clock clicks -milliseconds] if {$now >= $lastprogupdate + 100} { set progupdatepending 0 @@ -4195,20 +4200,30 @@ proc settextcursor {c} { set curtextcursor $c } -proc nowbusy {what} { - global isbusy +proc nowbusy {what {name {}}} { + global isbusy busyname statusw if {[array names isbusy] eq {}} { . config -cursor watch settextcursor watch } set isbusy($what) 1 + set busyname($what) $name + if {$name ne {}} { + $statusw conf -text $name + } } proc notbusy {what} { - global isbusy maincursor textcursor + global isbusy maincursor textcursor busyname statusw - catch {unset isbusy($what)} + catch { + unset isbusy($what) + if {$busyname($what) ne {} && + [$statusw cget -text] eq $busyname($what)} { + $statusw conf -text {} + } + } if {[array names isbusy] eq {}} { . config -cursor $maincursor settextcursor $textcursor @@ -6432,32 +6447,23 @@ proc resethead {} { error_popup $err } else { dohidelocalchanges - set w ".resetprogress" - filerun $fd [list readresetstat $fd $w] - toplevel $w - wm transient $w - wm title $w "Reset progress" - message $w.m -text "Reset in progress, please wait..." \ - -justify center -aspect 1000 - pack $w.m -side top -fill x -padx 20 -pady 5 - canvas $w.c -width 150 -height 20 -bg white - $w.c create rect 0 0 0 20 -fill green -tags rect - pack $w.c -side top -fill x -padx 20 -pady 5 -expand 1 - nowbusy reset + filerun $fd [list readresetstat $fd] + nowbusy reset "Resetting" } } -proc readresetstat {fd w} { - global mainhead mainheadid showlocalchanges +proc readresetstat {fd} { + global mainhead mainheadid showlocalchanges rprogcoord if {[gets $fd line] >= 0} { if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} { - set x [expr {($m * 150) / $n}] - $w.c coords rect 0 0 $x 20 + set rprogcoord [expr {1.0 * $m / $n}] + adjustprogress } return 1 } - destroy $w + set rprogcoord 0 + adjustprogress notbusy reset if {[catch {close $fd} err]} { error_popup $err From 4570b7e9d716e939287dea8193b7d2fb82e9f192 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 23 Oct 2007 21:19:06 +1000 Subject: [PATCH 045/123] gitk: Use the status window for other functions This sets the status window when reading commits, searching through commits, cherry-picking or checking out a head. Signed-off-by: Paul Mackerras --- gitk | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gitk b/gitk index 722e47869b..951d39e21e 100755 --- a/gitk +++ b/gitk @@ -111,7 +111,7 @@ proc start_rev_list {view} { fconfigure $fd -encoding $tclencoding } filerun $fd [list getcommitlines $fd $view] - nowbusy $view + nowbusy $view "Reading" if {$view == $curview} { set progressdirn 1 set progresscoords {0 0} @@ -4264,7 +4264,7 @@ proc dofind {{rev 0}} { set findstartline $selectedline } set findcurline $findstartline - nowbusy finding + nowbusy finding "Searching" if {$gdttype ne "containing:" && ![info exists filehighlight]} { after cancel do_file_hl $fh_serial do_file_hl $fh_serial @@ -4303,7 +4303,7 @@ proc findnext {restart} { } else { set find_dirn 1 run findmore - nowbusy finding + nowbusy finding "Searching" } } @@ -4316,7 +4316,7 @@ proc findprev {} { } else { set find_dirn -1 run findmorerev - nowbusy finding + nowbusy finding "Searching" } } @@ -6381,7 +6381,7 @@ proc cherrypick {} { included in branch $mainhead -- really re-apply it?"] if {!$ok} return } - nowbusy cherrypick + nowbusy cherrypick "Cherry-picking" update # Unfortunately git-cherry-pick writes stuff to stderr even when # no error occurs, and exec takes that as an indication of error... @@ -6505,7 +6505,7 @@ proc cobranch {} { # check the tree is clean first?? set oldmainhead $mainhead - nowbusy checkout + nowbusy checkout "Checking out" update dohidelocalchanges if {[catch { From bd8f677e1c8349b9128490e2a21e0f573d0bea1d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 23 Oct 2007 22:37:23 +1000 Subject: [PATCH 046/123] gitk: Fix some bugs with path limiting in the diff display First, we weren't putting "--" between the ids and the paths in the git diff-tree/diff-index/diff-files command, so if there was a tag and a file with the same name, we could get an ambiguity in the command. This puts the "--" in to make it clear that the paths are paths. Secondly, this implements the path limiting for merge diffs as well as the normal 2-way diffs. Signed-off-by: Paul Mackerras --- gitk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gitk b/gitk index 0d3705c43c..f41e30207b 100755 --- a/gitk +++ b/gitk @@ -4913,12 +4913,16 @@ proc mergediff {id l} { global diffmergeid diffopts mdifffd global diffids global parentlist + global limitdiffs viewfiles curview set diffmergeid $id set diffids $id # this doesn't seem to actually affect anything... set env(GIT_DIFF_OPTS) $diffopts set cmd [concat | git diff-tree --no-commit-id --cc $id] + if {$limitdiffs && $viewfiles($curview) ne {}} { + set cmd [concat $cmd -- $viewfiles($curview)] + } if {[catch {set mdf [open $cmd r]} err]} { error_popup "Error getting merge diffs: $err" return @@ -5152,7 +5156,7 @@ proc getblobdiffs {ids} { set env(GIT_DIFF_OPTS) $diffopts set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"] if {$limitdiffs && $viewfiles($curview) ne {}} { - set cmd [concat $cmd $viewfiles($curview)] + set cmd [concat $cmd -- $viewfiles($curview)] } if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" From 8d863c98b2f9b0d3777227b85b13b4095dd0f6dc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 23 Oct 2007 12:10:55 -0700 Subject: [PATCH 047/123] k.org git toppage: Add link to 1.5.3 release notes. Signed-off-by: Junio C Hamano --- Documentation/git.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index ce8f923a15..c4d87ac201 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -49,7 +49,8 @@ Documentation for older releases are available here: link:RelNotes-1.5.3.4.txt[1.5.3.4], link:RelNotes-1.5.3.3.txt[1.5.3.3], link:RelNotes-1.5.3.2.txt[1.5.3.2], - link:RelNotes-1.5.3.1.txt[1.5.3.1]. + link:RelNotes-1.5.3.1.txt[1.5.3.1], + link:RelNotes-1.5.3.txt[1.5.3]. * release notes for link:RelNotes-1.5.2.5.txt[1.5.2.5], From 74a40c71102ea925b174da15c74afb15b6b82537 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 24 Oct 2007 10:16:56 +1000 Subject: [PATCH 048/123] gitk: Fix a couple more bugs in the path limiting First, paths ending in a slash were not matching anything. This fixes path_filter to handle paths ending in a slash (such entries have to match a directory, and can't match a file, e.g., foo/bar/ can't match a plain file called foo/bar). Secondly, clicking in the file list pane (bottom right) was broken because $treediffs($ids) contained all the files modified by the commit, not just those within the file list. This fixes that too. Signed-off-by: Paul Mackerras --- gitk | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/gitk b/gitk index f41e30207b..ff5eb5e8c4 100755 --- a/gitk +++ b/gitk @@ -5023,28 +5023,25 @@ proc startdiff {ids} { proc path_filter {filter name} { foreach p $filter { set l [string length $p] - if {[string compare -length $l $p $name] == 0 && - ([string length $name] == $l || [string index $name $l] eq "/")} { - return 1 + if {[string index $p end] eq "/"} { + if {[string compare -length $l $p $name] == 0} { + return 1 + } + } else { + if {[string compare -length $l $p $name] == 0 && + ([string length $name] == $l || + [string index $name $l] eq "/")} { + return 1 + } } } return 0 } proc addtocflist {ids} { - global treediffs cflist viewfiles curview limitdiffs + global treediffs - if {$limitdiffs && $viewfiles($curview) ne {}} { - set flist {} - foreach f $treediffs($ids) { - if {[path_filter $viewfiles($curview) $f]} { - lappend flist $f - } - } - } else { - set flist $treediffs($ids) - } - add_flist $flist + add_flist $treediffs($ids) getblobdiffs $ids } @@ -5100,7 +5097,7 @@ proc gettreediffs {ids} { proc gettreediffline {gdtf ids} { global treediff treediffs treepending diffids diffmergeid - global cmitmode + global cmitmode viewfiles curview limitdiffs set nr 0 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} { @@ -5117,7 +5114,17 @@ proc gettreediffline {gdtf ids} { return [expr {$nr >= 1000? 2: 1}] } close $gdtf - set treediffs($ids) $treediff + if {$limitdiffs && $viewfiles($curview) ne {}} { + set flist {} + foreach f $treediff { + if {[path_filter $viewfiles($curview) $f]} { + lappend flist $f + } + } + set treediffs($ids) $flist + } else { + set treediffs($ids) $treediff + } unset treepending if {$cmitmode eq "tree"} { gettree $diffids @@ -7565,7 +7572,7 @@ proc prefscan {} { proc prefsok {} { global maxwidth maxgraphpct global oldprefs prefstop showneartags showlocalchanges - global charspc ctext tabstop limitdiffs + global charspc ctext tabstop limitdiffs treediffs catch {destroy $prefstop} unset prefstop @@ -7577,6 +7584,10 @@ proc prefsok {} { dohidelocalchanges } } + if {$limitdiffs != $oldprefs(limitdiffs)} { + # treediffs elements are limited by path + catch {unset treediffs} + } if {$maxwidth != $oldprefs(maxwidth) || $maxgraphpct != $oldprefs(maxgraphpct)} { redisplay From 59b2023fbb154bc4671782955daddf8ef3018c93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 24 Oct 2007 04:49:51 -0700 Subject: [PATCH 049/123] git-remote: fix "Use of uninitialized value in string ne" martin f krafft writes: > piper:~> git remote show origin > * remote origin > URL: ssh://git.madduck.net/~/git/etc/mailplate.git > Use of uninitialized value in string ne at /usr/local/stow/git/bin/git-remote line 248. This is because there might not be branch..remote defined but the code unconditionally dereferences $branch->{$name}{'REMOTE'} and compares with another string. Tested-by: Martin F Krafft Signed-off-by: Junio C Hamano --- git-remote.perl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-remote.perl b/git-remote.perl index 8e2dc4de73..11630b1a8b 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -244,7 +244,8 @@ sub show_remote { print "* remote $name\n"; print " URL: $info->{'URL'}\n"; for my $branchname (sort keys %$branch) { - next if ($branch->{$branchname}{'REMOTE'} ne $name); + next unless (defined $branch->{$branchname}{'REMOTE'} && + $branch->{$branchname}{'REMOTE'} eq $name); my @merged = map { s|^refs/heads/||; $_; From 2db9b49c6c19d3edaa3c20147f7d9f29588433df Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 1 Oct 2007 14:42:42 +0200 Subject: [PATCH 050/123] git-send-email: add a new sendemail.to configuration variable Some projects prefer to receive patches via a given email address. In these cases, it's handy to configure that address once. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 3 +++ git-send-email.perl | 1 + 2 files changed, 4 insertions(+) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 3727776a0b..e38b7021b4 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -159,6 +159,9 @@ sendemail.aliasfiletype:: Format of the file(s) specified in sendemail.aliasesfile. Must be one of 'mutt', 'mailrc', 'pine', or 'gnus'. +sendemail.to:: + Email address (or alias) to always send to. + sendemail.cccmd:: Command to execute to generate per patch file specific "Cc:"s. diff --git a/git-send-email.perl b/git-send-email.perl index 62e1429733..96051bc01e 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -191,6 +191,7 @@ my %config_settings = ( "smtpserverport" => \$smtp_server_port, "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, + "to" => \@to, "cccmd" => \$cc_cmd, "aliasfiletype" => \$aliasfiletype, "bcc" => \@bcclist, From ce58ec9158fe398dc7142e34cf575d336f408370 Mon Sep 17 00:00:00 2001 From: David Symonds Date: Tue, 23 Oct 2007 11:31:22 +1000 Subject: [PATCH 051/123] gitweb: Refactor abbreviation-with-title-attribute code. Signed-off-by: David Symonds Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 0d45769bc3..818783eba4 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -846,6 +846,23 @@ sub chop_str { return "$body$tail"; } +# takes the same arguments as chop_str, but also wraps a around the +# result with a title attribute if it does get chopped. Additionally, the +# string is HTML-escaped. +sub chop_and_escape_str { + my $str = shift; + my $len = shift; + my $add_len = shift || 10; + + my $chopped = chop_str($str, $len, $add_len); + if ($chopped eq $str) { + return esc_html($chopped); + } else { + return qq{} . + esc_html($chopped) . qq{}; + } +} + ## ---------------------------------------------------------------------- ## functions returning short strings @@ -3437,12 +3454,7 @@ sub git_shortlog_body { print "\n"; } $alternate ^= 1; - my $author = chop_str($co{'author_name'}, 10); - if ($author ne $co{'author_name'}) { - $author = "" . esc_html($author) . ""; - } else { - $author = esc_html($author); - } + my $author = chop_and_escape_str($co{'author_name'}, 10); # git_summary() used print "$co{'age_string'}\n" . print "$co{'age_string_date'}\n" . "" . $author . "\n" . @@ -3494,12 +3506,7 @@ sub git_history_body { } $alternate ^= 1; # shortlog uses chop_str($co{'author_name'}, 10) - my $author = chop_str($co{'author_name'}, 15, 3); - if ($author ne $co{'author_name'}) { - "" . esc_html($author) . ""; - } else { - $author = esc_html($author); - } + my $author = chop_and_escape_str($co{'author_name'}, 15, 3); print "$co{'age_string_date'}\n" . "" . $author . "\n" . ""; @@ -3655,12 +3662,7 @@ sub git_search_grep_body { print "\n"; } $alternate ^= 1; - my $author = chop_str($co{'author_name'}, 15, 5); - if ($author ne $co{'author_name'}) { - $author = "" . esc_html($author) . ""; - } else { - $author = esc_html($author); - } + my $author = chop_and_escape_str($co{'author_name'}, 15, 5); print "$co{'age_string_date'}\n" . "" . $author . "\n" . "" . @@ -5175,12 +5177,7 @@ sub git_search { print "\n"; } $alternate ^= 1; - my $author = chop_str($co{'author_name'}, 15, 5); - if ($author ne $co{'author_name'}) { - $author = "" . esc_html($author) . ""; - } else { - $author = esc_html($author); - } + my $author = chop_and_escape_str($co{'author_name'}, 15, 5); print "$co{'age_string_date'}\n" . "" . $author . "\n" . "" . From d3cd249565b6457d5845e7396530db7685a970bb Mon Sep 17 00:00:00 2001 From: David Symonds Date: Tue, 23 Oct 2007 11:31:23 +1000 Subject: [PATCH 052/123] gitweb: Use chop_and_escape_str in more places. Signed-off-by: David Symonds Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 818783eba4..8916950694 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -3412,7 +3412,7 @@ sub git_project_list_body { "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), -class => "list", -title => $pr->{'descr_long'}}, esc_html($pr->{'descr'})) . "\n" . - "" . esc_html(chop_str($pr->{'owner'}, 15)) . "\n"; + "" . chop_and_escape_str($pr->{'owner'}, 15) . "\n"; print "{'age'}) . "\">" . (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "\n" . "" . @@ -3667,7 +3667,7 @@ sub git_search_grep_body { "" . $author . "\n" . "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, - esc_html(chop_str($co{'title'}, 50)) . "
"); + chop_and_escape_str($co{'title'}, 50) . "
"); my $comment = $co{'comment'}; foreach my $line (@$comment) { if ($line =~ m/^(.*)($search_regexp)(.*)$/i) { @@ -5183,7 +5183,7 @@ sub git_search { "" . $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"}, - esc_html(chop_str($co{'title'}, 50)) . "
"); + chop_and_escape_str($co{'title'}, 50) . "
"); while (my $setref = shift @files) { my %set = %$setref; print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'}, From 55db1df0c86b0fded61731647a8f1cd6e7dc9b04 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 24 Oct 2007 22:03:38 +0200 Subject: [PATCH 053/123] Add some fancy colors in the test library when terminal supports it. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- t/test-lib.sh | 62 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 17 deletions(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index cc1253ccab..66efcdaacd 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -59,15 +59,11 @@ esac # ' # . ./test-lib.sh -error () { - echo "* error: $*" - trap - exit - exit 1 -} - -say () { - echo "* $*" -} +[ "x$TERM" != "xdumb" ] && + tput bold >/dev/null 2>&1 && + tput setaf 1 >/dev/null 2>&1 && + tput sgr0 >/dev/null 2>&1 && + color=t test "${test_description}" != "" || error "Test script did not set test_description." @@ -84,6 +80,8 @@ do exit 0 ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; + --no-color) + color=; shift ;; --no-python) # noop now... shift ;; @@ -92,6 +90,36 @@ do esac done +if test -n "$color"; then + say_color () { + case "$1" in + error) tput bold; tput setaf 1;; # bold red + skip) tput bold; tput setaf 2;; # bold green + pass) tput setaf 2;; # green + info) tput setaf 3;; # brown + *);; + esac + shift + echo "* $*" + tput sgr0 + } +else + say_color() { + shift + echo "* $*" + } +fi + +error () { + say_color error "error: $*" + trap - exit + exit 1 +} + +say () { + say_color info "$*" +} + exec 5>&1 if test "$verbose" = "t" then @@ -122,13 +150,13 @@ test_tick () { test_ok_ () { test_count=$(expr "$test_count" + 1) - say " ok $test_count: $@" + say_color "" " ok $test_count: $@" } test_failure_ () { test_count=$(expr "$test_count" + 1) test_failure=$(expr "$test_failure" + 1); - say "FAIL $test_count: $1" + say_color error "FAIL $test_count: $1" shift echo "$@" | sed -e 's/^/ /' test "$immediate" = "" || { trap - exit; exit 1; } @@ -158,9 +186,9 @@ test_skip () { done case "$to_skip" in t) - say >&3 "skipping test: $@" + say_color skip >&3 "skipping test: $@" test_count=$(expr "$test_count" + 1) - say "skip $test_count: $1" + say_color skip "skip $test_count: $1" : true ;; *) @@ -247,11 +275,11 @@ test_done () { # The Makefile provided will clean this test area so # we will leave things as they are. - say "passed all $test_count test(s)" + say_color pass "passed all $test_count test(s)" exit 0 ;; *) - say "failed $test_failure among $test_count test(s)" + say_color error "failed $test_failure among $test_count test(s)" exit 1 ;; esac @@ -296,8 +324,8 @@ do done case "$to_skip" in t) - say >&3 "skipping test $this_test altogether" - say "skip all tests in $this_test" + say_color skip >&3 "skipping test $this_test altogether" + say_color skip "skip all tests in $this_test" test_done esac done From 1ece127467c1d65062f17777b1eedcd84fd9ea69 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 24 Oct 2007 22:03:39 +0200 Subject: [PATCH 054/123] Support a --quiet option in the test-suite. This shuts down the "* ok ##: `test description`" messages. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- t/test-lib.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/t/test-lib.sh b/t/test-lib.sh index 66efcdaacd..714de6e575 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -80,6 +80,8 @@ do exit 0 ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=t; shift ;; --no-color) color=; shift ;; --no-python) @@ -97,7 +99,7 @@ if test -n "$color"; then skip) tput bold; tput setaf 2;; # bold green pass) tput setaf 2;; # green info) tput setaf 3;; # brown - *);; + *) test -n "$quiet" && return;; esac shift echo "* $*" @@ -105,6 +107,7 @@ if test -n "$color"; then } else say_color() { + test -z "$1" && test -n "$quiet" && return shift echo "* $*" } From c2e6b6d0d1e97c5bc2db24388e247bef62faf917 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Fri, 26 Oct 2007 09:59:12 +0200 Subject: [PATCH 055/123] fast-import.c: fix regression due to strbuf conversion Without this strbuf_detach(), it yields a double free later, the command is in fact stashed, and this is not a memory leak. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- fast-import.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fast-import.c b/fast-import.c index 6f888f6476..f93d7d6c9b 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1616,6 +1616,7 @@ static void cmd_data(struct strbuf *sb) char *term = xstrdup(command_buf.buf + 5 + 2); size_t term_len = command_buf.len - 5 - 2; + strbuf_detach(&command_buf, NULL); for (;;) { if (strbuf_getline(&command_buf, stdin, '\n') == EOF) die("EOF in data (terminator '%s' not found)", term); From d1a2057560b33c926ba481618105edbb65d2c91c Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 25 Oct 2007 22:17:24 +0200 Subject: [PATCH 056/123] Fix generation of perl/perl.mak The code generating perl/Makefile from Makefile.PL was causing trouble because it didn't considered NO_PERL_MAKEMAKER and ran makemaker unconditionally, rewriting perl.mak. Makemaker is FUBAR in ActiveState Perl, and perl/Makefile has a replacement for it. Besides, a changed Git.pm is *NOT* a reason to rebuild all the perl scripts, so remove the dependency too. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- Makefile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Makefile b/Makefile index c63d656f5d..e70e3209d9 100644 --- a/Makefile +++ b/Makefile @@ -778,7 +778,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak -perl/perl.mak: GIT-CFLAGS +perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F) $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl @@ -907,10 +907,6 @@ $(XDIFF_LIB): $(XDIFF_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS) -perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS - (cd perl && $(PERL_PATH) Makefile.PL \ - PREFIX='$(prefix_SQ)') - doc: $(MAKE) -C Documentation all From 4a21d13db4acb112bf210ab6febeed3269b28483 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 26 Oct 2007 12:48:41 +0200 Subject: [PATCH 057/123] hooks-pre-commit: use \t, rather than a literal TAB in regexp Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- templates/hooks--pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/hooks--pre-commit b/templates/hooks--pre-commit index 18b87309f6..a19279b3e4 100644 --- a/templates/hooks--pre-commit +++ b/templates/hooks--pre-commit @@ -58,7 +58,7 @@ perl -e ' if (/\s$/) { bad_line("trailing whitespace", $_); } - if (/^\s* /) { + if (/^\s* \t/) { bad_line("indent SP followed by a TAB", $_); } if (/^(?:[<>=]){7}/) { From 15387e32ff5116b82d3fe53df55b8c87eec83e01 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 26 Oct 2007 06:13:50 +0200 Subject: [PATCH 058/123] Test suite: reset TERM to its previous value after testing. Using konsole, I get no colored output at the end of "t7005-editor.sh" without this patch. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- t/t7005-editor.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 28643b0da4..01cc0c02b1 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -4,6 +4,8 @@ test_description='GIT_EDITOR, core.editor, and stuff' . ./test-lib.sh +OLD_TERM="$TERM" + for i in GIT_EDITOR core_editor EDITOR VISUAL vi do cat >e-$i.sh <<-EOF @@ -88,4 +90,6 @@ do ' done +TERM="$OLD_TERM" + test_done From 505f297989f4b1fc4c5cb5d0c94e783e385f8d6d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:16:50 -0700 Subject: [PATCH 059/123] Add 'diffcore.h' to LIB_H The diffcore.h header file is included by more than just the internal diff generation files, and needs to be part of the proper dependencies. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 72f5ef43ce..ba969e4397 100644 --- a/Makefile +++ b/Makefile @@ -290,7 +290,7 @@ LIB_H = \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h transport.h + mailmap.h remote.h transport.h diffcore.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -917,7 +917,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS) $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) -$(DIFF_OBJS): diffcore.h $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) From cb1491b6bff20748532c9e50afc7f9d6896167a8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:17:55 -0700 Subject: [PATCH 060/123] Split out "exact content match" phase of rename detection This makes the exact content match a separate function of its own. Partly to cut down a bit on the size of the diffcore_rename() function (which is too complex as it is), and partly because there are smarter ways to do this than an O(m*n) loop over it all, and that function should be rewritten to take that into account. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 90 +++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index 142e5376dd..2077a9b981 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -262,6 +262,58 @@ static int compute_stays(struct diff_queue_struct *q, return 1; } +/* + * Find exact renames first. + * + * The first round matches up the up-to-date entries, + * and then during the second round we try to match + * cache-dirty entries as well. + * + * Note: the rest of the rename logic depends on this + * phase also populating all the filespecs for any + * entry that isn't matched up with an exact rename, + * see "is_exact_match()". + */ +static int find_exact_renames(void) +{ + int rename_count = 0; + int contents_too; + + for (contents_too = 0; contents_too < 2; contents_too++) { + int i; + + for (i = 0; i < rename_dst_nr; i++) { + struct diff_filespec *two = rename_dst[i].two; + int j; + + if (rename_dst[i].pair) + continue; /* dealt with an earlier round */ + for (j = 0; j < rename_src_nr; j++) { + int k; + struct diff_filespec *one = rename_src[j].one; + if (!is_exact_match(one, two, contents_too)) + continue; + + /* see if there is a basename match, too */ + for (k = j; k < rename_src_nr; k++) { + one = rename_src[k].one; + if (basename_same(one, two) && + is_exact_match(one, two, + contents_too)) { + j = k; + break; + } + } + + record_rename_pair(i, j, (int)MAX_SCORE); + rename_count++; + break; /* we are done with this entry */ + } + } + } + return rename_count; +} + void diffcore_rename(struct diff_options *options) { int detect_rename = options->detect_rename; @@ -270,12 +322,11 @@ void diffcore_rename(struct diff_options *options) struct diff_queue_struct *q = &diff_queued_diff; struct diff_queue_struct outq; struct diff_score *mx; - int i, j, rename_count, contents_too; + int i, j, rename_count; int num_create, num_src, dst_cnt; if (!minimum_score) minimum_score = DEFAULT_RENAME_SCORE; - rename_count = 0; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; @@ -318,40 +369,11 @@ void diffcore_rename(struct diff_options *options) if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit) goto cleanup; - /* We really want to cull the candidates list early + /* + * We really want to cull the candidates list early * with cheap tests in order to avoid doing deltas. - * The first round matches up the up-to-date entries, - * and then during the second round we try to match - * cache-dirty entries as well. */ - for (contents_too = 0; contents_too < 2; contents_too++) { - for (i = 0; i < rename_dst_nr; i++) { - struct diff_filespec *two = rename_dst[i].two; - if (rename_dst[i].pair) - continue; /* dealt with an earlier round */ - for (j = 0; j < rename_src_nr; j++) { - int k; - struct diff_filespec *one = rename_src[j].one; - if (!is_exact_match(one, two, contents_too)) - continue; - - /* see if there is a basename match, too */ - for (k = j; k < rename_src_nr; k++) { - one = rename_src[k].one; - if (basename_same(one, two) && - is_exact_match(one, two, - contents_too)) { - j = k; - break; - } - } - - record_rename_pair(i, j, (int)MAX_SCORE); - rename_count++; - break; /* we are done with this entry */ - } - } - } + rename_count = find_exact_renames(); /* Have we run out the created file pool? If so we can avoid * doing the delta matrix altogether. From 9fb88419ba85e641006c80db53620423f37f1c93 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:19:10 -0700 Subject: [PATCH 061/123] Ref-count the filespecs used by diffcore Rather than copy the filespecs when introducing new versions of them (for rename or copy detection), use a refcount and increment the count when reusing the diff_filespec. This avoids unnecessary allocations, but the real reason behind this is a future enhancement: we will want to track shared data across the copy/rename detection. In order to efficiently notice when a filespec is used by a rename, the rename machinery wants to keep track of a rename usage count which is shared across all different users of the filespec. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 15 +++++++++++---- diffcore-rename.c | 16 ++++++---------- diffcore.h | 2 ++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/diff.c b/diff.c index dfb8595b70..0b320f6b96 100644 --- a/diff.c +++ b/diff.c @@ -1440,9 +1440,18 @@ struct diff_filespec *alloc_filespec(const char *path) memset(spec, 0, sizeof(*spec)); spec->path = (char *)(spec + 1); memcpy(spec->path, path, namelen+1); + spec->count = 1; return spec; } +void free_filespec(struct diff_filespec *spec) +{ + if (!--spec->count) { + diff_free_filespec_data(spec); + free(spec); + } +} + void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, unsigned short mode) { @@ -2435,10 +2444,8 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *queue, void diff_free_filepair(struct diff_filepair *p) { - diff_free_filespec_data(p->one); - diff_free_filespec_data(p->two); - free(p->one); - free(p->two); + free_filespec(p->one); + free_filespec(p->two); free(p); } diff --git a/diffcore-rename.c b/diffcore-rename.c index 2077a9b981..3da06b702b 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -209,21 +209,19 @@ static int estimate_similarity(struct diff_filespec *src, static void record_rename_pair(int dst_index, int src_index, int score) { - struct diff_filespec *one, *two, *src, *dst; + struct diff_filespec *src, *dst; struct diff_filepair *dp; if (rename_dst[dst_index].pair) die("internal error: dst already matched."); src = rename_src[src_index].one; - one = alloc_filespec(src->path); - fill_filespec(one, src->sha1, src->mode); + src->count++; dst = rename_dst[dst_index].two; - two = alloc_filespec(dst->path); - fill_filespec(two, dst->sha1, dst->mode); + dst->count++; - dp = diff_queue(NULL, one, two); + dp = diff_queue(NULL, src, dst); dp->renamed_pair = 1; if (!strcmp(src->path, dst->path)) dp->score = rename_src[src_index].score; @@ -526,10 +524,8 @@ void diffcore_rename(struct diff_options *options) } } - for (i = 0; i < rename_dst_nr; i++) { - diff_free_filespec_data(rename_dst[i].two); - free(rename_dst[i].two); - } + for (i = 0; i < rename_dst_nr; i++) + free_filespec(rename_dst[i].two); free(rename_dst); rename_dst = NULL; diff --git a/diffcore.h b/diffcore.h index eb618b1ec0..30055ac5a9 100644 --- a/diffcore.h +++ b/diffcore.h @@ -29,6 +29,7 @@ struct diff_filespec { void *cnt_data; const char *funcname_pattern_ident; unsigned long size; + int count; /* Reference count */ int xfrm_flags; /* for use by the xfrm */ unsigned short mode; /* file mode */ unsigned sha1_valid : 1; /* if true, use sha1 and trust mode; @@ -43,6 +44,7 @@ struct diff_filespec { }; extern struct diff_filespec *alloc_filespec(const char *); +extern void free_filespec(struct diff_filespec *); extern void fill_filespec(struct diff_filespec *, const unsigned char *, unsigned short); From 644797119d7a3b7a043a51a9cccd8758f8451f91 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:20:56 -0700 Subject: [PATCH 062/123] copy vs rename detection: avoid unnecessary O(n*m) loops The core rename detection had some rather stupid code to check if a pathname was used by a later modification or rename, which basically walked the whole pathname space for all renames for each rename, in order to tell whether it was a pure rename (no remaining users) or should be considered a copy (other users of the source file remaining). That's really silly, since we can just keep a count of users around, and replace all those complex and expensive loops with just testing that simple counter (but this all depends on the previous commit that shared the diff_filespec data structure by using a separate reference count). Note that the reference count is not the same as the rename count: they behave otherwise rather similarly, but the reference count is tied to the allocation (and decremented at de-allocation, so that when it turns zero we can get rid of the memory), while the rename count is tied to the renames and is decremented when we find a rename (so that when it turns zero we know that it was a rename, not a copy). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 40 ++++++++++++---------------- diffcore-rename.c | 68 ++++++++++++----------------------------------- diffcore.h | 2 +- 3 files changed, 35 insertions(+), 75 deletions(-) diff --git a/diff.c b/diff.c index 0b320f6b96..af85b94d1b 100644 --- a/diff.c +++ b/diff.c @@ -2597,9 +2597,9 @@ void diff_debug_filepair(const struct diff_filepair *p, int i) { diff_debug_filespec(p->one, i, "one"); diff_debug_filespec(p->two, i, "two"); - fprintf(stderr, "score %d, status %c stays %d broken %d\n", + fprintf(stderr, "score %d, status %c rename_used %d broken %d\n", p->score, p->status ? p->status : '?', - p->source_stays, p->broken_pair); + p->one->rename_used, p->broken_pair); } void diff_debug_queue(const char *msg, struct diff_queue_struct *q) @@ -2617,8 +2617,8 @@ void diff_debug_queue(const char *msg, struct diff_queue_struct *q) static void diff_resolve_rename_copy(void) { - int i, j; - struct diff_filepair *p, *pp; + int i; + struct diff_filepair *p; struct diff_queue_struct *q = &diff_queued_diff; diff_debug_queue("resolve-rename-copy", q); @@ -2640,27 +2640,21 @@ static void diff_resolve_rename_copy(void) * either in-place edit or rename/copy edit. */ else if (DIFF_PAIR_RENAME(p)) { - if (p->source_stays) { - p->status = DIFF_STATUS_COPIED; - continue; - } - /* See if there is some other filepair that - * copies from the same source as us. If so - * we are a copy. Otherwise we are either a - * copy if the path stays, or a rename if it - * does not, but we already handled "stays" case. + /* + * A rename might have re-connected a broken + * pair up, causing the pathnames to be the + * same again. If so, that's not a rename at + * all, just a modification.. + * + * Otherwise, see if this source was used for + * multiple renames, in which case we decrement + * the count, and call it a copy. */ - for (j = i + 1; j < q->nr; j++) { - pp = q->queue[j]; - if (strcmp(pp->one->path, p->one->path)) - continue; /* not us */ - if (!DIFF_PAIR_RENAME(pp)) - continue; /* not a rename/copy */ - /* pp is a rename/copy from the same source */ + if (!strcmp(p->one->path, p->two->path)) + p->status = DIFF_STATUS_MODIFIED; + else if (--p->one->rename_used > 0) p->status = DIFF_STATUS_COPIED; - break; - } - if (!p->status) + else p->status = DIFF_STATUS_RENAMED; } else if (hashcmp(p->one->sha1, p->two->sha1) || diff --git a/diffcore-rename.c b/diffcore-rename.c index 3da06b702b..edb2424d13 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -55,12 +55,10 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two, static struct diff_rename_src { struct diff_filespec *one; unsigned short score; /* to remember the break score */ - unsigned src_path_left : 1; } *rename_src; static int rename_src_nr, rename_src_alloc; static struct diff_rename_src *register_rename_src(struct diff_filespec *one, - int src_path_left, unsigned short score) { int first, last; @@ -92,7 +90,6 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one, (rename_src_nr - first - 1) * sizeof(*rename_src)); rename_src[first].one = one; rename_src[first].score = score; - rename_src[first].src_path_left = src_path_left; return &(rename_src[first]); } @@ -216,6 +213,7 @@ static void record_rename_pair(int dst_index, int src_index, int score) die("internal error: dst already matched."); src = rename_src[src_index].one; + src->rename_used++; src->count++; dst = rename_dst[dst_index].two; @@ -227,7 +225,6 @@ static void record_rename_pair(int dst_index, int src_index, int score) dp->score = rename_src[src_index].score; else dp->score = score; - dp->source_stays = rename_src[src_index].src_path_left; rename_dst[dst_index].pair = dp; } @@ -245,21 +242,6 @@ static int score_compare(const void *a_, const void *b_) return b->score - a->score; } -static int compute_stays(struct diff_queue_struct *q, - struct diff_filespec *one) -{ - int i; - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (strcmp(one->path, p->two->path)) - continue; - if (DIFF_PAIR_RENAME(p)) { - return 0; /* something else is renamed into this */ - } - } - return 1; -} - /* * Find exact renames first. * @@ -338,15 +320,25 @@ void diffcore_rename(struct diff_options *options) locate_rename_dst(p->two, 1); } else if (!DIFF_FILE_VALID(p->two)) { - /* If the source is a broken "delete", and + /* + * If the source is a broken "delete", and * they did not really want to get broken, * that means the source actually stays. + * So we increment the "rename_used" score + * by one, to indicate ourselves as a user */ - int stays = (p->broken_pair && !p->score); - register_rename_src(p->one, stays, p->score); + if (p->broken_pair && !p->score) + p->one->rename_used++; + register_rename_src(p->one, p->score); + } + else if (detect_rename == DIFF_DETECT_COPY) { + /* + * Increment the "rename_used" score by + * one, to indicate ourselves as a user. + */ + p->one->rename_used++; + register_rename_src(p->one, p->score); } - else if (detect_rename == DIFF_DETECT_COPY) - register_rename_src(p->one, 1, p->score); } if (rename_dst_nr == 0 || rename_src_nr == 0) goto cleanup; /* nothing to do */ @@ -472,16 +464,7 @@ void diffcore_rename(struct diff_options *options) pair_to_free = p; } else { - for (j = 0; j < rename_dst_nr; j++) { - if (!rename_dst[j].pair) - continue; - if (strcmp(rename_dst[j].pair-> - one->path, - p->one->path)) - continue; - break; - } - if (j < rename_dst_nr) + if (p->one->rename_used) /* this path remains */ pair_to_free = p; } @@ -507,23 +490,6 @@ void diffcore_rename(struct diff_options *options) *q = outq; diff_debug_queue("done collapsing", q); - /* We need to see which rename source really stays here; - * earlier we only checked if the path is left in the result, - * but even if a path remains in the result, if that is coming - * from copying something else on top of it, then the original - * source is lost and does not stay. - */ - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (DIFF_PAIR_RENAME(p) && p->source_stays) { - /* If one appears as the target of a rename-copy, - * then mark p->source_stays = 0; otherwise - * leave it as is. - */ - p->source_stays = compute_stays(q, p->one); - } - } - for (i = 0; i < rename_dst_nr; i++) free_filespec(rename_dst[i].two); diff --git a/diffcore.h b/diffcore.h index 30055ac5a9..cc96c20734 100644 --- a/diffcore.h +++ b/diffcore.h @@ -31,6 +31,7 @@ struct diff_filespec { unsigned long size; int count; /* Reference count */ int xfrm_flags; /* for use by the xfrm */ + int rename_used; /* Count of rename users */ unsigned short mode; /* file mode */ unsigned sha1_valid : 1; /* if true, use sha1 and trust mode; * if false, use the name and read from @@ -58,7 +59,6 @@ struct diff_filepair { struct diff_filespec *two; unsigned short int score; char status; /* M C R N D U (see Documentation/diff-format.txt) */ - unsigned source_stays : 1; /* all of R/C are copies */ unsigned broken_pair : 1; unsigned renamed_pair : 1; unsigned is_unmerged : 1; From 9027f53cb5051bf83a0254e7f8aeb5d1a206de0b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:23:26 -0700 Subject: [PATCH 063/123] Do linear-time/space rename logic for exact renames This implements a smarter rename detector for exact renames, which rather than doing a pairwise comparison (time O(m*n)) will just hash the files into a hash-table (size O(n+m)), and only do pairwise comparisons to renames that have the same hash (time O(n+m) except for unrealistic hash collissions, which we just cull aggressively). Admittedly the exact rename case is not nearly as interesting as the generic case, but it's an important case none-the-less. A similar general approach should work for the generic case too, but even then you do need to handle the exact renames/copies separately (to avoid the inevitable added cost factor that comes from the _size_ of the file), so this is worth doing. In the expectation that we will indeed do the same hashing trick for the general rename case, this code uses a generic hash-table implementation that can be used for other things too. In fact, we might be able to consolidate some of our existing hash tables with the new generic code in hash.[ch]. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 4 +- diffcore-rename.c | 203 ++++++++++++++++++++++++++++++++-------------- hash.c | 110 +++++++++++++++++++++++++ hash.h | 43 ++++++++++ 4 files changed, 299 insertions(+), 61 deletions(-) create mode 100644 hash.c create mode 100644 hash.h diff --git a/Makefile b/Makefile index ba969e4397..2e6fd8f219 100644 --- a/Makefile +++ b/Makefile @@ -290,7 +290,7 @@ LIB_H = \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h transport.h diffcore.h + mailmap.h remote.h transport.h diffcore.h hash.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -300,7 +300,7 @@ DIFF_OBJS = \ LIB_OBJS = \ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o \ - interpolate.o \ + interpolate.o hash.o \ lockfile.o \ patch-ids.o \ object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \ diff --git a/diffcore-rename.c b/diffcore-rename.c index edb2424d13..e7e370b2cc 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -4,6 +4,7 @@ #include "cache.h" #include "diff.h" #include "diffcore.h" +#include "hash.h" /* Table of rename/copy destinations */ @@ -93,29 +94,6 @@ static struct diff_rename_src *register_rename_src(struct diff_filespec *one, return &(rename_src[first]); } -static int is_exact_match(struct diff_filespec *src, - struct diff_filespec *dst, - int contents_too) -{ - if (src->sha1_valid && dst->sha1_valid && - !hashcmp(src->sha1, dst->sha1)) - return 1; - if (!contents_too) - return 0; - if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1)) - return 0; - if (src->size != dst->size) - return 0; - if (src->sha1_valid && dst->sha1_valid) - return !hashcmp(src->sha1, dst->sha1); - if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0)) - return 0; - if (src->size == dst->size && - !memcmp(src->data, dst->data, src->size)) - return 1; - return 0; -} - static int basename_same(struct diff_filespec *src, struct diff_filespec *dst) { int src_len = strlen(src->path), dst_len = strlen(dst->path); @@ -242,56 +220,163 @@ static int score_compare(const void *a_, const void *b_) return b->score - a->score; } +struct file_similarity { + int src_dst, index; + struct diff_filespec *filespec; + struct file_similarity *next; +}; + +static int find_identical_files(struct file_similarity *src, + struct file_similarity *dst) +{ + int renames = 0; + + /* + * Walk over all the destinations ... + */ + do { + struct diff_filespec *one = dst->filespec; + struct file_similarity *p, *best; + int i = 100; + + /* + * .. to find the best source match + */ + best = NULL; + for (p = src; p; p = p->next) { + struct diff_filespec *two = p->filespec; + + /* False hash collission? */ + if (hashcmp(one->sha1, two->sha1)) + continue; + /* Non-regular files? If so, the modes must match! */ + if (!S_ISREG(one->mode) || !S_ISREG(two->mode)) { + if (one->mode != two->mode) + continue; + } + best = p; + if (basename_same(one, two)) + break; + + /* Too many identical alternatives? Pick one */ + if (!--i) + break; + } + if (best) { + record_rename_pair(dst->index, best->index, MAX_SCORE); + renames++; + } + } while ((dst = dst->next) != NULL); + return renames; +} + +/* + * Note: the rest of the rename logic depends on this + * phase also populating all the filespecs for any + * entry that isn't matched up with an exact rename. + */ +static void free_similarity_list(struct file_similarity *p) +{ + while (p) { + struct file_similarity *entry = p; + p = p->next; + + /* Stupid special case, see note above! */ + diff_populate_filespec(entry->filespec, 0); + free(entry); + } +} + +static int find_same_files(void *ptr) +{ + int ret; + struct file_similarity *p = ptr; + struct file_similarity *src = NULL, *dst = NULL; + + /* Split the hash list up into sources and destinations */ + do { + struct file_similarity *entry = p; + p = p->next; + if (entry->src_dst < 0) { + entry->next = src; + src = entry; + } else { + entry->next = dst; + dst = entry; + } + } while (p); + + /* + * If we have both sources *and* destinations, see if + * we can match them up + */ + ret = (src && dst) ? find_identical_files(src, dst) : 0; + + /* Free the hashes and return the number of renames found */ + free_similarity_list(src); + free_similarity_list(dst); + return ret; +} + +static unsigned int hash_filespec(struct diff_filespec *filespec) +{ + unsigned int hash; + if (!filespec->sha1_valid) { + if (diff_populate_filespec(filespec, 0)) + return 0; + hash_sha1_file(filespec->data, filespec->size, "blob", filespec->sha1); + } + memcpy(&hash, filespec->sha1, sizeof(hash)); + return hash; +} + +static void insert_file_table(struct hash_table *table, int src_dst, int index, struct diff_filespec *filespec) +{ + void **pos; + unsigned int hash; + struct file_similarity *entry = xmalloc(sizeof(*entry)); + + entry->src_dst = src_dst; + entry->index = index; + entry->filespec = filespec; + entry->next = NULL; + + hash = hash_filespec(filespec); + pos = insert_hash(hash, entry, table); + + /* We already had an entry there? */ + if (pos) { + entry->next = *pos; + *pos = entry; + } +} + /* * Find exact renames first. * * The first round matches up the up-to-date entries, * and then during the second round we try to match * cache-dirty entries as well. - * - * Note: the rest of the rename logic depends on this - * phase also populating all the filespecs for any - * entry that isn't matched up with an exact rename, - * see "is_exact_match()". */ static int find_exact_renames(void) { - int rename_count = 0; - int contents_too; + int i; + struct hash_table file_table; - for (contents_too = 0; contents_too < 2; contents_too++) { - int i; + init_hash(&file_table); + for (i = 0; i < rename_src_nr; i++) + insert_file_table(&file_table, -1, i, rename_src[i].one); - for (i = 0; i < rename_dst_nr; i++) { - struct diff_filespec *two = rename_dst[i].two; - int j; + for (i = 0; i < rename_dst_nr; i++) + insert_file_table(&file_table, 1, i, rename_dst[i].two); - if (rename_dst[i].pair) - continue; /* dealt with an earlier round */ - for (j = 0; j < rename_src_nr; j++) { - int k; - struct diff_filespec *one = rename_src[j].one; - if (!is_exact_match(one, two, contents_too)) - continue; + /* Find the renames */ + i = for_each_hash(&file_table, find_same_files); - /* see if there is a basename match, too */ - for (k = j; k < rename_src_nr; k++) { - one = rename_src[k].one; - if (basename_same(one, two) && - is_exact_match(one, two, - contents_too)) { - j = k; - break; - } - } + /* .. and free the hash data structure */ + free_hash(&file_table); - record_rename_pair(i, j, (int)MAX_SCORE); - rename_count++; - break; /* we are done with this entry */ - } - } - } - return rename_count; + return i; } void diffcore_rename(struct diff_options *options) diff --git a/hash.c b/hash.c new file mode 100644 index 0000000000..7b492d4fc0 --- /dev/null +++ b/hash.c @@ -0,0 +1,110 @@ +/* + * Some generic hashing helpers. + */ +#include "cache.h" +#include "hash.h" + +/* + * Look up a hash entry in the hash table. Return the pointer to + * the existing entry, or the empty slot if none existed. The caller + * can then look at the (*ptr) to see whether it existed or not. + */ +static struct hash_table_entry *lookup_hash_entry(unsigned int hash, struct hash_table *table) +{ + unsigned int size = table->size, nr = hash % size; + struct hash_table_entry *array = table->array; + + while (array[nr].ptr) { + if (array[nr].hash == hash) + break; + nr++; + if (nr >= size) + nr = 0; + } + return array + nr; +} + + +/* + * Insert a new hash entry pointer into the table. + * + * If that hash entry already existed, return the pointer to + * the existing entry (and the caller can create a list of the + * pointers or do anything else). If it didn't exist, return + * NULL (and the caller knows the pointer has been inserted). + */ +static void **insert_hash_entry(unsigned int hash, void *ptr, struct hash_table *table) +{ + struct hash_table_entry *entry = lookup_hash_entry(hash, table); + + if (!entry->ptr) { + entry->ptr = ptr; + entry->hash = hash; + table->nr++; + return NULL; + } + return &entry->ptr; +} + +static void grow_hash_table(struct hash_table *table) +{ + unsigned int i; + unsigned int old_size = table->size, new_size; + struct hash_table_entry *old_array = table->array, *new_array; + + new_size = alloc_nr(old_size); + new_array = xcalloc(sizeof(struct hash_table_entry), new_size); + table->size = new_size; + table->array = new_array; + table->nr = 0; + for (i = 0; i < old_size; i++) { + unsigned int hash = old_array[i].hash; + void *ptr = old_array[i].ptr; + if (ptr) + insert_hash_entry(hash, ptr, table); + } + free(old_array); +} + +void *lookup_hash(unsigned int hash, struct hash_table *table) +{ + if (!table->array) + return NULL; + return &lookup_hash_entry(hash, table)->ptr; +} + +void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table) +{ + unsigned int nr = table->nr; + if (nr >= table->size/2) + grow_hash_table(table); + return insert_hash_entry(hash, ptr, table); +} + +int for_each_hash(struct hash_table *table, int (*fn)(void *)) +{ + int sum = 0; + unsigned int i; + unsigned int size = table->size; + struct hash_table_entry *array = table->array; + + for (i = 0; i < size; i++) { + void *ptr = array->ptr; + array++; + if (ptr) { + int val = fn(ptr); + if (val < 0) + return val; + sum += val; + } + } + return sum; +} + +void free_hash(struct hash_table *table) +{ + free(table->array); + table->array = NULL; + table->size = 0; + table->nr = 0; +} diff --git a/hash.h b/hash.h new file mode 100644 index 0000000000..a8b0fbb5b5 --- /dev/null +++ b/hash.h @@ -0,0 +1,43 @@ +#ifndef HASH_H +#define HASH_H + +/* + * These are some simple generic hash table helper functions. + * Not necessarily suitable for all users, but good for things + * where you want to just keep track of a list of things, and + * have a good hash to use on them. + * + * It keeps the hash table at roughly 50-75% free, so the memory + * cost of the hash table itself is roughly + * + * 3 * 2*sizeof(void *) * nr_of_objects + * + * bytes. + * + * FIXME: on 64-bit architectures, we waste memory. It would be + * good to have just 32-bit pointers, requiring a special allocator + * for hashed entries or something. + */ +struct hash_table_entry { + unsigned int hash; + void *ptr; +}; + +struct hash_table { + unsigned int size, nr; + struct hash_table_entry *array; +}; + +extern void *lookup_hash(unsigned int hash, struct hash_table *table); +extern void **insert_hash(unsigned int hash, void *ptr, struct hash_table *table); +extern int for_each_hash(struct hash_table *table, int (*fn)(void *)); +extern void free_hash(struct hash_table *table); + +static inline void init_hash(struct hash_table *table) +{ + table->size = 0; + table->nr = 0; + table->array = NULL; +} + +#endif From 17559a643ecef94834d930790498c6babe3e89a8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Oct 2007 11:24:47 -0700 Subject: [PATCH 064/123] Do exact rename detection regardless of rename limits Now that the exact rename detection is linear-time (with a very small constant factor to boot), there is no longer any reason to limit it by the number of files involved. In some trivial testing, I created a repository with a directory that had a hundred thousand files in it (all with different contents), and then moved that directory to show the effects of renaming 100,000 files. With the new code, that resulted in [torvalds@woody big-rename]$ time ~/git/git show -C | wc -l 400006 real 0m2.071s user 0m1.520s sys 0m0.576s ie the code can correctly detect the hundred thousand renames in about 2 seconds (the number "400006" comes from four lines for each rename: diff --git a/really-big-dir/file-1-1-1-1-1 b/moved-big-dir/file-1-1-1-1-1 similarity index 100% rename from really-big-dir/file-1-1-1-1-1 rename to moved-big-dir/file-1-1-1-1-1 and the extra six lines is from a one-liner commit message and all the commit information and spacing). Most of those two seconds weren't even really the rename detection, it's really all the other stuff needed to get there. With the old code, this wouldn't have been practically possible. Doing a pairwise check of the ten billion possible pairs would have been prohibitively expensive. In fact, even with the rename limiter in place, the old code would waste a lot of time just on the diff_filespec checks, and despite not even trying to find renames, it used to look like: [torvalds@woody big-rename]$ time git show -C | wc -l 1400006 real 0m12.337s user 0m12.285s sys 0m0.192s ie we used to take 12 seconds for this load and not even do any rename detection! (The number 1400006 comes from fourteen lines per file moved: seven lines each for the delete and the create of a one-liner file, and the same extra six lines of commit information). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index e7e370b2cc..394693222d 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -428,6 +428,12 @@ void diffcore_rename(struct diff_options *options) if (rename_dst_nr == 0 || rename_src_nr == 0) goto cleanup; /* nothing to do */ + /* + * We really want to cull the candidates list early + * with cheap tests in order to avoid doing deltas. + */ + rename_count = find_exact_renames(); + /* * This basically does a test for the rename matrix not * growing larger than a "rename_limit" square matrix, ie: @@ -444,12 +450,6 @@ void diffcore_rename(struct diff_options *options) if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit) goto cleanup; - /* - * We really want to cull the candidates list early - * with cheap tests in order to avoid doing deltas. - */ - rename_count = find_exact_renames(); - /* Have we run out the created file pool? If so we can avoid * doing the delta matrix altogether. */ From 81ac051d6ac9deef9a34de496fb981469aae77f0 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 26 Oct 2007 16:51:28 -0700 Subject: [PATCH 065/123] Fix ugly magic special case in exact rename detection For historical reasons, the exact rename detection had populated the filespecs for the entries it compared, and the rest of the similarity analysis depended on that. I hadn't even bothered to debug why that was the case when I re-did the rename detection, I just made the new one have the same broken behaviour, with a note about this special case. This fixes that fixme. The reason the exact rename detector needed to fill in the file sizes of the files it checked was that the _inexact_ rename detector was broken, and started comparing file sizes before it filled them in. Fixing that allows the exact phase to do the sane thing of never even caring (since all *it* cares about is really just the SHA1 itself, not the size nor the contents). It turns out that this also indirectly fixes a bug: trying to populate all the filespecs will run out of virtual memory if there is tons and tons of possible rename options. The fuzzy similarity analysis does the right thing in this regard, and free's the blob info after it has generated the hash tables, so the special case code caused more trouble than just some extra illogical code. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index 394693222d..7ed5ef81bf 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -144,6 +144,20 @@ static int estimate_similarity(struct diff_filespec *src, if (!S_ISREG(src->mode) || !S_ISREG(dst->mode)) return 0; + /* + * Need to check that source and destination sizes are + * filled in before comparing them. + * + * If we already have "cnt_data" filled in, we know it's + * all good (avoid checking the size for zero, as that + * is a possible size - we really should have a flag to + * say whether the size is valid or not!) + */ + if (!src->cnt_data && diff_populate_filespec(src, 0)) + return 0; + if (!dst->cnt_data && diff_populate_filespec(dst, 0)) + return 0; + max_size = ((src->size > dst->size) ? src->size : dst->size); base_size = ((src->size < dst->size) ? src->size : dst->size); delta_size = max_size - base_size; @@ -159,11 +173,6 @@ static int estimate_similarity(struct diff_filespec *src, if (base_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE) return 0; - if ((!src->cnt_data && diff_populate_filespec(src, 0)) - || (!dst->cnt_data && diff_populate_filespec(dst, 0))) - return 0; /* error but caught downstream */ - - delta_limit = (unsigned long) (base_size * (MAX_SCORE-minimum_score) / MAX_SCORE); if (diffcore_count_changes(src, dst, @@ -270,19 +279,11 @@ static int find_identical_files(struct file_similarity *src, return renames; } -/* - * Note: the rest of the rename logic depends on this - * phase also populating all the filespecs for any - * entry that isn't matched up with an exact rename. - */ static void free_similarity_list(struct file_similarity *p) { while (p) { struct file_similarity *entry = p; p = p->next; - - /* Stupid special case, see note above! */ - diff_populate_filespec(entry->filespec, 0); free(entry); } } From 42899ac898f2b539fc762c8452da2c981ccbd815 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 26 Oct 2007 16:56:34 -0700 Subject: [PATCH 066/123] Do the fuzzy rename detection limits with the exact renames removed When we do the fuzzy rename detection, we don't care about the destinations that we already handled with the exact rename detector. And, in fact, the code already knew that - but the rename limiter, which used to run *before* exact renames were detected, did not. This fixes it so that the rename detection limiter now bases its decisions on the *remaining* rename counts, rather than the original ones. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diffcore-rename.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/diffcore-rename.c b/diffcore-rename.c index 7ed5ef81bf..f9ebea5640 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -435,33 +435,37 @@ void diffcore_rename(struct diff_options *options) */ rename_count = find_exact_renames(); + /* Did we only want exact renames? */ + if (minimum_score == MAX_SCORE) + goto cleanup; + + /* + * Calculate how many renames are left (but all the source + * files still remain as options for rename/copies!) + */ + num_create = (rename_dst_nr - rename_count); + num_src = rename_src_nr; + + /* All done? */ + if (!num_create) + goto cleanup; + /* * This basically does a test for the rename matrix not * growing larger than a "rename_limit" square matrix, ie: * - * rename_dst_nr * rename_src_nr > rename_limit * rename_limit + * num_create * num_src > rename_limit * rename_limit * * but handles the potential overflow case specially (and we * assume at least 32-bit integers) */ if (rename_limit <= 0 || rename_limit > 32767) rename_limit = 32767; - if (rename_dst_nr > rename_limit && rename_src_nr > rename_limit) + if (num_create > rename_limit && num_src > rename_limit) goto cleanup; - if (rename_dst_nr * rename_src_nr > rename_limit * rename_limit) + if (num_create * num_src > rename_limit * rename_limit) goto cleanup; - /* Have we run out the created file pool? If so we can avoid - * doing the delta matrix altogether. - */ - if (rename_count == rename_dst_nr) - goto cleanup; - - if (minimum_score == MAX_SCORE) - goto cleanup; - - num_create = (rename_dst_nr - rename_count); - num_src = rename_src_nr; mx = xmalloc(sizeof(*mx) * num_create * num_src); for (dst_cnt = i = 0; i < rename_dst_nr; i++) { int base = dst_cnt * num_src; From a238917ba48c44de8a00e3aee7898d9beb5afcd8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 26 Oct 2007 23:26:41 -0700 Subject: [PATCH 067/123] help: remove extra blank line after "See 'git --help'" message The double LF were there only because we gave a list of common commands. WIth the list gone, there is no reason to have the extra blank line. Signed-off-by: Junio C Hamano --- help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help.c b/help.c index 814a8cd02a..e5662d9014 100644 --- a/help.c +++ b/help.c @@ -185,7 +185,7 @@ static void show_man_page(const char *git_cmd) void help_unknown_cmd(const char *cmd) { - fprintf(stderr, "git: '%s' is not a git-command. See --help\n\n", cmd); + fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd); exit(1); } From 50e62a8e703b78fbe59d5f98b1bb36464570a815 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:47:56 +0200 Subject: [PATCH 068/123] rev-list: implement --bisect-all This is Junio's patch with some stuff to make --bisect-all compatible with --bisect-vars. This option makes it possible to see all the potential bisection points. The best ones are displayed first. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce --- builtin-rev-list.c | 100 ++++++++++++++++++++++++++++++++++++++------- log-tree.c | 2 +- log-tree.h | 1 + 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 33726b8d84..44393320e8 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -9,6 +9,7 @@ #include "revision.h" #include "list-objects.h" #include "builtin.h" +#include "log-tree.h" /* bits #0-15 in revision.h */ @@ -38,7 +39,8 @@ static const char rev_list_usage[] = " --left-right\n" " special purpose:\n" " --bisect\n" -" --bisect-vars" +" --bisect-vars\n" +" --bisect-all" ; static struct rev_info revs; @@ -74,6 +76,7 @@ static void show_commit(struct commit *commit) parents = parents->next; } } + show_decorations(commit); if (revs.commit_format == CMIT_FMT_ONELINE) putchar(' '); else @@ -278,6 +281,57 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr) return best; } +struct commit_dist { + struct commit *commit; + int distance; +}; + +static int compare_commit_dist(const void *a_, const void *b_) +{ + struct commit_dist *a, *b; + + a = (struct commit_dist *)a_; + b = (struct commit_dist *)b_; + if (a->distance != b->distance) + return b->distance - a->distance; /* desc sort */ + return hashcmp(a->commit->object.sha1, b->commit->object.sha1); +} + +static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr) +{ + struct commit_list *p; + struct commit_dist *array = xcalloc(nr, sizeof(*array)); + int cnt, i; + + for (p = list, cnt = 0; p; p = p->next) { + int distance; + unsigned flags = p->item->object.flags; + + if (revs.prune_fn && !(flags & TREECHANGE)) + continue; + distance = weight(p); + if (nr - distance < distance) + distance = nr - distance; + array[cnt].commit = p->item; + array[cnt].distance = distance; + cnt++; + } + qsort(array, cnt, sizeof(*array), compare_commit_dist); + for (p = list, i = 0; i < cnt; i++) { + struct name_decoration *r = xmalloc(sizeof(*r) + 100); + struct object *obj = &(array[i].commit->object); + + sprintf(r->name, "dist=%d", array[i].distance); + r->next = add_decoration(&name_decoration, obj, r); + p->item = array[i].commit; + p = p->next; + } + if (p) + p->next = NULL; + free(array); + return list; +} + /* * zero or positive weight is the number of interesting commits it can * reach, including itself. Especially, weight = 0 means it does not @@ -292,7 +346,8 @@ static struct commit_list *best_bisection(struct commit_list *list, int nr) * or positive distance. */ static struct commit_list *do_find_bisection(struct commit_list *list, - int nr, int *weights) + int nr, int *weights, + int find_all) { int n, counted; struct commit_list *p; @@ -351,7 +406,7 @@ static struct commit_list *do_find_bisection(struct commit_list *list, clear_distance(list); /* Does it happen to be at exactly half-way? */ - if (halfway(p, nr)) + if (!find_all && halfway(p, nr)) return p; counted++; } @@ -389,19 +444,22 @@ static struct commit_list *do_find_bisection(struct commit_list *list, weight_set(p, weight(q)); /* Does it happen to be at exactly half-way? */ - if (halfway(p, nr)) + if (!find_all && halfway(p, nr)) return p; } } show_list("bisection 2 counted all", counted, nr, list); - /* Then find the best one */ - return best_bisection(list, nr); + if (!find_all) + return best_bisection(list, nr); + else + return best_bisection_sorted(list, nr); } static struct commit_list *find_bisection(struct commit_list *list, - int *reaches, int *all) + int *reaches, int *all, + int find_all) { int nr, on_list; struct commit_list *p, *best, *next, *last; @@ -434,14 +492,13 @@ static struct commit_list *find_bisection(struct commit_list *list, weights = xcalloc(on_list, sizeof(*weights)); /* Do the real work of finding bisection commit. */ - best = do_find_bisection(list, nr, weights); - + best = do_find_bisection(list, nr, weights, find_all); if (best) { - best->next = NULL; + if (!find_all) + best->next = NULL; *reaches = weight(best); } free(weights); - return best; } @@ -468,6 +525,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) int i; int read_from_stdin = 0; int bisect_show_vars = 0; + int bisect_find_all = 0; git_config(git_default_config); init_revisions(&revs, prefix); @@ -490,6 +548,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) bisect_list = 1; continue; } + if (!strcmp(arg, "--bisect-all")) { + bisect_list = 1; + bisect_find_all = 1; + continue; + } if (!strcmp(arg, "--bisect-vars")) { bisect_list = 1; bisect_show_vars = 1; @@ -536,9 +599,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) { int reaches = reaches, all = all; - revs.commits = find_bisection(revs.commits, &reaches, &all); + revs.commits = find_bisection(revs.commits, &reaches, &all, + bisect_find_all); if (bisect_show_vars) { int cnt; + char hex[41]; if (!revs.commits) return 1; /* @@ -550,15 +615,22 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) * A bisect set of size N has (N-1) commits further * to test, as we already know one bad one. */ - cnt = all-reaches; + cnt = all - reaches; if (cnt < reaches) cnt = reaches; + strcpy(hex, sha1_to_hex(revs.commits->item->object.sha1)); + + if (bisect_find_all) { + traverse_commit_list(&revs, show_commit, show_object); + printf("------\n"); + } + printf("bisect_rev=%s\n" "bisect_nr=%d\n" "bisect_good=%d\n" "bisect_bad=%d\n" "bisect_all=%d\n", - sha1_to_hex(revs.commits->item->object.sha1), + hex, cnt - 1, all - reaches - 1, reaches - 1, diff --git a/log-tree.c b/log-tree.c index 62edd34455..3763ce94fc 100644 --- a/log-tree.c +++ b/log-tree.c @@ -15,7 +15,7 @@ static void show_parents(struct commit *commit, int abbrev) } } -static void show_decorations(struct commit *commit) +void show_decorations(struct commit *commit) { const char *prefix; struct name_decoration *decoration; diff --git a/log-tree.h b/log-tree.h index e82b56a20d..b33f7cd7ac 100644 --- a/log-tree.h +++ b/log-tree.h @@ -12,5 +12,6 @@ int log_tree_diff_flush(struct rev_info *); int log_tree_commit(struct rev_info *, struct commit *); int log_tree_opt_parse(struct rev_info *, const char **, int); void show_log(struct rev_info *opt, const char *sep); +void show_decorations(struct commit *commit); #endif From 3ac9f612cbbb146743c0734c707e6cb512c43aa4 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:48:11 +0200 Subject: [PATCH 069/123] rev-list documentation: add "--bisect-all". Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce --- Documentation/git-rev-list.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 7cd0e8913e..485280423e 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -34,6 +34,7 @@ SYNOPSIS [ \--pretty | \--header ] [ \--bisect ] [ \--bisect-vars ] + [ \--bisect-all ] [ \--merge ] [ \--reverse ] [ \--walk-reflogs ] @@ -354,6 +355,21 @@ the expected number of commits to be tested if `bisect_rev` turns out to be bad to `bisect_bad`, and the number of commits we are bisecting right now to `bisect_all`. +--bisect-all:: + +This outputs all the commit objects between the included and excluded +commits, ordered by their distance to the included and excluded +commits. The farthest from them is displayed first. (This is the only +one displayed by `--bisect`.) + +This is useful because it makes it easy to choose a good commit to +test when you want to avoid to test some of them for some reason (they +may not compile for example). + +This option can be used along with `--bisect-vars`, in this case, +after all the sorted commit objects, there will be the same text as if +`--bisect-vars` had been used alone. + -- Commit Ordering From 8fe26f4481f274a6c752a6c13ac5da0460dbd1b6 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:48:23 +0200 Subject: [PATCH 070/123] Bisect: fix some white spaces and empty lines breakages. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce --- git-bisect.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 388887a556..436ccf66ff 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -64,7 +64,7 @@ bisect_start() { branch=`cat "$GIT_DIR/head-name"` else branch=master - fi + fi git checkout $branch || exit ;; refs/heads/*) @@ -95,11 +95,11 @@ bisect_start() { arg="$1" case "$arg" in --) - shift + shift break ;; *) - rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || { + rev=$(git rev-parse --verify "$arg^{commit}" 2>/dev/null) || { test $has_double_dash -eq 1 && die "'$arg' does not appear to be a valid revision" break @@ -110,10 +110,10 @@ bisect_start() { else bisect_write_good "$rev" fi - shift + shift ;; esac - done + done sq "$@" >"$GIT_DIR/BISECT_NAMES" echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG" @@ -143,7 +143,7 @@ bisect_write_bad() { bisect_good() { bisect_autostart - case "$#" in + case "$#" in 0) revs=$(git rev-parse --verify HEAD) || exit ;; *) revs=$(git rev-parse --revs-only --no-flags "$@") && test '' != "$revs" || die "Bad rev input: $@" ;; @@ -153,7 +153,6 @@ bisect_good() { rev=$(git rev-parse --verify "$rev^{commit}") || exit bisect_write_good "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" - done bisect_auto_next } @@ -207,7 +206,7 @@ bisect_auto_next() { } bisect_next() { - case "$#" in 0) ;; *) usage ;; esac + case "$#" in 0) ;; *) usage ;; esac bisect_autostart bisect_next_check good @@ -255,7 +254,7 @@ bisect_reset() { exit 1 } branch="$1" ;; - *) + *) usage ;; esac if git checkout "$branch"; then From 97e1c51e15d94f523c67a499ecb633b0e20324cb Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:48:36 +0200 Subject: [PATCH 071/123] Bisect: implement "bisect skip" to mark untestable revisions. When there are some "skip"ped revisions, we add the '--bisect-all' option to "git rev-list --bisect-vars". Then we filter out the "skip"ped revisions from the result of the rev-list command, and we modify the "bisect_rev" var accordingly. We don't always use "--bisect-all" because it is slower than "--bisect-vars" or "--bisect". When we cannot find for sure the first bad commit because of "skip"ped commits, we print the hash of each possible first bad commit and then we exit with code 2. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce --- git-bisect.sh | 125 ++++++++++++++++++++++++++++++++++-- t/t6030-bisect-porcelain.sh | 71 ++++++++++++++++++++ 2 files changed, 190 insertions(+), 6 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 436ccf66ff..cd46190302 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -17,6 +17,8 @@ git bisect replay replay bisection log. git bisect log show bisect log. +git bisect skip [...] + mark ... untestable revisions. git bisect run ... use ... to automatically bisect.' @@ -163,6 +165,28 @@ bisect_write_good() { echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" } +bisect_skip() { + bisect_autostart + case "$#" in + 0) revs=$(git rev-parse --verify HEAD) || exit ;; + *) revs=$(git rev-parse --revs-only --no-flags "$@") && + test '' != "$revs" || die "Bad rev input: $@" ;; + esac + for rev in $revs + do + rev=$(git rev-parse --verify "$rev^{commit}") || exit + bisect_write_skip "$rev" + echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" + done + bisect_auto_next +} + +bisect_write_skip() { + rev="$1" + echo "$rev" >"$GIT_DIR/refs/bisect/skip-$rev" + echo "# skip: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + bisect_next_check() { missing_good= missing_bad= git show-ref -q --verify refs/bisect/bad || missing_bad=t @@ -205,17 +229,97 @@ bisect_auto_next() { bisect_next_check && bisect_next || : } +filter_skipped() { + _eval="$1" + _skip="$2" + + if [ -z "$_skip" ]; then + eval $_eval + return + fi + + # Let's parse the output of: + # "git rev-list --bisect-vars --bisect-all ..." + eval $_eval | while read hash line + do + case "$VARS,$FOUND,$TRIED,$hash" in + # We display some vars. + 1,*,*,*) echo "$hash $line" ;; + + # Split line. + ,*,*,---*) ;; + + # We had nothing to search. + ,,,bisect_rev*) + echo "bisect_rev=" + VARS=1 + ;; + + # We did not find a good bisect rev. + # This should happen only if the "bad" + # commit is also a "skip" commit. + ,,*,bisect_rev*) + echo "bisect_rev=$TRIED" + VARS=1 + ;; + + # We are searching. + ,,*,*) + TRIED="${TRIED:+$TRIED|}$hash" + case "$_skip" in + *$hash*) ;; + *) + echo "bisect_rev=$hash" + echo "bisect_tried=\"$TRIED\"" + FOUND=1 + ;; + esac + ;; + + # We have already found a rev to be tested. + ,1,*,bisect_rev*) VARS=1 ;; + ,1,*,*) ;; + + # ??? + *) die "filter_skipped error " \ + "VARS: '$VARS' " \ + "FOUND: '$FOUND' " \ + "TRIED: '$TRIED' " \ + "hash: '$hash' " \ + "line: '$line'" + ;; + esac + done +} + +exit_if_skipped_commits () { + _tried=$1 + if expr "$_tried" : ".*[|].*" > /dev/null ; then + echo "There are only 'skip'ped commit left to test." + echo "The first bad commit could be any of:" + echo "$_tried" | sed -e 's/[|]/\n/g' + echo "We cannot bisect more!" + exit 2 + fi +} + bisect_next() { case "$#" in 0) ;; *) usage ;; esac bisect_autostart bisect_next_check good + skip=$(git for-each-ref --format='%(objectname)' \ + "refs/bisect/skip-*" | tr '[\012]' ' ') || exit + + BISECT_OPT='' + test -n "$skip" && BISECT_OPT='--bisect-all' + bad=$(git rev-parse --verify refs/bisect/bad) && good=$(git for-each-ref --format='^%(objectname)' \ "refs/bisect/good-*" | tr '[\012]' ' ') && - eval="git rev-list --bisect-vars $good $bad --" && + eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" && eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" && - eval=$(eval "$eval") && + eval=$(filter_skipped "$eval" "$skip") && eval "$eval" || exit if [ -z "$bisect_rev" ]; then @@ -223,11 +327,16 @@ bisect_next() { exit 1 fi if [ "$bisect_rev" = "$bad" ]; then + exit_if_skipped_commits "$bisect_tried" echo "$bisect_rev is first bad commit" git diff-tree --pretty $bisect_rev exit 0 fi + # We should exit here only if the "bad" + # commit is also a "skip" commit (see above). + exit_if_skipped_commits "$bisect_rev" + echo "Bisecting: $bisect_nr revisions left to test after this" echo "$bisect_rev" >"$GIT_DIR/refs/heads/new-bisect" git checkout -q new-bisect || exit @@ -286,15 +395,17 @@ bisect_replay () { eval "$cmd" ;; good) - echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" - echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_good "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" ;; bad) - echo "$rev" >"$GIT_DIR/refs/bisect/bad" - echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + bisect_write_bad "$rev" echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" ;; + skip) + bisect_write_skip "$rev" + echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" + ;; *) echo >&2 "?? what are you talking about?" exit 1 ;; @@ -362,6 +473,8 @@ case "$#" in bisect_bad "$@" ;; good) bisect_good "$@" ;; + skip) + bisect_skip "$@" ;; next) # Not sure we want "next" at the UI level anymore. bisect_next "$@" ;; diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 03cdba5808..db82259afe 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -71,6 +71,63 @@ test_expect_success 'bisect start with one bad and good' ' git bisect next ' +# $HASH1 is good, $HASH4 is bad, we skip $HASH3 +# but $HASH2 is bad, +# so we should find $HASH2 as the first bad commit +test_expect_success 'bisect skip: successfull result' ' + git bisect reset && + git bisect start $HASH4 $HASH1 && + git bisect skip && + git bisect bad > my_bisect_log.txt && + grep "$HASH2 is first bad commit" my_bisect_log.txt && + git bisect reset +' + +# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2 +# so we should not be able to tell the first bad commit +# among $HASH2, $HASH3 and $HASH4 +test_expect_success 'bisect skip: cannot tell between 3 commits' ' + git bisect start $HASH4 $HASH1 && + git bisect skip || return 1 + + if git bisect skip > my_bisect_log.txt + then + echo Oops, should have failed. + false + else + test $? -eq 2 && + grep "first bad commit could be any of" my_bisect_log.txt && + ! grep $HASH1 my_bisect_log.txt && + grep $HASH2 my_bisect_log.txt && + grep $HASH3 my_bisect_log.txt && + grep $HASH4 my_bisect_log.txt && + git bisect reset + fi +' + +# $HASH1 is good, $HASH4 is bad, we skip $HASH3 +# but $HASH2 is good, +# so we should not be able to tell the first bad commit +# among $HASH3 and $HASH4 +test_expect_success 'bisect skip: cannot tell between 2 commits' ' + git bisect start $HASH4 $HASH1 && + git bisect skip || return 1 + + if git bisect good > my_bisect_log.txt + then + echo Oops, should have failed. + false + else + test $? -eq 2 && + grep "first bad commit could be any of" my_bisect_log.txt && + ! grep $HASH1 my_bisect_log.txt && + ! grep $HASH2 my_bisect_log.txt && + grep $HASH3 my_bisect_log.txt && + grep $HASH4 my_bisect_log.txt && + git bisect reset + fi +' + # We want to automatically find the commit that # introduced "Another" into hello. test_expect_success \ @@ -99,6 +156,20 @@ test_expect_success \ grep "$HASH4 is first bad commit" my_bisect_log.txt && git bisect reset' +# $HASH1 is good, $HASH5 is bad, we skip $HASH3 +# but $HASH4 is good, +# so we should find $HASH5 as the first bad commit +HASH5= +test_expect_success 'bisect skip: add line and then a new test' ' + add_line_into_file "5: Another new line." hello && + HASH5=$(git rev-parse --verify HEAD) && + git bisect start $HASH5 $HASH1 && + git bisect skip && + git bisect good > my_bisect_log.txt && + grep "$HASH5 is first bad commit" my_bisect_log.txt && + git bisect reset +' + # # test_done From 55624f9af49733ca6ae4abc3cd21e0f9a4f9f486 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 24 Oct 2007 07:01:05 +0200 Subject: [PATCH 072/123] Bisect: refactor "bisect_write_*" functions. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- git-bisect.sh | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index cd46190302..82aa40433b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -108,9 +108,9 @@ bisect_start() { } if [ $bad_seen -eq 0 ]; then bad_seen=1 - bisect_write_bad "$rev" + bisect_write 'bad' "$rev" else - bisect_write_good "$rev" + bisect_write 'good' "$rev" fi shift ;; @@ -122,6 +122,18 @@ bisect_start() { bisect_auto_next } +bisect_write() { + state="$1" + rev="$2" + case "$state" in + bad) tag="$state" ;; + good|skip) tag="$state"-"$rev" ;; + *) die "Bad bisect_write argument: $state" ;; + esac + echo "$rev" >"$GIT_DIR/refs/bisect/$tag" + echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" +} + bisect_bad() { bisect_autostart case "$#" in @@ -132,17 +144,11 @@ bisect_bad() { *) usage ;; esac || exit - bisect_write_bad "$rev" + bisect_write 'bad' "$rev" echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" bisect_auto_next } -bisect_write_bad() { - rev="$1" - echo "$rev" >"$GIT_DIR/refs/bisect/bad" - echo "# bad: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" -} - bisect_good() { bisect_autostart case "$#" in @@ -153,18 +159,12 @@ bisect_good() { for rev in $revs do rev=$(git rev-parse --verify "$rev^{commit}") || exit - bisect_write_good "$rev" + bisect_write 'good' "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" done bisect_auto_next } -bisect_write_good() { - rev="$1" - echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev" - echo "# good: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" -} - bisect_skip() { bisect_autostart case "$#" in @@ -175,18 +175,12 @@ bisect_skip() { for rev in $revs do rev=$(git rev-parse --verify "$rev^{commit}") || exit - bisect_write_skip "$rev" + bisect_write 'skip' "$rev" echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" done bisect_auto_next } -bisect_write_skip() { - rev="$1" - echo "$rev" >"$GIT_DIR/refs/bisect/skip-$rev" - echo "# skip: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" -} - bisect_next_check() { missing_good= missing_bad= git show-ref -q --verify refs/bisect/bad || missing_bad=t @@ -395,15 +389,15 @@ bisect_replay () { eval "$cmd" ;; good) - bisect_write_good "$rev" + bisect_write 'good' "$rev" echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" ;; bad) - bisect_write_bad "$rev" + bisect_write 'bad' "$rev" echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" ;; skip) - bisect_write_skip "$rev" + bisect_write 'skip' "$rev" echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" ;; *) From 737c74ee4219a81ebb616262579f63ce2a3f9698 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 24 Oct 2007 07:01:13 +0200 Subject: [PATCH 073/123] Bisect: refactor some logging into "bisect_write". Also use "die" instead of "echo >&2 something ; exit 1". And simplify "bisect_replay". Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- git-bisect.sh | 47 ++++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 82aa40433b..61a2956647 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -106,12 +106,11 @@ bisect_start() { die "'$arg' does not appear to be a valid revision" break } - if [ $bad_seen -eq 0 ]; then - bad_seen=1 - bisect_write 'bad' "$rev" - else - bisect_write 'good' "$rev" - fi + case $bad_seen in + 0) state='bad' ; bad_seen=1 ;; + *) state='good' ;; + esac + bisect_write "$state" "$rev" 'nolog' shift ;; esac @@ -125,6 +124,7 @@ bisect_start() { bisect_write() { state="$1" rev="$2" + nolog="$3" case "$state" in bad) tag="$state" ;; good|skip) tag="$state"-"$rev" ;; @@ -132,6 +132,7 @@ bisect_write() { esac echo "$rev" >"$GIT_DIR/refs/bisect/$tag" echo "# $state: "$(git show-branch $rev) >>"$GIT_DIR/BISECT_LOG" + test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } bisect_bad() { @@ -145,7 +146,6 @@ bisect_bad() { usage ;; esac || exit bisect_write 'bad' "$rev" - echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" bisect_auto_next } @@ -160,7 +160,6 @@ bisect_good() { do rev=$(git rev-parse --verify "$rev^{commit}") || exit bisect_write 'good' "$rev" - echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" done bisect_auto_next } @@ -176,7 +175,6 @@ bisect_skip() { do rev=$(git rev-parse --verify "$rev^{commit}") || exit bisect_write 'skip' "$rev" - echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" done bisect_auto_next } @@ -352,10 +350,8 @@ bisect_reset() { else branch=master fi ;; - 1) git show-ref --verify --quiet -- "refs/heads/$1" || { - echo >&2 "$1 does not seem to be a valid branch" - exit 1 - } + 1) git show-ref --verify --quiet -- "refs/heads/$1" || + die "$1 does not seem to be a valid branch" branch="$1" ;; *) usage ;; @@ -375,10 +371,7 @@ bisect_clean_state() { } bisect_replay () { - test -r "$1" || { - echo >&2 "cannot read $1 for replaying" - exit 1 - } + test -r "$1" || die "cannot read $1 for replaying" bisect_reset while read bisect command rev do @@ -386,23 +379,11 @@ bisect_replay () { case "$command" in start) cmd="bisect_start $rev" - eval "$cmd" - ;; - good) - bisect_write 'good' "$rev" - echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG" - ;; - bad) - bisect_write 'bad' "$rev" - echo "git-bisect bad $rev" >>"$GIT_DIR/BISECT_LOG" - ;; - skip) - bisect_write 'skip' "$rev" - echo "git-bisect skip $rev" >>"$GIT_DIR/BISECT_LOG" - ;; + eval "$cmd" ;; + good|bad|skip) + bisect_write "$command" "$rev" ;; *) - echo >&2 "?? what are you talking about?" - exit 1 ;; + die "?? what are you talking about?" ;; esac done <"$1" bisect_auto_next From 155fc795b9c613af79781ebec5fd8eda084e1636 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 24 Oct 2007 07:01:21 +0200 Subject: [PATCH 074/123] Bisect: refactor "bisect_{bad,good,skip}" into "bisect_state". Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- git-bisect.sh | 82 +++++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 61a2956647..f8d0099059 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -135,47 +135,33 @@ bisect_write() { test -z "$nolog" && echo "git-bisect $state $rev" >>"$GIT_DIR/BISECT_LOG" } -bisect_bad() { +bisect_state() { bisect_autostart - case "$#" in - 0) - rev=$(git rev-parse --verify HEAD) ;; - 1) - rev=$(git rev-parse --verify "$1^{commit}") ;; + state=$1 + case "$#,$state" in + 0,*) + die "Please call 'bisect_state' with at least one argument." ;; + 1,bad|1,good|1,skip) + rev=$(git rev-parse --verify HEAD) || + die "Bad rev input: HEAD" + bisect_write "$state" "$rev" ;; + 2,bad) + rev=$(git rev-parse --verify "$2^{commit}") || + die "Bad rev input: $2" + bisect_write "$state" "$rev" ;; + *,good|*,skip) + shift + revs=$(git rev-parse --revs-only --no-flags "$@") && + test '' != "$revs" || die "Bad rev input: $@" + for rev in $revs + do + rev=$(git rev-parse --verify "$rev^{commit}") || + die "Bad rev commit: $rev^{commit}" + bisect_write "$state" "$rev" + done ;; *) usage ;; - esac || exit - bisect_write 'bad' "$rev" - bisect_auto_next -} - -bisect_good() { - bisect_autostart - case "$#" in - 0) revs=$(git rev-parse --verify HEAD) || exit ;; - *) revs=$(git rev-parse --revs-only --no-flags "$@") && - test '' != "$revs" || die "Bad rev input: $@" ;; esac - for rev in $revs - do - rev=$(git rev-parse --verify "$rev^{commit}") || exit - bisect_write 'good' "$rev" - done - bisect_auto_next -} - -bisect_skip() { - bisect_autostart - case "$#" in - 0) revs=$(git rev-parse --verify HEAD) || exit ;; - *) revs=$(git rev-parse --revs-only --no-flags "$@") && - test '' != "$revs" || die "Bad rev input: $@" ;; - esac - for rev in $revs - do - rev=$(git rev-parse --verify "$rev^{commit}") || exit - bisect_write 'skip' "$rev" - done bisect_auto_next } @@ -405,24 +391,22 @@ bisect_run () { exit $res fi - # Use "bisect_good" or "bisect_bad" - # depending on run success or failure. + # Find current state depending on run success or failure. if [ $res -gt 0 ]; then - next_bisect='bisect_bad' + state='bad' else - next_bisect='bisect_good' + state='good' fi - # We have to use a subshell because bisect_good or - # bisect_bad functions can exit. - ( $next_bisect > "$GIT_DIR/BISECT_RUN" ) + # We have to use a subshell because "bisect_state" can exit. + ( bisect_state $state > "$GIT_DIR/BISECT_RUN" ) res=$? cat "$GIT_DIR/BISECT_RUN" if [ $res -ne 0 ]; then echo >&2 "bisect run failed:" - echo >&2 "$next_bisect exited with error code $res" + echo >&2 "'bisect_state $state' exited with error code $res" exit $res fi @@ -444,12 +428,8 @@ case "$#" in case "$cmd" in start) bisect_start "$@" ;; - bad) - bisect_bad "$@" ;; - good) - bisect_good "$@" ;; - skip) - bisect_skip "$@" ;; + bad|good|skip) + bisect_state "$cmd" "$@" ;; next) # Not sure we want "next" at the UI level anymore. bisect_next "$@" ;; From c39ce91827e6f8c9e2cbfb482f90fe6f58d4d573 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:49:23 +0200 Subject: [PATCH 075/123] Bisect: add "bisect skip" to the documentation. Also fix "bisect bad" and "bisect good" short usage description. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-bisect.txt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 1072fb87d1..785f381423 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -16,8 +16,9 @@ The command takes various subcommands, and different options depending on the subcommand: git bisect start [ [...]] [--] [...] - git bisect bad - git bisect good + git bisect bad [] + git bisect good [...] + git bisect skip [...] git bisect reset [] git bisect visualize git bisect replay @@ -134,6 +135,20 @@ $ git reset --hard HEAD~3 # try 3 revs before what Then compile and test the one you chose to try. After that, tell bisect what the result was as usual. +Bisect skip +~~~~~~~~~~~~ + +Instead of choosing by yourself a nearby commit, you may just want git +to do it for you using: + +------------ +$ git bisect skip # Current version cannot be tested +------------ + +But computing the commit to test may be slower afterwards and git may +eventually not be able to tell the first bad among a bad and one or +more "skip"ped commits. + Cutting down bisection by giving more parameters to bisect start ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 37f9fd0dde454db08d342e5ce7625e012507bb9b Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 22 Oct 2007 07:49:39 +0200 Subject: [PATCH 076/123] Bisect: add a "bisect replay" test case. Signed-off-by: Christian Couder Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- t/t6030-bisect-porcelain.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index db82259afe..16d0c4a90e 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -167,6 +167,13 @@ test_expect_success 'bisect skip: add line and then a new test' ' git bisect skip && git bisect good > my_bisect_log.txt && grep "$HASH5 is first bad commit" my_bisect_log.txt && + git bisect log > log_to_replay.txt + git bisect reset +' + +test_expect_success 'bisect skip and bisect replay' ' + git bisect replay log_to_replay.txt > my_bisect_log.txt && + grep "$HASH5 is first bad commit" my_bisect_log.txt && git bisect reset ' From 71b0251cdd2cc35a983e21d4e71285db56d2a519 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Fri, 26 Oct 2007 05:39:37 +0200 Subject: [PATCH 077/123] Bisect run: "skip" current commit if script exit code is 125. This is incompatible with previous versions because an exit code of 125 used to mark current commit as "bad". But hopefully this exit code is not much used by test scripts or other programs. (126 and 127 are used by POSIX compliant shells to mean "found but not executable" and "command not found", respectively.) Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- Documentation/git-bisect.txt | 10 ++++++--- git-bisect.sh | 11 +++++++++- t/t6030-bisect-porcelain.sh | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 785f381423..4795349c10 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -182,14 +182,18 @@ $ git bisect run my_script ------------ Note that the "run" script (`my_script` in the above example) should -exit with code 0 in case the current source code is good and with a -code between 1 and 127 (included) in case the current source code is -bad. +exit with code 0 in case the current source code is good. Exit with a +code between 1 and 127 (inclusive), except 125, if the current +source code is bad. Any other exit code will abort the automatic bisect process. (A program that does "exit(-1)" leaves $? = 255, see exit(3) manual page, the value is chopped with "& 0377".) +The special exit code 125 should be used when the current source code +cannot be tested. If the "run" script exits with this code, the current +revision will be skipped, see `git bisect skip` above. + You may often find that during bisect you want to have near-constant tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or "revision that does not have this commit needs this patch applied to diff --git a/git-bisect.sh b/git-bisect.sh index f8d0099059..180c6c280c 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -392,7 +392,10 @@ bisect_run () { fi # Find current state depending on run success or failure. - if [ $res -gt 0 ]; then + # A special exit code of 125 means cannot test. + if [ $res -eq 125 ]; then + state='skip' + elif [ $res -gt 0 ]; then state='bad' else state='good' @@ -404,6 +407,12 @@ bisect_run () { cat "$GIT_DIR/BISECT_RUN" + if grep "first bad commit could be any of" "$GIT_DIR/BISECT_RUN" \ + > /dev/null; then + echo >&2 "bisect run cannot continue any more" + exit $res + fi + if [ $res -ne 0 ]; then echo >&2 "bisect run failed:" echo >&2 "'bisect_state $state' exited with error code $res" diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 16d0c4a90e..53956c08e2 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -177,6 +177,46 @@ test_expect_success 'bisect skip and bisect replay' ' git bisect reset ' +HASH6= +test_expect_success 'bisect run & skip: cannot tell between 2' ' + add_line_into_file "6: Yet a line." hello && + HASH6=$(git rev-parse --verify HEAD) && + echo "#"\!"/bin/sh" > test_script.sh && + echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh && + echo "grep line hello > /dev/null" >> test_script.sh && + echo "test \$? -ne 0" >> test_script.sh && + chmod +x test_script.sh && + git bisect start $HASH6 $HASH1 && + if git bisect run ./test_script.sh > my_bisect_log.txt + then + echo Oops, should have failed. + false + else + test $? -eq 2 && + grep "first bad commit could be any of" my_bisect_log.txt && + ! grep $HASH3 my_bisect_log.txt && + ! grep $HASH6 my_bisect_log.txt && + grep $HASH4 my_bisect_log.txt && + grep $HASH5 my_bisect_log.txt + fi +' + +HASH7= +test_expect_success 'bisect run & skip: find first bad' ' + git bisect reset && + add_line_into_file "7: Should be the last line." hello && + HASH7=$(git rev-parse --verify HEAD) && + echo "#"\!"/bin/sh" > test_script.sh && + echo "tail -1 hello | grep Ciao > /dev/null && exit 125" >> test_script.sh && + echo "tail -1 hello | grep day > /dev/null && exit 125" >> test_script.sh && + echo "grep Yet hello > /dev/null" >> test_script.sh && + echo "test \$? -ne 0" >> test_script.sh && + chmod +x test_script.sh && + git bisect start $HASH7 $HASH1 && + git bisect run ./test_script.sh > my_bisect_log.txt && + grep "$HASH6 is first bad commit" my_bisect_log.txt +' + # # test_done From cca5d946d692fde7ea5408a694cb4b1c97a5a838 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 27 Oct 2007 21:16:56 +1000 Subject: [PATCH 078/123] gitk: Simplify the code for finding commits This unifies findmore and findmorerev, and adds the ability to do a search with or without wrap around from the end of the list of commits to the beginning (or vice versa for reverse searches). findnext and findprev are gone, and the buttons and keys for searching all call dofind now. dofind doesn't unmark the matches to start with. Shift-up and shift-down are back by popular request, and the searches they do don't wrap around. The other keys that do searches (/, ?, return, M-f) do wrapping searches except for M-g. Signed-off-by: Paul Mackerras --- gitk | 212 ++++++++++++++++------------------------------------------- 1 file changed, 57 insertions(+), 155 deletions(-) diff --git a/gitk b/gitk index 135511e9fb..5230e3bb9d 100755 --- a/gitk +++ b/gitk @@ -764,8 +764,8 @@ proc makewindow {} { # build up the bottom bar of upper window label .tf.lbar.flabel -text "Find " -font uifont - button .tf.lbar.fnext -text "next" -command dofind -font uifont - button .tf.lbar.fprev -text "prev" -command {dofind 1} -font uifont + button .tf.lbar.fnext -text "next" -command {dofind 1 1} -font uifont + button .tf.lbar.fprev -text "prev" -command {dofind -1 1} -font uifont label .tf.lbar.flab2 -text " commit " -font uifont pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \ -side left -fill y @@ -959,6 +959,8 @@ proc makewindow {} { bindkey sellastline bind . "selnextline -1" bind . "selnextline 1" + bind . "dofind -1 0" + bind . "dofind 1 0" bindkey "goforw" bindkey "goback" bind . "selnextpage -1" @@ -983,14 +985,14 @@ proc makewindow {} { bindkey b "$ctext yview scroll -1 pages" bindkey d "$ctext yview scroll 18 units" bindkey u "$ctext yview scroll -18 units" - bindkey / {findnext 1} - bindkey {findnext 0} - bindkey ? findprev + bindkey / {dofind 1 1} + bindkey {dofind 1 1} + bindkey ? {dofind -1 1} bindkey f nextfile bindkey updatecommits bind . <$M1B-q> doquit - bind . <$M1B-f> dofind - bind . <$M1B-g> {findnext 0} + bind . <$M1B-f> {dofind 1 1} + bind . <$M1B-g> {dofind 1 0} bind . <$M1B-r> dosearchback bind . <$M1B-s> dosearch bind . <$M1B-equal> {incrfont 1} @@ -999,7 +1001,7 @@ proc makewindow {} { bind . <$M1B-KP_Subtract> {incrfont -1} wm protocol . WM_DELETE_WINDOW doquit bind . "click %W" - bind $fstring dofind + bind $fstring {dofind 1 1} bind $sha1entry gotocommit bind $sha1entry <> clearsha1 bind $cflist <1> {sel_flist %W %x %y; break} @@ -1325,8 +1327,8 @@ Gitk key bindings: <$M1T-Down> Scroll commit list down one line <$M1T-PageUp> Scroll commit list up one page <$M1T-PageDown> Scroll commit list down one page - Move to previous highlighted line - Move to next highlighted line + Find backwards (upwards, later commits) + Find forwards (downwards, earlier commits) , b Scroll diff view up one page Scroll diff view up one page Scroll diff view down one page @@ -2459,11 +2461,7 @@ proc readfhighlight {} { return 0 } if {[info exists find_dirn]} { - if {$find_dirn > 0} { - run findmore - } else { - run findmorerev - } + run findmore } return 1 } @@ -4247,15 +4245,18 @@ proc findmatches {f} { return $matches } -proc dofind {{rev 0}} { +proc dofind {{dirn 1} {wrap 1}} { global findstring findstartline findcurline selectedline numcommits - global gdttype filehighlight fh_serial find_dirn + global gdttype filehighlight fh_serial find_dirn findallowwrap - unmarkmatches + if {[info exists find_dirn]} { + if {$find_dirn == $dirn} return + stopfinding + } focus . if {$findstring eq {} || $numcommits == 0} return if {![info exists selectedline]} { - set findstartline [lindex [visiblerows] $rev] + set findstartline [lindex [visiblerows] [expr {$dirn < 0}]] } else { set findstartline $selectedline } @@ -4265,13 +4266,9 @@ proc dofind {{rev 0}} { after cancel do_file_hl $fh_serial do_file_hl $fh_serial } - if {!$rev} { - set find_dirn 1 - run findmore - } else { - set find_dirn -1 - run findmorerev - } + set find_dirn $dirn + set findallowwrap $wrap + run findmore } proc stopfinding {} { @@ -4286,61 +4283,50 @@ proc stopfinding {} { } } -proc findnext {restart} { - global findcurline find_dirn - - if {[info exists find_dirn]} return - if {![info exists findcurline]} { - if {$restart} { - dofind - } else { - bell - } - } else { - set find_dirn 1 - run findmore - nowbusy finding "Searching" - } -} - -proc findprev {} { - global findcurline find_dirn - - if {[info exists find_dirn]} return - if {![info exists findcurline]} { - dofind 1 - } else { - set find_dirn -1 - run findmorerev - nowbusy finding "Searching" - } -} - proc findmore {} { global commitdata commitinfo numcommits findpattern findloc global findstartline findcurline displayorder global find_dirn gdttype fhighlights fprogcoord + global findallowwrap if {![info exists find_dirn]} { return 0 } 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}] + set l $findcurline + set moretodo 0 + if {$find_dirn > 0} { + incr l + if {$l >= $numcommits} { + set l 0 + } + if {$l <= $findstartline} { + set lim [expr {$findstartline + 1}] + } else { + set lim $numcommits + set moretodo $findallowwrap + } } else { - set lim $numcommits + if {$l == 0} { + set l $numcommits + } + incr l -1 + if {$l >= $findstartline} { + set lim [expr {$findstartline - 1}] + } else { + set lim -1 + set moretodo $findallowwrap + } } - if {$lim - $l > 500} { - set lim [expr {$l + 500}] + set n [expr {($lim - $l) * $find_dirn}] + if {$n > 500} { + set n 500 + set moretodo 1 } set found 0 set domore 1 if {$gdttype eq "containing:"} { - for {} {$l < $lim} {incr l} { + for {} {$n > 0} {incr n -1; incr l $find_dirn} { set id [lindex $displayorder $l] # shouldn't happen unless git log doesn't give all the commits... if {![info exists commitdata($id)]} continue @@ -4359,13 +4345,13 @@ proc findmore {} { if {$found} break } } else { - for {} {$l < $lim} {incr l} { + for {} {$n > 0} {incr n -1; incr l $find_dirn} { set id [lindex $displayorder $l] if {![info exists fhighlights($l)]} { askfilehighlight $l $id if {$domore} { set domore 0 - set findcurline [expr {$l - 1}] + set findcurline [expr {$l - $find_dirn}] } } elseif {$fhighlights($l)} { set found $domore @@ -4373,7 +4359,7 @@ proc findmore {} { } } } - if {$found || ($domore && $l == $findstartline + 1)} { + if {$found || ($domore && !$moretodo)} { unset findcurline unset find_dirn notbusy finding @@ -4389,93 +4375,9 @@ proc findmore {} { if {!$domore} { flushhighlights } else { - set findcurline [expr {$l - 1}] + set findcurline [expr {$l - $find_dirn}] } - set n [expr {$findcurline - ($findstartline + 1)}] - if {$n < 0} { - incr n $numcommits - } - set fprogcoord [expr {$n * 1.0 / $numcommits}] - adjustprogress - return $domore -} - -proc findmorerev {} { - global commitdata commitinfo numcommits findpattern findloc - global findstartline findcurline displayorder - global find_dirn gdttype fhighlights fprogcoord - - if {![info exists find_dirn]} { - return 0 - } - 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 found 0 - set domore 1 - if {$gdttype eq "containing:"} { - for {} {$l > $lim} {incr l -1} { - set id [lindex $displayorder $l] - if {![info exists commitdata($id)]} continue - 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 found 1 - break - } - } - if {$found} break - } - } else { - for {} {$l > $lim} {incr l -1} { - set id [lindex $displayorder $l] - if {![info exists fhighlights($l)]} { - askfilehighlight $l $id - if {$domore} { - set domore 0 - set findcurline [expr {$l + 1}] - } - } elseif {$fhighlights($l)} { - set found $domore - break - } - } - } - if {$found || ($domore && $l == $findstartline - 1)} { - unset findcurline - unset find_dirn - notbusy finding - set fprogcoord 0 - adjustprogress - if {$found} { - findselectline $l - } else { - bell - } - return 0 - } - if {!$domore} { - flushhighlights - } else { - set findcurline [expr {$l + 1}] - } - set n [expr {($findstartline - 1) - $findcurline}] + set n [expr {($findcurline - $findstartline) * $find_dirn - 1}] if {$n < 0} { incr n $numcommits } From 7388bcbc5431552718dde5c3259d861d2fa75a12 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sat, 27 Oct 2007 21:31:07 +1000 Subject: [PATCH 079/123] gitk: Use the UI font for the diff/old version/new version radio buttons This makes the radio buttons for selecting whether to see the full diff, the old version or the new version use the same font as the other user interface elements. Signed-off-by: Paul Mackerras --- gitk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gitk b/gitk index 4efcbb7957..1da0b0af1d 100755 --- a/gitk +++ b/gitk @@ -830,11 +830,11 @@ proc makewindow {} { lappend entries $sstring trace add variable searchstring write incrsearch pack $sstring -side left -expand 1 -fill x - radiobutton .bleft.mid.diff -text "Diff" \ + radiobutton .bleft.mid.diff -text "Diff" -font uifont \ -command changediffdisp -variable diffelide -value {0 0} - radiobutton .bleft.mid.old -text "Old version" \ + radiobutton .bleft.mid.old -text "Old version" -font uifont \ -command changediffdisp -variable diffelide -value {0 1} - radiobutton .bleft.mid.new -text "New version" \ + radiobutton .bleft.mid.new -text "New version" -font uifont \ -command changediffdisp -variable diffelide -value {1 0} label .bleft.mid.labeldiffcontext -text " Lines of context: " \ -font uifont From 9ad7c5ae8ae4625bfe32d910b0b480cfea9819e0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 26 Oct 2007 23:09:48 -0700 Subject: [PATCH 080/123] git-fetch: do not fail when remote branch disappears When the branch named with branch.$name.merge is not covered by the fetch configuration for the remote repository named with branch.$name.remote, we automatically add that branch to the set of branches to be fetched. However, if the remote repository does not have that branch (e.g. it used to exist, but got removed), this is not a reason to fail the git-fetch itself. The situation however will be noticed if git-fetch was called by git-pull, as the resulting FETCH_HEAD would not have any entry that is marked for merging. Acked-By: Daniel Barkalow Signed-off-by: Junio C Hamano --- builtin-fetch.c | 24 ++++++++++++++++-------- remote.c | 20 ++++++++++++-------- remote.h | 5 ++++- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/builtin-fetch.c b/builtin-fetch.c index b9d2b0c27e..003ed76d16 100644 --- a/builtin-fetch.c +++ b/builtin-fetch.c @@ -48,15 +48,21 @@ static void add_merge_config(struct ref **head, if (rm) continue; - /* Not fetched to a tracking branch? We need to fetch + /* + * Not fetched to a tracking branch? We need to fetch * it anyway to allow this branch's "branch.$name.merge" - * to be honored by git-pull. + * to be honored by git-pull, but we do not have to + * fail if branch.$name.merge is misconfigured to point + * at a nonexisting branch. If we were indeed called by + * git-pull, it will notice the misconfiguration because + * there is no entry in the resulting FETCH_HEAD marked + * for merging. */ refspec.src = branch->merge[i]->src; refspec.dst = NULL; refspec.pattern = 0; refspec.force = 0; - get_fetch_map(remote_refs, &refspec, tail); + get_fetch_map(remote_refs, &refspec, tail, 1); for (rm = *old_tail; rm; rm = rm->next) rm->merge = 1; } @@ -75,7 +81,7 @@ static struct ref *get_ref_map(struct transport *transport, if (ref_count || tags) { for (i = 0; i < ref_count; i++) { - get_fetch_map(remote_refs, &refs[i], &tail); + get_fetch_map(remote_refs, &refs[i], &tail, 0); if (refs[i].dst && refs[i].dst[0]) *autotags = 1; } @@ -88,7 +94,7 @@ static struct ref *get_ref_map(struct transport *transport, refspec.dst = "refs/tags/"; refspec.pattern = 1; refspec.force = 0; - get_fetch_map(remote_refs, &refspec, &tail); + get_fetch_map(remote_refs, &refspec, &tail, 0); } } else { /* Use the defaults */ @@ -97,7 +103,7 @@ static struct ref *get_ref_map(struct transport *transport, int has_merge = branch_has_merge_config(branch); if (remote && (remote->fetch_refspec_nr || has_merge)) { for (i = 0; i < remote->fetch_refspec_nr; i++) { - get_fetch_map(remote_refs, &remote->fetch[i], &tail); + get_fetch_map(remote_refs, &remote->fetch[i], &tail, 0); if (remote->fetch[i].dst && remote->fetch[i].dst[0]) *autotags = 1; @@ -110,11 +116,13 @@ static struct ref *get_ref_map(struct transport *transport, * as given in branch..remote, we add the * ref given in branch..merge, too. */ - if (has_merge && !strcmp(branch->remote_name, - remote->name)) + if (has_merge && + !strcmp(branch->remote_name, remote->name)) add_merge_config(&ref_map, remote_refs, branch, &tail); } else { ref_map = get_remote_ref(remote_refs, "HEAD"); + if (!ref_map) + die("Couldn't find remote ref HEAD"); ref_map->merge = 1; } } diff --git a/remote.c b/remote.c index 170015aabf..bec2ba1adb 100644 --- a/remote.c +++ b/remote.c @@ -857,7 +857,7 @@ struct ref *get_remote_ref(struct ref *remote_refs, const char *name) struct ref *ref = find_ref_by_name_abbrev(remote_refs, name); if (!ref) - die("Couldn't find remote ref %s\n", name); + return NULL; return copy_ref(ref); } @@ -889,20 +889,24 @@ static struct ref *get_local_ref(const char *name) int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec, - struct ref ***tail) + struct ref ***tail, + int missing_ok) { struct ref *ref_map, *rm; if (refspec->pattern) { ref_map = get_expanded_map(remote_refs, refspec); } else { - ref_map = get_remote_ref(remote_refs, - refspec->src[0] ? - refspec->src : "HEAD"); + const char *name = refspec->src[0] ? refspec->src : "HEAD"; - ref_map->peer_ref = get_local_ref(refspec->dst); - if (ref_map->peer_ref && refspec->force) - ref_map->peer_ref->force = 1; + ref_map = get_remote_ref(remote_refs, name); + if (!missing_ok && !ref_map) + die("Couldn't find remote ref %s", name); + if (ref_map) { + ref_map->peer_ref = get_local_ref(refspec->dst); + if (ref_map->peer_ref && refspec->force) + ref_map->peer_ref->force = 1; + } } for (rm = ref_map; rm; rm = rm->next) { diff --git a/remote.h b/remote.h index c62636d78e..878b4ecc32 100644 --- a/remote.h +++ b/remote.h @@ -67,9 +67,12 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, * *tail is the pointer to the tail pointer of the list of results * beforehand, and will be set to the tail pointer of the list of * results afterward. + * + * missing_ok is usually false, but when we are adding branch.$name.merge + * it is Ok if the branch is not at the remote anymore. */ int get_fetch_map(struct ref *remote_refs, const struct refspec *refspec, - struct ref ***tail); + struct ref ***tail, int missing_ok); struct ref *get_remote_ref(struct ref *remote_refs, const char *name); From 3697c5f37a8b7b15d0a3be51d05147654a951115 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:11 +0100 Subject: [PATCH 081/123] git.el: Fix typo in "Reverted file" message. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4286d160a0..8cfbdd7be4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -955,7 +955,7 @@ Return the list of files that haven't been handled." (when modified (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) (git-update-status-files (append added modified) 'uptodate) - (git-success-message "Reverted" files)))) + (git-success-message "Reverted" (git-get-filenames files))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." From 6df023884b87ef140829d78b67fab90a7f9b1211 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:45 +0100 Subject: [PATCH 082/123] git.el: Fix typo in git-update-saved-file error handling. Spotted by Matthieu Lemerre. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 8cfbdd7be4..0e5091c1b7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1353,7 +1353,7 @@ Commands: "Update the corresponding git-status buffer when a file is saved. Meant to be used in `after-save-hook'." (let* ((file (expand-file-name buffer-file-name)) - (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)) (error nil))) (buffer (and dir (git-find-status-buffer dir)))) (when buffer (with-current-buffer buffer From 2f6e86a86fb9830c0c3205a317f6205f156cfacc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:06:27 +0100 Subject: [PATCH 083/123] git.el: Refresh only the changed file marks when marking/unmarking all. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 0e5091c1b7..e5ee8ce58b 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -842,7 +842,8 @@ Return the list of files that haven't been handled." "Mark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) t) t) git-status) + (ewoc-map (lambda (info) (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t))) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) @@ -850,7 +851,9 @@ Return the list of files that haven't been handled." "Unmark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) nil) t) git-status) + (ewoc-map (lambda (info) (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + t)) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) From d53a35020d380c199b010c9884ab15995f8e982b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:07:14 +0100 Subject: [PATCH 084/123] git.el: Run git-gc --auto after commits. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e5ee8ce58b..e147da0596 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -796,6 +796,7 @@ Return the list of files that haven't been handled." (with-current-buffer buffer (erase-buffer)) (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) From 1c1f79a1e44547d039e7bf94d9aaadd3fae007d1 Mon Sep 17 00:00:00 2001 From: Aurelien Bompard Date: Sun, 28 Oct 2007 18:47:30 +0100 Subject: [PATCH 085/123] honor the http.sslVerify option in shell scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aurélien Bompard Signed-off-by: Junio C Hamano --- git-clone.sh | 3 ++- git-ls-remote.sh | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/git-clone.sh b/git-clone.sh index 5e582fe247..0ea3c24f59 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -28,7 +28,8 @@ get_repo_base() { ) 2>/dev/null } -if [ -n "$GIT_SSL_NO_VERIFY" ]; then +if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then curl_extra_args="-k" fi diff --git a/git-ls-remote.sh b/git-ls-remote.sh index d56cf92ebf..fec70bbf88 100755 --- a/git-ls-remote.sh +++ b/git-ls-remote.sh @@ -54,9 +54,10 @@ tmpdir=$tmp-d case "$peek_repo" in http://* | https://* | ftp://* ) - if [ -n "$GIT_SSL_NO_VERIFY" ]; then - curl_extra_args="-k" - fi + if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then + curl_extra_args="-k" + fi if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ "`git config --bool http.noEPSV`" = true ]; then curl_extra_args="${curl_extra_args} --disable-epsv" From 399f0a8eedeec209c2eb97be6285331087234644 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Mon, 29 Oct 2007 08:00:33 +0100 Subject: [PATCH 086/123] Fix a small memory leak in builtin-add prune_directory and fill_directory allocated one byte per pathspec and never freed it. Signed-off-by: Benoit Sigoure Signed-off-by: Junio C Hamano --- builtin-add.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/builtin-add.c b/builtin-add.c index 3d8b8b4f89..373f87f9f2 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -44,6 +44,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p die("pathspec '%s' did not match any files", pathspec[i]); } + free(seen); } static void fill_directory(struct dir_struct *dir, const char **pathspec, @@ -140,6 +141,7 @@ static void refresh(int verbose, const char **pathspec) if (!seen[i]) die("pathspec '%s' did not match any files", pathspec[i]); } + free(seen); } static int git_add_config(const char *var, const char *value) From 3b27428b9d3d8dc1c3f00da0ab94a1c7555a9265 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Mon, 29 Oct 2007 08:00:32 +0100 Subject: [PATCH 087/123] core-tutorial: Catch up with current Git No longer talk about Cogito since it's deprecated. Some scripts (such as git-reset or git-branch) have undergone builtinification so adjust the text to reflect this. Fix a typo in the description of git-show-branch (merges are indicated by a `-', not by a `.'). git-pull/git-push do not seem to use the dumb git-ssh-fetch/git-ssh-upload (the text was probably missing a word). Adjust a link that wasn't rendered properly because it was wrapped. Signed-off-by: Benoit Sigoure Signed-off-by: Junio C Hamano --- Documentation/core-tutorial.txt | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 6b2590d072..d8e78ac8f1 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -553,13 +553,8 @@ can explore on your own. [NOTE] Most likely, you are not directly using the core -git Plumbing commands, but using Porcelain like Cogito on top -of it. Cogito works a bit differently and you usually do not -have to run `git-update-index` yourself for changed files (you -do tell underlying git about additions and removals via -`cg-add` and `cg-rm` commands). Just before you make a commit -with `cg-commit`, Cogito figures out which files you modified, -and runs `git-update-index` on them for you. +git Plumbing commands, but using Porcelain such as `git-add`, `git-rm' +and `git-commit'. Tagging a version @@ -686,8 +681,8 @@ $ git reset and in fact a lot of the common git command combinations can be scripted with the `git xyz` interfaces. You can learn things by just looking -at what the various git scripts do. For example, `git reset` is the -above two lines implemented in `git-reset`, but some things like +at what the various git scripts do. For example, `git reset` used to be +the above two lines implemented in `git-reset`, but some things like `git status` and `git commit` are slightly more complex scripts around the basic git commands. @@ -805,8 +800,8 @@ you have, you can say $ git branch ------------ -which is nothing more than a simple script around `ls .git/refs/heads`. -There will be asterisk in front of the branch you are currently on. +which used to be nothing more than a simple script around `ls .git/refs/heads`. +There will be an asterisk in front of the branch you are currently on. Sometimes you may wish to create a new branch _without_ actually checking it out and switching to it. If so, just use the command @@ -952,7 +947,7 @@ the later output lines is used to show commits contained in the `master` branch, and the second column for the `mybranch` branch. Three commits are shown along with their log messages. All of them have non blank characters in the first column (`*` -shows an ordinary commit on the current branch, `.` is a merge commit), which +shows an ordinary commit on the current branch, `-` is a merge commit), which means they are now part of the `master` branch. Only the "Some work" commit has the plus `+` character in the second column, because `mybranch` has not been merged to incorporate these @@ -1086,7 +1081,7 @@ to help dumb transport downloaders. There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload` programs, which are 'commit walkers'; they outlived their usefulness when git Native and SSH transports were introduced, -and not used by `git pull` or `git push` scripts. +and are not used by `git pull` or `git push` scripts. Once you fetch from the remote repository, you `merge` that with your current branch. @@ -1193,7 +1188,7 @@ $ mb=$(git-merge-base HEAD mybranch) The command writes the commit object name of the common ancestor to the standard output, so we captured its output to a variable, -because we will be using it in the next step. BTW, the common +because we will be using it in the next step. By the way, the common ancestor commit is the "New day." commit in this case. You can tell it by: @@ -1459,8 +1454,7 @@ Although git is a truly distributed system, it is often convenient to organize your project with an informal hierarchy of developers. Linux kernel development is run this way. There is a nice illustration (page 17, "Merges to Mainline") in -link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf -[Randy Dunlap's presentation]. +link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation]. It should be stressed that this hierarchy is purely *informal*. There is nothing fundamental in git that enforces the "chain of From 7109c889f11b39a2c5a5122e3726be7ffce09faf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 Oct 2007 11:53:55 -0700 Subject: [PATCH 088/123] sha1_file.c: avoid gcc signed overflow warnings With the recent gcc, we get: sha1_file.c: In check_packed_git_: sha1_file.c:527: warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false sha1_file.c:527: warning: assuming signed overflow does not occur when assuming that (X + c) < X is always false for a piece of code that tries to make sure that off_t is large enough to hold more than 2^32 offset. The test tried to make sure these do not wrap-around: /* make sure we can deal with large pack offsets */ off_t x = 0x7fffffffUL, y = 0xffffffffUL; if (x > (x + 1) || y > (y + 1)) { but gcc assumes it can do whatever optimization it wants for a signed overflow (undefined behaviour) and warns about this construct. Follow Linus's suggestion to check sizeof(off_t) instead to work around the problem. Signed-off-by: Junio C Hamano --- sha1_file.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sha1_file.c b/sha1_file.c index 9978a58da6..95b5a403d8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -521,13 +521,15 @@ static int check_packed_git_idx(const char *path, struct packed_git *p) munmap(idx_map, idx_size); return error("wrong index v2 file size in %s", path); } - if (idx_size != min_size) { - /* make sure we can deal with large pack offsets */ - off_t x = 0x7fffffffUL, y = 0xffffffffUL; - if (x > (x + 1) || y > (y + 1)) { - munmap(idx_map, idx_size); - return error("pack too large for current definition of off_t in %s", path); - } + if (idx_size != min_size && + /* + * make sure we can deal with large pack offsets. + * 31-bit signed offset won't be enough, neither + * 32-bit unsigned one will be. + */ + (sizeof(off_t) <= 4)) { + munmap(idx_map, idx_size); + return error("pack too large for current definition of off_t in %s", path); } } From f120ae2a8e8cbe1bcbb2b55ebcd2e1eeb9f03ea2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 Oct 2007 12:00:55 -0700 Subject: [PATCH 089/123] merge-recursive.c: mrtree in merge() is not used before set The called function merge_trees() sets its *result, to which the address of the variable mrtree in merge() function is passed, only when index_only is set. But that is Ok as the function uses the value in the variable only under index_only iteration. However, recent gcc does not realize this. Work it around by adding a fake initializer. Signed-off-by: Junio C Hamano --- merge-recursive.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/merge-recursive.c b/merge-recursive.c index 19d5f3b287..c2e1cb69e3 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1586,7 +1586,7 @@ static int merge(struct commit *h1, { struct commit_list *iter; struct commit *merged_common_ancestors; - struct tree *mrtree; + struct tree *mrtree = mrtree; int clean; if (show(4)) { From e720c4382f6c9317a3b495e80c7dfc609a0db5e6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 Oct 2007 12:02:59 -0700 Subject: [PATCH 090/123] RelNotes-1.5.3.5: describe recent fixes Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index 9581e03c49..e28d92f618 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -71,3 +71,24 @@ Fixes since v1.5.3.4 * "make clean" no longer deletes the configure script that ships with the git tarball, making multiple architecture builds easier. + + * "git-remote show origin" spewed a warning message from Perl + when no remote is defined for the current branch via + branch..remote configuration settings. + + * Building with NO_PERL_MAKEMAKER excessively rebuilt contents + of perl/ subdirectory by rewriting perl.mak. + + * http.sslVerify configuration settings were not used in scripted + Porcelains. + + * "git-add" leaked a bit of memory while scanning for files to add. + + * A few workarounds to squelch false warnings from recent gcc have + been added. + +-- +exec >/var/tmp/1 +O=v1.5.3.4-55-gf120ae2 +echo O=`git describe refs/heads/maint` +git shortlog --no-merges $O..refs/heads/maint From 8371d8fd094548c1d02b8583fdbff8204a725ffc Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sat, 27 Oct 2007 11:53:29 -0500 Subject: [PATCH 091/123] Remove outdated references to cogito in documentation Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- Documentation/git-tools.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/git-tools.txt b/Documentation/git-tools.txt index 10653ff898..a96403cb8c 100644 --- a/Documentation/git-tools.txt +++ b/Documentation/git-tools.txt @@ -22,6 +22,9 @@ Alternative/Augmentative Porcelains providing generally smoother user experience than the "raw" Core GIT itself and indeed many other version control systems. + Cogito is no longer maintained as most of its functionality + is now in core GIT. + - *pg* (http://www.spearce.org/category/projects/scm/pg/) @@ -33,7 +36,7 @@ Alternative/Augmentative Porcelains - *StGit* (http://www.procode.org/stgit/) Stacked GIT provides a quilt-like patch management functionality in the - GIT environment. You can easily manage your patches in the scope of GIT + GIT environment. You can easily manage your patches in the scope of GIT until they get merged upstream. From 6ca8b977e4f678050db8fcb0eec2091dd44a2bd0 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Mon, 29 Oct 2007 05:31:52 +0100 Subject: [PATCH 092/123] Bisect: add "skip" to the short usage string. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- git-bisect.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git-bisect.sh b/git-bisect.sh index 180c6c280c..b74f44df60 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,12 +1,14 @@ #!/bin/sh -USAGE='[start|bad|good|next|reset|visualize|replay|log|run]' +USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]' LONG_USAGE='git bisect start [ [...]] [--] [...] reset bisect state and start bisection. git bisect bad [] mark a known-bad revision. git bisect good [...] mark ... known-good revisions. +git bisect skip [...] + mark ... untestable revisions. git bisect next find next bisection to test and check it out. git bisect reset [] @@ -17,8 +19,6 @@ git bisect replay replay bisection log. git bisect log show bisect log. -git bisect skip [...] - mark ... untestable revisions. git bisect run ... use ... to automatically bisect.' From 5072a32382fb65fa7295811575d0f5c09cbe0dc3 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Mon, 29 Oct 2007 09:41:18 +0100 Subject: [PATCH 093/123] Teach git-pull about --[no-]ff, --no-squash and --commit These options are supported by git-merge, but git-pull didn't know about them. Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- git-pull.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/git-pull.sh b/git-pull.sh index 74bfc16744..75ec011969 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -4,7 +4,7 @@ # # Fetch one or more remote refs and merge it/them into the current HEAD. -USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [] ...' +USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [] ...' LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.' SUBDIRECTORY_OK=Yes . git-sh-setup @@ -15,7 +15,7 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." -strategy_args= no_summary= no_commit= squash= +strategy_args= no_summary= no_commit= squash= no_ff= while : do case "$1" in @@ -27,8 +27,16 @@ do ;; --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit) no_commit=--no-commit ;; + --c|--co|--com|--comm|--commi|--commit) + no_commit=--commit ;; --sq|--squ|--squa|--squas|--squash) squash=--squash ;; + --no-sq|--no-squ|--no-squa|--no-squas|--no-squash) + squash=--no-squash ;; + --ff) + no_ff=--ff ;; + --no-ff) + no_ff=--no-ff ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) @@ -133,5 +141,5 @@ then fi merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit -exec git-merge $no_summary $no_commit $squash $strategy_args \ +exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \ "$merge_name" HEAD $merge_head From 79f3368d9a1bba022f38806b67c25ffffc0e7e92 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 29 Oct 2007 13:09:01 -0700 Subject: [PATCH 094/123] RelNotes-1.5.4: describe recent updates Signed-off-by: Junio C Hamano --- .mailmap | 1 + Documentation/RelNotes-1.5.4.txt | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 5529b198e8..3b2ce578a1 100644 --- a/.mailmap +++ b/.mailmap @@ -37,6 +37,7 @@ Sam Vilain Santi Béjar Sean Estabrooks Shawn O. Pearce +Steven Grimm Theodore Ts'o Tony Luck Uwe Kleine-König diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt index ceee857232..133fa64d22 100644 --- a/Documentation/RelNotes-1.5.4.txt +++ b/Documentation/RelNotes-1.5.4.txt @@ -4,6 +4,8 @@ GIT v1.5.4 Release Notes Updates since v1.5.3 -------------------- + * Comes with much improved gitk. + * git-reset is now built-in. * git-send-email can optionally talk over ssmtp and use SMTP-AUTH. @@ -19,6 +21,29 @@ Updates since v1.5.3 * git-archive can optionally substitute keywords in files marked with export-subst attribute. + * git-for-each-ref learned %(xxxdate:) syntax to + show the various date fields in different formats. + + * git-gc --auto is a low-impact way to automatically run a + variant of git-repack that does not lose unreferenced objects + (read: safer than the usual one) after the user accumulates + too many loose objects. + + * git-push has been rewritten in C. + + * git-push learned --dry-run option to show what would happen + if a push is run. + + * git-remote learned "rm" subcommand. + + * git-rebase --interactive mode can now work on detached HEAD. + + * git-cvsserver can be run via git-shell. + + * git-am and git-rebase are far less verbose. + + * git-pull learned to pass --[no-]ff option to underlying git-merge. + * Various Perforce importer updates. Fixes since v1.5.3 @@ -29,7 +54,6 @@ this release, unless otherwise noted. -- exec >/var/tmp/1 -O=v1.5.3.2-99-ge4b2890 +O=v1.5.3.4-450-g952a9e5 echo O=`git describe refs/heads/master` git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint - From 68492fc73b7387fa72d09545f5743728c5fdb419 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Sun, 28 Oct 2007 21:27:13 +0100 Subject: [PATCH 095/123] Speedup scanning for excluded files. Try to avoid a lot of work scanning for excluded files, by caching some more information when setting up the exclusion data structure. Speeds up 'git runstatus' on a repository containing the Qt sources by 30% and reduces the amount of instructions executed (as measured by valgrind) by a factor of 2. Signed-off-by: Junio C Hamano --- dir.c | 59 +++++++++++++++++++++++++++++++++++++++++++---------------- dir.h | 7 +++++++ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/dir.c b/dir.c index 4c17d3643e..5bcc764c97 100644 --- a/dir.c +++ b/dir.c @@ -118,14 +118,32 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre return retval; } +static int no_wildcard(const char *string) +{ + return string[strcspn(string, "*?[{")] == '\0'; +} + void add_exclude(const char *string, const char *base, int baselen, struct exclude_list *which) { struct exclude *x = xmalloc(sizeof (*x)); + x->to_exclude = 1; + if (*string == '!') { + x->to_exclude = 0; + string++; + } x->pattern = string; + x->patternlen = strlen(string); x->base = base; x->baselen = baselen; + x->flags = 0; + if (!strchr(string, '/')) + x->flags |= EXC_FLAG_NODIR; + if (no_wildcard(string)) + x->flags |= EXC_FLAG_NOWILDCARD; + if (*string == '*' && no_wildcard(string+1)) + x->flags |= EXC_FLAG_ENDSWITH; if (which->nr == which->alloc) { which->alloc = alloc_nr(which->alloc); which->excludes = xrealloc(which->excludes, @@ -209,7 +227,7 @@ void pop_exclude_per_directory(struct dir_struct *dir, int stk) * Return 1 for exclude, 0 for include and -1 for undecided. */ static int excluded_1(const char *pathname, - int pathlen, + int pathlen, const char *basename, struct exclude_list *el) { int i; @@ -218,19 +236,21 @@ static int excluded_1(const char *pathname, for (i = el->nr - 1; 0 <= i; i--) { struct exclude *x = el->excludes[i]; const char *exclude = x->pattern; - int to_exclude = 1; + int to_exclude = x->to_exclude; - if (*exclude == '!') { - to_exclude = 0; - exclude++; - } - - if (!strchr(exclude, '/')) { + if (x->flags & EXC_FLAG_NODIR) { /* match basename */ - const char *basename = strrchr(pathname, '/'); - basename = (basename) ? basename+1 : pathname; - if (fnmatch(exclude, basename, 0) == 0) - return to_exclude; + if (x->flags & EXC_FLAG_NOWILDCARD) { + if (!strcmp(exclude, basename)) + return to_exclude; + } else if (x->flags & EXC_FLAG_ENDSWITH) { + if (x->patternlen - 1 <= pathlen && + !strcmp(exclude + 1, pathname + pathlen - x->patternlen + 1)) + return to_exclude; + } else { + if (fnmatch(exclude, basename, 0) == 0) + return to_exclude; + } } else { /* match with FNM_PATHNAME: @@ -246,9 +266,14 @@ static int excluded_1(const char *pathname, strncmp(pathname, x->base, baselen)) continue; - if (fnmatch(exclude, pathname+baselen, - FNM_PATHNAME) == 0) - return to_exclude; + if (x->flags & EXC_FLAG_NOWILDCARD) { + if (!strcmp(exclude, pathname + baselen)) + return to_exclude; + } else { + if (fnmatch(exclude, pathname+baselen, + FNM_PATHNAME) == 0) + return to_exclude; + } } } } @@ -259,9 +284,11 @@ int excluded(struct dir_struct *dir, const char *pathname) { int pathlen = strlen(pathname); int st; + const char *basename = strrchr(pathname, '/'); + basename = (basename) ? basename+1 : pathname; for (st = EXC_CMDL; st <= EXC_FILE; st++) { - switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) { + switch (excluded_1(pathname, pathlen, basename, &dir->exclude_list[st])) { case 0: return 0; case 1: diff --git a/dir.h b/dir.h index a248a23ac4..aaa247b256 100644 --- a/dir.h +++ b/dir.h @@ -17,13 +17,20 @@ struct dir_entry { char name[FLEX_ARRAY]; /* more */ }; +#define EXC_FLAG_NODIR 1 +#define EXC_FLAG_NOWILDCARD 2 +#define EXC_FLAG_ENDSWITH 4 + struct exclude_list { int nr; int alloc; struct exclude { const char *pattern; + int patternlen; const char *base; int baselen; + int to_exclude; + int flags; } **excludes; }; From 09149c7809a37a52b38d8d7a0621e2fb8943d8fe Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 29 Oct 2007 22:35:08 -0400 Subject: [PATCH 096/123] Correct handling of upload-pack in builtin-fetch-pack The field in the args was being ignored in favor of a static constant Signed-off-by: Daniel Barkalow Thanked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- builtin-fetch-pack.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 8f25d509a0..8753840a82 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -11,11 +11,12 @@ static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; static int unpack_limit = 100; -static struct fetch_pack_args args; +static struct fetch_pack_args args = { + /* .uploadpack = */ "git-upload-pack", +}; static const char fetch_pack_usage[] = "git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=] [--depth=] [--no-progress] [-v] [:] [...]"; -static const char *uploadpack = "git-upload-pack"; #define COMPLETE (1U << 0) #define COMMON (1U << 1) @@ -773,7 +774,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, st.st_mtime = 0; } - pid = git_connect(fd, (char *)dest, uploadpack, + pid = git_connect(fd, (char *)dest, args.uploadpack, args.verbose ? CONNECT_VERBOSE : 0); if (pid < 0) return NULL; From 3d7e2d857a570b1d6368285f95b67994fc95629e Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sat, 27 Oct 2007 01:36:49 -0700 Subject: [PATCH 097/123] "git" returns 1; "git help" and "git help -a" return 0 Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- builtin.h | 1 + git.c | 7 ++++--- help.c | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/builtin.h b/builtin.h index 65cc0fb34a..9a6213af12 100644 --- a/builtin.h +++ b/builtin.h @@ -6,6 +6,7 @@ extern const char git_version_string[]; extern const char git_usage_string[]; +extern void list_common_cmds_help(void); extern void help_unknown_cmd(const char *cmd); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); diff --git a/git.c b/git.c index 23a430c369..efed91c4f4 100644 --- a/git.c +++ b/git.c @@ -450,9 +450,10 @@ int main(int argc, const char **argv) if (!prefixcmp(argv[0], "--")) argv[0] += 2; } else { - /* Default command: "help" */ - argv[0] = "help"; - argc = 1; + /* The user didn't specify a command; give them help */ + printf("usage: %s\n\n", git_usage_string); + list_common_cmds_help(); + exit(1); } cmd = argv[0]; diff --git a/help.c b/help.c index e5662d9014..daefa75ea0 100644 --- a/help.c +++ b/help.c @@ -147,7 +147,7 @@ static void list_commands(const char *exec_path, const char *pattern) putchar('\n'); } -static void list_common_cmds_help(void) +void list_common_cmds_help(void) { int i, longest = 0; @@ -203,14 +203,14 @@ int cmd_help(int argc, const char **argv, const char *prefix) if (!help_cmd) { printf("usage: %s\n\n", git_usage_string); list_common_cmds_help(); - exit(1); + exit(0); } else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) { printf("usage: %s\n\n", git_usage_string); if(exec_path) list_commands(exec_path, "git-*"); - exit(1); + exit(0); } else From edb6ddc53e71f4f959665cbc3180bb21b7ee7634 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sat, 27 Oct 2007 01:36:50 -0700 Subject: [PATCH 098/123] remove unused/unneeded "pattern" argument of list_commands list_commands() currently accepts and ignores a "pattern" argument, and then hard codes a prefix as well as some magic numbers. This hardcodes the prefix inside of the function and removes the magic numbers. Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- help.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/help.c b/help.c index daefa75ea0..3d08973c9d 100644 --- a/help.c +++ b/help.c @@ -93,10 +93,12 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) } } -static void list_commands(const char *exec_path, const char *pattern) +static void list_commands(const char *exec_path) { unsigned int longest = 0; char path[PATH_MAX]; + const char *prefix = "git-"; + int prefix_len = strlen(prefix); int dirlen; DIR *dir = opendir(exec_path); struct dirent *de; @@ -120,7 +122,7 @@ static void list_commands(const char *exec_path, const char *pattern) struct stat st; int entlen; - if (prefixcmp(de->d_name, "git-")) + if (prefixcmp(de->d_name, prefix)) continue; strcpy(path+dirlen, de->d_name); if (stat(path, &st) || /* stat, not lstat */ @@ -128,14 +130,14 @@ static void list_commands(const char *exec_path, const char *pattern) !(st.st_mode & S_IXUSR)) continue; - entlen = strlen(de->d_name); + entlen = strlen(de->d_name) - prefix_len; if (has_extension(de->d_name, ".exe")) entlen -= 4; if (longest < entlen) longest = entlen; - add_cmdname(de->d_name + 4, entlen-4); + add_cmdname(de->d_name + prefix_len, entlen); } closedir(dir); @@ -143,7 +145,7 @@ static void list_commands(const char *exec_path, const char *pattern) printf("----------------------------"); mput_char('-', strlen(exec_path)); putchar('\n'); - pretty_print_string_list(cmdname, longest - 4); + pretty_print_string_list(cmdname, longest); putchar('\n'); } @@ -209,7 +211,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) { printf("usage: %s\n\n", git_usage_string); if(exec_path) - list_commands(exec_path, "git-*"); + list_commands(exec_path); exit(0); } From 384df83312d24ea4d11adcf4e73921fc192595d2 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sat, 27 Oct 2007 01:36:51 -0700 Subject: [PATCH 099/123] "current_exec_path" is a misleading name, use "argv_exec_path" Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- exec_cmd.c | 12 ++++++------ exec_cmd.h | 2 +- git.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/exec_cmd.c b/exec_cmd.c index 9b74ed2f42..33b17a6b45 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -5,11 +5,11 @@ extern char **environ; static const char *builtin_exec_path = GIT_EXEC_PATH; -static const char *current_exec_path; +static const char *argv_exec_path; -void git_set_exec_path(const char *exec_path) +void git_set_argv_exec_path(const char *exec_path) { - current_exec_path = exec_path; + argv_exec_path = exec_path; } @@ -18,8 +18,8 @@ const char *git_exec_path(void) { const char *env; - if (current_exec_path) - return current_exec_path; + if (argv_exec_path) + return argv_exec_path; env = getenv(EXEC_PATH_ENVIRONMENT); if (env && *env) { @@ -34,7 +34,7 @@ int execv_git_cmd(const char **argv) { char git_command[PATH_MAX + 1]; int i; - const char *paths[] = { current_exec_path, + const char *paths[] = { argv_exec_path, getenv(EXEC_PATH_ENVIRONMENT), builtin_exec_path }; diff --git a/exec_cmd.h b/exec_cmd.h index 849a8395a0..da99287552 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -1,7 +1,7 @@ #ifndef GIT_EXEC_CMD_H #define GIT_EXEC_CMD_H -extern void git_set_exec_path(const char *exec_path); +extern void git_set_argv_exec_path(const char *exec_path); extern const char* git_exec_path(void); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); diff --git a/git.c b/git.c index efed91c4f4..c7cabf5f34 100644 --- a/git.c +++ b/git.c @@ -51,7 +51,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) if (!prefixcmp(cmd, "--exec-path")) { cmd += 11; if (*cmd == '=') - git_set_exec_path(cmd + 1); + git_set_argv_exec_path(cmd + 1); else { puts(git_exec_path()); exit(0); From 0966003c8e8d1528912b10667b903cd981e3a7f6 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sat, 27 Oct 2007 01:36:52 -0700 Subject: [PATCH 100/123] list_commands(): simplify code by using chdir() The current code builds absolute path strings for each file to stat(), this can easily be avoided by chdir()ing into the directory. Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- help.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/help.c b/help.c index 3d08973c9d..34ac5db601 100644 --- a/help.c +++ b/help.c @@ -96,36 +96,24 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) static void list_commands(const char *exec_path) { unsigned int longest = 0; - char path[PATH_MAX]; const char *prefix = "git-"; int prefix_len = strlen(prefix); - int dirlen; DIR *dir = opendir(exec_path); struct dirent *de; - if (!dir) { + if (!dir || chdir(exec_path)) { fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno)); exit(1); } - dirlen = strlen(exec_path); - if (PATH_MAX - 20 < dirlen) { - fprintf(stderr, "git: insanely long exec-path '%s'\n", - exec_path); - exit(1); - } - - memcpy(path, exec_path, dirlen); - path[dirlen++] = '/'; - while ((de = readdir(dir)) != NULL) { struct stat st; int entlen; if (prefixcmp(de->d_name, prefix)) continue; - strcpy(path+dirlen, de->d_name); - if (stat(path, &st) || /* stat, not lstat */ + + if (stat(de->d_name, &st) || /* stat, not lstat */ !S_ISREG(st.st_mode) || !(st.st_mode & S_IXUSR)) continue; From 511707d42b3b3e57d9623493092590546ffeae80 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sun, 28 Oct 2007 04:17:20 -0700 Subject: [PATCH 101/123] use only the $PATH for exec'ing git commands We need to correctly set up $PATH for non-c based git commands. Since we already do this, we can just use that $PATH and execvp, instead of looping over the paths with execve. This patch adds a setup_path() function to exec_cmd.c, which sets the $PATH order correctly for our search order. execv_git_cmd() is stripped down to setting up argv and calling execvp(). git.c's main() only only needs to call setup_path(). Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- exec_cmd.c | 118 +++++++++++++++++++++++------------------------------ exec_cmd.h | 1 + git.c | 43 ++++--------------- 3 files changed, 59 insertions(+), 103 deletions(-) diff --git a/exec_cmd.c b/exec_cmd.c index 33b17a6b45..2d0a758512 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -29,85 +29,69 @@ const char *git_exec_path(void) return builtin_exec_path; } +static void add_path(struct strbuf *out, const char *path) +{ + if (path && *path) { + if (is_absolute_path(path)) + strbuf_addstr(out, path); + else + strbuf_addstr(out, make_absolute_path(path)); + + strbuf_addch(out, ':'); + } +} + +void setup_path(const char *cmd_path) +{ + const char *old_path = getenv("PATH"); + struct strbuf new_path; + + strbuf_init(&new_path, 0); + + add_path(&new_path, argv_exec_path); + add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); + add_path(&new_path, builtin_exec_path); + add_path(&new_path, cmd_path); + + if (old_path) + strbuf_addstr(&new_path, old_path); + else + strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin"); + + setenv("PATH", new_path.buf, 1); + + strbuf_release(&new_path); +} int execv_git_cmd(const char **argv) { - char git_command[PATH_MAX + 1]; - int i; - const char *paths[] = { argv_exec_path, - getenv(EXEC_PATH_ENVIRONMENT), - builtin_exec_path }; + struct strbuf cmd; + const char *tmp; - for (i = 0; i < ARRAY_SIZE(paths); ++i) { - size_t len; - int rc; - const char *exec_dir = paths[i]; - const char *tmp; + strbuf_init(&cmd, 0); + strbuf_addf(&cmd, "git-%s", argv[0]); - if (!exec_dir || !*exec_dir) continue; + /* + * argv[0] must be the git command, but the argv array + * belongs to the caller, and may be reused in + * subsequent loop iterations. Save argv[0] and + * restore it on error. + */ + tmp = argv[0]; + argv[0] = cmd.buf; - if (*exec_dir != '/') { - if (!getcwd(git_command, sizeof(git_command))) { - fprintf(stderr, "git: cannot determine " - "current directory: %s\n", - strerror(errno)); - break; - } - len = strlen(git_command); + trace_argv_printf(argv, -1, "trace: exec:"); - /* Trivial cleanup */ - while (!prefixcmp(exec_dir, "./")) { - exec_dir += 2; - while (*exec_dir == '/') - exec_dir++; - } + /* execvp() can only ever return if it fails */ + execvp(cmd.buf, (char **)argv); - rc = snprintf(git_command + len, - sizeof(git_command) - len, "/%s", - exec_dir); - if (rc < 0 || rc >= sizeof(git_command) - len) { - fprintf(stderr, "git: command name given " - "is too long.\n"); - break; - } - } else { - if (strlen(exec_dir) + 1 > sizeof(git_command)) { - fprintf(stderr, "git: command name given " - "is too long.\n"); - break; - } - strcpy(git_command, exec_dir); - } + trace_printf("trace: exec failed: %s\n", strerror(errno)); - len = strlen(git_command); - rc = snprintf(git_command + len, sizeof(git_command) - len, - "/git-%s", argv[0]); - if (rc < 0 || rc >= sizeof(git_command) - len) { - fprintf(stderr, - "git: command name given is too long.\n"); - break; - } + argv[0] = tmp; - /* argv[0] must be the git command, but the argv array - * belongs to the caller, and my be reused in - * subsequent loop iterations. Save argv[0] and - * restore it on error. - */ + strbuf_release(&cmd); - tmp = argv[0]; - argv[0] = git_command; - - trace_argv_printf(argv, -1, "trace: exec:"); - - /* execve() can only ever return if it fails */ - execve(git_command, (char **)argv, environ); - - trace_printf("trace: exec failed: %s\n", strerror(errno)); - - argv[0] = tmp; - } return -1; - } diff --git a/exec_cmd.h b/exec_cmd.h index da99287552..a892355c82 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -3,6 +3,7 @@ extern void git_set_argv_exec_path(const char *exec_path); extern const char* git_exec_path(void); +extern void setup_path(const char *); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); diff --git a/git.c b/git.c index c7cabf5f34..4e10581101 100644 --- a/git.c +++ b/git.c @@ -6,28 +6,6 @@ const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; -static void prepend_to_path(const char *dir, int len) -{ - const char *old_path = getenv("PATH"); - char *path; - int path_len = len; - - if (!old_path) - old_path = "/usr/local/bin:/usr/bin:/bin"; - - path_len = len + strlen(old_path) + 1; - - path = xmalloc(path_len + 1); - - memcpy(path, dir, len); - path[len] = ':'; - memcpy(path + len + 1, old_path, path_len - len); - - setenv("PATH", path, 1); - - free(path); -} - static int handle_options(const char*** argv, int* argc, int* envchanged) { int handled = 0; @@ -408,7 +386,7 @@ int main(int argc, const char **argv) { const char *cmd = argv[0] ? argv[0] : "git-help"; char *slash = strrchr(cmd, '/'); - const char *exec_path = NULL; + const char *cmd_path = NULL; int done_alias = 0; /* @@ -418,10 +396,7 @@ int main(int argc, const char **argv) */ if (slash) { *slash++ = 0; - if (*cmd == '/') - exec_path = cmd; - else - exec_path = xstrdup(make_absolute_path(cmd)); + cmd_path = cmd; cmd = slash; } @@ -458,16 +433,12 @@ int main(int argc, const char **argv) cmd = argv[0]; /* - * We execute external git command via execv_git_cmd(), - * which looks at "--exec-path" option, GIT_EXEC_PATH - * environment, and $(gitexecdir) in Makefile while built, - * in this order. For scripted commands, we prepend - * the value of the exec_path variable to the PATH. + * We use PATH to find git commands, but we prepend some higher + * precidence paths: the "--exec-path" option, the GIT_EXEC_PATH + * environment, and the $(gitexecdir) from the Makefile at build + * time. */ - if (exec_path) - prepend_to_path(exec_path, strlen(exec_path)); - exec_path = git_exec_path(); - prepend_to_path(exec_path, strlen(exec_path)); + setup_path(cmd_path); while (1) { /* See if it's an internal command */ From 1eb056905a2956ca9fabd2edcce05c0a29ce64b1 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sun, 28 Oct 2007 20:30:52 -0700 Subject: [PATCH 102/123] include $PATH in generating list of commands for "help -a" Git had previously been using the $PATH for scripts--a previous patch moved exec'ed commands to also use the $PATH. For consistency "help -a" should also list commands in the $PATH. The main commands are still listed from the git_exec_path(), but the $PATH is walked and other git commands (probably extensions) are listed. Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- help.c | 155 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 36 deletions(-) diff --git a/help.c b/help.c index 34ac5db601..855aeef92f 100644 --- a/help.c +++ b/help.c @@ -37,24 +37,25 @@ static inline void mput_char(char c, unsigned int num) putchar(c); } -static struct cmdname { - size_t len; - char name[1]; -} **cmdname; -static int cmdname_alloc, cmdname_cnt; +static struct cmdnames { + int alloc; + int cnt; + struct cmdname { + size_t len; + char name[1]; + } **names; +} main_cmds, other_cmds; -static void add_cmdname(const char *name, int len) +static void add_cmdname(struct cmdnames *cmds, const char *name, int len) { - struct cmdname *ent; - if (cmdname_alloc <= cmdname_cnt) { - cmdname_alloc = cmdname_alloc + 200; - cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname)); - } - ent = xmalloc(sizeof(*ent) + len); + struct cmdname *ent = xmalloc(sizeof(*ent) + len); + ent->len = len; memcpy(ent->name, name, len); ent->name[len] = 0; - cmdname[cmdname_cnt++] = ent; + + ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); + cmds->names[cmds->cnt++] = ent; } static int cmdname_compare(const void *a_, const void *b_) @@ -64,7 +65,42 @@ static int cmdname_compare(const void *a_, const void *b_) return strcmp(a->name, b->name); } -static void pretty_print_string_list(struct cmdname **cmdname, int longest) +static void uniq(struct cmdnames *cmds) +{ + int i, j; + + if (!cmds->cnt) + return; + + for (i = j = 1; i < cmds->cnt; i++) + if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) + cmds->names[j++] = cmds->names[i]; + + cmds->cnt = j; +} + +static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { + int ci, cj, ei; + int cmp; + + ci = cj = ei = 0; + while (ci < cmds->cnt && ei < excludes->cnt) { + cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); + if (cmp < 0) + cmds->names[cj++] = cmds->names[ci++]; + else if (cmp == 0) + ci++, ei++; + else if (cmp > 0) + ei++; + } + + while (ci < cmds->cnt) + cmds->names[cj++] = cmds->names[ci++]; + + cmds->cnt = cj; +} + +static void pretty_print_string_list(struct cmdnames *cmds, int longest) { int cols = 1, rows; int space = longest + 1; /* min 1 SP between words */ @@ -73,9 +109,7 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) if (space < max_cols) cols = max_cols / space; - rows = (cmdname_cnt + cols - 1) / cols; - - qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare); + rows = (cmds->cnt + cols - 1) / cols; for (i = 0; i < rows; i++) { printf(" "); @@ -83,28 +117,27 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) for (j = 0; j < cols; j++) { int n = j * rows + i; int size = space; - if (n >= cmdname_cnt) + if (n >= cmds->cnt) break; - if (j == cols-1 || n + rows >= cmdname_cnt) + if (j == cols-1 || n + rows >= cmds->cnt) size = 1; - printf("%-*s", size, cmdname[n]->name); + printf("%-*s", size, cmds->names[n]->name); } putchar('\n'); } } -static void list_commands(const char *exec_path) +static unsigned int list_commands_in_dir(struct cmdnames *cmds, + const char *path) { unsigned int longest = 0; const char *prefix = "git-"; int prefix_len = strlen(prefix); - DIR *dir = opendir(exec_path); + DIR *dir = opendir(path); struct dirent *de; - if (!dir || chdir(exec_path)) { - fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno)); - exit(1); - } + if (!dir || chdir(path)) + return 0; while ((de = readdir(dir)) != NULL) { struct stat st; @@ -125,16 +158,68 @@ static void list_commands(const char *exec_path) if (longest < entlen) longest = entlen; - add_cmdname(de->d_name + prefix_len, entlen); + add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); - printf("git commands available in '%s'\n", exec_path); - printf("----------------------------"); - mput_char('-', strlen(exec_path)); - putchar('\n'); - pretty_print_string_list(cmdname, longest); - putchar('\n'); + return longest; +} + +static void list_commands(void) +{ + unsigned int longest = 0; + unsigned int len; + const char *env_path = getenv("PATH"); + char *paths, *path, *colon; + const char *exec_path = git_exec_path(); + + if (exec_path) + longest = list_commands_in_dir(&main_cmds, exec_path); + + if (!env_path) { + fprintf(stderr, "PATH not set\n"); + exit(1); + } + + path = paths = xstrdup(env_path); + while (1) { + if ((colon = strchr(path, ':'))) + *colon = 0; + + len = list_commands_in_dir(&other_cmds, path); + if (len > longest) + longest = len; + + if (!colon) + break; + path = colon + 1; + } + free(paths); + + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), cmdname_compare); + uniq(&main_cmds); + + qsort(other_cmds.names, other_cmds.cnt, + sizeof(*other_cmds.names), cmdname_compare); + uniq(&other_cmds); + exclude_cmds(&other_cmds, &main_cmds); + + if (main_cmds.cnt) { + printf("available git commands in '%s'\n", exec_path); + printf("----------------------------"); + mput_char('-', strlen(exec_path)); + putchar('\n'); + pretty_print_string_list(&main_cmds, longest); + putchar('\n'); + } + + if (other_cmds.cnt) { + printf("git commands available from elsewhere on your $PATH\n"); + printf("---------------------------------------------------\n"); + pretty_print_string_list(&other_cmds, longest); + putchar('\n'); + } } void list_common_cmds_help(void) @@ -188,7 +273,6 @@ int cmd_version(int argc, const char **argv, const char *prefix) int cmd_help(int argc, const char **argv, const char *prefix) { const char *help_cmd = argc > 1 ? argv[1] : NULL; - const char *exec_path = git_exec_path(); if (!help_cmd) { printf("usage: %s\n\n", git_usage_string); @@ -198,8 +282,7 @@ int cmd_help(int argc, const char **argv, const char *prefix) else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) { printf("usage: %s\n\n", git_usage_string); - if(exec_path) - list_commands(exec_path); + list_commands(); exit(0); } From e8f5d87056093f40a271f89c2c91d1a7025e2440 Mon Sep 17 00:00:00 2001 From: Scott R Parish Date: Sat, 27 Oct 2007 01:36:55 -0700 Subject: [PATCH 103/123] shell should call the new setup_path() to setup $PATH Shell currently does its own manual thing for setting up the $PATH; it can now call setup_path(). Signed-off-by: Scott R Parish Signed-off-by: Junio C Hamano --- shell.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/shell.c b/shell.c index cfe372b213..9826109d5b 100644 --- a/shell.c +++ b/shell.c @@ -24,17 +24,11 @@ static int do_cvs_cmd(const char *me, char *arg) const char *cvsserver_argv[3] = { "cvsserver", "server", NULL }; - const char *oldpath = getenv("PATH"); - struct strbuf newpath = STRBUF_INIT; if (!arg || strcmp(arg, "server")) die("git-cvsserver only handles server: %s", arg); - strbuf_addstr(&newpath, git_exec_path()); - strbuf_addch(&newpath, ':'); - strbuf_addstr(&newpath, oldpath); - - setenv("PATH", strbuf_detach(&newpath, NULL), 1); + setup_path(NULL); return execv_git_cmd(cvsserver_argv); } From 6b9ff1e3bd089d1bef211ba74c12e056810755b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 27 Oct 2007 14:47:21 +0700 Subject: [PATCH 104/123] git-sh-setup.sh: use "git rev-parse --show-cdup" to check for SUBDIRECTORY_OK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "git rev-parse --git-dir" trick does not play well with worktree Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 3c325fd133..86d7d4c4e7 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -110,7 +110,7 @@ esac if [ -z "$SUBDIRECTORY_OK" ] then : ${GIT_DIR=.git} - GIT_DIR=$(GIT_DIR="$GIT_DIR" git rev-parse --git-dir) || { + test -z "$(git rev-parse --show-cdup)" || { exit=$? echo >&2 "You need to run this command from the toplevel of the working tree." exit $exit From 0cec6db5cfbb109eebb3a50b6213cab2dc68f0c8 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Tue, 30 Oct 2007 01:35:05 +0100 Subject: [PATCH 105/123] gitweb: Fix and simplify "split patch" detection There are some cases when one line from "raw" git-diff output (raw format) corresponds to more than one patch in the patchset git-diff output; we call this situation "split patch". Old code misdetected subsequent patches (for different files) with the same pre-image and post-image as fragments of "split patch", leading to mislabeled from-file/to-file diff header etc. Old code used pre-image and post-image SHA-1 identifier ('from_id' and 'to_id') to check if current patch corresponds to old raw diff format line, to find if one difftree raw line coresponds to more than one patch in the patch format. Now we use post-image filename for that. This assumes that post-image filename alone can be used to identify difftree raw line. In the case this changes (which is unlikely considering current diff engine) we can add 'from_id' and 'to_id' to detect "patch splitting" together with 'to_file'. Because old code got pre-image and post-image SHA-1 identifier for the patch from the "index" line in extended diff header, diff header had to be buffered. New code takes post-image filename from "git diff" header, which is first line of a patch; this allows to simplify git_patchset_body code. A side effect of resigning diff header buffering is that there is always "diff extended_header" div, even if extended diff header is empty. Alternate solution would be to check when git splits patches, and do not check if parsed info from current patch corresponds to current or next raw diff format output line. Git splits patches only for 'T' (typechange) status filepair, and there always two patches corresponding to one raw diff line. It was not used because it would tie gitweb code to minute details of git diff output. While at it, use newly introduced parsed_difftree_line wrapper subroutine in git_difftree_body. Noticed-by: Yann Dirson Diagnosed-by: Petr Baudis Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/gitweb.perl | 152 ++++++++++++++++++++------------------------- 1 file changed, 67 insertions(+), 85 deletions(-) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 48e21dad6c..1537b0ec1d 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -2000,6 +2000,19 @@ sub parse_difftree_raw_line { return wantarray ? %res : \%res; } +# wrapper: return parsed line of git-diff-tree "raw" output +# (the argument might be raw line, or parsed info) +sub parsed_difftree_line { + my $line_or_ref = shift; + + if (ref($line_or_ref) eq "HASH") { + # pre-parsed (or generated by hand) + return $line_or_ref; + } else { + return parse_difftree_raw_line($line_or_ref); + } +} + # parse line of git-ls-tree output sub parse_ls_tree_line ($;%) { my $line = shift; @@ -2043,6 +2056,7 @@ sub parse_from_to_diffinfo { } } } else { + # ordinary (not combined) diff $from->{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'}; if ($diffinfo->{'status'} ne "A") { # not new (added) file $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent, @@ -2766,6 +2780,7 @@ sub git_print_tree_entry { ## ...................................................................... ## functions printing large fragments of HTML +# get pre-image filenames for merge (combined) diff sub fill_from_file_info { my ($diff, @parents) = @_; @@ -2782,28 +2797,25 @@ sub fill_from_file_info { return $diff; } -# parameters can be strings, or references to arrays of strings -sub from_ids_eq { - my ($a, $b) = @_; - - if (ref($a) eq "ARRAY" && ref($b) eq "ARRAY" && @$a == @$b) { - for (my $i = 0; $i < @$a; ++$i) { - return 0 unless ($a->[$i] eq $b->[$i]); - } - return 1; - } elsif (!ref($a) && !ref($b)) { - return $a eq $b; - } else { - return 0; - } -} - +# is current raw difftree line of file deletion sub is_deleted { my $diffinfo = shift; return $diffinfo->{'to_id'} eq ('0' x 40); } +# does patch correspond to [previous] difftree raw line +# $diffinfo - hashref of parsed raw diff format +# $patchinfo - hashref of parsed patch diff format +# (the same keys as in $diffinfo) +sub is_patch_split { + my ($diffinfo, $patchinfo) = @_; + + return defined $diffinfo && defined $patchinfo + && ($diffinfo->{'to_file'} || $diffinfo->{'file'}) eq $patchinfo->{'to_file'}; +} + + sub git_difftree_body { my ($difftree, $hash, @parents) = @_; my ($parent) = $parents[0]; @@ -2840,13 +2852,7 @@ sub git_difftree_body { my $alternate = 1; my $patchno = 0; foreach my $line (@{$difftree}) { - my $diff; - if (ref($line) eq "HASH") { - # pre-parsed (or generated by hand) - $diff = $line; - } else { - $diff = parse_difftree_raw_line($line); - } + my $diff = parsed_difftree_line($line); if ($alternate) { print "\n"; @@ -3117,10 +3123,12 @@ sub git_patchset_body { my ($fd, $difftree, $hash, @hash_parents) = @_; my ($hash_parent) = $hash_parents[0]; + my $is_combined = (@hash_parents > 1); my $patch_idx = 0; my $patch_number = 0; my $patch_line; my $diffinfo; + my $to_name; my (%from, %to); print "
\n"; @@ -3134,73 +3142,46 @@ sub git_patchset_body { PATCH: while ($patch_line) { - my @diff_header; - my ($from_id, $to_id); - # git diff header - #assert($patch_line =~ m/^diff /) if DEBUG; - #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed - $patch_number++; - push @diff_header, $patch_line; - - # extended diff header - EXTENDED_HEADER: - while ($patch_line = <$fd>) { - chomp $patch_line; - - last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /); - - if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) { - $from_id = $1; - $to_id = $2; - } elsif ($patch_line =~ m/^index ((?:[0-9a-fA-F]{40},)+[0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) { - $from_id = [ split(',', $1) ]; - $to_id = $2; - } - - push @diff_header, $patch_line; + # parse "git diff" header line + if ($patch_line =~ m/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/) { + # $1 is from_name, which we do not use + $to_name = unquote($2); + $to_name =~ s!^b/!!; + } elsif ($patch_line =~ m/^diff --(cc|combined) ("?.*"?)$/) { + # $1 is 'cc' or 'combined', which we do not use + $to_name = unquote($2); + } else { + $to_name = undef; } - my $last_patch_line = $patch_line; # check if current patch belong to current raw line # and parse raw git-diff line if needed - if (defined $diffinfo && - defined $from_id && defined $to_id && - from_ids_eq($diffinfo->{'from_id'}, $from_id) && - $diffinfo->{'to_id'} eq $to_id) { + if (is_patch_split($diffinfo, { 'to_file' => $to_name })) { # this is continuation of a split patch print "
\n"; } else { # advance raw git-diff output if needed $patch_idx++ if defined $diffinfo; + # read and prepare patch information + $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); + # compact combined diff output can have some patches skipped - # find which patch (using pathname of result) we are at now - my $to_name; - if ($diff_header[0] =~ m!^diff --cc "?(.*)"?$!) { - $to_name = $1; - } - - do { - # read and prepare patch information - if (ref($difftree->[$patch_idx]) eq "HASH") { - # pre-parsed (or generated by hand) - $diffinfo = $difftree->[$patch_idx]; - } else { - $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]); - } - - # check if current raw line has no patch (it got simplified) - if (defined $to_name && $to_name ne $diffinfo->{'to_file'}) { + # find which patch (using pathname of result) we are at now; + if ($is_combined) { + while ($to_name ne $diffinfo->{'to_file'}) { print "
\n" . format_diff_cc_simplified($diffinfo, @hash_parents) . "
\n"; # class="patch" $patch_idx++; $patch_number++; + + last if $patch_idx > $#$difftree; + $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); } - } until (!defined $to_name || $to_name eq $diffinfo->{'to_file'} || - $patch_idx > $#$difftree); + } # modifies %from, %to hashes parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents); @@ -3210,30 +3191,36 @@ sub git_patchset_body { print "
\n"; } + # git diff header + #assert($patch_line =~ m/^diff /) if DEBUG; + #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed + $patch_number++; # print "git diff" header - $patch_line = shift @diff_header; print format_git_diff_header_line($patch_line, $diffinfo, \%from, \%to); # print extended diff header - print "
\n" if (@diff_header > 0); + print "
\n"; EXTENDED_HEADER: - foreach $patch_line (@diff_header) { + while ($patch_line = <$fd>) { + chomp $patch_line; + + last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /); + print format_extended_diff_header_line($patch_line, $diffinfo, \%from, \%to); } - print "
\n" if (@diff_header > 0); # class="diff extended_header" + print "
\n"; # class="diff extended_header" # from-file/to-file diff header - $patch_line = $last_patch_line; if (! $patch_line) { print "
\n"; # class="patch" last PATCH; } next PATCH if ($patch_line =~ m/^diff /); #assert($patch_line =~ m/^---/) if DEBUG; - #assert($patch_line eq $last_patch_line) if DEBUG; + my $last_patch_line = $patch_line; $patch_line = <$fd>; chomp $patch_line; #assert($patch_line =~ m/^\+\+\+/) if DEBUG; @@ -3258,16 +3245,11 @@ sub git_patchset_body { # for compact combined (--cc) format, with chunk and patch simpliciaction # patchset might be empty, but there might be unprocessed raw lines - for ($patch_idx++ if $patch_number > 0; + for (++$patch_idx if $patch_number > 0; $patch_idx < @$difftree; - $patch_idx++) { + ++$patch_idx) { # read and prepare patch information - if (ref($difftree->[$patch_idx]) eq "HASH") { - # pre-parsed (or generated by hand) - $diffinfo = $difftree->[$patch_idx]; - } else { - $diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]); - } + $diffinfo = parsed_difftree_line($difftree->[$patch_idx]); # generate anchor for "patch" links in difftree / whatchanged part print "
\n" . From 562e35c34ced4ca7af024d213737411238dbe0f5 Mon Sep 17 00:00:00 2001 From: "Michael W. Olson" Date: Tue, 30 Oct 2007 09:53:47 -0400 Subject: [PATCH 106/123] Documentation/git-cvsexportcommit.txt: s/mgs/msg/ in example Signed-off-by: Junio C Hamano --- Documentation/git-cvsexportcommit.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index 4c8d1e6386..c3922f9238 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -73,7 +73,7 @@ Merge one patch into CVS:: $ export GIT_DIR=~/project/.git $ cd ~/project_cvs_checkout $ git-cvsexportcommit -v -$ cvs commit -F .mgs +$ cvs commit -F .msg ------------ Merge pending patches into CVS automatically -- only if you really know what you are doing:: From b6c9fb5100f8cd4081f1c6df5aea7335a11e9659 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 Oct 2007 11:30:43 -0700 Subject: [PATCH 107/123] Prevent send-pack from segfaulting (backport from 'master') 4491e62ae932d5774f628d1bd3be663c11058a73 (Prevent send-pack from segfaulting when a branch doesn't match) is hereby cherry-picked back to 'maint'. If we can't find a source match, and we have no destination, we need to abort the match function early before we try to match the destination against the remote. Signed-off-by: Junio C Hamano --- remote.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/remote.c b/remote.c index cdbbdcb00d..9a88917aab 100644 --- a/remote.c +++ b/remote.c @@ -504,8 +504,11 @@ static int match_explicit(struct ref *src, struct ref *dst, if (!matched_src) errs = 1; - if (dst_value == NULL) + if (!dst_value) { + if (!matched_src) + return errs; dst_value = matched_src->name; + } switch (count_refspec_match(dst_value, dst, &matched_dst)) { case 1: From dee48c3c7ed7f7a32a524e8a492c6bc4e3c1c78d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 Oct 2007 11:54:11 -0700 Subject: [PATCH 108/123] git-merge: document but discourage the historical syntax Historically "git merge" took its command line arguments in a rather strange order. Document the historical syntax, and also document clearly that it is not encouraged in new scripts. There is no reason to deprecate the historical syntax, as the current code can sanely tell which syntax the caller is using, and existing scripts by people do use the historical syntax. Signed-off-by: Junio C Hamano --- Documentation/git-merge.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index eae49c4876..827838f7d0 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -11,26 +11,27 @@ SYNOPSIS [verse] 'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s ]... [-m ] ... +'git-merge' HEAD ... DESCRIPTION ----------- This is the top-level interface to the merge machinery which drives multiple merge strategy scripts. +The second syntax ( `HEAD` ) is supported for +historical reasons. Do not use it from the command line or in +new scripts. It is the same as `git merge -m `. + OPTIONS ------- include::merge-options.txt[] -:: +-m :: The commit message to be used for the merge commit (in case it is created). The `git-fmt-merge-msg` script can be used to give a good default for automated `git-merge` invocations. -:: - Our branch head commit. This has to be `HEAD`, so new - syntax does not require it - :: Other branch head merged into our branch. You need at least one . Specifying more than one From ba17892ddc85c0ffe8fecd600c29cb38ec7e5587 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Tue, 30 Oct 2007 22:54:02 +0300 Subject: [PATCH 109/123] core-tutorial: Use new syntax for git-merge. "git-merge HEAD " is still supported but we shouldn't encourage its use. Signed-off-by: Junio C Hamano --- Documentation/core-tutorial.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt index 6b2590d072..df147b5e76 100644 --- a/Documentation/core-tutorial.txt +++ b/Documentation/core-tutorial.txt @@ -883,7 +883,7 @@ script called `git merge`, which wants to know which branches you want to resolve and what the merge is all about: ------------ -$ git merge "Merge work in mybranch" HEAD mybranch +$ git merge -m "Merge work in mybranch" mybranch ------------ where the first argument is going to be used as the commit message if @@ -970,7 +970,7 @@ to the `master` branch. Let's go back to `mybranch`, and run ------------ $ git checkout mybranch -$ git merge "Merge upstream changes." HEAD master +$ git merge -m "Merge upstream changes." master ------------ This outputs something like this (the actual commit object names @@ -1613,8 +1613,8 @@ in both of them. You could merge in 'diff-fix' first and then 'commit-fix' next, like this: ------------ -$ git merge 'Merge fix in diff-fix' master diff-fix -$ git merge 'Merge fix in commit-fix' master commit-fix +$ git merge -m 'Merge fix in diff-fix' diff-fix +$ git merge -m 'Merge fix in commit-fix' commit-fix ------------ Which would result in: From 04bd8e5fea48a00816b461b0fb934627cfd2c45f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 15:59:24 -0400 Subject: [PATCH 110/123] cherry-pick/revert: more compact user direction message A failed cherry-pick (and friend) currently says: |Automatic cherry-pick failed. After resolving the conflicts, |mark the corrected paths with 'git-add ' |and commit the result. This can obviously be displayed on two lines only. While at it, change "git-add" to "git add". Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-revert.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-revert.c b/builtin-revert.c index 499bbe7343..eafafbc333 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -351,7 +351,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) die ("Error wrapping up %s", defmsg); fprintf(stderr, "Automatic %s failed. " "After resolving the conflicts,\n" - "mark the corrected paths with 'git-add '\n" + "mark the corrected paths with 'git add ' " "and commit the result.\n", me); if (action == CHERRY_PICK) { fprintf(stderr, "When commiting, use the option " From 07b45f8c1754a13bca67f70b600bf51ba33cf98d Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Tue, 25 Sep 2007 08:36:38 +0200 Subject: [PATCH 111/123] Make merge-recursive honor diff.renamelimit It might be a sign of source code management gone bad, but when two branches has diverged almost beyond recognition and time has come for the branches to merge, the user is going to need all the help his tool can give him. Honoring diff.renamelimit has great potential as a painkiller in such situations. The painkiller effect could have been achieved by e.g. 'merge.renamelimit', but the flexibility gained by a separate option is questionable: our user would probably expect git to detect renames equally good when merging as when diffing (I known I did). Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- merge-recursive.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/merge-recursive.c b/merge-recursive.c index c2e1cb69e3..fedfff4aed 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -96,6 +96,7 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int call_depth = 0; static int verbosity = 2; +static int rename_limit = -1; static int buffer_output = 1; static struct output_buffer *output_list, *output_end; @@ -372,6 +373,7 @@ static struct path_list *get_renames(struct tree *tree, diff_setup(&opts); opts.recursive = 1; opts.detect_rename = DIFF_DETECT_RENAME; + opts.rename_limit = rename_limit; opts.output_format = DIFF_FORMAT_NO_OUTPUT; if (diff_setup_done(&opts) < 0) die("diff setup failed"); @@ -1693,6 +1695,10 @@ static int merge_config(const char *var, const char *value) verbosity = git_config_int(var, value); return 0; } + if (!strcasecmp(var, "diff.renamelimit")) { + rename_limit = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } From 3524b282dabd254fc50b186e35ea5c6f612aeb46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Wed, 31 Oct 2007 03:20:30 +0100 Subject: [PATCH 112/123] Fix --strategy parsing in git-rebase--interactive.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the --strategy/-s option, git-rebase--interactive.sh dropped the parameter which it was trying to parse. Signed-off-by: Björn Steinbrink Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 50b79ff8ff..ebc67e515d 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -357,7 +357,6 @@ do output git reset --hard && do_rest ;; -s|--strategy) - shift case "$#,$1" in *,*=*) STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;; From 2a9c53e03d8293577b5163910f178075725b39dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Wed, 31 Oct 2007 03:20:31 +0100 Subject: [PATCH 113/123] git-rebase--interactive.sh: Don't pass a strategy to git-cherry-pick. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-cherry-pick doesn't support a strategy paramter, so don't pass one. This means that --strategy for interactive rebases is a no-op for anything but merge commits, but that's still better than being broken. A correct fix would probably need to port the --merge behaviour from plain git-rebase.sh, but I have no clue how to integrate that cleanly. Signed-off-by: Björn Steinbrink Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index ebc67e515d..db04057653 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -109,7 +109,7 @@ pick_one () { sha1=$(git rev-parse --short $sha1) output warn Fast forward to $sha1 else - output git cherry-pick $STRATEGY "$@" + output git cherry-pick "$@" fi } @@ -173,7 +173,7 @@ pick_one_preserving_merges () { fi ;; *) - output git cherry-pick $STRATEGY "$@" || + output git cherry-pick "$@" || die_with_patch $sha1 "Could not pick $sha1" esac esac From f91333d646e690d9a10717c96e166a069b2bb586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Wed, 31 Oct 2007 03:20:32 +0100 Subject: [PATCH 114/123] git-rebase--interactive.sh: Make 3-way merge strategies work for -p. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-rebase--interactive.sh used to pass all parents of a merge commit to git-merge, which means that we have at least 3 heads to merge: HEAD, first parent and second parent. So 3-way merge strategies like recursive wouldn't work. Fortunately, we have checked out the first parent right before the merge anyway, so that is HEAD. Therefore we can drop simply it from the list of parents, making 3-way strategies work for merge commits with only two parents. Signed-off-by: Björn Steinbrink Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- git-rebase--interactive.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index db04057653..f28c3df204 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -165,6 +165,8 @@ pick_one_preserving_merges () { eval "$author_script" msg="$(git cat-file commit $sha1 | \ sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")" + # No point in merging the first parent, that's HEAD + new_parents=${new_parents# $first_parent} # NEEDSWORK: give rerere a chance if ! output git merge $STRATEGY -m "$msg" $new_parents then From 0bdb5af7a571ee2bd71550a545632b1cb7f8f8f3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 30 Oct 2007 21:32:49 -0700 Subject: [PATCH 115/123] Update GIT 1.5.3.5 Release Notes Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index e28d92f618..bf6a279ede 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -87,8 +87,14 @@ Fixes since v1.5.3.4 * A few workarounds to squelch false warnings from recent gcc have been added. + * "git-send-pack $remote frotz" segfaulted when there is nothing + named 'frotz' on the local end. + + * "git-rebase -interactive" did not handle its "--strategy" option + properly. + -- exec >/var/tmp/1 -O=v1.5.3.4-55-gf120ae2 +O=v1.5.3.4-65-gf91333d echo O=`git describe refs/heads/maint` git shortlog --no-merges $O..refs/heads/maint From fee9832a8dea5d9c98c5c3a4797615d52814df16 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 30 Oct 2007 14:24:27 +0000 Subject: [PATCH 116/123] No longer install git-svnimport, move to contrib/examples This has been proposed for a few times without much reaction from the list. Actually remove it to see who screams. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- .gitignore | 1 - Documentation/cmd-list.perl | 1 - Makefile | 3 +-- contrib/completion/git-completion.bash | 1 - git-svnimport.perl => contrib/examples/git-svnimport.perl | 0 {Documentation => contrib/examples}/git-svnimport.txt | 0 6 files changed, 1 insertion(+), 5 deletions(-) rename git-svnimport.perl => contrib/examples/git-svnimport.perl (100%) rename {Documentation => contrib/examples}/git-svnimport.txt (100%) diff --git a/.gitignore b/.gitignore index 62afef2347..8670081adf 100644 --- a/.gitignore +++ b/.gitignore @@ -128,7 +128,6 @@ git-status git-stripspace git-submodule git-svn -git-svnimport git-symbolic-ref git-tag git-tar-tree diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index 1061fd8bcd..8d21d423e5 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -185,7 +185,6 @@ git-status mainporcelain git-stripspace purehelpers git-submodule mainporcelain git-svn foreignscminterface -git-svnimport foreignscminterface git-symbolic-ref plumbingmanipulators git-tag mainporcelain git-tar-tree plumbinginterrogators diff --git a/Makefile b/Makefile index 72f5ef43ce..eb98d91bdf 100644 --- a/Makefile +++ b/Makefile @@ -225,8 +225,7 @@ SCRIPT_SH = \ SCRIPT_PERL = \ git-add--interactive.perl \ git-archimport.perl git-cvsimport.perl git-relink.perl \ - git-cvsserver.perl git-remote.perl \ - git-svnimport.perl git-cvsexportcommit.perl \ + git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \ git-send-email.perl git-svn.perl SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e760930740..599b2fc571 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -346,7 +346,6 @@ __git_commands () ssh-*) : transport;; stripspace) : plumbing;; svn) : import export;; - svnimport) : import;; symbolic-ref) : plumbing;; tar-tree) : deprecated;; unpack-file) : plumbing;; diff --git a/git-svnimport.perl b/contrib/examples/git-svnimport.perl similarity index 100% rename from git-svnimport.perl rename to contrib/examples/git-svnimport.perl diff --git a/Documentation/git-svnimport.txt b/contrib/examples/git-svnimport.txt similarity index 100% rename from Documentation/git-svnimport.txt rename to contrib/examples/git-svnimport.txt From 3f2a7ae2c84c921e11041a5edc2522964fc1cce5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 31 Oct 2007 12:20:05 -0700 Subject: [PATCH 117/123] GIT 1.5.3.5 Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.5.txt | 6 ------ GIT-VERSION-GEN | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Documentation/RelNotes-1.5.3.5.txt b/Documentation/RelNotes-1.5.3.5.txt index bf6a279ede..4e46d2c2a2 100644 --- a/Documentation/RelNotes-1.5.3.5.txt +++ b/Documentation/RelNotes-1.5.3.5.txt @@ -92,9 +92,3 @@ Fixes since v1.5.3.4 * "git-rebase -interactive" did not handle its "--strategy" option properly. - --- -exec >/var/tmp/1 -O=v1.5.3.4-65-gf91333d -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 223c4f5e14..88a4acad15 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.3.4.GIT +DEF_VER=v1.5.3.5.GIT LF=' ' From 6b6012e6ca628a00d2e0fa6f686017f9e2d66203 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 31 Oct 2007 16:55:13 -0400 Subject: [PATCH 118/123] cvsexportcommit: fix for commits that do not have parents Previously commits without parents would fail to export with a message indicating that the commits had more than one parent. Instead we should use the --root option for git-diff-tree in place of a parent. Signed-off-by: Brad King Signed-off-by: Junio C Hamano --- git-cvsexportcommit.perl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 7b19a33ad1..b2c9f98e86 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -87,6 +87,7 @@ foreach my $line (@commit) { } } +my $noparent = "0000000000000000000000000000000000000000"; if ($parent) { my $found; # double check that it's a valid parent @@ -100,8 +101,10 @@ if ($parent) { } else { # we don't have a parent from the cmdline... if (@parents == 1) { # it's safe to get it from the commit $parent = $parents[0]; - } else { # or perhaps not! - die "This commit has more than one parent -- please name the parent you want to use explicitly"; + } elsif (@parents == 0) { # there is no parent + $parent = $noparent; + } else { # cannot choose automatically from multiple parents + die "This commit has more than one parent -- please name the parent you want to use explicitly"; } } @@ -121,7 +124,11 @@ if ($opt_a) { } close MSG; -`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff"; +if ($parent eq $noparent) { + `git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff"; +} else { + `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff"; +} ## apply non-binary changes From 1e3a2eb667256716e101707f4da7179da2ba3c65 Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Thu, 1 Nov 2007 13:45:33 +0100 Subject: [PATCH 119/123] gitweb: Update config file example for snapshot feature in gitweb/INSTALL Commit a3c8ab30a54c30a6a434760bedf04548425416ef by Matt McCutchen "gitweb: snapshot cleanups & support for offering multiple formats" introduced new format of $feature{'snapshot'}{'default'} value. Update "Config file example" in gitweb/INSTALL accordingly. Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- gitweb/INSTALL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitweb/INSTALL b/gitweb/INSTALL index 6328e26f56..9cd5b0a2b1 100644 --- a/gitweb/INSTALL +++ b/gitweb/INSTALL @@ -116,7 +116,7 @@ GITWEB_CONFIG file: $feature{'pickaxe'}{'default'} = [1]; $feature{'pickaxe'}{'override'} = 1; - $feature{'snapshot'}{'default'} = ['x-gzip', 'gz', 'gzip']; + $feature{'snapshot'}{'default'} = ['zip', 'tgz']; $feature{'snapshot'}{'override'} = 1; From d9d10bb8549ca0353d13298e372c1c87bcc530ef Mon Sep 17 00:00:00 2001 From: Ralf Wildenhues Date: Thu, 1 Nov 2007 09:46:02 +0100 Subject: [PATCH 120/123] git-clone.txt: Improve --depth description. Avoid abbreviation 'revs', improve the language a bit. Signed-off-by: Junio C Hamano --- Documentation/git-clone.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 253f4f03c5..cca14d6b5d 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -111,11 +111,11 @@ OPTIONS --depth :: Create a 'shallow' clone with a history truncated to the - specified number of revs. A shallow repository has + specified number of revisions. A shallow repository has a number of limitations (you cannot clone or fetch from it, nor push from nor into it), but is adequate if you - want to only look at near the tip of a large project - with a long history, and would want to send in a fixes + are only interested in the recent history of a large project + with a long history, and would want to send in fixes as patches. :: From 8451c565bceaf9c402fadba16d2b9c2dcf71bbc2 Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Thu, 1 Nov 2007 16:24:11 +0300 Subject: [PATCH 121/123] git-filter-branch.txt: fix a typo. Signed-off-by: Sergei Organov Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-filter-branch.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt index ba9b4fbca7..385ecc900f 100644 --- a/Documentation/git-filter-branch.txt +++ b/Documentation/git-filter-branch.txt @@ -219,7 +219,7 @@ git filter-branch --commit-filter ' fi' HEAD ------------------------------------------------------------------------------ -The function 'skip_commits' is defined as follows: +The function 'skip_commit' is defined as follows: -------------------------- skip_commit() From 136e6316705e9e83cbb09c2a9ac4cb29c3e4662d Mon Sep 17 00:00:00 2001 From: Sergei Organov Date: Thu, 1 Nov 2007 17:21:39 +0300 Subject: [PATCH 122/123] git-format-patch.txt: fix explanation of an example. Signed-off-by: Sergei Organov Signed-off-by: Junio C Hamano --- Documentation/git-format-patch.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index c9857a2d62..f0617efa0a 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -168,7 +168,7 @@ git-format-patch origin:: is created in the current directory. git-format-patch \--root origin:: - Extract all commits which that leads to 'origin' since the + Extract all commits that lead to 'origin' since the inception of the project. git-format-patch -M -B origin:: From f31dfa604c83fd998f7b57942a7bac4defc8c435 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Thu, 1 Nov 2007 15:01:58 +0100 Subject: [PATCH 123/123] Do no colorify test output if stdout is not a terminal like when the output is redirected into a file in a cron job. Signed-off-by: Junio C Hamano --- t/test-lib.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/t/test-lib.sh b/t/test-lib.sh index 714de6e575..603a8cd5e7 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -60,6 +60,7 @@ esac # . ./test-lib.sh [ "x$TERM" != "xdumb" ] && + [ -t 1 ] && tput bold >/dev/null 2>&1 && tput setaf 1 >/dev/null 2>&1 && tput sgr0 >/dev/null 2>&1 &&