From 50b44eceed2105db0bee194904649249cc5f0308 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 4 Apr 2006 10:16:22 +1000 Subject: [PATCH 001/212] gitk: Implement multiple views With this, gitk can know about the graphs for multiple sets of files and directories of interest. Each set of files/dirs and its graph is called a "view". There is always the "All files" view, which is the complete graph showing all commits. If files or dirs are specified on the command line, a "Command line" view is automatically created. Users can create new views and switch between them, and can delete any view except the "All files" view. This required a bit of reengineering. In particular, some more things that were arrays have now become lists. The idrowranges array is still used while the graph is being laid out, but for rows that have been laid out we use the rowrangelist list instead. The cornercrossings and crossings arrays no longer exist, and instead we compute the crossings when needed (in assigncolor). Still to be done: make the back/forward buttons switch views as necessary; make the updatecommits function work right; preserve the selection if possible when the new view has to be read in; fix the case when the user switches away from the current view while we are still reading it in and laying it out; further optimizations. Signed-off-by: Paul Mackerras --- gitk | 452 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 334 insertions(+), 118 deletions(-) diff --git a/gitk b/gitk index 90afec92d..961b582a4 100755 --- a/gitk +++ b/gitk @@ -17,19 +17,38 @@ proc gitdir {} { } proc parse_args {rargs} { - global parsed_args + global parsed_args cmdline_files + set parsed_args {} + set cmdline_files {} if {[catch { set parse_args [concat --default HEAD $rargs] - set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"] + set args [split [eval exec git-rev-parse $parse_args] "\n"] + set i 0 + foreach arg $args { + if {![regexp {^[0-9a-f]{40}$} $arg]} { + if {$arg eq "--"} { + incr i + } + set cmdline_files [lrange $args $i end] + break + } + lappend parsed_args $arg + incr i + } }]} { # if git-rev-parse failed for some reason... + set i [lsearch -exact $rargs "--"] + if {$i >= 0} { + set cmdline_files [lrange $rargs [expr {$i+1}] end] + set rargs [lrange $rargs 0 [expr {$i-1}]] + } if {$rargs == {}} { - set rargs HEAD + set parsed_args HEAD + } else { + set parsed_args $rargs } - set parsed_args $rargs } - return $parsed_args } proc start_rev_list {rlargs} { @@ -65,7 +84,7 @@ proc getcommits {rargs} { global phase canv mainfont set phase getcommits - start_rev_list [parse_args $rargs] + start_rev_list $rargs $canv delete all $canv create text 3 3 -anchor nw -text "Reading commits..." \ -font $mainfont -tags textitems @@ -141,11 +160,12 @@ proc getcommitlines {commfd} { set id [lindex $ids 0] if {$listed} { set olds [lrange $ids 1 end] - if {[llength $olds] > 1} { - set olds [lsort -unique $olds] - } + set i 0 foreach p $olds { - lappend children($p) $id + if {$i == 0 || [lsearch -exact $olds $p] >= $i} { + lappend children($p) $id + } + incr i } } else { set olds {} @@ -196,18 +216,18 @@ proc readcommit {id} { parsecommit $id $contents 0 } -proc updatecommits {rargs} { +proc updatecommits {} { + global parsed_args + + unselectline stopfindproc - foreach v {colormap selectedline matchinglines treediffs - mergefilelist currentid rowtextx commitrow - rowidlist rowoffsets idrowranges idrangedrawn iddrawn - linesegends crossings cornercrossings} { + foreach v {matchinglines treediffs currentid} { global $v catch {unset $v} } - allcanvs delete all + clear_display readrefs - getcommits $rargs + getcommits $parsed_args } proc parsecommit {id contents listed} { @@ -344,12 +364,18 @@ proc makewindow {rargs} { menu .bar .bar add cascade -label "File" -menu .bar.file menu .bar.file - .bar.file add command -label "Update" -command [list updatecommits $rargs] + .bar.file add command -label "Update" -command updatecommits .bar.file add command -label "Reread references" -command rereadrefs .bar.file add command -label "Quit" -command doquit menu .bar.edit .bar add cascade -label "Edit" -menu .bar.edit .bar.edit add command -label "Preferences" -command doprefs + menu .bar.view + .bar add cascade -label "View" -menu .bar.view + .bar.view add command -label "New view..." -command newview + .bar.view add command -label "Delete view" -command delview -state disabled + .bar.view add separator + .bar.view add command -label "All files" -command {showview 0} menu .bar.help .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About gitk" -command about @@ -718,6 +744,169 @@ Use and redistribute under the terms of the GNU General Public License} \ pack $w.ok -side bottom } +proc newview {} { + global newviewname nextviewnum newviewtop + + set top .gitkview + if {[winfo exists $top]} { + raise $top + return + } + set newviewtop $top + toplevel $top + wm title $top "Gitk view definition" + label $top.nl -text "Name" + entry $top.name -width 20 -textvariable newviewname + set newviewname "View $nextviewnum" + grid $top.nl $top.name -sticky w + label $top.l -text "Files and directories to include:" + grid $top.l - -sticky w -pady 10 + text $top.t -width 30 -height 10 + grid $top.t - -sticky w + frame $top.buts + button $top.buts.ok -text "OK" -command newviewok + button $top.buts.can -text "Cancel" -command newviewcan + grid $top.buts.ok $top.buts.can + grid columnconfigure $top.buts 0 -weight 1 -uniform a + grid columnconfigure $top.buts 1 -weight 1 -uniform a + grid $top.buts - -pady 10 -sticky ew + focus $top.t +} + +proc newviewok {} { + global newviewtop nextviewnum + global viewname viewfiles + + set n $nextviewnum + incr nextviewnum + set viewname($n) [$newviewtop.name get] + set files {} + foreach f [split [$newviewtop.t get 0.0 end] "\n"] { + set ft [string trim $f] + if {$ft ne {}} { + lappend files $ft + } + } + set viewfiles($n) $files + catch {destroy $newviewtop} + unset newviewtop + .bar.view add command -label $viewname($n) -command [list showview $n] + after idle showview $n +} + +proc newviewcan {} { + global newviewtop + + catch {destroy $newviewtop} + unset newviewtop +} + +proc delview {} { + global curview viewdata + + if {$curview == 0} return + set nmenu [.bar.view index end] + set targetcmd [list showview $curview] + for {set i 5} {$i <= $nmenu} {incr i} { + if {[.bar.view entrycget $i -command] eq $targetcmd} { + .bar.view delete $i + break + } + } + set viewdata($curview) {} + showview 0 +} + +proc saveview {} { + global curview viewdata + global displayorder parentlist childlist rowidlist rowoffsets + global rowrangelist commitlisted + +} + +proc showview {n} { + global curview viewdata viewfiles + global displayorder parentlist childlist rowidlist rowoffsets + global colormap rowtextx commitrow + global numcommits rowrangelist commitlisted idrowranges + global selectedline currentid canv canvy0 + global matchinglines treediffs + global parsed_args + + if {$n == $curview} return + set selid {} + if {[info exists selectedline]} { + set selid $currentid + set y [yc $selectedline] + set ymax [lindex [$canv cget -scrollregion] 3] + set span [$canv yview] + set ytop [expr {[lindex $span 0] * $ymax}] + set ybot [expr {[lindex $span 1] * $ymax}] + if {$ytop < $y && $y < $ybot} { + set yscreen [expr {$y - $ytop}] + } else { + set yscreen [expr {($ybot - $ytop) / 2}] + } + } + unselectline + stopfindproc + if {![info exists viewdata($curview)]} { + set viewdata($curview) \ + [list $displayorder $parentlist $childlist $rowidlist \ + $rowoffsets $rowrangelist $commitlisted] + } + catch {unset matchinglines} + catch {unset treediffs} + clear_display + + set curview $n + .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] + + if {![info exists viewdata($n)]} { + set args $parsed_args + if {$viewfiles($n) ne {}} { + set args [concat $args "--" $viewfiles($n)] + } + getcommits $args + return + } + + set displayorder [lindex $viewdata($n) 0] + set parentlist [lindex $viewdata($n) 1] + set childlist [lindex $viewdata($n) 2] + set rowidlist [lindex $viewdata($n) 3] + set rowoffsets [lindex $viewdata($n) 4] + set rowrangelist [lindex $viewdata($n) 5] + set commitlisted [lindex $viewdata($n) 6] + set numcommits [llength $displayorder] + catch {unset colormap} + catch {unset rowtextx} + catch {unset commitrow} + catch {unset idrowranges} + set curview $n + set row 0 + foreach id $displayorder { + set commitrow($id) $row + incr row + } + setcanvscroll + set yf 0 + set row 0 + if {$selid ne {} && [info exists commitrow($selid)]} { + set row $commitrow($selid) + # try to get the selected row in the same position on the screen + set ymax [lindex [$canv cget -scrollregion] 3] + set ytop [expr {[yc $row] - $yscreen}] + if {$ytop < 0} { + set ytop 0 + } + set yf [expr {$ytop * 1.0 / $ymax}] + } + allcanvs yview moveto $yf + drawvisible + selectline $row 0 +} + proc shortids {ids} { set res {} foreach id $ids { @@ -834,10 +1023,12 @@ proc makeuparrow {oid x y z} { proc initlayout {} { global rowidlist rowoffsets displayorder commitlisted global rowlaidout rowoptim - global idinlist rowchk + global idinlist rowchk rowrangelist idrowranges global commitidx numcommits canvxmax canv global nextcolor global parentlist childlist children + global colormap rowtextx commitrow + global linesegends set commitidx 0 set numcommits 0 @@ -845,6 +1036,7 @@ proc initlayout {} { set commitlisted {} set parentlist {} set childlist {} + set rowrangelist {{}} catch {unset children} set nextcolor 0 set rowidlist {{}} @@ -854,6 +1046,11 @@ proc initlayout {} { set rowlaidout 0 set rowoptim 0 set canvxmax [$canv cget -width] + catch {unset colormap} + catch {unset rowtextx} + catch {unset commitrow} + catch {unset idrowranges} + catch {unset linesegends} } proc setcanvscroll {} { @@ -892,7 +1089,6 @@ proc layoutmore {} { set rowlaidout [layoutrows $row $commitidx 0] set orow [expr {$rowlaidout - $uparrowlen - 1}] if {$orow > $rowoptim} { - checkcrossings $rowoptim $orow optimize_rows $rowoptim 0 $orow set rowoptim $orow } @@ -947,7 +1143,7 @@ proc layoutrows {row endrow last} { global childlist parentlist global idrowranges linesegends global commitidx - global idinlist rowchk + global idinlist rowchk rowrangelist set idlist [lindex $rowidlist $row] set offs [lindex $rowoffsets $row] @@ -1008,6 +1204,7 @@ proc layoutrows {row endrow last} { } if {[info exists idrowranges($id)]} { lappend idrowranges($id) $row + lappend rowrangelist $idrowranges($id) } incr row set offs [ntimes [llength $idlist] 0] @@ -1068,7 +1265,7 @@ proc addextraid {id row} { proc layouttail {} { global rowidlist rowoffsets idinlist commitidx - global idrowranges + global idrowranges rowrangelist set row $commitidx set idlist [lindex $rowidlist $row] @@ -1078,6 +1275,7 @@ proc layouttail {} { addextraid $id $row unset idinlist($id) lappend idrowranges($id) $row + lappend rowrangelist $idrowranges($id) incr row set offs [ntimes $col 0] set idlist [lreplace $idlist $col $col] @@ -1091,6 +1289,7 @@ proc layouttail {} { lset rowoffsets $row 0 makeuparrow $id 0 $row 0 lappend idrowranges($id) $row + lappend rowrangelist $idrowranges($id) incr row lappend rowidlist {} lappend rowoffsets {} @@ -1107,7 +1306,7 @@ proc insert_pad {row col npad} { } proc optimize_rows {row col endrow} { - global rowidlist rowoffsets idrowranges linesegends displayorder + global rowidlist rowoffsets idrowranges displayorder for {} {$row < $endrow} {incr row} { set idlist [lindex $rowidlist $row] @@ -1240,13 +1439,33 @@ proc linewidth {id} { return $wid } +proc rowranges {id} { + global idrowranges commitrow numcommits rowrangelist + + set ranges {} + if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} { + set ranges [lindex $rowrangelist $commitrow($id)] + } elseif {[info exists idrowranges($id)]} { + set ranges $idrowranges($id) + } + return $ranges +} + proc drawlineseg {id i} { - global rowoffsets rowidlist idrowranges + global rowoffsets rowidlist global displayorder global canv colormap linespc + global numcommits commitrow - set startrow [lindex $idrowranges($id) [expr {2 * $i}]] - set row [lindex $idrowranges($id) [expr {2 * $i + 1}]] + set ranges [rowranges $id] + set downarrow 1 + if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} { + set downarrow [expr {$i < [llength $ranges] / 2 - 1}] + } else { + set downarrow 1 + } + set startrow [lindex $ranges [expr {2 * $i}]] + set row [lindex $ranges [expr {2 * $i + 1}]] if {$startrow == $row} return assigncolor $id set coords {} @@ -1290,8 +1509,7 @@ proc drawlineseg {id i} { } } if {[llength $coords] < 4} return - set last [expr {[llength $idrowranges($id)] / 2 - 1}] - if {$i < $last} { + if {$downarrow} { # This line has an arrow at the lower end: check if the arrow is # on a diagonal segment, and if so, work around the Tk 8.4 # refusal to draw arrows on diagonal lines. @@ -1311,7 +1529,7 @@ proc drawlineseg {id i} { } } } - set arrow [expr {2 * ($i > 0) + ($i < $last)}] + set arrow [expr {2 * ($i > 0) + $downarrow}] set arrow [lindex {none first last both} $arrow] set t [$canv create line $coords -width [linewidth $id] \ -fill $colormap($id) -tags lines.$id -arrow $arrow] @@ -1320,7 +1538,7 @@ proc drawlineseg {id i} { } proc drawparentlinks {id row col olds} { - global rowidlist canv colormap idrowranges + global rowidlist canv colormap set row2 [expr {$row + 1}] set x [xc $row $col] @@ -1339,9 +1557,9 @@ proc drawparentlinks {id row col olds} { if {$x2 > $rmx} { set rmx $x2 } - if {[info exists idrowranges($p)] && - $row2 == [lindex $idrowranges($p) 0] && - $row2 < [lindex $idrowranges($p) 1]} { + set ranges [rowranges $p] + if {$ranges ne {} && $row2 == [lindex $ranges 0] + && $row2 < [lindex $ranges 1]} { # drawlineseg will do this one for us continue } @@ -1364,11 +1582,11 @@ proc drawparentlinks {id row col olds} { proc drawlines {id} { global colormap canv - global idrowranges idrangedrawn + global idrangedrawn global childlist iddrawn commitrow rowidlist $canv delete lines.$id - set nr [expr {[llength $idrowranges($id)] / 2}] + set nr [expr {[llength [rowranges $id]] / 2}] for {set i 0} {$i < $nr} {incr i} { if {[info exists idrangedrawn($id,$i)]} { drawlineseg $id $i @@ -1431,14 +1649,13 @@ proc drawcmittext {id row col rmx} { proc drawcmitrow {row} { global displayorder rowidlist - global idrowranges idrangedrawn iddrawn + global idrangedrawn iddrawn global commitinfo commitlisted parentlist numcommits if {$row >= $numcommits} return foreach id [lindex $rowidlist $row] { - if {![info exists idrowranges($id)]} continue set i -1 - foreach {s e} $idrowranges($id) { + foreach {s e} [rowranges $id] { incr i if {$row < $s} continue if {$e eq {}} break @@ -1507,10 +1724,50 @@ proc clear_display {} { catch {unset idrangedrawn} } +proc findcrossings {id} { + global rowidlist parentlist numcommits rowoffsets displayorder + + set cross {} + set ccross {} + foreach {s e} [rowranges $id] { + if {$e >= $numcommits} { + 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 olds [lindex $parentlist $row] + set kid [lindex $displayorder $row] + set kidx [lsearch -exact [lindex $rowidlist $row] $kid] + if {$kidx < 0} continue + set nextrow [lindex $rowidlist [expr {$row + 1}]] + foreach p $olds { + set px [lsearch -exact $nextrow $p] + if {$px < 0} continue + if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} { + if {[lsearch -exact $ccross $p] >= 0} continue + if {$x == $px + ($kidx < $px? -1: 1)} { + lappend ccross $p + } elseif {[lsearch -exact $cross $p] < 0} { + lappend cross $p + } + } + } + set inc [lindex $rowoffsets $row $x] + if {$inc eq {}} break + incr x $inc + } + } + return [concat $ccross {{}} $cross] +} + proc assigncolor {id} { global colormap colors nextcolor global commitrow parentlist children childlist - global cornercrossings crossings if {[info exists colormap($id)]} return set ncolors [llength $colors] @@ -1530,32 +1787,22 @@ proc assigncolor {id} { } } set badcolors {} - if {[info exists cornercrossings($id)]} { - foreach x $cornercrossings($id) { - if {[info exists colormap($x)] - && [lsearch -exact $badcolors $colormap($x)] < 0} { - lappend badcolors $colormap($x) - } + set origbad {} + foreach x [findcrossings $id] { + if {$x eq {}} { + # delimiter between corner crossings and other crossings + if {[llength $badcolors] >= $ncolors - 1} break + set origbad $badcolors } - if {[llength $badcolors] >= $ncolors} { - set badcolors {} + if {[info exists colormap($x)] + && [lsearch -exact $badcolors $colormap($x)] < 0} { + lappend badcolors $colormap($x) } } - set origbad $badcolors - if {[llength $badcolors] < $ncolors - 1} { - if {[info exists crossings($id)]} { - foreach x $crossings($id) { - if {[info exists colormap($x)] - && [lsearch -exact $badcolors $colormap($x)] < 0} { - lappend badcolors $colormap($x) - } - } - if {[llength $badcolors] >= $ncolors} { - set badcolors $origbad - } - } - set origbad $badcolors + if {[llength $badcolors] >= $ncolors} { + set badcolors $origbad } + set origbad $badcolors if {[llength $badcolors] < $ncolors - 1} { foreach child $kids { if {[info exists colormap($child)] @@ -1659,55 +1906,6 @@ proc drawtags {id x xt y1} { return $xt } -proc checkcrossings {row endrow} { - global displayorder parentlist rowidlist - - for {} {$row < $endrow} {incr row} { - set id [lindex $displayorder $row] - set i [lsearch -exact [lindex $rowidlist $row] $id] - if {$i < 0} continue - set idlist [lindex $rowidlist [expr {$row+1}]] - foreach p [lindex $parentlist $row] { - set j [lsearch -exact $idlist $p] - if {$j > 0} { - if {$j < $i - 1} { - notecrossings $row $p $j $i [expr {$j+1}] - } elseif {$j > $i + 1} { - notecrossings $row $p $i $j [expr {$j-1}] - } - } - } - } -} - -proc notecrossings {row id lo hi corner} { - global rowidlist crossings cornercrossings - - for {set i $lo} {[incr i] < $hi} {} { - set p [lindex [lindex $rowidlist $row] $i] - if {$p == {}} continue - if {$i == $corner} { - if {![info exists cornercrossings($id)] - || [lsearch -exact $cornercrossings($id) $p] < 0} { - lappend cornercrossings($id) $p - } - if {![info exists cornercrossings($p)] - || [lsearch -exact $cornercrossings($p) $id] < 0} { - lappend cornercrossings($p) $id - } - } else { - if {![info exists crossings($id)] - || [lsearch -exact $crossings($id) $p] < 0} { - lappend crossings($id) $p - } - if {![info exists crossings($p)] - || [lsearch -exact $crossings($p) $id] < 0} { - lappend crossings($p) $id - } - } - } -} - proc xcoord {i level ln} { global canvx0 xspc1 xspc2 @@ -1763,7 +1961,7 @@ proc drawrest {} { showstuff $commitidx set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}] - puts "overall $drawmsecs ms for $numcommits commits" + #puts "overall $drawmsecs ms for $numcommits commits" } proc findmatches {f} { @@ -2433,9 +2631,10 @@ proc selnextline {dir} { } proc unselectline {} { - global selectedline + global selectedline currentid catch {unset selectedline} + catch {unset currentid} allcanvs delete secsel } @@ -2948,12 +3147,13 @@ proc linehover {} { } proc clickisonarrow {id y} { - global lthickness idrowranges + global lthickness + set ranges [rowranges $id] set thresh [expr {2 * $lthickness + 6}] - set n [expr {[llength $idrowranges($id)] - 1}] + set n [expr {[llength $ranges] - 1}] for {set i 1} {$i < $n} {incr i} { - set row [lindex $idrowranges($id) $i] + set row [lindex $ranges $i] if {abs([yc $row] - $y) < $thresh} { return $i } @@ -2962,11 +3162,11 @@ proc clickisonarrow {id y} { } proc arrowjump {id n y} { - global idrowranges canv + global canv # 1 <-> 2, 3 <-> 4, etc... set n [expr {(($n - 1) ^ 1) + 1}] - set row [lindex $idrowranges($id) $n] + set row [lindex [rowranges $id] $n] set yt [yc $row] set ymax [lindex [$canv cget -scrollregion] 3] if {$ymax eq {} || $ymax <= 0} return @@ -3819,10 +4019,26 @@ set historyindex 0 set optim_delay 16 +set nextviewnum 1 +set curview 0 +set viewfiles(0) {} + set stopped 0 set stuffsaved 0 set patchnum 0 setcoords makewindow $revtreeargs readrefs -getcommits $revtreeargs +parse_args $revtreeargs +set args $parsed_args +if {$cmdline_files ne {}} { + # create a view for the files/dirs specified on the command line + set curview 1 + set nextviewnum 2 + set viewname(1) "Command line" + set viewfiles(1) $cmdline_files + .bar.view add command -label $viewname(1) -command {showview 1} + .bar.view entryconf 2 -state normal + set args [concat $args "--" $cmdline_files] +} +getcommits $args From d94f8cd6adc746b850aad421e30d5f90f7f11ee4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 6 Apr 2006 10:18:23 +1000 Subject: [PATCH 002/212] gitk: Make File->Update work properly again If a view is selected it will now just update that view. Also fixed a few other things - if you switch away from a view while gitk is still reading it in, then switch back, gitk will re-read it from scratch. We now re-read the references when switching views. If something was selected before a view change, and we need to read in the new view, we now select the previously-selected commit when we come across it. Fixed a bug in setting of rowrangelist plus a couple of other minor things. Signed-off-by: Paul Mackerras --- gitk | 59 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/gitk b/gitk index 961b582a4..c6649a56d 100755 --- a/gitk +++ b/gitk @@ -22,8 +22,8 @@ proc parse_args {rargs} { set parsed_args {} set cmdline_files {} if {[catch { - set parse_args [concat --default HEAD $rargs] - set args [split [eval exec git-rev-parse $parse_args] "\n"] + set args [concat --default HEAD $rargs] + set args [split [eval exec git-rev-parse $args] "\n"] set i 0 foreach arg $args { if {![regexp {^[0-9a-f]{40}$} $arg]} { @@ -217,17 +217,13 @@ proc readcommit {id} { } proc updatecommits {} { - global parsed_args + global viewdata curview revtreeargs - unselectline - stopfindproc - foreach v {matchinglines treediffs currentid} { - global $v - catch {unset $v} - } - clear_display - readrefs - getcommits $parsed_args + set n $curview + set curview -1 + catch {unset viewdata($n)} + parse_args $revtreeargs + showview $n } proc parsecommit {id contents listed} { @@ -354,7 +350,7 @@ proc error_popup msg { tkwait window $w } -proc makewindow {rargs} { +proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist textfont global findtype findtypemenu findloc findstring fstring geometry global entries sha1entry sha1string sha1but @@ -817,13 +813,6 @@ proc delview {} { showview 0 } -proc saveview {} { - global curview viewdata - global displayorder parentlist childlist rowidlist rowoffsets - global rowrangelist commitlisted - -} - proc showview {n} { global curview viewdata viewfiles global displayorder parentlist childlist rowidlist rowoffsets @@ -832,6 +821,7 @@ proc showview {n} { global selectedline currentid canv canvy0 global matchinglines treediffs global parsed_args + global pending_select phase if {$n == $curview} return set selid {} @@ -850,7 +840,7 @@ proc showview {n} { } unselectline stopfindproc - if {![info exists viewdata($curview)]} { + if {$curview >= 0 && $phase eq {} && ![info exists viewdata($curview)]} { set viewdata($curview) \ [list $displayorder $parentlist $childlist $rowidlist \ $rowoffsets $rowrangelist $commitlisted] @@ -858,6 +848,7 @@ proc showview {n} { catch {unset matchinglines} catch {unset treediffs} clear_display + readrefs set curview $n .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] @@ -867,6 +858,7 @@ proc showview {n} { if {$viewfiles($n) ne {}} { set args [concat $args "--" $viewfiles($n)] } + set pending_select $selid getcommits $args return } @@ -1036,7 +1028,7 @@ proc initlayout {} { set commitlisted {} set parentlist {} set childlist {} - set rowrangelist {{}} + set rowrangelist {} catch {unset children} set nextcolor 0 set rowidlist {{}} @@ -1099,7 +1091,7 @@ proc layoutmore {} { } proc showstuff {canshow} { - global numcommits + global numcommits commitrow pending_select global linesegends idrowranges idrangedrawn if {$numcommits == 0} { @@ -1113,6 +1105,7 @@ proc showstuff {canshow} { set rows [visiblerows] set r0 [lindex $rows 0] set r1 [lindex $rows 1] + set selrow -1 for {set r $row} {$r < $canshow} {incr r} { if {[info exists linesegends($r)]} { foreach id $linesegends($r) { @@ -1135,6 +1128,11 @@ proc showstuff {canshow} { drawcmitrow $row incr row } + if {[info exists pending_select] && + [info exists commitrow($pending_select)] && + $commitrow($pending_select) < $numcommits} { + selectline $commitrow($pending_select) 1 + } } proc layoutrows {row endrow last} { @@ -1202,10 +1200,12 @@ proc layoutrows {row endrow last} { } else { unset idinlist($id) } + set ranges {} if {[info exists idrowranges($id)]} { lappend idrowranges($id) $row - lappend rowrangelist $idrowranges($id) + set ranges $idrowranges($id) } + lappend rowrangelist $ranges incr row set offs [ntimes [llength $idlist] 0] set l [llength $newolds] @@ -1732,8 +1732,8 @@ proc findcrossings {id} { foreach {s e} [rowranges $id] { if {$e >= $numcommits} { set e [expr {$numcommits - 1}] - if {$e < $s} continue } + if {$e <= $s} continue set x [lsearch -exact [lindex $rowidlist $e] $id] if {$x < 0} { puts "findcrossings: oops, no [shortids $id] in row $e" @@ -2360,7 +2360,7 @@ proc donefilediff {} { } } -proc findcont {id} { +proc findcont {} { global findid treediffs parentlist global ffileline findstartline finddidsel global displayorder numcommits matchinglines findinprogress @@ -2493,8 +2493,9 @@ proc selectline {l isnew} { global canvy0 linespc parentlist childlist global cflist currentid sha1entry global commentend idtags linknum - global mergemax numcommits + global mergemax numcommits pending_select + catch {unset pending_select} $canv delete hover normalline if {$l < 0 || $l >= $numcommits} return @@ -2784,7 +2785,7 @@ proc getmergediffline {mdf id np} { incr nextupdate 100 fileevent $mdf readable {} update - fileevent $mdf readable [list getmergediffline $mdf $id] + fileevent $mdf readable [list getmergediffline $mdf $id $np] } } @@ -4027,7 +4028,7 @@ set stopped 0 set stuffsaved 0 set patchnum 0 setcoords -makewindow $revtreeargs +makewindow readrefs parse_args $revtreeargs set args $parsed_args From fdedbcfb0d71d99f96e08c1903ce8bd5a8f51a39 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 6 Apr 2006 21:22:52 +1000 Subject: [PATCH 003/212] gitk: Fix various bugs in the view support - don't re-read refs when switching views, it's too slow; just do it if the user did File->Update - make the view menu use the uifont - if we have a graph line selected, unselect it before changing the view - if a row is selected and appears in the new view, but we have to read in the new view, select that row when we come across it - if no row was previously selected, or if we don't find the previously selected row in the new view, select the first row Signed-off-by: Paul Mackerras --- gitk | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/gitk b/gitk index cfd0a3a69..093213f24 100755 --- a/gitk +++ b/gitk @@ -223,6 +223,7 @@ proc updatecommits {} { set curview -1 catch {unset viewdata($n)} parse_args $revtreeargs + readrefs showview $n } @@ -351,7 +352,8 @@ proc error_popup msg { } proc makewindow {} { - global canv canv2 canv3 linespc charspc ctext cflist textfont mainfont uifont + global canv canv2 canv3 linespc charspc ctext cflist + global textfont mainfont uifont global findtype findtypemenu findloc findstring fstring geometry global entries sha1entry sha1string sha1but global maincursor textcursor curtextcursor @@ -369,7 +371,7 @@ proc makewindow {} { .bar add cascade -label "Edit" -menu .bar.edit .bar.edit add command -label "Preferences" -command doprefs .bar.edit configure -font $uifont - menu .bar.view + menu .bar.view -font $uifont .bar add cascade -label "View" -menu .bar.view .bar.view add command -label "New view..." -command newview .bar.view add command -label "Delete view" -command delview -state disabled @@ -907,6 +909,7 @@ proc showview {n} { } } unselectline + normalline stopfindproc if {$curview >= 0 && $phase eq {} && ![info exists viewdata($curview)]} { set viewdata($curview) \ @@ -916,7 +919,6 @@ proc showview {n} { catch {unset matchinglines} catch {unset treediffs} clear_display - readrefs set curview $n .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] @@ -1159,7 +1161,7 @@ proc layoutmore {} { } proc showstuff {canshow} { - global numcommits commitrow pending_select + global numcommits commitrow pending_select selectedline global linesegends idrowranges idrangedrawn if {$numcommits == 0} { @@ -1201,6 +1203,9 @@ proc showstuff {canshow} { $commitrow($pending_select) < $numcommits} { selectline $commitrow($pending_select) 1 } + if {![info exists selectedline] && ![info exists pending_select]} { + selectline 0 1 + } } proc layoutrows {row endrow last} { @@ -1989,7 +1994,7 @@ proc xcoord {i level ln} { proc finishcommits {} { global commitidx phase global canv mainfont ctext maincursor textcursor - global findinprogress + global findinprogress pending_select if {$commitidx > 0} { drawrest @@ -2003,6 +2008,7 @@ proc finishcommits {} { settextcursor $textcursor } set phase {} + catch {unset pending_select} } # Don't change the text pane cursor if it is currently the hand cursor, @@ -2021,12 +2027,16 @@ proc drawrest {} { global startmsecs global canvy0 numcommits linespc global rowlaidout commitidx + global pending_select set row $rowlaidout layoutrows $rowlaidout $commitidx 1 layouttail optimize_rows $row 0 $commitidx showstuff $commitidx + if {[info exists pending_select]} { + selectline 0 1 + } set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}] #puts "overall $drawmsecs ms for $numcommits commits" From 22626ef4fd5a0b2cf1fc8e90bbb23f8ff0ed43ea Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 17 Apr 2006 09:56:02 +1000 Subject: [PATCH 004/212] gitk: Don't reread git-rev-list output from scratch on view switch Previously, if we switched away from a view before we had finished reading the git-rev-list output for it and laying out the graph, we would discard the partially-laid-out graph and reread it from scratch if we switched back to the view. With this, we preserve the state of the partially-laid-out graph in viewdata($view) and restore it if we switch back. The pipe to git-rev-list remains open but we just don't read from it any more until we switch back to that view. This also makes linesegends a list rather than an array, which turns out to be slightly faster, as well as being easier to save and restore. The `update' menu item now kills the git-rev-list process if there is one still running when we do the update. Signed-off-by: Paul Mackerras --- gitk | 157 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 116 insertions(+), 41 deletions(-) diff --git a/gitk b/gitk index 093213f24..85f426ab2 100755 --- a/gitk +++ b/gitk @@ -80,6 +80,18 @@ proc start_rev_list {rlargs} { settextcursor watch } +proc stop_rev_list {} { + global commfd + + if {![info exists commfd]} return + catch { + set pid [pid $commfd] + exec kill $pid + } + catch {close $commfd} + unset commfd +} + proc getcommits {rargs} { global phase canv mainfont @@ -173,6 +185,7 @@ proc getcommitlines {commfd} { lappend parentlist $olds if {[info exists children($id)]} { lappend childlist $children($id) + unset children($id) } else { lappend childlist {} } @@ -217,8 +230,12 @@ proc readcommit {id} { } proc updatecommits {} { - global viewdata curview revtreeargs + global viewdata curview revtreeargs phase + if {$phase ne {}} { + stop_rev_list + set phase {} + } set n $curview set curview -1 catch {unset viewdata($n)} @@ -883,6 +900,25 @@ proc delview {} { showview 0 } +proc flatten {var} { + global $var + + set ret {} + foreach i [array names $var] { + lappend ret $i [set $var\($i\)] + } + return $ret +} + +proc unflatten {var l} { + global $var + + catch {unset $var} + foreach {i v} $l { + set $var\($i\) $v + } +} + proc showview {n} { global curview viewdata viewfiles global displayorder parentlist childlist rowidlist rowoffsets @@ -892,6 +928,8 @@ proc showview {n} { global matchinglines treediffs global parsed_args global pending_select phase + global commitidx rowlaidout rowoptim linesegends leftover + global commfd nextupdate if {$n == $curview} return set selid {} @@ -911,10 +949,21 @@ proc showview {n} { unselectline normalline stopfindproc - if {$curview >= 0 && $phase eq {} && ![info exists viewdata($curview)]} { - set viewdata($curview) \ - [list $displayorder $parentlist $childlist $rowidlist \ - $rowoffsets $rowrangelist $commitlisted] + if {$curview >= 0} { + if {$phase ne {}} { + set viewdata($curview) \ + [list $phase $displayorder $parentlist $childlist $rowidlist \ + $rowoffsets $rowrangelist $commitlisted \ + [flatten children] [flatten idrowranges] \ + [flatten idinlist] \ + $commitidx $rowlaidout $rowoptim $numcommits \ + $linesegends $leftover $commfd] + fileevent $commfd readable {} + } elseif {![info exists viewdata($curview)]} { + set viewdata($curview) \ + [list {} $displayorder $parentlist $childlist $rowidlist \ + $rowoffsets $rowrangelist $commitlisted] + } } catch {unset matchinglines} catch {unset treediffs} @@ -933,18 +982,37 @@ proc showview {n} { return } - set displayorder [lindex $viewdata($n) 0] - set parentlist [lindex $viewdata($n) 1] - set childlist [lindex $viewdata($n) 2] - set rowidlist [lindex $viewdata($n) 3] - set rowoffsets [lindex $viewdata($n) 4] - set rowrangelist [lindex $viewdata($n) 5] - set commitlisted [lindex $viewdata($n) 6] - set numcommits [llength $displayorder] + set v $viewdata($n) + set phase [lindex $v 0] + set displayorder [lindex $v 1] + set parentlist [lindex $v 2] + set childlist [lindex $v 3] + set rowidlist [lindex $v 4] + set rowoffsets [lindex $v 5] + set rowrangelist [lindex $v 6] + set commitlisted [lindex $v 7] + if {$phase eq {}} { + set numcommits [llength $displayorder] + catch {unset idrowranges} + catch {unset children} + } else { + unflatten children [lindex $v 8] + unflatten idrowranges [lindex $v 9] + unflatten idinlist [lindex $v 10] + set commitidx [lindex $v 11] + set rowlaidout [lindex $v 12] + set rowoptim [lindex $v 13] + set numcommits [lindex $v 14] + set linesegends [lindex $v 15] + set leftover [lindex $v 16] + set commfd [lindex $v 17] + fileevent $commfd readable [list getcommitlines $commfd] + set nextupdate [expr {[clock clicks -milliseconds] + 100}] + } + catch {unset colormap} catch {unset rowtextx} catch {unset commitrow} - catch {unset idrowranges} set curview $n set row 0 foreach id $displayorder { @@ -1004,20 +1072,21 @@ proc ntimes {n o} { } proc usedinrange {id l1 l2} { - global children commitrow + global children commitrow childlist if {[info exists commitrow($id)]} { set r $commitrow($id) if {$l1 <= $r && $r <= $l2} { return [expr {$r - $l1 + 1}] } + set kids [lindex $childlist $r] + } else { + set kids $children($id) } - foreach c $children($id) { - if {[info exists commitrow($c)]} { - set r $commitrow($c) - if {$l1 <= $r && $r <= $l2} { - return [expr {$r - $l1 + 1}] - } + foreach c $kids { + set r $commitrow($c) + if {$l1 <= $r && $r <= $l2} { + return [expr {$r - $l1 + 1}] } } return 0 @@ -1112,7 +1181,7 @@ proc initlayout {} { catch {unset rowtextx} catch {unset commitrow} catch {unset idrowranges} - catch {unset linesegends} + set linesegends {} } proc setcanvscroll {} { @@ -1177,16 +1246,14 @@ proc showstuff {canshow} { set r1 [lindex $rows 1] set selrow -1 for {set r $row} {$r < $canshow} {incr r} { - if {[info exists linesegends($r)]} { - foreach id $linesegends($r) { - set i -1 - foreach {s e} $idrowranges($id) { - incr i - if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0 - && ![info exists idrangedrawn($id,$i)]} { - drawlineseg $id $i - set idrangedrawn($id,$i) 1 - } + foreach id [lindex $linesegends [expr {$r+1}]] { + set i -1 + foreach {s e} [rowranges $id] { + incr i + if {$e ne {} && $e < $numcommits && $s <= $r1 && $e >= $r0 + && ![info exists idrangedrawn($id,$i)]} { + drawlineseg $id $i + set idrangedrawn($id,$i) 1 } } } @@ -1229,6 +1296,7 @@ proc layoutrows {row endrow last} { lappend oldolds $p } } + set lse {} set nev [expr {[llength $idlist] + [llength $newolds] + [llength $oldolds] - $maxwidth + 1}] if {$nev > 0} { @@ -1244,7 +1312,7 @@ proc layoutrows {row endrow last} { set offs [incrange $offs $x 1] set idinlist($i) 0 set rm1 [expr {$row - 1}] - lappend linesegends($rm1) $i + lappend lse $i lappend idrowranges($i) $rm1 if {[incr nev -1] <= 0} break continue @@ -1255,6 +1323,7 @@ proc layoutrows {row endrow last} { lset rowidlist $row $idlist lset rowoffsets $row $offs } + lappend linesegends $lse set col [lsearch -exact $idlist $id] if {$col < 0} { set col [llength $idlist] @@ -1275,8 +1344,9 @@ proc layoutrows {row endrow last} { } set ranges {} if {[info exists idrowranges($id)]} { - lappend idrowranges($id) $row set ranges $idrowranges($id) + lappend ranges $row + unset idrowranges($id) } lappend rowrangelist $ranges incr row @@ -1331,6 +1401,7 @@ proc addextraid {id row} { } if {[info exists children($id)]} { lappend childlist $children($id) + unset children($id) } else { lappend childlist {} } @@ -1349,6 +1420,7 @@ proc layouttail {} { unset idinlist($id) lappend idrowranges($id) $row lappend rowrangelist $idrowranges($id) + unset idrowranges($id) incr row set offs [ntimes $col 0] set idlist [lreplace $idlist $col $col] @@ -1363,6 +1435,7 @@ proc layouttail {} { makeuparrow $id 0 $row 0 lappend idrowranges($id) $row lappend rowrangelist $idrowranges($id) + unset idrowranges($id) incr row lappend rowidlist {} lappend rowoffsets {} @@ -1398,8 +1471,8 @@ proc optimize_rows {row col endrow} { set z0 [lindex $rowoffsets $y0 $x0] if {$z0 eq {}} { set id [lindex $idlist $col] - if {[info exists idrowranges($id)] && - $y0 > [lindex $idrowranges($id) 0]} { + set ranges [rowranges $id] + if {$ranges ne {} && $y0 > [lindex $ranges 0]} { set isarrow 1 } } @@ -1457,8 +1530,8 @@ proc optimize_rows {row col endrow} { if {$o eq {}} { # check if this is the link to the first child set id [lindex $idlist $col] - if {[info exists idrowranges($id)] && - $row == [lindex $idrowranges($id) 0]} { + 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] @@ -1513,10 +1586,11 @@ proc linewidth {id} { } proc rowranges {id} { - global idrowranges commitrow numcommits rowrangelist + global phase idrowranges commitrow rowlaidout rowrangelist set ranges {} - if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} { + if {$phase eq {} || + ([info exists commitrow($id)] && $commitrow($id) < $rowlaidout)} { set ranges [lindex $rowrangelist $commitrow($id)] } elseif {[info exists idrowranges($id)]} { set ranges $idrowranges($id) @@ -1727,6 +1801,7 @@ proc drawcmitrow {row} { if {$row >= $numcommits} return foreach id [lindex $rowidlist $row] { + if {$id eq {}} continue set i -1 foreach {s e} [rowranges $id] { incr i @@ -3142,7 +3217,7 @@ proc incrfont {inc} { foreach e $entries { $e conf -font $mainfont } - if {$phase == "getcommits"} { + if {$phase eq "getcommits"} { $canv itemconf textitems -font $mainfont } redisplay From 2516dae2f63b6e6c4f7dbe2eb1322ba32da092c5 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 21 Apr 2006 10:35:31 +1000 Subject: [PATCH 005/212] gitk: Remember the view in the history list When moving backwards or forwards through the history list, this automatically switches the view so that each point that we jump to is shown in the same view that it was originally displayed in. Signed-off-by: Paul Mackerras --- gitk | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/gitk b/gitk index 85f426ab2..305aa2ef0 100755 --- a/gitk +++ b/gitk @@ -959,7 +959,8 @@ proc showview {n} { $commitidx $rowlaidout $rowoptim $numcommits \ $linesegends $leftover $commfd] fileevent $commfd readable {} - } elseif {![info exists viewdata($curview)]} { + } elseif {![info exists viewdata($curview)] + || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ [list {} $displayorder $parentlist $childlist $rowidlist \ $rowoffsets $rowrangelist $commitlisted] @@ -1035,6 +1036,14 @@ proc showview {n} { allcanvs yview moveto $yf drawvisible selectline $row 0 + if {$phase eq {}} { + global maincursor textcursor + . config -cursor $maincursor + settextcursor $textcursor + } else { + . config -cursor watch + settextcursor watch + } } proc shortids {ids} { @@ -2301,7 +2310,7 @@ proc stopfindproc {{done 0}} { } if {[info exists findinprogress]} { unset findinprogress - if {$phase != "incrdraw"} { + if {$phase eq {}} { . config -cursor $maincursor settextcursor $textcursor } @@ -2840,17 +2849,18 @@ proc unselectline {} { } proc addtohistory {cmd} { - global history historyindex + global history historyindex curview + set elt [list $curview $cmd] if {$historyindex > 0 - && [lindex $history [expr {$historyindex - 1}]] == $cmd} { + && [lindex $history [expr {$historyindex - 1}]] == $elt} { return } if {$historyindex < [llength $history]} { - set history [lreplace $history $historyindex end $cmd] + set history [lreplace $history $historyindex end $elt] } else { - lappend history $cmd + lappend history $elt } incr historyindex if {$historyindex > 1} { @@ -2861,13 +2871,23 @@ proc addtohistory {cmd} { .ctop.top.bar.rightbut conf -state disabled } +proc godo {elt} { + global curview + + set view [lindex $elt 0] + set cmd [lindex $elt 1] + if {$curview != $view} { + showview $view + } + eval $cmd +} + proc goback {} { global history historyindex if {$historyindex > 1} { incr historyindex -1 - set cmd [lindex $history [expr {$historyindex - 1}]] - eval $cmd + godo [lindex $history [expr {$historyindex - 1}]] .ctop.top.bar.rightbut conf -state normal } if {$historyindex <= 1} { @@ -2881,7 +2901,7 @@ proc goforw {} { if {$historyindex < [llength $history]} { set cmd [lindex $history $historyindex] incr historyindex - eval $cmd + godo $cmd .ctop.top.bar.leftbut conf -state normal } if {$historyindex >= [llength $history]} { From a8aaf19c419a30d335f0640ab644112ecda6dd12 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 23 Apr 2006 22:45:55 +1000 Subject: [PATCH 006/212] gitk: Use git-rev-parse only to identify file/dir names on cmd line This uses git-rev-parse --no-revs --no-flags to give us just the file and directory names on the command line, so that we can create the "Command line" view if any were specified. All other arguments just get passed to git-rev-list (without a pass through git-rev-parse). Signed-off-by: Paul Mackerras --- gitk | 73 +++++++++++++++++++----------------------------------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/gitk b/gitk index 305aa2ef0..6cefc8721 100755 --- a/gitk +++ b/gitk @@ -16,56 +16,26 @@ proc gitdir {} { } } -proc parse_args {rargs} { - global parsed_args cmdline_files - - set parsed_args {} - set cmdline_files {} - if {[catch { - set args [concat --default HEAD $rargs] - set args [split [eval exec git-rev-parse $args] "\n"] - set i 0 - foreach arg $args { - if {![regexp {^[0-9a-f]{40}$} $arg]} { - if {$arg eq "--"} { - incr i - } - set cmdline_files [lrange $args $i end] - break - } - lappend parsed_args $arg - incr i - } - }]} { - # if git-rev-parse failed for some reason... - set i [lsearch -exact $rargs "--"] - if {$i >= 0} { - set cmdline_files [lrange $rargs [expr {$i+1}] end] - set rargs [lrange $rargs 0 [expr {$i-1}]] - } - if {$rargs == {}} { - set parsed_args HEAD - } else { - set parsed_args $rargs - } - } -} - -proc start_rev_list {rlargs} { +proc start_rev_list {} { global startmsecs nextupdate ncmupdate global commfd leftover tclencoding datemode + global revtreeargs curview viewfiles set startmsecs [clock clicks -milliseconds] set nextupdate [expr {$startmsecs + 100}] set ncmupdate 1 initlayout + set args $revtreeargs + if {$viewfiles($curview) ne {}} { + set args [concat $args "--" $viewfiles($curview)] + } set order "--topo-order" if {$datemode} { set order "--date-order" } if {[catch { set commfd [open [concat | git-rev-list --header $order \ - --parents --boundary $rlargs] r] + --parents --boundary --default HEAD $args] r] } err]} { puts stderr "Error executing git-rev-list: $err" exit 1 @@ -92,11 +62,11 @@ proc stop_rev_list {} { unset commfd } -proc getcommits {rargs} { +proc getcommits {} { global phase canv mainfont set phase getcommits - start_rev_list $rargs + start_rev_list $canv delete all $canv create text 3 3 -anchor nw -text "Reading commits..." \ -font $mainfont -tags textitems @@ -239,7 +209,6 @@ proc updatecommits {} { set n $curview set curview -1 catch {unset viewdata($n)} - parse_args $revtreeargs readrefs showview $n } @@ -926,7 +895,6 @@ proc showview {n} { global numcommits rowrangelist commitlisted idrowranges global selectedline currentid canv canvy0 global matchinglines treediffs - global parsed_args global pending_select phase global commitidx rowlaidout rowoptim linesegends leftover global commfd nextupdate @@ -974,12 +942,8 @@ proc showview {n} { .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] if {![info exists viewdata($n)]} { - set args $parsed_args - if {$viewfiles($n) ne {}} { - set args [concat $args "--" $viewfiles($n)] - } set pending_select $selid - getcommits $args + getcommits return } @@ -4252,8 +4216,18 @@ set patchnum 0 setcoords makewindow readrefs -parse_args $revtreeargs -set args $parsed_args + +set cmdline_files {} +catch { + set fileargs [eval exec git-rev-parse --no-revs --no-flags $revtreeargs] + set cmdline_files [split $fileargs "\n"] + set n [llength $cmdline_files] + set revtreeargs [lrange $revtreeargs 0 end-$n] +} +if {[lindex $revtreeargs end] eq "--"} { + set revtreeargs [lrange $revtreeargs 0 end-1] +} + if {$cmdline_files ne {}} { # create a view for the files/dirs specified on the command line set curview 1 @@ -4262,6 +4236,5 @@ if {$cmdline_files ne {}} { set viewfiles(1) $cmdline_files .bar.view add command -label $viewname(1) -command {showview 1} .bar.view entryconf 2 -state normal - set args [concat $args "--" $cmdline_files] } -getcommits $args +getcommits From 08abe669c05521499149dbf84fedefb04a8fa34d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 24 Apr 2006 23:07:47 -0400 Subject: [PATCH 007/212] split the diff-delta interface This patch splits the diff-delta interface into index creation and delta generation. A wrapper is provided to preserve the diff-delta() call. This will allow for an optimization in pack-objects.c where the source object could be fixed and a full window of objects tentatively tried against that same source object without recomputing the source index each time. This patch only restructure things, plus a couple cleanups for good measure. There is no performance change yet. Signed-off-by: Nicolas Pitre --- delta.h | 75 +++++++++++++++++++--- diff-delta.c | 168 +++++++++++++++++++++++++------------------------- patch-delta.c | 4 +- 3 files changed, 155 insertions(+), 92 deletions(-) diff --git a/delta.h b/delta.h index 9464f3e9b..727ae30e9 100644 --- a/delta.h +++ b/delta.h @@ -1,12 +1,73 @@ #ifndef DELTA_H #define DELTA_H -/* handling of delta buffers */ -extern void *diff_delta(void *from_buf, unsigned long from_size, - void *to_buf, unsigned long to_size, - unsigned long *delta_size, unsigned long max_size); -extern void *patch_delta(void *src_buf, unsigned long src_size, - void *delta_buf, unsigned long delta_size, +/* opaque object for delta index */ +struct delta_index; + +/* + * create_delta_index: compute index data from given buffer + * + * This returns a pointer to a struct delta_index that should be passed to + * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer + * is returned on failure. The given buffer must not be freed nor altered + * before free_delta_index() is called. The returned pointer must be freed + * using free_delta_index(). + */ +extern struct delta_index * +create_delta_index(const void *buf, unsigned long bufsize); + +/* + * free_delta_index: free the index created by create_delta_index() + */ +extern void free_delta_index(struct delta_index *index); + +/* + * create_delta: create a delta from given index for the given buffer + * + * This function may be called multiple times with different buffers using + * the same delta_index pointer. If max_delta_size is non-zero and the + * resulting delta is to be larger than max_delta_size then NULL is returned. + * On success, a non-NULL pointer to the buffer with the delta data is + * returned and *delta_size is updated with its size. The returned buffer + * must be freed by the caller. + */ +extern void * +create_delta(const struct delta_index *index, + const void *buf, unsigned long bufsize, + unsigned long *delta_size, unsigned long max_delta_size); + +/* + * diff_delta: create a delta from source buffer to target buffer + * + * If max_delta_size is non-zero and the resulting delta is to be larger + * than max_delta_size then NULL is returned. On success, a non-NULL + * pointer to the buffer with the delta data is returned and *delta_size is + * updated with its size. The returned buffer must be freed by the caller. + */ +static inline void * +diff_delta(const void *src_buf, unsigned long src_bufsize, + const void *trg_buf, unsigned long trg_bufsize, + unsigned long *delta_size, unsigned long max_delta_size) +{ + struct delta_index *index = create_delta_index(src_buf, src_bufsize); + if (index) { + void *delta = create_delta(index, trg_buf, trg_bufsize, + delta_size, max_delta_size); + free_delta_index(index); + return delta; + } + return NULL; +} + +/* + * patch_delta: recreate target buffer given source buffer and delta data + * + * On success, a non-NULL pointer to the target buffer is returned and + * *trg_bufsize is updated with its size. On failure a NULL pointer is + * returned. The returned buffer must be freed by the caller. + */ +extern void *patch_delta(const void *src_buf, unsigned long src_size, + const void *delta_buf, unsigned long delta_size, unsigned long *dst_size); /* the smallest possible delta size is 4 bytes */ @@ -14,7 +75,7 @@ extern void *patch_delta(void *src_buf, unsigned long src_size, /* * This must be called twice on the delta data buffer, first to get the - * expected reference buffer size, and again to get the result buffer size. + * expected source buffer size, and again to get the target buffer size. */ static inline unsigned long get_delta_hdr_size(const unsigned char **datap, const unsigned char *top) diff --git a/diff-delta.c b/diff-delta.c index 1188b31cd..fdedf94cc 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -27,53 +27,70 @@ /* block size: min = 16, max = 64k, power of 2 */ #define BLK_SIZE 16 -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +/* maximum hash entry list for the same hash bucket */ +#define HASH_LIMIT 64 #define GR_PRIME 0x9e370001 #define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift)) -struct index { +struct index_entry { const unsigned char *ptr; unsigned int val; - struct index *next; + struct index_entry *next; }; -static struct index ** delta_index(const unsigned char *buf, - unsigned long bufsize, - unsigned long trg_bufsize, - unsigned int *hash_shift) +struct delta_index { + const void *src_buf; + unsigned long src_size; + unsigned int hash_shift; + struct index_entry *hash[0]; +}; + +struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) { - unsigned int i, hsize, hshift, hlimit, entries, *hash_count; - const unsigned char *data; - struct index *entry, **hash; + unsigned int i, hsize, hshift, entries, *hash_count; + const unsigned char *data, *buffer = buf; + struct delta_index *index; + struct index_entry *entry, **hash; void *mem; + if (!buf || !bufsize) + return NULL; + /* determine index hash size */ entries = bufsize / BLK_SIZE; hsize = entries / 4; for (i = 4; (1 << i) < hsize && i < 31; i++); hsize = 1 << i; hshift = 32 - i; - *hash_shift = hshift; /* allocate lookup index */ - mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry)); + mem = malloc(sizeof(*index) + + sizeof(*hash) * hsize + + sizeof(*entry) * entries); if (!mem) return NULL; + index = mem; + mem = index + 1; hash = mem; - entry = mem + hsize * sizeof(*hash); + mem = hash + hsize; + entry = mem; + + index->src_buf = buf; + index->src_size = bufsize; + index->hash_shift = hshift; memset(hash, 0, hsize * sizeof(*hash)); /* allocate an array to count hash entries */ hash_count = calloc(hsize, sizeof(*hash_count)); if (!hash_count) { - free(hash); + free(index); return NULL; } /* then populate the index */ - data = buf + entries * BLK_SIZE - BLK_SIZE; - while (data >= buf) { + data = buffer + entries * BLK_SIZE - BLK_SIZE; + while (data >= buffer) { unsigned int val = adler32(0, data, BLK_SIZE); i = HASH(val, hshift); entry->ptr = data; @@ -91,27 +108,18 @@ static struct index ** delta_index(const unsigned char *buf, * bucket that would bring us to O(m*n) computing costs (m and n * corresponding to reference and target buffer sizes). * - * The more the target buffer is large, the more it is important to - * have small entry lists for each hash buckets. With such a limit - * the cost is bounded to something more like O(m+n). - */ - hlimit = (1 << 26) / trg_bufsize; - if (hlimit < 4*BLK_SIZE) - hlimit = 4*BLK_SIZE; - - /* - * Now make sure none of the hash buckets has more entries than + * Make sure none of the hash buckets has more entries than * we're willing to test. Otherwise we cull the entry list * uniformly to still preserve a good repartition across * the reference buffer. */ for (i = 0; i < hsize; i++) { - if (hash_count[i] < hlimit) + if (hash_count[i] < HASH_LIMIT) continue; entry = hash[i]; do { - struct index *keep = entry; - int skip = hash_count[i] / hlimit / 2; + struct index_entry *keep = entry; + int skip = hash_count[i] / HASH_LIMIT / 2; do { entry = entry->next; } while(--skip && entry); @@ -120,7 +128,12 @@ static struct index ** delta_index(const unsigned char *buf, } free(hash_count); - return hash; + return index; +} + +void free_delta_index(struct delta_index *index) +{ + free(index); } /* provide the size of the copy opcode given the block offset and size */ @@ -131,21 +144,17 @@ static struct index ** delta_index(const unsigned char *buf, /* the maximum size for any opcode */ #define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff) -void *diff_delta(void *from_buf, unsigned long from_size, - void *to_buf, unsigned long to_size, - unsigned long *delta_size, - unsigned long max_size) +void * +create_delta(const struct delta_index *index, + const void *trg_buf, unsigned long trg_size, + unsigned long *delta_size, unsigned long max_size) { unsigned int i, outpos, outsize, hash_shift; int inscnt; const unsigned char *ref_data, *ref_top, *data, *top; unsigned char *out; - struct index *entry, **hash; - if (!from_size || !to_size) - return NULL; - hash = delta_index(from_buf, from_size, to_size, &hash_shift); - if (!hash) + if (!trg_buf || !trg_size) return NULL; outpos = 0; @@ -153,60 +162,55 @@ void *diff_delta(void *from_buf, unsigned long from_size, if (max_size && outsize >= max_size) outsize = max_size + MAX_OP_SIZE + 1; out = malloc(outsize); - if (!out) { - free(hash); + if (!out) return NULL; - } - - ref_data = from_buf; - ref_top = from_buf + from_size; - data = to_buf; - top = to_buf + to_size; /* store reference buffer size */ - out[outpos++] = from_size; - from_size >>= 7; - while (from_size) { - out[outpos - 1] |= 0x80; - out[outpos++] = from_size; - from_size >>= 7; + i = index->src_size; + while (i >= 0x80) { + out[outpos++] = i | 0x80; + i >>= 7; } + out[outpos++] = i; /* store target buffer size */ - out[outpos++] = to_size; - to_size >>= 7; - while (to_size) { - out[outpos - 1] |= 0x80; - out[outpos++] = to_size; - to_size >>= 7; + i = trg_size; + while (i >= 0x80) { + out[outpos++] = i | 0x80; + i >>= 7; } + out[outpos++] = i; + ref_data = index->src_buf; + ref_top = ref_data + index->src_size; + data = trg_buf; + top = trg_buf + trg_size; + hash_shift = index->hash_shift; inscnt = 0; while (data < top) { unsigned int moff = 0, msize = 0; - if (data + BLK_SIZE <= top) { - unsigned int val = adler32(0, data, BLK_SIZE); - i = HASH(val, hash_shift); - for (entry = hash[i]; entry; entry = entry->next) { - const unsigned char *ref = entry->ptr; - const unsigned char *src = data; - unsigned int ref_size = ref_top - ref; - if (entry->val != val) - continue; - if (ref_size > top - src) - ref_size = top - src; - if (ref_size > 0x10000) - ref_size = 0x10000; - if (ref_size <= msize) - break; - while (ref_size-- && *src++ == *ref) - ref++; - if (msize < ref - entry->ptr) { - /* this is our best match so far */ - msize = ref - entry->ptr; - moff = entry->ptr - ref_data; - } + struct index_entry *entry; + unsigned int val = adler32(0, data, BLK_SIZE); + i = HASH(val, hash_shift); + for (entry = index->hash[i]; entry; entry = entry->next) { + const unsigned char *ref = entry->ptr; + const unsigned char *src = data; + unsigned int ref_size = ref_top - ref; + if (entry->val != val) + continue; + if (ref_size > top - src) + ref_size = top - src; + if (ref_size > 0x10000) + ref_size = 0x10000; + if (ref_size <= msize) + break; + while (ref_size-- && *src++ == *ref) + ref++; + if (msize < ref - entry->ptr) { + /* this is our best match so far */ + msize = ref - entry->ptr; + moff = entry->ptr - ref_data; } } @@ -271,7 +275,6 @@ void *diff_delta(void *from_buf, unsigned long from_size, out = realloc(out, outsize); if (!out) { free(tmp); - free(hash); return NULL; } } @@ -280,7 +283,6 @@ void *diff_delta(void *from_buf, unsigned long from_size, if (inscnt) out[outpos - inscnt - 1] = inscnt; - free(hash); *delta_size = outpos; return out; } diff --git a/patch-delta.c b/patch-delta.c index d95f0d972..8f318ed8a 100644 --- a/patch-delta.c +++ b/patch-delta.c @@ -13,8 +13,8 @@ #include #include "delta.h" -void *patch_delta(void *src_buf, unsigned long src_size, - void *delta_buf, unsigned long delta_size, +void *patch_delta(const void *src_buf, unsigned long src_size, + const void *delta_buf, unsigned long delta_size, unsigned long *dst_size) { const unsigned char *data, *top; From de791f15a1b01e63f3c870c8497c594c47452fab Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:25 +0200 Subject: [PATCH 008/212] git-repo-config --list support This adds git-repo-config --list (or git-repo-config -l) support, similar to what git-var -l does now (to be phased out so that we have a single sane interface to the config file instead of fragmented and confused API). Signed-off-by: Petr Baudis --- Documentation/git-repo-config.txt | 4 ++++ repo-config.c | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 71f96bdd1..566cfa183 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -15,6 +15,7 @@ SYNOPSIS 'git-repo-config' [type] --get-all name [value_regex] 'git-repo-config' [type] --unset name [value_regex] 'git-repo-config' [type] --unset-all name [value_regex] +'git-repo-config' -l | --list DESCRIPTION ----------- @@ -64,6 +65,9 @@ OPTIONS --unset-all:: Remove all matching lines from .git/config. +-l, --list:: + List all variables set in .git/config. + EXAMPLE ------- diff --git a/repo-config.c b/repo-config.c index c5ebb7668..fa8aba7a1 100644 --- a/repo-config.c +++ b/repo-config.c @@ -2,7 +2,7 @@ #include static const char git_config_set_usage[] = -"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; +"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list"; static char* key = NULL; static char* value = NULL; @@ -12,6 +12,15 @@ static int do_not_match = 0; static int seen = 0; static enum { T_RAW, T_INT, T_BOOL } type = T_RAW; +static int show_all_config(const char *key_, const char *value_) +{ + if (value_) + printf("%s=%s\n", key_, value_); + else + printf("%s\n", key_); + return 0; +} + static int show_config(const char* key_, const char* value_) { if (value_ == NULL) @@ -67,7 +76,7 @@ static int get_value(const char* key_, const char* regex_) } } - i = git_config(show_config); + git_config(show_config); if (value) { printf("%s\n", value); free(value); @@ -99,6 +108,9 @@ int main(int argc, const char **argv) argv++; } + if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) + return git_config(show_all_config); + switch (argc) { case 2: return get_value(argv[1], NULL); From e1cbc46d12a0524fd5e710cbfaf3f178fc3da504 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:30 +0200 Subject: [PATCH 009/212] Deprecate usage of git-var -l for getting config vars list This has been an unfortunate sideway in the git API evolution. We use git-repo-config for all the other .git/config interaction so let's also use git-repo-config -l for the variable listing. Signed-off-by: Petr Baudis --- Documentation/git-var.txt | 3 ++- git-cvsserver.perl | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 379571eef..a5b1a0dba 100644 --- a/Documentation/git-var.txt +++ b/Documentation/git-var.txt @@ -19,7 +19,8 @@ OPTIONS -l:: Cause the logical variables to be listed. In addition, all the variables of the git configuration file .git/config are listed - as well. + as well. (However, the configuration variables listing functionality + is deprecated in favor of `git-repo-config -l`.) EXAMPLE -------- diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 7d3f78e37..0b37d26e0 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -171,11 +171,11 @@ sub req_Root return 0; } - my @gitvars = `git-var -l`; + my @gitvars = `git-repo-config -l`; if ($?) { - print "E problems executing git-var on the server -- this is not a git repository or the PATH is not set correcly.\n"; + print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correcly.\n"; print "E \n"; - print "error 1 - problem executing git-var\n"; + print "error 1 - problem executing git-repo-config\n"; return 0; } foreach my $line ( @gitvars ) From a90a6d249bc4fe29c11ce3291f9ec90d8e5e67f4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Apr 2006 17:12:46 +1000 Subject: [PATCH 010/212] gitk: Implement "permanent" views (stored in ~/.gitk) With this the user can now mark a view as "permanent" and it will appear in the list every time gitk is started (until it is deleted). Also tidied up the view definition window, and changed the view menu to use radiobuttons for the view selections so there is some feedback as to which is the current view. Signed-off-by: Paul Mackerras --- gitk | 61 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/gitk b/gitk index 4ee51223b..6d6a2f32c 100755 --- a/gitk +++ b/gitk @@ -362,7 +362,8 @@ proc makewindow {} { .bar.view add command -label "New view..." -command newview .bar.view add command -label "Delete view" -command delview -state disabled .bar.view add separator - .bar.view add command -label "All files" -command {showview 0} + .bar.view add radiobutton -label "All files" -command {showview 0} \ + -variable selectedview -value 0 menu .bar.help .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About gitk" -command about @@ -632,6 +633,7 @@ proc savestuff {w} { global canv canv2 canv3 ctext cflist mainfont textfont uifont global stuffsaved findmergefiles maxgraphpct global maxwidth + global viewname viewfiles viewperm nextviewnum if {$stuffsaved} return if {![winfo viewable .]} return @@ -655,6 +657,13 @@ proc savestuff {w} { set wid [expr {([winfo width $cflist] - 11) \ / [font measure [$cflist cget -font] "0"]}] puts $f "set geometry(cflistw) $wid" + puts -nonewline $f "set permviews {" + for {set v 0} {$v < $nextviewnum} {incr v} { + if {$viewperm($v)} { + puts $f "{[list $viewname($v) $viewfiles($v)]}" + } + } + puts $f "}" close $f file rename -force "~/.gitk-new" "~/.gitk" } @@ -797,7 +806,7 @@ f Scroll diff view to next file } proc newview {} { - global newviewname nextviewnum newviewtop + global newviewname nextviewnum newviewtop newviewperm uifont set top .gitkview if {[winfo exists $top]} { @@ -807,14 +816,18 @@ proc newview {} { set newviewtop $top toplevel $top wm title $top "Gitk view definition" - label $top.nl -text "Name" + label $top.nl -text "Name" -font $uifont entry $top.name -width 20 -textvariable newviewname set newviewname "View $nextviewnum" - grid $top.nl $top.name -sticky w - label $top.l -text "Files and directories to include:" - grid $top.l - -sticky w -pady 10 - text $top.t -width 30 -height 10 - grid $top.t - -sticky w + grid $top.nl $top.name -sticky w -pady 5 + set newviewperm 0 + checkbutton $top.perm -text "Remember this view" -variable newviewperm + grid $top.perm - -pady 5 -sticky w + message $top.l -aspect 500 -font $uifont \ + -text "Enter files and directories to include, one per line:" + grid $top.l - -sticky w + text $top.t -width 40 -height 10 -background white + grid $top.t - -sticky w -padx 5 frame $top.buts button $top.buts.ok -text "OK" -command newviewok button $top.buts.can -text "Cancel" -command newviewcan @@ -826,12 +839,13 @@ proc newview {} { } proc newviewok {} { - global newviewtop nextviewnum - global viewname viewfiles + global newviewtop nextviewnum newviewperm + global viewname viewfiles viewperm selectedview set n $nextviewnum incr nextviewnum set viewname($n) [$newviewtop.name get] + set viewperm($n) $newviewperm set files {} foreach f [split [$newviewtop.t get 0.0 end] "\n"] { set ft [string trim $f] @@ -842,7 +856,8 @@ proc newviewok {} { set viewfiles($n) $files catch {destroy $newviewtop} unset newviewtop - .bar.view add command -label $viewname($n) -command [list showview $n] + .bar.view add radiobutton -label $viewname($n) \ + -command [list showview $n] -variable selectedview -value $n after idle showview $n } @@ -854,7 +869,7 @@ proc newviewcan {} { } proc delview {} { - global curview viewdata + global curview viewdata viewperm if {$curview == 0} return set nmenu [.bar.view index end] @@ -866,6 +881,7 @@ proc delview {} { } } set viewdata($curview) {} + set viewperm($curview) 0 showview 0 } @@ -898,6 +914,7 @@ proc showview {n} { global pending_select phase global commitidx rowlaidout rowoptim linesegends leftover global commfd nextupdate + global selectedview if {$n == $curview} return set selid {} @@ -939,6 +956,7 @@ proc showview {n} { clear_display set curview $n + set selectedview $n .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] if {![info exists viewdata($n)]} { @@ -4209,7 +4227,9 @@ set optim_delay 16 set nextviewnum 1 set curview 0 +set selectedview 0 set viewfiles(0) {} +set viewperm(0) 0 set stopped 0 set stuffsaved 0 @@ -4232,10 +4252,25 @@ if {[lindex $revtreeargs end] eq "--"} { if {$cmdline_files ne {}} { # create a view for the files/dirs specified on the command line set curview 1 + set selectedview 1 set nextviewnum 2 set viewname(1) "Command line" set viewfiles(1) $cmdline_files - .bar.view add command -label $viewname(1) -command {showview 1} + set viewperm(1) 0 + .bar.view add radiobutton -label $viewname(1) -command {showview 1} \ + -variable selectedview -value 1 .bar.view entryconf 2 -state normal } + +if {[info exists permviews]} { + foreach v $permviews { + set n $nextviewnum + incr nextviewnum + set viewname($n) [lindex $v 0] + set viewfiles($n) [lindex $v 1] + set viewperm($n) 1 + .bar.view add radiobutton -label $viewname($n) \ + -command [list showview $n] -variable selectedview -value $n + } +} getcommits From d16c0812a9472794175b2543c2aa2df45a927202 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Apr 2006 21:21:10 +1000 Subject: [PATCH 011/212] gitk: add menu item for editing the current view This allows the user to change the name of the view, whether it is permanent, and the list of files/directories for the view. Signed-off-by: Paul Mackerras --- gitk | 127 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/gitk b/gitk index 6d6a2f32c..a33a1e8a9 100755 --- a/gitk +++ b/gitk @@ -360,6 +360,7 @@ proc makewindow {} { menu .bar.view -font $uifont .bar add cascade -label "View" -menu .bar.view .bar.view add command -label "New view..." -command newview + .bar.view add command -label "Edit view..." -command editview .bar.view add command -label "Delete view" -command delview -state disabled .bar.view add separator .bar.view add radiobutton -label "All files" -command {showview 0} \ @@ -806,31 +807,59 @@ f Scroll diff view to next file } proc newview {} { - global newviewname nextviewnum newviewtop newviewperm uifont + global nextviewnum newviewname newviewperm uifont set top .gitkview if {[winfo exists $top]} { raise $top return } - set newviewtop $top + set newviewname($nextviewnum) "View $nextviewnum" + set newviewperm($nextviewnum) 0 + vieweditor $top $nextviewnum "Gitk view definition" +} + +proc editview {} { + global curview + global viewname viewperm newviewname newviewperm + + set top .gitkvedit-$curview + if {[winfo exists $top]} { + raise $top + return + } + set newviewname($curview) $viewname($curview) + set newviewperm($curview) $viewperm($curview) + vieweditor $top $curview "Gitk: edit view $viewname($curview)" +} + +proc vieweditor {top n title} { + global newviewname newviewperm viewfiles + global uifont + toplevel $top - wm title $top "Gitk view definition" + wm title $top $title label $top.nl -text "Name" -font $uifont - entry $top.name -width 20 -textvariable newviewname - set newviewname "View $nextviewnum" + entry $top.name -width 20 -textvariable newviewname($n) grid $top.nl $top.name -sticky w -pady 5 - set newviewperm 0 - checkbutton $top.perm -text "Remember this view" -variable newviewperm + checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) grid $top.perm - -pady 5 -sticky w message $top.l -aspect 500 -font $uifont \ -text "Enter files and directories to include, one per line:" grid $top.l - -sticky w text $top.t -width 40 -height 10 -background white + if {[info exists viewfiles($n)]} { + foreach f $viewfiles($n) { + $top.t insert end $f + $top.t insert end "\n" + } + $top.t delete {end - 1c} end + $top.t mark set insert 0.0 + } grid $top.t - -sticky w -padx 5 frame $top.buts - button $top.buts.ok -text "OK" -command newviewok - button $top.buts.can -text "Cancel" -command newviewcan + button $top.buts.ok -text "OK" -command [list newviewok $top $n] + button $top.buts.can -text "Cancel" -command [list destroy $top] grid $top.buts.ok $top.buts.can grid columnconfigure $top.buts 0 -weight 1 -uniform a grid columnconfigure $top.buts 1 -weight 1 -uniform a @@ -838,47 +867,64 @@ proc newview {} { focus $top.t } -proc newviewok {} { - global newviewtop nextviewnum newviewperm - global viewname viewfiles viewperm selectedview +proc viewmenuitem {n} { + set nmenu [.bar.view index end] + set targetcmd [list showview $n] + for {set i 6} {$i <= $nmenu} {incr i} { + if {[.bar.view entrycget $i -command] eq $targetcmd} { + return $i + } + } + return {} +} + +proc newviewok {top n} { + global nextviewnum newviewperm newviewname + global viewname viewfiles viewperm selectedview curview - set n $nextviewnum - incr nextviewnum - set viewname($n) [$newviewtop.name get] - set viewperm($n) $newviewperm set files {} - foreach f [split [$newviewtop.t get 0.0 end] "\n"] { + foreach f [split [$top.t get 0.0 end] "\n"] { set ft [string trim $f] if {$ft ne {}} { lappend files $ft } } - set viewfiles($n) $files - catch {destroy $newviewtop} - unset newviewtop - .bar.view add radiobutton -label $viewname($n) \ - -command [list showview $n] -variable selectedview -value $n - after idle showview $n -} - -proc newviewcan {} { - global newviewtop - - catch {destroy $newviewtop} - unset newviewtop + if {![info exists viewfiles($n)]} { + # creating a new view + incr nextviewnum + set viewname($n) $newviewname($n) + set viewperm($n) $newviewperm($n) + set viewfiles($n) $files + .bar.view add radiobutton -label $viewname($n) \ + -command [list showview $n] -variable selectedview -value $n + after idle showview $n + } else { + # editing an existing view + set viewperm($n) $newviewperm($n) + if {$newviewname($n) ne $viewname($n)} { + set viewname($n) $newviewname($n) + set i [viewmenuitem $n] + if {$i ne {}} { + .bar.view entryconf $i -label $viewname($n) + } + } + if {$files ne $viewfiles($n)} { + set viewfiles($n) $files + if {$curview == $n} { + after idle updatecommits + } + } + } + catch {destroy $top} } proc delview {} { global curview viewdata viewperm if {$curview == 0} return - set nmenu [.bar.view index end] - set targetcmd [list showview $curview] - for {set i 5} {$i <= $nmenu} {incr i} { - if {[.bar.view entrycget $i -command] eq $targetcmd} { - .bar.view delete $i - break - } + set i [viewmenuitem $curview] + if {$i ne {}} { + .bar.view delete $i } set viewdata($curview) {} set viewperm($curview) 0 @@ -958,6 +1004,7 @@ proc showview {n} { set curview $n set selectedview $n .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] + .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}] if {![info exists viewdata($n)]} { set pending_select $selid @@ -1025,6 +1072,11 @@ proc showview {n} { } else { . config -cursor watch settextcursor watch + if {$phase eq "getcommits"} { + global mainfont + $canv create text 3 3 -anchor nw -text "Reading commits..." \ + -font $mainfont -tags textitems + } } } @@ -4260,6 +4312,7 @@ if {$cmdline_files ne {}} { .bar.view add radiobutton -label $viewname(1) -command {showview 1} \ -variable selectedview -value 1 .bar.view entryconf 2 -state normal + .bar.view entryconf 3 -state normal } if {[info exists permviews]} { From a970fcf249534ca21ad721b289402c135722a24c Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Tue, 18 Apr 2006 23:53:07 +0200 Subject: [PATCH 012/212] [PATCH] gitk: Add a visual tag for remote refs This patch partly changes the background color for remote refs. It makes it easy to quickly distinguish remote refs from local developer branches. I ignore remote HEADs, as these really should be drawn as aliases to other heads. But there is no simple way to detect that HEADs really are aliases for other refs via "git-ls-remote". Signed-off-by: Josef Weidendorfer Signed-off-by: Paul Mackerras --- gitk | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/gitk b/gitk index 5362b76be..7e80a7b98 100755 --- a/gitk +++ b/gitk @@ -274,10 +274,16 @@ proc readrefs {} { match id path]} { continue } + if {[regexp {^remotes/.*/HEAD$} $path match]} { + continue + } if {![regexp {^(tags|heads)/(.*)$} $path match type name]} { set type others set name $path } + if {[regexp {^remotes/} $path match]} { + set type heads + } if {$type == "tags"} { set tagids($name) $id lappend idtags($id) $name @@ -1702,6 +1708,14 @@ proc drawtags {id x xt y1} { set xl [expr {$xl - $delta/2}] $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ -width 1 -outline black -fill $col -tags tag.$id + if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} { + set rwid [font measure $mainfont $remoteprefix] + set xi [expr {$x + 1}] + set yti [expr {$yt + 1}] + set xri [expr {$x + $rwid}] + $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \ + -width 0 -fill "#ffddaa" -tags tag.$id + } } set t [$canv create text $xl $y1 -anchor w -text $tag \ -font $mainfont -tags tag.$id] From 992793c832acfd98107068d90b886643f0344d04 Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Wed, 26 Apr 2006 12:26:16 +1200 Subject: [PATCH 013/212] git-cvsexportcommit: Add -f(orce) and -m(essage prefix) flags, small cleanups. --- Documentation/git-cvsexportcommit.txt | 9 ++++++++- git-cvsexportcommit.perl | 22 +++++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index d30435a9e..56bd3e517 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -8,7 +8,7 @@ git-cvsexportcommit - Export a commit to a CVS checkout SYNOPSIS -------- -'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [PARENTCOMMIT] COMMITID +'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID DESCRIPTION @@ -39,6 +39,13 @@ OPTIONS Be pedantic (paranoid) when applying patches. Invokes patch with --fuzz=0 +-f:: + Force the merge even if the files are not up to date. + +-m:: + Prepend the commit message with the provided prefix. + Useful for patch series and the like. + -v:: Verbose. diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 7b3a3d323..f994443c6 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -10,9 +10,9 @@ die "GIT_DIR is not defined or is unreadable"; } -our ($opt_h, $opt_p, $opt_v, $opt_c ); +our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_m ); -getopts('hpvc'); +getopts('hpvcfm:'); $opt_h && usage(); @@ -77,12 +77,16 @@ $opt_v && print "Applying to CVS commit $commit from parent $parent\n"; # grab the commit message -`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`; +open(MSG, ">.msg") or die "Cannot open .msg for writing"; +print MSG $opt_m; +close MSG; + +`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`; $? && die "Error extracting the commit message"; my (@afiles, @dfiles, @mfiles); my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit); -print @files; +#print @files; $? && die "Error in git-diff-tree"; foreach my $f (@files) { chomp $f; @@ -109,7 +113,7 @@ if (@status > 1) { warn 'Strange! cvs status returned more than one line?'}; unless ($status[0] =~ m/Status: Unknown$/) { $dirty = 1; - warn "File $f is already known in your CVS checkout!\n"; + warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n"; } } foreach my $f (@mfiles, @dfiles) { @@ -122,7 +126,11 @@ } } if ($dirty) { - die "Exiting: your CVS tree is not clean for this merge."; + if ($opt_f) { warn "The tree is not clean -- forced merge\n"; + $dirty = 0; + } else { + die "Exiting: your CVS tree is not clean for this merge."; + } } ### @@ -215,7 +223,7 @@ } sub usage { print STDERR < Date: Tue, 25 Apr 2006 23:40:09 -0700 Subject: [PATCH 014/212] diff --stat: show complete rewrites consistently. The patch format shows complete rewrite as deletion of all old lines followed by addition of all new lines. Count lines consistenly with that when doing diffstat. Signed-off-by: Junio C Hamano --- diff.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/diff.c b/diff.c index 13b216f27..6762fcee5 100644 --- a/diff.c +++ b/diff.c @@ -485,7 +485,8 @@ static void builtin_diff(const char *name_a, static void builtin_diffstat(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, - struct diffstat_t *diffstat) + struct diffstat_t *diffstat, + int complete_rewrite) { mmfile_t mf1, mf2; struct diffstat_file *data; @@ -496,7 +497,13 @@ static void builtin_diffstat(const char *name_a, const char *name_b, data->is_unmerged = 1; return; } - + if (complete_rewrite) { + diff_populate_filespec(one, 0); + diff_populate_filespec(two, 0); + data->deleted = count_lines(one->data, one->size); + data->added = count_lines(two->data, two->size); + return; + } if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1055,10 +1062,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, { const char *name; const char *other; + int complete_rewrite = 0; if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat); + builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0); return; } @@ -1068,7 +1076,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, diff_fill_sha1_info(p->one); diff_fill_sha1_info(p->two); - builtin_diffstat(name, other, p->one, p->two, diffstat); + if (p->status == DIFF_STATUS_MODIFIED && p->score) + complete_rewrite = 1; + builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite); } void diff_setup(struct diff_options *options) From 5348b6e7f4f07b75b2ea23a3c8fee62919fa254c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Apr 2006 23:59:28 -0700 Subject: [PATCH 015/212] git-cvsserver: typofixes Signed-off-by: Junio C Hamano --- git-cvsserver.perl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 0b37d26e0..11d153c4c 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -88,7 +88,7 @@ $log->debug("Temporary directory is '$TEMP_DIR'"); # if we are called with a pserver argument, -# deal with the authentication cat before entereing the +# deal with the authentication cat before entering the # main loop if (@ARGV && $ARGV[0] eq 'pserver') { my $line = ; chomp $line; @@ -117,7 +117,7 @@ { chomp; - # Check to see if we've seen this method, and call appropiate function. + # Check to see if we've seen this method, and call appropriate function. if ( /^([\w-]+)(?:\s+(.*))?$/ and defined($methods->{$1}) ) { # use the $methods hash to call the appropriate sub for this command @@ -173,7 +173,7 @@ sub req_Root my @gitvars = `git-repo-config -l`; if ($?) { - print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correcly.\n"; + print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correctly.\n"; print "E \n"; print "error 1 - problem executing git-repo-config\n"; return 0; @@ -224,7 +224,7 @@ sub req_Globaloption sub req_Validresponses { my ( $cmd, $data ) = @_; - $log->debug("req_Validrepsonses : $data"); + $log->debug("req_Validresponses : $data"); # TODO : re-enable this, currently it's not particularly useful #$state->{validresponses} = [ split /\s+/, $data ]; @@ -733,7 +733,7 @@ sub req_update argsplit("update"); # - # It may just be a client exploring the available heads/modukles + # It may just be a client exploring the available heads/modules # in that case, list them as top level directories and leave it # at that. Eclipse uses this technique to offer you a list of # projects (heads in this case) to checkout. @@ -1731,7 +1731,7 @@ sub transmitfile } # This method takes a file name, and returns ( $dirpart, $filepart ) which -# refers to the directory porition and the file portion of the filename +# refers to the directory portion and the file portion of the filename # respectively sub filenamesplit { @@ -1790,7 +1790,7 @@ =head1 METHODS =head2 new Creates a new log object, optionally you can specify a filename here to -indicate the file to log to. If no log file is specified, you can specifiy one +indicate the file to log to. If no log file is specified, you can specify one later with method setfile, or indicate you no longer want logging with method nofile. @@ -2595,7 +2595,7 @@ sub in_array =head2 safe_pipe_capture -an alterative to `command` that allows input to be passed as an array +an alternative to `command` that allows input to be passed as an array to work around shell problems with weird characters in arguments =cut From ea92f41ff92b2fef54c8da4abb03c8101160a034 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 15:09:27 -0700 Subject: [PATCH 016/212] revision parsing: make "rev -- paths" checks stronger. If you don't have a "--" marker, then: - all of the arguments we are going to assume are pathspecs must exist in the working tree. - none of the arguments we parsed as revisions could be interpreted as a filename. so that there really isn't any possibility of confusion in case somebody does have a revision that looks like a pathname too. The former rule has been in effect; this implements the latter. Signed-off-by: Junio C Hamano --- cache.h | 1 + revision.c | 14 +++++++++++++- setup.c | 24 ++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/cache.h b/cache.h index 4d8fabc6d..a4f253ec2 100644 --- a/cache.h +++ b/cache.h @@ -135,6 +135,7 @@ extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); extern void verify_filename(const char *prefix, const char *name); +extern void verify_non_filename(const char *prefix, const char *name); #define alloc_nr(x) (((x)+16)*3/2) diff --git a/revision.c b/revision.c index f2a9f25fe..b6ed0149a 100644 --- a/revision.c +++ b/revision.c @@ -740,6 +740,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch include = get_reference(revs, next, sha1, flags); if (!exclude || !include) die("Invalid revision range %s..%s", arg, next); + + if (!seen_dashdash) { + *dotdot = '.'; + verify_non_filename(revs->prefix, arg); + } add_pending_object(revs, exclude, this); add_pending_object(revs, include, next); continue; @@ -757,13 +762,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (seen_dashdash || local_flags) die("bad revision '%s'", arg); - /* If we didn't have a "--", all filenames must exist */ + /* If we didn't have a "--": + * (1) all filenames must exist; + * (2) all rev-args must not be interpretable + * as a valid filename. + * but the latter we have checked in the main loop. + */ for (j = i; j < argc; j++) verify_filename(revs->prefix, argv[j]); revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } + if (!seen_dashdash) + verify_non_filename(revs->prefix, arg); object = get_reference(revs, arg, sha1, flags ^ local_flags); add_pending_object(revs, object, arg); } diff --git a/setup.c b/setup.c index cce9bb806..fe7f88469 100644 --- a/setup.c +++ b/setup.c @@ -80,11 +80,31 @@ void verify_filename(const char *prefix, const char *arg) if (!lstat(name, &st)) return; if (errno == ENOENT) - die("ambiguous argument '%s': unknown revision or filename\n" - "Use '--' to separate filenames from revisions", arg); + die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" + "Use '--' to separate paths from revisions", arg); die("'%s': %s", arg, strerror(errno)); } +/* + * Opposite of the above: the command line did not have -- marker + * and we parsed the arg as a refname. It should not be interpretable + * as a filename. + */ +void verify_non_filename(const char *prefix, const char *arg) +{ + const char *name; + struct stat st; + + if (*arg == '-') + return; /* flag */ + name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; + if (!lstat(name, &st)) + die("ambiguous argument '%s': both revision and filename\n" + "Use '--' to separate filenames from revisions", arg); + if (errno != ENOENT) + die("'%s': %s", arg, strerror(errno)); +} + const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; From 031321c654de3c19de45b2dacbdc990b5a694e34 Mon Sep 17 00:00:00 2001 From: sean Date: Wed, 26 Apr 2006 10:49:38 -0400 Subject: [PATCH 017/212] Add --continue and --abort options to git-rebase. git rebase [--onto ] [] git rebase --continue git rebase --abort Add "--continue" to restart the rebase process after manually resolving conflicts. The user is warned if there are still differences between the index and the working files. Add "--abort" to restore the original branch, and remove the .dotest working files. Some minor additions to the git-rebase documentation. [jc: fix that applies to the maintenance track has been dealt with separately.] Signed-off-by: Junio C Hamano --- Documentation/git-rebase.txt | 76 ++++++++++++++++++++++++++++++------ git-rebase.sh | 62 +++++++++++++++++------------ 2 files changed, 101 insertions(+), 37 deletions(-) diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 4a7e67a4d..1b482abec 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -3,38 +3,54 @@ git-rebase(1) NAME ---- -git-rebase - Rebase local commits to new upstream head +git-rebase - Rebase local commits to a new head SYNOPSIS -------- 'git-rebase' [--onto ] [] +'git-rebase' --continue + +'git-rebase' --abort + DESCRIPTION ----------- -git-rebase applies to (or optionally to ) commits -from that do not appear in . When is not -specified it defaults to the current branch (HEAD). +git-rebase replaces with a new branch of the same name. When +the --onto option is provided the new branch starts out with a HEAD equal +to , otherwise it is equal to . It then attempts to +create a new commit for each commit from the original that does +not exist in the branch. -When git-rebase is complete, will be updated to point to the -newly created line of commit objects, so the previous line will not be -accessible unless there are other references to it already. +It is possible that a merge failure will prevent this process from being +completely automatic. You will have to resolve any such merge failure +and run `git rebase --continue`. If you can not resolve the merge +failure, running `git rebase --abort` will restore the original +and remove the working files found in the .dotest directory. + +Note that if is not specified on the command line, the currently +checked out branch is used. Assume the following history exists and the current branch is "topic": +------------ A---B---C topic / D---E---F---G master +------------ From this point, the result of either of the following commands: + git-rebase master git-rebase master topic would be: +------------ A'--B'--C' topic / D---E---F---G master +------------ While, starting from the same point, the result of either of the following commands: @@ -44,21 +60,33 @@ commands: would be: +------------ A'--B'--C' topic / D---E---F---G master +------------ In case of conflict, git-rebase will stop at the first problematic commit -and leave conflict markers in the tree. After resolving the conflict manually -and updating the index with the desired resolution, you can continue the -rebasing process with +and leave conflict markers in the tree. You can use git diff to locate +the markers (<<<<<<) and make edits to resolve the conflict. For each +file you edit, you need to tell git that the conflict has been resolved, +typically this would be done with + + + git update-index + + +After resolving the conflict manually and updating the index with the +desired resolution, you can continue the rebasing process with + + + git rebase --continue - git am --resolved --3way Alternatively, you can undo the git-rebase with - git reset --hard ORIG_HEAD - rm -r .dotest + + git rebase --abort OPTIONS ------- @@ -73,6 +101,28 @@ OPTIONS :: Working branch; defaults to HEAD. +--continue:: + Restart the rebasing process after having resolved a merge conflict. + +--abort:: + Restore the original branch and abort the rebase operation. + +NOTES +----- +When you rebase a branch, you are changing its history in a way that +will cause problems for anyone who already has a copy of the branch +in their repository and tries to pull updates from you. You should +understand the implications of using 'git rebase' on a repository that +you share. + +When the git rebase command is run, it will first execute a "pre-rebase" +hook if one exists. You can use this hook to do sanity checks and +reject the rebase if it isn't appropriate. Please see the template +pre-rebase hook script for an example. + +You must be in the top directory of your project to start (or continue) +a rebase. Upon completion, will be the current branch. + Author ------ Written by Junio C Hamano diff --git a/git-rebase.sh b/git-rebase.sh index f7b2b9401..9e259028e 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -4,37 +4,51 @@ # USAGE='[--onto ] []' -LONG_USAGE='git-rebase applies to (or optionally to ) commits -from that do not appear in . When is not -specified it defaults to the current branch (HEAD). - -When git-rebase is complete, will be updated to point to the -newly created line of commit objects, so the previous line will not be -accessible unless there are other references to it already. - -Assuming the following history: - - A---B---C topic - / - D---E---F---G master - -The result of the following command: - - git-rebase --onto master~1 master topic - - would be: - - A'\''--B'\''--C'\'' topic - / - D---E---F---G master +LONG_USAGE='git-rebase replaces with a new branch of the +same name. When the --onto option is provided the new branch starts +out with a HEAD equal to , otherwise it is equal to +It then attempts to create a new commit for each commit from the original + that does not exist in the branch. + +It is possible that a merge failure will prevent this process from being +completely automatic. You will have to resolve any such merge failure +and run git-rebase --continue. If you can not resolve the merge failure, +running git-rebase --abort will restore the original and remove +the working files found in the .dotest directory. + +Note that if is not specified on the command line, the +currently checked out branch is used. You must be in the top +directory of your project to start (or continue) a rebase. + +Example: git-rebase master~1 topic + + A---B---C topic A'\''--B'\''--C'\'' topic + / --> / + D---E---F---G master D---E---F---G master ' - . git-sh-setup unset newbase while case "$#" in 0) break ;; esac do case "$1" in + --continue) + diff=$(git-diff-files) + case "$diff" in + ?*) echo "You must edit all merge conflicts and then" + echo "mark them as resolved using git update-index" + exit 1 + ;; + esac + git am --resolved --3way + exit + ;; + --abort) + [ -d .dotest ] || die "No rebase in progress?" + git reset --hard ORIG_HEAD + rm -r .dotest + exit + ;; --onto) test 2 -le "$#" || usage newbase="$2" From 093b06889104d218e95251176af5cf4aa5dd5cdd Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Wed, 26 Apr 2006 09:10:01 -0700 Subject: [PATCH 018/212] git-fetch: resolve remote symrefs for HTTP transport git-fetch validates that a remote ref resolves to a SHA1 prior to calling git-http-fetch. This adds support for resolving a few levels of symrefs to get to the SHA1. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano --- git-fetch.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/git-fetch.sh b/git-fetch.sh index 83143f82c..280f62e4b 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -270,14 +270,22 @@ fetch_main () { if [ -n "$GIT_SSL_NO_VERIFY" ]; then curl_extra_args="-k" fi - remote_name_quoted=$(perl -e ' + max_depth=5 + depth=0 + head="ref: $remote_name" + while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null + do + remote_name_quoted=$(perl -e ' my $u = $ARGV[0]; + $u =~ s/^ref:\s*//; $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg; print "$u"; - ' "$remote_name") - head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") && + ' "$head") + head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") + depth=$( expr \( $depth + 1 \) ) + done expr "z$head" : "z$_x40\$" >/dev/null || - die "Failed to fetch $remote_name from $remote" + die "Failed to fetch $remote_name from $remote" echo >&2 Fetching "$remote_name from $remote" using http git-http-fetch -v -a "$head" "$remote/" || exit ;; From f6c7081aa97aa67baa06390a1ef36088c33bf010 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 26 Apr 2006 23:58:00 -0400 Subject: [PATCH 019/212] use delta index data when finding best delta matches This patch allows for computing the delta index for each base object only once and reuse it when trying to find the best delta match. This should set the mark and pave the way for possibly better delta generator algorithms. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- pack-objects.c | 78 ++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/pack-objects.c b/pack-objects.c index c0acc460b..5b2ef9a51 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -994,6 +994,7 @@ static int type_size_sort(const struct object_entry *a, const struct object_entr struct unpacked { struct object_entry *entry; void *data; + struct delta_index *index; }; /* @@ -1004,64 +1005,56 @@ struct unpacked { * more importantly, the bigger file is likely the more recent * one. */ -static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth) +static int try_delta(struct unpacked *trg, struct unpacked *src, + struct delta_index *src_index, unsigned max_depth) { - struct object_entry *cur_entry = cur->entry; - struct object_entry *old_entry = old->entry; - unsigned long size, oldsize, delta_size, sizediff; - long max_size; + struct object_entry *trg_entry = trg->entry; + struct object_entry *src_entry = src->entry; + unsigned long size, src_size, delta_size, sizediff, max_size; void *delta_buf; /* Don't bother doing diffs between different types */ - if (cur_entry->type != old_entry->type) + if (trg_entry->type != src_entry->type) return -1; /* We do not compute delta to *create* objects we are not * going to pack. */ - if (cur_entry->preferred_base) + if (trg_entry->preferred_base) return -1; - /* If the current object is at pack edge, take the depth the + /* + * If the current object is at pack edge, take the depth the * objects that depend on the current object into account -- * otherwise they would become too deep. */ - if (cur_entry->delta_child) { - if (max_depth <= cur_entry->delta_limit) + if (trg_entry->delta_child) { + if (max_depth <= trg_entry->delta_limit) return 0; - max_depth -= cur_entry->delta_limit; + max_depth -= trg_entry->delta_limit; } - - size = cur_entry->size; - oldsize = old_entry->size; - sizediff = oldsize > size ? oldsize - size : size - oldsize; - - if (size < 50) - return -1; - if (old_entry->depth >= max_depth) + if (src_entry->depth >= max_depth) return 0; - /* - * NOTE! - * - * We always delta from the bigger to the smaller, since that's - * more space-efficient (deletes don't have to say _what_ they - * delete). - */ + /* Now some size filtering euristics. */ + size = trg_entry->size; max_size = size / 2 - 20; - if (cur_entry->delta) - max_size = cur_entry->delta_size-1; + if (trg_entry->delta) + max_size = trg_entry->delta_size-1; + src_size = src_entry->size; + sizediff = src_size < size ? size - src_size : 0; if (sizediff >= max_size) return 0; - delta_buf = diff_delta(old->data, oldsize, - cur->data, size, &delta_size, max_size); + + delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size); if (!delta_buf) return 0; - cur_entry->delta = old_entry; - cur_entry->delta_size = delta_size; - cur_entry->depth = old_entry->depth + 1; + + trg_entry->delta = src_entry; + trg_entry->delta_size = delta_size; + trg_entry->depth = src_entry->depth + 1; free(delta_buf); - return 0; + return 1; } static void progress_interval(int signum) @@ -1109,11 +1102,19 @@ static void find_deltas(struct object_entry **list, int window, int depth) */ continue; + if (entry->size < 50) + continue; + if (n->index) + free_delta_index(n->index); free(n->data); n->entry = entry; n->data = read_sha1_file(entry->sha1, type, &size); if (size != entry->size) - die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size); + die("object %s inconsistent object length (%lu vs %lu)", + sha1_to_hex(entry->sha1), size, entry->size); + n->index = create_delta_index(n->data, size); + if (!n->index) + die("out of memory"); j = window; while (--j > 0) { @@ -1124,7 +1125,7 @@ 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, depth) < 0) + if (try_delta(n, m, m->index, depth) < 0) break; } #if 0 @@ -1144,8 +1145,11 @@ static void find_deltas(struct object_entry **list, int window, int depth) if (progress) fputc('\n', stderr); - for (i = 0; i < window; ++i) + for (i = 0; i < window; ++i) { + if (array[i].index) + free_delta_index(array[i].index); free(array[i].data); + } free(array); } From 61678d87c2aad3bd668196993d8c32f298ddf5ff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 01:59:00 -0700 Subject: [PATCH 020/212] diff-index: fix compilation warnings. Signed-off-by: Junio C Hamano --- diff-index.c | 1 - 1 file changed, 1 deletion(-) diff --git a/diff-index.c b/diff-index.c index 86940123b..8c9f60173 100644 --- a/diff-index.c +++ b/diff-index.c @@ -11,7 +11,6 @@ COMMON_DIFF_OPTIONS_HELP; int main(int argc, const char **argv) { struct rev_info rev; - int match_missing = 0; int cached = 0; int i; From 7fcceed7a024f5e2868c123c4ace5b7894ea3423 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 27 Apr 2006 19:21:49 +1000 Subject: [PATCH 021/212] gitk: Use a text widget for the file list This lets us do things like highlighting all the entries for which the corresponding part of the diff is at least partly visible in the commit/patch display window, and in future it will let us display the file list in a hierarchical form rather than as a flat file list. Signed-off-by: Paul Mackerras --- gitk | 190 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 131 insertions(+), 59 deletions(-) diff --git a/gitk b/gitk index 7c25d2ef9..bd205f876 100755 --- a/gitk +++ b/gitk @@ -481,7 +481,7 @@ proc makewindow {} { set ctext .ctop.cdet.left.ctext text $ctext -bg white -state disabled -font $textfont \ -width $geometry(ctextw) -height $geometry(ctexth) \ - -yscrollcommand ".ctop.cdet.left.sb set" -wrap none + -yscrollcommand scrolltext -wrap none scrollbar .ctop.cdet.left.sb -command "$ctext yview" pack .ctop.cdet.left.sb -side right -fill y pack $ctext -side left -fill both -expand 1 @@ -515,11 +515,16 @@ proc makewindow {} { frame .ctop.cdet.right set cflist .ctop.cdet.right.cfiles - listbox $cflist -bg white -selectmode extended -width $geometry(cflistw) \ - -yscrollcommand ".ctop.cdet.right.sb set" -font $mainfont + set indent [font measure $mainfont "nn"] + text $cflist -width $geometry(cflistw) -background white -font $mainfont \ + -tabs [list $indent [expr {2 * $indent}]] \ + -yscrollcommand ".ctop.cdet.right.sb set" \ + -cursor [. cget -cursor] \ + -spacing1 1 -spacing3 1 scrollbar .ctop.cdet.right.sb -command "$cflist yview" pack .ctop.cdet.right.sb -side right -fill y pack $cflist -side left -fill both -expand 1 + $cflist tag configure highlight -background yellow .ctop.cdet add .ctop.cdet.right bind .ctop.cdet {resizecdetpanes %W %w} @@ -571,12 +576,13 @@ proc makewindow {} { bind . {incrfont 1} bind . {incrfont -1} bind . {incrfont -1} - bind $cflist <> listboxsel bind . {savestuff %W} bind . "click %W" bind $fstring dofind bind $sha1entry gotocommit bind $sha1entry <> clearsha1 + bind $cflist <1> {sel_flist %W %x %y; break} + bind $cflist {sel_flist %W %x %y; break} set maincursor [. cget -cursor] set textcursor [$ctext cget -cursor] @@ -812,6 +818,101 @@ f Scroll diff view to next file pack $w.ok -side bottom } +# Procedures for manipulating the file list window at the +# bottom right of the overall window. +proc init_flist {first} { + global cflist cflist_top cflist_bot selectedline difffilestart + + $cflist conf -state normal + $cflist delete 0.0 end + if {$first ne {}} { + $cflist insert end $first + set cflist_top 1 + set cflist_bot 1 + $cflist tag add highlight 1.0 "1.0 lineend" + } else { + catch {unset cflist_top} + } + $cflist conf -state disabled + set difffilestart {} +} + +proc add_flist {f} { + global flistmode cflist + + $cflist conf -state normal + if {$flistmode eq "flat"} { + $cflist insert end "\n$f" + } + $cflist conf -state disabled +} + +proc sel_flist {w x y} { + global flistmode ctext difffilestart cflist cflist_top + + if {![info exists cflist_top]} return + set l [lindex [split [$w index "@$x,$y"] "."] 0] + if {$flistmode eq "flat"} { + if {$l == 1} { + $ctext yview 1.0 + } else { + catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]} + } + highlight_flist $l + } +} + +proc scrolltext {f0 f1} { + global cflist_top + + .ctop.cdet.left.sb set $f0 $f1 + if {[info exists cflist_top]} { + highlight_flist $cflist_top + } +} + +# Given an index $tl in the $ctext window, this works out which line +# of the $cflist window displays the filename whose patch is shown +# at the given point in the $ctext window. $ll is a hint about which +# line it might be, and is used as the starting point of the search. +proc ctext_index {tl ll} { + global ctext difffilestart + + while {$ll >= 2 && [$ctext compare $tl < \ + [lindex $difffilestart [expr {$ll - 2}]]]} { + incr ll -1 + } + set nfiles [llength $difffilestart] + while {$ll - 1 < $nfiles && [$ctext compare $tl >= \ + [lindex $difffilestart [expr {$ll - 1}]]]} { + incr ll + } + return $ll +} + +proc highlight_flist {ll} { + global ctext cflist cflist_top cflist_bot difffilestart + + if {![info exists difffilestart] || [llength $difffilestart] == 0} return + set ll [ctext_index [$ctext index @0,1] $ll] + set lb $cflist_bot + if {$lb < $ll} { + set lb $ll + } + set y [expr {[winfo height $ctext] - 2}] + set lb [ctext_index [$ctext index @0,$y] $lb] + if {$ll != $cflist_top || $lb != $cflist_bot} { + $cflist tag remove highlight $cflist_top.0 "$cflist_bot.0 lineend" + for {set l $ll} {$l <= $lb} {incr l} { + $cflist tag add highlight $l.0 "$l.0 lineend" + } + set cflist_top $ll + set cflist_bot $lb + } +} + +# Code to implement multiple views + proc newview {} { global nextviewnum newviewname newviewperm uifont @@ -2718,7 +2819,7 @@ proc selectline {l isnew} { global canv canv2 canv3 ctext commitinfo selectedline global displayorder linehtag linentag linedtag global canvy0 linespc parentlist childlist - global cflist currentid sha1entry + global currentid sha1entry global commentend idtags linknum global mergemax numcommits pending_select @@ -2841,8 +2942,7 @@ proc selectline {l isnew} { $ctext conf -state disabled set commentend [$ctext index "end - 1c"] - $cflist delete 0 end - $cflist insert end "Comments" + init_flist "Comments" if {[llength $olds] <= 1} { startdiff $id } else { @@ -2960,12 +3060,11 @@ proc goforw {} { proc mergediff {id l} { global diffmergeid diffopts mdifffd - global difffilestart diffids + global diffids global parentlist set diffmergeid $id set diffids $id - catch {unset difffilestart} # this doesn't seem to actually affect anything... set env(GIT_DIFF_OPTS) $diffopts set cmd [concat | git-diff-tree --no-commit-id --cc $id] @@ -3000,11 +3099,10 @@ proc getmergediffline {mdf id np} { # start of a new file $ctext insert end "\n" set here [$ctext index "end - 1c"] - set i [$cflist index end] - $ctext mark set fmark.$i $here - $ctext mark gravity fmark.$i left - set difffilestart([expr {$i-1}]) $here - $cflist insert end $fname + $ctext mark set f:$fname $here + $ctext mark gravity f:$fname left + lappend difffilestart $here + add_flist $fname set l [expr {(78 - [string length $fname]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert end "$pad $fname $pad\n" filesep @@ -3075,7 +3173,7 @@ proc startdiff {ids} { proc addtocflist {ids} { global treediffs cflist foreach f $treediffs($ids) { - $cflist insert end $f + add_flist $f } getblobdiffs $ids } @@ -3115,7 +3213,7 @@ proc gettreediffline {gdtf ids} { proc getblobdiffs {ids} { global diffopts blobdifffd diffids env curdifftag curtagstart - global difffilestart nextupdate diffinhdr treediffs + global nextupdate diffinhdr treediffs set env(GIT_DIFF_OPTS) $diffopts set cmd [concat | git-diff-tree --no-commit-id -r -p -C $ids] @@ -3128,7 +3226,6 @@ proc getblobdiffs {ids} { set blobdifffd($ids) $bdf set curdifftag Comments set curtagstart 0.0 - catch {unset difffilestart} fileevent $bdf readable [list getblobdiffline $bdf $diffids] set nextupdate [expr {[clock clicks -milliseconds] + 100}] } @@ -3156,24 +3253,15 @@ proc getblobdiffline {bdf ids} { # start of a new file $ctext insert end "\n" $ctext tag add $curdifftag $curtagstart end - set curtagstart [$ctext index "end - 1c"] - set header $newname set here [$ctext index "end - 1c"] - set i [lsearch -exact $treediffs($diffids) $fname] - if {$i >= 0} { - set difffilestart($i) $here - incr i - $ctext mark set fmark.$i $here - $ctext mark gravity fmark.$i left - } + set curtagstart $here + set header $newname + lappend difffilestart $here + $ctext mark set f:$fname $here + $ctext mark gravity f:$fname left if {$newname != $fname} { - set i [lsearch -exact $treediffs($diffids) $newname] - if {$i >= 0} { - set difffilestart($i) $here - incr i - $ctext mark set fmark.$i $here - $ctext mark gravity fmark.$i left - } + $ctext mark set f:$newfname $here + $ctext mark gravity f:$newfname left } set curdifftag "f:$fname" $ctext tag delete $curdifftag @@ -3222,26 +3310,11 @@ proc getblobdiffline {bdf ids} { proc nextfile {} { global difffilestart ctext set here [$ctext index @0,0] - for {set i 0} {[info exists difffilestart($i)]} {incr i} { - if {[$ctext compare $difffilestart($i) > $here]} { - if {![info exists pos] - || [$ctext compare $difffilestart($i) < $pos]} { - set pos $difffilestart($i) - } + foreach loc $difffilestart { + if {[$ctext compare $loc > $here]} { + $ctext yview $loc } } - if {[info exists pos]} { - $ctext yview $pos - } -} - -proc listboxsel {} { - global ctext cflist currentid - if {![info exists currentid]} return - set sel [lsort [$cflist curselection]] - if {$sel eq {}} return - set first [lindex $sel 0] - catch {$ctext yview fmark.$first} } proc setcoords {} { @@ -3452,7 +3525,7 @@ proc arrowjump {id n y} { } proc lineclick {x y id isnew} { - global ctext commitinfo childlist commitrow cflist canv thickerline + global ctext commitinfo childlist commitrow canv thickerline if {![info exists commitinfo($id)] && ![getcommit $id]} return unmarkmatches @@ -3509,8 +3582,7 @@ proc lineclick {x y id isnew} { } } $ctext conf -state disabled - - $cflist delete 0 end + init_flist {} } proc normalline {} { @@ -3568,15 +3640,14 @@ proc diffvssel {dirn} { } proc doseldiff {oldid newid} { - global ctext cflist + global ctext global commitinfo $ctext conf -state normal $ctext delete 0.0 end $ctext mark set fmark.0 0.0 $ctext mark gravity fmark.0 left - $cflist delete 0 end - $cflist insert end "Top" + init_flist "Top" $ctext insert end "From " $ctext tag conf link -foreground blue -underline 1 $ctext tag bind link { %W configure -cursor hand2 } @@ -3862,7 +3933,7 @@ proc rereadrefs {} { } proc showtag {tag isnew} { - global ctext cflist tagcontents tagids linknum + global ctext tagcontents tagids linknum if {$isnew} { addtohistory [list showtag $tag 0] @@ -3877,7 +3948,7 @@ proc showtag {tag isnew} { } appendwithlinks $text $ctext conf -state disabled - $cflist delete 0 end + init_flist {} } proc doquit {} { @@ -4259,6 +4330,7 @@ set fastdate 0 set uparrowlen 7 set downarrowlen 7 set mingaplen 30 +set flistmode "flat" set colors {green red blue magenta darkgrey brown orange} From 83aa18eade572679319406d6dc12a81599a4d57e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 27 Apr 2006 09:02:54 -0700 Subject: [PATCH 022/212] Fix "git help -a" terminal autosizing When I split out the builtin commands into their own files, I left the include of in git.c rather than moving it to the file that needed it (builtin-help.c). Nobody seems to have noticed, because everything still worked, but because the TIOCGWINSZ macro was now no longer defined when compiling the "term_columns()" function, it would no longer automatically notice the terminal size unless your system used the ancient "COLUMNS" environment variable approach. Trivially fixed by just moving the header include to the file that actually needs it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-help.c | 1 + git.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-help.c b/builtin-help.c index 10a59cc40..7470faa56 100644 --- a/builtin-help.c +++ b/builtin-help.c @@ -3,6 +3,7 @@ * * Builtin help-related commands (help, usage, version) */ +#include #include "cache.h" #include "builtin.h" #include "exec_cmd.h" diff --git a/git.c b/git.c index aa2b814d9..01b7e28b8 100644 --- a/git.c +++ b/git.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "git-compat-util.h" #include "exec_cmd.h" From 55e1805dffeb5d2a8ccd717b2d07ca8887436a69 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 15:42:01 -0700 Subject: [PATCH 023/212] verify-pack: check integrity in a saner order. Check internal integrity to report corrupt pack or idx, and then check cross-integrity between idx and pack. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- pack-check.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pack-check.c b/pack-check.c index 84ed90d36..e57587909 100644 --- a/pack-check.c +++ b/pack-check.c @@ -29,12 +29,12 @@ static int verify_packfile(struct packed_git *p) pack_base = p->pack_base; SHA1_Update(&ctx, pack_base, pack_size - 20); SHA1_Final(sha1, &ctx); - if (memcmp(sha1, index_base + index_size - 40, 20)) - return error("Packfile %s SHA1 mismatch with idx", - p->pack_name); if (memcmp(sha1, pack_base + pack_size - 20, 20)) return error("Packfile %s SHA1 mismatch with itself", p->pack_name); + if (memcmp(sha1, index_base + index_size - 40, 20)) + return error("Packfile %s SHA1 mismatch with idx", + p->pack_name); /* Make sure everything reachable from idx is valid. Since we * have verified that nr_objects matches between idx and pack, From 9a8b6a0a9d4de3d9c1005b872b9b57a213d3e9f8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 19:31:46 -0700 Subject: [PATCH 024/212] pack-objects: update size heuristucs. We used to omit delta base candidates that is much bigger than the target, but delta size does not grow when we delete more, so that was not a very good heuristics. Signed-off-by: Junio C Hamano --- pack-objects.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pack-objects.c b/pack-objects.c index c0acc460b..660433813 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -1032,12 +1032,6 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de max_depth -= cur_entry->delta_limit; } - size = cur_entry->size; - oldsize = old_entry->size; - sizediff = oldsize > size ? oldsize - size : size - oldsize; - - if (size < 50) - return -1; if (old_entry->depth >= max_depth) return 0; @@ -1048,9 +1042,12 @@ static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_de * more space-efficient (deletes don't have to say _what_ they * delete). */ + size = cur_entry->size; max_size = size / 2 - 20; if (cur_entry->delta) max_size = cur_entry->delta_size-1; + oldsize = old_entry->size; + sizediff = oldsize < size ? size - oldsize : 0; if (sizediff >= max_size) return 0; delta_buf = diff_delta(old->data, oldsize, @@ -1109,6 +1106,9 @@ static void find_deltas(struct object_entry **list, int window, int depth) */ continue; + if (entry->size < 50) + continue; + free(n->data); n->entry = entry; n->data = read_sha1_file(entry->sha1, type, &size); From c74320872b445104fe5c265e60785d9d26d94cc5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 15:37:18 -0700 Subject: [PATCH 025/212] built-in count-objects. Also it learned to do -v (verbose) to report: - number of loose objects - disk occupied by loose objects - number of objects in local packs - number of loose objects that are also in pack - unrecognised garbage in .git/objects/??/. Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-count.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git.c | 1 + 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 builtin-count.c diff --git a/Makefile b/Makefile index 8ce27a65f..14193aa1e 100644 --- a/Makefile +++ b/Makefile @@ -214,7 +214,7 @@ LIB_OBJS = \ $(DIFF_OBJS) BUILTIN_OBJS = \ - builtin-log.o builtin-help.o + builtin-log.o builtin-help.o builtin-count.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-count.c b/builtin-count.c new file mode 100644 index 000000000..0256369d5 --- /dev/null +++ b/builtin-count.c @@ -0,0 +1,123 @@ +/* + * Builtin "git count-objects". + * + * Copyright (c) 2006 Junio C Hamano + */ + +#include "cache.h" +#include "builtin.h" + +static const char count_objects_usage[] = "git-count-objects [-v]"; + +static void count_objects(DIR *d, char *path, int len, int verbose, + unsigned long *loose, + unsigned long *loose_size, + unsigned long *packed_loose, + unsigned long *garbage) +{ + struct dirent *ent; + while ((ent = readdir(d)) != NULL) { + char hex[41]; + unsigned char sha1[20]; + const char *cp; + int bad = 0; + + if ((ent->d_name[0] == '.') && + (ent->d_name[1] == 0 || + ((ent->d_name[1] == '.') && (ent->d_name[2] == 0)))) + continue; + for (cp = ent->d_name; *cp; cp++) { + int ch = *cp; + if (('0' <= ch && ch <= '9') || + ('a' <= ch && ch <= 'f')) + continue; + bad = 1; + break; + } + if (cp - ent->d_name != 38) + bad = 1; + else { + struct stat st; + memcpy(path + len + 3, ent->d_name, 38); + path[len + 2] = '/'; + path[len + 41] = 0; + if (lstat(path, &st) || !S_ISREG(st.st_mode)) + bad = 1; + else + (*loose_size) += st.st_blocks; + } + if (bad) { + if (verbose) { + error("garbage found: %.*s/%s", + len + 2, path, ent->d_name); + (*garbage)++; + } + continue; + } + (*loose)++; + if (!verbose) + continue; + memcpy(hex, path+len, 2); + memcpy(hex+2, ent->d_name, 38); + hex[40] = 0; + if (get_sha1_hex(hex, sha1)) + die("internal error"); + if (has_sha1_pack(sha1)) + (*packed_loose)++; + } +} + +int cmd_count_objects(int ac, const char **av, char **ep) +{ + int i; + int verbose = 0; + const char *objdir = get_object_directory(); + int len = strlen(objdir); + char *path = xmalloc(len + 50); + unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0; + unsigned long loose_size = 0; + + for (i = 1; i < ac; i++) { + const char *arg = av[i]; + if (*arg != '-') + break; + else if (!strcmp(arg, "-v")) + verbose = 1; + else + usage(count_objects_usage); + } + + /* we do not take arguments other than flags for now */ + if (i < ac) + usage(count_objects_usage); + memcpy(path, objdir, len); + if (len && objdir[len-1] != '/') + path[len++] = '/'; + for (i = 0; i < 256; i++) { + DIR *d; + sprintf(path + len, "%02x", i); + d = opendir(path); + if (!d) + continue; + count_objects(d, path, len, verbose, + &loose, &loose_size, &packed_loose, &garbage); + closedir(d); + } + if (verbose) { + struct packed_git *p; + for (p = packed_git; p; p = p->next) { + if (!p->pack_local) + continue; + packed += num_packed_objects(p); + } + printf("count: %lu\n", loose); + printf("size: %lu\n", loose_size / 2); + printf("in-pack: %lu\n", packed); + printf("prune-packable: %lu\n", packed_loose); + printf("garbage: %lu\n", garbage); + } + else + printf("%lu objects, %lu kilobytes\n", + loose, loose_size / 2); + return 0; +} diff --git a/builtin.h b/builtin.h index 47408a058..76169e3f0 100644 --- a/builtin.h +++ b/builtin.h @@ -19,5 +19,6 @@ extern int cmd_version(int argc, const char **argv, char **envp); extern int cmd_whatchanged(int argc, const char **argv, char **envp); extern int cmd_show(int argc, const char **argv, char **envp); extern int cmd_log(int argc, const char **argv, char **envp); +extern int cmd_count_objects(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 01b7e28b8..00fb39972 100644 --- a/git.c +++ b/git.c @@ -46,6 +46,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "log", cmd_log }, { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, + { "count-objects", cmd_count_objects }, }; int i; From c1d1128bef97ae16a6740538abe5fb81c67ed239 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 28 Apr 2006 02:32:44 -0700 Subject: [PATCH 026/212] git-am --resolved: more usable error message. After doing the hard work of hand resolving the conflicts in the working tree, if the user forgets to run update-index to mark the paths that have been resolved, the command gave an unfriendly "fatal: git-write-tree: not able to write tree" error message. Catch the situation early and give more meaningful message and suggestion. Noticed and suggested by Len Brown. Signed-off-by: Junio C Hamano --- git-am.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/git-am.sh b/git-am.sh index eab4aa891..872145b92 100755 --- a/git-am.sh +++ b/git-am.sh @@ -376,6 +376,13 @@ do echo "No changes - did you forget update-index?" stop_here $this fi + unmerged=$(git-ls-files -u) + if test -n "$unmerged" + then + echo "You still have unmerged paths in your index" + echo "did you forget update-index?" + stop_here $this + fi apply_status=0 ;; esac From d0ad1653662a214baddc1d1ce669a28b91a3ae76 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 28 Apr 2006 10:42:28 +0200 Subject: [PATCH 027/212] annotate: fix warning about uninitialized scalar Use of uninitialized value in scalar chomp at ./git-annotate.perl line 212, <$kid> chunk 4. Signed-off-by: Matthias Kestenholz --- git-annotate.perl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/git-annotate.perl b/git-annotate.perl index 9df72a166..a7aab2566 100755 --- a/git-annotate.perl +++ b/git-annotate.perl @@ -208,6 +208,9 @@ sub find_parent_renames { while (my $change = <$patch>) { chomp $change; my $filename = <$patch>; + if (!defined $filename) { + next; + } chomp $filename; if ($change =~ m/^[AMD]$/ ) { From fe77bb1a02d5b8dd3915cd27a83b660debaecda4 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 28 Apr 2006 10:41:19 +0200 Subject: [PATCH 028/212] annotate: display usage information if no filename was given Signed-off-by: Matthias Kestenholz --- git-annotate.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/git-annotate.perl b/git-annotate.perl index a7aab2566..a6a7a482c 100755 --- a/git-annotate.perl +++ b/git-annotate.perl @@ -10,9 +10,10 @@ use strict; use Getopt::Long; use POSIX qw(strftime gmtime); +use File::Basename qw(basename dirname); sub usage() { - print STDERR 'Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ] + print STDERR "Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ] -l, --long Show long rev (Defaults off) -t, --time @@ -23,7 +24,7 @@ () Use revs from revs-file instead of calling git-rev-list -h, --help This message. -'; +"; exit(1); } @@ -35,7 +36,7 @@ () "help|h" => \$help, "rename|r" => \$rename, "rev-file|S=s" => \$rev_file); -if (!$rc or $help) { +if (!$rc or $help or !@ARGV) { usage(); } From 2eaf273d518717c84d748051e05210656f7b7e88 Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:04 -0400 Subject: [PATCH 029/212] Update the git-branch man page to include the "-r" option, and fix up asciidoc "callouts" Signed-off-by: Sean Estabrooks --- Documentation/git-branch.txt | 57 ++++++++++++++++++++++++------------ git-branch.sh | 2 +- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 71ecd858a..72fb2f89b 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -3,22 +3,27 @@ git-branch(1) NAME ---- -git-branch - Create a new branch, or remove an old one +git-branch - List, create, or delete branches. SYNOPSIS -------- [verse] -'git-branch' [[-f] []] -'git-branch' (-d | -D) +'git-branch' [-r] +'git-branch' [-f] [] +'git-branch' (-d | -D) ... DESCRIPTION ----------- -If no argument is provided, show available branches and mark current -branch with star. Otherwise, create a new branch of name . -If a starting point is also specified, that will be where the branch is -created, otherwise it will be created at the current HEAD. +With no arguments given (or just `-r`) a list of available branches +will be shown, the current branch will be highlighted with an asterisk. -With a `-d` or `-D` option, `` will be deleted. +In its second form, a new branch named will be created. +It will start out with a head equal to the one given as . +If no is given, the branch will be created with a head +equal to that of the currently checked out branch. + +With a `-d` or `-D` option, `` will be deleted. You may +specify more than one branch for deletion. OPTIONS @@ -30,40 +35,56 @@ OPTIONS Delete a branch irrespective of its index status. -f:: - Force a reset of to (or current head). + Force the creation of a new branch even if it means deleting + a branch that already exists with the same name. + +-r:: + List only the "remote" branches. :: The name of the branch to create or delete. :: - Where to create the branch; defaults to HEAD. This - option has no meaning with -d and -D. + The new branch will be created with a HEAD equal to this. It may + be given as a branch name, a commit-id, or a tag. If this option + is omitted, the current branch is assumed. + Examples -~~~~~~~~ +-------- Start development off of a known tag:: + ------------ $ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6 $ cd my2.6 -$ git branch my2.6.14 v2.6.14 <1> +$ git branch my2.6.14 v2.6.14 <1> $ git checkout my2.6.14 - -<1> These two steps are the same as "checkout -b my2.6.14 v2.6.14". ------------ ++ +<1> This step and the next one could be combined into a single step with +"checkout -b my2.6.14 v2.6.14". Delete unneeded branch:: + ------------ $ git clone git://git.kernel.org/.../git.git my.git $ cd my.git -$ git branch -D todo <1> - +$ git branch -D todo <1> +------------ ++ <1> delete todo branch even if the "master" branch does not have all commits from todo branch. ------------- + + +Notes +----- + +If you are creating a branch that you want to immediately checkout, it's +easier to use the git checkout command with its `-b` option to create +a branch and check it out with a single command. + Author ------ diff --git a/git-branch.sh b/git-branch.sh index 663a3a370..ebcc8989d 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -1,6 +1,6 @@ #!/bin/sh -USAGE='[(-d | -D) ] | [[-f] []]' +USAGE='[(-d | -D) ] | [[-f] []] | -r' LONG_USAGE='If no arguments, show available branches and mark current branch with a star. If one argument, create a new branch based off of current HEAD. If two arguments, create a new branch based off of .' From 48aeecdcc14684111ddb8ac0ec3bfdc5245ee75e Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:05 -0400 Subject: [PATCH 030/212] Fix up remaining man pages that use asciidoc "callouts". Unfortunately docbook does not allow a callout to be referenced from inside a callout list description. Rewrite one paragraph in git-reset man page to work around this limitation. Signed-off-by: Sean Estabrooks --- Documentation/everyday.txt | 45 +++++++++---------- Documentation/git-checkout.txt | 18 ++++---- Documentation/git-diff.txt | 38 ++++++++-------- Documentation/git-init-db.txt | 8 ++-- Documentation/git-reset.txt | 72 +++++++++++++++--------------- Documentation/git-update-index.txt | 31 +++++++------ 6 files changed, 104 insertions(+), 108 deletions(-) diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt index 3ab9b916c..4b5637093 100644 --- a/Documentation/everyday.txt +++ b/Documentation/everyday.txt @@ -61,7 +61,8 @@ $ git prune $ git count-objects <2> $ git repack <3> $ git prune <4> - +------------ ++ <1> running without "--full" is usually cheap and assures the repository health reasonably well. <2> check how many loose objects there are and how much @@ -69,17 +70,16 @@ diskspace is wasted by not repacking. <3> without "-a" repacks incrementally. repacking every 4-5MB of loose objects accumulation may be a good rule of thumb. <4> after repack, prune removes the duplicate loose objects. ------------- Repack a small project into single pack.:: + ------------ $ git repack -a -d <1> $ git prune - +------------ ++ <1> pack all the objects reachable from the refs into one pack and remove unneeded other packs ------------- Individual Developer (Standalone)[[Individual Developer (Standalone)]] @@ -129,10 +129,10 @@ $ git-init-db $ git add . <1> $ git commit -m 'import of frotz source tree.' $ git tag v2.43 <2> - +------------ ++ <1> add everything under the current directory. <2> make a lightweight, unannotated tag. ------------- Create a topic branch and develop.:: + @@ -153,7 +153,8 @@ $ git checkout master <9> $ git pull . alsa-audio <10> $ git log --since='3 days ago' <11> $ git log v2.43.. curses/ <12> - +------------ ++ <1> create a new topic branch. <2> revert your botched changes in "curses/ux_audio_oss.c". <3> you need to tell git if you added a new file; removal and @@ -170,7 +171,6 @@ you originally wrote. combined and include --max-count=10 (show 10 commits), --until='2005-12-10'. <12> view only the changes that touch what's in curses/ directory, since v2.43 tag. ------------- Individual Developer (Participant)[[Individual Developer (Participant)]] @@ -208,7 +208,8 @@ $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5> $ git reset --hard ORIG_HEAD <6> $ git prune <7> $ git fetch --tags <8> - +------------ ++ <1> repeat as needed. <2> extract patches from your branch for e-mail submission. <3> "pull" fetches from "origin" by default and merges into the @@ -221,7 +222,6 @@ area we are interested in. <7> garbage collect leftover objects from reverted pull. <8> from time to time, obtain official tags from the "origin" and store them under .git/refs/tags/. ------------- Push into another repository.:: @@ -239,7 +239,8 @@ satellite$ git push origin <4> mothership$ cd frotz mothership$ git checkout master mothership$ git pull . satellite <5> - +------------ ++ <1> mothership machine has a frotz repository under your home directory; clone from it to start a repository on the satellite machine. @@ -252,7 +253,6 @@ to local "origin" branch. mothership machine. You could use this as a back-up method. <5> on mothership machine, merge the work done on the satellite machine into the master branch. ------------- Branch off of a specific tag.:: + @@ -262,12 +262,12 @@ $ edit/compile/test; git commit -a $ git checkout master $ git format-patch -k -m --stdout v2.6.14..private2.6.14 | git am -3 -k <2> - +------------ ++ <1> create a private branch based on a well known (but somewhat behind) tag. <2> forward port all changes in private2.6.14 branch to master branch without a formal "merging". ------------- Integrator[[Integrator]] @@ -317,7 +317,8 @@ $ git tag -s -m 'GIT 0.99.9x' v0.99.9x <10> $ git fetch ko && git show-branch master maint 'tags/ko-*' <11> $ git push ko <12> $ git push ko v0.99.9x <13> - +------------ ++ <1> see what I was in the middle of doing, if any. <2> see what topic branches I have and think about how ready they are. @@ -346,7 +347,6 @@ In the output from "git show-branch", "master" should have everything "ko-master" has. <12> push out the bleeding edge. <13> push the tag out, too. ------------- Repository Administration[[Repository Administration]] @@ -367,7 +367,6 @@ example of managing a shared central repository. Examples ~~~~~~~~ - Run git-daemon to serve /pub/scm from inetd.:: + ------------ @@ -388,13 +387,13 @@ cindy:x:1002:1002::/home/cindy:/usr/bin/git-shell david:x:1003:1003::/home/david:/usr/bin/git-shell $ grep git /etc/shells <2> /usr/bin/git-shell - +------------ ++ <1> log-in shell is set to /usr/bin/git-shell, which does not allow anything but "git push" and "git pull". The users should get an ssh access to the machine. <2> in many distributions /etc/shells needs to list what is used as the login shell. ------------- CVS-style shared repository.:: + @@ -419,7 +418,8 @@ $ cat info/allowed-users <4> refs/heads/master alice\|cindy refs/heads/doc-update bob refs/tags/v[0-9]* david - +------------ ++ <1> place the developers into the same git group. <2> and make the shared repository writable by the group. <3> use update-hook example by Carl from Documentation/howto/ @@ -427,7 +427,6 @@ for branch policy control. <4> alice and cindy can push into master, only bob can push into doc-update. david is the release manager and is the only person who can create and push version tags. ------------- HTTP server to support dumb protocol transfer.:: + @@ -435,7 +434,7 @@ HTTP server to support dumb protocol transfer.:: dev$ git update-server-info <1> dev$ ftp user@isp.example.com <2> ftp> cp -r .git /home/user/myproject.git - +------------ ++ <1> make sure your info/refs and objects/info/packs are up-to-date <2> upload to public HTTP server hosted by your ISP. ------------- diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 985bb2f82..095128906 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -66,19 +66,19 @@ the `Makefile` to two revisions back, deletes hello.c by mistake, and gets it back from the index. + ------------ -$ git checkout master <1> -$ git checkout master~2 Makefile <2> +$ git checkout master <1> +$ git checkout master~2 Makefile <2> $ rm -f hello.c -$ git checkout hello.c <3> - +$ git checkout hello.c <3> +------------ ++ <1> switch branch <2> take out a file out of other commit -<3> or "git checkout -- hello.c", as in the next example. ------------- +<3> restore hello.c from HEAD of current branch + -If you have an unfortunate branch that is named `hello.c`, the -last step above would be confused as an instruction to switch to -that branch. You should instead write: +If you have an unfortunate branch that is named `hello.c`, this +step would be confused as an instruction to switch to that branch. +You should instead write: + ------------ $ git checkout -- hello.c diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt index 890931c89..7267bcd7a 100644 --- a/Documentation/git-diff.txt +++ b/Documentation/git-diff.txt @@ -46,40 +46,41 @@ EXAMPLES Various ways to check your working tree:: + ------------ -$ git diff <1> -$ git diff --cached <2> -$ git diff HEAD <3> - +$ git diff <1> +$ git diff --cached <2> +$ git diff HEAD <3> +------------ ++ <1> changes in the working tree since your last git-update-index. <2> changes between the index and your last commit; what you would be committing if you run "git commit" without "-a" option. <3> changes in the working tree since your last commit; what you would be committing if you run "git commit -a" ------------- Comparing with arbitrary commits:: + ------------ -$ git diff test <1> -$ git diff HEAD -- ./test <2> -$ git diff HEAD^ HEAD <3> - +$ git diff test <1> +$ git diff HEAD -- ./test <2> +$ git diff HEAD^ HEAD <3> +------------ ++ <1> instead of using the tip of the current branch, compare with the tip of "test" branch. <2> instead of comparing with the tip of "test" branch, compare with the tip of the current branch, but limit the comparison to the file "test". <3> compare the version before the last commit and the last commit. ------------- Limiting the diff output:: + ------------ -$ git diff --diff-filter=MRC <1> -$ git diff --name-status -r <2> -$ git diff arch/i386 include/asm-i386 <3> - +$ git diff --diff-filter=MRC <1> +$ git diff --name-status -r <2> +$ git diff arch/i386 include/asm-i386 <3> +------------ ++ <1> show only modification, rename and copy, but not addition nor deletion. <2> show only names and the nature of change, but not actual @@ -88,18 +89,17 @@ which in turn also disables recursive behaviour, so without -r you would only see the directory name if there is a change in a file in a subdirectory. <3> limit diff output to named subtrees. ------------- Munging the diff output:: + ------------ -$ git diff --find-copies-harder -B -C <1> -$ git diff -R <2> - +$ git diff --find-copies-harder -B -C <1> +$ git diff -R <2> +------------ ++ <1> spend extra cycles to find renames, copies and complete rewrites (very expensive). <2> output diff in reverse. ------------- Author diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt index aeb1115af..8a150d861 100644 --- a/Documentation/git-init-db.txt +++ b/Documentation/git-init-db.txt @@ -60,12 +60,12 @@ Start a new git repository for an existing code base:: + ---------------- $ cd /path/to/my/codebase -$ git-init-db <1> -$ git-add . <2> - +$ git-init-db <1> +$ git-add . <2> +---------------- ++ <1> prepare /path/to/my/codebase/.git directory <2> add all existing file to the index ----------------- Author diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index b7b9798bf..ebcfe5edb 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -49,10 +49,11 @@ Undo a commit and redo:: + ------------ $ git commit ... -$ git reset --soft HEAD^ <1> -$ edit <2> -$ git commit -a -c ORIG_HEAD <3> - +$ git reset --soft HEAD^ <1> +$ edit <2> +$ git commit -a -c ORIG_HEAD <3> +------------ ++ <1> This is most often done when you remembered what you just committed is incomplete, or you misspelled your commit message, or both. Leaves working tree as it was before "reset". @@ -60,43 +61,43 @@ message, or both. Leaves working tree as it was before "reset". <3> "reset" copies the old head to .git/ORIG_HEAD; redo the commit by starting with its log message. If you do not need to edit the message further, you can give -C option instead. ------------- Undo commits permanently:: + ------------ $ git commit ... -$ git reset --hard HEAD~3 <1> - +$ git reset --hard HEAD~3 <1> +------------ ++ <1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad and you do not want to ever see them again. Do *not* do this if you have already given these commits to somebody else. ------------- Undo a commit, making it a topic branch:: + ------------ -$ git branch topic/wip <1> -$ git reset --hard HEAD~3 <2> -$ git checkout topic/wip <3> - +$ git branch topic/wip <1> +$ git reset --hard HEAD~3 <2> +$ git checkout topic/wip <3> +------------ ++ <1> You have made some commits, but realize they were premature to be in the "master" branch. You want to continue polishing them in a topic branch, so create "topic/wip" branch off of the current HEAD. <2> Rewind the master branch to get rid of those three commits. <3> Switch to "topic/wip" branch and keep working. ------------- Undo update-index:: + ------------ -$ edit <1> +$ edit <1> $ git-update-index frotz.c filfre.c -$ mailx <2> -$ git reset <3> -$ git pull git://info.example.com/ nitfol <4> - +$ mailx <2> +$ git reset <3> +$ git pull git://info.example.com/ nitfol <4> +------------ ++ <1> you are happily working on something, and find the changes in these files are in good order. You do not want to see them when you run "git diff", because you plan to work on other files @@ -109,12 +110,11 @@ index changes for these two files. Your changes in working tree remain there. <4> then you can pull and merge, leaving frotz.c and filfre.c changes still in the working tree. ------------- Undo a merge or pull:: + ------------ -$ git pull <1> +$ git pull <1> Trying really trivial in-index merge... fatal: Merge requires file-level merging Nope. @@ -122,20 +122,19 @@ Nope. Auto-merging nitfol CONFLICT (content): Merge conflict in nitfol Automatic merge failed/prevented; fix up by hand -$ git reset --hard <2> - +$ git reset --hard <2> +$ git pull . topic/branch <3> +Updating from 41223... to 13134... +Fast forward +$ git reset --hard ORIG_HEAD <4> +------------ ++ <1> try to update from the upstream resulted in a lot of conflicts; you were not ready to spend a lot of time merging right now, so you decide to do that later. <2> "pull" has not made merge commit, so "git reset --hard" which is a synonym for "git reset --hard HEAD" clears the mess from the index file and the working tree. - -$ git pull . topic/branch <3> -Updating from 41223... to 13134... -Fast forward -$ git reset --hard ORIG_HEAD <4> - <3> merge a topic branch into the current branch, which resulted in a fast forward. <4> but you decided that the topic branch is not ready for public @@ -143,7 +142,6 @@ consumption yet. "pull" or "merge" always leaves the original tip of the current branch in ORIG_HEAD, so resetting hard to it brings your index file and the working tree back to that state, and resets the tip of the branch to that commit. ------------- Interrupted workflow:: + @@ -155,21 +153,21 @@ need to get to the other branch for a quick bugfix. ------------ $ git checkout feature ;# you were working in "feature" branch and $ work work work ;# got interrupted -$ git commit -a -m 'snapshot WIP' <1> +$ git commit -a -m 'snapshot WIP' <1> $ git checkout master $ fix fix fix $ git commit ;# commit with real log $ git checkout feature -$ git reset --soft HEAD^ ;# go back to WIP state <2> -$ git reset <3> - +$ git reset --soft HEAD^ ;# go back to WIP state <2> +$ git reset <3> +------------ ++ <1> This commit will get blown away so a throw-away log message is OK. <2> This removes the 'WIP' commit from the commit history, and sets your working tree to the state just before you made that snapshot. -<3> After <2>, the index file still has all the WIP changes you - committed in <1>. This sets it to the last commit you were - basing the WIP changes on. ------------- +<3> At this point the index file still has all the WIP changes you + committed as 'snapshot WIP'. This updates the index to show your + WIP files as uncommitted. Author ------ diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 0a1b0ad56..d4137fc87 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -247,34 +247,33 @@ To update and refresh only the files already checked out: $ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh ---------------- -On an inefficient filesystem with `core.ignorestat` set: - +On an inefficient filesystem with `core.ignorestat` set:: ++ ------------ -$ git update-index --really-refresh <1> -$ git update-index --no-assume-unchanged foo.c <2> -$ git diff --name-only <3> +$ git update-index --really-refresh <1> +$ git update-index --no-assume-unchanged foo.c <2> +$ git diff --name-only <3> $ edit foo.c -$ git diff --name-only <4> +$ git diff --name-only <4> M foo.c -$ git update-index foo.c <5> -$ git diff --name-only <6> +$ git update-index foo.c <5> +$ git diff --name-only <6> $ edit foo.c -$ git diff --name-only <7> -$ git update-index --no-assume-unchanged foo.c <8> -$ git diff --name-only <9> +$ git diff --name-only <7> +$ git update-index --no-assume-unchanged foo.c <8> +$ git diff --name-only <9> M foo.c - -<1> forces lstat(2) to set "assume unchanged" bits for paths - that match index. +------------ ++ +<1> forces lstat(2) to set "assume unchanged" bits for paths that match index. <2> mark the path to be edited. <3> this does lstat(2) and finds index matches the path. -<4> this does lstat(2) and finds index does not match the path. +<4> this does lstat(2) and finds index does *not* match the path. <5> registering the new version to index sets "assume unchanged" bit. <6> and it is assumed unchanged. <7> even after you edit it. <8> you can tell about the change after the fact. <9> now it checks with lstat(2) and finds it has been changed. ------------- Configuration From 776e994af5b85d77424581bda37fb615897063cc Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:06 -0400 Subject: [PATCH 031/212] Properly render asciidoc "callouts" in git man pages. Adds an xsl fragment to render docbook callouts when converting to man page format. Update the Makefile to have "xmlto" use it when generating man pages. Signed-off-by: Sean Estabrooks --- Documentation/Makefile | 2 +- Documentation/callouts.xsl | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Documentation/callouts.xsl diff --git a/Documentation/Makefile b/Documentation/Makefile index f4cbf7e15..c1af22ce0 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -79,7 +79,7 @@ clean: asciidoc -b xhtml11 -d manpage -f asciidoc.conf $< %.1 %.7 : %.xml - xmlto man $< + xmlto -m callouts.xsl man $< %.xml : %.txt asciidoc -b docbook -d manpage -f asciidoc.conf $< diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl new file mode 100644 index 000000000..ad03755d8 --- /dev/null +++ b/Documentation/callouts.xsl @@ -0,0 +1,16 @@ + + + + + + + .sp + + + + + + + .br + + From aa6bf0eb6489d652c5877d65160ed33c857afa74 Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:03 -0400 Subject: [PATCH 032/212] Fix trivial typo in git-log man page. Signed-off-by: Sean Estabrooks --- Documentation/git-log.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 76cb894ca..af378ffcf 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -14,13 +14,12 @@ DESCRIPTION ----------- Shows the commit logs. -The command takes options applicable to the gitlink::git-rev-list[1] +The command takes options applicable to the gitlink:git-rev-list[1] command to control what is shown and how, and options applicable to -the gitlink::git-diff-tree[1] commands to control how the change +the gitlink:git-diff-tree[1] commands to control how the change each commit introduces are shown. -This manual page describes only the most frequently used -options. +This manual page describes only the most frequently used options. OPTIONS From 3dc5a9e4cdcc7124c665a050547d1285d86a421f Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sat, 29 Apr 2006 00:58:05 -0400 Subject: [PATCH 033/212] replace adler32 with Rabin's polynomial in diff-delta This brings another small repacking speedup for sensibly the same pack size. On the Linux kernel repo, git-repack -a -f is 3.7% faster for a 0.4% larger pack. Credits to Geert Bosch who brought the Rabin's polynomial idea to my attention. This also eliminate the issue of adler32() reading past the data buffer, as noticed by Johannes Schindelin. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- Makefile | 2 +- diff-delta.c | 184 +++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 151 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index d9a3a82fe..38c980bfb 100644 --- a/Makefile +++ b/Makefile @@ -609,7 +609,7 @@ test-date$X: test-date.c date.o ctype.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o test-delta$X: test-delta.c diff-delta.o patch-delta.o - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ check: for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done diff --git a/diff-delta.c b/diff-delta.c index fdedf94cc..35e517d2d 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -20,18 +20,106 @@ #include #include -#include #include "delta.h" -/* block size: min = 16, max = 64k, power of 2 */ -#define BLK_SIZE 16 - /* maximum hash entry list for the same hash bucket */ #define HASH_LIMIT 64 -#define GR_PRIME 0x9e370001 -#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift)) +#define RABIN_SHIFT 23 +#define RABIN_WINDOW 16 + +static const unsigned int T[256] = { + 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344, + 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259, + 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85, + 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2, + 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a, + 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db, + 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753, + 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964, + 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8, + 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5, + 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81, + 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6, + 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e, + 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77, + 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff, + 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8, + 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc, + 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1, + 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d, + 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a, + 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2, + 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02, + 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a, + 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd, + 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61, + 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c, + 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08, + 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f, + 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7, + 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe, + 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76, + 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141, + 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65, + 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78, + 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4, + 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93, + 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b, + 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa, + 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872, + 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645, + 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99, + 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84, + 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811 +}; + +static const unsigned int U[256] = { + 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a, + 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48, + 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511, + 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d, + 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8, + 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe, + 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb, + 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937, + 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e, + 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c, + 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d, + 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1, + 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4, + 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa, + 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef, + 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263, + 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302, + 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000, + 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59, + 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5, + 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90, + 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7, + 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2, + 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e, + 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467, + 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765, + 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604, + 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88, + 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd, + 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3, + 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996, + 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a, + 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b, + 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609, + 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50, + 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc, + 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99, + 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf, + 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa, + 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176, + 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f, + 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d, + 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a +}; struct index_entry { const unsigned char *ptr; @@ -42,13 +130,13 @@ struct index_entry { struct delta_index { const void *src_buf; unsigned long src_size; - unsigned int hash_shift; + unsigned int hash_mask; struct index_entry *hash[0]; }; struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) { - unsigned int i, hsize, hshift, entries, *hash_count; + unsigned int i, hsize, hmask, entries, *hash_count; const unsigned char *data, *buffer = buf; struct delta_index *index; struct index_entry *entry, **hash; @@ -57,12 +145,14 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) if (!buf || !bufsize) return NULL; - /* determine index hash size */ - entries = bufsize / BLK_SIZE; + /* Determine index hash size. Note that indexing skips the + first byte to allow for optimizing the rabin polynomial + initialization in create_delta(). */ + entries = (bufsize - 1) / RABIN_WINDOW; hsize = entries / 4; for (i = 4; (1 << i) < hsize && i < 31; i++); hsize = 1 << i; - hshift = 32 - i; + hmask = hsize - 1; /* allocate lookup index */ mem = malloc(sizeof(*index) + @@ -78,7 +168,7 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) index->src_buf = buf; index->src_size = bufsize; - index->hash_shift = hshift; + index->hash_mask = hmask; memset(hash, 0, hsize * sizeof(*hash)); /* allocate an array to count hash entries */ @@ -89,17 +179,19 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize) } /* then populate the index */ - data = buffer + entries * BLK_SIZE - BLK_SIZE; + data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW; while (data >= buffer) { - unsigned int val = adler32(0, data, BLK_SIZE); - i = HASH(val, hshift); - entry->ptr = data; + unsigned int val = 0; + for (i = 1; i <= RABIN_WINDOW; i++) + val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT]; + i = val & hmask; + entry->ptr = data + RABIN_WINDOW; entry->val = val; entry->next = hash[i]; hash[i] = entry++; hash_count[i]++; - data -= BLK_SIZE; - } + data -= RABIN_WINDOW; + } /* * Determine a limit on the number of entries in the same hash @@ -136,20 +228,18 @@ void free_delta_index(struct delta_index *index) free(index); } -/* provide the size of the copy opcode given the block offset and size */ -#define COPYOP_SIZE(o, s) \ - (!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \ - !!(s & 0xff) + !!(s & 0xff00) + 1) - -/* the maximum size for any opcode */ -#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff) +/* + * The maximum size for any opcode sequence, including the initial header + * plus rabin window plus biggest copy. + */ +#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7) void * create_delta(const struct delta_index *index, const void *trg_buf, unsigned long trg_size, unsigned long *delta_size, unsigned long max_size) { - unsigned int i, outpos, outsize, hash_shift; + unsigned int i, outpos, outsize, hash_mask, val; int inscnt; const unsigned char *ref_data, *ref_top, *data, *top; unsigned char *out; @@ -185,14 +275,22 @@ create_delta(const struct delta_index *index, ref_top = ref_data + index->src_size; data = trg_buf; top = trg_buf + trg_size; - hash_shift = index->hash_shift; - inscnt = 0; + hash_mask = index->hash_mask; + + outpos++; + val = 0; + for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) { + out[outpos++] = *data; + val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT]; + } + inscnt = i; while (data < top) { unsigned int moff = 0, msize = 0; struct index_entry *entry; - unsigned int val = adler32(0, data, BLK_SIZE); - i = HASH(val, hash_shift); + val ^= U[data[-RABIN_WINDOW]]; + val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT]; + i = val & hash_mask; for (entry = index->hash[i]; entry; entry = entry->next) { const unsigned char *ref = entry->ptr; const unsigned char *src = data; @@ -214,7 +312,7 @@ create_delta(const struct delta_index *index, } } - if (!msize || msize < COPYOP_SIZE(moff, msize)) { + if (msize < 4) { if (!inscnt) outpos++; out[outpos++] = *data++; @@ -226,6 +324,20 @@ create_delta(const struct delta_index *index, } else { unsigned char *op; + if (msize >= RABIN_WINDOW) { + const unsigned char *sk; + sk = data + msize - RABIN_WINDOW; + val = 0; + for (i = 0; i < RABIN_WINDOW; i++) + val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT]; + } else { + const unsigned char *sk = data + 1; + for (i = 1; i < msize; i++) { + val ^= U[sk[-RABIN_WINDOW]]; + val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT]; + } + } + if (inscnt) { while (moff && ref_data[moff-1] == data[-1]) { if (msize == 0x10000) @@ -270,9 +382,8 @@ create_delta(const struct delta_index *index, if (max_size && outsize >= max_size) outsize = max_size + MAX_OP_SIZE + 1; if (max_size && outpos > max_size) - out = NULL; - else - out = realloc(out, outsize); + break; + out = realloc(out, outsize); if (!out) { free(tmp); return NULL; @@ -283,6 +394,11 @@ create_delta(const struct delta_index *index, if (inscnt) out[outpos - inscnt - 1] = inscnt; + if (max_size && outpos > max_size) { + free(out); + return NULL; + } + *delta_size = outpos; return out; } From 65056021f2d2dcb7a72f05c5d6cbbd79a79b9d40 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 28 Apr 2006 23:20:52 -0700 Subject: [PATCH 034/212] built-in diff. This starts to replace the shell script version of "git diff". Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-diff.c | 332 +++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git.c | 1 + 4 files changed, 335 insertions(+), 1 deletion(-) create mode 100644 builtin-diff.c diff --git a/Makefile b/Makefile index 8ce27a65f..277c1ac07 100644 --- a/Makefile +++ b/Makefile @@ -214,7 +214,7 @@ LIB_OBJS = \ $(DIFF_OBJS) BUILTIN_OBJS = \ - builtin-log.o builtin-help.o + builtin-log.o builtin-help.o builtin-diff.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-diff.c b/builtin-diff.c new file mode 100644 index 000000000..196821294 --- /dev/null +++ b/builtin-diff.c @@ -0,0 +1,332 @@ +/* + * Builtin "git diff" + * + * Copyright (c) 2006 Junio C Hamano + */ +#include "cache.h" +#include "commit.h" +#include "blob.h" +#include "tag.h" +#include "diff.h" +#include "diffcore.h" +#include "revision.h" +#include "log-tree.h" +#include "builtin.h" + +/* NEEDSWORK: struct object has place for name but we _do_ + * know mode when we extracted the blob out of a tree, which + * we currently lose. + */ +struct blobinfo { + unsigned char sha1[20]; + const char *name; +}; + +static const char builtin_diff_usage[] = +"diff {0,2} -- *"; + +static int builtin_diff_files(struct rev_info *revs, + int argc, const char **argv) +{ + int silent = 0; + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--base")) + revs->max_count = 1; + else if (!strcmp(arg, "--ours")) + revs->max_count = 2; + else if (!strcmp(arg, "--theirs")) + revs->max_count = 3; + else if (!strcmp(arg, "-q")) + silent = 1; + else if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + /* + * Make sure there are NO revision (i.e. pending object) parameter, + * rev.max_count is reasonable (0 <= n <= 3), + * there is no other revision filtering parameters. + */ + if (revs->pending_objects || + revs->min_age != -1 || + revs->max_age != -1) + usage(builtin_diff_usage); + /* + * Backward compatibility wart - "diff-files -s" used to + * defeat the common diff option "-s" which asked for + * DIFF_FORMAT_NO_OUTPUT. + */ + if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + return run_diff_files(revs, silent); +} + +static void stuff_change(struct diff_options *opt, + unsigned old_mode, unsigned new_mode, + const unsigned char *old_sha1, + const unsigned char *new_sha1, + const char *old_name, + const char *new_name) +{ + struct diff_filespec *one, *two; + + if (memcmp(null_sha1, old_sha1, 20) && + memcmp(null_sha1, new_sha1, 20) && + !memcmp(old_sha1, new_sha1, 20)) + return; + + if (opt->reverse_diff) { + unsigned tmp; + const + const unsigned char *tmp_u; + const char *tmp_c; + tmp = old_mode; old_mode = new_mode; new_mode = tmp; + tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u; + tmp_c = old_name; old_name = new_name; new_name = tmp_c; + } + one = alloc_filespec(old_name); + two = alloc_filespec(new_name); + fill_filespec(one, old_sha1, old_mode); + fill_filespec(two, new_sha1, new_mode); + + /* NEEDSWORK: shouldn't this part of diffopt??? */ + diff_queue(&diff_queued_diff, one, two); +} + +static int builtin_diff_b_f(struct rev_info *revs, + int argc, const char **argv, + struct blobinfo *blob, + const char *path) +{ + /* Blob vs file in the working tree*/ + struct stat st; + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + if (lstat(path, &st)) + die("'%s': %s", path, strerror(errno)); + if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) + die("'%s': not a regular file or symlink", path); + stuff_change(&revs->diffopt, + canon_mode(st.st_mode), canon_mode(st.st_mode), + blob[0].sha1, null_sha1, + blob[0].name, path); + diffcore_std(&revs->diffopt); + diff_flush(&revs->diffopt); + return 0; +} + +static int builtin_diff_blobs(struct rev_info *revs, + int argc, const char **argv, + struct blobinfo *blob) +{ + /* Blobs */ + unsigned mode = canon_mode(S_IFREG | 0644); + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + stuff_change(&revs->diffopt, + mode, mode, + blob[0].sha1, blob[1].sha1, + blob[1].name, blob[1].name); + diffcore_std(&revs->diffopt); + diff_flush(&revs->diffopt); + return 0; +} + +static int builtin_diff_index(struct rev_info *revs, + int argc, const char **argv) +{ + int cached = 0; + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--cached")) + cached = 1; + else if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + /* + * Make sure there is one revision (i.e. pending object), + * and there is no revision filtering parameters. + */ + if (!revs->pending_objects || revs->pending_objects->next || + revs->max_count != -1 || revs->min_age != -1 || + revs->max_age != -1) + usage(builtin_diff_usage); + return run_diff_index(revs, cached); +} + +static int builtin_diff_tree(struct rev_info *revs, + int argc, const char **argv, + struct object_list *ent) +{ + /* We saw two trees, ent[0] and ent[1]. + * unless ent[0] is unintesting, they are swapped + */ + const unsigned char *(sha1[2]); + int swap = 1; + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + if (ent[0].item->flags & UNINTERESTING) + swap = 0; + sha1[swap] = ent[0].item->sha1; + sha1[1-swap] = ent[1].item->sha1; + diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt); + log_tree_diff_flush(revs); + return 0; +} + +static void add_head(struct rev_info *revs) +{ + unsigned char sha1[20]; + struct object *obj; + if (get_sha1("HEAD", sha1)) + return; + obj = parse_object(sha1); + if (!obj) + return; + add_object(obj, &revs->pending_objects, NULL, "HEAD"); +} + +int cmd_diff(int argc, const char **argv, char **envp) +{ + struct rev_info rev; + struct object_list *list, ent[2]; + int ents = 0, blobs = 0, paths = 0; + const char *path = NULL; + struct blobinfo blob[2]; + + /* + * We could get N tree-ish in the rev.pending_objects list. + * Also there could be M blobs there, and P pathspecs. + * + * N=0, M=0: + * cache vs files (diff-files) + * N=0, M=2: + * compare two random blobs. P must be zero. + * N=0, M=1, P=1: + * compare a blob with a working tree file. + * + * N=1, M=0: + * tree vs cache (diff-index --cached) + * + * N=2, M=0: + * tree vs tree (diff-tree) + * + * Other cases are errors. + */ + + git_config(git_diff_config); + init_revisions(&rev); + rev.diffopt.output_format = DIFF_FORMAT_PATCH; + + argc = setup_revisions(argc, argv, &rev, NULL); + /* Do we have --cached and not have a pending object, then + * default to HEAD by hand. Eek. + */ + if (!rev.pending_objects) { + int i; + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (!strcmp(arg, "--")) + break; + else if (!strcmp(arg, "--cached")) { + add_head(&rev); + break; + } + } + } + + for (list = rev.pending_objects; list; list = list->next) { + struct object *obj = list->item; + const char *name = list->name; + int flags = (obj->flags & UNINTERESTING); + if (!obj->parsed) + obj = parse_object(obj->sha1); + obj = deref_tag(obj, NULL, 0); + if (!obj) + die("invalid object '%s' given.", name); + if (!strcmp(obj->type, commit_type)) + obj = &((struct commit *)obj)->tree->object; + if (!strcmp(obj->type, tree_type)) { + if (2 <= ents) + die("more than two trees given: '%s'", name); + obj->flags |= flags; + ent[ents].item = obj; + ent[ents].name = name; + ents++; + continue; + } + if (!strcmp(obj->type, blob_type)) { + if (2 <= blobs) + die("more than two blobs given: '%s'", name); + memcpy(blob[blobs].sha1, obj->sha1, 20); + blob[blobs].name = name; + blobs++; + continue; + + } + die("unhandled object '%s' given.", name); + } + if (rev.prune_data) { + const char **pathspec = rev.prune_data; + while (*pathspec) { + if (!path) + path = *pathspec; + paths++; + pathspec++; + } + } + + /* + * Now, do the arguments look reasonable? + */ + if (!ents) { + switch (blobs) { + case 0: + return builtin_diff_files(&rev, argc, argv); + break; + case 1: + if (paths != 1) + usage(builtin_diff_usage); + return builtin_diff_b_f(&rev, argc, argv, blob, path); + break; + case 2: + return builtin_diff_blobs(&rev, argc, argv, blob); + break; + default: + usage(builtin_diff_usage); + } + } + else if (blobs) + usage(builtin_diff_usage); + else if (ents == 1) + return builtin_diff_index(&rev, argc, argv); + else if (ents == 2) + return builtin_diff_tree(&rev, argc, argv, ent); + usage(builtin_diff_usage); +} diff --git a/builtin.h b/builtin.h index 47408a058..52ffa526f 100644 --- a/builtin.h +++ b/builtin.h @@ -19,5 +19,6 @@ extern int cmd_version(int argc, const char **argv, char **envp); extern int cmd_whatchanged(int argc, const char **argv, char **envp); extern int cmd_show(int argc, const char **argv, char **envp); extern int cmd_log(int argc, const char **argv, char **envp); +extern int cmd_diff(int argc, const char **argv, char **envp); #endif diff --git a/git.c b/git.c index 01b7e28b8..ff9b87abb 100644 --- a/git.c +++ b/git.c @@ -46,6 +46,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "log", cmd_log }, { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, + { "diffn", cmd_diff }, }; int i; From 0fe7c1de16f71312e6adac4b85bddf0d62a47168 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 29 Apr 2006 01:24:49 -0700 Subject: [PATCH 035/212] built-in diff: assorted updates. "git diff(n)" without --base, --ours, etc. defaults to --cc, which usually is the same as -p unless you are in the middle of a conflicted merge, just like the shell script version. "git diff(n) blobA blobB path" complains and dies. "git diff(n) tree0 tree1 tree2...treeN" does combined diff that shows a merge of tree1..treeN to result in tree0. Giving "-c" option to any command that defaults to "--cc" turns off dense-combined flag. Signed-off-by: Junio C Hamano --- builtin-diff.c | 55 +++++++++++++++++++++++++++++++++++++++++--------- combine-diff.c | 47 ++++++++++++++++++++++++++---------------- diff.h | 2 ++ revision.c | 1 + 4 files changed, 79 insertions(+), 26 deletions(-) diff --git a/builtin-diff.c b/builtin-diff.c index 196821294..c543105b8 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -47,13 +47,17 @@ static int builtin_diff_files(struct rev_info *revs, } /* * Make sure there are NO revision (i.e. pending object) parameter, - * rev.max_count is reasonable (0 <= n <= 3), - * there is no other revision filtering parameters. + * specified rev.max_count is reasonable (0 <= n <= 3), and + * there is no other revision filtering parameter. */ if (revs->pending_objects || revs->min_age != -1 || - revs->max_age != -1) + revs->max_age != -1 || + 3 < revs->max_count) usage(builtin_diff_usage); + if (revs->max_count < 0 && + (revs->diffopt.output_format == DIFF_FORMAT_PATCH)) + revs->combine_merges = revs->dense_combined_merges = 1; /* * Backward compatibility wart - "diff-files -s" used to * defeat the common diff option "-s" which asked for @@ -178,9 +182,6 @@ static int builtin_diff_tree(struct rev_info *revs, int argc, const char **argv, struct object_list *ent) { - /* We saw two trees, ent[0] and ent[1]. - * unless ent[0] is unintesting, they are swapped - */ const unsigned char *(sha1[2]); int swap = 1; while (1 < argc) { @@ -191,6 +192,10 @@ static int builtin_diff_tree(struct rev_info *revs, usage(builtin_diff_usage); argv++; argc--; } + + /* We saw two trees, ent[0] and ent[1]. + * unless ent[0] is unintesting, they are swapped + */ if (ent[0].item->flags & UNINTERESTING) swap = 0; sha1[swap] = ent[0].item->sha1; @@ -200,6 +205,33 @@ static int builtin_diff_tree(struct rev_info *revs, return 0; } +static int builtin_diff_combined(struct rev_info *revs, + int argc, const char **argv, + struct object_list *ent, + int ents) +{ + const unsigned char (*parent)[20]; + int i; + + while (1 < argc) { + const char *arg = argv[1]; + if (!strcmp(arg, "--raw")) + revs->diffopt.output_format = DIFF_FORMAT_RAW; + else + usage(builtin_diff_usage); + argv++; argc--; + } + if (!revs->dense_combined_merges && !revs->combine_merges) + revs->dense_combined_merges = revs->combine_merges = 1; + parent = xmalloc(ents * sizeof(*parent)); + /* Again, the revs are all reverse */ + for (i = 0; i < ents; i++) + memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20); + diff_tree_combined(parent[0], parent + 1, ents - 1, + revs->dense_combined_merges, revs); + return 0; +} + static void add_head(struct rev_info *revs) { unsigned char sha1[20]; @@ -215,7 +247,7 @@ static void add_head(struct rev_info *revs) int cmd_diff(int argc, const char **argv, char **envp) { struct rev_info rev; - struct object_list *list, ent[2]; + struct object_list *list, ent[100]; int ents = 0, blobs = 0, paths = 0; const char *path = NULL; struct blobinfo blob[2]; @@ -273,8 +305,9 @@ int cmd_diff(int argc, const char **argv, char **envp) if (!strcmp(obj->type, commit_type)) obj = &((struct commit *)obj)->tree->object; if (!strcmp(obj->type, tree_type)) { - if (2 <= ents) - die("more than two trees given: '%s'", name); + if (ARRAY_SIZE(ent) <= ents) + die("more than %d trees given: '%s'", + ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; @@ -316,6 +349,8 @@ int cmd_diff(int argc, const char **argv, char **envp) return builtin_diff_b_f(&rev, argc, argv, blob, path); break; case 2: + if (paths) + usage(builtin_diff_usage); return builtin_diff_blobs(&rev, argc, argv, blob); break; default: @@ -328,5 +363,7 @@ int cmd_diff(int argc, const char **argv, char **envp) return builtin_diff_index(&rev, argc, argv); else if (ents == 2) return builtin_diff_tree(&rev, argc, argv, ent); + else + return builtin_diff_combined(&rev, argc, argv, ent, ents); usage(builtin_diff_usage); } diff --git a/combine-diff.c b/combine-diff.c index ca36f5d5e..8a8fe3863 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -831,15 +831,16 @@ void show_combined_diff(struct combine_diff_path *p, } } -void diff_tree_combined_merge(const unsigned char *sha1, - int dense, struct rev_info *rev) +void diff_tree_combined(const unsigned char *sha1, + const unsigned char parent[][20], + int num_parent, + int dense, + struct rev_info *rev) { struct diff_options *opt = &rev->diffopt; - struct commit *commit = lookup_commit(sha1); struct diff_options diffopts; - struct commit_list *parents; struct combine_diff_path *p, *paths = NULL; - int num_parent, i, num_paths; + int i, num_paths; int do_diffstat; do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT || @@ -849,17 +850,8 @@ void diff_tree_combined_merge(const unsigned char *sha1, diffopts.with_stat = 0; diffopts.recursive = 1; - /* count parents */ - for (parents = commit->parents, num_parent = 0; - parents; - parents = parents->next, num_parent++) - ; /* nothing */ - /* find set of paths that everybody touches */ - for (parents = commit->parents, i = 0; - parents; - parents = parents->next, i++) { - struct commit *parent = parents->item; + for (i = 0; i < num_parent; i++) { /* show stat against the first parent even * when doing combined diff. */ @@ -867,8 +859,7 @@ void diff_tree_combined_merge(const unsigned char *sha1, diffopts.output_format = DIFF_FORMAT_DIFFSTAT; else diffopts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_tree_sha1(parent->object.sha1, commit->object.sha1, "", - &diffopts); + diff_tree_sha1(parent[i], sha1, "", &diffopts); diffcore_std(&diffopts); paths = intersect_paths(paths, i, num_parent); @@ -907,3 +898,25 @@ void diff_tree_combined_merge(const unsigned char *sha1, free(tmp); } } + +void diff_tree_combined_merge(const unsigned char *sha1, + int dense, struct rev_info *rev) +{ + int num_parent; + const unsigned char (*parent)[20]; + struct commit *commit = lookup_commit(sha1); + struct commit_list *parents; + + /* count parents */ + for (parents = commit->parents, num_parent = 0; + parents; + parents = parents->next, num_parent++) + ; /* nothing */ + + parent = xmalloc(num_parent * sizeof(*parent)); + for (parents = commit->parents, num_parent = 0; + parents; + parents = parents->next, num_parent++) + memcpy(parent + num_parent, parents->item->object.sha1, 20); + diff_tree_combined(sha1, parent, num_parent, dense, rev); +} diff --git a/diff.h b/diff.h index 7150b9038..b3b2c4dd2 100644 --- a/diff.h +++ b/diff.h @@ -75,6 +75,8 @@ struct combine_diff_path { extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, int dense, struct rev_info *); +extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev); + extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *); extern void diff_addremove(struct diff_options *, diff --git a/revision.c b/revision.c index f2a9f25fe..4381d455e 100644 --- a/revision.c +++ b/revision.c @@ -664,6 +664,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (!strcmp(arg, "-c")) { revs->diff = 1; + revs->dense_combined_merges = 0; revs->combine_merges = 1; continue; } From 334b506a3487f762493d3db2d3053114f25590e3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 00:26:41 -0700 Subject: [PATCH 036/212] builtin-diff.c: die() formatting type fix. Signed-off-by: Junio C Hamano --- builtin-diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-diff.c b/builtin-diff.c index c543105b8..b6114ce94 100644 --- a/builtin-diff.c +++ b/builtin-diff.c @@ -307,7 +307,7 @@ int cmd_diff(int argc, const char **argv, char **envp) if (!strcmp(obj->type, tree_type)) { if (ARRAY_SIZE(ent) <= ents) die("more than %d trees given: '%s'", - ARRAY_SIZE(ent), name); + (int) ARRAY_SIZE(ent), name); obj->flags |= flags; ent[ents].item = obj; ent[ents].name = name; From 262a6ef76a1dde97ab50d79fa5cd6d3f9f125765 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Sat, 29 Apr 2006 15:50:28 +0000 Subject: [PATCH 037/212] git-format-patch: Use rfc2822 compliant date. Signed-off-by: Huw Davies Signed-off-by: Junio C Hamano --- git-format-patch.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/git-format-patch.sh b/git-format-patch.sh index c7133bc12..c077f44ca 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -205,11 +205,10 @@ sub show_date { } my $t = $time + $minutes * 60; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t); - return sprintf("%s %s %d %02d:%02d:%02d %d %+05d", - $weekday_names[$wday], - $month_names[$mon], - $mday, $hour, $min, $sec, - $year+1900, $tz); + return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d", + $weekday_names[$wday], $mday, + $month_names[$mon], $year+1900, + $hour, $min, $sec, $tz); } print "From nobody Mon Sep 17 00:00:00 2001\n"; From 755225de6ce4c7b39137afefb35208d0dbc14d9d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 29 Apr 2006 21:22:49 -0700 Subject: [PATCH 038/212] git builtin "push" This adds a builtin "push" command, which is largely just a C'ification of the "git-push.sh" script. Now, the reason I did it as a built-in is partly because it's yet another step on relying less on shell, but it's actually mostly because I've wanted to be able to push to _multiple_ repositories, and the most obvious and simplest interface for that would seem be to just have a "remotes" file that has multiple URL entries. (For "pull", having multiple entries should either just select the first one, or you could fall back on the others on failure - your choice). And quite frankly, it just became too damn messy to do that in shell. Besides, we actually have a fair amount of infrastructure in C, so it just wasn't that hard to do. Of course, this is almost totally untested. It probably doesn't work for anything but the one trial I threw at it. "Simple" doesn't necessarily mean "obviously correct". Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-push.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 2 + git.c | 1 + 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 builtin-push.c diff --git a/Makefile b/Makefile index 8ce27a65f..991999239 100644 --- a/Makefile +++ b/Makefile @@ -214,7 +214,7 @@ LIB_OBJS = \ $(DIFF_OBJS) BUILTIN_OBJS = \ - builtin-log.o builtin-help.o + builtin-log.o builtin-help.o builtin-push.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-push.c b/builtin-push.c new file mode 100644 index 000000000..a0c1caab5 --- /dev/null +++ b/builtin-push.c @@ -0,0 +1,255 @@ +/* + * "git push" + */ +#include "cache.h" +#include "refs.h" +#include "run-command.h" +#include "builtin.h" + +#define MAX_URI (16) + +static const char push_usage[] = "git push [--all] [--tags] [--force] [...]"; + +static int all = 0, tags = 0, force = 0, thin = 1; +static const char *execute = NULL; + +#define BUF_SIZE (2084) +static char buffer[BUF_SIZE]; + +static const char **refspec = NULL; +static int refspec_nr = 0; + +static void add_refspec(const char *ref) +{ + int nr = refspec_nr + 1; + refspec = xrealloc(refspec, nr * sizeof(char *)); + refspec[nr-1] = ref; + refspec_nr = nr; +} + +static int expand_one_ref(const char *ref, const unsigned char *sha1) +{ + /* Ignore the "refs/" at the beginning of the refname */ + ref += 5; + + if (strncmp(ref, "tags/", 5)) + return 0; + + add_refspec(strdup(ref)); + return 0; +} + +static void expand_refspecs(void) +{ + if (all) { + if (refspec_nr) + die("cannot mix '--all' and a refspec"); + + /* + * No need to expand "--all" - we'll just use + * the "--all" flag to send-pack + */ + return; + } + if (!tags) + return; + for_each_ref(expand_one_ref); +} + +static void set_refspecs(const char **refs, int nr) +{ + if (nr) { + size_t bytes = nr * sizeof(char *); + + refspec = xrealloc(refspec, bytes); + memcpy(refspec, refs, bytes); + refspec_nr = nr; + } + expand_refspecs(); +} + +static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) +{ + int n = 0; + FILE *f = fopen(git_path("remotes/%s", repo), "r"); + + if (!f) + return -1; + while (fgets(buffer, BUF_SIZE, f)) { + char *s, *p; + + if (strncmp("URL: ", buffer, 5)) + continue; + s = buffer + 5; + + /* Remove whitespace at the head.. */ + while (isspace(*s)) + s++; + if (!*s) + continue; + + /* ..and at the end */ + p = s + strlen(s); + while (isspace(p[-1])) + *--p = 0; + + uri[n++] = strdup(s); + if (n == MAX_URI) + break; + } + fclose(f); + if (!n) + die("remote '%s' has no URL", repo); + return n; +} + +static int get_branches_uri(const char *repo, const char *uri[MAX_URI]) +{ + const char *slash = strchr(repo, '/'); + int n = slash ? slash - repo : 1000; + FILE *f = fopen(git_path("branches/%.*s", n, repo), "r"); + char *s, *p; + int len; + + if (!f) + return 0; + s = fgets(buffer, BUF_SIZE, f); + fclose(f); + if (!s) + return 0; + while (isspace(*s)) + s++; + if (!*s) + return 0; + p = s + strlen(s); + while (isspace(p[-1])) + *--p = 0; + len = p - s; + if (slash) + len += strlen(slash); + p = xmalloc(len + 1); + strcpy(p, s); + if (slash) + strcat(p, slash); + uri[0] = p; + return 1; +} + +static int get_uri(const char *repo, const char *uri[MAX_URI]) +{ + int n; + + if (*repo != '/') { + n = get_remotes_uri(repo, uri); + if (n > 0) + return n; + + n = get_branches_uri(repo, uri); + if (n > 0) + return n; + } + + uri[0] = repo; + return 1; +} + +static int do_push(const char *repo) +{ + const char *uri[MAX_URI]; + int i, n = get_uri(repo, uri); + int remote; + const char **argv; + int argc; + + n = get_uri(repo, uri); + if (n <= 0) + die("bad repository '%s'", repo); + + argv = xmalloc((refspec_nr + 10) * sizeof(char *)); + argv[0] = "dummy-send-pack"; + argc = 1; + if (all) + argv[argc++] = "--all"; + if (force) + argv[argc++] = "--force"; + if (execute) + argv[argc++] = execute; + if (thin) + argv[argc++] = "--thin"; + remote = argc; + argv[argc++] = "dummy-remote"; + while (refspec_nr--) + argv[argc++] = *refspec++; + argv[argc] = NULL; + + for (i = 0; i < n; i++) { + int error; + const char *dest = uri[i]; + const char *sender = "git-send-pack"; + if (!strncmp(dest, "http://", 7) || + !strncmp(dest, "https://", 8)) + sender = "git-http-push"; + argv[0] = sender; + argv[remote] = dest; + error = run_command_v(argc, argv); + if (!error) + continue; + switch (error) { + case -ERR_RUN_COMMAND_FORK: + die("unable to fork for %s", sender); + case -ERR_RUN_COMMAND_EXEC: + die("unable to exec %s", sender); + case -ERR_RUN_COMMAND_WAITPID: + case -ERR_RUN_COMMAND_WAITPID_WRONG_PID: + case -ERR_RUN_COMMAND_WAITPID_SIGNAL: + case -ERR_RUN_COMMAND_WAITPID_NOEXIT: + die("%s died with strange error", sender); + default: + return -error; + } + } + return 0; +} + +int cmd_push(int argc, const char **argv, char **envp) +{ + int i; + const char *repo = "origin"; // default repository + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + if (arg[0] != '-') { + repo = arg; + i++; + break; + } + if (!strcmp(arg, "--all")) { + all = 1; + continue; + } + if (!strcmp(arg, "--tags")) { + tags = 1; + continue; + } + if (!strcmp(arg, "--force")) { + force = 1; + continue; + } + if (!strcmp(arg, "--thin")) { + thin = 1; + continue; + } + if (!strcmp(arg, "--no-thin")) { + thin = 0; + continue; + } + if (!strncmp(arg, "--exec=", 7)) { + execute = arg; + continue; + } + usage(push_usage); + } + set_refspecs(argv + i, argc - i); + return do_push(repo); +} diff --git a/builtin.h b/builtin.h index 47408a058..94fa9b546 100644 --- a/builtin.h +++ b/builtin.h @@ -20,4 +20,6 @@ extern int cmd_whatchanged(int argc, const char **argv, char **envp); extern int cmd_show(int argc, const char **argv, char **envp); extern int cmd_log(int argc, const char **argv, char **envp); +extern int cmd_push(int argc, const char **argv, char **envp); + #endif diff --git a/git.c b/git.c index 01b7e28b8..fd479e97c 100644 --- a/git.c +++ b/git.c @@ -46,6 +46,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp) { "log", cmd_log }, { "whatchanged", cmd_whatchanged }, { "show", cmd_show }, + { "push", cmd_push }, }; int i; From 5c477b972583e96b65ef1613c5d0fb1b90af3d0e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Apr 2006 14:05:55 +0200 Subject: [PATCH 039/212] builtin-push: resurrect parsing of Push: lines The C'ification of push left these behind. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- builtin-push.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index a0c1caab5..4e659f0f2 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -68,6 +68,10 @@ static void set_refspecs(const char **refs, int nr) expand_refspecs(); } +#define MAX_REFSPECS 10 +static int current_refspec = 0; +static char *refspecs_[MAX_REFSPECS]; + static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) { int n = 0; @@ -76,11 +80,17 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) if (!f) return -1; while (fgets(buffer, BUF_SIZE, f)) { + int is_refspec; char *s, *p; - if (strncmp("URL: ", buffer, 5)) + if (!strncmp("URL: ", buffer, 5)) { + is_refspec = 0; + s = buffer + 5; + } else if (!strncmp("Push: ", buffer, 6)) { + is_refspec = 1; + s = buffer + 6; + } else continue; - s = buffer + 5; /* Remove whitespace at the head.. */ while (isspace(*s)) @@ -93,9 +103,10 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) while (isspace(p[-1])) *--p = 0; - uri[n++] = strdup(s); - if (n == MAX_URI) - break; + if (!is_refspec && n < MAX_URI) + uri[n++] = strdup(s); + else if (is_refspec && current_refspec < MAX_REFSPECS) + refspecs_[current_refspec++] = strdup(s); } fclose(f); if (!n) @@ -140,6 +151,8 @@ static int get_uri(const char *repo, const char *uri[MAX_URI]) int n; if (*repo != '/') { + current_refspec = 0; + n = get_remotes_uri(repo, uri); if (n > 0) return n; @@ -165,6 +178,9 @@ static int do_push(const char *repo) if (n <= 0) die("bad repository '%s'", repo); + if (refspec_nr == 0) + set_refspecs((const char**)refspecs_, current_refspec); + argv = xmalloc((refspec_nr + 10) * sizeof(char *)); argv[0] = "dummy-send-pack"; argc = 1; From 7aaf83dafbde7aaa60a1e220e967faaeb28a46f6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 16:03:27 -0700 Subject: [PATCH 040/212] Fix builtin-push to honor Push: lines in remotes file. [jc: originally from Johannes Schindelin, but reworked to lift a hard limit of Push: lines] Signed-off-by: Junio C Hamano --- builtin-push.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/builtin-push.c b/builtin-push.c index 4e659f0f2..9a861b5af 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -68,14 +68,11 @@ static void set_refspecs(const char **refs, int nr) expand_refspecs(); } -#define MAX_REFSPECS 10 -static int current_refspec = 0; -static char *refspecs_[MAX_REFSPECS]; - static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) { int n = 0; FILE *f = fopen(git_path("remotes/%s", repo), "r"); + int has_explicit_refspec = refspec_nr; if (!f) return -1; @@ -103,10 +100,14 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) while (isspace(p[-1])) *--p = 0; - if (!is_refspec && n < MAX_URI) - uri[n++] = strdup(s); - else if (is_refspec && current_refspec < MAX_REFSPECS) - refspecs_[current_refspec++] = strdup(s); + if (!is_refspec) { + if (n < MAX_URI) + uri[n++] = strdup(s); + else + error("more than %d URL's specified, ignoreing the rest", MAX_URI); + } + else if (is_refspec && !has_explicit_refspec) + add_refspec(strdup(s)); } fclose(f); if (!n) @@ -146,13 +147,17 @@ static int get_branches_uri(const char *repo, const char *uri[MAX_URI]) return 1; } -static int get_uri(const char *repo, const char *uri[MAX_URI]) +/* + * Read remotes and branches file, fill the push target URI + * list. If there is no command line refspecs, read Push: lines + * to set up the *refspec list as well. + * return the number of push target URIs + */ +static int read_config(const char *repo, const char *uri[MAX_URI]) { int n; if (*repo != '/') { - current_refspec = 0; - n = get_remotes_uri(repo, uri); if (n > 0) return n; @@ -169,18 +174,15 @@ static int get_uri(const char *repo, const char *uri[MAX_URI]) static int do_push(const char *repo) { const char *uri[MAX_URI]; - int i, n = get_uri(repo, uri); + int i, n; int remote; const char **argv; int argc; - n = get_uri(repo, uri); + n = read_config(repo, uri); if (n <= 0) die("bad repository '%s'", repo); - if (refspec_nr == 0) - set_refspecs((const char**)refspecs_, current_refspec); - argv = xmalloc((refspec_nr + 10) * sizeof(char *)); argv[0] = "dummy-send-pack"; argc = 1; From f8b28a4078a29cbf93cac6f9edd8d5c203777313 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 1 May 2006 09:50:57 +1000 Subject: [PATCH 041/212] gitk: Add a tree-browsing mode You can now select whether you want to see the patch for a commit or the whole tree. If you select the tree, gitk will now display the commit message plus the contents of one file in the bottom-left pane, when you click on the name of the file in the bottom-right pane. Signed-off-by: Paul Mackerras --- gitk | 378 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 358 insertions(+), 20 deletions(-) diff --git a/gitk b/gitk index bd205f876..f983deee8 100755 --- a/gitk +++ b/gitk @@ -514,6 +514,13 @@ proc makewindow {} { $ctext tag conf found -back yellow frame .ctop.cdet.right + frame .ctop.cdet.right.mode + radiobutton .ctop.cdet.right.mode.patch -text "Patch" \ + -command reselectline -variable cmitmode -value "patch" + radiobutton .ctop.cdet.right.mode.tree -text "Tree" \ + -command reselectline -variable cmitmode -value "tree" + grid .ctop.cdet.right.mode.patch .ctop.cdet.right.mode.tree -sticky ew + pack .ctop.cdet.right.mode -side top -fill x set cflist .ctop.cdet.right.cfiles set indent [font measure $mainfont "nn"] text $cflist -width $geometry(cflistw) -background white -font $mainfont \ @@ -583,6 +590,7 @@ proc makewindow {} { bind $sha1entry <> clearsha1 bind $cflist <1> {sel_flist %W %x %y; break} bind $cflist {sel_flist %W %x %y; break} + bind $cflist {treeclick %W %x %y} set maincursor [. cget -cursor] set textcursor [$ctext cget -cursor] @@ -647,6 +655,7 @@ proc savestuff {w} { global stuffsaved findmergefiles maxgraphpct global maxwidth global viewname viewfiles viewperm nextviewnum + global cmitmode if {$stuffsaved} return if {![winfo viewable .]} return @@ -658,6 +667,7 @@ proc savestuff {w} { puts $f [list set findmergefiles $findmergefiles] puts $f [list set maxgraphpct $maxgraphpct] puts $f [list set maxwidth $maxwidth] + puts $f [list set cmitmode $cmitmode] puts $f "set geometry(width) [winfo width .ctop]" puts $f "set geometry(height) [winfo height .ctop]" puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]" @@ -820,6 +830,236 @@ f Scroll diff view to next file # Procedures for manipulating the file list window at the # bottom right of the overall window. + +proc treeview {w l openlevs} { + global treecontents treediropen treeheight treeparent treeindex + + set ix 0 + set treeindex() 0 + set lev 0 + set prefix {} + set prefixend -1 + set prefendstack {} + set htstack {} + set ht 0 + set treecontents() {} + $w conf -state normal + foreach f $l { + while {[string range $f 0 $prefixend] ne $prefix} { + if {$lev <= $openlevs} { + $w mark set e:$treeindex($prefix) "end -1c" + $w mark gravity e:$treeindex($prefix) left + } + set treeheight($prefix) $ht + incr ht [lindex $htstack end] + set htstack [lreplace $htstack end end] + set prefixend [lindex $prefendstack end] + set prefendstack [lreplace $prefendstack end end] + set prefix [string range $prefix 0 $prefixend] + incr lev -1 + } + set tail [string range $f [expr {$prefixend+1}] end] + while {[set slash [string first "/" $tail]] >= 0} { + lappend htstack $ht + set ht 0 + lappend prefendstack $prefixend + incr prefixend [expr {$slash + 1}] + set d [string range $tail 0 $slash] + lappend treecontents($prefix) $d + set oldprefix $prefix + append prefix $d + set treecontents($prefix) {} + set treeindex($prefix) [incr ix] + set treeparent($prefix) $oldprefix + set tail [string range $tail [expr {$slash+1}] end] + if {$lev <= $openlevs} { + set ht 1 + set treediropen($prefix) [expr {$lev < $openlevs}] + set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}] + $w mark set d:$ix "end -1c" + $w mark gravity d:$ix left + set str "\n" + for {set i 0} {$i < $lev} {incr i} {append str "\t"} + $w insert end $str + $w image create end -align center -image $bm -padx 1 \ + -name a:$ix + $w insert end $d + $w mark set s:$ix "end -1c" + $w mark gravity s:$ix left + } + incr lev + } + if {$tail ne {}} { + if {$lev <= $openlevs} { + incr ht + set str "\n" + for {set i 0} {$i < $lev} {incr i} {append str "\t"} + $w insert end $str + $w insert end $tail + } + lappend treecontents($prefix) $tail + } + } + while {$htstack ne {}} { + set treeheight($prefix) $ht + incr ht [lindex $htstack end] + set htstack [lreplace $htstack end end] + } + $w conf -state disabled +} + +proc linetoelt {l} { + global treeheight treecontents + + set y 2 + set prefix {} + while {1} { + foreach e $treecontents($prefix) { + if {$y == $l} { + return "$prefix$e" + } + set n 1 + if {[string index $e end] eq "/"} { + set n $treeheight($prefix$e) + if {$y + $n > $l} { + append prefix $e + incr y + break + } + } + incr y $n + } + } +} + +proc treeclosedir {w dir} { + global treediropen treeheight treeparent treeindex + + set ix $treeindex($dir) + $w conf -state normal + $w delete s:$ix e:$ix + set treediropen($dir) 0 + $w image configure a:$ix -image tri-rt + $w conf -state disabled + set n [expr {1 - $treeheight($dir)}] + while {$dir ne {}} { + incr treeheight($dir) $n + set dir $treeparent($dir) + } +} + +proc treeopendir {w dir} { + global treediropen treeheight treeparent treecontents treeindex + + set ix $treeindex($dir) + $w conf -state normal + $w image configure a:$ix -image tri-dn + $w mark set e:$ix s:$ix + $w mark gravity e:$ix right + set lev 0 + set str "\n" + set n [llength $treecontents($dir)] + for {set x $dir} {$x ne {}} {set x $treeparent($x)} { + incr lev + append str "\t" + incr treeheight($x) $n + } + foreach e $treecontents($dir) { + if {[string index $e end] eq "/"} { + set de $dir$e + set iy $treeindex($de) + $w mark set d:$iy e:$ix + $w mark gravity d:$iy left + $w insert e:$ix $str + set treediropen($de) 0 + $w image create e:$ix -align center -image tri-rt -padx 1 \ + -name a:$iy + $w insert e:$ix $e + $w mark set s:$iy e:$ix + $w mark gravity s:$iy left + set treeheight($de) 1 + } else { + $w insert e:$ix $str + $w insert e:$ix $e + } + } + $w mark gravity e:$ix left + $w conf -state disabled + set treediropen($dir) 1 + set top [lindex [split [$w index @0,0] .] 0] + set ht [$w cget -height] + set l [lindex [split [$w index s:$ix] .] 0] + if {$l < $top} { + $w yview $l.0 + } elseif {$l + $n + 1 > $top + $ht} { + set top [expr {$l + $n + 2 - $ht}] + if {$l < $top} { + set top $l + } + $w yview $top.0 + } +} + +proc treeclick {w x y} { + global treediropen cmitmode ctext cflist cflist_top + + if {$cmitmode ne "tree"} return + if {![info exists cflist_top]} return + set l [lindex [split [$w index "@$x,$y"] "."] 0] + $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend" + $cflist tag add highlight $l.0 "$l.0 lineend" + set cflist_top $l + if {$l == 1} { + $ctext yview 1.0 + return + } + set e [linetoelt $l] + if {[string index $e end] ne "/"} { + showfile $e + } elseif {$treediropen($e)} { + treeclosedir $w $e + } else { + treeopendir $w $e + } +} + +proc setfilelist {id} { + global treefilelist cflist + + treeview $cflist $treefilelist($id) 0 +} + +image create bitmap tri-rt -background black -foreground blue -data { + #define tri-rt_width 13 + #define tri-rt_height 13 + static unsigned char tri-rt_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00, + 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00}; +} -maskdata { + #define tri-rt-mask_width 13 + #define tri-rt-mask_height 13 + static unsigned char tri-rt-mask_bits[] = { + 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01, + 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00, + 0x08, 0x00}; +} +image create bitmap tri-dn -background black -foreground blue -data { + #define tri-dn_width 13 + #define tri-dn_height 13 + static unsigned char tri-dn_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03, + 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; +} -maskdata { + #define tri-dn-mask_width 13 + #define tri-dn-mask_height 13 + static unsigned char tri-dn-mask_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07, + 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00}; +} + proc init_flist {first} { global cflist cflist_top cflist_bot selectedline difffilestart @@ -837,29 +1077,30 @@ proc init_flist {first} { set difffilestart {} } -proc add_flist {f} { +proc add_flist {fl} { global flistmode cflist $cflist conf -state normal if {$flistmode eq "flat"} { - $cflist insert end "\n$f" + foreach f $fl { + $cflist insert end "\n$f" + } } $cflist conf -state disabled } proc sel_flist {w x y} { - global flistmode ctext difffilestart cflist cflist_top + global flistmode ctext difffilestart cflist cflist_top cmitmode + if {$cmitmode eq "tree"} return if {![info exists cflist_top]} return set l [lindex [split [$w index "@$x,$y"] "."] 0] - if {$flistmode eq "flat"} { - if {$l == 1} { - $ctext yview 1.0 - } else { - catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]} - } - highlight_flist $l + if {$l == 1} { + $ctext yview 1.0 + } else { + catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]} } + highlight_flist $l } proc scrolltext {f0 f1} { @@ -2822,6 +3063,7 @@ proc selectline {l isnew} { global currentid sha1entry global commentend idtags linknum global mergemax numcommits pending_select + global cmitmode catch {unset pending_select} $canv delete hover @@ -2893,8 +3135,6 @@ proc selectline {l isnew} { $ctext conf -state normal $ctext delete 0.0 end set linknum 0 - $ctext mark set fmark.0 0.0 - $ctext mark gravity fmark.0 left set info $commitinfo($id) set date [formatdate [lindex $info 2]] $ctext insert end "Author: [lindex $info 1] $date\n" @@ -2943,7 +3183,9 @@ proc selectline {l isnew} { set commentend [$ctext index "end - 1c"] init_flist "Comments" - if {[llength $olds] <= 1} { + if {$cmitmode eq "tree"} { + gettree $id + } elseif {[llength $olds] <= 1} { startdiff $id } else { mergediff $id $l @@ -2997,6 +3239,14 @@ proc unselectline {} { allcanvs delete secsel } +proc reselectline {} { + global selectedline + + if {[info exists selectedline]} { + selectline $selectedline 0 + } +} + proc addtohistory {cmd} { global history historyindex curview @@ -3058,6 +3308,94 @@ proc goforw {} { } } +proc gettree {id} { + global treefilelist treeidlist diffids diffmergeid treepending + + set diffids $id + catch {unset diffmergeid} + if {![info exists treefilelist($id)]} { + if {![info exists treepending]} { + if {[catch {set gtf [open [concat | git-ls-tree -r $id] r]}]} { + return + } + set treepending $id + set treefilelist($id) {} + set treeidlist($id) {} + fconfigure $gtf -blocking 0 + fileevent $gtf readable [list gettreeline $gtf $id] + } + } else { + setfilelist $id + } +} + +proc gettreeline {gtf id} { + global treefilelist treeidlist treepending cmitmode diffids + + while {[gets $gtf line] >= 0} { + if {[lindex $line 1] ne "blob"} continue + set sha1 [lindex $line 2] + set fname [lindex $line 3] + lappend treefilelist($id) $fname + lappend treeidlist($id) $sha1 + } + if {![eof $gtf]} return + close $gtf + unset treepending + if {$cmitmode ne "tree"} { + if {![info exists diffmergeid]} { + gettreediffs $diffids + } + } elseif {$id ne $diffids} { + gettree $diffids + } else { + setfilelist $id + } +} + +proc showfile {f} { + global treefilelist treeidlist diffids + global ctext commentend + + set i [lsearch -exact $treefilelist($diffids) $f] + if {$i < 0} { + puts "oops, $f not in list for id $diffids" + return + } + set blob [lindex $treeidlist($diffids) $i] + if {[catch {set bf [open [concat | git-cat-file blob $blob] r]} err]} { + puts "oops, error reading blob $blob: $err" + return + } + fconfigure $bf -blocking 0 + fileevent $bf readable [list getblobline $bf $diffids] + $ctext config -state normal + $ctext delete $commentend end + $ctext insert end "\n" + $ctext insert end "$f\n" filesep + $ctext config -state disabled + $ctext yview $commentend +} + +proc getblobline {bf id} { + global diffids cmitmode ctext + + if {$id ne $diffids || $cmitmode ne "tree"} { + catch {close $bf} + return + } + $ctext config -state normal + while {[gets $bf line] >= 0} { + $ctext insert end "$line\n" + } + if {[eof $bf]} { + # delete last newline + $ctext delete "end - 2c" "end - 1c" + close $bf + } + $ctext config -state disabled +} + proc mergediff {id l} { global diffmergeid diffopts mdifffd global diffids @@ -3102,7 +3440,7 @@ proc getmergediffline {mdf id np} { $ctext mark set f:$fname $here $ctext mark gravity f:$fname left lappend difffilestart $here - add_flist $fname + add_flist [list $fname] set l [expr {(78 - [string length $fname]) / 2}] set pad [string range "----------------------------------------" 1 $l] $ctext insert end "$pad $fname $pad\n" filesep @@ -3172,9 +3510,7 @@ proc startdiff {ids} { proc addtocflist {ids} { global treediffs cflist - foreach f $treediffs($ids) { - add_flist $f - } + add_flist $treediffs($ids) getblobdiffs $ids } @@ -3191,6 +3527,7 @@ proc gettreediffs {ids} { proc gettreediffline {gdtf ids} { global treediff treediffs treepending diffids diffmergeid + global cmitmode set n [gets $gdtf line] if {$n < 0} { @@ -3198,7 +3535,9 @@ proc gettreediffline {gdtf ids} { close $gdtf set treediffs($ids) $treediff unset treepending - if {$ids != $diffids} { + if {$cmitmode eq "tree"} { + gettree $diffids + } elseif {$ids != $diffids} { if {![info exists diffmergeid]} { gettreediffs $diffids } @@ -3645,8 +3984,6 @@ proc doseldiff {oldid newid} { $ctext conf -state normal $ctext delete 0.0 end - $ctext mark set fmark.0 0.0 - $ctext mark gravity fmark.0 left init_flist "Top" $ctext insert end "From " $ctext tag conf link -foreground blue -underline 1 @@ -4331,6 +4668,7 @@ set uparrowlen 7 set downarrowlen 7 set mingaplen 30 set flistmode "flat" +set cmitmode "patch" set colors {green red blue magenta darkgrey brown orange} From ea4a19e172e8db52779c06723b1e99cc12f228c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 00:54:29 -0700 Subject: [PATCH 042/212] Extended SHA1 -- "rev^@" syntax to mean "all parents" A short-hand "rev^@" is understood to be "all parents of the named commit" with this patch. So you can do git show v1.0.0^@ to view the parents of a merge commit, gitk ^v1.0.0^@ v1.0.4 to view the log between two revs (including the bottom one), and git diff --cc v1.1.0 v1.0.0^@ to inspect what got changed from the merge parents of v1.0.0 to v1.1.0. This might be just my shiny new toy that is not very useful in practice. I needed it to do the multi-tree diff on Len's infamous 12-way Octopus; typing "diff --cc funmerge funmerge^1 funmerge^2 funmerge^3 ..." was too painful. [jc: taking suggestions from Linus and Johannes to match expectations from shell users who are used to see $@ or $* either of which makes sense. I tend to write "$@" more often so...] Signed-off-by: Junio C Hamano --- revision.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/revision.c b/revision.c index f2a9f25fe..d1f85a8f8 100644 --- a/revision.c +++ b/revision.c @@ -477,6 +477,36 @@ static void handle_all(struct rev_info *revs, unsigned flags) for_each_ref(handle_one_ref); } +static int add_parents_only(struct rev_info *revs, const char *arg, int flags) +{ + unsigned char sha1[20]; + struct object *it; + struct commit *commit; + struct commit_list *parents; + + if (*arg == '^') { + flags ^= UNINTERESTING; + arg++; + } + if (get_sha1(arg, sha1)) + return 0; + while (1) { + it = get_reference(revs, arg, sha1, 0); + if (strcmp(it->type, tag_type)) + break; + memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20); + } + if (strcmp(it->type, commit_type)) + return 0; + commit = (struct commit *)it; + for (parents = commit->parents; parents; parents = parents->next) { + it = &parents->item->object; + it->flags |= flags; + add_pending_object(revs, it, arg); + } + return 1; +} + void init_revisions(struct rev_info *revs) { memset(revs, 0, sizeof(*revs)); @@ -746,6 +776,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } *dotdot = '.'; } + dotdot = strstr(arg, "^@"); + if (dotdot && !dotdot[2]) { + *dotdot = 0; + if (add_parents_only(revs, arg, flags)) + continue; + *dotdot = '^'; + } local_flags = 0; if (*arg == '^') { local_flags = UNINTERESTING; From 73b0e5af9daf38734d94ed170f8800481e15567f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Apr 2006 17:31:04 -0700 Subject: [PATCH 043/212] get_sha1(): :path and :[0-3]:path to extract from index. Earlier patch to say : by Linus was very useful, and this extends the same idea to the current index. An sha1 expression : extracts the object name for the named path from the current index. Signed-off-by: Junio C Hamano --- sha1_name.c | 54 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/sha1_name.c b/sha1_name.c index 345935bb2..ec5cd2c9e 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -458,17 +458,55 @@ int get_sha1(const char *name, unsigned char *sha1) { int ret; unsigned unused; + int namelen = strlen(name); + const char *cp; prepare_alt_odb(); - ret = get_sha1_1(name, strlen(name), sha1); - if (ret < 0) { - const char *cp = strchr(name, ':'); - if (cp) { - unsigned char tree_sha1[20]; - if (!get_sha1_1(name, cp-name, tree_sha1)) - return get_tree_entry(tree_sha1, cp+1, sha1, - &unused); + ret = get_sha1_1(name, namelen, sha1); + if (!ret) + return ret; + /* sha1:path --> object name of path in ent sha1 + * :path -> object name of path in index + * :[0-3]:path -> object name of path in index at stage + */ + if (name[0] == ':') { + int stage = 0; + struct cache_entry *ce; + int pos; + if (namelen < 3 || + name[2] != ':' || + name[1] < '0' || '3' < name[1]) + cp = name + 1; + else { + stage = name[1] - '0'; + cp = name + 3; } + namelen = namelen - (cp - name); + if (!active_cache) + read_cache(); + if (active_nr < 0) + return -1; + pos = cache_name_pos(cp, namelen); + if (pos < 0) + pos = -pos - 1; + while (pos < active_nr) { + ce = active_cache[pos]; + if (ce_namelen(ce) != namelen || + memcmp(ce->name, cp, namelen)) + break; + if (ce_stage(ce) == stage) { + memcpy(sha1, ce->sha1, 20); + return 0; + } + } + return -1; + } + cp = strchr(name, ':'); + if (cp) { + unsigned char tree_sha1[20]; + if (!get_sha1_1(name, cp-name, tree_sha1)) + return get_tree_entry(tree_sha1, cp+1, sha1, + &unused); } return ret; } From 5010cb5fcca30269ad25f2eb38b31455af3205d7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 23:28:15 -0700 Subject: [PATCH 044/212] built-in "git grep" This attempts to set up built-in "git grep" to further reduce our dependence on the shell, while at the same time optionally allowing to run grep against object database. You could do funky things like these: git grep --cached -e pattern ;# grep from index git grep -e pattern master ;# or in a rev git grep -e pattern master next ;# or in multiple revs git grep -e pattern pu^@ ;# even like this with an ;# extension from another topic ;-) git grep -e pattern master..next ;# or even from rev ranges git grep -e pattern master~20:Documentation ;# or an arbitrary tree git grep -e pattern next:git-commit.sh ;# or an arbitrary blob Right now, it does not understand and/or obey many options grep should accept, and the pattern must be given with -e option due to the way the parameter parser is structured, both of which obviously need to be fixed for usability. But this is going in the right direction. The shell script version is one of the worst Portability offender in the git barebone Porcelainish; it uses xargs -0 to pass paths around and shell arrays to sift flags and parameters. Signed-off-by: Junio C Hamano --- Makefile | 2 +- builtin-grep.c | 454 +++++++++++++++++++++++++++++++++++++++++++++++++ builtin.h | 1 + git.c | 1 + 4 files changed, 457 insertions(+), 1 deletion(-) create mode 100644 builtin-grep.c diff --git a/Makefile b/Makefile index 8ce27a65f..8d5122bdd 100644 --- a/Makefile +++ b/Makefile @@ -214,7 +214,7 @@ LIB_OBJS = \ $(DIFF_OBJS) BUILTIN_OBJS = \ - builtin-log.o builtin-help.o + builtin-log.o builtin-help.o builtin-grep.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz diff --git a/builtin-grep.c b/builtin-grep.c new file mode 100644 index 000000000..36150bf4e --- /dev/null +++ b/builtin-grep.c @@ -0,0 +1,454 @@ +/* + * Builtin "git grep" + * + * Copyright (c) 2006 Junio C Hamano + */ +#include "cache.h" +#include "blob.h" +#include "tree.h" +#include "commit.h" +#include "tag.h" +#include "diff.h" +#include "revision.h" +#include "builtin.h" +#include + +static int pathspec_matches(struct diff_options *opt, const char *name) +{ + int i, j; + int namelen; + if (!opt->nr_paths) + return 1; + namelen = strlen(name); + for (i = 0; i < opt->nr_paths; i++) { + const char *match = opt->paths[i]; + int matchlen = opt->pathlens[i]; + if (matchlen <= namelen) { + if (!strncmp(name, match, matchlen)) + return 1; + continue; + } + /* If name is "Documentation" and pathspec is + * "Documentation/", they should match. Maybe + * we would want to strip it in get_pathspec()??? + */ + if (strncmp(name, match, namelen)) + continue; + for (j = namelen; j < matchlen; j++) + if (match[j] != '/') + break; + if (matchlen <= j) + return 1; + } + return 0; +} + +struct grep_opt { + const char *pattern; + regex_t regexp; + unsigned linenum:1; + unsigned invert:1; + int regflags; + unsigned pre_context; + unsigned post_context; +}; + +static char *end_of_line(char *cp, unsigned long *left) +{ + unsigned long l = *left; + while (l && *cp != '\n') { + l--; + cp++; + } + *left = l; + return cp; +} + +static void show_line(struct grep_opt *opt, const char *bol, const char *eol, + const char *name, unsigned lno, char sign) +{ + printf("%s%c", name, sign); + if (opt->linenum) + printf("%d%c", lno, sign); + printf("%.*s\n", eol-bol, bol); +} + +static int grep_buffer(struct grep_opt *opt, const char *name, + char *buf, unsigned long size) +{ + char *bol = buf; + unsigned long left = size; + unsigned lno = 1; + struct pre_context_line { + char *bol; + char *eol; + } *prev = NULL, *pcl; + unsigned last_hit = 0; + unsigned last_shown = 0; + const char *hunk_mark = ""; + + if (opt->pre_context) + prev = xcalloc(opt->pre_context, sizeof(*prev)); + if (opt->pre_context || opt->post_context) + hunk_mark = "--\n"; + + while (left) { + regmatch_t pmatch[10]; + char *eol, ch; + int hit; + + eol = end_of_line(bol, &left); + ch = *eol; + *eol = 0; + + hit = !regexec(&opt->regexp, bol, ARRAY_SIZE(pmatch), + pmatch, 0); + if (opt->invert) + hit = !hit; + if (hit) { + /* Hit at this line. If we haven't shown the + * pre-context lines, we would need to show them. + */ + if (opt->pre_context) { + unsigned from; + if (opt->pre_context < lno) + from = lno - opt->pre_context; + else + from = 1; + if (from <= last_shown) + from = last_shown + 1; + if (last_shown && from != last_shown + 1) + printf(hunk_mark); + while (from < lno) { + pcl = &prev[lno-from-1]; + show_line(opt, pcl->bol, pcl->eol, + name, from, '-'); + from++; + } + last_shown = lno-1; + } + if (last_shown && lno != last_shown + 1) + printf(hunk_mark); + show_line(opt, bol, eol, name, lno, ':'); + last_shown = last_hit = lno; + } + else if (last_hit && + lno <= last_hit + opt->post_context) { + /* If the last hit is within the post context, + * we need to show this line. + */ + if (last_shown && lno != last_shown + 1) + printf(hunk_mark); + show_line(opt, bol, eol, name, lno, '-'); + last_shown = lno; + } + if (opt->pre_context) { + memmove(prev+1, prev, + (opt->pre_context-1) * sizeof(*prev)); + prev->bol = bol; + prev->eol = eol; + } + *eol = ch; + bol = eol + 1; + left--; + lno++; + } + return !!last_hit; +} + +static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name) +{ + unsigned long size; + char *data; + char type[20]; + int hit; + data = read_sha1_file(sha1, type, &size); + if (!data) { + error("'%s': unable to read %s", name, sha1_to_hex(sha1)); + return 0; + } + hit = grep_buffer(opt, name, data, size); + free(data); + return hit; +} + +static int grep_file(struct grep_opt *opt, const char *filename) +{ + struct stat st; + int i; + char *data; + if (lstat(filename, &st) < 0) { + err_ret: + if (errno != ENOENT) + error("'%s': %s", filename, strerror(errno)); + return 0; + } + if (!st.st_size) + return 0; /* empty file -- no grep hit */ + if (!S_ISREG(st.st_mode)) + return 0; + i = open(filename, O_RDONLY); + if (i < 0) + goto err_ret; + data = xmalloc(st.st_size + 1); + if (st.st_size != xread(i, data, st.st_size)) { + error("'%s': short read %s", filename, strerror(errno)); + close(i); + free(data); + return 0; + } + close(i); + i = grep_buffer(opt, filename, data, st.st_size); + free(data); + return i; +} + +static int grep_cache(struct grep_opt *opt, struct rev_info *revs, int cached) +{ + int hit = 0; + int nr; + read_cache(); + + for (nr = 0; nr < active_nr; nr++) { + struct cache_entry *ce = active_cache[nr]; + if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode))) + continue; + if (!pathspec_matches(&revs->diffopt, ce->name)) + continue; + if (cached) + hit |= grep_sha1(opt, ce->sha1, ce->name); + else + hit |= grep_file(opt, ce->name); + } + return hit; +} + +static int grep_tree(struct grep_opt *opt, struct rev_info *revs, + struct tree_desc *tree, + const char *tree_name, const char *base) +{ + unsigned mode; + int len; + int hit = 0; + const char *path; + const unsigned char *sha1; + char *down_base; + char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100); + + if (tree_name[0]) { + int offset = sprintf(path_buf, "%s:", tree_name); + down_base = path_buf + offset; + strcat(down_base, base); + } + else { + down_base = path_buf; + strcpy(down_base, base); + } + len = strlen(path_buf); + + while (tree->size) { + int pathlen; + sha1 = tree_entry_extract(tree, &path, &mode); + pathlen = strlen(path); + strcpy(path_buf + len, path); + + if (!pathspec_matches(&revs->diffopt, down_base)) + ; + else if (S_ISREG(mode)) + hit |= grep_sha1(opt, sha1, path_buf); + else if (S_ISDIR(mode)) { + char type[20]; + struct tree_desc sub; + void *data; + data = read_sha1_file(sha1, type, &sub.size); + if (!data) + die("unable to read tree (%s)", + sha1_to_hex(sha1)); + strcpy(path_buf + len + pathlen, "/"); + sub.buf = data; + hit = grep_tree(opt, revs, &sub, tree_name, down_base); + free(data); + } + update_tree_entry(tree); + } + return hit; +} + +static int grep_object(struct grep_opt *opt, struct rev_info *revs, + struct object *obj, const char *name) +{ + if (!strcmp(obj->type, blob_type)) + return grep_sha1(opt, obj->sha1, name); + if (!strcmp(obj->type, commit_type) || + !strcmp(obj->type, tree_type)) { + struct tree_desc tree; + void *data; + int hit; + data = read_object_with_reference(obj->sha1, tree_type, + &tree.size, NULL); + if (!data) + die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); + tree.buf = data; + hit = grep_tree(opt, revs, &tree, name, ""); + free(data); + return hit; + } + die("unable to grep from object of type %s", obj->type); +} + +static const char builtin_grep_usage[] = +"git-grep