From 6e8c87070306a757c4d7fd2c55cca3a90fe140c7 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 31 Jul 2007 21:03:06 +1000 Subject: [PATCH 001/602] 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/602] 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 daf422895c9b00255b5e79be5c1c30bdebcb19a9 Mon Sep 17 00:00:00 2001 From: Nguyen Thai Ngoc Duy Date: Tue, 31 Jul 2007 11:09:48 -0400 Subject: [PATCH 003/602] Mention libiconv as a requirement for git-am --- README.MinGW | 1 + 1 file changed, 1 insertion(+) diff --git a/README.MinGW b/README.MinGW index 89b7065aac..c0b8f66b93 100644 --- a/README.MinGW +++ b/README.MinGW @@ -28,6 +28,7 @@ In order to compile this code you need: zlib-1.2.3-mingwPORT-1.tar w32api-3.6.tar.gz tcltk-8.4.1-1.exe (for gitk, git-gui) + libiconv-1.9.2-1-{lib,bin}.zip (for git-am, from http://gnuwin32.sourceforge.net/packages/libiconv.htm) STATUS From 2e2f5df3c1433a6f1bd6943dbec1b0b631759499 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 6 Aug 2007 21:39:06 +0200 Subject: [PATCH 004/602] Link with libiconv (1.9.2). --- Makefile | 3 ++- README.MinGW | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 760b5448be..f45f0e6179 100644 --- a/Makefile +++ b/Makefile @@ -482,7 +482,8 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_UNSETENV=YesPlease NO_STRCASESTR=YesPlease NO_STRLCPY=YesPlease - NO_ICONV=YesPlease + NEEDS_LIBICONV = YesPlease + OLD_ICONV = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease NO_SYMLINKS=YesPlease diff --git a/README.MinGW b/README.MinGW index c0b8f66b93..436d7b7849 100644 --- a/README.MinGW +++ b/README.MinGW @@ -28,7 +28,8 @@ In order to compile this code you need: zlib-1.2.3-mingwPORT-1.tar w32api-3.6.tar.gz tcltk-8.4.1-1.exe (for gitk, git-gui) - libiconv-1.9.2-1-{lib,bin}.zip (for git-am, from http://gnuwin32.sourceforge.net/packages/libiconv.htm) + libiconv-1.9.2-1-{lib,bin,lib}.zip (for git-am, + from http://gnuwin32.sourceforge.net/packages/libiconv.htm) STATUS From 637fc51696dc067c006b1651689b2f0aac26ec04 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 7 Aug 2007 20:50:42 +0200 Subject: [PATCH 005/602] Implement is_absolute_path() on Windows. To keep the brief, we don't check that there must be a letter before the colon and a slash or backslash after the colon - we just assume that we don't have to work with drive-relative paths. --- cache.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cache.h b/cache.h index d9598bb685..a701139fd8 100644 --- a/cache.h +++ b/cache.h @@ -364,7 +364,11 @@ int safe_create_leading_directories(char *path); char *enter_repo(char *path, int strict); static inline int is_absolute_path(const char *path) { +#ifndef __MINGW32__ return path[0] == '/'; +#else + return path[0] == '/' || (path[0] && path[1] == ':'); +#endif } const char *make_absolute_path(const char *path); From ab793ce23595a1d7b4891646dc5d9a04af720211 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 7 Aug 2007 22:24:03 +0200 Subject: [PATCH 006/602] Work around misbehaved rename() on Windows. Windows's rename() is based on the MoveFile() API, which fails if the destination exists. Here we work around the problem by using MoveFileEx(). Furthermore, the posixly correct error is returned if the destination is a directory. The implementation is still slightly incomplete, however, because of the missing error code translation: We assume that the failure is due to permissions. --- builtin-reflog.c | 6 ------ compat/mingw.c | 25 +++++++++++++++++++++++++ git-compat-util.h | 3 +++ lockfile.c | 1 - refs.c | 10 ---------- 5 files changed, 28 insertions(+), 17 deletions(-) diff --git a/builtin-reflog.c b/builtin-reflog.c index 27e251ff6d..ce093cad78 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -269,12 +269,6 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, if (fclose(cb.newlog)) status |= error("%s: %s", strerror(errno), newlog_path); -#ifdef __MINGW32__ - /* rename fails if the destination exists */ - if (unlink(log_file)) - status |= error("cannot remove %s", log_file); - else -#endif if (rename(newlog_path, log_file)) { status |= error("cannot rename %s to %s", newlog_path, log_file); diff --git a/compat/mingw.c b/compat/mingw.c index ac8b8e0414..7711a3fb39 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -322,3 +322,28 @@ int mingw_socket(int domain, int type, int protocol) } return s; } + +#undef rename +int mingw_rename(const char *pold, const char *pnew) +{ + /* + * Try native rename() first to get errno right. + * It is based on MoveFile(), which cannot overwrite existing files. + */ + if (!rename(pold, pnew)) + return 0; + if (errno != EEXIST) + return -1; + if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) + return 0; + /* TODO: translate more errors */ + if (GetLastError() == ERROR_ACCESS_DENIED) { + struct stat st; + if (!stat(pnew, &st) && S_ISDIR(st.st_mode)) { + errno = EISDIR; + return -1; + } + } + errno = EACCES; + return -1; +} diff --git a/git-compat-util.h b/git-compat-util.h index ddafec997f..98ad78d571 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -466,6 +466,9 @@ char *mingw_getcwd(char *pointer, int len); int mingw_socket(int domain, int type, int protocol); #define socket mingw_socket +int mingw_rename(const char*, const char*); +#define rename mingw_rename + #define setlinebuf(x) #define fsync(x) 0 diff --git a/lockfile.c b/lockfile.c index 84a6aa33bd..258fb3f5ef 100644 --- a/lockfile.c +++ b/lockfile.c @@ -167,7 +167,6 @@ int commit_lock_file(struct lock_file *lk) strcpy(result_file, lk->filename); i = strlen(result_file) - 5; /* .lock */ result_file[i] = 0; - unlink(result_file); i = rename(lk->filename, result_file); lk->filename[0] = 0; return i; diff --git a/refs.c b/refs.c index 3c477309bb..9c07f83b72 100644 --- a/refs.c +++ b/refs.c @@ -963,15 +963,6 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg) retry: if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) { -#ifdef __MINGW32__ - if (errno == EEXIST) { - struct stat st; - if (stat(git_path("logs/%s", newref), &st) == 0 && S_ISDIR(st.st_mode)) - errno = EISDIR; - else - errno = EEXIST; - } -#endif if (errno==EISDIR || errno==ENOTDIR) { /* * rename(a, b) when b is an existing @@ -1237,7 +1228,6 @@ int create_symref(const char *ref_target, const char *refs_heads_master, error("Unable to write to %s", lockpath); goto error_unlink_return; } - unlink(git_HEAD); if (rename(lockpath, git_HEAD) < 0) { error("Unable to create %s", git_HEAD); goto error_unlink_return; From 1c32cf28d70781ebb843d436558e0b328b438ec6 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 7 Aug 2007 22:25:13 +0200 Subject: [PATCH 007/602] Skip again some tests involving symbolic links. --- t/t0000-basic.sh | 1 + t/t1300-repo-config.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 2c3b6b797c..a213268cd6 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -305,6 +305,7 @@ test_expect_success 'update-index D/F conflict' ' test $numpath0 = 1 ' +test "$no_symlinks" || test_expect_success 'absolute path works as expected' ' mkdir first && ln -s ../.git first/.git && diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 1d2bf2c060..12285d674b 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -601,6 +601,7 @@ echo >>result test_expect_success '--null --get-regexp' 'cmp result expect' +test "$no_symlinks" || test_expect_success 'symlinked configuration' ' ln -s notyet myconfig && From 52b4f704fd0d3f1ced8419cbcb460dd8374d8220 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Tue, 7 Aug 2007 22:26:45 +0200 Subject: [PATCH 008/602] builtin run_command: do not exit with -1. The shell does not correctly detect an exit code of -1 as a failure. We simply truncate the status code to the lower 8 bits. --- git.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/git.c b/git.c index fd604b51fc..adbc188d86 100644 --- a/git.c +++ b/git.c @@ -288,7 +288,7 @@ static int run_command(struct cmd_struct *p, int argc, const char **argv) status = p->fn(argc, argv, prefix); if (status) - return status; + return status & 0xff; /* Somebody closed stdout? */ if (fstat(fileno(stdout), &st)) From c603988c10c1d897e6f15e18b7727ec1bb1711b9 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 8 Aug 2007 21:35:02 +0200 Subject: [PATCH 009/602] t/test-lib.sh: Automatically detect whether symbolic links are supported. This reverts the part of commit cb0ad0c0af084b52a72791feeca7c70bd8ed855a that hands down the explicit NO_SYMLINKS setting from the top-level Makefile. --- Makefile | 2 -- t/Makefile | 4 ---- t/test-lib.sh | 6 ++++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f94bf01450..8127a5569d 100644 --- a/Makefile +++ b/Makefile @@ -492,7 +492,6 @@ ifneq (,$(findstring MINGW,$(uname_S))) OLD_ICONV = YesPlease NO_C99_FORMAT = YesPlease NO_STRTOUMAX = YesPlease - NO_SYMLINKS=YesPlease NO_SVN_TESTS=YesPlease NO_PERL_MAKEMAKER=YesPlease COMPAT_CFLAGS += -DNO_ETC_PASSWD -DNO_ST_BLOCKS -DSTRIP_EXTENSION=\".exe\" -I compat @@ -987,7 +986,6 @@ all: $(TEST_PROGRAMS) # However, the environment gets quite big, and some programs have problems # with that. -export NO_SYMLINKS export NO_SVN_TESTS test: all diff --git a/t/Makefile b/t/Makefile index f8e396c108..72d7884232 100644 --- a/t/Makefile +++ b/t/Makefile @@ -14,10 +14,6 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) TSVN = $(wildcard t91[0-9][0-9]-*.sh) -ifdef NO_SYMLINKS - GIT_TEST_OPTS += --no-symlinks -endif - all: $(T) clean $(T): diff --git a/t/test-lib.sh b/t/test-lib.sh index 6081d172ab..a3f3d7c922 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -87,8 +87,6 @@ do --no-python) # noop now... shift ;; - --no-symlinks) - no_symlinks=t; shift ;; *) break ;; esac @@ -285,6 +283,10 @@ rm -fr "$test" test_create_repo $test cd "$test" +# test for symbolic link capability +ln -s x y 2> /dev/null && test -l y 2> /dev/null || no_symlinks=1 +rm -f y + this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$') for skp in $GIT_SKIP_TESTS do From 7e6ad42588fb341f1328acb072f0720a4c411705 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 23 Jul 2007 22:11:54 +0200 Subject: [PATCH 010/602] Skip some git diff tests if the file system does not support funny names. Signed-off-by: Johannes Sixt --- t/t3902-quoted.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index 63f950b179..e8ad85afe4 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -14,6 +14,13 @@ LF=' ' DQ='"' +echo foo > "Name and an${HT}HT" +test -f "Name and an${HT}HT" || { + # since FAT/NTFS does not allow tabs in filenames, skip this test + say 'Your filesystem does not allow tabs in filenames, test skipped.' + test_done +} + for_each_name () { for name in \ Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \ From 733bd67d92a3dfa0a10a4c17ebbe82195891bd3e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 9 Aug 2007 22:32:09 +0200 Subject: [PATCH 011/602] Fix mode of shell scripts. These are the result of mismerges by old Windows versions of git. --- git-clone.sh | 0 git-rebase.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 git-clone.sh mode change 100644 => 100755 git-rebase.sh diff --git a/git-clone.sh b/git-clone.sh old mode 100644 new mode 100755 diff --git a/git-rebase.sh b/git-rebase.sh old mode 100644 new mode 100755 From 37ebf62c74202db267d2eb33283555d614edc70b Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 9 Aug 2007 22:33:20 +0200 Subject: [PATCH 012/602] Revert style breakage. The breakage is a result of editing and incomplete reversal. --- builtin-prune-packed.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c index 6473d327e6..977730064b 100644 --- a/builtin-prune-packed.c +++ b/builtin-prune-packed.c @@ -25,10 +25,8 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts) memcpy(pathname + len, de->d_name, 38); if (opts & DRY_RUN) printf("rm -f %s\n", pathname); - else { - if (unlink(pathname) < 0) - error("unable to unlink %s", pathname); - } + else if (unlink(pathname) < 0) + error("unable to unlink %s", pathname); } pathname[len] = 0; rmdir(pathname); From decd70079244e8daa16eb49c95f82c407629b540 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Thu, 9 Aug 2007 23:07:30 +0200 Subject: [PATCH 013/602] Use is_absolute_path() in more places. --- builtin-init-db.c | 2 +- config.c | 2 +- exec_cmd.c | 12 ++---------- git.c | 14 ++++---------- setup.c | 5 +---- 5 files changed, 9 insertions(+), 26 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index 2a20737249..c51c586141 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -140,7 +140,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di * interpreted relative to the exec_dir */ template_dir = DEFAULT_GIT_TEMPLATE_DIR; - if (template_dir[0] != '/' && template_dir[1] != ':') { + if (!is_absolute_path(template_dir)) { const char *exec_path = git_exec_path(); template_dir = prefix_path(exec_path, strlen(exec_path), template_dir); diff --git a/config.c b/config.c index fb4576f87f..78bd267070 100644 --- a/config.c +++ b/config.c @@ -454,7 +454,7 @@ const char *git_etc_gitconfig(void) if (!system_wide) { system_wide = ETC_GITCONFIG; /* interpret path relative to exec-dir */ - if (system_wide[0] != '/' && system_wide[1] != ':') { + if (!is_absolute_path(system_wide)) { const char *exec_path = git_exec_path(); system_wide = prefix_path(exec_path, strlen(exec_path), system_wide); diff --git a/exec_cmd.c b/exec_cmd.c index a9886b87fd..bad4843113 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -80,11 +80,7 @@ int execv_git_cmd(const char **argv) if (!exec_dir || !*exec_dir) continue; -#ifdef __MINGW32__ - if (*exec_dir != '/' && exec_dir[1] != ':') { -#else - if (*exec_dir != '/') { -#endif + if (!is_absolute_path(exec_dir)) { if (!getcwd(git_command, sizeof(git_command))) { fprintf(stderr, "git: cannot determine " "current directory: %s\n", @@ -190,11 +186,7 @@ int spawnv_git_cmd(const char **argv, int pin[2], int pout[2]) if (!exec_dir || !*exec_dir) continue; -#ifdef __MINGW32__ - if (*exec_dir != '/' && exec_dir[1] != ':') { -#else - if (*exec_dir != '/') { -#endif + if (!is_absolute_path(exec_dir)) { if (!getcwd(p[i], sizeof(p[i]))) { fprintf(stderr, "git: cannot determine " "current directory: %s\n", diff --git a/git.c b/git.c index adbc188d86..9908a60007 100644 --- a/git.c +++ b/git.c @@ -422,22 +422,16 @@ int main(int argc, const char **argv) * if it's an absolute path and we don't have * anything better. */ - if (slash) { - *slash++ = 0; - if (*cmd == '/') - exec_path = cmd; - cmd = slash; - } - #ifdef __MINGW32__ - slash = strrchr(cmd, '\\'); + if (!slash) + slash = strrchr(cmd, '\\'); +#endif if (slash) { *slash++ = 0; - if (cmd[1] == ':') + if (is_absolute_path(cmd)) exec_path = cmd; cmd = slash; } -#endif /* * "git-xxxx" is the same as "git xxxx", but we obviously: diff --git a/setup.c b/setup.c index fa5f724d21..15100bc1b7 100644 --- a/setup.c +++ b/setup.c @@ -88,10 +88,7 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) return arg; #else /* don't add prefix to absolute paths */ - const int is_absolute = - is_dir_sep(arg[0]) || - (arg[0] && arg[1] == ':' && is_dir_sep(arg[2])); - if (is_absolute) + if (is_absolute_path(arg)) pfx_len = 0; else #endif From ec33dd4c530319fc95f5481d057b2ad940e91694 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Sat, 11 Aug 2007 22:14:34 +0200 Subject: [PATCH 014/602] t7501-commit.sh: Not all seds understand option -i Use mv instead. Signed-off-by: Johannes Sixt --- t/t7501-commit.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 6bd3c9e3e0..d1284140d7 100644 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -69,7 +69,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/a file/an amend commit/g" $1 +sed -e "s/a file/an amend commit/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor @@ -88,7 +89,8 @@ test_expect_success \ cat >editor <<\EOF #!/bin/sh -sed -i -e "s/amend/older/g" $1 +sed -e "s/amend/older/g" < $1 > $1- +mv $1- $1 EOF chmod 755 editor From e341c06d8140b689001ddc183ec3476c1ede264a Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 12 Aug 2007 12:42:57 +1000 Subject: [PATCH 015/602] 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 6c75a2449182af7153d00c06bcda853258a10b21 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 17 Aug 2007 11:53:24 +0200 Subject: [PATCH 016/602] Do not hard-code paths for the shell and perl. The reason to set SHELL_PATH and PERL_PATH was that 'make' failed to execute /bin/sh some-script It turns out that different MinGW/MSYS environments have different levels of support for this construct, so use the default and let the user override the settings in config.mak if the defaults don't work. --- Makefile | 2 -- README.MinGW | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8127a5569d..bd25c8abf6 100644 --- a/Makefile +++ b/Makefile @@ -475,8 +475,6 @@ ifeq ($(uname_S),IRIX64) BASIC_LDFLAGS += -L/usr/lib32 endif ifneq (,$(findstring MINGW,$(uname_S))) - SHELL_PATH = $(shell cd /bin && pwd -W)/sh - PERL_PATH = $(shell cd /bin && pwd -W)/perl NO_MMAP=YesPlease NO_PREAD=YesPlease NO_OPENSSL=YesPlease diff --git a/README.MinGW b/README.MinGW index 436d7b7849..74eb104c2c 100644 --- a/README.MinGW +++ b/README.MinGW @@ -31,6 +31,13 @@ In order to compile this code you need: libiconv-1.9.2-1-{lib,bin,lib}.zip (for git-am, from http://gnuwin32.sourceforge.net/packages/libiconv.htm) +I am using these settings in config.mak to have pointers to the right tools: + +TCL_PATH=tclsh84 +TCLTK_PATH=wish84 +SHELL_PATH=D:/MSYS/1.0/bin/sh +PERL_PATH=D:/MSYS/1.0/bin/perl + STATUS ------ From a9601507cf0bc6d987117dfcd0e3d98873f4ab61 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 17 Aug 2007 12:27:54 +0200 Subject: [PATCH 017/602] Move MinGW specific declaration of mkstmp() up to avoid warnings. The declaration is needed in the xmkstmp() implementation. --- git-compat-util.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/git-compat-util.h b/git-compat-util.h index 219ac871b4..75b2a4bc9b 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -54,6 +54,8 @@ #include #include #include +#else +int mkstemp (char *__template); #endif #include #include @@ -435,7 +437,6 @@ int kill(pid_t pid, int sig); unsigned int sleep (unsigned int __seconds); const char *inet_ntop(int af, const void *src, char *dst, size_t cnt); -int mkstemp (char *__template); int gettimeofday(struct timeval *tv, void *tz); int pipe(int filedes[2]); From 94be0afa317c2cd54456652970cfefa7767b464f Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 17 Aug 2007 13:16:01 +0200 Subject: [PATCH 018/602] Provide a definition of SIGPIPE. --- git-compat-util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/git-compat-util.h b/git-compat-util.h index 75b2a4bc9b..22a53d3668 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -431,6 +431,7 @@ typedef int pid_t; #define SIGKILL 0 #define SIGCHLD 0 #define SIGALRM 0 +#define SIGPIPE 0 #define ECONNABORTED 0 int kill(pid_t pid, int sig); From 009ab81675846be538bd337d9addd9965d2925aa Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 17 Aug 2007 15:14:57 +0200 Subject: [PATCH 019/602] Avoid spawning 'ls' for each directory that is passed to the cpio emulator. We assume that the file names are passed in using find -depth so that a directory is listed after its contents. --- cpio.sh | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/cpio.sh b/cpio.sh index 2816e34851..df5f64c14c 100644 --- a/cpio.sh +++ b/cpio.sh @@ -24,16 +24,24 @@ while test $# -gt 0; do shift done +prev= + filterdirs() { while read f; do if test -d "$f"; then # list only empty directories - if test -z "$(ls -A "$f")"; then - echo "$f" - fi + # here we assume that directories are listed after + # its files (aka 'find -depth'), hence, a directory + # that is not empty will be a leading sub-string + # of the preceding entry + case "$prev" in + "$f"/* ) ;; + *) echo "$f";; + esac else echo "$f" fi + prev="$f" done } From 942afeb88b16e1be0b653114491668b2140ca5e1 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 17 Aug 2007 18:40:36 +0200 Subject: [PATCH 020/602] Work around a Windows oddity when a pipe with no reader is written to. The first WriteFile() returns ERROR_BROKEN_PIPE, subsequent WriteFile()s return ERROR_NO_DATA, which is not translated to EPIPE, but EINVAL. Hmpf! --- write_or_die.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/write_or_die.c b/write_or_die.c index e125e11d3b..40b046acb0 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -34,7 +34,16 @@ void maybe_flush_or_die(FILE *f, const char *desc) return; } if (fflush(f)) { +#ifndef __MINGW32__ if (errno == EPIPE) +#else + /* + * On Windows, EPIPE is returned only by the first write() + * after the reading end has closed its handle; subsequent + * write()s return EINVAL. + */ + if (errno == EPIPE || errno == EINVAL) +#endif exit(0); die("write failure on %s: %s", desc, strerror(errno)); } From fce4254719a628b469c7a8c1a2ceb025e630bf2a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 20 Aug 2007 16:36:58 +0200 Subject: [PATCH 021/602] Fix an infinite loop in setup_git_directory_gently(). When a git command was invoked outside any git directory, it would not terminate. --- setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.c b/setup.c index d575529ac9..404191ab8f 100644 --- a/setup.c +++ b/setup.c @@ -302,7 +302,7 @@ const char *setup_git_directory_gently(int *nongit_ok) } chdir(".."); do { - if (!offset) { + if (offset <= minoffset) { if (nongit_ok) { if (chdir(cwd)) die("Cannot come back to cwd"); From 92ed666fa761554c67c8f883863517870a65376d Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 22 Aug 2007 22:35:28 +1000 Subject: [PATCH 022/602] 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 023/602] 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 024/602] 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 025/602] 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 6e6b01d5d308a9bbb5d73cd9272d8736039d83a2 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 29 Aug 2007 13:34:54 +0200 Subject: [PATCH 026/602] Add a primitive implementation for getppid(). --- git-compat-util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/git-compat-util.h b/git-compat-util.h index 22a53d3668..1ba499f750 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -483,6 +483,7 @@ int mingw_rename(const char*, const char*); #define setlinebuf(x) #define fsync(x) 0 +#define getppid() 1 extern void quote_argv(const char **dst, const char **src); extern const char *parse_interpreter(const char *cmd); From 0380081c65c3e8a46caad9aebe8e97ff65510453 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 29 Aug 2007 21:45:21 +1000 Subject: [PATCH 027/602] 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 028/602] 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 029/602] 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 14e86ec37e13f6018513c630f4b8b54096e794f0 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 29 Aug 2007 14:49:47 +0200 Subject: [PATCH 030/602] Use 'pwd -W' as reference directory. git does not know about the notation /x/dir/ec/tory that MSYS uses instead of x:/dir/ec/tory; hence, when git's notion of an absolute path is compared to the shell's notion, the shell must produce the x:/ kind of path. --- t/t0001-init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t0001-init.sh b/t/t0001-init.sh index b14b3ec394..ca5b631012 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -97,7 +97,7 @@ test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' ' mkdir git-dir-wt-1.git && GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init ) && - check_config git-dir-wt-1.git false "$(pwd)" + check_config git-dir-wt-1.git false "$(pwd -W)" ' test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' ' From 205d0cb456f7d253f6fcd0566528c1d2796f0be5 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Wed, 29 Aug 2007 14:50:58 +0200 Subject: [PATCH 031/602] Use /usr/bin/find instead of plain find. --- t/t9300-fast-import.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 0595041af5..a64f78f5a0 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -880,7 +880,7 @@ INPUT_END test_expect_success \ 'O: blank lines not necessary after other commands' \ 'git-fast-import actual && git diff expect actual' From b01b123d9dea5658a5e8eb8d392c5cdbe23256a5 Mon Sep 17 00:00:00 2001 From: Dmitry Kakurin Date: Wed, 29 Aug 2007 15:35:00 +0200 Subject: [PATCH 032/602] Fix error 'fatal: Not a git repository' on Vista Define __USE_MINGW_ACCESS, which changes access( ..., X_OK ) into F_OK. Signed-off-by: Dmitry Kakurin gmail.com> --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f828f6cd12..9669817bf4 100644 --- a/Makefile +++ b/Makefile @@ -493,7 +493,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) NO_STRTOUMAX = YesPlease NO_SVN_TESTS=YesPlease NO_PERL_MAKEMAKER=YesPlease - COMPAT_CFLAGS += -DNO_ETC_PASSWD -DNO_ST_BLOCKS -DSTRIP_EXTENSION=\".exe\" -I compat + COMPAT_CFLAGS += -DNO_ETC_PASSWD -DNO_ST_BLOCKS -DSTRIP_EXTENSION=\".exe\" -D__USE_MINGW_ACCESS -I compat COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o EXTLIBS += -lws2_32 X = .exe From fce2bf8842e8224822619afabff1bfcfc3546436 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 17 Aug 2007 14:50:29 +0200 Subject: [PATCH 033/602] Revert fa807fcb: 'Revert "Makefile: remove $foo when $foo.exe...' It works in msysgit, so there is no reason why we should not remove the scripted versions. Especially since some tests were failing for the sole reason that _not_ the builtin, but the script was executed. Signed-off-by: Johannes Schindelin --- Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 9669817bf4..bc8e9d2125 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # The default target of this Makefile is... -all: +all:: # Define V=1 to have a more verbose compile. # @@ -752,9 +752,12 @@ export TAR INSTALL DESTDIR SHELL_PATH ### Build rules -all: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) +all:: $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) +ifneq (,$X) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';) +endif -all: +all:: ifndef NO_TCLTK $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) all endif @@ -982,7 +985,7 @@ endif TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X -all: $(TEST_PROGRAMS) +all:: $(TEST_PROGRAMS) # GNU make supports exporting all variables by "export" without parameters. # However, the environment gets quite big, and some programs have problems @@ -1030,6 +1033,9 @@ endif '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ fi $(foreach p,$(BUILT_INS), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) +ifneq (,$X) + $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';) +endif install-doc: $(MAKE) -C Documentation install From 5cd15b6b7f87dc61f729ad31a682ffc394560273 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 30 Aug 2007 21:54:17 +1000 Subject: [PATCH 034/602] 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 776398709dee4050fc194fec45c5818ba9b01afe Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 1 Sep 2007 23:53:47 -0700 Subject: [PATCH 035/602] Keep last used delta base in the delta window This is based on Martin Koegler's idea to keep the object that was successfully used as the base of the delta when it is about to fall off the edge of the window. Instead of doing so only for the objects at the edge of the window, this makes the window a lru eviction mechanism. If an entry is used as a base, it is moved to the last of the queue to be evicted. This is a quick-and-dirty implementation, as it keeps the original implementation of the data structure used for the window. This originally was done as an array, not as an array of pointers, because it was meant to be used as a cyclic FIFO buffer and a plain array avoids an extra pointer indirection, while its FIFOness eant that we are not "moving" the entries like this patch does. The runtime from three versions were comparable. It seems to make the resulting chain even shorter, which can only be good. (stock "master") 15782196 bytes chain length = 1: 2972 objects chain length = 2: 2651 objects chain length = 3: 2369 objects chain length = 4: 2121 objects chain length = 5: 1877 objects ... chain length = 46: 490 objects chain length = 47: 515 objects chain length = 48: 527 objects chain length = 49: 570 objects chain length = 50: 408 objects (with your patch) 15745736 bytes (0.23% smaller) chain length = 1: 3137 objects chain length = 2: 2688 objects chain length = 3: 2322 objects chain length = 4: 2146 objects chain length = 5: 1824 objects ... chain length = 46: 503 objects chain length = 47: 509 objects chain length = 48: 536 objects chain length = 49: 588 objects chain length = 50: 357 objects (with this patch) 15612086 bytes (1.08% smaller) chain length = 1: 4831 objects chain length = 2: 3811 objects chain length = 3: 2964 objects chain length = 4: 2352 objects chain length = 5: 1944 objects ... chain length = 46: 327 objects chain length = 47: 353 objects chain length = 48: 304 objects chain length = 49: 298 objects chain length = 50: 135 objects [jc: this is with code simplification follow-up from Nico] Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 12509faa77..e64e3a03a0 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1460,7 +1460,7 @@ static void find_deltas(struct object_entry **list, int window, int depth) do { struct object_entry *entry = list[--i]; struct unpacked *n = array + idx; - int j; + int j, best_base = -1; if (!entry->preferred_base) processed++; @@ -1505,6 +1505,7 @@ static void find_deltas(struct object_entry **list, int window, int depth) j = window; while (--j > 0) { + int ret; uint32_t other_idx = idx + j; struct unpacked *m; if (other_idx >= window) @@ -1512,8 +1513,11 @@ static void find_deltas(struct object_entry **list, int window, int depth) m = array + other_idx; if (!m->entry) break; - if (try_delta(n, m, max_depth) < 0) + ret = try_delta(n, m, max_depth); + if (ret < 0) break; + else if (ret > 0) + best_base = other_idx; } /* if we made n a delta, and if n is already at max @@ -1523,6 +1527,23 @@ static void find_deltas(struct object_entry **list, int window, int depth) if (entry->delta && depth <= n->depth) continue; + /* + * Move the best delta base up in the window, after the + * currently deltified object, to keep it longer. It will + * be the first base object to be attempted next. + */ + if (entry->delta) { + struct unpacked swap = array[best_base]; + int dist = (window + idx - best_base) % window; + int dst = best_base; + while (dist--) { + int src = (dst + 1) % window; + array[dst] = array[src]; + dst = src; + } + array[dst] = swap; + } + next: idx++; if (count + 1 < window) From 030e0e5fb77ed04316b2604b5c53a38b0e746460 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Sep 2007 15:03:26 -0700 Subject: [PATCH 036/602] Typofix: 1.5.3 release notes --- Documentation/RelNotes-1.5.3.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt index 317c8b9db6..d03894b926 100644 --- a/Documentation/RelNotes-1.5.3.txt +++ b/Documentation/RelNotes-1.5.3.txt @@ -152,7 +152,7 @@ Updates since v1.5.2 cloning locally. - URL used for "git clone" and friends can specify nonstandard SSH port - by using sh://host:port/path/to/repo syntax. + by using ssh://host:port/path/to/repo syntax. - "git bundle create" can now create a bundle without negative refs, i.e. "everything since the beginning up to certain points". From 5587cac28be66acf5edc2a4b83b67c8cfffbc5e9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Sep 2007 15:16:44 -0700 Subject: [PATCH 037/602] GIT 1.5.3.1: obsolete git-p4 in RPM spec file. HPA noticed that yum does not like the newer git RPM set; it turns out that we do not ship git-p4 anymore but existing installations do not realize the package is gone if we do not tell anything about it. David Kastrup suggests using Obsoletes in the spec file of the new RPM to replace the old package, so here is a try. Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.3.1.txt | 10 ++++++++++ Documentation/git.txt | 5 ++++- GIT-VERSION-GEN | 2 +- RelNotes | 2 +- git.spec.in | 1 + 5 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 Documentation/RelNotes-1.5.3.1.txt diff --git a/Documentation/RelNotes-1.5.3.1.txt b/Documentation/RelNotes-1.5.3.1.txt new file mode 100644 index 0000000000..7ff546c743 --- /dev/null +++ b/Documentation/RelNotes-1.5.3.1.txt @@ -0,0 +1,10 @@ +GIT v1.5.3.1 Release Notes +========================== + +Fixes since v1.5.3 +------------------ + +This is solely to fix the generated RPM's dependencies. We used +to have git-p4 package but we do not anymore. As suggested on +the mailing list, this release makes git-core "Obsolete" git-p4, +so that yum update would not complain. diff --git a/Documentation/git.txt b/Documentation/git.txt index ceca892a0d..6f7db2935b 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -43,7 +43,10 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.5.2.5/git.html[documentation for release 1.5.2.5] +* link:v1.5.3/git.html[documentation for release 1.5.3] + +* release notes for + link:RelNotes-1.5.3.1.txt[1.5.3.1]. * release notes for link:RelNotes-1.5.2.5.txt[1.5.2.5], diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 3c0032cec5..3835fb3965 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.3.GIT +DEF_VER=v1.5.3.1.GIT LF=' ' diff --git a/RelNotes b/RelNotes index 0de5e663a2..ea8f800cbb 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.3.txt \ No newline at end of file +Documentation/RelNotes-1.5.3.1.txt \ No newline at end of file diff --git a/git.spec.in b/git.spec.in index fe7b3d8215..bdb293d990 100644 --- a/git.spec.in +++ b/git.spec.in @@ -25,6 +25,7 @@ This is a dummy package which brings in all subpackages. Summary: Core git tools Group: Development/Tools Requires: zlib >= 1.2, rsync, curl, less, openssh-clients, expat +Obsoletes: git-p4 %description core Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations From 1e61b7640d09015213dbcae3564fa27ac6a8c151 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 3 Sep 2007 02:40:06 -0700 Subject: [PATCH 038/602] Start 1.5.4 cycle Signed-off-by: Junio C Hamano --- Documentation/RelNotes-1.5.4.txt | 14 ++++++++++++++ GIT-VERSION-GEN | 2 +- RelNotes | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 Documentation/RelNotes-1.5.4.txt diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt new file mode 100644 index 0000000000..1df66af9ce --- /dev/null +++ b/Documentation/RelNotes-1.5.4.txt @@ -0,0 +1,14 @@ +GIT v1.5.4 Release Notes +======================== + +Updates since v1.5.3 +-------------------- + + + +Fixes since v1.5.3 +------------------ + +All of the fixes in v1.5.3 maintenance series are included in +this release, unless otherwise noted. + diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 3835fb3965..3c0032cec5 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.3.1.GIT +DEF_VER=v1.5.3.GIT LF=' ' diff --git a/RelNotes b/RelNotes index ea8f800cbb..46308cee0b 120000 --- a/RelNotes +++ b/RelNotes @@ -1 +1 @@ -Documentation/RelNotes-1.5.3.1.txt \ No newline at end of file +Documentation/RelNotes-1.5.4.txt \ No newline at end of file From 34cc60ce2b48f6037997543ddbab1ed9903df4a8 Mon Sep 17 00:00:00 2001 From: Douglas Stockwell Date: Mon, 3 Sep 2007 03:06:25 +0900 Subject: [PATCH 039/602] send-email: Add support for SSL and SMTP-AUTH Allows username and password to be given using --smtp-user and --smtp-pass. SSL use is flagged by --smtp-ssl. These are backed by corresponding defaults in the git configuration file. This implements Junio's 'mail identity' suggestion in a slightly more generalised manner. --identity=$identity, backed by sendemail.identity indicates that the configuration subsection [sendemail "$identity"] should take priority over the [sendemail] section for all configuration values. Signed-off-by: Douglas Stockwell Signed-off-by: Junio C Hamano --- Documentation/git-send-email.txt | 35 ++++++++++- git-send-email.perl | 101 +++++++++++++++++++++++-------- 2 files changed, 109 insertions(+), 27 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 16bfd7be22..1ec61affab 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -75,6 +75,12 @@ The --cc option must be repeated for each user you want on the cc list. Make git-send-email less verbose. One line per email should be all that is output. +--identity:: + A configuration identity. When given, causes values in the + 'sendemail.' subsection to take precedence over + values in the 'sendemail' section. The default identity is + the value of 'sendemail.identity'. + --smtp-server:: If set, specifies the outgoing SMTP server to use (e.g. `smtp.example.com` or a raw IP address). Alternatively it can @@ -85,6 +91,17 @@ The --cc option must be repeated for each user you want on the cc list. `/usr/lib/sendmail` if such program is available, or `localhost` otherwise. +--smtp-user, --smtp-pass:: + Username and password for SMTP-AUTH. Defaults are the values of + the configuration values 'sendemail.smtpuser' and + 'sendemail.smtppass', but see also 'sendemail.identity'. + If not set, authentication is not attempted. + +--smtp-ssl:: + If set, connects to the SMTP server using SSL. + Default is the value of the 'sendemail.smtpssl' configuration value; + if that is unspecified, does not use SSL. + --subject:: Specify the initial subject of the email thread. Only necessary if --compose is also set. If --compose @@ -122,6 +139,13 @@ The --to option must be repeated for each user you want on the to list. CONFIGURATION ------------- +sendemail.identity:: + The default configuration identity. When specified, + 'sendemail..' will have higher precedence than + 'sendemail.'. This is useful to declare multiple SMTP + identities and to hoist sensitive authentication information + out of the repository and into the global configuation file. + sendemail.aliasesfile:: To avoid typing long email addresses, point this to one or more email aliases files. You must also supply 'sendemail.aliasfiletype'. @@ -141,7 +165,16 @@ sendemail.chainreplyto:: parameter. sendemail.smtpserver:: - Default smtp server to use. + Default SMTP server to use. + +sendemail.smtpuser:: + Default SMTP-AUTH username. + +sendemail.smtppass:: + Default SMTP-AUTH password. + +sendemail.smtpssl:: + Boolean value specifying the default to the '--smtp-ssl' parameter. Author ------ diff --git a/git-send-email.perl b/git-send-email.perl index e0b7d1245e..dd7560b180 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -73,9 +73,18 @@ Options: --signed-off-cc Automatically add email addresses that appear in Signed-off-by: or Cc: lines to the cc: list. Defaults to on. + --identity The configuration identity, a subsection to prioritise over + the default section. + --smtp-server If set, specifies the outgoing SMTP server to use. Defaults to localhost. + --smtp-user The username for SMTP-AUTH. + + --smtp-pass The password for SMTP-AUTH. + + --smtp-ssl If set, connects to the SMTP server using SSL. + --suppress-from Suppress sending emails to yourself if your address appears in a From: line. Defaults to off. @@ -145,7 +154,6 @@ my $compose_filename = ".msg.$$"; my (@to,@cc,@initial_cc,@bcclist,@xh, $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time); -my $smtp_server; my $envelope_sender; # Example reply to: @@ -164,24 +172,26 @@ my ($quiet, $dry_run) = (0, 0); # Variables with corresponding config settings my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd); +my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl); +my ($identity, $aliasfiletype, @alias_files); -my %config_settings = ( +my %config_bool_settings = ( "thread" => [\$thread, 1], "chainreplyto" => [\$chain_reply_to, 1], "suppressfrom" => [\$suppress_from, 0], "signedoffcc" => [\$signed_off_cc, 1], - "cccmd" => [\$cc_cmd, ""], + "smtpssl" => [\$smtp_ssl, 0], ); -foreach my $setting (keys %config_settings) { - my $config = $repo->config_bool("sendemail.$setting"); - ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1]; -} - -@bcclist = $repo->config('sendemail.bcc'); -if (!@bcclist or !$bcclist[0]) { - @bcclist = (); -} +my %config_settings = ( + "smtpserver" => \$smtp_server, + "smtpuser" => \$smtp_authuser, + "smtppass" => \$smtp_authpass, + "cccmd" => \$cc_cmd, + "aliasfiletype" => \$aliasfiletype, + "bcc" => \@bcclist, + "aliasesfile" => \@alias_files, +); # Begin by accumulating all the variables (defined above), that we will end up # needing, first, from the command line: @@ -194,6 +204,10 @@ my $rc = GetOptions("sender|from=s" => \$sender, "bcc=s" => \@bcclist, "chain-reply-to!" => \$chain_reply_to, "smtp-server=s" => \$smtp_server, + "smtp-user=s" => \$smtp_authuser, + "smtp-pass=s" => \$smtp_authpass, + "smtp-ssl!" => \$smtp_ssl, + "identity=s" => \$identity, "compose" => \$compose, "quiet" => \$quiet, "cc-cmd=s" => \$cc_cmd, @@ -208,6 +222,43 @@ unless ($rc) { usage(); } +# Now, let's fill any that aren't set in with defaults: + +sub read_config { + my ($prefix) = @_; + + foreach my $setting (keys %config_bool_settings) { + my $target = $config_bool_settings{$setting}->[0]; + $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target); + } + + foreach my $setting (keys %config_settings) { + my $target = $config_settings{$setting}; + if (ref($target) eq "ARRAY") { + unless (@$target) { + my @values = $repo->config("$prefix.$setting"); + @$target = @values if (@values && defined $values[0]); + } + } + else { + $$target = $repo->config("$prefix.$setting") unless (defined $$target); + } + } +} + +# read configuration from [sendemail "$identity"], fall back on [sendemail] +$identity = $repo->config("sendemail.identity") unless (defined $identity); +read_config("sendemail.$identity") if (defined $identity); +read_config("sendemail"); + +# fall back on builtin bool defaults +foreach my $setting (values %config_bool_settings) { + ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]})); +} + +my ($repoauthor) = $repo->ident_person('author'); +my ($repocommitter) = $repo->ident_person('committer'); + # Verify the user input foreach my $entry (@to) { @@ -222,14 +273,7 @@ foreach my $entry (@bcclist) { die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/; } -# Now, let's fill any that aren't set in with defaults: - -my ($repoauthor) = $repo->ident_person('author'); -my ($repocommitter) = $repo->ident_person('committer'); - my %aliases; -my @alias_files = $repo->config('sendemail.aliasesfile'); -my $aliasfiletype = $repo->config('sendemail.aliasfiletype'); my %parse_alias = ( # multiline formats can be supported in the future mutt => sub { my $fh = shift; while (<$fh>) { @@ -320,10 +364,7 @@ if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to =~ s/(^\s+|\s+$)//g; } -if (!$smtp_server) { - $smtp_server = $repo->config('sendemail.smtpserver'); -} -if (!$smtp_server) { +if (!defined $smtp_server) { foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) { if (-x $_) { $smtp_server = $_; @@ -553,8 +594,16 @@ X-Mailer: git-send-email $gitversion print $sm "$header\n$message"; close $sm or die $?; } else { - require Net::SMTP; - $smtp ||= Net::SMTP->new( $smtp_server ); + if ($smtp_ssl) { + require Net::SMTP::SSL; + $smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 ); + } + else { + require Net::SMTP; + $smtp ||= Net::SMTP->new( $smtp_server ); + } + $smtp->auth( $smtp_authuser, $smtp_authpass ) + or die $smtp->message if (defined $smtp_authuser); $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; $smtp->data or die $smtp->message; @@ -661,7 +710,7 @@ foreach my $t (@files) { } close F; - if ($cc_cmd ne "") { + if (defined $cc_cmd) { open(F, "$cc_cmd $t |") or die "(cc-cmd) Could not execute '$cc_cmd'"; while() { From 38944390220425cc3c4208dd31172397e7f18e8c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 2 Sep 2007 21:10:14 +0100 Subject: [PATCH 040/602] Teach "git remote" a mirror mode When using the "--mirror" option to "git remote add", the refs will not be stored in the refs/remotes/ namespace, but in the same location as on the remote side. This option probably only makes sense in a bare repository. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- Documentation/git-remote.txt | 6 +++++- git-remote.perl | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt index 61a6022ce8..94b9f17772 100644 --- a/Documentation/git-remote.txt +++ b/Documentation/git-remote.txt @@ -10,7 +10,7 @@ SYNOPSIS -------- [verse] 'git-remote' -'git-remote' add [-t ] [-m ] [-f] +'git-remote' add [-t ] [-m ] [-f] [--mirror] 'git-remote' show 'git-remote' prune 'git-remote' update [group] @@ -45,6 +45,10 @@ multiple branches without grabbing all branches. With `-m ` option, `$GIT_DIR/remotes//HEAD` is set up to point at remote's `` branch instead of whatever branch the `HEAD` at the remote repository actually points at. ++ +In mirror mode, enabled with `--mirror`, the refs will not be stored +in the 'refs/remotes/' namespace, but in 'refs/heads/'. This option +only makes sense in bare repositories. 'show':: diff --git a/git-remote.perl b/git-remote.perl index 01cf480221..f6f283ea4f 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -278,7 +278,9 @@ sub add_remote { for (@$track) { $git->command('config', '--add', "remote.$name.fetch", - "+refs/heads/$_:refs/remotes/$name/$_"); + $opts->{'mirror'} ? + "+refs/$_:refs/$_" : + "+refs/heads/$_:refs/remotes/$name/$_"); } if ($opts->{'fetch'}) { $git->command('fetch', $name); @@ -409,6 +411,10 @@ elsif ($ARGV[0] eq 'add') { shift @ARGV; next; } + if ($opt eq '--mirror') { + $opts{'mirror'} = 1; + next; + } add_usage(); } if (@ARGV != 3) { From 31f9ec129ef37e50b5cacf26a2ebcb5420fcdc5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 21 Aug 2007 11:53:02 +0200 Subject: [PATCH 041/602] git-p4: Always call 'p4 sync ...' before submitting to Perforce. Acked-by: Marius Storm-Olsen Acked-by: Thiago Macieira --- contrib/fast-import/git-p4 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5775..3728cbf9aa 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -664,9 +664,8 @@ class P4Submit(Command): f.close(); os.chdir(self.clientPath) - response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) - if response == "y" or response == "yes": - system("p4 sync ...") + print "Syncronizing p4 checkout..." + system("p4 sync ...") if self.reset: self.firstTime = True From 14594f4b5747e51b051f647f6430089e6664e77d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 22 Aug 2007 09:07:15 +0200 Subject: [PATCH 042/602] git-p4: After submission to p4 always synchronize from p4 again (into refs/remotes). Whether to rebase HEAD or not is still left as question to the end-user. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3728cbf9aa..16e0a7bc81 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -704,10 +704,14 @@ class P4Submit(Command): else: print "All changes applied!" os.chdir(self.oldWorkingDirectory) - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + + sync = P4Sync() + sync.run([]) + + response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": rebase = P4Rebase() - rebase.run([]) + rebase.rebase() os.remove(self.configFile) return True @@ -1439,6 +1443,9 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) + return self.rebase() + + def rebase(self): [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") From 4f6432d8cc6ebdcdc366cf67ab39e8125c449d80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 15:56:36 +0200 Subject: [PATCH 043/602] git-p4: Cleanup; moved the code for getting a sorted list of p4 changes for a list of given depot paths into a standalone method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 16e0a7bc81..e9feb7498c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -281,6 +281,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent def originP4BranchesExist(): return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") +def p4ChangesForPaths(depotPaths, changeRange): + assert depotPaths + output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange) + for p in depotPaths])) + + changes = [] + for line in output: + changeNum = line.split(" ")[1] + changes.append(int(changeNum)) + + changes.sort() + return changes + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -1322,15 +1335,7 @@ class P4Sync(Command): if self.verbose: print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) - assert self.depotPaths - output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) - for p in self.depotPaths])) - - for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) - - changes.sort() + changes = p4ChangesForPaths(self.depotPaths, self.changeRange) if len(self.maxChanges) > 0: changes = changes[:min(int(self.maxChanges), len(changes))] From e87f37ae42dad89b39620c234fc29c94529a4d07 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:00:52 +0200 Subject: [PATCH 044/602] git-p4: Cleanup; moved the code to import a list of p4 changes using fast-import into a separate member function of P4Sync. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 140 +++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e9feb7498c..c00702c895 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,76 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def importChanges(self, changes): + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + self.updateOptionDict(description) + + if not self.silent: + sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + if self.detectBranches: + branches = self.splitFilesIntoBranches(description) + for branch in branches.keys(): + ## HACK --hwn + branchPrefix = self.depotPaths[0] + branch + "/" + + parent = "" + + filesForCommit = branches[branch] + + if self.verbose: + print "branch is %s" % branch + + self.updatedBranches.add(branch) + + if branch not in self.createdBranches: + self.createdBranches.add(branch) + parent = self.knownBranches[branch] + if parent == branch: + parent = "" + elif self.verbose: + print "parent determined through known branches: %s" % parent + + # main branch? use master + if branch == "main": + branch = "master" + else: + + ## FIXME + branch = self.projectName + branch + + if parent == "main": + parent = "master" + elif len(parent) > 0: + ## FIXME + parent = self.projectName + parent + + branch = self.refPrefix + branch + if len(parent) > 0: + parent = self.refPrefix + parent + + if self.verbose: + print "looking for initial parent for %s; current parent is %s" % (branch, parent) + + if len(parent) == 0 and branch in self.initialParents: + parent = self.initialParents[branch] + del self.initialParents[branch] + + self.commit(description, filesForCommit, branch, [branchPrefix], parent) + else: + files = self.extractFilesFromCommit(description) + self.commit(description, files, self.branch, self.depotPaths, + self.initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1350,74 +1420,7 @@ class P4Sync(Command): self.updatedBranches = set() - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - self.updateOptionDict(description) - - if not self.silent: - sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - if self.detectBranches: - branches = self.splitFilesIntoBranches(description) - for branch in branches.keys(): - ## HACK --hwn - branchPrefix = self.depotPaths[0] + branch + "/" - - parent = "" - - filesForCommit = branches[branch] - - if self.verbose: - print "branch is %s" % branch - - self.updatedBranches.add(branch) - - if branch not in self.createdBranches: - self.createdBranches.add(branch) - parent = self.knownBranches[branch] - if parent == branch: - parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent - - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent - - if self.verbose: - print "looking for initial parent for %s; current parent is %s" % (branch, parent) - - if len(parent) == 0 and branch in self.initialParents: - parent = self.initialParents[branch] - del self.initialParents[branch] - - self.commit(description, filesForCommit, branch, [branchPrefix], parent) - else: - files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPaths, - self.initialParent) - self.initialParent = "" - except IOError: - print self.gitError.read() - sys.exit(1) + self.importChanges(changes) if not self.silent: print "" @@ -1427,7 +1430,6 @@ class P4Sync(Command): sys.stdout.write("%s " % b) sys.stdout.write("\n") - self.gitStream.close() if importProcess.wait() != 0: die("fast-import failed: %s" % self.gitError.read()) From 1c49fc197bd05a4c2ed602efcdbe277ef798813a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:04:34 +0200 Subject: [PATCH 045/602] git-p4: Cleanup; Turn self.revision into a function local variable (it's not used anywhere outside the function). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c00702c895..d7c5becc0e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1285,7 +1285,7 @@ class P4Sync(Command): self.depotPaths = sorted(args) - self.revision = "" + revision = "" self.users = {} newPaths = [] @@ -1296,15 +1296,15 @@ class P4Sync(Command): if self.changeRange == "@all": self.changeRange = "" elif ',' not in self.changeRange: - self.revision = self.changeRange + revision = self.changeRange self.changeRange = "" p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") - self.revision = p[hashIdx:] + revision = p[hashIdx:] p = p[:hashIdx] elif self.previousDepotPaths == []: - self.revision = "#head" + revision = "#head" p = re.sub ("\.\.\.$", "", p) if not p.endswith("/"): @@ -1345,19 +1345,19 @@ class P4Sync(Command): self.gitStream = importProcess.stdin self.gitError = importProcess.stderr - if self.revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch) + if revision: + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), self.revision)) - details["change"] = self.revision + % (' '.join(self.depotPaths), revision)) + details["change"] = revision newestRevision = 0 fileCnt = 0 for info in p4CmdList("files " + ' '.join(["%s...%s" - % (p, self.revision) + % (p, revision) for p in self.depotPaths])): if info['code'] == 'error': From c208a24310582d9cf337b66f41a0d7a9fe106bb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:07:18 +0200 Subject: [PATCH 046/602] git-p4: Cleanup; moved the code for the initial #head or revision import into a separate function, out of P4Sync.run. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 87 ++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d7c5becc0e..2c67190ffc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1188,6 +1188,50 @@ class P4Sync(Command): print self.gitError.read() sys.exit(1) + def importHeadRevision(self, revision): + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = ("Initial import of %s from the state at revision %s" + % (' '.join(self.depotPaths), revision)) + details["change"] = revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files " + + ' '.join(["%s...%s" + % (p, revision) + for p in self.depotPaths])): + + if info['code'] == 'error': + sys.stderr.write("p4 returned an error: %s\n" + % info['data']) + sys.exit(1) + + + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! + #fileCnt = fileCnt + 1 + continue + + for prop in ["depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + self.updateOptionDict(details) + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) + except IOError: + print "IO error with git fast-import. Is your git version recent enough?" + print self.gitError.read() + + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1346,48 +1390,7 @@ class P4Sync(Command): self.gitError = importProcess.stderr if revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), revision)) - details["change"] = revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files " - + ' '.join(["%s...%s" - % (p, revision) - for p in self.depotPaths])): - - if info['code'] == 'error': - sys.stderr.write("p4 returned an error: %s\n" - % info['data']) - sys.exit(1) - - - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! - #fileCnt = fileCnt + 1 - continue - - for prop in ["depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - self.updateOptionDict(details) - try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) - except IOError: - print "IO error with git fast-import. Is your git version recent enough?" - print self.gitError.read() - + self.importHeadRevision(revision) else: changes = [] From 8134f69c21ff47283d8b3ea3cc5b306727cde256 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:44:55 +0200 Subject: [PATCH 047/602] git-p4: Cleanup; moved the (duplicated) code for turning a branch into a git ref (for example foo -> refs/remotes/p4//foo) into a separate method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2c67190ffc..406bec1a29 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,15 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def gitRefForBranch(self, branch): + if branch == "main": + return self.refPrefix + "master" + + if len(branch) <= 0: + return branch + + return self.refPrefix + self.projectName + branch + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1153,23 +1162,8 @@ class P4Sync(Command): elif self.verbose: print "parent determined through known branches: %s" % parent - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent + branch = self.gitRefForBranch(branch) + parent = self.gitRefForBranch(parent) if self.verbose: print "looking for initial parent for %s; current parent is %s" % (branch, parent) From 1ca3d71069620f1438d9f89165a3e69e8d47d302 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 17:36:55 +0200 Subject: [PATCH 048/602] git-p4: Added support for automatically importing newly appearing perforce branches. If a change in a p4 "branch" appears that hasn't seen any previous commit and that has a known branch mapping we now try to import it properly. First we find the p4 change of the source branch that the new p4 branch is based on. Then we using git rev-list --bisect to locate the corresponding git commit to that change. Finally we import all changes in the new p4 branch up to the current change and resume with the regular import. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 76 +++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 406bec1a29..adaaae6633 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1127,6 +1127,67 @@ class P4Sync(Command): return self.refPrefix + self.projectName + branch + def gitCommitByP4Change(self, ref, change): + if self.verbose: + print "looking in ref " + ref + " for change %s using bisect..." % change + + earliestCommit = "" + latestCommit = parseRevision(ref) + + while True: + if self.verbose: + print "trying: earliest %s latest %s" % (earliestCommit, latestCommit) + next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip() + if len(next) == 0: + if self.verbose: + print "argh" + return "" + log = extractLogMessageFromGitCommit(next) + settings = extractSettingsGitLog(log) + currentChange = int(settings['change']) + if self.verbose: + print "current change %s" % currentChange + + if currentChange == change: + if self.verbose: + print "found %s" % next + return next + + if currentChange < change: + earliestCommit = "^%s" % next + else: + latestCommit = "%s" % next + + return "" + + def importNewBranch(self, branch, maxChange): + # make fast-import flush all changes to disk and update the refs using the checkpoint + # command so that we can try to find the branch parent in the git history + self.gitStream.write("checkpoint\n\n"); + self.gitStream.flush(); + branchPrefix = self.depotPaths[0] + branch + "/" + range = "@1,%s" % maxChange + #print "prefix" + branchPrefix + changes = p4ChangesForPaths([branchPrefix], range) + if len(changes) <= 0: + return False + firstChange = changes[0] + #print "first change in branch: %s" % firstChange + sourceBranch = self.knownBranches[branch] + sourceDepotPath = self.depotPaths[0] + sourceBranch + sourceRef = self.gitRefForBranch(sourceBranch) + #print "source " + sourceBranch + + branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"]) + #print "branch parent: %s" % branchParentChange + gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange) + if len(gitParent) > 0: + self.initialParents[self.gitRefForBranch(branch)] = gitParent + #print "parent git commit: %s" % gitParent + + self.importChanges(changes) + return True + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1159,8 +1220,19 @@ class P4Sync(Command): parent = self.knownBranches[branch] if parent == branch: parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent + else: + fullBranch = self.projectName + branch + if fullBranch not in self.p4BranchesInGit: + if not self.silent: + print("\n Importing new branch %s" % fullBranch); + if self.importNewBranch(branch, change - 1): + parent = "" + self.p4BranchesInGit.append(fullBranch) + if not self.silent: + print("\n Resuming with change %s" % change); + + if self.verbose: + print "parent determined through known branches: %s" % parent branch = self.gitRefForBranch(branch) parent = self.gitRefForBranch(parent) From fec60a261d9375d1f129313bb68036fbd2a5175c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 3 Sep 2007 17:51:43 +0100 Subject: [PATCH 049/602] verify-tag: also grok CR/LFs in the tag signature On some people's favorite platform, gpg outputs signatures with CR/LF line endings. So verify-tag has to play nice with them. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-verify-tag.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c index dfcfcd0455..cc4c55d7ee 100644 --- a/builtin-verify-tag.c +++ b/builtin-verify-tag.c @@ -35,7 +35,7 @@ static int run_gpg_verify(const char *buf, unsigned long size, int verbose) /* find the length without signature */ len = 0; - while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) { + while (len < size && prefixcmp(buf + len, PGP_SIGNATURE)) { eol = memchr(buf + len, '\n', size - len); len += eol ? eol - (buf + len) + 1 : size - len; } From 7b95089c0f59a25bb1c506b6962eb64412c585eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=1B=2Cbi=1B=28B=20Scharfe?= Date: Mon, 3 Sep 2007 20:06:36 +0200 Subject: [PATCH 050/602] Export format_commit_message() Drop the parameter "msg" of format_commit_message() (as it can be inferred from the parameter "commit"), add a parameter "template" in order to avoid accessing the static variable user_format directly and export the result. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- commit.c | 9 +++++---- commit.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/commit.c b/commit.c index dc5a0643f3..99f65cee0e 100644 --- a/commit.c +++ b/commit.c @@ -787,8 +787,8 @@ static void fill_person(struct interp *table, const char *msg, int len) interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -static long format_commit_message(const struct commit *commit, - const char *msg, char **buf_p, unsigned long *space_p) +long format_commit_message(const struct commit *commit, const void *format, + char **buf_p, unsigned long *space_p) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -843,6 +843,7 @@ static long format_commit_message(const struct commit *commit, char parents[1024]; int i; enum { HEADER, SUBJECT, BODY } state; + const char *msg = commit->buffer; if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) die("invalid interp table!"); @@ -924,7 +925,7 @@ static long format_commit_message(const struct commit *commit, char *buf = *buf_p; unsigned long space = *space_p; - space = interpolate(buf, space, user_format, + space = interpolate(buf, space, format, table, ARRAY_SIZE(table)); if (!space) break; @@ -1165,7 +1166,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char *buf; if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, msg, buf_p, space_p); + return format_commit_message(commit, user_format, buf_p, space_p); encoding = (git_log_output_encoding ? git_log_output_encoding diff --git a/commit.h b/commit.h index 467872eeca..a8d76616d2 100644 --- a/commit.h +++ b/commit.h @@ -61,6 +61,7 @@ enum cmit_fmt { }; extern enum cmit_fmt get_commit_format(const char *arg); +extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p); extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode); /** Removes the first commit from a list sorted by date, and adds all From 8460b2fcd45668d91567c36a22ea4f1b14ba133d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 3 Sep 2007 20:07:01 +0200 Subject: [PATCH 051/602] archive: specfile support (--pretty=format: in archive files) Add support for a new attribute, specfile. Files marked as being specfiles are expanded by git-archive when they are written to an archive. It has no effect on worktree files. The same placeholders as those for the option --pretty=format: of git-log et al. can be used. The attribute is useful for creating auto-updating specfiles. It is limited by the underlying function format_commit_message(), though. E.g. currently there is no placeholder for git-describe like output, and expanded specfiles can't contain NUL bytes. That can be fixed in format_commit_message() later and will then benefit users of git-log, too. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 14 +++++++++ archive-tar.c | 5 ++- archive-zip.c | 5 ++- archive.h | 3 ++ builtin-archive.c | 55 ++++++++++++++++++++++++++++++++- t/t5000-tar-tree.sh | 19 ++++++++++++ 6 files changed, 98 insertions(+), 3 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 46f9d591aa..47a621b733 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -421,6 +421,20 @@ frotz unspecified ---------------------------------------------------------------- +Creating an archive +~~~~~~~~~~~~~~~~~~~ + +`specfile` +^^^^^^^^^^ + +If the attribute `specfile` is set for a file then git will expand +several placeholders when adding this file to an archive. The +expansion depends on the availability of a commit ID, i.e. if +gitlink:git-archive[1] has been given a tree instead of a commit or a +tag then no replacement will be done. The placeholders are the same +as those for the option `--pretty=format:` of gitlink:git-log[1]. + + GIT --- Part of the gitlink:git[7] suite diff --git a/archive-tar.c b/archive-tar.c index 66fe3e375b..c0d95dab0d 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -17,6 +17,7 @@ static unsigned long offset; static time_t archive_time; static int tar_umask = 002; static int verbose; +static const struct commit *commit; /* writes out the whole block, but only if it is full */ static void write_if_needed(void) @@ -285,7 +286,8 @@ static int write_tar_entry(const unsigned char *sha1, buffer = NULL; size = 0; } else { - buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size); + buffer = sha1_file_to_archive(path.buf, sha1, mode, &type, + &size, commit); if (!buffer) die("cannot read %s", sha1_to_hex(sha1)); } @@ -304,6 +306,7 @@ int write_tar_archive(struct archiver_args *args) archive_time = args->time; verbose = args->verbose; + commit = args->commit; if (args->commit_sha1) write_global_extended_header(args->commit_sha1); diff --git a/archive-zip.c b/archive-zip.c index 444e1623db..f63dff3834 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -12,6 +12,7 @@ static int verbose; static int zip_date; static int zip_time; +static const struct commit *commit; static unsigned char *zip_dir; static unsigned int zip_dir_size; @@ -195,7 +196,8 @@ static int write_zip_entry(const unsigned char *sha1, if (S_ISREG(mode) && zlib_compression_level != 0) method = 8; result = 0; - buffer = convert_sha1_file(path, sha1, mode, &type, &size); + buffer = sha1_file_to_archive(path, sha1, mode, &type, &size, + commit); if (!buffer) die("cannot read %s", sha1_to_hex(sha1)); crc = crc32(crc, buffer, size); @@ -316,6 +318,7 @@ int write_zip_archive(struct archiver_args *args) zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; verbose = args->verbose; + commit = args->commit; if (args->base && plen > 0 && args->base[plen - 1] == '/') { char *base = xstrdup(args->base); diff --git a/archive.h b/archive.h index 6838dc788f..5791e657e9 100644 --- a/archive.h +++ b/archive.h @@ -8,6 +8,7 @@ struct archiver_args { const char *base; struct tree *tree; const unsigned char *commit_sha1; + const struct commit *commit; time_t time; const char **pathspec; unsigned int verbose : 1; @@ -42,4 +43,6 @@ extern int write_tar_archive(struct archiver_args *); extern int write_zip_archive(struct archiver_args *); extern void *parse_extra_zip_args(int argc, const char **argv); +extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit); + #endif /* ARCHIVE_H */ diff --git a/builtin-archive.c b/builtin-archive.c index 187491bc17..faccce302a 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -10,6 +10,7 @@ #include "exec_cmd.h" #include "pkt-line.h" #include "sideband.h" +#include "attr.h" static const char archive_usage[] = \ "git-archive --format= [--prefix=/] [--verbose] [] [path...]"; @@ -80,6 +81,57 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } +static void *convert_to_archive(const char *path, + const void *src, unsigned long *sizep, + const struct commit *commit) +{ + static struct git_attr *attr_specfile; + struct git_attr_check check[1]; + char *interpolated = NULL; + unsigned long allocated = 0; + + if (!commit) + return NULL; + + if (!attr_specfile) + attr_specfile = git_attr("specfile", 8); + + check[0].attr = attr_specfile; + if (git_checkattr(path, ARRAY_SIZE(check), check)) + return NULL; + if (!ATTR_TRUE(check[0].value)) + return NULL; + + *sizep = format_commit_message(commit, src, &interpolated, &allocated); + + return interpolated; +} + +void *sha1_file_to_archive(const char *path, const unsigned char *sha1, + unsigned int mode, enum object_type *type, + unsigned long *size, + const struct commit *commit) +{ + void *buffer, *converted; + + buffer = read_sha1_file(sha1, type, size); + if (buffer && S_ISREG(mode)) { + converted = convert_to_working_tree(path, buffer, size); + if (converted) { + free(buffer); + buffer = converted; + } + + converted = convert_to_archive(path, buffer, size, commit); + if (converted) { + free(buffer); + buffer = converted; + } + } + + return buffer; +} + static int init_archiver(const char *name, struct archiver *ar) { int rv = -1, i; @@ -109,7 +161,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, const unsigned char *commit_sha1; time_t archive_time; struct tree *tree; - struct commit *commit; + const struct commit *commit; unsigned char sha1[20]; if (get_sha1(name, sha1)) @@ -142,6 +194,7 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args, } ar_args->tree = tree; ar_args->commit_sha1 = commit_sha1; + ar_args->commit = commit; ar_args->time = archive_time; } diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 1a4c53a031..3d5d01be78 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -28,12 +28,15 @@ commit id embedding: TAR=${TAR:-tar} UNZIP=${UNZIP:-unzip} +SPECFILEFORMAT=%H%n + test_expect_success \ 'populate workdir' \ 'mkdir a b c && echo simple textfile >a/a && mkdir a/bin && cp /bin/sh a/bin && + printf "%s" "$SPECFILEFORMAT" >a/specfile && ln -s a a/l1 && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && @@ -104,6 +107,22 @@ test_expect_success \ 'validate file contents with prefix' \ 'diff -r a c/prefix/a' +test_expect_success \ + 'create an archive with a specfile' \ + 'echo specfile specfile >a/.gitattributes && + git archive HEAD >f.tar && + rm a/.gitattributes' + +test_expect_success \ + 'extract specfile' \ + '(mkdir f && cd f && $TAR xf -) f/a/specfile.expected && + diff f/a/specfile.expected f/a/specfile' + test_expect_success \ 'git archive --format=zip' \ 'git archive --format=zip HEAD >d.zip' From 89b4256cfbb8d878cc4cd1104ac4865ba1f2a58e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Mon, 3 Sep 2007 20:08:01 +0200 Subject: [PATCH 052/602] Remove unused function convert_sha1_file() convert_sha1_file() became unused by the previous patch -- remove it. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- cache.h | 1 - convert.c | 15 --------------- 2 files changed, 16 deletions(-) diff --git a/cache.h b/cache.h index 70abbd59bf..493983cbae 100644 --- a/cache.h +++ b/cache.h @@ -592,7 +592,6 @@ extern void trace_argv_printf(const char **argv, int count, const char *format, /* convert.c */ extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep); extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep); -extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size); /* diff.c */ extern int diff_auto_refresh_index; diff --git a/convert.c b/convert.c index 21908b1039..d77c8eb8b2 100644 --- a/convert.c +++ b/convert.c @@ -687,18 +687,3 @@ char *convert_to_working_tree(const char *path, const char *src, unsigned long * return buf; } - -void *convert_sha1_file(const char *path, const unsigned char *sha1, - unsigned int mode, enum object_type *type, - unsigned long *size) -{ - void *buffer = read_sha1_file(sha1, type, size); - if (S_ISREG(mode) && buffer) { - void *converted = convert_to_working_tree(path, buffer, size); - if (converted) { - free(buffer); - buffer = converted; - } - } - return buffer; -} From 0cce83445cb03b9ae93c2f7f3427e0ca2063dc73 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Mon, 3 Sep 2007 20:40:26 +0200 Subject: [PATCH 053/602] Add a new lstat and fstat implementation based on Win32 API This gives us a significant speedup when adding, committing and stat'ing files. Also, since Windows doesn't really handle symlinks, we let stat just uses lstat. We also need to replace fstat, since our implementation and the standard stat() functions report slightly different timestamps, possibly due to timezones. We simply report UTC in our implementation, and do our FILETIME to time_t conversion based on the document at http://support.microsoft.com/kb/167296. With Moe's repo structure (100K files in 100 dirs, containing 2-4 bytes) mkdir bummer && cd bummer; for ((i=0;i<100;i++)); do mkdir $i && pushd $i; for ((j=0;j<1000;j++)); do echo "$j" >$j; done; popd; done We get the following performance boost: With normal lstat & stat Custom lstat/fstat ------------------------ ------------------------ Command: git init Command: git init ------------------------ ------------------------ real 0m 0.047s real 0m 0.063s user 0m 0.031s user 0m 0.015s sys 0m 0.000s sys 0m 0.015s ------------------------ ------------------------ Command: git add . Command: git add . ------------------------ ------------------------ real 0m19.390s real 0m12.031s 1.6x user 0m 0.015s user 0m 0.031s sys 0m 0.030s sys 0m 0.000s ------------------------ ------------------------ Command: git commit -a.. Command: git commit -a.. ------------------------ ------------------------ real 0m30.812s real 0m16.875s 1.8x user 0m 0.015s user 0m 0.015s sys 0m 0.000s sys 0m 0.015s ------------------------ ------------------------ 3x Command: git-status 3x Command: git-status ------------------------ ------------------------ real 0m11.860s real 0m 5.266s 2.2x user 0m 0.015s user 0m 0.015s sys 0m 0.015s sys 0m 0.015s real 0m11.703s real 0m 5.234s user 0m 0.015s user 0m 0.015s sys 0m 0.000s sys 0m 0.000s real 0m11.672s real 0m 5.250s user 0m 0.031s user 0m 0.015s sys 0m 0.000s sys 0m 0.000s ------------------------ ------------------------ Command: git commit... Command: git commit... (single file) (single file) ------------------------ ------------------------ real 0m14.234s real 0m 7.735s 1.8x user 0m 0.015s user 0m 0.031s sys 0m 0.000s sys 0m 0.000s Signed-off-by: Marius Storm-Olsen --- compat/mingw.c | 107 ++++++++++++++++++++++++++++++++++++++++++++-- git-compat-util.h | 8 ++++ 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 7711a3fb39..20eadd2334 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -23,12 +23,81 @@ int fchmod(int fildes, mode_t mode) return -1; } -int lstat(const char *file_name, struct stat *buf) +static inline time_t filetime_to_time_t(const FILETIME *ft) +{ + long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; + winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ + winTime /= 10000000; /* Nano to seconds resolution */ + return (time_t)winTime; +} + +extern int _getdrive( void ); +/* We keep the do_lstat code in a separate function to avoid recursion. + * When a path ends with a slash, the stat will fail with ENOENT. In + * this case, we strip the trailing slashes and stat again. + */ +static int do_lstat(const char *file_name, struct stat *buf) +{ + WIN32_FILE_ATTRIBUTE_DATA fdata; + + if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) { + int fMode = S_IREAD; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + fMode |= S_IFDIR; + else + fMode |= S_IFREG; + if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + fMode |= S_IWRITE; + + buf->st_ino = 0; + buf->st_gid = 0; + buf->st_uid = 0; + buf->st_nlink = 1; + buf->st_mode = fMode; + buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_dev = buf->st_rdev = (_getdrive() - 1); + buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); + buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); + buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); + errno = 0; + return 0; + } + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_SHARING_BUFFER_EXCEEDED: + errno = EACCES; + break; + case ERROR_BUFFER_OVERFLOW: + errno = ENAMETOOLONG; + break; + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case ERROR_INVALID_NAME: + errno = EFAULT; + break; + default: + errno = ENOENT; + break; + } + return -1; +} + +/* We provide our own lstat/fstat functions, since the provided + * lstat/fstat functions are so slow. These stat functions are + * tailored for Git's usage (read: fast), and are not meant to be + * complete. Note that Git stat()s are redirected to git_lstat() + * too, since Windows doesn't really handle symlinks that well. + */ +int git_lstat(const char *file_name, struct stat *buf) { int namelen; static char alt_name[PATH_MAX]; - if (!stat(file_name, buf)) + if (!do_lstat(file_name, buf)) return 0; /* if file_name ended in a '/', Windows returned ENOENT; @@ -36,6 +105,7 @@ int lstat(const char *file_name, struct stat *buf) */ if (errno != ENOENT) return -1; + namelen = strlen(file_name); if (namelen && file_name[namelen-1] != '/') return -1; @@ -43,9 +113,40 @@ int lstat(const char *file_name, struct stat *buf) --namelen; if (!namelen || namelen >= PATH_MAX) return -1; + memcpy(alt_name, file_name, namelen); alt_name[namelen] = 0; - return stat(alt_name, buf); + return do_lstat(alt_name, buf); +} + +int git_fstat(int fd, struct stat *buf) +{ + HANDLE fh = (HANDLE)_get_osfhandle(fd); + BY_HANDLE_FILE_INFORMATION fdata; + + if (fh != INVALID_HANDLE_VALUE && GetFileInformationByHandle(fh, &fdata)) { + int fMode = S_IREAD; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + fMode |= S_IFDIR; + else + fMode |= S_IFREG; + if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + fMode |= S_IWRITE; + + buf->st_ino = 0; + buf->st_gid = 0; + buf->st_uid = 0; + buf->st_nlink = 1; + buf->st_mode = fMode; + buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_dev = buf->st_rdev = (_getdrive() - 1); + buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); + buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); + buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); + return 0; + } + errno = EBADF; + return -1; } /* missing: link, mkstemp, fchmod, getuid (?), gettimeofday */ diff --git a/git-compat-util.h b/git-compat-util.h index 1ba499f750..bdc2e3f5fc 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -488,6 +488,14 @@ int mingw_rename(const char*, const char*); extern void quote_argv(const char **dst, const char **src); extern const char *parse_interpreter(const char *cmd); +/* Use git_lstat() instead of lstat()/stat() and + * git_fstat() instead of fstat() on Windows + */ +int git_lstat(const char *file_name, struct stat *buf); +int git_fstat(int fd, struct stat *buf); +#define lstat(x,y) git_lstat(x,y) +#define stat(x,y) git_lstat(x,y) +#define fstat(x,y) git_fstat(x,y) #endif /* __MINGW32__ */ #endif From f5f3c2e29f51a38261daa91073a3f227d4532325 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 5 Sep 2007 02:19:56 +1000 Subject: [PATCH 054/602] 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 3d9f037c60ceae1bd60ee3c861564812a89b05b1 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Wed, 5 Sep 2007 03:38:24 +0200 Subject: [PATCH 055/602] Function for updating refs. A function intended to be called from builtins updating refs by locking them before write, specially those that came from scripts using "git update-ref". [jc: with minor fixups] Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- builtin-fetch--tool.c | 21 ++++++++------------- builtin-update-ref.c | 9 ++------- refs.c | 27 +++++++++++++++++++++++++++ refs.h | 6 ++++++ send-pack.c | 12 +++--------- 5 files changed, 46 insertions(+), 29 deletions(-) diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index e2f8ede9ae..24c7e6f7db 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -31,24 +31,19 @@ static void show_new(enum object_type type, unsigned char *sha1_new) find_unique_abbrev(sha1_new, DEFAULT_ABBREV)); } -static int update_ref(const char *action, +static int update_ref_env(const char *action, const char *refname, unsigned char *sha1, unsigned char *oldval) { char msg[1024]; char *rla = getenv("GIT_REFLOG_ACTION"); - static struct ref_lock *lock; if (!rla) rla = "(reflog update)"; - snprintf(msg, sizeof(msg), "%s: %s", rla, action); - lock = lock_any_ref_for_update(refname, oldval, 0); - if (!lock) - return 1; - if (write_ref_sha1(lock, sha1, msg) < 0) - return 1; - return 0; + if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg)) + warning("reflog message too long: %.*s...", 50, msg); + return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR); } static int update_local_ref(const char *name, @@ -88,7 +83,7 @@ static int update_local_ref(const char *name, fprintf(stderr, "* %s: storing %s\n", name, note); show_new(type, sha1_new); - return update_ref(msg, name, sha1_new, NULL); + return update_ref_env(msg, name, sha1_new, NULL); } if (!hashcmp(sha1_old, sha1_new)) { @@ -102,7 +97,7 @@ static int update_local_ref(const char *name, if (!strncmp(name, "refs/tags/", 10)) { fprintf(stderr, "* %s: updating with %s\n", name, note); show_new(type, sha1_new); - return update_ref("updating tag", name, sha1_new, NULL); + return update_ref_env("updating tag", name, sha1_new, NULL); } current = lookup_commit_reference(sha1_old); @@ -117,7 +112,7 @@ static int update_local_ref(const char *name, fprintf(stderr, "* %s: fast forward to %s\n", name, note); fprintf(stderr, " old..new: %s..%s\n", oldh, newh); - return update_ref("fast forward", name, sha1_new, sha1_old); + return update_ref_env("fast forward", name, sha1_new, sha1_old); } if (!force) { fprintf(stderr, @@ -131,7 +126,7 @@ static int update_local_ref(const char *name, "* %s: forcing update to non-fast forward %s\n", name, note); fprintf(stderr, " old...new: %s...%s\n", oldh, newh); - return update_ref("forced-update", name, sha1_new, sha1_old); + return update_ref_env("forced-update", name, sha1_new, sha1_old); } static int append_fetch_head(FILE *fp, diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 8339cf19e2..fe1f74c9f3 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -8,7 +8,6 @@ static const char git_update_ref_usage[] = int cmd_update_ref(int argc, const char **argv, const char *prefix) { const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; - struct ref_lock *lock; unsigned char sha1[20], oldsha1[20]; int i, delete, ref_flags; @@ -62,10 +61,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (oldval && *oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); - lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags); - if (!lock) - die("%s: cannot lock the ref", refname); - if (write_ref_sha1(lock, sha1, msg) < 0) - die("%s: cannot update the ref", refname); - return 0; + return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, + ref_flags, DIE_ON_ERR); } diff --git a/refs.c b/refs.c index 09a2c87fc2..7fb3350789 100644 --- a/refs.c +++ b/refs.c @@ -1455,3 +1455,30 @@ int for_each_reflog(each_ref_fn fn, void *cb_data) { return do_for_each_reflog("", fn, cb_data); } + +int update_ref(const char *action, const char *refname, + const unsigned char *sha1, const unsigned char *oldval, + int flags, enum action_on_err onerr) +{ + static struct ref_lock *lock; + lock = lock_any_ref_for_update(refname, oldval, flags); + if (!lock) { + const char *str = "Cannot lock the ref '%s'."; + switch (onerr) { + case MSG_ON_ERR: error(str, refname); break; + case DIE_ON_ERR: die(str, refname); break; + case QUIET_ON_ERR: break; + } + return 1; + } + if (write_ref_sha1(lock, sha1, action) < 0) { + const char *str = "Cannot update the ref '%s'."; + switch (onerr) { + case MSG_ON_ERR: error(str, refname); break; + case DIE_ON_ERR: die(str, refname); break; + case QUIET_ON_ERR: break; + } + return 1; + } + return 0; +} diff --git a/refs.h b/refs.h index f234eb76ba..6eb98a4caf 100644 --- a/refs.h +++ b/refs.h @@ -64,4 +64,10 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg /** resolve ref in nested "gitlink" repository */ extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result); +/** lock a ref and then write its file */ +enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR }; +int update_ref(const char *action, const char *refname, + const unsigned char *sha1, const unsigned char *oldval, + int flags, enum action_on_err onerr); + #endif /* REFS_H */ diff --git a/send-pack.c b/send-pack.c index 9fc8a812f4..f74e66a8ba 100644 --- a/send-pack.c +++ b/send-pack.c @@ -307,20 +307,14 @@ static int send_pack(int in, int out, struct remote *remote, int nr_refspec, cha rs.src = ref->name; rs.dst = NULL; if (!remote_find_tracking(remote, &rs)) { - struct ref_lock *lock; fprintf(stderr, " Also local %s\n", rs.dst); if (will_delete_ref) { if (delete_ref(rs.dst, NULL)) { error("Failed to delete"); } - } else { - lock = lock_any_ref_for_update(rs.dst, NULL, 0); - if (!lock) - error("Failed to lock"); - else - write_ref_sha1(lock, ref->new_sha1, - "update by push"); - } + } else + update_ref("update by push", rs.dst, + ref->new_sha1, NULL, 0, 0); free(rs.dst); } } From 05b4df31537a653eaa30d2c6f53e05d7a12d1bc8 Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Wed, 5 Sep 2007 11:35:29 +0200 Subject: [PATCH 056/602] git-svn: add support for --first-parent When git-svn uses git-log to find embedded 'git-svn-id'-lines in commit messages, it can get confused when local history contains merges with other git-svn branches. But if --first-parent is supplied to git-log, working_head_info() will only see 'branch-local' commits and thus the first commit containing a 'git-svn-id' line should refer to the correct subversion branch. Signed-off-by: Lars Hjemli Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 10 ++++++++++ git-svn.perl | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index be2e34eb8f..42d7b82a37 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -317,6 +317,16 @@ This is only used with the 'dcommit' command. Print out the series of git arguments that would show which diffs would be committed to SVN. +--first-parent:: + +This is only used with the 'dcommit', 'rebase', 'log', 'find-rev' and +'show-ignore' commands. + +These commands tries to detect the upstream subversion branch by means of +the embedded 'git-svn-id' line in commit messages. When --first-parent is +specified, git-svn only follows the first parent of each commit, effectively +ignoring commits brought into the current branch through merge-operations. + -- ADVANCED OPTIONS diff --git a/git-svn.perl b/git-svn.perl index d3c8cd0b8e..d21eb7fa9e 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -59,7 +59,7 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_merge, $_strategy, $_dry_run, $_local, - $_prefix, $_no_checkout, $_verbose); + $_prefix, $_no_checkout, $_verbose, $_first_parent); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -119,12 +119,15 @@ my %cmd = ( 'dry-run|n' => \$_dry_run, 'fetch-all|all' => \$_fetch_all, 'no-rebase' => \$_no_rebase, + 'first-parent' => \$_first_parent, %cmt_opts, %fc_opts } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ], 'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings", - { 'revision|r=i' => \$_revision } ], + { 'revision|r=i' => \$_revision, + 'first-parent' => \$_first_parent + } ], 'multi-fetch' => [ \&cmd_multi_fetch, "Deprecated alias for $0 fetch --all", { 'revision|r=s' => \$_revision, %fc_opts } ], @@ -145,15 +148,19 @@ my %cmd = ( 'authors-file|A=s' => \$_authors, 'color' => \$Git::SVN::Log::color, 'pager=s' => \$Git::SVN::Log::pager, + 'first-parent' => \$_first_parent } ], 'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish", - { } ], + { + 'first-parent' => \$_first_parent + } ], 'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory", { 'merge|m|M' => \$_merge, 'verbose|v' => \$_verbose, 'strategy|s=s' => \$_strategy, 'local|l' => \$_local, 'fetch-all|all' => \$_fetch_all, + 'first-parent' => \$_first_parent, %fc_opts } ], 'commit-diff' => [ \&cmd_commit_diff, 'Commit a diff between two trees', @@ -811,7 +818,9 @@ sub cmt_metadata { sub working_head_info { my ($head, $refs) = @_; - my ($fh, $ctx) = command_output_pipe('log', '--no-color', $head); + my @args = ('log', '--no-color'); + push @args, '--first-parent' if $_first_parent; + my ($fh, $ctx) = command_output_pipe(@args, $head); my $hash; my %max; while (<$fh>) { From 6b763c424e4ace1678ade5310f3ca3ffbd11af2c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Sep 2007 21:58:40 -0700 Subject: [PATCH 057/602] git-apply: do not read past the end of buffer When the preimage we are patching is shorter than what the patch text expects, we tried to match the buffer contents at the "original" line with the fragment in full, without checking we have enough data to match in the preimage. This caused the size of a later memmove() to wrap around and attempt to scribble almost the entire address space. Not good. The code that follows the part this patch touches tries to match the fragment with line offsets. Curiously, that code does not have the problem --- it guards against reading past the end of the preimage. Signed-off-by: Junio C Hamano --- builtin-apply.c | 3 ++- t/t4123-apply-shrink.sh | 58 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 t/t4123-apply-shrink.sh diff --git a/builtin-apply.c b/builtin-apply.c index 25b1447901..976ec77041 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -1514,7 +1514,8 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment } /* Exact line number? */ - if (!memcmp(buf + start, fragment, fragsize)) + if ((start + fragsize <= size) && + !memcmp(buf + start, fragment, fragsize)) return start; /* diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh new file mode 100755 index 0000000000..984157f03b --- /dev/null +++ b/t/t4123-apply-shrink.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +test_description='apply a patch that is larger than the preimage' + +. ./test-lib.sh + +cat >F <<\EOF +1 +2 +3 +4 +5 +6 +7 +8 +999999 +A +B +C +D +E +F +G +H +I +J + +EOF + +test_expect_success setup ' + + git add F && + mv F G && + sed -e "s/1/11/" -e "s/999999/9/" -e "s/H/HH/" F && + git diff >patch && + sed -e "/^\$/d" F && + git add F + +' + +test_expect_success 'apply should fail gracefully' ' + + if git apply --index patch + then + echo Oops, should not have succeeded + false + else + status=$? + echo "Status was $status" + if test -f .git/index.lock + then + echo Oops, should not have crashed + false + fi + fi +' + +test_done From ea09ea22d65d118328642e03ad23c8257304499d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 5 Sep 2007 23:33:41 -0400 Subject: [PATCH 058/602] Don't allow contrib/workdir/git-new-workdir to trash existing dirs Recently I found that doing a sequence like the following: git-new-workdir a b ... git-new-workdir a b by accident will cause a (and now also b) to have an infinite cycle in its refs directory. This is caused by git-new-workdir trying to create the "refs" symlink over again, only during the second time it is being created within a's refs directory and is now also pointing back at a's refs. This causes confusion in git as suddenly branches are named things like "refs/refs/refs/refs/refs/refs/refs/heads/foo" instead of the more commonly accepted "refs/heads/foo". Plenty of commands start to see ambiguous ref names and others just take ages to compute. git-clone has the same safety check, so git-new-workdir should behave just like it. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index c6e154a84f..2838546d16 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -48,6 +48,12 @@ then "a complete repository." fi +# don't recreate a workdir over an existing repository +if test -e "$new_workdir" +then + die "destination directory '$new_workdir' already exists." +fi + # make sure the the links use full paths git_dir=$(cd "$git_dir"; pwd) From 6b1b40d9f41cdc1db9ad60f143c08f8260eef815 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Thu, 6 Sep 2007 03:22:51 +0400 Subject: [PATCH 059/602] Makefile: Add cache-tree.h to the headers list The dependency was missing. Signed-off-by: Dmitry V. Levin Signed-off-by: Junio C Hamano --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51af531c9a..dace2112f9 100644 --- a/Makefile +++ b/Makefile @@ -281,7 +281,7 @@ LIB_FILE=libgit.a XDIFF_LIB=xdiff/lib.a LIB_H = \ - archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \ + archive.h blob.h cache.h cache-tree.h commit.h csum-file.h delta.h grep.h \ diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.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 \ From 432e93a16441a1a11aeb8158a634f76c214abb31 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 5 Sep 2007 22:15:21 -0400 Subject: [PATCH 060/602] Cleanup unnecessary file modifications in t1400-update-ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kristian Høgsberg pointed out that the two file modifications we were doing during the 'creating initial files' step are not even used within the test suite. This was actually confusing as we do not even need these changes for the tests to pass. All that really matters here is the specific commit dates are used so that these appear in the branch's reflog, and that the dates are different so that the branch will update when asked and the reflog entry is also updated. There is no need for the file modification. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- t/t1400-update-ref.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index c4c0dfaab1..ce045b2a57 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -198,11 +198,9 @@ test_expect_success \ GIT_AUTHOR_DATE="2005-05-26 23:41" \ GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a && h_OTHER=$(git rev-parse --verify HEAD) && - echo FIXED >F && GIT_AUTHOR_DATE="2005-05-26 23:44" \ GIT_COMMITTER_DATE="2005-05-26 23:44" git-commit --amend && h_FIXED=$(git rev-parse --verify HEAD) && - echo TEST+FIXED >F && echo Merged initial commit and a later commit. >M && echo $h_TEST >.git/MERGE_HEAD && GIT_AUTHOR_DATE="2005-05-26 23:45" \ From 4e560158c6de154fafab9fc3f6028d9edcc53e6b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 6 Sep 2007 00:44:08 -0400 Subject: [PATCH 061/602] Include a git-push example for creating a remote branch Many users get confused when `git push origin master:foo` works when foo already exists on the remote repository but are confused when foo doesn't exist as a branch and this form does not create the branch foo. This new example highlights the trick of including refs/heads/ in front of the desired branch name to create a branch. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- Documentation/git-push.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 0dd9caf867..7b8e075c42 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -117,6 +117,12 @@ git push origin master:satellite/master:: the ref that matches `satellite/master` (most likely, it would be `refs/remotes/satellite/master`) in `origin` repository with it. +git push origin master:refs/heads/experimental:: + Create the branch `experimental` in the `origin` repository + by copying the current `master` branch. This form is usually + needed to create a new branch in the remote repository as + there is no `experimental` branch to match. + Author ------ Written by Junio C Hamano , later rewritten in C From 75d3985319f2eb40008e9fe6454880ecc620a0de Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 6 Sep 2007 02:13:08 -0400 Subject: [PATCH 062/602] straighten the list of objects to deltify Not all objects are subject to deltification, so avoid carrying those along, and provide the real count to progress display. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 77 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index e64e3a03a0..b1c64bec3e 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1313,12 +1313,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, if (trg_entry->type != src_entry->type) return -1; - /* We do not compute delta to *create* objects we are not - * going to pack. - */ - if (trg_entry->preferred_base) - return -1; - /* * We do not bother to try a delta that we discarded * on an earlier try, but only when reusing delta data. @@ -1443,43 +1437,24 @@ static void free_unpacked(struct unpacked *n) n->depth = 0; } -static void find_deltas(struct object_entry **list, int window, int depth) +static void find_deltas(struct object_entry **list, unsigned list_size, + unsigned nr_deltas, int window, int depth) { - uint32_t i = nr_objects, idx = 0, count = 0, processed = 0; + uint32_t i = list_size, idx = 0, count = 0, processed = 0; unsigned int array_size = window * sizeof(struct unpacked); struct unpacked *array; int max_depth; - if (!nr_objects) - return; array = xmalloc(array_size); memset(array, 0, array_size); if (progress) - start_progress(&progress_state, "Deltifying %u objects...", "", nr_result); + start_progress(&progress_state, "Deltifying %u objects...", "", nr_deltas); do { struct object_entry *entry = list[--i]; struct unpacked *n = array + idx; int j, best_base = -1; - if (!entry->preferred_base) - processed++; - - if (progress) - display_progress(&progress_state, processed); - - if (entry->delta) - /* This happens if we decided to reuse existing - * delta from a pack. "!no_reuse_delta &&" is implied. - */ - continue; - - if (entry->size < 50) - continue; - - if (entry->no_try_delta) - continue; - free_unpacked(n); n->entry = entry; @@ -1491,6 +1466,15 @@ static void find_deltas(struct object_entry **list, int window, int depth) count--; } + /* We do not compute delta to *create* objects we are not + * going to pack. + */ + if (entry->preferred_base) + goto next; + + if (progress) + display_progress(&progress_state, ++processed); + /* * If the current object is at pack edge, take the depth the * objects that depend on the current object into account @@ -1565,18 +1549,41 @@ static void find_deltas(struct object_entry **list, int window, int depth) static void prepare_pack(int window, int depth) { struct object_entry **delta_list; - uint32_t i; + uint32_t i, n, nr_deltas; get_object_details(); - if (!window || !depth) + if (!nr_objects || !window || !depth) return; delta_list = xmalloc(nr_objects * sizeof(*delta_list)); - for (i = 0; i < nr_objects; i++) - delta_list[i] = objects + i; - qsort(delta_list, nr_objects, sizeof(*delta_list), type_size_sort); - find_deltas(delta_list, window+1, depth); + nr_deltas = n = 0; + + for (i = 0; i < nr_objects; i++) { + struct object_entry *entry = objects + i; + + if (entry->delta) + /* This happens if we decided to reuse existing + * delta from a pack. "!no_reuse_delta &&" is implied. + */ + continue; + + if (entry->size < 50) + continue; + + if (entry->no_try_delta) + continue; + + if (!entry->preferred_base) + nr_deltas++; + + delta_list[n++] = entry; + } + + if (nr_deltas) { + qsort(delta_list, n, sizeof(*delta_list), type_size_sort); + find_deltas(delta_list, n, nr_deltas, window+1, depth); + } free(delta_list); } From ef0316fcd996c1679fef37ae2a53bef403c77356 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 6 Sep 2007 02:13:09 -0400 Subject: [PATCH 063/602] localize window memory usage accounting This is to help threadification of delta searching. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index b1c64bec3e..b8495bf924 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -78,7 +78,6 @@ static unsigned long delta_cache_size = 0; static unsigned long max_delta_cache_size = 0; static unsigned long cache_max_small_delta_size = 1000; -static unsigned long window_memory_usage = 0; static unsigned long window_memory_limit = 0; /* @@ -1300,7 +1299,7 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size, * one. */ static int try_delta(struct unpacked *trg, struct unpacked *src, - unsigned max_depth) + unsigned max_depth, unsigned long *mem_usage) { struct object_entry *trg_entry = trg->entry; struct object_entry *src_entry = src->entry; @@ -1356,7 +1355,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, if (sz != trg_size) die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(trg_entry->idx.sha1), sz, trg_size); - window_memory_usage += sz; + *mem_usage += sz; } if (!src->data) { src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz); @@ -1366,7 +1365,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, if (sz != src_size) die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(src_entry->idx.sha1), sz, src_size); - window_memory_usage += sz; + *mem_usage += sz; } if (!src->index) { src->index = create_delta_index(src->data, src_size); @@ -1376,7 +1375,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, warning("suboptimal pack - out of memory"); return 0; } - window_memory_usage += sizeof_delta_index(src->index); + *mem_usage += sizeof_delta_index(src->index); } delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size); @@ -1423,18 +1422,19 @@ static unsigned int check_delta_limit(struct object_entry *me, unsigned int n) return m; } -static void free_unpacked(struct unpacked *n) +static unsigned long free_unpacked(struct unpacked *n) { - window_memory_usage -= sizeof_delta_index(n->index); + unsigned long freed_mem = sizeof_delta_index(n->index); free_delta_index(n->index); n->index = NULL; if (n->data) { + freed_mem += n->entry->size; free(n->data); n->data = NULL; - window_memory_usage -= n->entry->size; } n->entry = NULL; n->depth = 0; + return freed_mem; } static void find_deltas(struct object_entry **list, unsigned list_size, @@ -1443,7 +1443,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, uint32_t i = list_size, idx = 0, count = 0, processed = 0; unsigned int array_size = window * sizeof(struct unpacked); struct unpacked *array; - int max_depth; + unsigned long mem_usage = 0; array = xmalloc(array_size); memset(array, 0, array_size); @@ -1453,16 +1453,16 @@ static void find_deltas(struct object_entry **list, unsigned list_size, do { struct object_entry *entry = list[--i]; struct unpacked *n = array + idx; - int j, best_base = -1; + int j, max_depth, best_base = -1; - free_unpacked(n); + mem_usage -= free_unpacked(n); n->entry = entry; while (window_memory_limit && - window_memory_usage > window_memory_limit && + mem_usage > window_memory_limit && count > 1) { uint32_t tail = (idx + window - count) % window; - free_unpacked(array + tail); + mem_usage -= free_unpacked(array + tail); count--; } @@ -1497,7 +1497,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size, m = array + other_idx; if (!m->entry) break; - ret = try_delta(n, m, max_depth); + ret = try_delta(n, m, max_depth, &mem_usage); if (ret < 0) break; else if (ret > 0) From e334977dfad575cd8ac1a9e5f8e73fe4d018cec0 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 6 Sep 2007 02:13:10 -0400 Subject: [PATCH 064/602] rearrange delta search progress reporting This is to help threadification of the delta search code, with a bonus consistency check. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- builtin-pack-objects.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index b8495bf924..9d565925e7 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1438,17 +1438,15 @@ static unsigned long free_unpacked(struct unpacked *n) } static void find_deltas(struct object_entry **list, unsigned list_size, - unsigned nr_deltas, int window, int depth) + int window, int depth, unsigned *processed) { - uint32_t i = list_size, idx = 0, count = 0, processed = 0; + uint32_t i = list_size, idx = 0, count = 0; unsigned int array_size = window * sizeof(struct unpacked); struct unpacked *array; unsigned long mem_usage = 0; array = xmalloc(array_size); memset(array, 0, array_size); - if (progress) - start_progress(&progress_state, "Deltifying %u objects...", "", nr_deltas); do { struct object_entry *entry = list[--i]; @@ -1472,8 +1470,9 @@ static void find_deltas(struct object_entry **list, unsigned list_size, if (entry->preferred_base) goto next; + (*processed)++; if (progress) - display_progress(&progress_state, ++processed); + display_progress(&progress_state, *processed); /* * If the current object is at pack edge, take the depth the @@ -1536,9 +1535,6 @@ static void find_deltas(struct object_entry **list, unsigned list_size, idx = 0; } while (i > 0); - if (progress) - stop_progress(&progress_state); - for (i = 0; i < window; ++i) { free_delta_index(array[i].index); free(array[i].data); @@ -1581,8 +1577,17 @@ static void prepare_pack(int window, int depth) } if (nr_deltas) { + unsigned nr_done = 0; + if (progress) + start_progress(&progress_state, + "Deltifying %u objects...", "", + nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); - find_deltas(delta_list, n, nr_deltas, window+1, depth); + find_deltas(delta_list, n, window+1, depth, &nr_done); + if (progress) + stop_progress(&progress_state); + if (nr_done != nr_deltas) + die("inconsistency with delta count"); } free(delta_list); } From 8ecce684a38f7cea084abe9eef80bda04d7c77be Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 6 Sep 2007 02:13:11 -0400 Subject: [PATCH 065/602] basic threaded delta search this is still rough, hence it is disabled by default. You need to compile with "make THREADED_DELTA_SEARCH=1 ..." at the moment. Threading is done on different portions of the object list to be deltified. This is currently done by spliting the list into n parts and then a thread is spawned for each of them. A better method would consist of spliting the list into more smaller parts and have the n threads pick the next part available. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Makefile | 8 ++++ builtin-pack-objects.c | 83 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 51af531c9a..a92fb31695 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,9 @@ all:: # If not set it defaults to the bare 'wish'. If it is set to the empty # string then NO_TCLTK will be forced (this is used by configure script). # +# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit +# parallel delta searching when packing objects. +# GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -662,6 +665,11 @@ ifdef NO_HSTRERROR COMPAT_OBJS += compat/hstrerror.o endif +ifdef THREADED_DELTA_SEARCH + BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH + EXTLIBS += -lpthread +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK=NoThanks endif diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 9d565925e7..1bcee23ca1 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -15,6 +15,10 @@ #include "list-objects.h" #include "progress.h" +#ifdef THREADED_DELTA_SEARCH +#include +#endif + static const char pack_usage[] = "\ git-pack-objects [{ -q | --progress | --all-progress }] \n\ [--max-pack-size=N] [--local] [--incremental] \n\ @@ -1290,6 +1294,25 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size, return 0; } +#ifdef THREADED_DELTA_SEARCH + +static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER; +#define read_lock() pthread_mutex_lock(&read_mutex) +#define read_unlock() pthread_mutex_unlock(&read_mutex) + +static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; +#define progress_lock() pthread_mutex_lock(&progress_mutex) +#define progress_unlock() pthread_mutex_unlock(&progress_mutex) + +#else + +#define read_lock() 0 +#define read_unlock() 0 +#define progress_lock() 0 +#define progress_unlock() 0 + +#endif + /* * We search for deltas _backwards_ in a list sorted by type and * by size, so that we see progressively smaller and smaller files. @@ -1348,7 +1371,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { + read_lock(); trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz); + read_unlock(); if (!trg->data) die("object %s cannot be read", sha1_to_hex(trg_entry->idx.sha1)); @@ -1358,7 +1383,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, *mem_usage += sz; } if (!src->data) { + read_lock(); src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz); + read_unlock(); if (!src->data) die("object %s cannot be read", sha1_to_hex(src_entry->idx.sha1)); @@ -1470,9 +1497,11 @@ static void find_deltas(struct object_entry **list, unsigned list_size, if (entry->preferred_base) goto next; + progress_lock(); (*processed)++; if (progress) display_progress(&progress_state, *processed); + progress_unlock(); /* * If the current object is at pack edge, take the depth the @@ -1542,6 +1571,58 @@ static void find_deltas(struct object_entry **list, unsigned list_size, free(array); } +#ifdef THREADED_DELTA_SEARCH + +struct thread_params { + pthread_t thread; + struct object_entry **list; + unsigned list_size; + int window; + int depth; + unsigned *processed; +}; + +static void *threaded_find_deltas(void *arg) +{ + struct thread_params *p = arg; + if (p->list_size) + find_deltas(p->list, p->list_size, + p->window, p->depth, p->processed); + return NULL; +} + +#define NR_THREADS 8 + +static void ll_find_deltas(struct object_entry **list, unsigned list_size, + int window, int depth, unsigned *processed) +{ + struct thread_params p[NR_THREADS]; + int i, ret; + + for (i = 0; i < NR_THREADS; i++) { + unsigned sublist_size = list_size / (NR_THREADS - i); + p[i].list = list; + p[i].list_size = sublist_size; + p[i].window = window; + p[i].depth = depth; + p[i].processed = processed; + ret = pthread_create(&p[i].thread, NULL, + threaded_find_deltas, &p[i]); + if (ret) + die("unable to create thread: %s", strerror(ret)); + list += sublist_size; + list_size -= sublist_size; + } + + for (i = 0; i < NR_THREADS; i++) { + pthread_join(p[i].thread, NULL); + } +} + +#else +#define ll_find_deltas find_deltas +#endif + static void prepare_pack(int window, int depth) { struct object_entry **delta_list; @@ -1583,7 +1664,7 @@ static void prepare_pack(int window, int depth) "Deltifying %u objects...", "", nr_deltas); qsort(delta_list, n, sizeof(*delta_list), type_size_sort); - find_deltas(delta_list, n, window+1, depth, &nr_done); + ll_find_deltas(delta_list, n, window+1, depth, &nr_done); if (progress) stop_progress(&progress_state); if (nr_done != nr_deltas) From 2c3c4399477533329579ca6b84824ef0b125914f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Sep 2007 13:01:37 -0700 Subject: [PATCH 066/602] Implement git gc --auto This implements a new option "git gc --auto". When gc.auto is set to a positive value, and the object database has accumulated roughly that many number of loose objects, this runs a lightweight version of "git gc". The primary difference from the full "git gc" is that it does not pass "-a" option to "git repack", which means we do not try to repack _everything_, but only repack incrementally. We still do "git prune-packed". The default threshold is arbitrarily set by yours truly to: - not trigger it for fully unpacked git v0.99 history; - do trigger it for fully unpacked git v1.0.0 history; - not trigger it for incremental update to git v1.0.0 starting from fully packed git v0.99 history. This patch does not add invocation of the "auto repacking". It is left to key Porcelain commands that could produce tons of loose objects to add a call to "git gc --auto" after they are done their work. Signed-off-by: Junio C Hamano --- builtin-gc.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/builtin-gc.c b/builtin-gc.c index 9397482610..093b3dda9f 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -20,6 +20,7 @@ static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; static int pack_refs = 1; static int aggressive_window = -1; +static int gc_auto_threshold = 6700; #define MAX_ADD 10 static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL}; @@ -28,6 +29,8 @@ static const char *argv_repack[MAX_ADD] = {"repack", "-a", "-d", "-l", NULL}; static const char *argv_prune[] = {"prune", NULL}; static const char *argv_rerere[] = {"rerere", "gc", NULL}; +static const char *argv_repack_auto[] = {"repack", "-d", "-l", NULL}; + static int gc_config(const char *var, const char *value) { if (!strcmp(var, "gc.packrefs")) { @@ -41,6 +44,10 @@ static int gc_config(const char *var, const char *value) aggressive_window = git_config_int(var, value); return 0; } + if (!strcmp(var, "gc.auto")) { + gc_auto_threshold = git_config_int(var, value); + return 0; + } return git_default_config(var, value); } @@ -57,10 +64,49 @@ static void append_option(const char **cmd, const char *opt, int max_length) cmd[i] = NULL; } +static int need_to_gc(void) +{ + /* + * Quickly check if a "gc" is needed, by estimating how + * many loose objects there are. Because SHA-1 is evenly + * distributed, we can check only one and get a reasonable + * estimate. + */ + char path[PATH_MAX]; + const char *objdir = get_object_directory(); + DIR *dir; + struct dirent *ent; + int auto_threshold; + int num_loose = 0; + int needed = 0; + + if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) { + warning("insanely long object directory %.*s", 50, objdir); + return 0; + } + dir = opendir(path); + if (!dir) + return 0; + + auto_threshold = (gc_auto_threshold + 255) / 256; + while ((ent = readdir(dir)) != NULL) { + if (strspn(ent->d_name, "0123456789abcdef") != 38 || + ent->d_name[38] != '\0') + continue; + if (++num_loose > auto_threshold) { + needed = 1; + break; + } + } + closedir(dir); + return needed; +} + int cmd_gc(int argc, const char **argv, const char *prefix) { int i; int prune = 0; + int auto_gc = 0; char buf[80]; git_config(gc_config); @@ -82,12 +128,28 @@ int cmd_gc(int argc, const char **argv, const char *prefix) } continue; } - /* perhaps other parameters later... */ + if (!strcmp(arg, "--auto")) { + if (gc_auto_threshold <= 0) + return 0; + auto_gc = 1; + continue; + } break; } if (i != argc) usage(builtin_gc_usage); + if (auto_gc) { + /* + * Auto-gc should be least intrusive as possible. + */ + prune = 0; + for (i = 0; i < ARRAY_SIZE(argv_repack_auto); i++) + argv_repack[i] = argv_repack_auto[i]; + if (!need_to_gc()) + return 0; + } + if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD)) return error(FAILED_RUN, argv_pack_refs[0]); From d4bb43ee273528064192848165f93f8fc3512be1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 5 Sep 2007 14:59:59 -0700 Subject: [PATCH 067/602] Invoke "git gc --auto" from commit, merge, am and rebase. The point of auto gc is to pack new objects created in loose format, so a good rule of thumb is where we do update-ref after creating a new commit. Signed-off-by: Junio C Hamano --- git-am.sh | 2 ++ git-commit.sh | 1 + git-merge.sh | 1 + git-rebase--interactive.sh | 2 ++ 4 files changed, 6 insertions(+) diff --git a/git-am.sh b/git-am.sh index 6809aa07f6..4db4701c9e 100755 --- a/git-am.sh +++ b/git-am.sh @@ -466,6 +466,8 @@ do "$GIT_DIR"/hooks/post-applypatch fi + git gc --auto + go_next done diff --git a/git-commit.sh b/git-commit.sh index 1d04f1ff31..d22d35eadc 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -652,6 +652,7 @@ git rerere if test "$ret" = 0 then + git gc --auto if test -x "$GIT_DIR"/hooks/post-commit then "$GIT_DIR"/hooks/post-commit diff --git a/git-merge.sh b/git-merge.sh index 3a01db0d75..697bec24fa 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -82,6 +82,7 @@ finish () { ;; *) git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1 + git gc --auto ;; esac ;; diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index abc2b1c3e0..8258b7adf9 100755 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -307,6 +307,8 @@ do_next () { rm -rf "$DOTEST" && warn "Successfully rebased and updated $HEADNAME." + git gc --auto + exit } From b21b9f1de313acb5550c070911ae58c735cdb451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 7 Sep 2007 00:32:54 +0200 Subject: [PATCH 068/602] add memmem() memmem() is a nice GNU extension for searching a length limited string in another one. This compat version is based on the version found in glibc 2.2 (GPL 2); I only removed the optimization of checking the first char by hand, and generally tried to keep the code simple. We can add it back if memcmp shows up high in a profile, but for now I prefer to keep it (almost trivially) simple. Since I don't really know which platforms beside those with a glibc have their own memmem(), I used a heuristic: if NO_STRCASESTR is set, then NO_MEMMEM is set, too. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Makefile | 11 +++++++++++ compat/memmem.c | 29 +++++++++++++++++++++++++++++ git-compat-util.h | 6 ++++++ 3 files changed, 46 insertions(+) create mode 100644 compat/memmem.c diff --git a/Makefile b/Makefile index 51af531c9a..bae073fe37 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,8 @@ all:: # # Define NO_STRCASESTR if you don't have strcasestr. # +# Define NO_MEMMEM if you don't have memmem. +# # Define NO_STRLCPY if you don't have strlcpy. # # Define NO_STRTOUMAX if you don't have strtoumax in the C library. @@ -402,6 +404,7 @@ ifeq ($(uname_S),SunOS) NEEDS_NSL = YesPlease SHELL_PATH = /bin/bash NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease NO_HSTRERROR = YesPlease ifeq ($(uname_R),5.8) NEEDS_LIBICONV = YesPlease @@ -424,6 +427,7 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes @@ -442,6 +446,7 @@ ifeq ($(uname_S),FreeBSD) endif ifeq ($(uname_S),OpenBSD) NO_STRCASESTR = YesPlease + NO_MEMMEM = YesPlease NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib @@ -456,6 +461,7 @@ ifeq ($(uname_S),NetBSD) endif ifeq ($(uname_S),AIX) NO_STRCASESTR=YesPlease + NO_MEMMEM = YesPlease NO_STRLCPY = YesPlease NEEDS_LIBICONV=YesPlease endif @@ -467,6 +473,7 @@ ifeq ($(uname_S),IRIX64) NO_IPV6=YesPlease NO_SETENV=YesPlease NO_STRCASESTR=YesPlease + NO_MEMMEM = YesPlease NO_STRLCPY = YesPlease NO_SOCKADDR_STORAGE=YesPlease SHELL_PATH=/usr/gnu/bin/bash @@ -661,6 +668,10 @@ ifdef NO_HSTRERROR COMPAT_CFLAGS += -DNO_HSTRERROR COMPAT_OBJS += compat/hstrerror.o endif +ifdef NO_MEMMEM + COMPAT_CFLAGS += -DNO_MEMMEM + COMPAT_OBJS += compat/memmem.o +endif ifeq ($(TCLTK_PATH),) NO_TCLTK=NoThanks diff --git a/compat/memmem.c b/compat/memmem.c new file mode 100644 index 0000000000..cd0d877364 --- /dev/null +++ b/compat/memmem.c @@ -0,0 +1,29 @@ +#include "../git-compat-util.h" + +void *gitmemmem(const void *haystack, size_t haystack_len, + const void *needle, size_t needle_len) +{ + const char *begin = haystack; + const char *last_possible = begin + haystack_len - needle_len; + + /* + * The first occurrence of the empty string is deemed to occur at + * the beginning of the string. + */ + if (needle_len == 0) + return (void *)begin; + + /* + * Sanity check, otherwise the loop might search through the whole + * memory. + */ + if (haystack_len < needle_len) + return NULL; + + for (; begin <= last_possible; begin++) { + if (!memcmp(begin, needle, needle_len)) + return (void *)begin; + } + + return NULL; +} diff --git a/git-compat-util.h b/git-compat-util.h index ca0a597a28..1bfbdeb94f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -172,6 +172,12 @@ extern uintmax_t gitstrtoumax(const char *, char **, int); extern const char *githstrerror(int herror); #endif +#ifdef NO_MEMMEM +#define memmem gitmemmem +void *gitmemmem(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); +#endif + extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) From df4a394f91d7d107c2a57e6c1df3638517cab54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Fri, 7 Sep 2007 00:34:06 +0200 Subject: [PATCH 069/602] archive: specfile syntax change: "$Format:%PLCHLDR$" instead of just "%PLCHLDR" (take 2) As suggested by Johannes, --pretty=format: placeholders in specfiles need to be wrapped in $Format:...$ now. This syntax change restricts the expansion of placeholders and makes it easier to use with files that contain non-placeholder percent signs. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 5 +++- builtin-archive.c | 52 +++++++++++++++++++++++++++++---- t/t5000-tar-tree.sh | 4 +-- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 47a621b733..37b3be8b72 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -432,7 +432,10 @@ several placeholders when adding this file to an archive. The expansion depends on the availability of a commit ID, i.e. if gitlink:git-archive[1] has been given a tree instead of a commit or a tag then no replacement will be done. The placeholders are the same -as those for the option `--pretty=format:` of gitlink:git-log[1]. +as those for the option `--pretty=format:` of gitlink:git-log[1], +except that they need to be wrapped like this: `$Format:PLACEHOLDERS$` +in the file. E.g. the string `$Format:%H$` will be replaced by the +commit hash. GIT diff --git a/builtin-archive.c b/builtin-archive.c index faccce302a..65bf9cbec1 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -81,14 +81,58 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } +static void *format_specfile(const struct commit *commit, const char *format, + unsigned long *sizep) +{ + unsigned long len = *sizep, result_len = 0; + const char *a = format; + char *result = NULL; + + for (;;) { + const char *b, *c; + char *fmt, *formatted = NULL; + unsigned long a_len, fmt_len, formatted_len, allocated = 0; + + b = memmem(a, len, "$Format:", 8); + if (!b || a + len < b + 9) + break; + c = memchr(b + 8, '$', len - 8); + if (!c) + break; + + a_len = b - a; + fmt_len = c - b - 8; + fmt = xmalloc(fmt_len + 1); + memcpy(fmt, b + 8, fmt_len); + fmt[fmt_len] = '\0'; + + formatted_len = format_commit_message(commit, fmt, &formatted, + &allocated); + result = xrealloc(result, result_len + a_len + formatted_len); + memcpy(result + result_len, a, a_len); + memcpy(result + result_len + a_len, formatted, formatted_len); + result_len += a_len + formatted_len; + len -= c + 1 - a; + a = c + 1; + } + + if (result && len) { + result = xrealloc(result, result_len + len); + memcpy(result + result_len, a, len); + result_len += len; + } + + *sizep = result_len; + + return result; +} + static void *convert_to_archive(const char *path, const void *src, unsigned long *sizep, const struct commit *commit) { static struct git_attr *attr_specfile; struct git_attr_check check[1]; - char *interpolated = NULL; - unsigned long allocated = 0; if (!commit) return NULL; @@ -102,9 +146,7 @@ static void *convert_to_archive(const char *path, if (!ATTR_TRUE(check[0].value)) return NULL; - *sizep = format_commit_message(commit, src, &interpolated, &allocated); - - return interpolated; + return format_specfile(commit, src, sizep); } void *sha1_file_to_archive(const char *path, const unsigned char *sha1, diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 3d5d01be78..6e89e07272 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -36,7 +36,7 @@ test_expect_success \ echo simple textfile >a/a && mkdir a/bin && cp /bin/sh a/bin && - printf "%s" "$SPECFILEFORMAT" >a/specfile && + printf "A\$Format:%s\$O" "$SPECFILEFORMAT" >a/specfile && ln -s a a/l1 && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && @@ -119,7 +119,7 @@ test_expect_success \ test_expect_success \ 'validate specfile contents' \ - 'git log --max-count=1 "--pretty=format:$SPECFILEFORMAT" HEAD \ + 'git log --max-count=1 "--pretty=format:A${SPECFILEFORMAT}O" HEAD \ >f/a/specfile.expected && diff f/a/specfile.expected f/a/specfile' From 38c9c9b798a0df875968ae49d699298131dfa24d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Thu, 6 Sep 2007 18:51:11 +0200 Subject: [PATCH 070/602] archive: rename attribute specfile to export-subst As suggested by Junio and Johannes, change the name of the former attribute specfile to export-subst to indicate its function rather than purpose and to make clear that it is not applied to working tree files. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- Documentation/gitattributes.txt | 6 +++--- builtin-archive.c | 14 +++++++------- t/t5000-tar-tree.sh | 18 +++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 37b3be8b72..d0e951ee6f 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -424,10 +424,10 @@ frotz unspecified Creating an archive ~~~~~~~~~~~~~~~~~~~ -`specfile` -^^^^^^^^^^ +`export-subst` +^^^^^^^^^^^^^^ -If the attribute `specfile` is set for a file then git will expand +If the attribute `export-subst` is set for a file then git will expand several placeholders when adding this file to an archive. The expansion depends on the availability of a commit ID, i.e. if gitlink:git-archive[1] has been given a tree instead of a commit or a diff --git a/builtin-archive.c b/builtin-archive.c index 65bf9cbec1..e221f115f9 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -81,8 +81,8 @@ static int run_remote_archiver(const char *remote, int argc, return !!rv; } -static void *format_specfile(const struct commit *commit, const char *format, - unsigned long *sizep) +static void *format_subst(const struct commit *commit, const char *format, + unsigned long *sizep) { unsigned long len = *sizep, result_len = 0; const char *a = format; @@ -131,22 +131,22 @@ static void *convert_to_archive(const char *path, const void *src, unsigned long *sizep, const struct commit *commit) { - static struct git_attr *attr_specfile; + static struct git_attr *attr_export_subst; struct git_attr_check check[1]; if (!commit) return NULL; - if (!attr_specfile) - attr_specfile = git_attr("specfile", 8); + if (!attr_export_subst) + attr_export_subst = git_attr("export-subst", 12); - check[0].attr = attr_specfile; + check[0].attr = attr_export_subst; if (git_checkattr(path, ARRAY_SIZE(check), check)) return NULL; if (!ATTR_TRUE(check[0].value)) return NULL; - return format_specfile(commit, src, sizep); + return format_subst(commit, src, sizep); } void *sha1_file_to_archive(const char *path, const unsigned char *sha1, diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 6e89e07272..42e28ab758 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -28,7 +28,7 @@ commit id embedding: TAR=${TAR:-tar} UNZIP=${UNZIP:-unzip} -SPECFILEFORMAT=%H%n +SUBSTFORMAT=%H%n test_expect_success \ 'populate workdir' \ @@ -36,7 +36,7 @@ test_expect_success \ echo simple textfile >a/a && mkdir a/bin && cp /bin/sh a/bin && - printf "A\$Format:%s\$O" "$SPECFILEFORMAT" >a/specfile && + printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile && ln -s a a/l1 && (p=long_path_to_a_file && cd a && for depth in 1 2 3 4 5; do mkdir $p && cd $p; done && @@ -108,20 +108,20 @@ test_expect_success \ 'diff -r a c/prefix/a' test_expect_success \ - 'create an archive with a specfile' \ - 'echo specfile specfile >a/.gitattributes && + 'create an archive with a substfile' \ + 'echo substfile export-subst >a/.gitattributes && git archive HEAD >f.tar && rm a/.gitattributes' test_expect_success \ - 'extract specfile' \ + 'extract substfile' \ '(mkdir f && cd f && $TAR xf -) f/a/specfile.expected && - diff f/a/specfile.expected f/a/specfile' + 'validate substfile contents' \ + 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ + >f/a/substfile.expected && + diff f/a/substfile.expected f/a/substfile' test_expect_success \ 'git archive --format=zip' \ From 4dbfe2e9bdfdde2d3257194573cee0d41471592b Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Fri, 7 Sep 2007 02:00:08 +0200 Subject: [PATCH 071/602] git-svn: always use --first-parent This makes git-svn unconditionally invoke git-log with --first-parent when it is trying to discover its upstream subversion branch and collecting the commit ids which should be pushed to it with dcommit. The reason for always using --first-parent is to make git-svn behave in a predictable way when the ancestry chain contains merges with other git-svn branches. Since git-svn now always uses 'git-log --first-parent' there is no longer any need for the --first-parent option to git-svn, so this is removed. Signed-off-by: Lars Hjemli Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- Documentation/git-svn.txt | 10 ---------- git-svn.perl | 17 +++++------------ 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt index 42d7b82a37..be2e34eb8f 100644 --- a/Documentation/git-svn.txt +++ b/Documentation/git-svn.txt @@ -317,16 +317,6 @@ This is only used with the 'dcommit' command. Print out the series of git arguments that would show which diffs would be committed to SVN. ---first-parent:: - -This is only used with the 'dcommit', 'rebase', 'log', 'find-rev' and -'show-ignore' commands. - -These commands tries to detect the upstream subversion branch by means of -the embedded 'git-svn-id' line in commit messages. When --first-parent is -specified, git-svn only follows the first parent of each commit, effectively -ignoring commits brought into the current branch through merge-operations. - -- ADVANCED OPTIONS diff --git a/git-svn.perl b/git-svn.perl index d21eb7fa9e..badcd33c2c 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -59,7 +59,7 @@ my ($_stdin, $_help, $_edit, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_merge, $_strategy, $_dry_run, $_local, - $_prefix, $_no_checkout, $_verbose, $_first_parent); + $_prefix, $_no_checkout, $_verbose); $Git::SVN::_follow_parent = 1; my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username, 'config-dir=s' => \$Git::SVN::Ra::config_dir, @@ -119,14 +119,12 @@ my %cmd = ( 'dry-run|n' => \$_dry_run, 'fetch-all|all' => \$_fetch_all, 'no-rebase' => \$_no_rebase, - 'first-parent' => \$_first_parent, %cmt_opts, %fc_opts } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", { 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ], 'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings", - { 'revision|r=i' => \$_revision, - 'first-parent' => \$_first_parent + { 'revision|r=i' => \$_revision } ], 'multi-fetch' => [ \&cmd_multi_fetch, "Deprecated alias for $0 fetch --all", @@ -147,20 +145,16 @@ my %cmd = ( 'non-recursive' => \$Git::SVN::Log::non_recursive, 'authors-file|A=s' => \$_authors, 'color' => \$Git::SVN::Log::color, - 'pager=s' => \$Git::SVN::Log::pager, - 'first-parent' => \$_first_parent + 'pager=s' => \$Git::SVN::Log::pager } ], 'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish", - { - 'first-parent' => \$_first_parent - } ], + {} ], 'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory", { 'merge|m|M' => \$_merge, 'verbose|v' => \$_verbose, 'strategy|s=s' => \$_strategy, 'local|l' => \$_local, 'fetch-all|all' => \$_fetch_all, - 'first-parent' => \$_first_parent, %fc_opts } ], 'commit-diff' => [ \&cmd_commit_diff, 'Commit a diff between two trees', @@ -818,8 +812,7 @@ sub cmt_metadata { sub working_head_info { my ($head, $refs) = @_; - my @args = ('log', '--no-color'); - push @args, '--first-parent' if $_first_parent; + my @args = ('log', '--no-color', '--first-parent'); my ($fh, $ctx) = command_output_pipe(@args, $head); my $hash; my %max; From b449f4cfc972929b638b90d375b8960c37790618 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:05 +0200 Subject: [PATCH 072/602] Rework strbuf API and semantics. The gory details are explained in strbuf.h. The change of semantics this patch enforces is that the embeded buffer has always a '\0' character after its last byte, to always make it a C-string. The offs-by-one changes are all related to that very change. A strbuf can be used to store byte arrays, or as an extended string library. The `buf' member can be passed to any C legacy string function, because strbuf operations always ensure there is a terminating \0 at the end of the buffer, not accounted in the `len' field of the structure. A strbuf can be used to generate a string/buffer whose final size is not really known, and then "strbuf_detach" can be used to get the built buffer, and keep the wrapping "strbuf" structure usable for further work again. Other interesting feature: strbuf_grow(sb, size) ensure that there is enough allocated space in `sb' to put `size' new octets of data in the buffer. It helps avoiding reallocating data for nothing when the problem the strbuf helps to solve has a known typical size. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- archive-tar.c | 2 +- fast-import.c | 15 ++++---- mktree.c | 4 +- strbuf.c | 101 +++++++++++++++++++++++++++++++++++++++++++------- strbuf.h | 86 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 180 insertions(+), 28 deletions(-) diff --git a/archive-tar.c b/archive-tar.c index 66fe3e375b..a0763c5b5a 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -166,7 +166,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1)); } else { if (verbose) - fprintf(stderr, "%.*s\n", path->len, path->buf); + fprintf(stderr, "%.*s\n", (int)path->len, path->buf); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { *header.typeflag = TYPEFLAG_DIR; mode = (mode | 0777) & ~tar_umask; diff --git a/fast-import.c b/fast-import.c index 078079d404..2f7baf4917 100644 --- a/fast-import.c +++ b/fast-import.c @@ -1595,7 +1595,7 @@ static void read_next_command(void) } else { struct recent_command *rc; - command_buf.buf = NULL; + strbuf_detach(&command_buf); read_line(&command_buf, stdin, '\n'); if (command_buf.eof) return; @@ -1649,7 +1649,6 @@ static void *cmd_data (size_t *size) size_t sz = 8192, term_len = command_buf.len - 5 - 2; length = 0; buffer = xmalloc(sz); - command_buf.buf = NULL; for (;;) { read_line(&command_buf, stdin, '\n'); if (command_buf.eof) @@ -1657,11 +1656,11 @@ static void *cmd_data (size_t *size) if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - ALLOC_GROW(buffer, length + command_buf.len, sz); + ALLOC_GROW(buffer, length + command_buf.len + 1, sz); memcpy(buffer + length, command_buf.buf, - command_buf.len - 1); - length += command_buf.len - 1; + command_buf.len); + length += command_buf.len; buffer[length++] = '\n'; } free(term); @@ -2101,7 +2100,7 @@ static void cmd_new_commit(void) } /* file_change* */ - while (!command_buf.eof && command_buf.len > 1) { + while (!command_buf.eof && command_buf.len > 0) { if (!prefixcmp(command_buf.buf, "M ")) file_change_m(b); else if (!prefixcmp(command_buf.buf, "D ")) @@ -2256,7 +2255,7 @@ static void cmd_reset_branch(void) else b = new_branch(sp); read_next_command(); - if (!cmd_from(b) && command_buf.len > 1) + if (!cmd_from(b) && command_buf.len > 0) unread_command_buf = 1; } @@ -2273,7 +2272,7 @@ static void cmd_checkpoint(void) static void cmd_progress(void) { - fwrite(command_buf.buf, 1, command_buf.len - 1, stdout); + fwrite(command_buf.buf, 1, command_buf.len, stdout); fputc('\n', stdout); fflush(stdout); skip_optional_lf(); diff --git a/mktree.c b/mktree.c index d86dde89d6..86de5eb5f6 100644 --- a/mktree.c +++ b/mktree.c @@ -92,7 +92,6 @@ int main(int ac, char **av) strbuf_init(&sb); while (1) { - int len; char *ptr, *ntr; unsigned mode; enum object_type type; @@ -101,7 +100,6 @@ int main(int ac, char **av) read_line(&sb, stdin, line_termination); if (sb.eof) break; - len = sb.len; ptr = sb.buf; /* Input is non-recursive ls-tree output format * mode SP type SP sha1 TAB name @@ -111,7 +109,7 @@ int main(int ac, char **av) die("input format error: %s", sb.buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); - if (!ntr || sb.buf + len <= ntr + 41 || + if (!ntr || sb.buf + sb.len <= ntr + 40 || ntr[41] != '\t' || get_sha1_hex(ntr + 1, sha1)) die("input format error: %s", sb.buf); diff --git a/strbuf.c b/strbuf.c index e33d06b87c..7136de14c6 100644 --- a/strbuf.c +++ b/strbuf.c @@ -2,40 +2,113 @@ #include "strbuf.h" void strbuf_init(struct strbuf *sb) { - sb->buf = NULL; - sb->eof = sb->alloc = sb->len = 0; + memset(sb, 0, sizeof(*sb)); } -static void strbuf_begin(struct strbuf *sb) { +void strbuf_release(struct strbuf *sb) { free(sb->buf); + memset(sb, 0, sizeof(*sb)); +} + +void strbuf_reset(struct strbuf *sb) { + if (sb->len) + strbuf_setlen(sb, 0); + sb->eof = 0; +} + +char *strbuf_detach(struct strbuf *sb) { + char *res = sb->buf; strbuf_init(sb); + return res; } -static void inline strbuf_add(struct strbuf *sb, int ch) { - if (sb->alloc <= sb->len) { - sb->alloc = sb->alloc * 3 / 2 + 16; - sb->buf = xrealloc(sb->buf, sb->alloc); +void strbuf_grow(struct strbuf *sb, size_t extra) { + if (sb->len + extra + 1 <= sb->len) + die("you want to use way too much memory"); + ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); +} + +void strbuf_add(struct strbuf *sb, const void *data, size_t len) { + strbuf_grow(sb, len); + memcpy(sb->buf + sb->len, data, len); + strbuf_setlen(sb, sb->len + len); +} + +void strbuf_addf(struct strbuf *sb, const char *fmt, ...) { + int len; + va_list ap; + + va_start(ap, fmt); + len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + va_end(ap); + if (len < 0) { + len = 0; } - sb->buf[sb->len++] = ch; + if (len >= strbuf_avail(sb)) { + strbuf_grow(sb, len); + va_start(ap, fmt); + len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); + va_end(ap); + if (len >= strbuf_avail(sb)) { + die("this should not happen, your snprintf is broken"); + } + } + strbuf_setlen(sb, sb->len + len); } -static void strbuf_end(struct strbuf *sb) { - strbuf_add(sb, 0); +size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) { + size_t res; + + strbuf_grow(sb, size); + res = fread(sb->buf + sb->len, 1, size, f); + if (res > 0) { + strbuf_setlen(sb, sb->len + res); + } + return res; +} + +ssize_t strbuf_read(struct strbuf *sb, int fd) +{ + size_t oldlen = sb->len; + + for (;;) { + ssize_t cnt; + + strbuf_grow(sb, 8192); + cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); + if (cnt < 0) { + strbuf_setlen(sb, oldlen); + return -1; + } + if (!cnt) + break; + sb->len += cnt; + } + + sb->buf[sb->len] = '\0'; + return sb->len - oldlen; } void read_line(struct strbuf *sb, FILE *fp, int term) { int ch; - strbuf_begin(sb); if (feof(fp)) { + strbuf_release(sb); sb->eof = 1; return; } + + strbuf_reset(sb); while ((ch = fgetc(fp)) != EOF) { if (ch == term) break; - strbuf_add(sb, ch); + strbuf_grow(sb, 1); + sb->buf[sb->len++] = ch; } - if (ch == EOF && sb->len == 0) + if (ch == EOF && sb->len == 0) { + strbuf_release(sb); sb->eof = 1; - strbuf_end(sb); + } + + strbuf_grow(sb, 1); + sb->buf[sb->len] = '\0'; } diff --git a/strbuf.h b/strbuf.h index 74cc012c2c..b40dc99fd0 100644 --- a/strbuf.h +++ b/strbuf.h @@ -1,13 +1,95 @@ #ifndef STRBUF_H #define STRBUF_H + +/* + * Strbuf's can be use in many ways: as a byte array, or to store arbitrary + * long, overflow safe strings. + * + * Strbufs has some invariants that are very important to keep in mind: + * + * 1. the ->buf member is always malloc-ed, hence strbuf's can be used to + * build complex strings/buffers whose final size isn't easily known. + * + * It is legal to copy the ->buf pointer away. Though if you want to reuse + * the strbuf after that, setting ->buf to NULL isn't legal. + * `strbuf_detach' is the operation that detachs a buffer from its shell + * while keeping the shell valid wrt its invariants. + * + * 2. the ->buf member is a byte array that has at least ->len + 1 bytes + * allocated. The extra byte is used to store a '\0', allowing the ->buf + * member to be a valid C-string. Every strbuf function ensure this + * invariant is preserved. + * + * Note that it is OK to "play" with the buffer directly if you work it + * that way: + * + * strbuf_grow(sb, SOME_SIZE); + * // ... here the memory areay starting at sb->buf, and of length + * // sb_avail(sb) is all yours, and you are sure that sb_avail(sb) is at + * // least SOME_SIZE + * strbuf_setlen(sb, sb->len + SOME_OTHER_SIZE); + * + * Of course, SOME_OTHER_SIZE must be smaller or equal to sb_avail(sb). + * + * Doing so is safe, though if it has to be done in many places, adding the + * missing API to the strbuf module is the way to go. + * + * XXX: do _not_ assume that the area that is yours is of size ->alloc - 1 + * even if it's true in the current implementation. Alloc is somehow a + * "private" member that should not be messed with. + */ + +#include + struct strbuf { - int alloc; - int len; + size_t alloc; + size_t len; int eof; char *buf; }; +#define STRBUF_INIT { 0, 0, 0, NULL } + +/*----- strbuf life cycle -----*/ extern void strbuf_init(struct strbuf *); +extern void strbuf_release(struct strbuf *); +extern void strbuf_reset(struct strbuf *); +extern char *strbuf_detach(struct strbuf *); + +/*----- strbuf size related -----*/ +static inline size_t strbuf_avail(struct strbuf *sb) { + return sb->alloc ? sb->alloc - sb->len - 1 : 0; +} +static inline void strbuf_setlen(struct strbuf *sb, size_t len) { + assert (len < sb->alloc); + sb->len = len; + sb->buf[len] = '\0'; +} + +extern void strbuf_grow(struct strbuf *, size_t); + +/*----- add data in your buffer -----*/ +static inline void strbuf_addch(struct strbuf *sb, int c) { + strbuf_grow(sb, 1); + sb->buf[sb->len++] = c; + sb->buf[sb->len] = '\0'; +} + +extern void strbuf_add(struct strbuf *, const void *, size_t); +static inline void strbuf_addstr(struct strbuf *sb, const char *s) { + strbuf_add(sb, s, strlen(s)); +} +static inline void strbuf_addbuf(struct strbuf *sb, struct strbuf *sb2) { + strbuf_add(sb, sb2->buf, sb2->len); +} + +__attribute__((format(printf,2,3))) +extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...); + +extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); +/* XXX: if read fails, any partial read is undone */ +extern ssize_t strbuf_read(struct strbuf *, int fd); + extern void read_line(struct strbuf *, FILE *, int); #endif /* STRBUF_H */ From 7a604f16b71e3bfd1c6e30d400f05be918e5376e Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:06 +0200 Subject: [PATCH 073/602] Simplify strbuf uses in archive-tar.c using strbuf API This is just cleaner way to deal with strbufs, using its API rather than reinventing it in the module (e.g. strbuf_append_string is just the plain strbuf_addstr function, and it was used to perform what strbuf_addch does anyways). Signed-off-by: Junio C Hamano --- archive-tar.c | 65 +++++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/archive-tar.c b/archive-tar.c index a0763c5b5a..c84d7c0652 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -78,19 +78,6 @@ static void write_trailer(void) } } -static void strbuf_append_string(struct strbuf *sb, const char *s) -{ - int slen = strlen(s); - int total = sb->len + slen; - if (total + 1 > sb->alloc) { - sb->buf = xrealloc(sb->buf, total + 1); - sb->alloc = total + 1; - } - memcpy(sb->buf + sb->len, s, slen); - sb->len = total; - sb->buf[total] = '\0'; -} - /* * pax extended header records have the format "%u %s=%s\n". %u contains * the size of the whole string (including the %u), the first %s is the @@ -100,26 +87,17 @@ static void strbuf_append_string(struct strbuf *sb, const char *s) static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword, const char *value, unsigned int valuelen) { - char *p; - int len, total, tmp; + int len, tmp; /* "%u %s=%s\n" */ len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1; for (tmp = len; tmp > 9; tmp /= 10) len++; - total = sb->len + len; - if (total > sb->alloc) { - sb->buf = xrealloc(sb->buf, total); - sb->alloc = total; - } - - p = sb->buf; - p += sprintf(p, "%u %s=", len, keyword); - memcpy(p, value, valuelen); - p += valuelen; - *p = '\n'; - sb->len = total; + strbuf_grow(sb, len); + strbuf_addf(sb, "%u %s=", len, keyword); + strbuf_add(sb, value, valuelen); + strbuf_addch(sb, '\n'); } static unsigned int ustar_header_chksum(const struct ustar_header *header) @@ -153,8 +131,7 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, struct strbuf ext_header; memset(&header, 0, sizeof(header)); - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + strbuf_init(&ext_header); if (!sha1) { *header.typeflag = TYPEFLAG_GLOBAL_HEADER; @@ -225,8 +202,8 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, if (ext_header.len > 0) { write_entry(sha1, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); } + strbuf_release(&ext_header); write_blocked(&header, sizeof(header)); if (S_ISREG(mode) && buffer && size > 0) write_blocked(buffer, size); @@ -235,11 +212,11 @@ static void write_entry(const unsigned char *sha1, struct strbuf *path, static void write_global_extended_header(const unsigned char *sha1) { struct strbuf ext_header; - ext_header.buf = NULL; - ext_header.len = ext_header.alloc = 0; + + strbuf_init(&ext_header); strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40); write_entry(NULL, NULL, 0, ext_header.buf, ext_header.len); - free(ext_header.buf); + strbuf_release(&ext_header); } static int git_tar_config(const char *var, const char *value) @@ -260,28 +237,18 @@ static int write_tar_entry(const unsigned char *sha1, const char *base, int baselen, const char *filename, unsigned mode, int stage) { - static struct strbuf path; + static struct strbuf path = STRBUF_INIT; int filenamelen = strlen(filename); void *buffer; enum object_type type; unsigned long size; - if (!path.alloc) { - path.buf = xmalloc(PATH_MAX); - path.alloc = PATH_MAX; - path.len = path.eof = 0; - } - if (path.alloc < baselen + filenamelen + 1) { - free(path.buf); - path.buf = xmalloc(baselen + filenamelen + 1); - path.alloc = baselen + filenamelen + 1; - } - memcpy(path.buf, base, baselen); - memcpy(path.buf + baselen, filename, filenamelen); - path.len = baselen + filenamelen; - path.buf[path.len] = '\0'; + strbuf_grow(&path, MAX(PATH_MAX, baselen + filenamelen + 1)); + strbuf_reset(&path); + strbuf_add(&path, base, baselen); + strbuf_add(&path, filename, filenamelen); if (S_ISDIR(mode) || S_ISGITLINK(mode)) { - strbuf_append_string(&path, "/"); + strbuf_addch(&path, '/'); buffer = NULL; size = 0; } else { From 4a241d79c9020dcafb1f254774bcab200171ab46 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:07 +0200 Subject: [PATCH 074/602] fast-import: Use strbuf API, and simplify cmd_data() This patch features the use of strbuf_detach, and prevent the programmer to mess with allocation directly. The code is as efficent as before, just more concise and more straightforward. Signed-off-by: Junio C Hamano --- fast-import.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/fast-import.c b/fast-import.c index 2f7baf4917..74ff0fdadd 100644 --- a/fast-import.c +++ b/fast-import.c @@ -340,7 +340,7 @@ static struct tag *last_tag; /* Input stream parsing */ static whenspec_type whenspec = WHENSPEC_RAW; -static struct strbuf command_buf; +static struct strbuf command_buf = STRBUF_INIT; static int unread_command_buf; static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL}; static struct recent_command *cmd_tail = &cmd_hist; @@ -1638,17 +1638,16 @@ static void cmd_mark(void) static void *cmd_data (size_t *size) { - size_t length; - char *buffer; + struct strbuf buffer; + strbuf_init(&buffer); if (prefixcmp(command_buf.buf, "data ")) die("Expected 'data n' command, found: %s", command_buf.buf); if (!prefixcmp(command_buf.buf + 5, "<<")) { char *term = xstrdup(command_buf.buf + 5 + 2); - size_t sz = 8192, term_len = command_buf.len - 5 - 2; - length = 0; - buffer = xmalloc(sz); + size_t term_len = command_buf.len - 5 - 2; + for (;;) { read_line(&command_buf, stdin, '\n'); if (command_buf.eof) @@ -1656,21 +1655,18 @@ static void *cmd_data (size_t *size) if (term_len == command_buf.len && !strcmp(term, command_buf.buf)) break; - ALLOC_GROW(buffer, length + command_buf.len + 1, sz); - memcpy(buffer + length, - command_buf.buf, - command_buf.len); - length += command_buf.len; - buffer[length++] = '\n'; + strbuf_addbuf(&buffer, &command_buf); + strbuf_addch(&buffer, '\n'); } free(term); } else { - size_t n = 0; + size_t n = 0, length; + length = strtoul(command_buf.buf + 5, NULL, 10); - buffer = xmalloc(length); + while (n < length) { - size_t s = fread(buffer + n, 1, length - n, stdin); + size_t s = strbuf_fread(&buffer, length - n, stdin); if (!s && feof(stdin)) die("EOF in data (%lu bytes remaining)", (unsigned long)(length - n)); @@ -1679,8 +1675,8 @@ static void *cmd_data (size_t *size) } skip_optional_lf(); - *size = length; - return buffer; + *size = buffer.len; + return strbuf_detach(&buffer); } static int validate_raw_date(const char *src, char *result, int maxlen) From d52bc66152834dff3fb5f32a54f6ed57730f58c6 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:08 +0200 Subject: [PATCH 075/602] mktree: Simplify write_tree() using strbuf API Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- mktree.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/mktree.c b/mktree.c index 86de5eb5f6..2e84889c02 100644 --- a/mktree.c +++ b/mktree.c @@ -44,30 +44,23 @@ static int ent_compare(const void *a_, const void *b_) static void write_tree(unsigned char *sha1) { - char *buffer; - unsigned long size, offset; + struct strbuf buf; + size_t size; int i; qsort(entries, used, sizeof(*entries), ent_compare); for (size = i = 0; i < used; i++) size += 32 + entries[i]->len; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buf); + strbuf_grow(&buf, size); for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; - - if (offset + ent->len + 100 < size) { - size = alloc_nr(offset + ent->len + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, "%o ", ent->mode); - offset += sprintf(buffer + offset, "%s", ent->name); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, ent->sha1); - offset += 20; + strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); + strbuf_add(&buf, ent->sha1, 20); } - write_sha1_file(buffer, offset, tree_type, sha1); + + write_sha1_file(buf.buf, buf.len, tree_type, sha1); } static const char mktree_usage[] = "git-mktree [-z]"; From af6eb82262e35687aa8f00d688e327cb845973fa Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:09 +0200 Subject: [PATCH 076/602] Use strbuf API in apply, blame, commit-tree and diff Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-apply.c | 30 +++++++--------------- builtin-blame.c | 35 +++++++++---------------- builtin-commit-tree.c | 60 ++++++++++--------------------------------- diff.c | 25 ++++++------------ 4 files changed, 43 insertions(+), 107 deletions(-) diff --git a/builtin-apply.c b/builtin-apply.c index 976ec77041..90e328ef91 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -12,6 +12,7 @@ #include "blob.h" #include "delta.h" #include "builtin.h" +#include "strbuf.h" /* * --check turns on checking that the working tree matches the @@ -181,34 +182,21 @@ static void say_patch_name(FILE *output, const char *pre, struct patch *patch, c static void *read_patch_file(int fd, unsigned long *sizep) { - unsigned long size = 0, alloc = CHUNKSIZE; - void *buffer = xmalloc(alloc); + struct strbuf buf; - for (;;) { - ssize_t nr = alloc - size; - if (nr < 1024) { - alloc += CHUNKSIZE; - buffer = xrealloc(buffer, alloc); - nr = alloc - size; - } - nr = xread(fd, (char *) buffer + size, nr); - if (!nr) - break; - if (nr < 0) - die("git-apply: read returned %s", strerror(errno)); - size += nr; - } - *sizep = size; + strbuf_init(&buf); + if (strbuf_read(&buf, fd) < 0) + die("git-apply: read returned %s", strerror(errno)); + *sizep = buf.len; /* * Make sure that we have some slop in the buffer * so that we can do speculative "memcmp" etc, and * see to it that it is NUL-filled. */ - if (alloc < size + SLOP) - buffer = xrealloc(buffer, size + SLOP); - memset((char *) buffer + size, 0, SLOP); - return buffer; + strbuf_grow(&buf, SLOP); + memset(buf.buf + buf.len, 0, SLOP); + return strbuf_detach(&buf); } static unsigned long linelen(const char *buffer, unsigned long size) diff --git a/builtin-blame.c b/builtin-blame.c index dc88a953a5..1b1e6da853 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -18,6 +18,7 @@ #include "cache-tree.h" #include "path-list.h" #include "mailmap.h" +#include "strbuf.h" static char blame_usage[] = "git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S ] [-M] [-C] [-C] [--contents ] [--incremental] [commit] [--] file\n" @@ -2001,11 +2002,10 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con struct commit *commit; struct origin *origin; unsigned char head_sha1[20]; - char *buf; + struct strbuf buf; const char *ident; int fd; time_t now; - unsigned long fin_size; int size, len; struct cache_entry *ce; unsigned mode; @@ -2023,9 +2023,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con origin = make_origin(commit, path); + strbuf_init(&buf); if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; + unsigned long fin_size; if (contents_from) { if (stat(contents_from, &st) < 0) @@ -2038,19 +2040,19 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con read_from = path; } fin_size = xsize_t(st.st_size); - buf = xmalloc(fin_size+1); mode = canon_mode(st.st_mode); switch (st.st_mode & S_IFMT) { case S_IFREG: fd = open(read_from, O_RDONLY); if (fd < 0) die("cannot open %s", read_from); - if (read_in_full(fd, buf, fin_size) != fin_size) + if (strbuf_read(&buf, fd) != xsize_t(st.st_size)) die("cannot read %s", read_from); break; case S_IFLNK: - if (readlink(read_from, buf, fin_size+1) != fin_size) + if (readlink(read_from, buf.buf, buf.alloc) != fin_size) die("cannot readlink %s", read_from); + buf.len = fin_size; break; default: die("unsupported file type %s", read_from); @@ -2059,26 +2061,13 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con else { /* Reading from stdin */ contents_from = "standard input"; - buf = NULL; - fin_size = 0; mode = 0; - while (1) { - ssize_t cnt = 8192; - buf = xrealloc(buf, fin_size + cnt); - cnt = xread(0, buf + fin_size, cnt); - if (cnt < 0) - die("read error %s from stdin", - strerror(errno)); - if (!cnt) - break; - fin_size += cnt; - } - buf = xrealloc(buf, fin_size + 1); + if (strbuf_read(&buf, 0) < 0) + die("read error %s from stdin", strerror(errno)); } - buf[fin_size] = 0; - origin->file.ptr = buf; - origin->file.size = fin_size; - pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1); + origin->file.ptr = buf.buf; + origin->file.size = buf.len; + pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_sha1); commit->util = origin; /* diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c index ccbcbe30da..bc9502c135 100644 --- a/builtin-commit-tree.c +++ b/builtin-commit-tree.c @@ -8,42 +8,13 @@ #include "tree.h" #include "builtin.h" #include "utf8.h" +#include "strbuf.h" #define BLOCKING (1ul << 14) /* * FIXME! Share the code with "write-tree.c" */ -static void init_buffer(char **bufp, unsigned int *sizep) -{ - *bufp = xmalloc(BLOCKING); - *sizep = 0; -} - -static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) -{ - char one_line[2048]; - va_list args; - int len; - unsigned long alloc, size, newsize; - char *buf; - - va_start(args, fmt); - len = vsnprintf(one_line, sizeof(one_line), fmt, args); - va_end(args); - size = *sizep; - newsize = size + len + 1; - alloc = (size + 32767) & ~32767; - buf = *bufp; - if (newsize > alloc) { - alloc = (newsize + 32767) & ~32767; - buf = xrealloc(buf, alloc); - *bufp = buf; - } - *sizep = newsize - 1; - memcpy(buf + size, one_line, len); -} - static void check_valid(unsigned char *sha1, enum object_type expect) { enum object_type type = sha1_object_info(sha1, NULL); @@ -87,9 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) int parents = 0; unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; - char comment[1000]; - char *buffer; - unsigned int size; + struct strbuf buffer; int encoding_is_utf8; git_config(git_default_config); @@ -118,8 +87,9 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) /* Not having i18n.commitencoding is the same as having utf-8 */ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - init_buffer(&buffer, &size); - add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1)); + strbuf_init(&buffer); + strbuf_grow(&buffer, 8192); /* should avoid reallocs for the headers */ + strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree_sha1)); /* * NOTE! This ordering means that the same exact tree merged with a @@ -127,26 +97,24 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix) * if everything else stays the same. */ for (i = 0; i < parents; i++) - add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i])); + strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i])); /* Person/date information */ - add_buffer(&buffer, &size, "author %s\n", git_author_info(1)); - add_buffer(&buffer, &size, "committer %s\n", git_committer_info(1)); + strbuf_addf(&buffer, "author %s\n", git_author_info(1)); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(1)); if (!encoding_is_utf8) - add_buffer(&buffer, &size, - "encoding %s\n", git_commit_encoding); - add_buffer(&buffer, &size, "\n"); + strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); + strbuf_addch(&buffer, '\n'); /* And add the comment */ - while (fgets(comment, sizeof(comment), stdin) != NULL) - add_buffer(&buffer, &size, "%s", comment); + if (strbuf_read(&buffer, 0) < 0) + die("git-commit-tree: read returned %s", strerror(errno)); /* And check the encoding */ - buffer[size] = '\0'; - if (encoding_is_utf8 && !is_utf8(buffer)) + if (encoding_is_utf8 && !is_utf8(buffer.buf)) fprintf(stderr, commit_utf8_warn); - if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) { + if (!write_sha1_file(buffer.buf, buffer.len, commit_type, commit_sha1)) { printf("%s\n", sha1_to_hex(commit_sha1)); return 0; } diff --git a/diff.c b/diff.c index 0d30d05263..c054b234b8 100644 --- a/diff.c +++ b/diff.c @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" +#include "strbuf.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1545,26 +1546,16 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int static int populate_from_stdin(struct diff_filespec *s) { -#define INCREMENT 1024 - char *buf; - unsigned long size; - ssize_t got; + struct strbuf buf; - size = 0; - buf = NULL; - while (1) { - buf = xrealloc(buf, size + INCREMENT); - got = xread(0, buf + size, INCREMENT); - if (!got) - break; /* EOF */ - if (got < 0) - return error("error while reading from stdin %s", + strbuf_init(&buf); + if (strbuf_read(&buf, 0) < 0) + return error("error while reading from stdin %s", strerror(errno)); - size += got; - } + s->should_munmap = 0; - s->data = buf; - s->size = size; + s->size = buf.len; + s->data = strbuf_detach(&buf); s->should_free = 1; return 0; } From 19b358e8daafdfe5a5d25ff7972e041493b05156 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:10 +0200 Subject: [PATCH 077/602] Use strbuf API in buitin-rerere.c Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-rerere.c | 56 ++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/builtin-rerere.c b/builtin-rerere.c index 29d057c98c..98d7702168 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -1,6 +1,7 @@ #include "builtin.h" #include "cache.h" #include "path-list.h" +#include "strbuf.h" #include "xdiff/xdiff.h" #include "xdiff-interface.h" @@ -66,41 +67,20 @@ static int write_rr(struct path_list *rr, int out_fd) return commit_lock_file(&write_lock); } -struct buffer { - char *ptr; - int nr, alloc; -}; - -static void append_line(struct buffer *buffer, const char *line) -{ - int len = strlen(line); - - if (buffer->nr + len > buffer->alloc) { - buffer->alloc = alloc_nr(buffer->nr + len); - buffer->ptr = xrealloc(buffer->ptr, buffer->alloc); - } - memcpy(buffer->ptr + buffer->nr, line, len); - buffer->nr += len; -} - -static void clear_buffer(struct buffer *buffer) -{ - free(buffer->ptr); - buffer->ptr = NULL; - buffer->nr = buffer->alloc = 0; -} - static int handle_file(const char *path, unsigned char *sha1, const char *output) { SHA_CTX ctx; char buf[1024]; int hunk = 0, hunk_no = 0; - struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 }; - struct buffer *one = &minus, *two = + + struct strbuf minus, plus; + struct strbuf *one = &minus, *two = + FILE *f = fopen(path, "r"); FILE *out; + strbuf_init(&minus); + strbuf_init(&plus); + if (!f) return error("Could not open %s", path); @@ -122,36 +102,36 @@ static int handle_file(const char *path, else if (!prefixcmp(buf, "=======")) hunk = 2; else if (!prefixcmp(buf, ">>>>>>> ")) { - int one_is_longer = (one->nr > two->nr); - int common_len = one_is_longer ? two->nr : one->nr; - int cmp = memcmp(one->ptr, two->ptr, common_len); + int one_is_longer = (one->len > two->len); + int common_len = one_is_longer ? two->len : one->len; + int cmp = memcmp(one->buf, two->buf, common_len); hunk_no++; hunk = 0; if ((cmp > 0) || ((cmp == 0) && one_is_longer)) { - struct buffer *swap = one; + struct strbuf *swap = one; one = two; two = swap; } if (out) { fputs("<<<<<<<\n", out); - fwrite(one->ptr, one->nr, 1, out); + fwrite(one->buf, one->len, 1, out); fputs("=======\n", out); - fwrite(two->ptr, two->nr, 1, out); + fwrite(two->buf, two->len, 1, out); fputs(">>>>>>>\n", out); } if (sha1) { - SHA1_Update(&ctx, one->ptr, one->nr); + SHA1_Update(&ctx, one->buf, one->len); SHA1_Update(&ctx, "\0", 1); - SHA1_Update(&ctx, two->ptr, two->nr); + SHA1_Update(&ctx, two->buf, two->len); SHA1_Update(&ctx, "\0", 1); } - clear_buffer(one); - clear_buffer(two); + strbuf_release(one); + strbuf_release(two); } else if (hunk == 1) - append_line(one, buf); + strbuf_addstr(one, buf); else if (hunk == 2) - append_line(two, buf); + strbuf_addstr(two, buf); else if (out) fputs(buf, out); } From 5242bcbb638f031818e9ebd4467c8e55d5a06bfb Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 6 Sep 2007 13:20:11 +0200 Subject: [PATCH 078/602] Use strbuf API in cache-tree.c Should even be marginally faster. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- cache-tree.c | 59 ++++++++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/cache-tree.c b/cache-tree.c index 077f034369..76af6f5d99 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "strbuf.h" #include "tree.h" #include "cache-tree.h" @@ -235,8 +236,7 @@ static int update_one(struct cache_tree *it, int missing_ok, int dryrun) { - unsigned long size, offset; - char *buffer; + struct strbuf buffer; int i; if (0 <= it->entry_count && has_sha1_file(it->sha1)) @@ -293,9 +293,8 @@ static int update_one(struct cache_tree *it, /* * Then write out the tree object for this level. */ - size = 8192; - buffer = xmalloc(size); - offset = 0; + strbuf_init(&buffer); + strbuf_grow(&buffer, 8192); for (i = 0; i < entries; i++) { struct cache_entry *ce = cache[i]; @@ -332,15 +331,9 @@ static int update_one(struct cache_tree *it, if (!ce->ce_mode) continue; /* entry being removed */ - if (size < offset + entlen + 100) { - size = alloc_nr(offset + entlen + 100); - buffer = xrealloc(buffer, size); - } - offset += sprintf(buffer + offset, - "%o %.*s", mode, entlen, path + baselen); - buffer[offset++] = 0; - hashcpy((unsigned char*)buffer + offset, sha1); - offset += 20; + strbuf_grow(&buffer, entlen + 100); + strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0'); + strbuf_add(&buffer, sha1, 20); #if DEBUG fprintf(stderr, "cache-tree update-one %o %.*s\n", @@ -349,10 +342,10 @@ static int update_one(struct cache_tree *it, } if (dryrun) - hash_sha1_file(buffer, offset, tree_type, it->sha1); + hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else - write_sha1_file(buffer, offset, tree_type, it->sha1); - free(buffer); + write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); + strbuf_release(&buffer); it->entry_count = i; #if DEBUG fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n", @@ -378,12 +371,10 @@ int cache_tree_update(struct cache_tree *it, return 0; } -static void *write_one(struct cache_tree *it, +static void write_one(struct cache_tree *it, char *path, int pathlen, - char *buffer, - unsigned long *size, - unsigned long *offset) + struct strbuf *buffer) { int i; @@ -393,13 +384,9 @@ static void *write_one(struct cache_tree *it, * tree-sha1 (missing if invalid) * subtree_nr "cache-tree" entries for subtrees. */ - if (*size < *offset + pathlen + 100) { - *size = alloc_nr(*offset + pathlen + 100); - buffer = xrealloc(buffer, *size); - } - *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n", - pathlen, path, 0, - it->entry_count, it->subtree_nr); + strbuf_grow(buffer, pathlen + 100); + strbuf_add(buffer, path, pathlen); + strbuf_addf(buffer, "%c%d %d\n", 0, it->entry_count, it->subtree_nr); #if DEBUG if (0 <= it->entry_count) @@ -412,8 +399,7 @@ static void *write_one(struct cache_tree *it, #endif if (0 <= it->entry_count) { - hashcpy((unsigned char*)buffer + *offset, it->sha1); - *offset += 20; + strbuf_add(buffer, it->sha1, 20); } for (i = 0; i < it->subtree_nr; i++) { struct cache_tree_sub *down = it->down[i]; @@ -423,21 +409,20 @@ static void *write_one(struct cache_tree *it, prev->name, prev->namelen) <= 0) die("fatal - unsorted cache subtree"); } - buffer = write_one(down->cache_tree, down->name, down->namelen, - buffer, size, offset); + write_one(down->cache_tree, down->name, down->namelen, buffer); } - return buffer; } void *cache_tree_write(struct cache_tree *root, unsigned long *size_p) { char path[PATH_MAX]; - unsigned long size = 8192; - char *buffer = xmalloc(size); + struct strbuf buffer; - *size_p = 0; path[0] = 0; - return write_one(root, path, 0, buffer, &size, size_p); + strbuf_init(&buffer); + write_one(root, path, 0, &buffer); + *size_p = buffer.len; + return strbuf_detach(&buffer); } static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) From 451e593181c554b06f1ce292b4233d396a355753 Mon Sep 17 00:00:00 2001 From: Mike Ralphson Date: Fri, 7 Sep 2007 17:43:37 +0100 Subject: [PATCH 079/602] Documentation / grammer nit If we're counting, a smaller number is 'fewer' not 'less' Signed-off-by: Mike Ralphson Signed-off-by: Junio C Hamano --- Documentation/git-clone.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 227f092e26..253f4f03c5 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -68,7 +68,7 @@ OPTIONS automatically setup .git/objects/info/alternates to obtain objects from the reference repository. Using an already existing repository as an alternate will - require less objects to be copied from the repository + require fewer objects to be copied from the repository being cloned, reducing network and local storage costs. --quiet:: From 059f446d57d51fbacdace3fbadf2414916c201dd Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 7 Sep 2007 10:20:50 -0400 Subject: [PATCH 080/602] git-rebase: support --whitespace=