From 50b44eceed2105db0bee194904649249cc5f0308 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 4 Apr 2006 10:16:22 +1000 Subject: 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 diff --git a/gitk b/gitk index 90afec9..961b582 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 -- cgit v0.10.2-6-g49f6 From d94f8cd6adc746b850aad421e30d5f90f7f11ee4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 6 Apr 2006 10:18:23 +1000 Subject: 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 diff --git a/gitk b/gitk index 961b582..c6649a5 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 -- cgit v0.10.2-6-g49f6 From fdedbcfb0d71d99f96e08c1903ce8bd5a8f51a39 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 6 Apr 2006 21:22:52 +1000 Subject: 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 diff --git a/gitk b/gitk index cfd0a3a..093213f 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" -- cgit v0.10.2-6-g49f6 From 22626ef4fd5a0b2cf1fc8e90bbb23f8ff0ed43ea Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 17 Apr 2006 09:56:02 +1000 Subject: 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 diff --git a/gitk b/gitk index 093213f..85f426a 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 -- cgit v0.10.2-6-g49f6 From 0032d548db56eac9ea09b4ba05843365f6325b85 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Tue, 18 Apr 2006 08:11:06 -0500 Subject: socksetup: don't return on set_reuse_addr() error The set_reuse_addr() error case was the only error case in socklist() where we returned rather than continued. Not sure why. Either we must free the socklist, or continue. This patch continues on error. Signed-off-by: Serge E. Hallyn Signed-off-by: Junio C Hamano diff --git a/daemon.c b/daemon.c index a1ccda3..776749e 100644 --- a/daemon.c +++ b/daemon.c @@ -535,7 +535,7 @@ static int socksetup(int port, int **socklist_p) if (set_reuse_addr(sockfd)) { close(sockfd); - return 0; /* not fatal */ + continue; } if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { -- cgit v0.10.2-6-g49f6 From ec167793d84ba7b765e1eb71b0257ce7baca2d26 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Apr 2006 12:51:29 -0700 Subject: Add git-unresolve ... This is an attempt to address the issue raised on #git channel recently by Carl Worth. After a conflicted automerge, "git diff" shows a combined diff to give you how the tentative automerge result differs from what came from each branch. During a complex merge, it is tempting to be able to resolve a few paths at a time, mark them "I've dealt with them" with git-update-index to unclutter the next "git diff" output, and keep going. However, when the final result does not compile or otherwise found to be a mismerge, the workflow to fix the mismerged paths suddenly changes to "git diff HEAD -- path" (to get a diff from our HEAD before merging) and "git diff MERGE_HEAD -- path" (to get a diff from theirs), and it cannot show the combined anymore. With git-unresolve ..., the versions from our branch and their branch for specified blobs are placed in stage #2 and stage #3, without touching the working tree files. This gives you the combined diff back for easier review, along with "diff --ours" and "diff --theirs". One thing it does not do is to place the base in stage #1; this means "diff --base" would behave differently between the run immediately after a conflicted three-way merge, and the run after an update-index by mistake followed by a git-unresolve. We could theoretically run merge-base between HEAD and MERGE_HEAD to find which tree to place in stage #1, but reviewing "diff --base" is not that useful so.... Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index b5959d6..1e4ba7b 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ git-tag git-tar-tree git-unpack-file git-unpack-objects +git-unresolve git-update-index git-update-ref git-update-server-info diff --git a/Makefile b/Makefile index 3ecd674..51dcce3 100644 --- a/Makefile +++ b/Makefile @@ -165,7 +165,8 @@ PROGRAMS = \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ - git-describe$X git-merge-tree$X git-blame$X git-imap-send$X + git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \ + git-unresolve$X BUILT_INS = git-log$X diff --git a/unresolve.c b/unresolve.c new file mode 100644 index 0000000..0b23b9b --- /dev/null +++ b/unresolve.c @@ -0,0 +1,146 @@ +#include "cache.h" +#include "tree-walk.h" + +static const char unresolve_usage[] = +"git-unresolve ..."; + +static struct cache_file cache_file; +static unsigned char head_sha1[20]; +static unsigned char merge_head_sha1[20]; + +static struct cache_entry *read_one_ent(const char *which, + unsigned char *ent, const char *path, + int namelen, int stage) +{ + unsigned mode; + unsigned char sha1[20]; + int size; + struct cache_entry *ce; + + if (get_tree_entry(ent, path, sha1, &mode)) { + error("%s: not in %s branch.", path, which); + return NULL; + } + if (mode == S_IFDIR) { + error("%s: not a blob in %s branch.", path, which); + return NULL; + } + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, namelen); + ce->ce_flags = create_ce_flags(namelen, stage); + ce->ce_mode = create_ce_mode(mode); + return ce; +} + +static int unresolve_one(const char *path) +{ + int namelen = strlen(path); + int pos; + int ret = 0; + struct cache_entry *ce_2 = NULL, *ce_3 = NULL; + + /* See if there is such entry in the index. */ + pos = cache_name_pos(path, namelen); + if (pos < 0) { + /* If there isn't, either it is unmerged, or + * resolved as "removed" by mistake. We do not + * want to do anything in the former case. + */ + pos = -pos-1; + if (pos < active_nr) { + struct cache_entry *ce = active_cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, path, namelen)) { + fprintf(stderr, + "%s: skipping still unmerged path.\n", + path); + goto free_return; + } + } + } + + /* Grab blobs from given path from HEAD and MERGE_HEAD, + * stuff HEAD version in stage #2, + * stuff MERGE_HEAD version in stage #3. + */ + ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); + ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); + + if (!ce_2 || !ce_3) { + ret = -1; + goto free_return; + } + if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && + ce_2->ce_mode == ce_3->ce_mode) { + fprintf(stderr, "%s: identical in both, skipping.\n", + path); + goto free_return; + } + + remove_file_from_cache(path); + if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { + error("%s: cannot add our version to the index.", path); + ret = -1; + goto free_return; + } + if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) + return 0; + error("%s: cannot add their version to the index.", path); + ret = -1; + free_return: + free(ce_2); + free(ce_3); + return ret; +} + +static void read_head_pointers(void) +{ + if (read_ref(git_path("HEAD"), head_sha1)) + die("Cannot read HEAD -- no initial commit yet?"); + if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { + fprintf(stderr, "Not in the middle of a merge.\n"); + exit(0); + } +} + +int main(int ac, char **av) +{ + int i; + int err = 0; + int newfd; + + if (ac < 2) + usage(unresolve_usage); + + git_config(git_default_config); + + /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we + * are not doing a merge, so exit with success status. + */ + read_head_pointers(); + + /* Otherwise we would need to update the cache. */ + newfd= hold_index_file_for_update(&cache_file, get_index_file()); + if (newfd < 0) + die("unable to create new cachefile"); + + if (read_cache() < 0) + die("cache corrupted"); + + for (i = 1; i < ac; i++) { + char *arg = av[i]; + err |= unresolve_one(arg); + } + if (err) + die("Error encountered; index not updated."); + + if (active_cache_changed) { + if (write_cache(newfd, active_cache, active_nr) || + commit_index_file(&cache_file)) + die("Unable to write new cachefile"); + } + return 0; +} -- cgit v0.10.2-6-g49f6 From 44aad15f0d03860884bbe1cc95ff6dfc4badf505 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Apr 2006 20:41:14 -0700 Subject: diff --stat: do not drop rename information. When a verbatim rename or copy is detected, we did not show anything on the "diff --stat" for the filepair. This makes it to show the rename information. Signed-off-by: Junio C Hamano diff --git a/diff-lib.c b/diff-lib.c index 0a832c3..13b216f 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -195,6 +195,56 @@ static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) return 0; } +static char *pprint_rename(const char *a, const char *b) +{ + const char *old = a; + const char *new = b; + char *name = NULL; + int pfx_length, sfx_length; + int len_a = strlen(a); + int len_b = strlen(b); + + /* Find common prefix */ + pfx_length = 0; + while (*old && *new && *old == *new) { + if (*old == '/') + pfx_length = old - a + 1; + old++; + new++; + } + + /* Find common suffix */ + old = a + len_a; + new = b + len_b; + sfx_length = 0; + while (a <= old && b <= new && *old == *new) { + if (*old == '/') + sfx_length = len_a - (old - a); + old--; + new--; + } + + /* + * pfx{mid-a => mid-b}sfx + * {pfx-a => pfx-b}sfx + * pfx{sfx-a => sfx-b} + * name-a => name-b + */ + if (pfx_length + sfx_length) { + name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7); + sprintf(name, "%.*s{%.*s => %.*s}%s", + pfx_length, a, + len_a - pfx_length - sfx_length, a + pfx_length, + len_b - pfx_length - sfx_length, b + pfx_length, + a + len_a - sfx_length); + } + else { + name = xmalloc(len_a + len_b + 5); + sprintf(name, "%s => %s", a, b); + } + return name; +} + struct diffstat_t { struct xdiff_emit_state xm; @@ -204,12 +254,14 @@ struct diffstat_t { char *name; unsigned is_unmerged:1; unsigned is_binary:1; + unsigned is_renamed:1; unsigned int added, deleted; } **files; }; static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, - const char *name) + const char *name_a, + const char *name_b) { struct diffstat_file *x; x = xcalloc(sizeof (*x), 1); @@ -219,7 +271,12 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, diffstat->alloc * sizeof(x)); } diffstat->files[diffstat->nr++] = x; - x->name = strdup(name); + if (name_b) { + x->name = pprint_rename(name_a, name_b); + x->is_renamed = 1; + } + else + x->name = strdup(name_a); return x; } @@ -305,7 +362,8 @@ static void show_stats(struct diffstat_t* data) printf(" %s%-*s | Unmerged\n", prefix, len, name); goto free_diffstat_file; } - else if (added + deleted == 0) { + else if (!data->files[i]->is_renamed && + (added + deleted == 0)) { total_files--; goto free_diffstat_file; } @@ -425,13 +483,14 @@ 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 diff_filespec *one, + struct diff_filespec *two, + struct diffstat_t *diffstat) { mmfile_t mf1, mf2; struct diffstat_file *data; - data = diffstat_add(diffstat, name_a ? name_a : name_b); + data = diffstat_add(diffstat, name_a, name_b); if (!one || !two) { data->is_unmerged = 1; @@ -992,7 +1051,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) } static void run_diffstat(struct diff_filepair *p, struct diff_options *o, - struct diffstat_t *diffstat) + struct diffstat_t *diffstat) { const char *name; const char *other; @@ -1374,7 +1433,7 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) } static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, - struct diffstat_t *diffstat) + struct diffstat_t *diffstat) { if (diff_unmodified_pair(p)) return; @@ -1559,7 +1618,7 @@ void diff_flush(struct diff_options *options) for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options, - diffstat); + diffstat); } show_stats(diffstat); free(diffstat); -- cgit v0.10.2-6-g49f6 From 2bd452d3b9f62ecc3406307cd6a5553856d21ff2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Apr 2006 23:52:05 -0700 Subject: git-update-index --unresolve Retire git-unresolve and make it into "git-update-index --unresolve". It processes all paths that follow. During a merge, you would mark a path that is dealt with with: $ git update-index hello and you would "undo" it with: $ git update-index --unresolve hello Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 1e4ba7b..b5959d6 100644 --- a/.gitignore +++ b/.gitignore @@ -111,7 +111,6 @@ git-tag git-tar-tree git-unpack-file git-unpack-objects -git-unresolve git-update-index git-update-ref git-update-server-info diff --git a/Makefile b/Makefile index 51dcce3..3ecd674 100644 --- a/Makefile +++ b/Makefile @@ -165,8 +165,7 @@ PROGRAMS = \ git-upload-pack$X git-verify-pack$X git-write-tree$X \ git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ - git-describe$X git-merge-tree$X git-blame$X git-imap-send$X \ - git-unresolve$X + git-describe$X git-merge-tree$X git-blame$X git-imap-send$X BUILT_INS = git-log$X diff --git a/unresolve.c b/unresolve.c deleted file mode 100644 index 0b23b9b..0000000 --- a/unresolve.c +++ /dev/null @@ -1,146 +0,0 @@ -#include "cache.h" -#include "tree-walk.h" - -static const char unresolve_usage[] = -"git-unresolve ..."; - -static struct cache_file cache_file; -static unsigned char head_sha1[20]; -static unsigned char merge_head_sha1[20]; - -static struct cache_entry *read_one_ent(const char *which, - unsigned char *ent, const char *path, - int namelen, int stage) -{ - unsigned mode; - unsigned char sha1[20]; - int size; - struct cache_entry *ce; - - if (get_tree_entry(ent, path, sha1, &mode)) { - error("%s: not in %s branch.", path, which); - return NULL; - } - if (mode == S_IFDIR) { - error("%s: not a blob in %s branch.", path, which); - return NULL; - } - size = cache_entry_size(namelen); - ce = xcalloc(1, size); - - memcpy(ce->sha1, sha1, 20); - memcpy(ce->name, path, namelen); - ce->ce_flags = create_ce_flags(namelen, stage); - ce->ce_mode = create_ce_mode(mode); - return ce; -} - -static int unresolve_one(const char *path) -{ - int namelen = strlen(path); - int pos; - int ret = 0; - struct cache_entry *ce_2 = NULL, *ce_3 = NULL; - - /* See if there is such entry in the index. */ - pos = cache_name_pos(path, namelen); - if (pos < 0) { - /* If there isn't, either it is unmerged, or - * resolved as "removed" by mistake. We do not - * want to do anything in the former case. - */ - pos = -pos-1; - if (pos < active_nr) { - struct cache_entry *ce = active_cache[pos]; - if (ce_namelen(ce) == namelen && - !memcmp(ce->name, path, namelen)) { - fprintf(stderr, - "%s: skipping still unmerged path.\n", - path); - goto free_return; - } - } - } - - /* Grab blobs from given path from HEAD and MERGE_HEAD, - * stuff HEAD version in stage #2, - * stuff MERGE_HEAD version in stage #3. - */ - ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); - ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); - - if (!ce_2 || !ce_3) { - ret = -1; - goto free_return; - } - if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && - ce_2->ce_mode == ce_3->ce_mode) { - fprintf(stderr, "%s: identical in both, skipping.\n", - path); - goto free_return; - } - - remove_file_from_cache(path); - if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { - error("%s: cannot add our version to the index.", path); - ret = -1; - goto free_return; - } - if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) - return 0; - error("%s: cannot add their version to the index.", path); - ret = -1; - free_return: - free(ce_2); - free(ce_3); - return ret; -} - -static void read_head_pointers(void) -{ - if (read_ref(git_path("HEAD"), head_sha1)) - die("Cannot read HEAD -- no initial commit yet?"); - if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { - fprintf(stderr, "Not in the middle of a merge.\n"); - exit(0); - } -} - -int main(int ac, char **av) -{ - int i; - int err = 0; - int newfd; - - if (ac < 2) - usage(unresolve_usage); - - git_config(git_default_config); - - /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we - * are not doing a merge, so exit with success status. - */ - read_head_pointers(); - - /* Otherwise we would need to update the cache. */ - newfd= hold_index_file_for_update(&cache_file, get_index_file()); - if (newfd < 0) - die("unable to create new cachefile"); - - if (read_cache() < 0) - die("cache corrupted"); - - for (i = 1; i < ac; i++) { - char *arg = av[i]; - err |= unresolve_one(arg); - } - if (err) - die("Error encountered; index not updated."); - - if (active_cache_changed) { - if (write_cache(newfd, active_cache, active_nr) || - commit_index_file(&cache_file)) - die("Unable to write new cachefile"); - } - return 0; -} diff --git a/update-index.c b/update-index.c index 1efac27..64f4c49 100644 --- a/update-index.c +++ b/update-index.c @@ -6,6 +6,7 @@ #include "cache.h" #include "strbuf.h" #include "quote.h" +#include "tree-walk.h" /* * Default to not allowing changes to the list of files. The @@ -471,6 +472,124 @@ static void read_index_info(int line_termination) static const char update_index_usage[] = "git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--ignore-missing] [-z] [--verbose] [--] ..."; +static unsigned char head_sha1[20]; +static unsigned char merge_head_sha1[20]; + +static struct cache_entry *read_one_ent(const char *which, + unsigned char *ent, const char *path, + int namelen, int stage) +{ + unsigned mode; + unsigned char sha1[20]; + int size; + struct cache_entry *ce; + + if (get_tree_entry(ent, path, sha1, &mode)) { + error("%s: not in %s branch.", path, which); + return NULL; + } + if (mode == S_IFDIR) { + error("%s: not a blob in %s branch.", path, which); + return NULL; + } + size = cache_entry_size(namelen); + ce = xcalloc(1, size); + + memcpy(ce->sha1, sha1, 20); + memcpy(ce->name, path, namelen); + ce->ce_flags = create_ce_flags(namelen, stage); + ce->ce_mode = create_ce_mode(mode); + return ce; +} + +static int unresolve_one(const char *path) +{ + int namelen = strlen(path); + int pos; + int ret = 0; + struct cache_entry *ce_2 = NULL, *ce_3 = NULL; + + /* See if there is such entry in the index. */ + pos = cache_name_pos(path, namelen); + if (pos < 0) { + /* If there isn't, either it is unmerged, or + * resolved as "removed" by mistake. We do not + * want to do anything in the former case. + */ + pos = -pos-1; + if (pos < active_nr) { + struct cache_entry *ce = active_cache[pos]; + if (ce_namelen(ce) == namelen && + !memcmp(ce->name, path, namelen)) { + fprintf(stderr, + "%s: skipping still unmerged path.\n", + path); + goto free_return; + } + } + } + + /* Grab blobs from given path from HEAD and MERGE_HEAD, + * stuff HEAD version in stage #2, + * stuff MERGE_HEAD version in stage #3. + */ + ce_2 = read_one_ent("our", head_sha1, path, namelen, 2); + ce_3 = read_one_ent("their", merge_head_sha1, path, namelen, 3); + + if (!ce_2 || !ce_3) { + ret = -1; + goto free_return; + } + if (!memcmp(ce_2->sha1, ce_3->sha1, 20) && + ce_2->ce_mode == ce_3->ce_mode) { + fprintf(stderr, "%s: identical in both, skipping.\n", + path); + goto free_return; + } + + remove_file_from_cache(path); + if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) { + error("%s: cannot add our version to the index.", path); + ret = -1; + goto free_return; + } + if (!add_cache_entry(ce_3, ADD_CACHE_OK_TO_ADD)) + return 0; + error("%s: cannot add their version to the index.", path); + ret = -1; + free_return: + free(ce_2); + free(ce_3); + return ret; +} + +static void read_head_pointers(void) +{ + if (read_ref(git_path("HEAD"), head_sha1)) + die("No HEAD -- no initial commit yet?\n"); + if (read_ref(git_path("MERGE_HEAD"), merge_head_sha1)) { + fprintf(stderr, "Not in the middle of a merge.\n"); + exit(0); + } +} + +static int do_unresolve(int ac, const char **av) +{ + int i; + int err = 0; + + /* Read HEAD and MERGE_HEAD; if MERGE_HEAD does not exist, we + * are not doing a merge, so exit with success status. + */ + read_head_pointers(); + + for (i = 1; i < ac; i++) { + const char *arg = av[i]; + err |= unresolve_one(arg); + } + return err; +} + int main(int argc, const char **argv) { int i, newfd, entries, has_errors = 0, line_termination = '\n'; @@ -581,6 +700,12 @@ int main(int argc, const char **argv) read_index_info(line_termination); break; } + if (!strcmp(path, "--unresolve")) { + has_errors = do_unresolve(argc - i, argv + i); + if (has_errors) + active_cache_changed = 0; + goto finish; + } if (!strcmp(path, "--ignore-missing")) { not_new = 1; continue; @@ -612,6 +737,8 @@ int main(int argc, const char **argv) free(path_name); } } + + finish: if (active_cache_changed) { if (write_cache(newfd, active_cache, active_nr) || commit_index_file(&cache_file)) -- cgit v0.10.2-6-g49f6 From 2516dae2f63b6e6c4f7dbe2eb1322ba32da092c5 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 21 Apr 2006 10:35:31 +1000 Subject: 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 diff --git a/gitk b/gitk index 85f426a..305aa2e 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]} { -- cgit v0.10.2-6-g49f6 From 08df61713c89be4086b1930ef6b9e46ccf38928d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Apr 2006 21:56:13 -0700 Subject: Makefile: dependency for builtin-help.o builtin-help.c wants to include common-cmds.h which is a generated file. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index a83c502..d9a3a82 100644 --- a/Makefile +++ b/Makefile @@ -470,6 +470,8 @@ git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) +builtin-help.o: common-cmds.h + $(BUILT_INS): git$X rm -f $@ && ln git$X $@ -- cgit v0.10.2-6-g49f6 From 96afa0764e6b58a38b21befded2c8fd0543fab2a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 11 Apr 2006 23:05:14 -0700 Subject: Add colordiff for git to contrib/colordiff. I hacked it up to teach it the git extended diff headers, made it not to read the whole patch in the array. Also, the original program, when arguments are given, ran "diff" with the given arguments and showed the output from it. Of course, I changed it to run "git diff" ;-). Signed-off-by: Junio C Hamano diff --git a/contrib/colordiff/README b/contrib/colordiff/README new file mode 100644 index 0000000..2678fdf --- /dev/null +++ b/contrib/colordiff/README @@ -0,0 +1,2 @@ +This is "colordiff" (http://colordiff.sourceforge.net/) by Dave +Ewart , modified specifically for git. diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl new file mode 100755 index 0000000..5789cfb --- /dev/null +++ b/contrib/colordiff/colordiff.perl @@ -0,0 +1,196 @@ +#!/usr/bin/perl -w +# +# $Id: colordiff.pl,v 1.4.2.10 2004/01/04 15:02:59 daveewart Exp $ + +######################################################################## +# # +# ColorDiff - a wrapper/replacment for 'diff' producing # +# colourful output # +# # +# Copyright (C)2002-2004 Dave Ewart (davee@sungate.co.uk) # +# # +######################################################################## +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # +# # +######################################################################## + +use strict; +use Getopt::Long qw(:config pass_through); +use IPC::Open2; + +my $app_name = 'colordiff'; +my $version = '1.0.4'; +my $author = 'Dave Ewart'; +my $author_email = 'davee@sungate.co.uk'; +my $app_www = 'http://colordiff.sourceforge.net/'; +my $copyright = '(C)2002-2004'; +my $show_banner = 1; + +# ANSI sequences for colours +my %colour; +$colour{white} = "\033[1;37m"; +$colour{yellow} = "\033[1;33m"; +$colour{green} = "\033[1;32m"; +$colour{blue} = "\033[1;34m"; +$colour{cyan} = "\033[1;36m"; +$colour{red} = "\033[1;31m"; +$colour{magenta} = "\033[1;35m"; +$colour{black} = "\033[1;30m"; +$colour{darkwhite} = "\033[0;37m"; +$colour{darkyellow} = "\033[0;33m"; +$colour{darkgreen} = "\033[0;32m"; +$colour{darkblue} = "\033[0;34m"; +$colour{darkcyan} = "\033[0;36m"; +$colour{darkred} = "\033[0;31m"; +$colour{darkmagenta} = "\033[0;35m"; +$colour{darkblack} = "\033[0;30m"; +$colour{OFF} = "\033[0;0m"; + +# Default colours if /etc/colordiffrc or ~/.colordiffrc do not exist +my $plain_text = $colour{OFF}; +my $file_old = $colour{red}; +my $file_new = $colour{blue}; +my $diff_stuff = $colour{magenta}; + +# Locations for personal and system-wide colour configurations +my $HOME = $ENV{HOME}; +my $etcdir = '/etc'; + +my ($setting, $value); +my @config_files = ("$etcdir/colordiffrc", "$HOME/.colordiffrc"); +my $config_file; + +foreach $config_file (@config_files) { + if (open(COLORDIFFRC, "<$config_file")) { + while () { + chop; + next if (/^#/ || /^$/); + s/\s+//g; + ($setting, $value) = split ('='); + if ($setting eq 'banner') { + if ($value eq 'no') { + $show_banner = 0; + } + next; + } + if (!defined $colour{$value}) { + print "Invalid colour specification ($value) in $config_file\n"; + next; + } + if ($setting eq 'plain') { + $plain_text = $colour{$value}; + } + elsif ($setting eq 'oldtext') { + $file_old = $colour{$value}; + } + elsif ($setting eq 'newtext') { + $file_new = $colour{$value}; + } + elsif ($setting eq 'diffstuff') { + $diff_stuff = $colour{$value}; + } + else { + print "Unknown option in $etcdir/colordiffrc: $setting\n"; + } + } + close COLORDIFFRC; + } +} + +# colordiff specfic options here. Need to pre-declare if using variables +GetOptions( + "no-banner" => sub { $show_banner = 0 }, + "plain-text=s" => \&set_color, + "file-old=s" => \&set_color, + "file-new=s" => \&set_color, + "diff-stuff=s" => \&set_color +); + +if ($show_banner == 1) { + print STDERR "$app_name $version ($app_www)\n"; + print STDERR "$copyright $author, $author_email\n\n"; +} + +if (defined $ARGV[0]) { + # More reliable way of pulling in arguments + open2(\*INPUTSTREAM, undef, "git", "diff", @ARGV); +} +else { + *INPUTSTREAM = \*STDIN; +} + +my $record; +my $nrecs = 0; +my $inside_file_old = 1; +my $nparents = undef; + +while () { + $nrecs++; + if (/^(\@\@+) -[-+0-9, ]+ \1/) { + print "$diff_stuff"; + $nparents = length($1) - 1; + } + elsif (/^diff -/ || /^index / || + /^old mode / || /^new mode / || + /^deleted file mode / || /^new file mode / || + /^similarity index / || /^dissimilarity index / || + /^copy from / || /^copy to / || + /^rename from / || /^rename to /) { + $nparents = undef; + print "$diff_stuff"; + } + elsif (defined $nparents) { + if ($nparents == 1) { + if (/^\+/) { + print $file_new; + } + elsif (/^-/) { + print $file_old; + } + else { + print $plain_text; + } + } + elsif (/^ {$nparents}/) { + print "$plain_text"; + } + elsif (/^[+ ]{$nparents}/) { + print "$file_new"; + } + elsif (/^[- ]{$nparents}/) { + print "$file_old"; + } + else { + print $plain_text; + } + } + elsif (/^--- / || /^\+\+\+ /) { + print $diff_stuff; + } + else { + print "$plain_text"; + } + s/$/$colour{OFF}/; + print "$_"; +} +close INPUTSTREAM; + +sub set_color { + my ($type, $color) = @_; + + $type =~ s/-/_/; + eval "\$$type = \$colour{$color}"; +} -- cgit v0.10.2-6-g49f6 From 96ab4f4e7a0fac1992a1f27ad2dc1106db09a5de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Apr 2006 19:39:04 -0700 Subject: Fix "git show --stat" Signed-off-by: Junio C Hamano diff --git a/revision.c b/revision.c index 113dd5a..f9c7d15 100644 --- a/revision.c +++ b/revision.c @@ -789,7 +789,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch } if (revs->combine_merges) { revs->ignore_merges = 0; - if (revs->dense_combined_merges) + if (revs->dense_combined_merges && + (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT)) revs->diffopt.output_format = DIFF_FORMAT_PATCH; } revs->diffopt.abbrev = revs->abbrev; -- cgit v0.10.2-6-g49f6 From 6973dcaee76ef7b7bfcabd2f26e76205aae07858 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 21 Apr 2006 23:57:45 -0700 Subject: Libify diff-files. This is the first installment to libify diff brothers. The updated diff-files uses revision.c::setup_revisions() infrastructure to parse its command line arguments, which means the pathname arguments are checked more strictly than before. The tests are adjusted to separate possibly missing paths from the rest of arguments with double-dashes, to show the kosher way. As Linus pointed out, renaming diff.c to diff-lib.c was simply stupid, so I am renaming it back. The new diff-lib.c is to contain pieces extracted from diff brothers. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index d9a3a82..0924f84 100644 --- a/Makefile +++ b/Makefile @@ -199,7 +199,7 @@ LIB_H = \ tree-walk.h log-tree.h DIFF_OBJS = \ - diff-lib.o diffcore-break.o diffcore-order.o \ + diff.o diff-lib.o diffcore-break.o diffcore-order.o \ diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \ diffcore-delta.o log-tree.o diff --git a/diff-files.c b/diff-files.c index ffbef48..b9d193d 100644 --- a/diff-files.c +++ b/diff-files.c @@ -12,203 +12,43 @@ static const char diff_files_usage[] = "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [] [...]" COMMON_DIFF_OPTIONS_HELP; -static struct rev_info rev; -static int silent = 0; -static int diff_unmerged_stage = 2; -static int combine_merges = 0; -static int dense_combined_merges = 0; - -static void show_unmerge(const char *path) -{ - diff_unmerge(&rev.diffopt, path); -} - -static void show_file(int pfx, struct cache_entry *ce) -{ - diff_addremove(&rev.diffopt, pfx, ntohl(ce->ce_mode), - ce->sha1, ce->name, NULL); -} - -static void show_modified(int oldmode, int mode, - const unsigned char *old_sha1, const unsigned char *sha1, - char *path) -{ - diff_change(&rev.diffopt, oldmode, mode, old_sha1, sha1, path, NULL); -} - int main(int argc, const char **argv) { - const char **pathspec; - const char *prefix = setup_git_directory(); - int entries, i; + struct rev_info rev; + int silent = 0; git_config(git_diff_config); - diff_setup(&rev.diffopt); + init_revisions(&rev); + rev.abbrev = 0; + + argc = setup_revisions(argc, argv, &rev, NULL); while (1 < argc && argv[1][0] == '-') { - if (!strcmp(argv[1], "--")) { - argv++; - argc--; - break; - } - if (!strcmp(argv[1], "-0")) - diff_unmerged_stage = 0; - else if (!strcmp(argv[1], "-1")) - diff_unmerged_stage = 1; - else if (!strcmp(argv[1], "-2")) - diff_unmerged_stage = 2; - else if (!strcmp(argv[1], "-3")) - diff_unmerged_stage = 3; - else if (!strcmp(argv[1], "--base")) - diff_unmerged_stage = 1; + if (!strcmp(argv[1], "--base")) + rev.max_count = 1; else if (!strcmp(argv[1], "--ours")) - diff_unmerged_stage = 2; + rev.max_count = 2; else if (!strcmp(argv[1], "--theirs")) - diff_unmerged_stage = 3; + rev.max_count = 3; else if (!strcmp(argv[1], "-q")) silent = 1; - else if (!strcmp(argv[1], "-r")) - ; /* no-op */ - else if (!strcmp(argv[1], "-s")) - ; /* no-op */ - else if (!strcmp(argv[1], "-c")) - combine_merges = 1; - else if (!strcmp(argv[1], "--cc")) - dense_combined_merges = combine_merges = 1; - else { - int diff_opt_cnt; - diff_opt_cnt = diff_opt_parse(&rev.diffopt, - argv+1, argc-1); - if (diff_opt_cnt < 0) - usage(diff_files_usage); - else if (diff_opt_cnt) { - argv += diff_opt_cnt; - argc -= diff_opt_cnt; - continue; - } - else - usage(diff_files_usage); - } + else + usage(diff_files_usage); argv++; argc--; } - if (dense_combined_merges) - rev.diffopt.output_format = DIFF_FORMAT_PATCH; - - /* Find the directory, and set up the pathspec */ - pathspec = get_pathspec(prefix, argv + 1); - entries = read_cache(); - - if (diff_setup_done(&rev.diffopt) < 0) + /* + * 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 (rev.pending_objects || + rev.min_age != -1 || rev.max_age != -1) usage(diff_files_usage); - - /* At this point, if argc == 1, then we are doing everything. - * Otherwise argv[1] .. argv[argc-1] have the explicit paths. + /* + * Backward compatibility wart - "diff-files -s" used to + * defeat the common diff option "-s" which asked for + * DIFF_FORMAT_NO_OUTPUT. */ - if (entries < 0) { - perror("read_cache"); - exit(1); - } - - for (i = 0; i < entries; i++) { - struct stat st; - unsigned int oldmode, newmode; - struct cache_entry *ce = active_cache[i]; - int changed; - - if (!ce_path_match(ce, pathspec)) - continue; - - if (ce_stage(ce)) { - struct { - struct combine_diff_path p; - struct combine_diff_parent filler[5]; - } combine; - int num_compare_stages = 0; - - combine.p.next = NULL; - combine.p.len = ce_namelen(ce); - combine.p.path = xmalloc(combine.p.len + 1); - memcpy(combine.p.path, ce->name, combine.p.len); - combine.p.path[combine.p.len] = 0; - combine.p.mode = 0; - memset(combine.p.sha1, 0, 20); - memset(&combine.p.parent[0], 0, - sizeof(combine.filler)); - - while (i < entries) { - struct cache_entry *nce = active_cache[i]; - int stage; - - if (strcmp(ce->name, nce->name)) - break; - - /* Stage #2 (ours) is the first parent, - * stage #3 (theirs) is the second. - */ - stage = ce_stage(nce); - if (2 <= stage) { - int mode = ntohl(nce->ce_mode); - num_compare_stages++; - memcpy(combine.p.parent[stage-2].sha1, - nce->sha1, 20); - combine.p.parent[stage-2].mode = - canon_mode(mode); - combine.p.parent[stage-2].status = - DIFF_STATUS_MODIFIED; - } - - /* diff against the proper unmerged stage */ - if (stage == diff_unmerged_stage) - ce = nce; - i++; - } - /* - * Compensate for loop update - */ - i--; - - if (combine_merges && num_compare_stages == 2) { - show_combined_diff(&combine.p, 2, - dense_combined_merges, - &rev); - free(combine.p.path); - continue; - } - free(combine.p.path); - - /* - * Show the diff for the 'ce' if we found the one - * from the desired stage. - */ - show_unmerge(ce->name); - if (ce_stage(ce) != diff_unmerged_stage) - continue; - } - - if (lstat(ce->name, &st) < 0) { - if (errno != ENOENT && errno != ENOTDIR) { - perror(ce->name); - continue; - } - if (silent) - continue; - show_file('-', ce); - continue; - } - changed = ce_match_stat(ce, &st, 0); - if (!changed && !rev.diffopt.find_copies_harder) - continue; - oldmode = ntohl(ce->ce_mode); - - newmode = canon_mode(st.st_mode); - if (!trust_executable_bit && - S_ISREG(newmode) && S_ISREG(oldmode) && - ((newmode ^ oldmode) == 0111)) - newmode = oldmode; - show_modified(oldmode, newmode, - ce->sha1, (changed ? null_sha1 : ce->sha1), - ce->name); - } - diffcore_std(&rev.diffopt); - diff_flush(&rev.diffopt); - return 0; + if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT) + rev.diffopt.output_format = DIFF_FORMAT_RAW; + return run_diff_files(&rev, silent); } diff --git a/diff-lib.c b/diff-lib.c index 13b216f..a28dd3d 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -1,1795 +1,133 @@ /* * Copyright (C) 2005 Junio C Hamano */ -#include -#include -#include #include "cache.h" #include "quote.h" +#include "commit.h" #include "diff.h" #include "diffcore.h" -#include "xdiff-interface.h" - -static int use_size_cache; - -int diff_rename_limit_default = -1; - -int git_diff_config(const char *var, const char *value) -{ - if (!strcmp(var, "diff.renamelimit")) { - diff_rename_limit_default = git_config_int(var, value); - return 0; - } - - return git_default_config(var, value); -} - -static char *quote_one(const char *str) -{ - int needlen; - char *xp; - - if (!str) - return NULL; - needlen = quote_c_style(str, NULL, NULL, 0); - if (!needlen) - return strdup(str); - xp = xmalloc(needlen + 1); - quote_c_style(str, xp, NULL, 0); - return xp; -} - -static char *quote_two(const char *one, const char *two) -{ - int need_one = quote_c_style(one, NULL, NULL, 1); - int need_two = quote_c_style(two, NULL, NULL, 1); - char *xp; - - if (need_one + need_two) { - if (!need_one) need_one = strlen(one); - if (!need_two) need_one = strlen(two); - - xp = xmalloc(need_one + need_two + 3); - xp[0] = '"'; - quote_c_style(one, xp + 1, NULL, 1); - quote_c_style(two, xp + need_one + 1, NULL, 1); - strcpy(xp + need_one + need_two + 1, "\""); - return xp; - } - need_one = strlen(one); - need_two = strlen(two); - xp = xmalloc(need_one + need_two + 1); - strcpy(xp, one); - strcpy(xp + need_one, two); - return xp; -} - -static const char *external_diff(void) -{ - static const char *external_diff_cmd = NULL; - static int done_preparing = 0; - - if (done_preparing) - return external_diff_cmd; - external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); - done_preparing = 1; - return external_diff_cmd; -} - -#define TEMPFILE_PATH_LEN 50 - -static struct diff_tempfile { - const char *name; /* filename external diff should read from */ - char hex[41]; - char mode[10]; - char tmp_path[TEMPFILE_PATH_LEN]; -} diff_temp[2]; - -static int count_lines(const char *data, int size) -{ - int count, ch, completely_empty = 1, nl_just_seen = 0; - count = 0; - while (0 < size--) { - ch = *data++; - if (ch == '\n') { - count++; - nl_just_seen = 1; - completely_empty = 0; - } - else { - nl_just_seen = 0; - completely_empty = 0; - } - } - if (completely_empty) - return 0; - if (!nl_just_seen) - count++; /* no trailing newline */ - return count; -} - -static void print_line_count(int count) -{ - switch (count) { - case 0: - printf("0,0"); - break; - case 1: - printf("1"); - break; - default: - printf("1,%d", count); - break; - } -} - -static void copy_file(int prefix, const char *data, int size) -{ - int ch, nl_just_seen = 1; - while (0 < size--) { - ch = *data++; - if (nl_just_seen) - putchar(prefix); - putchar(ch); - if (ch == '\n') - nl_just_seen = 1; - else - nl_just_seen = 0; - } - if (!nl_just_seen) - printf("\n\\ No newline at end of file\n"); -} - -static void emit_rewrite_diff(const char *name_a, - const char *name_b, - struct diff_filespec *one, - struct diff_filespec *two) -{ - int lc_a, lc_b; - diff_populate_filespec(one, 0); - diff_populate_filespec(two, 0); - lc_a = count_lines(one->data, one->size); - lc_b = count_lines(two->data, two->size); - printf("--- %s\n+++ %s\n@@ -", name_a, name_b); - print_line_count(lc_a); - printf(" +"); - print_line_count(lc_b); - printf(" @@\n"); - if (lc_a) - copy_file('-', one->data, one->size); - if (lc_b) - copy_file('+', two->data, two->size); -} - -static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) -{ - if (!DIFF_FILE_VALID(one)) { - mf->ptr = ""; /* does not matter */ - mf->size = 0; - return 0; - } - else if (diff_populate_filespec(one, 0)) - return -1; - mf->ptr = one->data; - mf->size = one->size; - return 0; -} - -struct emit_callback { - const char **label_path; -}; - -static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) -{ - int i; - struct emit_callback *ecbdata = priv; - - if (ecbdata->label_path[0]) { - printf("--- %s\n", ecbdata->label_path[0]); - printf("+++ %s\n", ecbdata->label_path[1]); - ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; - } - for (i = 0; i < nbuf; i++) - if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout)) - return -1; - return 0; -} - -static char *pprint_rename(const char *a, const char *b) -{ - const char *old = a; - const char *new = b; - char *name = NULL; - int pfx_length, sfx_length; - int len_a = strlen(a); - int len_b = strlen(b); - - /* Find common prefix */ - pfx_length = 0; - while (*old && *new && *old == *new) { - if (*old == '/') - pfx_length = old - a + 1; - old++; - new++; - } - - /* Find common suffix */ - old = a + len_a; - new = b + len_b; - sfx_length = 0; - while (a <= old && b <= new && *old == *new) { - if (*old == '/') - sfx_length = len_a - (old - a); - old--; - new--; - } - - /* - * pfx{mid-a => mid-b}sfx - * {pfx-a => pfx-b}sfx - * pfx{sfx-a => sfx-b} - * name-a => name-b - */ - if (pfx_length + sfx_length) { - name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7); - sprintf(name, "%.*s{%.*s => %.*s}%s", - pfx_length, a, - len_a - pfx_length - sfx_length, a + pfx_length, - len_b - pfx_length - sfx_length, b + pfx_length, - a + len_a - sfx_length); - } - else { - name = xmalloc(len_a + len_b + 5); - sprintf(name, "%s => %s", a, b); - } - return name; -} - -struct diffstat_t { - struct xdiff_emit_state xm; - - int nr; - int alloc; - struct diffstat_file { - char *name; - unsigned is_unmerged:1; - unsigned is_binary:1; - unsigned is_renamed:1; - unsigned int added, deleted; - } **files; -}; - -static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, - const char *name_a, - const char *name_b) -{ - struct diffstat_file *x; - x = xcalloc(sizeof (*x), 1); - if (diffstat->nr == diffstat->alloc) { - diffstat->alloc = alloc_nr(diffstat->alloc); - diffstat->files = xrealloc(diffstat->files, - diffstat->alloc * sizeof(x)); - } - diffstat->files[diffstat->nr++] = x; - if (name_b) { - x->name = pprint_rename(name_a, name_b); - x->is_renamed = 1; - } - else - x->name = strdup(name_a); - return x; -} - -static void diffstat_consume(void *priv, char *line, unsigned long len) -{ - struct diffstat_t *diffstat = priv; - struct diffstat_file *x = diffstat->files[diffstat->nr - 1]; - - if (line[0] == '+') - x->added++; - else if (line[0] == '-') - x->deleted++; -} - -static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; -static const char minuses[]= "----------------------------------------------------------------------"; - -static void show_stats(struct diffstat_t* data) -{ - char *prefix = ""; - int i, len, add, del, total, adds = 0, dels = 0; - int max, max_change = 0, max_len = 0; - int total_files = data->nr; - - if (data->nr == 0) - return; - - for (i = 0; i < data->nr; i++) { - struct diffstat_file *file = data->files[i]; - - len = strlen(file->name); - if (max_len < len) - max_len = len; - - if (file->is_binary || file->is_unmerged) - continue; - if (max_change < file->added + file->deleted) - max_change = file->added + file->deleted; - } - - for (i = 0; i < data->nr; i++) { - char *name = data->files[i]->name; - int added = data->files[i]->added; - int deleted = data->files[i]->deleted; - - if (0 < (len = quote_c_style(name, NULL, NULL, 0))) { - char *qname = xmalloc(len + 1); - quote_c_style(name, qname, NULL, 0); - free(name); - data->files[i]->name = name = qname; - } - - /* - * "scale" the filename - */ - len = strlen(name); - max = max_len; - if (max > 50) - max = 50; - if (len > max) { - char *slash; - prefix = "..."; - max -= 3; - name += len - max; - slash = strchr(name, '/'); - if (slash) - name = slash; - } - len = max; - - /* - * scale the add/delete - */ - max = max_change; - if (max + len > 70) - max = 70 - len; - - if (data->files[i]->is_binary) { - printf(" %s%-*s | Bin\n", prefix, len, name); - goto free_diffstat_file; - } - else if (data->files[i]->is_unmerged) { - printf(" %s%-*s | Unmerged\n", prefix, len, name); - goto free_diffstat_file; - } - else if (!data->files[i]->is_renamed && - (added + deleted == 0)) { - total_files--; - goto free_diffstat_file; - } - - add = added; - del = deleted; - total = add + del; - adds += add; - dels += del; - - if (max_change > 0) { - total = (total * max + max_change / 2) / max_change; - add = (add * max + max_change / 2) / max_change; - del = total - add; - } - printf(" %s%-*s |%5d %.*s%.*s\n", prefix, - len, name, added + deleted, - add, pluses, del, minuses); - free_diffstat_file: - free(data->files[i]->name); - free(data->files[i]); - } - free(data->files); - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", - total_files, adds, dels); -} - -#define FIRST_FEW_BYTES 8000 -static int mmfile_is_binary(mmfile_t *mf) -{ - long sz = mf->size; - if (FIRST_FEW_BYTES < sz) - sz = FIRST_FEW_BYTES; - if (memchr(mf->ptr, 0, sz)) - return 1; - return 0; -} - -static void builtin_diff(const char *name_a, - const char *name_b, - struct diff_filespec *one, - struct diff_filespec *two, - const char *xfrm_msg, - int complete_rewrite) -{ - mmfile_t mf1, mf2; - const char *lbl[2]; - char *a_one, *b_two; - - a_one = quote_two("a/", name_a); - b_two = quote_two("b/", name_b); - lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; - lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; - printf("diff --git %s %s\n", a_one, b_two); - if (lbl[0][0] == '/') { - /* /dev/null */ - printf("new file mode %06o\n", two->mode); - if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); - } - else if (lbl[1][0] == '/') { - printf("deleted file mode %06o\n", one->mode); - if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); - } - else { - if (one->mode != two->mode) { - printf("old mode %06o\n", one->mode); - printf("new mode %06o\n", two->mode); - } - if (xfrm_msg && xfrm_msg[0]) - puts(xfrm_msg); - /* - * we do not run diff between different kind - * of objects. - */ - if ((one->mode ^ two->mode) & S_IFMT) - goto free_ab_and_return; - if (complete_rewrite) { - emit_rewrite_diff(name_a, name_b, one, two); - goto free_ab_and_return; - } - } - - if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) - die("unable to read files to diff"); - - if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) - printf("Binary files %s and %s differ\n", lbl[0], lbl[1]); - else { - /* Crazy xdl interfaces.. */ - const char *diffopts = getenv("GIT_DIFF_OPTS"); - xpparam_t xpp; - xdemitconf_t xecfg; - xdemitcb_t ecb; - struct emit_callback ecbdata; - - ecbdata.label_path = lbl; - xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = 3; - xecfg.flags = XDL_EMIT_FUNCNAMES; - if (!diffopts) - ; - else if (!strncmp(diffopts, "--unified=", 10)) - xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); - else if (!strncmp(diffopts, "-u", 2)) - xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); - ecb.outf = fn_out; - ecb.priv = &ecbdata; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); - } - - free_ab_and_return: - free(a_one); - free(b_two); - return; -} - -static void builtin_diffstat(const char *name_a, const char *name_b, - struct diff_filespec *one, - struct diff_filespec *two, - struct diffstat_t *diffstat) -{ - mmfile_t mf1, mf2; - struct diffstat_file *data; - - data = diffstat_add(diffstat, name_a, name_b); - - if (!one || !two) { - data->is_unmerged = 1; - return; - } - - if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) - die("unable to read files to diff"); - - if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) - data->is_binary = 1; - else { - /* Crazy xdl interfaces.. */ - xpparam_t xpp; - xdemitconf_t xecfg; - xdemitcb_t ecb; - - xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = 0; - xecfg.flags = 0; - ecb.outf = xdiff_outf; - ecb.priv = diffstat; - xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); - } -} - -struct diff_filespec *alloc_filespec(const char *path) -{ - int namelen = strlen(path); - struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1); - - memset(spec, 0, sizeof(*spec)); - spec->path = (char *)(spec + 1); - memcpy(spec->path, path, namelen+1); - return spec; -} - -void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, - unsigned short mode) -{ - if (mode) { - spec->mode = canon_mode(mode); - memcpy(spec->sha1, sha1, 20); - spec->sha1_valid = !!memcmp(sha1, null_sha1, 20); - } -} +#include "revision.h" /* - * Given a name and sha1 pair, if the dircache tells us the file in - * the work tree has that object contents, return true, so that - * prepare_temp_file() does not have to inflate and extract. + * diff-files */ -static int work_tree_matches(const char *name, const unsigned char *sha1) -{ - struct cache_entry *ce; - struct stat st; - int pos, len; - - /* We do not read the cache ourselves here, because the - * benchmark with my previous version that always reads cache - * shows that it makes things worse for diff-tree comparing - * two linux-2.6 kernel trees in an already checked out work - * tree. This is because most diff-tree comparisons deal with - * only a small number of files, while reading the cache is - * expensive for a large project, and its cost outweighs the - * savings we get by not inflating the object to a temporary - * file. Practically, this code only helps when we are used - * by diff-cache --cached, which does read the cache before - * calling us. - */ - if (!active_cache) - return 0; - - len = strlen(name); - pos = cache_name_pos(name, len); - if (pos < 0) - return 0; - ce = active_cache[pos]; - if ((lstat(name, &st) < 0) || - !S_ISREG(st.st_mode) || /* careful! */ - ce_match_stat(ce, &st, 0) || - memcmp(sha1, ce->sha1, 20)) - return 0; - /* we return 1 only when we can stat, it is a regular file, - * stat information matches, and sha1 recorded in the cache - * matches. I.e. we know the file in the work tree really is - * the same as the pair. - */ - return 1; -} - -static struct sha1_size_cache { - unsigned char sha1[20]; - unsigned long size; -} **sha1_size_cache; -static int sha1_size_cache_nr, sha1_size_cache_alloc; -static struct sha1_size_cache *locate_size_cache(unsigned char *sha1, - int find_only, - unsigned long size) +int run_diff_files(struct rev_info *revs, int silent_on_removed) { - int first, last; - struct sha1_size_cache *e; + int entries, i; + int diff_unmerged_stage = revs->max_count; - first = 0; - last = sha1_size_cache_nr; - while (last > first) { - int cmp, next = (last + first) >> 1; - e = sha1_size_cache[next]; - cmp = memcmp(e->sha1, sha1, 20); - if (!cmp) - return e; - if (cmp < 0) { - last = next; - continue; - } - first = next+1; - } - /* not found */ - if (find_only) - return NULL; - /* insert to make it at "first" */ - if (sha1_size_cache_alloc <= sha1_size_cache_nr) { - sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc); - sha1_size_cache = xrealloc(sha1_size_cache, - sha1_size_cache_alloc * - sizeof(*sha1_size_cache)); - } - sha1_size_cache_nr++; - if (first < sha1_size_cache_nr) - memmove(sha1_size_cache + first + 1, sha1_size_cache + first, - (sha1_size_cache_nr - first - 1) * - sizeof(*sha1_size_cache)); - e = xmalloc(sizeof(struct sha1_size_cache)); - sha1_size_cache[first] = e; - memcpy(e->sha1, sha1, 20); - e->size = size; - return e; -} - -/* - * While doing rename detection and pickaxe operation, we may need to - * grab the data for the blob (or file) for our own in-core comparison. - * diff_filespec has data and size fields for this purpose. - */ -int diff_populate_filespec(struct diff_filespec *s, int size_only) -{ - int err = 0; - if (!DIFF_FILE_VALID(s)) - die("internal error: asking to populate invalid file."); - if (S_ISDIR(s->mode)) + if (diff_unmerged_stage < 0) + diff_unmerged_stage = 2; + entries = read_cache(); + if (entries < 0) { + perror("read_cache"); return -1; - - if (!use_size_cache) - size_only = 0; - - if (s->data) - return err; - if (!s->sha1_valid || - work_tree_matches(s->path, s->sha1)) { - struct stat st; - int fd; - if (lstat(s->path, &st) < 0) { - if (errno == ENOENT) { - err_empty: - err = -1; - empty: - s->data = ""; - s->size = 0; - return err; - } - } - s->size = st.st_size; - if (!s->size) - goto empty; - if (size_only) - return 0; - if (S_ISLNK(st.st_mode)) { - int ret; - s->data = xmalloc(s->size); - s->should_free = 1; - ret = readlink(s->path, s->data, s->size); - if (ret < 0) { - free(s->data); - goto err_empty; - } - return 0; - } - fd = open(s->path, O_RDONLY); - if (fd < 0) - goto err_empty; - s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0); - close(fd); - if (s->data == MAP_FAILED) - goto err_empty; - s->should_munmap = 1; } - else { - char type[20]; - struct sha1_size_cache *e; - - if (size_only) { - e = locate_size_cache(s->sha1, 1, 0); - if (e) { - s->size = e->size; - return 0; - } - if (!sha1_object_info(s->sha1, type, &s->size)) - locate_size_cache(s->sha1, 0, s->size); - } - else { - s->data = read_sha1_file(s->sha1, type, &s->size); - s->should_free = 1; - } - } - return 0; -} - -void diff_free_filespec_data(struct diff_filespec *s) -{ - if (s->should_free) - free(s->data); - else if (s->should_munmap) - munmap(s->data, s->size); - s->should_free = s->should_munmap = 0; - s->data = NULL; - free(s->cnt_data); - s->cnt_data = NULL; -} - -static void prep_temp_blob(struct diff_tempfile *temp, - void *blob, - unsigned long size, - const unsigned char *sha1, - int mode) -{ - int fd; - - fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX"); - if (fd < 0) - die("unable to create temp-file"); - if (write(fd, blob, size) != size) - die("unable to write temp-file"); - close(fd); - temp->name = temp->tmp_path; - strcpy(temp->hex, sha1_to_hex(sha1)); - temp->hex[40] = 0; - sprintf(temp->mode, "%06o", mode); -} - -static void prepare_temp_file(const char *name, - struct diff_tempfile *temp, - struct diff_filespec *one) -{ - if (!DIFF_FILE_VALID(one)) { - not_a_valid_file: - /* A '-' entry produces this for file-2, and - * a '+' entry produces this for file-1. - */ - temp->name = "/dev/null"; - strcpy(temp->hex, "."); - strcpy(temp->mode, "."); - return; - } - - if (!one->sha1_valid || - work_tree_matches(name, one->sha1)) { + for (i = 0; i < entries; i++) { struct stat st; - if (lstat(name, &st) < 0) { - if (errno == ENOENT) - goto not_a_valid_file; - die("stat(%s): %s", name, strerror(errno)); - } - if (S_ISLNK(st.st_mode)) { - int ret; - char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ - if (sizeof(buf) <= st.st_size) - die("symlink too long: %s", name); - ret = readlink(name, buf, st.st_size); - if (ret < 0) - die("readlink(%s)", name); - prep_temp_blob(temp, buf, st.st_size, - (one->sha1_valid ? - one->sha1 : null_sha1), - (one->sha1_valid ? - one->mode : S_IFLNK)); - } - else { - /* we can borrow from the file in the work tree */ - temp->name = name; - if (!one->sha1_valid) - strcpy(temp->hex, sha1_to_hex(null_sha1)); - else - strcpy(temp->hex, sha1_to_hex(one->sha1)); - /* Even though we may sometimes borrow the - * contents from the work tree, we always want - * one->mode. mode is trustworthy even when - * !(one->sha1_valid), as long as - * DIFF_FILE_VALID(one). - */ - sprintf(temp->mode, "%06o", one->mode); - } - return; - } - else { - if (diff_populate_filespec(one, 0)) - die("cannot read data blob for %s", one->path); - prep_temp_blob(temp, one->data, one->size, - one->sha1, one->mode); - } -} - -static void remove_tempfile(void) -{ - int i; - - for (i = 0; i < 2; i++) - if (diff_temp[i].name == diff_temp[i].tmp_path) { - unlink(diff_temp[i].name); - diff_temp[i].name = NULL; - } -} - -static void remove_tempfile_on_signal(int signo) -{ - remove_tempfile(); - signal(SIGINT, SIG_DFL); - raise(signo); -} - -static int spawn_prog(const char *pgm, const char **arg) -{ - pid_t pid; - int status; + unsigned int oldmode, newmode; + struct cache_entry *ce = active_cache[i]; + int changed; - fflush(NULL); - pid = fork(); - if (pid < 0) - die("unable to fork"); - if (!pid) { - execvp(pgm, (char *const*) arg); - exit(255); - } - - while (waitpid(pid, &status, 0) < 0) { - if (errno == EINTR) + if (!ce_path_match(ce, revs->prune_data)) continue; - return -1; - } - - /* Earlier we did not check the exit status because - * diff exits non-zero if files are different, and - * we are not interested in knowing that. It was a - * mistake which made it harder to quit a diff-* - * session that uses the git-apply-patch-script as - * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF - * should also exit non-zero only when it wants to - * abort the entire diff-* session. - */ - if (WIFEXITED(status) && !WEXITSTATUS(status)) - return 0; - return -1; -} - -/* An external diff command takes: - * - * diff-cmd name infile1 infile1-sha1 infile1-mode \ - * infile2 infile2-sha1 infile2-mode [ rename-to ] - * - */ -static void run_external_diff(const char *pgm, - const char *name, - const char *other, - struct diff_filespec *one, - struct diff_filespec *two, - const char *xfrm_msg, - int complete_rewrite) -{ - const char *spawn_arg[10]; - struct diff_tempfile *temp = diff_temp; - int retval; - static int atexit_asked = 0; - const char *othername; - const char **arg = &spawn_arg[0]; - - othername = (other? other : name); - if (one && two) { - prepare_temp_file(name, &temp[0], one); - prepare_temp_file(othername, &temp[1], two); - if (! atexit_asked && - (temp[0].name == temp[0].tmp_path || - temp[1].name == temp[1].tmp_path)) { - atexit_asked = 1; - atexit(remove_tempfile); - } - signal(SIGINT, remove_tempfile_on_signal); - } - if (one && two) { - *arg++ = pgm; - *arg++ = name; - *arg++ = temp[0].name; - *arg++ = temp[0].hex; - *arg++ = temp[0].mode; - *arg++ = temp[1].name; - *arg++ = temp[1].hex; - *arg++ = temp[1].mode; - if (other) { - *arg++ = other; - *arg++ = xfrm_msg; - } - } else { - *arg++ = pgm; - *arg++ = name; - } - *arg = NULL; - retval = spawn_prog(pgm, spawn_arg); - remove_tempfile(); - if (retval) { - fprintf(stderr, "external diff died, stopping at %s.\n", name); - exit(1); - } -} - -static void run_diff_cmd(const char *pgm, - const char *name, - const char *other, - struct diff_filespec *one, - struct diff_filespec *two, - const char *xfrm_msg, - int complete_rewrite) -{ - if (pgm) { - run_external_diff(pgm, name, other, one, two, xfrm_msg, - complete_rewrite); - return; - } - if (one && two) - builtin_diff(name, other ? other : name, - one, two, xfrm_msg, complete_rewrite); - else - printf("* Unmerged path %s\n", name); -} - -static void diff_fill_sha1_info(struct diff_filespec *one) -{ - if (DIFF_FILE_VALID(one)) { - if (!one->sha1_valid) { - struct stat st; - if (lstat(one->path, &st) < 0) - die("stat %s", one->path); - if (index_path(one->sha1, one->path, &st, 0)) - die("cannot hash %s\n", one->path); - } - } - else - memset(one->sha1, 0, 20); -} - -static void run_diff(struct diff_filepair *p, struct diff_options *o) -{ - const char *pgm = external_diff(); - char msg[PATH_MAX*2+300], *xfrm_msg; - struct diff_filespec *one; - struct diff_filespec *two; - const char *name; - const char *other; - char *name_munged, *other_munged; - int complete_rewrite = 0; - int len; - - if (DIFF_PAIR_UNMERGED(p)) { - /* unmerged */ - run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0); - return; - } - - name = p->one->path; - other = (strcmp(name, p->two->path) ? p->two->path : NULL); - name_munged = quote_one(name); - other_munged = quote_one(other); - one = p->one; two = p->two; - - diff_fill_sha1_info(one); - diff_fill_sha1_info(two); - - len = 0; - switch (p->status) { - case DIFF_STATUS_COPIED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "copy from %s\n" - "copy to %s\n", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name_munged, other_munged); - break; - case DIFF_STATUS_RENAMED: - len += snprintf(msg + len, sizeof(msg) - len, - "similarity index %d%%\n" - "rename from %s\n" - "rename to %s\n", - (int)(0.5 + p->score * 100.0/MAX_SCORE), - name_munged, other_munged); - break; - case DIFF_STATUS_MODIFIED: - if (p->score) { - len += snprintf(msg + len, sizeof(msg) - len, - "dissimilarity index %d%%\n", - (int)(0.5 + p->score * - 100.0/MAX_SCORE)); - complete_rewrite = 1; - break; - } - /* fallthru */ - default: - /* nothing */ - ; - } - - if (memcmp(one->sha1, two->sha1, 20)) { - char one_sha1[41]; - int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; - memcpy(one_sha1, sha1_to_hex(one->sha1), 41); - - len += snprintf(msg + len, sizeof(msg) - len, - "index %.*s..%.*s", - abbrev, one_sha1, abbrev, - sha1_to_hex(two->sha1)); - if (one->mode == two->mode) - len += snprintf(msg + len, sizeof(msg) - len, - " %06o", one->mode); - len += snprintf(msg + len, sizeof(msg) - len, "\n"); - } - - if (len) - msg[--len] = 0; - xfrm_msg = len ? msg : NULL; - - if (!pgm && - DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && - (S_IFMT & one->mode) != (S_IFMT & two->mode)) { - /* a filepair that changes between file and symlink - * needs to be split into deletion and creation. - */ - struct diff_filespec *null = alloc_filespec(two->path); - run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0); - free(null); - null = alloc_filespec(one->path); - run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0); - free(null); - } - else - run_diff_cmd(pgm, name, other, one, two, xfrm_msg, - complete_rewrite); - - free(name_munged); - free(other_munged); -} - -static void run_diffstat(struct diff_filepair *p, struct diff_options *o, - struct diffstat_t *diffstat) -{ - const char *name; - const char *other; - - if (DIFF_PAIR_UNMERGED(p)) { - /* unmerged */ - builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat); - return; - } - - name = p->one->path; - other = (strcmp(name, p->two->path) ? p->two->path : NULL); - - diff_fill_sha1_info(p->one); - diff_fill_sha1_info(p->two); - - builtin_diffstat(name, other, p->one, p->two, diffstat); -} - -void diff_setup(struct diff_options *options) -{ - memset(options, 0, sizeof(*options)); - options->output_format = DIFF_FORMAT_RAW; - options->line_termination = '\n'; - options->break_opt = -1; - options->rename_limit = -1; - - options->change = diff_change; - options->add_remove = diff_addremove; -} - -int diff_setup_done(struct diff_options *options) -{ - if ((options->find_copies_harder && - options->detect_rename != DIFF_DETECT_COPY) || - (0 <= options->rename_limit && !options->detect_rename)) - return -1; - - /* - * These cases always need recursive; we do not drop caller-supplied - * recursive bits for other formats here. - */ - if ((options->output_format == DIFF_FORMAT_PATCH) || - (options->output_format == DIFF_FORMAT_DIFFSTAT)) - options->recursive = 1; - - if (options->detect_rename && options->rename_limit < 0) - options->rename_limit = diff_rename_limit_default; - if (options->setup & DIFF_SETUP_USE_CACHE) { - if (!active_cache) - /* read-cache does not die even when it fails - * so it is safe for us to do this here. Also - * it does not smudge active_cache or active_nr - * when it fails, so we do not have to worry about - * cleaning it up ourselves either. + if (ce_stage(ce)) { + struct { + struct combine_diff_path p; + struct combine_diff_parent filler[5]; + } combine; + int num_compare_stages = 0; + + combine.p.next = NULL; + combine.p.len = ce_namelen(ce); + combine.p.path = xmalloc(combine.p.len + 1); + memcpy(combine.p.path, ce->name, combine.p.len); + combine.p.path[combine.p.len] = 0; + combine.p.mode = 0; + memset(combine.p.sha1, 0, 20); + memset(&combine.p.parent[0], 0, + sizeof(combine.filler)); + + while (i < entries) { + struct cache_entry *nce = active_cache[i]; + int stage; + + if (strcmp(ce->name, nce->name)) + break; + + /* Stage #2 (ours) is the first parent, + * stage #3 (theirs) is the second. + */ + stage = ce_stage(nce); + if (2 <= stage) { + int mode = ntohl(nce->ce_mode); + num_compare_stages++; + memcpy(combine.p.parent[stage-2].sha1, + nce->sha1, 20); + combine.p.parent[stage-2].mode = + canon_mode(mode); + combine.p.parent[stage-2].status = + DIFF_STATUS_MODIFIED; + } + + /* diff against the proper unmerged stage */ + if (stage == diff_unmerged_stage) + ce = nce; + i++; + } + /* + * Compensate for loop update */ - read_cache(); - } - if (options->setup & DIFF_SETUP_USE_SIZE_CACHE) - use_size_cache = 1; - if (options->abbrev <= 0 || 40 < options->abbrev) - options->abbrev = 40; /* full */ + i--; - return 0; -} - -int diff_opt_parse(struct diff_options *options, const char **av, int ac) -{ - const char *arg = av[0]; - if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) - options->output_format = DIFF_FORMAT_PATCH; - else if (!strcmp(arg, "--patch-with-raw")) { - options->output_format = DIFF_FORMAT_PATCH; - options->with_raw = 1; - } - else if (!strcmp(arg, "--stat")) - options->output_format = DIFF_FORMAT_DIFFSTAT; - else if (!strcmp(arg, "--patch-with-stat")) { - options->output_format = DIFF_FORMAT_PATCH; - options->with_stat = 1; - } - else if (!strcmp(arg, "-z")) - options->line_termination = 0; - else if (!strncmp(arg, "-l", 2)) - options->rename_limit = strtoul(arg+2, NULL, 10); - else if (!strcmp(arg, "--full-index")) - options->full_index = 1; - else if (!strcmp(arg, "--name-only")) - options->output_format = DIFF_FORMAT_NAME; - else if (!strcmp(arg, "--name-status")) - options->output_format = DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(arg, "-R")) - options->reverse_diff = 1; - else if (!strncmp(arg, "-S", 2)) - options->pickaxe = arg + 2; - else if (!strcmp(arg, "-s")) - options->output_format = DIFF_FORMAT_NO_OUTPUT; - else if (!strncmp(arg, "-O", 2)) - options->orderfile = arg + 2; - else if (!strncmp(arg, "--diff-filter=", 14)) - options->filter = arg + 14; - else if (!strcmp(arg, "--pickaxe-all")) - options->pickaxe_opts = DIFF_PICKAXE_ALL; - else if (!strcmp(arg, "--pickaxe-regex")) - options->pickaxe_opts = DIFF_PICKAXE_REGEX; - else if (!strncmp(arg, "-B", 2)) { - if ((options->break_opt = - diff_scoreopt_parse(arg)) == -1) - return -1; - } - else if (!strncmp(arg, "-M", 2)) { - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) - return -1; - options->detect_rename = DIFF_DETECT_RENAME; - } - else if (!strncmp(arg, "-C", 2)) { - if ((options->rename_score = - diff_scoreopt_parse(arg)) == -1) - return -1; - options->detect_rename = DIFF_DETECT_COPY; - } - else if (!strcmp(arg, "--find-copies-harder")) - options->find_copies_harder = 1; - else if (!strcmp(arg, "--abbrev")) - options->abbrev = DEFAULT_ABBREV; - else if (!strncmp(arg, "--abbrev=", 9)) { - options->abbrev = strtoul(arg + 9, NULL, 10); - if (options->abbrev < MINIMUM_ABBREV) - options->abbrev = MINIMUM_ABBREV; - else if (40 < options->abbrev) - options->abbrev = 40; - } - else - return 0; - return 1; -} - -static int parse_num(const char **cp_p) -{ - unsigned long num, scale; - int ch, dot; - const char *cp = *cp_p; - - num = 0; - scale = 1; - dot = 0; - for(;;) { - ch = *cp; - if ( !dot && ch == '.' ) { - scale = 1; - dot = 1; - } else if ( ch == '%' ) { - scale = dot ? scale*100 : 100; - cp++; /* % is always at the end */ - break; - } else if ( ch >= '0' && ch <= '9' ) { - if ( scale < 100000 ) { - scale *= 10; - num = (num*10) + (ch-'0'); + if (revs->combine_merges && num_compare_stages == 2) { + show_combined_diff(&combine.p, 2, + revs->dense_combined_merges, + revs); + free(combine.p.path); + continue; } - } else { - break; - } - cp++; - } - *cp_p = cp; - - /* user says num divided by scale and we say internally that - * is MAX_SCORE * num / scale. - */ - return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale); -} - -int diff_scoreopt_parse(const char *opt) -{ - int opt1, opt2, cmd; + free(combine.p.path); - if (*opt++ != '-') - return -1; - cmd = *opt++; - if (cmd != 'M' && cmd != 'C' && cmd != 'B') - return -1; /* that is not a -M, -C nor -B option */ - - opt1 = parse_num(&opt); - if (cmd != 'B') - opt2 = 0; - else { - if (*opt == 0) - opt2 = 0; - else if (*opt != '/') - return -1; /* we expect -B80/99 or -B80 */ - else { - opt++; - opt2 = parse_num(&opt); + /* + * Show the diff for the 'ce' if we found the one + * from the desired stage. + */ + diff_unmerge(&revs->diffopt, ce->name); + if (ce_stage(ce) != diff_unmerged_stage) + continue; } - } - if (*opt != 0) - return -1; - return opt1 | (opt2 << 16); -} - -struct diff_queue_struct diff_queued_diff; -void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp) -{ - if (queue->alloc <= queue->nr) { - queue->alloc = alloc_nr(queue->alloc); - queue->queue = xrealloc(queue->queue, - sizeof(dp) * queue->alloc); - } - queue->queue[queue->nr++] = dp; -} - -struct diff_filepair *diff_queue(struct diff_queue_struct *queue, - struct diff_filespec *one, - struct diff_filespec *two) -{ - struct diff_filepair *dp = xmalloc(sizeof(*dp)); - dp->one = one; - dp->two = two; - dp->score = 0; - dp->status = 0; - dp->source_stays = 0; - dp->broken_pair = 0; - if (queue) - diff_q(queue, dp); - return dp; -} - -void diff_free_filepair(struct diff_filepair *p) -{ - diff_free_filespec_data(p->one); - diff_free_filespec_data(p->two); - free(p->one); - free(p->two); - free(p); -} - -/* This is different from find_unique_abbrev() in that - * it stuffs the result with dots for alignment. - */ -const char *diff_unique_abbrev(const unsigned char *sha1, int len) -{ - int abblen; - const char *abbrev; - if (len == 40) - return sha1_to_hex(sha1); - - abbrev = find_unique_abbrev(sha1, len); - if (!abbrev) - return sha1_to_hex(sha1); - abblen = strlen(abbrev); - if (abblen < 37) { - static char hex[41]; - if (len < abblen && abblen <= len + 2) - sprintf(hex, "%s%.*s", abbrev, len+3-abblen, ".."); - else - sprintf(hex, "%s...", abbrev); - return hex; - } - return sha1_to_hex(sha1); -} - -static void diff_flush_raw(struct diff_filepair *p, - int line_termination, - int inter_name_termination, - struct diff_options *options, - int output_format) -{ - int two_paths; - char status[10]; - int abbrev = options->abbrev; - const char *path_one, *path_two; - - path_one = p->one->path; - path_two = p->two->path; - if (line_termination) { - path_one = quote_one(path_one); - path_two = quote_one(path_two); - } - - if (p->score) - sprintf(status, "%c%03d", p->status, - (int)(0.5 + p->score * 100.0/MAX_SCORE)); - else { - status[0] = p->status; - status[1] = 0; - } - switch (p->status) { - case DIFF_STATUS_COPIED: - case DIFF_STATUS_RENAMED: - two_paths = 1; - break; - case DIFF_STATUS_ADDED: - case DIFF_STATUS_DELETED: - two_paths = 0; - break; - default: - two_paths = 0; - break; - } - if (output_format != DIFF_FORMAT_NAME_STATUS) { - printf(":%06o %06o %s ", - p->one->mode, p->two->mode, - diff_unique_abbrev(p->one->sha1, abbrev)); - printf("%s ", - diff_unique_abbrev(p->two->sha1, abbrev)); - } - printf("%s%c%s", status, inter_name_termination, path_one); - if (two_paths) - printf("%c%s", inter_name_termination, path_two); - putchar(line_termination); - if (path_one != p->one->path) - free((void*)path_one); - if (path_two != p->two->path) - free((void*)path_two); -} - -static void diff_flush_name(struct diff_filepair *p, - int inter_name_termination, - int line_termination) -{ - char *path = p->two->path; - - if (line_termination) - path = quote_one(p->two->path); - else - path = p->two->path; - printf("%s%c", path, line_termination); - if (p->two->path != path) - free(path); -} - -int diff_unmodified_pair(struct diff_filepair *p) -{ - /* This function is written stricter than necessary to support - * the currently implemented transformers, but the idea is to - * let transformers to produce diff_filepairs any way they want, - * and filter and clean them up here before producing the output. - */ - struct diff_filespec *one, *two; - - if (DIFF_PAIR_UNMERGED(p)) - return 0; /* unmerged is interesting */ - - one = p->one; - two = p->two; - - /* deletion, addition, mode or type change - * and rename are all interesting. - */ - if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) || - DIFF_PAIR_MODE_CHANGED(p) || - strcmp(one->path, two->path)) - return 0; - - /* both are valid and point at the same path. that is, we are - * dealing with a change. - */ - if (one->sha1_valid && two->sha1_valid && - !memcmp(one->sha1, two->sha1, sizeof(one->sha1))) - return 1; /* no change */ - if (!one->sha1_valid && !two->sha1_valid) - return 1; /* both look at the same file on the filesystem. */ - return 0; -} - -static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) -{ - if (diff_unmodified_pair(p)) - return; - - if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || - (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) - return; /* no tree diffs in patch format */ - - run_diff(p, o); -} - -static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, - struct diffstat_t *diffstat) -{ - if (diff_unmodified_pair(p)) - return; - - if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || - (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) - return; /* no tree diffs in patch format */ - - run_diffstat(p, o, diffstat); -} - -int diff_queue_is_empty(void) -{ - struct diff_queue_struct *q = &diff_queued_diff; - int i; - for (i = 0; i < q->nr; i++) - if (!diff_unmodified_pair(q->queue[i])) - return 0; - return 1; -} - -#if DIFF_DEBUG -void diff_debug_filespec(struct diff_filespec *s, int x, const char *one) -{ - fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n", - x, one ? one : "", - s->path, - DIFF_FILE_VALID(s) ? "valid" : "invalid", - s->mode, - s->sha1_valid ? sha1_to_hex(s->sha1) : ""); - fprintf(stderr, "queue[%d] %s size %lu flags %d\n", - x, one ? one : "", - s->size, s->xfrm_flags); -} - -void diff_debug_filepair(const struct diff_filepair *p, int i) -{ - diff_debug_filespec(p->one, i, "one"); - diff_debug_filespec(p->two, i, "two"); - fprintf(stderr, "score %d, status %c stays %d broken %d\n", - p->score, p->status ? p->status : '?', - p->source_stays, p->broken_pair); -} - -void diff_debug_queue(const char *msg, struct diff_queue_struct *q) -{ - int i; - if (msg) - fprintf(stderr, "%s\n", msg); - fprintf(stderr, "q->nr = %d\n", q->nr); - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - diff_debug_filepair(p, i); - } -} -#endif - -static void diff_resolve_rename_copy(void) -{ - int i, j; - struct diff_filepair *p, *pp; - struct diff_queue_struct *q = &diff_queued_diff; - - diff_debug_queue("resolve-rename-copy", q); - - for (i = 0; i < q->nr; i++) { - p = q->queue[i]; - p->status = 0; /* undecided */ - if (DIFF_PAIR_UNMERGED(p)) - p->status = DIFF_STATUS_UNMERGED; - else if (!DIFF_FILE_VALID(p->one)) - p->status = DIFF_STATUS_ADDED; - else if (!DIFF_FILE_VALID(p->two)) - p->status = DIFF_STATUS_DELETED; - else if (DIFF_PAIR_TYPE_CHANGED(p)) - p->status = DIFF_STATUS_TYPE_CHANGED; - - /* from this point on, we are dealing with a pair - * whose both sides are valid and of the same type, i.e. - * either in-place edit or rename/copy edit. - */ - else if (DIFF_PAIR_RENAME(p)) { - if (p->source_stays) { - p->status = DIFF_STATUS_COPIED; + if (lstat(ce->name, &st) < 0) { + if (errno != ENOENT && errno != ENOTDIR) { + perror(ce->name); continue; } - /* See if there is some other filepair that - * copies from the same source as us. If so - * we are a copy. Otherwise we are either a - * copy if the path stays, or a rename if it - * does not, but we already handled "stays" case. - */ - for (j = i + 1; j < q->nr; j++) { - pp = q->queue[j]; - if (strcmp(pp->one->path, p->one->path)) - continue; /* not us */ - if (!DIFF_PAIR_RENAME(pp)) - continue; /* not a rename/copy */ - /* pp is a rename/copy from the same source */ - p->status = DIFF_STATUS_COPIED; - break; - } - if (!p->status) - p->status = DIFF_STATUS_RENAMED; - } - else if (memcmp(p->one->sha1, p->two->sha1, 20) || - p->one->mode != p->two->mode) - p->status = DIFF_STATUS_MODIFIED; - else { - /* This is a "no-change" entry and should not - * happen anymore, but prepare for broken callers. - */ - error("feeding unmodified %s to diffcore", - p->one->path); - p->status = DIFF_STATUS_UNKNOWN; - } - } - diff_debug_queue("resolve-rename-copy done", q); -} - -static void flush_one_pair(struct diff_filepair *p, - int diff_output_format, - struct diff_options *options, - struct diffstat_t *diffstat) -{ - int inter_name_termination = '\t'; - int line_termination = options->line_termination; - if (!line_termination) - inter_name_termination = 0; - - switch (p->status) { - case DIFF_STATUS_UNKNOWN: - break; - case 0: - die("internal error in diff-resolve-rename-copy"); - break; - default: - switch (diff_output_format) { - case DIFF_FORMAT_DIFFSTAT: - diff_flush_stat(p, options, diffstat); - break; - case DIFF_FORMAT_PATCH: - diff_flush_patch(p, options); - break; - case DIFF_FORMAT_RAW: - case DIFF_FORMAT_NAME_STATUS: - diff_flush_raw(p, line_termination, - inter_name_termination, - options, diff_output_format); - break; - case DIFF_FORMAT_NAME: - diff_flush_name(p, - inter_name_termination, - line_termination); - break; - case DIFF_FORMAT_NO_OUTPUT: - break; - } - } -} - -void diff_flush(struct diff_options *options) -{ - struct diff_queue_struct *q = &diff_queued_diff; - int i; - int diff_output_format = options->output_format; - struct diffstat_t *diffstat = NULL; - - if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) { - diffstat = xcalloc(sizeof (struct diffstat_t), 1); - diffstat->xm.consume = diffstat_consume; - } - - if (options->with_raw) { - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL); - } - putchar(options->line_termination); - } - if (options->with_stat) { - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options, - diffstat); - } - show_stats(diffstat); - free(diffstat); - diffstat = NULL; - putchar(options->line_termination); - } - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - flush_one_pair(p, diff_output_format, options, diffstat); - diff_free_filepair(p); - } - - if (diffstat) { - show_stats(diffstat); - free(diffstat); - } - - free(q->queue); - q->queue = NULL; - q->nr = q->alloc = 0; -} - -static void diffcore_apply_filter(const char *filter) -{ - int i; - struct diff_queue_struct *q = &diff_queued_diff; - struct diff_queue_struct outq; - outq.queue = NULL; - outq.nr = outq.alloc = 0; - - if (!filter) - return; - - if (strchr(filter, DIFF_STATUS_FILTER_AON)) { - int found; - for (i = found = 0; !found && i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (((p->status == DIFF_STATUS_MODIFIED) && - ((p->score && - strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || - (!p->score && - strchr(filter, DIFF_STATUS_MODIFIED)))) || - ((p->status != DIFF_STATUS_MODIFIED) && - strchr(filter, p->status))) - found++; - } - if (found) - return; - - /* otherwise we will clear the whole queue - * by copying the empty outq at the end of this - * function, but first clear the current entries - * in the queue. - */ - for (i = 0; i < q->nr; i++) - diff_free_filepair(q->queue[i]); - } - else { - /* Only the matching ones */ - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - - if (((p->status == DIFF_STATUS_MODIFIED) && - ((p->score && - strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || - (!p->score && - strchr(filter, DIFF_STATUS_MODIFIED)))) || - ((p->status != DIFF_STATUS_MODIFIED) && - strchr(filter, p->status))) - diff_q(&outq, p); - else - diff_free_filepair(p); + if (silent_on_removed) + continue; + diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode), + ce->sha1, ce->name, NULL); + continue; } - } - free(q->queue); - *q = outq; -} - -void diffcore_std(struct diff_options *options) -{ - if (options->break_opt != -1) - diffcore_break(options->break_opt); - if (options->detect_rename) - diffcore_rename(options); - if (options->break_opt != -1) - diffcore_merge_broken(); - if (options->pickaxe) - diffcore_pickaxe(options->pickaxe, options->pickaxe_opts); - if (options->orderfile) - diffcore_order(options->orderfile); - diff_resolve_rename_copy(); - diffcore_apply_filter(options->filter); -} - - -void diffcore_std_no_resolve(struct diff_options *options) -{ - if (options->pickaxe) - diffcore_pickaxe(options->pickaxe, options->pickaxe_opts); - if (options->orderfile) - diffcore_order(options->orderfile); - diffcore_apply_filter(options->filter); -} - -void diff_addremove(struct diff_options *options, - int addremove, unsigned mode, - const unsigned char *sha1, - const char *base, const char *path) -{ - char concatpath[PATH_MAX]; - struct diff_filespec *one, *two; - - /* This may look odd, but it is a preparation for - * feeding "there are unchanged files which should - * not produce diffs, but when you are doing copy - * detection you would need them, so here they are" - * entries to the diff-core. They will be prefixed - * with something like '=' or '*' (I haven't decided - * which but should not make any difference). - * Feeding the same new and old to diff_change() - * also has the same effect. - * Before the final output happens, they are pruned after - * merged into rename/copy pairs as appropriate. - */ - if (options->reverse_diff) - addremove = (addremove == '+' ? '-' : - addremove == '-' ? '+' : addremove); - - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); - one = alloc_filespec(concatpath); - two = alloc_filespec(concatpath); - - if (addremove != '+') - fill_filespec(one, sha1, mode); - if (addremove != '-') - fill_filespec(two, sha1, mode); - - diff_queue(&diff_queued_diff, one, two); -} + changed = ce_match_stat(ce, &st, 0); + if (!changed && !revs->diffopt.find_copies_harder) + continue; + oldmode = ntohl(ce->ce_mode); -void diff_change(struct diff_options *options, - unsigned old_mode, unsigned new_mode, - const unsigned char *old_sha1, - const unsigned char *new_sha1, - const char *base, const char *path) -{ - char concatpath[PATH_MAX]; - struct diff_filespec *one, *two; + newmode = canon_mode(st.st_mode); + if (!trust_executable_bit && + S_ISREG(newmode) && S_ISREG(oldmode) && + ((newmode ^ oldmode) == 0111)) + newmode = oldmode; + diff_change(&revs->diffopt, oldmode, newmode, + ce->sha1, (changed ? null_sha1 : ce->sha1), + ce->name, NULL); - if (options->reverse_diff) { - unsigned tmp; - const unsigned char *tmp_c; - tmp = old_mode; old_mode = new_mode; new_mode = tmp; - tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; } - if (!path) path = ""; - sprintf(concatpath, "%s%s", base, path); - one = alloc_filespec(concatpath); - two = alloc_filespec(concatpath); - fill_filespec(one, old_sha1, old_mode); - fill_filespec(two, new_sha1, new_mode); - - diff_queue(&diff_queued_diff, one, two); + diffcore_std(&revs->diffopt); + diff_flush(&revs->diffopt); + return 0; } -void diff_unmerge(struct diff_options *options, - const char *path) -{ - struct diff_filespec *one, *two; - one = alloc_filespec(path); - two = alloc_filespec(path); - diff_queue(&diff_queued_diff, one, two); -} diff --git a/diff.c b/diff.c new file mode 100644 index 0000000..13b216f --- /dev/null +++ b/diff.c @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2005 Junio C Hamano + */ +#include +#include +#include +#include "cache.h" +#include "quote.h" +#include "diff.h" +#include "diffcore.h" +#include "xdiff-interface.h" + +static int use_size_cache; + +int diff_rename_limit_default = -1; + +int git_diff_config(const char *var, const char *value) +{ + if (!strcmp(var, "diff.renamelimit")) { + diff_rename_limit_default = git_config_int(var, value); + return 0; + } + + return git_default_config(var, value); +} + +static char *quote_one(const char *str) +{ + int needlen; + char *xp; + + if (!str) + return NULL; + needlen = quote_c_style(str, NULL, NULL, 0); + if (!needlen) + return strdup(str); + xp = xmalloc(needlen + 1); + quote_c_style(str, xp, NULL, 0); + return xp; +} + +static char *quote_two(const char *one, const char *two) +{ + int need_one = quote_c_style(one, NULL, NULL, 1); + int need_two = quote_c_style(two, NULL, NULL, 1); + char *xp; + + if (need_one + need_two) { + if (!need_one) need_one = strlen(one); + if (!need_two) need_one = strlen(two); + + xp = xmalloc(need_one + need_two + 3); + xp[0] = '"'; + quote_c_style(one, xp + 1, NULL, 1); + quote_c_style(two, xp + need_one + 1, NULL, 1); + strcpy(xp + need_one + need_two + 1, "\""); + return xp; + } + need_one = strlen(one); + need_two = strlen(two); + xp = xmalloc(need_one + need_two + 1); + strcpy(xp, one); + strcpy(xp + need_one, two); + return xp; +} + +static const char *external_diff(void) +{ + static const char *external_diff_cmd = NULL; + static int done_preparing = 0; + + if (done_preparing) + return external_diff_cmd; + external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); + done_preparing = 1; + return external_diff_cmd; +} + +#define TEMPFILE_PATH_LEN 50 + +static struct diff_tempfile { + const char *name; /* filename external diff should read from */ + char hex[41]; + char mode[10]; + char tmp_path[TEMPFILE_PATH_LEN]; +} diff_temp[2]; + +static int count_lines(const char *data, int size) +{ + int count, ch, completely_empty = 1, nl_just_seen = 0; + count = 0; + while (0 < size--) { + ch = *data++; + if (ch == '\n') { + count++; + nl_just_seen = 1; + completely_empty = 0; + } + else { + nl_just_seen = 0; + completely_empty = 0; + } + } + if (completely_empty) + return 0; + if (!nl_just_seen) + count++; /* no trailing newline */ + return count; +} + +static void print_line_count(int count) +{ + switch (count) { + case 0: + printf("0,0"); + break; + case 1: + printf("1"); + break; + default: + printf("1,%d", count); + break; + } +} + +static void copy_file(int prefix, const char *data, int size) +{ + int ch, nl_just_seen = 1; + while (0 < size--) { + ch = *data++; + if (nl_just_seen) + putchar(prefix); + putchar(ch); + if (ch == '\n') + nl_just_seen = 1; + else + nl_just_seen = 0; + } + if (!nl_just_seen) + printf("\n\\ No newline at end of file\n"); +} + +static void emit_rewrite_diff(const char *name_a, + const char *name_b, + struct diff_filespec *one, + struct diff_filespec *two) +{ + int lc_a, lc_b; + diff_populate_filespec(one, 0); + diff_populate_filespec(two, 0); + lc_a = count_lines(one->data, one->size); + lc_b = count_lines(two->data, two->size); + printf("--- %s\n+++ %s\n@@ -", name_a, name_b); + print_line_count(lc_a); + printf(" +"); + print_line_count(lc_b); + printf(" @@\n"); + if (lc_a) + copy_file('-', one->data, one->size); + if (lc_b) + copy_file('+', two->data, two->size); +} + +static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) +{ + if (!DIFF_FILE_VALID(one)) { + mf->ptr = ""; /* does not matter */ + mf->size = 0; + return 0; + } + else if (diff_populate_filespec(one, 0)) + return -1; + mf->ptr = one->data; + mf->size = one->size; + return 0; +} + +struct emit_callback { + const char **label_path; +}; + +static int fn_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + int i; + struct emit_callback *ecbdata = priv; + + if (ecbdata->label_path[0]) { + printf("--- %s\n", ecbdata->label_path[0]); + printf("+++ %s\n", ecbdata->label_path[1]); + ecbdata->label_path[0] = ecbdata->label_path[1] = NULL; + } + for (i = 0; i < nbuf; i++) + if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout)) + return -1; + return 0; +} + +static char *pprint_rename(const char *a, const char *b) +{ + const char *old = a; + const char *new = b; + char *name = NULL; + int pfx_length, sfx_length; + int len_a = strlen(a); + int len_b = strlen(b); + + /* Find common prefix */ + pfx_length = 0; + while (*old && *new && *old == *new) { + if (*old == '/') + pfx_length = old - a + 1; + old++; + new++; + } + + /* Find common suffix */ + old = a + len_a; + new = b + len_b; + sfx_length = 0; + while (a <= old && b <= new && *old == *new) { + if (*old == '/') + sfx_length = len_a - (old - a); + old--; + new--; + } + + /* + * pfx{mid-a => mid-b}sfx + * {pfx-a => pfx-b}sfx + * pfx{sfx-a => sfx-b} + * name-a => name-b + */ + if (pfx_length + sfx_length) { + name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7); + sprintf(name, "%.*s{%.*s => %.*s}%s", + pfx_length, a, + len_a - pfx_length - sfx_length, a + pfx_length, + len_b - pfx_length - sfx_length, b + pfx_length, + a + len_a - sfx_length); + } + else { + name = xmalloc(len_a + len_b + 5); + sprintf(name, "%s => %s", a, b); + } + return name; +} + +struct diffstat_t { + struct xdiff_emit_state xm; + + int nr; + int alloc; + struct diffstat_file { + char *name; + unsigned is_unmerged:1; + unsigned is_binary:1; + unsigned is_renamed:1; + unsigned int added, deleted; + } **files; +}; + +static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, + const char *name_a, + const char *name_b) +{ + struct diffstat_file *x; + x = xcalloc(sizeof (*x), 1); + if (diffstat->nr == diffstat->alloc) { + diffstat->alloc = alloc_nr(diffstat->alloc); + diffstat->files = xrealloc(diffstat->files, + diffstat->alloc * sizeof(x)); + } + diffstat->files[diffstat->nr++] = x; + if (name_b) { + x->name = pprint_rename(name_a, name_b); + x->is_renamed = 1; + } + else + x->name = strdup(name_a); + return x; +} + +static void diffstat_consume(void *priv, char *line, unsigned long len) +{ + struct diffstat_t *diffstat = priv; + struct diffstat_file *x = diffstat->files[diffstat->nr - 1]; + + if (line[0] == '+') + x->added++; + else if (line[0] == '-') + x->deleted++; +} + +static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; +static const char minuses[]= "----------------------------------------------------------------------"; + +static void show_stats(struct diffstat_t* data) +{ + char *prefix = ""; + int i, len, add, del, total, adds = 0, dels = 0; + int max, max_change = 0, max_len = 0; + int total_files = data->nr; + + if (data->nr == 0) + return; + + for (i = 0; i < data->nr; i++) { + struct diffstat_file *file = data->files[i]; + + len = strlen(file->name); + if (max_len < len) + max_len = len; + + if (file->is_binary || file->is_unmerged) + continue; + if (max_change < file->added + file->deleted) + max_change = file->added + file->deleted; + } + + for (i = 0; i < data->nr; i++) { + char *name = data->files[i]->name; + int added = data->files[i]->added; + int deleted = data->files[i]->deleted; + + if (0 < (len = quote_c_style(name, NULL, NULL, 0))) { + char *qname = xmalloc(len + 1); + quote_c_style(name, qname, NULL, 0); + free(name); + data->files[i]->name = name = qname; + } + + /* + * "scale" the filename + */ + len = strlen(name); + max = max_len; + if (max > 50) + max = 50; + if (len > max) { + char *slash; + prefix = "..."; + max -= 3; + name += len - max; + slash = strchr(name, '/'); + if (slash) + name = slash; + } + len = max; + + /* + * scale the add/delete + */ + max = max_change; + if (max + len > 70) + max = 70 - len; + + if (data->files[i]->is_binary) { + printf(" %s%-*s | Bin\n", prefix, len, name); + goto free_diffstat_file; + } + else if (data->files[i]->is_unmerged) { + printf(" %s%-*s | Unmerged\n", prefix, len, name); + goto free_diffstat_file; + } + else if (!data->files[i]->is_renamed && + (added + deleted == 0)) { + total_files--; + goto free_diffstat_file; + } + + add = added; + del = deleted; + total = add + del; + adds += add; + dels += del; + + if (max_change > 0) { + total = (total * max + max_change / 2) / max_change; + add = (add * max + max_change / 2) / max_change; + del = total - add; + } + printf(" %s%-*s |%5d %.*s%.*s\n", prefix, + len, name, added + deleted, + add, pluses, del, minuses); + free_diffstat_file: + free(data->files[i]->name); + free(data->files[i]); + } + free(data->files); + printf(" %d files changed, %d insertions(+), %d deletions(-)\n", + total_files, adds, dels); +} + +#define FIRST_FEW_BYTES 8000 +static int mmfile_is_binary(mmfile_t *mf) +{ + long sz = mf->size; + if (FIRST_FEW_BYTES < sz) + sz = FIRST_FEW_BYTES; + if (memchr(mf->ptr, 0, sz)) + return 1; + return 0; +} + +static void builtin_diff(const char *name_a, + const char *name_b, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg, + int complete_rewrite) +{ + mmfile_t mf1, mf2; + const char *lbl[2]; + char *a_one, *b_two; + + a_one = quote_two("a/", name_a); + b_two = quote_two("b/", name_b); + lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; + lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; + printf("diff --git %s %s\n", a_one, b_two); + if (lbl[0][0] == '/') { + /* /dev/null */ + printf("new file mode %06o\n", two->mode); + if (xfrm_msg && xfrm_msg[0]) + puts(xfrm_msg); + } + else if (lbl[1][0] == '/') { + printf("deleted file mode %06o\n", one->mode); + if (xfrm_msg && xfrm_msg[0]) + puts(xfrm_msg); + } + else { + if (one->mode != two->mode) { + printf("old mode %06o\n", one->mode); + printf("new mode %06o\n", two->mode); + } + if (xfrm_msg && xfrm_msg[0]) + puts(xfrm_msg); + /* + * we do not run diff between different kind + * of objects. + */ + if ((one->mode ^ two->mode) & S_IFMT) + goto free_ab_and_return; + if (complete_rewrite) { + emit_rewrite_diff(name_a, name_b, one, two); + goto free_ab_and_return; + } + } + + if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + die("unable to read files to diff"); + + if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) + printf("Binary files %s and %s differ\n", lbl[0], lbl[1]); + else { + /* Crazy xdl interfaces.. */ + const char *diffopts = getenv("GIT_DIFF_OPTS"); + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + struct emit_callback ecbdata; + + ecbdata.label_path = lbl; + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 3; + xecfg.flags = XDL_EMIT_FUNCNAMES; + if (!diffopts) + ; + else if (!strncmp(diffopts, "--unified=", 10)) + xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); + else if (!strncmp(diffopts, "-u", 2)) + xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); + ecb.outf = fn_out; + ecb.priv = &ecbdata; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } + + free_ab_and_return: + free(a_one); + free(b_two); + return; +} + +static void builtin_diffstat(const char *name_a, const char *name_b, + struct diff_filespec *one, + struct diff_filespec *two, + struct diffstat_t *diffstat) +{ + mmfile_t mf1, mf2; + struct diffstat_file *data; + + data = diffstat_add(diffstat, name_a, name_b); + + if (!one || !two) { + data->is_unmerged = 1; + return; + } + + if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + die("unable to read files to diff"); + + if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) + data->is_binary = 1; + else { + /* Crazy xdl interfaces.. */ + xpparam_t xpp; + xdemitconf_t xecfg; + xdemitcb_t ecb; + + xpp.flags = XDF_NEED_MINIMAL; + xecfg.ctxlen = 0; + xecfg.flags = 0; + ecb.outf = xdiff_outf; + ecb.priv = diffstat; + xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); + } +} + +struct diff_filespec *alloc_filespec(const char *path) +{ + int namelen = strlen(path); + struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1); + + memset(spec, 0, sizeof(*spec)); + spec->path = (char *)(spec + 1); + memcpy(spec->path, path, namelen+1); + return spec; +} + +void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1, + unsigned short mode) +{ + if (mode) { + spec->mode = canon_mode(mode); + memcpy(spec->sha1, sha1, 20); + spec->sha1_valid = !!memcmp(sha1, null_sha1, 20); + } +} + +/* + * Given a name and sha1 pair, if the dircache tells us the file in + * the work tree has that object contents, return true, so that + * prepare_temp_file() does not have to inflate and extract. + */ +static int work_tree_matches(const char *name, const unsigned char *sha1) +{ + struct cache_entry *ce; + struct stat st; + int pos, len; + + /* We do not read the cache ourselves here, because the + * benchmark with my previous version that always reads cache + * shows that it makes things worse for diff-tree comparing + * two linux-2.6 kernel trees in an already checked out work + * tree. This is because most diff-tree comparisons deal with + * only a small number of files, while reading the cache is + * expensive for a large project, and its cost outweighs the + * savings we get by not inflating the object to a temporary + * file. Practically, this code only helps when we are used + * by diff-cache --cached, which does read the cache before + * calling us. + */ + if (!active_cache) + return 0; + + len = strlen(name); + pos = cache_name_pos(name, len); + if (pos < 0) + return 0; + ce = active_cache[pos]; + if ((lstat(name, &st) < 0) || + !S_ISREG(st.st_mode) || /* careful! */ + ce_match_stat(ce, &st, 0) || + memcmp(sha1, ce->sha1, 20)) + return 0; + /* we return 1 only when we can stat, it is a regular file, + * stat information matches, and sha1 recorded in the cache + * matches. I.e. we know the file in the work tree really is + * the same as the pair. + */ + return 1; +} + +static struct sha1_size_cache { + unsigned char sha1[20]; + unsigned long size; +} **sha1_size_cache; +static int sha1_size_cache_nr, sha1_size_cache_alloc; + +static struct sha1_size_cache *locate_size_cache(unsigned char *sha1, + int find_only, + unsigned long size) +{ + int first, last; + struct sha1_size_cache *e; + + first = 0; + last = sha1_size_cache_nr; + while (last > first) { + int cmp, next = (last + first) >> 1; + e = sha1_size_cache[next]; + cmp = memcmp(e->sha1, sha1, 20); + if (!cmp) + return e; + if (cmp < 0) { + last = next; + continue; + } + first = next+1; + } + /* not found */ + if (find_only) + return NULL; + /* insert to make it at "first" */ + if (sha1_size_cache_alloc <= sha1_size_cache_nr) { + sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc); + sha1_size_cache = xrealloc(sha1_size_cache, + sha1_size_cache_alloc * + sizeof(*sha1_size_cache)); + } + sha1_size_cache_nr++; + if (first < sha1_size_cache_nr) + memmove(sha1_size_cache + first + 1, sha1_size_cache + first, + (sha1_size_cache_nr - first - 1) * + sizeof(*sha1_size_cache)); + e = xmalloc(sizeof(struct sha1_size_cache)); + sha1_size_cache[first] = e; + memcpy(e->sha1, sha1, 20); + e->size = size; + return e; +} + +/* + * While doing rename detection and pickaxe operation, we may need to + * grab the data for the blob (or file) for our own in-core comparison. + * diff_filespec has data and size fields for this purpose. + */ +int diff_populate_filespec(struct diff_filespec *s, int size_only) +{ + int err = 0; + if (!DIFF_FILE_VALID(s)) + die("internal error: asking to populate invalid file."); + if (S_ISDIR(s->mode)) + return -1; + + if (!use_size_cache) + size_only = 0; + + if (s->data) + return err; + if (!s->sha1_valid || + work_tree_matches(s->path, s->sha1)) { + struct stat st; + int fd; + if (lstat(s->path, &st) < 0) { + if (errno == ENOENT) { + err_empty: + err = -1; + empty: + s->data = ""; + s->size = 0; + return err; + } + } + s->size = st.st_size; + if (!s->size) + goto empty; + if (size_only) + return 0; + if (S_ISLNK(st.st_mode)) { + int ret; + s->data = xmalloc(s->size); + s->should_free = 1; + ret = readlink(s->path, s->data, s->size); + if (ret < 0) { + free(s->data); + goto err_empty; + } + return 0; + } + fd = open(s->path, O_RDONLY); + if (fd < 0) + goto err_empty; + s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (s->data == MAP_FAILED) + goto err_empty; + s->should_munmap = 1; + } + else { + char type[20]; + struct sha1_size_cache *e; + + if (size_only) { + e = locate_size_cache(s->sha1, 1, 0); + if (e) { + s->size = e->size; + return 0; + } + if (!sha1_object_info(s->sha1, type, &s->size)) + locate_size_cache(s->sha1, 0, s->size); + } + else { + s->data = read_sha1_file(s->sha1, type, &s->size); + s->should_free = 1; + } + } + return 0; +} + +void diff_free_filespec_data(struct diff_filespec *s) +{ + if (s->should_free) + free(s->data); + else if (s->should_munmap) + munmap(s->data, s->size); + s->should_free = s->should_munmap = 0; + s->data = NULL; + free(s->cnt_data); + s->cnt_data = NULL; +} + +static void prep_temp_blob(struct diff_tempfile *temp, + void *blob, + unsigned long size, + const unsigned char *sha1, + int mode) +{ + int fd; + + fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX"); + if (fd < 0) + die("unable to create temp-file"); + if (write(fd, blob, size) != size) + die("unable to write temp-file"); + close(fd); + temp->name = temp->tmp_path; + strcpy(temp->hex, sha1_to_hex(sha1)); + temp->hex[40] = 0; + sprintf(temp->mode, "%06o", mode); +} + +static void prepare_temp_file(const char *name, + struct diff_tempfile *temp, + struct diff_filespec *one) +{ + if (!DIFF_FILE_VALID(one)) { + not_a_valid_file: + /* A '-' entry produces this for file-2, and + * a '+' entry produces this for file-1. + */ + temp->name = "/dev/null"; + strcpy(temp->hex, "."); + strcpy(temp->mode, "."); + return; + } + + if (!one->sha1_valid || + work_tree_matches(name, one->sha1)) { + struct stat st; + if (lstat(name, &st) < 0) { + if (errno == ENOENT) + goto not_a_valid_file; + die("stat(%s): %s", name, strerror(errno)); + } + if (S_ISLNK(st.st_mode)) { + int ret; + char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */ + if (sizeof(buf) <= st.st_size) + die("symlink too long: %s", name); + ret = readlink(name, buf, st.st_size); + if (ret < 0) + die("readlink(%s)", name); + prep_temp_blob(temp, buf, st.st_size, + (one->sha1_valid ? + one->sha1 : null_sha1), + (one->sha1_valid ? + one->mode : S_IFLNK)); + } + else { + /* we can borrow from the file in the work tree */ + temp->name = name; + if (!one->sha1_valid) + strcpy(temp->hex, sha1_to_hex(null_sha1)); + else + strcpy(temp->hex, sha1_to_hex(one->sha1)); + /* Even though we may sometimes borrow the + * contents from the work tree, we always want + * one->mode. mode is trustworthy even when + * !(one->sha1_valid), as long as + * DIFF_FILE_VALID(one). + */ + sprintf(temp->mode, "%06o", one->mode); + } + return; + } + else { + if (diff_populate_filespec(one, 0)) + die("cannot read data blob for %s", one->path); + prep_temp_blob(temp, one->data, one->size, + one->sha1, one->mode); + } +} + +static void remove_tempfile(void) +{ + int i; + + for (i = 0; i < 2; i++) + if (diff_temp[i].name == diff_temp[i].tmp_path) { + unlink(diff_temp[i].name); + diff_temp[i].name = NULL; + } +} + +static void remove_tempfile_on_signal(int signo) +{ + remove_tempfile(); + signal(SIGINT, SIG_DFL); + raise(signo); +} + +static int spawn_prog(const char *pgm, const char **arg) +{ + pid_t pid; + int status; + + fflush(NULL); + pid = fork(); + if (pid < 0) + die("unable to fork"); + if (!pid) { + execvp(pgm, (char *const*) arg); + exit(255); + } + + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + return -1; + } + + /* Earlier we did not check the exit status because + * diff exits non-zero if files are different, and + * we are not interested in knowing that. It was a + * mistake which made it harder to quit a diff-* + * session that uses the git-apply-patch-script as + * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF + * should also exit non-zero only when it wants to + * abort the entire diff-* session. + */ + if (WIFEXITED(status) && !WEXITSTATUS(status)) + return 0; + return -1; +} + +/* An external diff command takes: + * + * diff-cmd name infile1 infile1-sha1 infile1-mode \ + * infile2 infile2-sha1 infile2-mode [ rename-to ] + * + */ +static void run_external_diff(const char *pgm, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg, + int complete_rewrite) +{ + const char *spawn_arg[10]; + struct diff_tempfile *temp = diff_temp; + int retval; + static int atexit_asked = 0; + const char *othername; + const char **arg = &spawn_arg[0]; + + othername = (other? other : name); + if (one && two) { + prepare_temp_file(name, &temp[0], one); + prepare_temp_file(othername, &temp[1], two); + if (! atexit_asked && + (temp[0].name == temp[0].tmp_path || + temp[1].name == temp[1].tmp_path)) { + atexit_asked = 1; + atexit(remove_tempfile); + } + signal(SIGINT, remove_tempfile_on_signal); + } + + if (one && two) { + *arg++ = pgm; + *arg++ = name; + *arg++ = temp[0].name; + *arg++ = temp[0].hex; + *arg++ = temp[0].mode; + *arg++ = temp[1].name; + *arg++ = temp[1].hex; + *arg++ = temp[1].mode; + if (other) { + *arg++ = other; + *arg++ = xfrm_msg; + } + } else { + *arg++ = pgm; + *arg++ = name; + } + *arg = NULL; + retval = spawn_prog(pgm, spawn_arg); + remove_tempfile(); + if (retval) { + fprintf(stderr, "external diff died, stopping at %s.\n", name); + exit(1); + } +} + +static void run_diff_cmd(const char *pgm, + const char *name, + const char *other, + struct diff_filespec *one, + struct diff_filespec *two, + const char *xfrm_msg, + int complete_rewrite) +{ + if (pgm) { + run_external_diff(pgm, name, other, one, two, xfrm_msg, + complete_rewrite); + return; + } + if (one && two) + builtin_diff(name, other ? other : name, + one, two, xfrm_msg, complete_rewrite); + else + printf("* Unmerged path %s\n", name); +} + +static void diff_fill_sha1_info(struct diff_filespec *one) +{ + if (DIFF_FILE_VALID(one)) { + if (!one->sha1_valid) { + struct stat st; + if (lstat(one->path, &st) < 0) + die("stat %s", one->path); + if (index_path(one->sha1, one->path, &st, 0)) + die("cannot hash %s\n", one->path); + } + } + else + memset(one->sha1, 0, 20); +} + +static void run_diff(struct diff_filepair *p, struct diff_options *o) +{ + const char *pgm = external_diff(); + char msg[PATH_MAX*2+300], *xfrm_msg; + struct diff_filespec *one; + struct diff_filespec *two; + const char *name; + const char *other; + char *name_munged, *other_munged; + int complete_rewrite = 0; + int len; + + if (DIFF_PAIR_UNMERGED(p)) { + /* unmerged */ + run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0); + return; + } + + name = p->one->path; + other = (strcmp(name, p->two->path) ? p->two->path : NULL); + name_munged = quote_one(name); + other_munged = quote_one(other); + one = p->one; two = p->two; + + diff_fill_sha1_info(one); + diff_fill_sha1_info(two); + + len = 0; + switch (p->status) { + case DIFF_STATUS_COPIED: + len += snprintf(msg + len, sizeof(msg) - len, + "similarity index %d%%\n" + "copy from %s\n" + "copy to %s\n", + (int)(0.5 + p->score * 100.0/MAX_SCORE), + name_munged, other_munged); + break; + case DIFF_STATUS_RENAMED: + len += snprintf(msg + len, sizeof(msg) - len, + "similarity index %d%%\n" + "rename from %s\n" + "rename to %s\n", + (int)(0.5 + p->score * 100.0/MAX_SCORE), + name_munged, other_munged); + break; + case DIFF_STATUS_MODIFIED: + if (p->score) { + len += snprintf(msg + len, sizeof(msg) - len, + "dissimilarity index %d%%\n", + (int)(0.5 + p->score * + 100.0/MAX_SCORE)); + complete_rewrite = 1; + break; + } + /* fallthru */ + default: + /* nothing */ + ; + } + + if (memcmp(one->sha1, two->sha1, 20)) { + char one_sha1[41]; + int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; + memcpy(one_sha1, sha1_to_hex(one->sha1), 41); + + len += snprintf(msg + len, sizeof(msg) - len, + "index %.*s..%.*s", + abbrev, one_sha1, abbrev, + sha1_to_hex(two->sha1)); + if (one->mode == two->mode) + len += snprintf(msg + len, sizeof(msg) - len, + " %06o", one->mode); + len += snprintf(msg + len, sizeof(msg) - len, "\n"); + } + + if (len) + msg[--len] = 0; + xfrm_msg = len ? msg : NULL; + + if (!pgm && + DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && + (S_IFMT & one->mode) != (S_IFMT & two->mode)) { + /* a filepair that changes between file and symlink + * needs to be split into deletion and creation. + */ + struct diff_filespec *null = alloc_filespec(two->path); + run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0); + free(null); + null = alloc_filespec(one->path); + run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0); + free(null); + } + else + run_diff_cmd(pgm, name, other, one, two, xfrm_msg, + complete_rewrite); + + free(name_munged); + free(other_munged); +} + +static void run_diffstat(struct diff_filepair *p, struct diff_options *o, + struct diffstat_t *diffstat) +{ + const char *name; + const char *other; + + if (DIFF_PAIR_UNMERGED(p)) { + /* unmerged */ + builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat); + return; + } + + name = p->one->path; + other = (strcmp(name, p->two->path) ? p->two->path : NULL); + + diff_fill_sha1_info(p->one); + diff_fill_sha1_info(p->two); + + builtin_diffstat(name, other, p->one, p->two, diffstat); +} + +void diff_setup(struct diff_options *options) +{ + memset(options, 0, sizeof(*options)); + options->output_format = DIFF_FORMAT_RAW; + options->line_termination = '\n'; + options->break_opt = -1; + options->rename_limit = -1; + + options->change = diff_change; + options->add_remove = diff_addremove; +} + +int diff_setup_done(struct diff_options *options) +{ + if ((options->find_copies_harder && + options->detect_rename != DIFF_DETECT_COPY) || + (0 <= options->rename_limit && !options->detect_rename)) + return -1; + + /* + * These cases always need recursive; we do not drop caller-supplied + * recursive bits for other formats here. + */ + if ((options->output_format == DIFF_FORMAT_PATCH) || + (options->output_format == DIFF_FORMAT_DIFFSTAT)) + options->recursive = 1; + + if (options->detect_rename && options->rename_limit < 0) + options->rename_limit = diff_rename_limit_default; + if (options->setup & DIFF_SETUP_USE_CACHE) { + if (!active_cache) + /* read-cache does not die even when it fails + * so it is safe for us to do this here. Also + * it does not smudge active_cache or active_nr + * when it fails, so we do not have to worry about + * cleaning it up ourselves either. + */ + read_cache(); + } + if (options->setup & DIFF_SETUP_USE_SIZE_CACHE) + use_size_cache = 1; + if (options->abbrev <= 0 || 40 < options->abbrev) + options->abbrev = 40; /* full */ + + return 0; +} + +int diff_opt_parse(struct diff_options *options, const char **av, int ac) +{ + const char *arg = av[0]; + if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) + options->output_format = DIFF_FORMAT_PATCH; + else if (!strcmp(arg, "--patch-with-raw")) { + options->output_format = DIFF_FORMAT_PATCH; + options->with_raw = 1; + } + else if (!strcmp(arg, "--stat")) + options->output_format = DIFF_FORMAT_DIFFSTAT; + else if (!strcmp(arg, "--patch-with-stat")) { + options->output_format = DIFF_FORMAT_PATCH; + options->with_stat = 1; + } + else if (!strcmp(arg, "-z")) + options->line_termination = 0; + else if (!strncmp(arg, "-l", 2)) + options->rename_limit = strtoul(arg+2, NULL, 10); + else if (!strcmp(arg, "--full-index")) + options->full_index = 1; + else if (!strcmp(arg, "--name-only")) + options->output_format = DIFF_FORMAT_NAME; + else if (!strcmp(arg, "--name-status")) + options->output_format = DIFF_FORMAT_NAME_STATUS; + else if (!strcmp(arg, "-R")) + options->reverse_diff = 1; + else if (!strncmp(arg, "-S", 2)) + options->pickaxe = arg + 2; + else if (!strcmp(arg, "-s")) + options->output_format = DIFF_FORMAT_NO_OUTPUT; + else if (!strncmp(arg, "-O", 2)) + options->orderfile = arg + 2; + else if (!strncmp(arg, "--diff-filter=", 14)) + options->filter = arg + 14; + else if (!strcmp(arg, "--pickaxe-all")) + options->pickaxe_opts = DIFF_PICKAXE_ALL; + else if (!strcmp(arg, "--pickaxe-regex")) + options->pickaxe_opts = DIFF_PICKAXE_REGEX; + else if (!strncmp(arg, "-B", 2)) { + if ((options->break_opt = + diff_scoreopt_parse(arg)) == -1) + return -1; + } + else if (!strncmp(arg, "-M", 2)) { + if ((options->rename_score = + diff_scoreopt_parse(arg)) == -1) + return -1; + options->detect_rename = DIFF_DETECT_RENAME; + } + else if (!strncmp(arg, "-C", 2)) { + if ((options->rename_score = + diff_scoreopt_parse(arg)) == -1) + return -1; + options->detect_rename = DIFF_DETECT_COPY; + } + else if (!strcmp(arg, "--find-copies-harder")) + options->find_copies_harder = 1; + else if (!strcmp(arg, "--abbrev")) + options->abbrev = DEFAULT_ABBREV; + else if (!strncmp(arg, "--abbrev=", 9)) { + options->abbrev = strtoul(arg + 9, NULL, 10); + if (options->abbrev < MINIMUM_ABBREV) + options->abbrev = MINIMUM_ABBREV; + else if (40 < options->abbrev) + options->abbrev = 40; + } + else + return 0; + return 1; +} + +static int parse_num(const char **cp_p) +{ + unsigned long num, scale; + int ch, dot; + const char *cp = *cp_p; + + num = 0; + scale = 1; + dot = 0; + for(;;) { + ch = *cp; + if ( !dot && ch == '.' ) { + scale = 1; + dot = 1; + } else if ( ch == '%' ) { + scale = dot ? scale*100 : 100; + cp++; /* % is always at the end */ + break; + } else if ( ch >= '0' && ch <= '9' ) { + if ( scale < 100000 ) { + scale *= 10; + num = (num*10) + (ch-'0'); + } + } else { + break; + } + cp++; + } + *cp_p = cp; + + /* user says num divided by scale and we say internally that + * is MAX_SCORE * num / scale. + */ + return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale); +} + +int diff_scoreopt_parse(const char *opt) +{ + int opt1, opt2, cmd; + + if (*opt++ != '-') + return -1; + cmd = *opt++; + if (cmd != 'M' && cmd != 'C' && cmd != 'B') + return -1; /* that is not a -M, -C nor -B option */ + + opt1 = parse_num(&opt); + if (cmd != 'B') + opt2 = 0; + else { + if (*opt == 0) + opt2 = 0; + else if (*opt != '/') + return -1; /* we expect -B80/99 or -B80 */ + else { + opt++; + opt2 = parse_num(&opt); + } + } + if (*opt != 0) + return -1; + return opt1 | (opt2 << 16); +} + +struct diff_queue_struct diff_queued_diff; + +void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp) +{ + if (queue->alloc <= queue->nr) { + queue->alloc = alloc_nr(queue->alloc); + queue->queue = xrealloc(queue->queue, + sizeof(dp) * queue->alloc); + } + queue->queue[queue->nr++] = dp; +} + +struct diff_filepair *diff_queue(struct diff_queue_struct *queue, + struct diff_filespec *one, + struct diff_filespec *two) +{ + struct diff_filepair *dp = xmalloc(sizeof(*dp)); + dp->one = one; + dp->two = two; + dp->score = 0; + dp->status = 0; + dp->source_stays = 0; + dp->broken_pair = 0; + if (queue) + diff_q(queue, dp); + return dp; +} + +void diff_free_filepair(struct diff_filepair *p) +{ + diff_free_filespec_data(p->one); + diff_free_filespec_data(p->two); + free(p->one); + free(p->two); + free(p); +} + +/* This is different from find_unique_abbrev() in that + * it stuffs the result with dots for alignment. + */ +const char *diff_unique_abbrev(const unsigned char *sha1, int len) +{ + int abblen; + const char *abbrev; + if (len == 40) + return sha1_to_hex(sha1); + + abbrev = find_unique_abbrev(sha1, len); + if (!abbrev) + return sha1_to_hex(sha1); + abblen = strlen(abbrev); + if (abblen < 37) { + static char hex[41]; + if (len < abblen && abblen <= len + 2) + sprintf(hex, "%s%.*s", abbrev, len+3-abblen, ".."); + else + sprintf(hex, "%s...", abbrev); + return hex; + } + return sha1_to_hex(sha1); +} + +static void diff_flush_raw(struct diff_filepair *p, + int line_termination, + int inter_name_termination, + struct diff_options *options, + int output_format) +{ + int two_paths; + char status[10]; + int abbrev = options->abbrev; + const char *path_one, *path_two; + + path_one = p->one->path; + path_two = p->two->path; + if (line_termination) { + path_one = quote_one(path_one); + path_two = quote_one(path_two); + } + + if (p->score) + sprintf(status, "%c%03d", p->status, + (int)(0.5 + p->score * 100.0/MAX_SCORE)); + else { + status[0] = p->status; + status[1] = 0; + } + switch (p->status) { + case DIFF_STATUS_COPIED: + case DIFF_STATUS_RENAMED: + two_paths = 1; + break; + case DIFF_STATUS_ADDED: + case DIFF_STATUS_DELETED: + two_paths = 0; + break; + default: + two_paths = 0; + break; + } + if (output_format != DIFF_FORMAT_NAME_STATUS) { + printf(":%06o %06o %s ", + p->one->mode, p->two->mode, + diff_unique_abbrev(p->one->sha1, abbrev)); + printf("%s ", + diff_unique_abbrev(p->two->sha1, abbrev)); + } + printf("%s%c%s", status, inter_name_termination, path_one); + if (two_paths) + printf("%c%s", inter_name_termination, path_two); + putchar(line_termination); + if (path_one != p->one->path) + free((void*)path_one); + if (path_two != p->two->path) + free((void*)path_two); +} + +static void diff_flush_name(struct diff_filepair *p, + int inter_name_termination, + int line_termination) +{ + char *path = p->two->path; + + if (line_termination) + path = quote_one(p->two->path); + else + path = p->two->path; + printf("%s%c", path, line_termination); + if (p->two->path != path) + free(path); +} + +int diff_unmodified_pair(struct diff_filepair *p) +{ + /* This function is written stricter than necessary to support + * the currently implemented transformers, but the idea is to + * let transformers to produce diff_filepairs any way they want, + * and filter and clean them up here before producing the output. + */ + struct diff_filespec *one, *two; + + if (DIFF_PAIR_UNMERGED(p)) + return 0; /* unmerged is interesting */ + + one = p->one; + two = p->two; + + /* deletion, addition, mode or type change + * and rename are all interesting. + */ + if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) || + DIFF_PAIR_MODE_CHANGED(p) || + strcmp(one->path, two->path)) + return 0; + + /* both are valid and point at the same path. that is, we are + * dealing with a change. + */ + if (one->sha1_valid && two->sha1_valid && + !memcmp(one->sha1, two->sha1, sizeof(one->sha1))) + return 1; /* no change */ + if (!one->sha1_valid && !two->sha1_valid) + return 1; /* both look at the same file on the filesystem. */ + return 0; +} + +static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) +{ + if (diff_unmodified_pair(p)) + return; + + if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || + (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) + return; /* no tree diffs in patch format */ + + run_diff(p, o); +} + +static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, + struct diffstat_t *diffstat) +{ + if (diff_unmodified_pair(p)) + return; + + if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) || + (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode))) + return; /* no tree diffs in patch format */ + + run_diffstat(p, o, diffstat); +} + +int diff_queue_is_empty(void) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i; + for (i = 0; i < q->nr; i++) + if (!diff_unmodified_pair(q->queue[i])) + return 0; + return 1; +} + +#if DIFF_DEBUG +void diff_debug_filespec(struct diff_filespec *s, int x, const char *one) +{ + fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n", + x, one ? one : "", + s->path, + DIFF_FILE_VALID(s) ? "valid" : "invalid", + s->mode, + s->sha1_valid ? sha1_to_hex(s->sha1) : ""); + fprintf(stderr, "queue[%d] %s size %lu flags %d\n", + x, one ? one : "", + s->size, s->xfrm_flags); +} + +void diff_debug_filepair(const struct diff_filepair *p, int i) +{ + diff_debug_filespec(p->one, i, "one"); + diff_debug_filespec(p->two, i, "two"); + fprintf(stderr, "score %d, status %c stays %d broken %d\n", + p->score, p->status ? p->status : '?', + p->source_stays, p->broken_pair); +} + +void diff_debug_queue(const char *msg, struct diff_queue_struct *q) +{ + int i; + if (msg) + fprintf(stderr, "%s\n", msg); + fprintf(stderr, "q->nr = %d\n", q->nr); + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + diff_debug_filepair(p, i); + } +} +#endif + +static void diff_resolve_rename_copy(void) +{ + int i, j; + struct diff_filepair *p, *pp; + struct diff_queue_struct *q = &diff_queued_diff; + + diff_debug_queue("resolve-rename-copy", q); + + for (i = 0; i < q->nr; i++) { + p = q->queue[i]; + p->status = 0; /* undecided */ + if (DIFF_PAIR_UNMERGED(p)) + p->status = DIFF_STATUS_UNMERGED; + else if (!DIFF_FILE_VALID(p->one)) + p->status = DIFF_STATUS_ADDED; + else if (!DIFF_FILE_VALID(p->two)) + p->status = DIFF_STATUS_DELETED; + else if (DIFF_PAIR_TYPE_CHANGED(p)) + p->status = DIFF_STATUS_TYPE_CHANGED; + + /* from this point on, we are dealing with a pair + * whose both sides are valid and of the same type, i.e. + * either in-place edit or rename/copy edit. + */ + else if (DIFF_PAIR_RENAME(p)) { + if (p->source_stays) { + p->status = DIFF_STATUS_COPIED; + continue; + } + /* See if there is some other filepair that + * copies from the same source as us. If so + * we are a copy. Otherwise we are either a + * copy if the path stays, or a rename if it + * does not, but we already handled "stays" case. + */ + for (j = i + 1; j < q->nr; j++) { + pp = q->queue[j]; + if (strcmp(pp->one->path, p->one->path)) + continue; /* not us */ + if (!DIFF_PAIR_RENAME(pp)) + continue; /* not a rename/copy */ + /* pp is a rename/copy from the same source */ + p->status = DIFF_STATUS_COPIED; + break; + } + if (!p->status) + p->status = DIFF_STATUS_RENAMED; + } + else if (memcmp(p->one->sha1, p->two->sha1, 20) || + p->one->mode != p->two->mode) + p->status = DIFF_STATUS_MODIFIED; + else { + /* This is a "no-change" entry and should not + * happen anymore, but prepare for broken callers. + */ + error("feeding unmodified %s to diffcore", + p->one->path); + p->status = DIFF_STATUS_UNKNOWN; + } + } + diff_debug_queue("resolve-rename-copy done", q); +} + +static void flush_one_pair(struct diff_filepair *p, + int diff_output_format, + struct diff_options *options, + struct diffstat_t *diffstat) +{ + int inter_name_termination = '\t'; + int line_termination = options->line_termination; + if (!line_termination) + inter_name_termination = 0; + + switch (p->status) { + case DIFF_STATUS_UNKNOWN: + break; + case 0: + die("internal error in diff-resolve-rename-copy"); + break; + default: + switch (diff_output_format) { + case DIFF_FORMAT_DIFFSTAT: + diff_flush_stat(p, options, diffstat); + break; + case DIFF_FORMAT_PATCH: + diff_flush_patch(p, options); + break; + case DIFF_FORMAT_RAW: + case DIFF_FORMAT_NAME_STATUS: + diff_flush_raw(p, line_termination, + inter_name_termination, + options, diff_output_format); + break; + case DIFF_FORMAT_NAME: + diff_flush_name(p, + inter_name_termination, + line_termination); + break; + case DIFF_FORMAT_NO_OUTPUT: + break; + } + } +} + +void diff_flush(struct diff_options *options) +{ + struct diff_queue_struct *q = &diff_queued_diff; + int i; + int diff_output_format = options->output_format; + struct diffstat_t *diffstat = NULL; + + if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) { + diffstat = xcalloc(sizeof (struct diffstat_t), 1); + diffstat->xm.consume = diffstat_consume; + } + + if (options->with_raw) { + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL); + } + putchar(options->line_termination); + } + if (options->with_stat) { + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options, + diffstat); + } + show_stats(diffstat); + free(diffstat); + diffstat = NULL; + putchar(options->line_termination); + } + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + flush_one_pair(p, diff_output_format, options, diffstat); + diff_free_filepair(p); + } + + if (diffstat) { + show_stats(diffstat); + free(diffstat); + } + + free(q->queue); + q->queue = NULL; + q->nr = q->alloc = 0; +} + +static void diffcore_apply_filter(const char *filter) +{ + int i; + struct diff_queue_struct *q = &diff_queued_diff; + struct diff_queue_struct outq; + outq.queue = NULL; + outq.nr = outq.alloc = 0; + + if (!filter) + return; + + if (strchr(filter, DIFF_STATUS_FILTER_AON)) { + int found; + for (i = found = 0; !found && i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (((p->status == DIFF_STATUS_MODIFIED) && + ((p->score && + strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || + (!p->score && + strchr(filter, DIFF_STATUS_MODIFIED)))) || + ((p->status != DIFF_STATUS_MODIFIED) && + strchr(filter, p->status))) + found++; + } + if (found) + return; + + /* otherwise we will clear the whole queue + * by copying the empty outq at the end of this + * function, but first clear the current entries + * in the queue. + */ + for (i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); + } + else { + /* Only the matching ones */ + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + + if (((p->status == DIFF_STATUS_MODIFIED) && + ((p->score && + strchr(filter, DIFF_STATUS_FILTER_BROKEN)) || + (!p->score && + strchr(filter, DIFF_STATUS_MODIFIED)))) || + ((p->status != DIFF_STATUS_MODIFIED) && + strchr(filter, p->status))) + diff_q(&outq, p); + else + diff_free_filepair(p); + } + } + free(q->queue); + *q = outq; +} + +void diffcore_std(struct diff_options *options) +{ + if (options->break_opt != -1) + diffcore_break(options->break_opt); + if (options->detect_rename) + diffcore_rename(options); + if (options->break_opt != -1) + diffcore_merge_broken(); + if (options->pickaxe) + diffcore_pickaxe(options->pickaxe, options->pickaxe_opts); + if (options->orderfile) + diffcore_order(options->orderfile); + diff_resolve_rename_copy(); + diffcore_apply_filter(options->filter); +} + + +void diffcore_std_no_resolve(struct diff_options *options) +{ + if (options->pickaxe) + diffcore_pickaxe(options->pickaxe, options->pickaxe_opts); + if (options->orderfile) + diffcore_order(options->orderfile); + diffcore_apply_filter(options->filter); +} + +void diff_addremove(struct diff_options *options, + int addremove, unsigned mode, + const unsigned char *sha1, + const char *base, const char *path) +{ + char concatpath[PATH_MAX]; + struct diff_filespec *one, *two; + + /* This may look odd, but it is a preparation for + * feeding "there are unchanged files which should + * not produce diffs, but when you are doing copy + * detection you would need them, so here they are" + * entries to the diff-core. They will be prefixed + * with something like '=' or '*' (I haven't decided + * which but should not make any difference). + * Feeding the same new and old to diff_change() + * also has the same effect. + * Before the final output happens, they are pruned after + * merged into rename/copy pairs as appropriate. + */ + if (options->reverse_diff) + addremove = (addremove == '+' ? '-' : + addremove == '-' ? '+' : addremove); + + if (!path) path = ""; + sprintf(concatpath, "%s%s", base, path); + one = alloc_filespec(concatpath); + two = alloc_filespec(concatpath); + + if (addremove != '+') + fill_filespec(one, sha1, mode); + if (addremove != '-') + fill_filespec(two, sha1, mode); + + diff_queue(&diff_queued_diff, one, two); +} + +void diff_change(struct diff_options *options, + unsigned old_mode, unsigned new_mode, + const unsigned char *old_sha1, + const unsigned char *new_sha1, + const char *base, const char *path) +{ + char concatpath[PATH_MAX]; + struct diff_filespec *one, *two; + + if (options->reverse_diff) { + unsigned tmp; + const unsigned char *tmp_c; + tmp = old_mode; old_mode = new_mode; new_mode = tmp; + tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; + } + if (!path) path = ""; + sprintf(concatpath, "%s%s", base, path); + one = alloc_filespec(concatpath); + two = alloc_filespec(concatpath); + fill_filespec(one, old_sha1, old_mode); + fill_filespec(two, new_sha1, new_mode); + + diff_queue(&diff_queued_diff, one, two); +} + +void diff_unmerge(struct diff_options *options, + const char *path) +{ + struct diff_filespec *one, *two; + one = alloc_filespec(path); + two = alloc_filespec(path); + diff_queue(&diff_queued_diff, one, two); +} diff --git a/diff.h b/diff.h index 52fff66..da4bd42 100644 --- a/diff.h +++ b/diff.h @@ -28,10 +28,11 @@ struct diff_options { with_raw:1, with_stat:1, tree_in_recursive:1, - full_index:1; + full_index:1, + silent_on_remove:1, + find_copies_harder:1; int break_opt; int detect_rename; - int find_copies_harder; int line_termination; int output_format; int pickaxe_opts; @@ -168,4 +169,6 @@ extern void diff_flush(struct diff_options*); extern const char *diff_unique_abbrev(const unsigned char *, int); +extern int run_diff_files(struct rev_info *revs, int silent_on_removed); + #endif /* DIFF_H */ diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index d0ed242..75e4c9a 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -37,7 +37,7 @@ compare_change () { } check_cache_at () { - clean_if_empty=`git-diff-files "$1"` + clean_if_empty=`git-diff-files -- "$1"` case "$clean_if_empty" in '') echo "$1: clean" ;; ?*) echo "$1: dirty" ;; diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh index 861ef4c..4d175d8 100755 --- a/t/t1002-read-tree-m-u-2way.sh +++ b/t/t1002-read-tree-m-u-2way.sh @@ -20,7 +20,7 @@ compare_change () { } check_cache_at () { - clean_if_empty=`git-diff-files "$1"` + clean_if_empty=`git-diff-files -- "$1"` case "$clean_if_empty" in '') echo "$1: clean" ;; ?*) echo "$1: dirty" ;; -- cgit v0.10.2-6-g49f6 From e09ad6e1e3308fde346b4b6287d9441363806832 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 22 Apr 2006 02:43:00 -0700 Subject: Libify diff-index. The second installment to libify diff brothers. The pathname arguments are checked more strictly than before because we now use the revision.c::setup_revisions() infrastructure. Signed-off-by: Junio C Hamano diff --git a/diff-index.c b/diff-index.c index e376d65..4a243b3 100644 --- a/diff-index.c +++ b/diff-index.c @@ -1,166 +1,7 @@ #include "cache.h" -#include "tree.h" #include "diff.h" - -static int cached_only = 0; -static int match_nonexisting = 0; -static struct diff_options diff_options; - -/* A file entry went away or appeared */ -static void show_file(const char *prefix, - struct cache_entry *ce, - unsigned char *sha1, unsigned int mode) -{ - diff_addremove(&diff_options, prefix[0], ntohl(mode), - sha1, ce->name, NULL); -} - -static int get_stat_data(struct cache_entry *ce, - unsigned char ** sha1p, unsigned int *modep) -{ - unsigned char *sha1 = ce->sha1; - unsigned int mode = ce->ce_mode; - - if (!cached_only) { - static unsigned char no_sha1[20]; - int changed; - struct stat st; - if (lstat(ce->name, &st) < 0) { - if (errno == ENOENT && match_nonexisting) { - *sha1p = sha1; - *modep = mode; - return 0; - } - return -1; - } - changed = ce_match_stat(ce, &st, 0); - if (changed) { - mode = create_ce_mode(st.st_mode); - if (!trust_executable_bit && S_ISREG(st.st_mode)) - mode = ce->ce_mode; - sha1 = no_sha1; - } - } - - *sha1p = sha1; - *modep = mode; - return 0; -} - -static void show_new_file(struct cache_entry *new) -{ - unsigned char *sha1; - unsigned int mode; - - /* New file in the index: it might actually be different in - * the working copy. - */ - if (get_stat_data(new, &sha1, &mode) < 0) - return; - - show_file("+", new, sha1, mode); -} - -static int show_modified(struct cache_entry *old, - struct cache_entry *new, - int report_missing) -{ - unsigned int mode, oldmode; - unsigned char *sha1; - - if (get_stat_data(new, &sha1, &mode) < 0) { - if (report_missing) - show_file("-", old, old->sha1, old->ce_mode); - return -1; - } - - oldmode = old->ce_mode; - if (mode == oldmode && !memcmp(sha1, old->sha1, 20) && - !diff_options.find_copies_harder) - return 0; - - mode = ntohl(mode); - oldmode = ntohl(oldmode); - - diff_change(&diff_options, oldmode, mode, - old->sha1, sha1, old->name, NULL); - return 0; -} - -static int diff_cache(struct cache_entry **ac, int entries, const char **pathspec) -{ - while (entries) { - struct cache_entry *ce = *ac; - int same = (entries > 1) && ce_same_name(ce, ac[1]); - - if (!ce_path_match(ce, pathspec)) - goto skip_entry; - - switch (ce_stage(ce)) { - case 0: - /* No stage 1 entry? That means it's a new file */ - if (!same) { - show_new_file(ce); - break; - } - /* Show difference between old and new */ - show_modified(ac[1], ce, 1); - break; - case 1: - /* No stage 3 (merge) entry? That means it's been deleted */ - if (!same) { - show_file("-", ce, ce->sha1, ce->ce_mode); - break; - } - /* We come here with ce pointing at stage 1 - * (original tree) and ac[1] pointing at stage - * 3 (unmerged). show-modified with - * report-missing set to false does not say the - * file is deleted but reports true if work - * tree does not have it, in which case we - * fall through to report the unmerged state. - * Otherwise, we show the differences between - * the original tree and the work tree. - */ - if (!cached_only && !show_modified(ce, ac[1], 0)) - break; - /* fallthru */ - case 3: - diff_unmerge(&diff_options, ce->name); - break; - - default: - die("impossible cache entry stage"); - } - -skip_entry: - /* - * Ignore all the different stages for this file, - * we've handled the relevant cases now. - */ - do { - ac++; - entries--; - } while (entries && ce_same_name(ce, ac[0])); - } - return 0; -} - -/* - * This turns all merge entries into "stage 3". That guarantees that - * when we read in the new tree (into "stage 1"), we won't lose sight - * of the fact that we had unmerged entries. - */ -static void mark_merge_entries(void) -{ - int i; - for (i = 0; i < active_nr; i++) { - struct cache_entry *ce = active_cache[i]; - if (!ce_stage(ce)) - continue; - ce->ce_flags |= htons(CE_STAGEMASK); - } -} +#include "commit.h" +#include "revision.h" static const char diff_cache_usage[] = "git-diff-index [-m] [--cached] " @@ -169,85 +10,32 @@ COMMON_DIFF_OPTIONS_HELP; int main(int argc, const char **argv) { - const char *tree_name = NULL; - unsigned char sha1[20]; - const char *prefix = setup_git_directory(); - const char **pathspec = NULL; - struct tree *tree; - int ret; - int allow_options = 1; + struct rev_info rev; + int match_missing = 0; + int cached = 0; int i; git_config(git_diff_config); - diff_setup(&diff_options); + init_revisions(&rev); + rev.abbrev = 0; + + argc = setup_revisions(argc, argv, &rev, NULL); for (i = 1; i < argc; i++) { const char *arg = argv[i]; - int diff_opt_cnt; - - if (!allow_options || *arg != '-') { - if (tree_name) - break; - tree_name = arg; - continue; - } - if (!strcmp(arg, "--")) { - allow_options = 0; - continue; - } - if (!strcmp(arg, "-r")) { - /* We accept the -r flag just to look like git-diff-tree */ - continue; - } - if (!strcmp(arg, "--cc")) - /* - * I _think_ "diff-index --cached HEAD" with an - * unmerged index could show something else - * later, but pretend --cc is the same as -p for - * now. "git diff" uses --cc by default. - */ - argv[i] = arg = "-p"; - diff_opt_cnt = diff_opt_parse(&diff_options, argv + i, - argc - i); - if (diff_opt_cnt < 0) + if (!strcmp(arg, "-m")) + match_missing = 1; + else if (!strcmp(arg, "--cached")) + cached = 1; + else usage(diff_cache_usage); - else if (diff_opt_cnt) { - i += diff_opt_cnt - 1; - continue; - } - - if (!strcmp(arg, "-m")) { - match_nonexisting = 1; - continue; - } - if (!strcmp(arg, "--cached")) { - cached_only = 1; - continue; - } - usage(diff_cache_usage); } - - pathspec = get_pathspec(prefix, argv + i); - - if (diff_setup_done(&diff_options) < 0) - usage(diff_cache_usage); - - if (!tree_name || get_sha1(tree_name, sha1)) + /* + * Make sure there is one revision (i.e. pending object), + * and there is no revision filtering parameters. + */ + if (!rev.pending_objects || rev.pending_objects->next || + rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1) usage(diff_cache_usage); - - read_cache(); - - mark_merge_entries(); - - tree = parse_tree_indirect(sha1); - if (!tree) - die("bad tree object %s", tree_name); - if (read_tree(tree, 1, pathspec)) - die("unable to read tree object %s", tree_name); - - ret = diff_cache(active_cache, active_nr, pathspec); - - diffcore_std(&diff_options); - diff_flush(&diff_options); - return ret; + return run_diff_index(&rev, cached, match_missing); } diff --git a/diff-lib.c b/diff-lib.c index a28dd3d..63da3b5 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -131,3 +131,206 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed) return 0; } +/* + * diff-index + */ + +/* A file entry went away or appeared */ +static void diff_index_show_file(struct rev_info *revs, + const char *prefix, + struct cache_entry *ce, + unsigned char *sha1, unsigned int mode) +{ + diff_addremove(&revs->diffopt, prefix[0], ntohl(mode), + sha1, ce->name, NULL); +} + +static int get_stat_data(struct cache_entry *ce, + unsigned char **sha1p, + unsigned int *modep, + int cached, int match_missing) +{ + unsigned char *sha1 = ce->sha1; + unsigned int mode = ce->ce_mode; + + if (!cached) { + static unsigned char no_sha1[20]; + int changed; + struct stat st; + if (lstat(ce->name, &st) < 0) { + if (errno == ENOENT && match_missing) { + *sha1p = sha1; + *modep = mode; + return 0; + } + return -1; + } + changed = ce_match_stat(ce, &st, 0); + if (changed) { + mode = create_ce_mode(st.st_mode); + if (!trust_executable_bit && S_ISREG(st.st_mode)) + mode = ce->ce_mode; + sha1 = no_sha1; + } + } + + *sha1p = sha1; + *modep = mode; + return 0; +} + +static void show_new_file(struct rev_info *revs, + struct cache_entry *new, + int cached, int match_missing) +{ + unsigned char *sha1; + unsigned int mode; + + /* New file in the index: it might actually be different in + * the working copy. + */ + if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) + return; + + diff_index_show_file(revs, "+", new, sha1, mode); +} + +static int show_modified(struct rev_info *revs, + struct cache_entry *old, + struct cache_entry *new, + int report_missing, + int cached, int match_missing) +{ + unsigned int mode, oldmode; + unsigned char *sha1; + + if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) { + if (report_missing) + diff_index_show_file(revs, "-", old, + old->sha1, old->ce_mode); + return -1; + } + + oldmode = old->ce_mode; + if (mode == oldmode && !memcmp(sha1, old->sha1, 20) && + !revs->diffopt.find_copies_harder) + return 0; + + mode = ntohl(mode); + oldmode = ntohl(oldmode); + + diff_change(&revs->diffopt, oldmode, mode, + old->sha1, sha1, old->name, NULL); + return 0; +} + +static int diff_cache(struct rev_info *revs, + struct cache_entry **ac, int entries, + const char **pathspec, + int cached, int match_missing) +{ + while (entries) { + struct cache_entry *ce = *ac; + int same = (entries > 1) && ce_same_name(ce, ac[1]); + + if (!ce_path_match(ce, pathspec)) + goto skip_entry; + + switch (ce_stage(ce)) { + case 0: + /* No stage 1 entry? That means it's a new file */ + if (!same) { + show_new_file(revs, ce, cached, match_missing); + break; + } + /* Show difference between old and new */ + show_modified(revs,ac[1], ce, 1, + cached, match_missing); + break; + case 1: + /* No stage 3 (merge) entry? + * That means it's been deleted. + */ + if (!same) { + diff_index_show_file(revs, "-", ce, + ce->sha1, ce->ce_mode); + break; + } + /* We come here with ce pointing at stage 1 + * (original tree) and ac[1] pointing at stage + * 3 (unmerged). show-modified with + * report-missing set to false does not say the + * file is deleted but reports true if work + * tree does not have it, in which case we + * fall through to report the unmerged state. + * Otherwise, we show the differences between + * the original tree and the work tree. + */ + if (!cached && + !show_modified(revs, ce, ac[1], 0, + cached, match_missing)) + break; + /* fallthru */ + case 3: + diff_unmerge(&revs->diffopt, ce->name); + break; + + default: + die("impossible cache entry stage"); + } + +skip_entry: + /* + * Ignore all the different stages for this file, + * we've handled the relevant cases now. + */ + do { + ac++; + entries--; + } while (entries && ce_same_name(ce, ac[0])); + } + return 0; +} + +/* + * This turns all merge entries into "stage 3". That guarantees that + * when we read in the new tree (into "stage 1"), we won't lose sight + * of the fact that we had unmerged entries. + */ +static void mark_merge_entries(void) +{ + int i; + for (i = 0; i < active_nr; i++) { + struct cache_entry *ce = active_cache[i]; + if (!ce_stage(ce)) + continue; + ce->ce_flags |= htons(CE_STAGEMASK); + } +} + +int run_diff_index(struct rev_info *revs, int cached, int match_missing) +{ + int ret; + struct object *ent; + struct tree *tree; + const char *tree_name; + + if (read_cache() < 0) { + perror("read_cache"); + return -1; + } + mark_merge_entries(); + + ent = revs->pending_objects->item; + tree_name = revs->pending_objects->name; + tree = parse_tree_indirect(ent->sha1); + if (!tree) + return error("bad tree object %s", tree_name); + if (read_tree(tree, 1, revs->prune_data)) + return error("unable to read tree object %s", tree_name); + ret = diff_cache(revs, active_cache, active_nr, revs->prune_data, + cached, match_missing); + diffcore_std(&revs->diffopt); + diff_flush(&revs->diffopt); + return ret; +} diff --git a/diff.h b/diff.h index da4bd42..837d449 100644 --- a/diff.h +++ b/diff.h @@ -171,4 +171,6 @@ extern const char *diff_unique_abbrev(const unsigned char *, int); extern int run_diff_files(struct rev_info *revs, int silent_on_removed); +extern int run_diff_index(struct rev_info *revs, int cached, int match_missing); + #endif /* DIFF_H */ diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh index 8db329d..9e1544d 100755 --- a/t/t4010-diff-pathspec.sh +++ b/t/t4010-diff-pathspec.sh @@ -28,7 +28,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'limit to path should show nothing' \ - 'git-diff-index --cached $tree path >current && + 'git-diff-index --cached $tree -- path >current && compare_diff_raw current expected' cat >expected <<\EOF @@ -36,7 +36,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'limit to path1 should show path1/file1' \ - 'git-diff-index --cached $tree path1 >current && + 'git-diff-index --cached $tree -- path1 >current && compare_diff_raw current expected' cat >expected <<\EOF @@ -44,7 +44,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'limit to path1/ should show path1/file1' \ - 'git-diff-index --cached $tree path1/ >current && + 'git-diff-index --cached $tree -- path1/ >current && compare_diff_raw current expected' cat >expected <<\EOF @@ -52,14 +52,14 @@ cat >expected <<\EOF EOF test_expect_success \ 'limit to file0 should show file0' \ - 'git-diff-index --cached $tree file0 >current && + 'git-diff-index --cached $tree -- file0 >current && compare_diff_raw current expected' cat >expected <<\EOF EOF test_expect_success \ 'limit to file0/ should emit nothing.' \ - 'git-diff-index --cached $tree file0/ >current && + 'git-diff-index --cached $tree -- file0/ >current && compare_diff_raw current expected' test_done -- cgit v0.10.2-6-g49f6 From 5c21ac0e7c475c82039ab604ee9d7d8430889346 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 22 Apr 2006 03:58:04 -0700 Subject: Libified diff-index: backward compatibility fix. "diff-index -m" does not mean "do not ignore merges", but means "pretend missing files match the index". The previous round tried to address this, but failed because setup_revisions() ate "-m" flag before the caller had a chance to intervene. Signed-off-by: Junio C Hamano diff --git a/diff-index.c b/diff-index.c index 4a243b3..8694012 100644 --- a/diff-index.c +++ b/diff-index.c @@ -23,9 +23,7 @@ int main(int argc, const char **argv) for (i = 1; i < argc; i++) { const char *arg = argv[i]; - if (!strcmp(arg, "-m")) - match_missing = 1; - else if (!strcmp(arg, "--cached")) + if (!strcmp(arg, "--cached")) cached = 1; else usage(diff_cache_usage); @@ -37,5 +35,5 @@ int main(int argc, const char **argv) if (!rev.pending_objects || rev.pending_objects->next || rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1) usage(diff_cache_usage); - return run_diff_index(&rev, cached, match_missing); + return run_diff_index(&rev, cached); } diff --git a/diff-lib.c b/diff-lib.c index 63da3b5..2183b41 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -308,12 +308,20 @@ static void mark_merge_entries(void) } } -int run_diff_index(struct rev_info *revs, int cached, int match_missing) +int run_diff_index(struct rev_info *revs, int cached) { int ret; struct object *ent; struct tree *tree; const char *tree_name; + int match_missing = 0; + + /* + * Backward compatibility wart - "diff-index -m" does + * not mean "do not ignore merges", but totally different. + */ + if (!revs->ignore_merges) + match_missing = 1; if (read_cache() < 0) { perror("read_cache"); diff --git a/diff.h b/diff.h index 837d449..7150b90 100644 --- a/diff.h +++ b/diff.h @@ -171,6 +171,6 @@ extern const char *diff_unique_abbrev(const unsigned char *, int); extern int run_diff_files(struct rev_info *revs, int silent_on_removed); -extern int run_diff_index(struct rev_info *revs, int cached, int match_missing); +extern int run_diff_index(struct rev_info *revs, int cached); #endif /* DIFF_H */ -- cgit v0.10.2-6-g49f6 From 227bdb186f06fa65afb1a0e091b2b191132c29ba Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Sun, 23 Apr 2006 09:01:29 +0200 Subject: make update-index --chmod work with multiple files and --stdin The patch makes "--chmod=-x" and "--chmod=+x" act like "--add" and "--remove" to affect the behaviour of the command for the rest of the path parameters, not just the following one. Signed-off-by: Junio C Hamano diff --git a/update-index.c b/update-index.c index 1efac27..e2b2b88 100644 --- a/update-index.c +++ b/update-index.c @@ -328,7 +328,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1, return 0; } -static int chmod_path(int flip, const char *path) +static void chmod_path(int flip, const char *path) { int pos; struct cache_entry *ce; @@ -336,21 +336,24 @@ static int chmod_path(int flip, const char *path) pos = cache_name_pos(path, strlen(path)); if (pos < 0) - return -1; + goto fail; ce = active_cache[pos]; mode = ntohl(ce->ce_mode); if (!S_ISREG(mode)) - return -1; + goto fail; switch (flip) { case '+': ce->ce_mode |= htonl(0111); break; case '-': ce->ce_mode &= htonl(~0111); break; default: - return -1; + goto fail; } active_cache_changed = 1; - return 0; + report("chmod %cx '%s'", flip, path); + return; + fail: + die("git-update-index: cannot chmod %cx '%s'", flip, path); } static struct cache_file cache_file; @@ -478,6 +481,7 @@ int main(int argc, const char **argv) int read_from_stdin = 0; const char *prefix = setup_git_directory(); int prefix_length = prefix ? strlen(prefix) : 0; + char set_executable_bit = 0; git_config(git_default_config); @@ -544,8 +548,7 @@ int main(int argc, const char **argv) !strcmp(path, "--chmod=+x")) { if (argc <= i+1) die("git-update-index: %s ", path); - if (chmod_path(path[8], argv[++i])) - die("git-update-index: %s cannot chmod %s", path, argv[i]); + set_executable_bit = path[8]; continue; } if (!strcmp(path, "--assume-unchanged")) { @@ -594,6 +597,8 @@ int main(int argc, const char **argv) die("unknown option %s", path); } update_one(path, prefix, prefix_length); + if (set_executable_bit) + chmod_path(set_executable_bit, path); } if (read_from_stdin) { struct strbuf buf; @@ -608,6 +613,10 @@ int main(int argc, const char **argv) else path_name = buf.buf; update_one(path_name, prefix, prefix_length); + if (set_executable_bit) { + const char *p = prefix_path(prefix, prefix_length, path_name); + chmod_path(set_executable_bit, p); + } if (path_name != buf.buf) free(path_name); } -- cgit v0.10.2-6-g49f6 From 61e56c8823afd2c5a753a4969330ead0ba850c66 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 23 Apr 2006 18:00:24 +1000 Subject: gitk: Let git-rev-list do the argument list parsing This is a fix for a problem reported by Jim Radford where an argument list somewhere overflows on repositories with lots of tags. In fact it's now unnecessary to use git-rev-parse since git-rev-list can take all the arguments that git-rev-parse can. This is inspired by but not the same as the solutions suggested by Jim Radford and Linus Torvalds. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index 87e7162..5362b76 100755 --- a/gitk +++ b/gitk @@ -16,22 +16,6 @@ proc gitdir {} { } } -proc parse_args {rargs} { - global parsed_args - - if {[catch { - set parse_args [concat --default HEAD $rargs] - set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"] - }]} { - # if git-rev-parse failed for some reason... - if {$rargs == {}} { - set rargs HEAD - } - set parsed_args $rargs - } - return $parsed_args -} - proc start_rev_list {rlargs} { global startmsecs nextupdate ncmupdate global commfd leftover tclencoding datemode @@ -46,7 +30,7 @@ proc start_rev_list {rlargs} { } if {[catch { set commfd [open [concat | git-rev-list --header $order \ - --parents --boundary $rlargs] r] + --parents --boundary --default HEAD $rlargs] r] } err]} { puts stderr "Error executing git-rev-list: $err" exit 1 @@ -65,7 +49,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 -- cgit v0.10.2-6-g49f6 From a8aaf19c419a30d335f0640ab644112ecda6dd12 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 23 Apr 2006 22:45:55 +1000 Subject: 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 diff --git a/gitk b/gitk index 305aa2e..6cefc87 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 -- cgit v0.10.2-6-g49f6 From 3e1a70d925b46b9eac1ae523589298ddb6e295af Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Apr 2006 10:00:03 +1000 Subject: rev-parse: better error message for ambiguous arguments Currently, if git-rev-parse encounters an argument that is neither a recognizable revision name nor the name of an existing file or directory, and it hasn't encountered a "--" argument, it prints an error message saying "No such file or directory". This can be confusing for users, including users of programs such as gitk that use git-rev-parse, who may then think that they can't ask about the history of files that no longer exist. This makes it print a better error message, one that points out the ambiguity and tells the user what to do to fix it. Signed-off-by: Paul Mackerras diff --git a/rev-parse.c b/rev-parse.c index e956cd5..7f66ae2 100644 --- a/rev-parse.c +++ b/rev-parse.c @@ -160,6 +160,14 @@ static int show_file(const char *arg) return 0; } +static void die_badfile(const char *arg) +{ + if (errno != ENOENT) + die("'%s': %s", arg, strerror(errno)); + die("'%s' is ambiguous - revision name or file/directory name?\n" + "Please put '--' before the list of filenames.", arg); +} + int main(int argc, char **argv) { int i, as_is = 0, verify = 0; @@ -176,7 +184,7 @@ int main(int argc, char **argv) if (as_is) { if (show_file(arg) && as_is < 2) if (lstat(arg, &st) < 0) - die("'%s': %s", arg, strerror(errno)); + die_badfile(arg); continue; } if (!strcmp(arg,"-n")) { @@ -343,7 +351,7 @@ int main(int argc, char **argv) if (verify) die("Needed a single revision"); if (lstat(arg, &st) < 0) - die("'%s': %s", arg, strerror(errno)); + die_badfile(arg); } show_default(); if (verify && revs_count != 1) -- cgit v0.10.2-6-g49f6 From 4ee6bc99133760531dd25013c7a7165b2feed9cd Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:28 +0200 Subject: Document git-var -l listing also configuration variables Signed-off-by: Petr Baudis diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 90cb157..379571e 100644 --- a/Documentation/git-var.txt +++ b/Documentation/git-var.txt @@ -17,7 +17,9 @@ Prints a git logical variable. OPTIONS ------- -l:: - Cause the logical variables to be listed. + Cause the logical variables to be listed. In addition, all the + variables of the git configuration file .git/config are listed + as well. EXAMPLE -------- @@ -46,6 +48,7 @@ See Also -------- gitlink:git-commit-tree[1] gitlink:git-tag[1] +gitlink:git-repo-config[1] Author ------ -- cgit v0.10.2-6-g49f6 From 1ab661ddb7eb564c1c3cb97a512257860a651459 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:33 +0200 Subject: Document the configuration file This patch adds a Documentation/config.txt file included by git-repo-config and currently aggregating hopefully all the available git plumbing / core porcelain configuration variables, as well as briefly describing the format. It also updates an outdated bit of the example in git-repo-config(1). Signed-off-by: Petr Baudis diff --git a/Documentation/config.txt b/Documentation/config.txt new file mode 100644 index 0000000..b27b0d5 --- /dev/null +++ b/Documentation/config.txt @@ -0,0 +1,181 @@ +CONFIGURATION FILE +------------------ + +The git configuration file contains a number of variables that affect +the git commands behaviour. They can be used by both the git plumbing +and the porcelains. The variables are divided to sections, where +in the fully qualified variable name the variable itself is the last +dot-separated segment and the section name is everything before the last +dot. The variable names are case-insensitive and only alphanumeric +characters are allowed. Some variables may appear multiple times. + +The syntax is fairly flexible and permissive; whitespaces are mostly +ignored. The '#' and ';' characters begin commends to the end of line, +blank lines are ignored, lines containing strings enclosed in square +brackets start sections and all the other lines are recognized +as setting variables, in the form 'name = value'. If there is no equal +sign on the line, the entire line is taken as 'name' and the variable +is recognized as boolean "true". String values may be entirely or partially +enclosed in double quotes; some variables may require special value format. + +Example +~~~~~~~ + + # Core variables + [core] + ; Don't trust file modes + filemode = false + + # Our diff algorithm + [diff] + external = "/usr/local/bin/gnu-diff -u" + renames = true + +Variables +~~~~~~~~~ + +Note that this list is non-comprehensive and not necessarily complete. +For command-specific variables, you will find more detailed description +in the appropriate manual page. You will find description of non-core +porcelain configuration variables in the respective porcelain documentation. + +core.fileMode:: + If false, the executable bit differences between the index and + the working copy are ignored; useful on broken filesystems like FAT. + See gitlink:git-update-index[1]. True by default. + +core.gitProxy:: + A "proxy command" to execute (as 'command host port') instead + of establishing direct connection to the remote server when + using the git protocol for fetching. If the variable value is + in the "COMMAND for DOMAIN" format, the command is applied only + on hostnames ending with the specified domain string. This variable + may be set multiple times and is matched in the given order; + the first match wins. + + Can be overriden by the 'GIT_PROXY_COMMAND' environment variable + (which always applies universally, without the special "for" + handling). + +core.ignoreStat:: + The working copy files are assumed to stay unchanged until you + mark them otherwise manually - Git will not detect the file changes + by lstat() calls. This is useful on systems where those are very + slow, such as Microsoft Windows. See gitlink:git-update-index[1]. + False by default. + +core.onlyUseSymrefs:: + Always use the "symref" format instead of symbolic links for HEAD + and other symbolic reference files. True by default. + +core.repositoryFormatVersion:: + Internal variable identifying the repository format and layout + version. + +core.sharedRepository:: + If true, the repository is made shareable between several users + in a group (making sure all the files and objects are group-writable). + See gitlink:git-init-db[1]. False by default. + +core.warnAmbiguousRefs:: + If true, git will warn you if the ref name you passed it is ambiguous + and might match multiple refs in the .git/refs/ tree. True by default. + +apply.whitespace:: + Tells `git-apply` how to handle whitespaces, in the same way + as the '--whitespace' option. See gitlink:git-apply[1]. + +diff.renameLimit:: + The number of files to consider when performing the copy/rename + detection; equivalent to the git diff option '-l'. + +format.headers:: + Additional email headers to include in a patch to be submitted + by mail. See gitlink:git-format-patch[1]. + +gitcvs.enabled:: + Whether the cvs pserver interface is enabled for this repository. + See gitlink:git-cvsserver[1]. + +gitcvs.logfile:: + Path to a log file where the cvs pserver interface well... logs + various stuff. See gitlink:git-cvsserver[1]. + +http.sslVerify:: + Whether to verify the SSL certificate when fetching or pushing + over HTTPS. Can be overriden by the 'GIT_SSL_NO_VERIFY' environment + variable. + +http.sslCert:: + File containing the SSL certificate when fetching or pushing + over HTTPS. Can be overriden by the 'GIT_SSL_CERT' environment + variable. + +http.sslKey:: + File containing the SSL private key when fetching or pushing + over HTTPS. Can be overriden by the 'GIT_SSL_KEY' environment + variable. + +http.sslCAInfo:: + File containing the certificates to verify the peer with when + fetching or pushing over HTTPS. Can be overriden by the + 'GIT_SSL_CAINFO' environment variable. + +http.sslCAPath:: + Path containing files with the CA certificates to verify the peer + with when fetching or pushing over HTTPS. Can be overriden + by the 'GIT_SSL_CAPATH' environment variable. + +http.maxRequests:: + How many HTTP requests to launch in parallel. Can be overriden + by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5. + +http.lowSpeedLimit, http.lowSpeedTime:: + If the HTTP transfer speed is less than 'http.lowSpeedLimit' + for longer than 'http.lowSpeedTime' seconds, the transfer is aborted. + Can be overriden by the 'GIT_HTTP_LOW_SPEED_LIMIT' and + 'GIT_HTTP_LOW_SPEED_TIME' environment variables. + +i18n.commitEncoding:: + Character encoding the commit messages are stored in; git itself + does not care per se, but this information is necessary e.g. when + importing commits from emails or in the gitk graphical history + browser (and possibly at other places in the future or in other + porcelains). See e.g. gitlink:git-mailinfo[1]. Defaults to 'utf-8'. + +merge.summary:: + Whether to include summaries of merged commits in newly created + merge commit messages. False by default. + +pull.octopus:: + The default merge strategy to use when pulling multiple branches + at once. + +pull.twohead:: + The default merge strategy to use when pulling a single branch. + +show.difftree:: + The default gitlink:git-diff-tree[1] arguments to be used + for gitlink:git-show[1]. + +showbranch.default:: + The default set of branches for gitlink:git-show-branch[1]. + See gitlink:git-show-branch[1]. + +user.email:: + Your email address to be recorded in any newly created commits. + Can be overriden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL' + environment variables. See gitlink:git-commit-tree[1]. + +user.name:: + Your full name to be recorded in any newly created commits. + Can be overriden by the 'GIT_AUTHOR_NAME' and 'GIT_COMMITTER_NAME' + environment variables. See gitlink:git-commit-tree[1]. + +whatchanged.difftree:: + The default gitlink:git-diff-tree[1] arguments to be used + for gitlink:git-whatchanged[1]. + +imap:: + The configuration variables in the 'imap' section are described + in gitlink:git-imap-send[1]. diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 26759a8..71f96bd 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -87,11 +87,11 @@ Given a .git/config like this: renames = true ; Proxy settings - [proxy] - command="ssh" for "ssh://kernel.org/" - command="proxy-command" for kernel.org - command="myprotocol-command" for "my://" - command=default-proxy ; for all the rest + [core] + gitproxy="ssh" for "ssh://kernel.org/" + gitproxy="proxy-command" for kernel.org + gitproxy="myprotocol-command" for "my://" + gitproxy=default-proxy ; for all the rest you can set the filemode to true with @@ -104,7 +104,7 @@ to what URL they apply. Here is how to change the entry for kernel.org to "ssh". ------------ -% git repo-config proxy.command '"ssh" for kernel.org' 'for kernel.org$' +% git repo-config core.gitproxy '"ssh" for kernel.org' 'for kernel.org$' ------------ This makes sure that only the key/value pair for kernel.org is replaced. @@ -115,7 +115,7 @@ To delete the entry for renames, do % git repo-config --unset diff.renames ------------ -If you want to delete an entry for a multivar (like proxy.command above), +If you want to delete an entry for a multivar (like core.gitproxy above), you have to provide a regex matching the value of exactly one line. To query the value for a given key, do @@ -133,27 +133,27 @@ or or, to query a multivar: ------------ -% git repo-config --get proxy.command "for kernel.org$" +% git repo-config --get core.gitproxy "for kernel.org$" ------------ If you want to know all the values for a multivar, do: ------------ -% git repo-config --get-all proxy.command +% git repo-config --get-all core.gitproxy ------------ -If you like to live dangerous, you can replace *all* proxy.commands by a +If you like to live dangerous, you can replace *all* core.gitproxy by a new one with ------------ -% git repo-config --replace-all proxy.command ssh +% git repo-config --replace-all core.gitproxy ssh ------------ However, if you really only want to replace the line for the default proxy, i.e. the one without a "for ..." postfix, do something like this: ------------ -% git repo-config proxy.command ssh '! for ' +% git repo-config core.gitproxy ssh '! for ' ------------ To actually match only values with an exclamation mark, you have to @@ -163,13 +163,16 @@ To actually match only values with an exclamation mark, you have to ------------ +include::config.txt[] + + Author ------ Written by Johannes Schindelin Documentation -------------- -Documentation by Johannes Schindelin. +Documentation by Johannes Schindelin, Petr Baudis and the git-list . GIT --- diff --git a/config.c b/config.c index 7ea8a73..4e1f0c2 100644 --- a/config.c +++ b/config.c @@ -252,7 +252,7 @@ int git_default_config(const char *var, const char *value) return 0; } - /* Add other config variables here.. */ + /* Add other config variables here and to Documentation/config.txt. */ return 0; } -- cgit v0.10.2-6-g49f6 From de791f15a1b01e63f3c870c8497c594c47452fab Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:25 +0200 Subject: 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 diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 71f96bd..566cfa1 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 c5ebb76..fa8aba7 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); -- cgit v0.10.2-6-g49f6 From e1cbc46d12a0524fd5e710cbfaf3f178fc3da504 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 25 Apr 2006 00:59:30 +0200 Subject: 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 diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt index 379571e..a5b1a0d 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 7d3f78e..0b37d26 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 ) -- cgit v0.10.2-6-g49f6 From 7d09fbe4ab7f080a8f8f5dcef7e0f3edf5e26019 Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Tue, 18 Apr 2006 08:11:06 -0500 Subject: socksetup: don't return on set_reuse_addr() error The set_reuse_addr() error case was the only error case in socklist() where we returned rather than continued. Not sure why. Either we must free the socklist, or continue. This patch continues on error. Signed-off-by: Serge E. Hallyn Signed-off-by: Junio C Hamano (cherry picked from 0032d548db56eac9ea09b4ba05843365f6325b85 commit) diff --git a/daemon.c b/daemon.c index a1ccda3..776749e 100644 --- a/daemon.c +++ b/daemon.c @@ -535,7 +535,7 @@ static int socksetup(int port, int **socklist_p) if (set_reuse_addr(sockfd)) { close(sockfd); - return 0; /* not fatal */ + continue; } if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { -- cgit v0.10.2-6-g49f6 From a90a6d249bc4fe29c11ce3291f9ec90d8e5e67f4 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Apr 2006 17:12:46 +1000 Subject: 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 diff --git a/gitk b/gitk index 4ee5122..6d6a2f3 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 -- cgit v0.10.2-6-g49f6 From d16c0812a9472794175b2543c2aa2df45a927202 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 25 Apr 2006 21:21:10 +1000 Subject: 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 diff --git a/gitk b/gitk index 6d6a2f3..a33a1e8 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]} { -- cgit v0.10.2-6-g49f6 From a970fcf249534ca21ad721b289402c135722a24c Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Tue, 18 Apr 2006 23:53:07 +0200 Subject: [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 diff --git a/gitk b/gitk index 5362b76..7e80a7b 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] -- cgit v0.10.2-6-g49f6 From 71459c193d04870076efa0a387c317390b53e3e2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Apr 2006 23:11:17 -0700 Subject: Makefile: remove and create libgit.a from scratch. Foolishly I renamed diff.o around which caused an old diff.o taken out of libgit.a and got linked into resulting binary and exhibited mysterious breakage for many people. This borrows from the kernel Makefile (scripts/Makefile.build) to first remove the target and then recreate. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index d9a3a82..809383c 100644 --- a/Makefile +++ b/Makefile @@ -575,7 +575,7 @@ $(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS) $(DIFF_OBJS): diffcore.h $(LIB_FILE): $(LIB_OBJS) - $(AR) rcs $@ $(LIB_OBJS) + rm -f $@ && $(AR) rcs $@ $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o -- cgit v0.10.2-6-g49f6 From 710158e3ca3f45a30e08a50dadf427fe95311b9c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Apr 2006 23:40:09 -0700 Subject: 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 --git a/diff.c b/diff.c index 13b216f..6762fce 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) -- cgit v0.10.2-6-g49f6 From 5348b6e7f4f07b75b2ea23a3c8fee62919fa254c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Apr 2006 23:59:28 -0700 Subject: git-cvsserver: typofixes Signed-off-by: Junio C Hamano diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 0b37d26..11d153c 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -88,7 +88,7 @@ my $TEMP_DIR = tempdir( CLEANUP => 1 ); $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 @@ while () { 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 @@ Log::Log4perl =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 -- cgit v0.10.2-6-g49f6 From fdeb6bf55b7c578ce1735092c6dfbf6968cbee5c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 02:27:59 -0700 Subject: t0000-basic: Add ls-tree recursive test back. When we updated ls-tree recursive output to omit the tree nodes, 246cc52f388cae8ca99e5a12b8458c9bfa467765 adjusted the old test so that we do not expect to see trees in its output. Later, with 0f8f45cb4a7e664b396f73c25891da46b953b8b8, we added back the ability to show both with -t option, but we forgot to update the test as well. Signed-off-by: Junio C Hamano diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 6729a18..c69706f 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -174,6 +174,27 @@ test_expect_success \ 'git-ls-tree -r output for a known tree.' \ 'diff current expected' +# But with -r -t we can have both. +test_expect_success \ + 'showing tree with git-ls-tree -r -t' \ + 'git-ls-tree -r -t $tree >current' +cat >expected <<\EOF +100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 +120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym +040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2 +100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2 +120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym +040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3 +100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3 +120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym +040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3 +100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3 +120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym +EOF +test_expect_success \ + 'git-ls-tree -r output for a known tree.' \ + 'diff current expected' + ################################################################ rm .git/index test_expect_success \ -- cgit v0.10.2-6-g49f6 From 2d86d2c6fcbd62696447fe3dbfac664e6c82b0ce Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 03:12:58 -0700 Subject: Makefile: remove and create xdiff library from scratch. ... in the same spirit as 71459c193d04870076efa0a387c317390b53e3e2. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 087ff27..8ce27a6 100644 --- a/Makefile +++ b/Makefile @@ -580,7 +580,7 @@ $(LIB_FILE): $(LIB_OBJS) XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o $(XDIFF_LIB): $(XDIFF_OBJS) - $(AR) rcs $@ $(XDIFF_OBJS) + rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS) doc: -- cgit v0.10.2-6-g49f6 From 3496277a561307c3d31d2085347af8eb4c667c36 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 03:18:51 -0700 Subject: commit-tree: allow generic object name for the tree as well. We use get_sha1() for -p (parent) objects, but still used get_sha1_hex() for the tree. Just to be consistent, allow extended SHA1 expression for the tree object name. Note that this is not to encourage funky things like this: git-commit-tree HEAD^{tree} -p HEAD Signed-off-by: Junio C Hamano diff --git a/commit-tree.c b/commit-tree.c index 2d86518..e91af4b 100644 --- a/commit-tree.c +++ b/commit-tree.c @@ -92,7 +92,7 @@ int main(int argc, char **argv) git_config(git_default_config); - if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0) + if (argc < 2 || get_sha1(argv[1], tree_sha1) < 0) usage(commit_tree_usage); check_valid(tree_sha1, tree_type); -- cgit v0.10.2-6-g49f6 From b176e6ba5bc37466ffcb6c8c0f38c47ec6e9e73a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 12:07:42 -0700 Subject: rebase: typofix. Noticed by Sean. Signed-off-by: Junio C Hamano diff --git a/git-rebase.sh b/git-rebase.sh index 86dfe9c..f7b2b94 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -107,7 +107,7 @@ onto=$(git-rev-parse --verify "${onto_name}^0") || exit # Check if we are already based on $onto, but this should be # done only when upstream and onto are the same. -if test "$upstream" = "onto" +if test "$upstream" = "$onto" then mb=$(git-merge-base "$onto" "$branch") if test "$mb" = "$onto" -- cgit v0.10.2-6-g49f6 From e23d0b4a4a55cc07e133905f0e9526b3550dd61b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 26 Apr 2006 10:15:54 -0700 Subject: Fix filename verification when in a subdirectory When we are in a subdirectory of a git archive, we need to take the prefix of that subdirectory into accoung when we verify filename arguments. Noted by Matthias Lederhofer This also uses the improved error reporting for all the other git commands that use the revision parsing interfaces, not just git-rev-parse. Also, it makes the error reporting for mixed filenames and argument flags clearer (you cannot put flags after the start of the pathname list). [jc: with fix to a trivial typo noticed by Timo Hirvonen] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 69801b0..4d8fabc 100644 --- a/cache.h +++ b/cache.h @@ -134,6 +134,7 @@ extern const char *setup_git_directory_gently(int *); 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); #define alloc_nr(x) (((x)+16)*3/2) diff --git a/rev-parse.c b/rev-parse.c index 7f66ae2..62e16af 100644 --- a/rev-parse.c +++ b/rev-parse.c @@ -160,14 +160,6 @@ static int show_file(const char *arg) return 0; } -static void die_badfile(const char *arg) -{ - if (errno != ENOENT) - die("'%s': %s", arg, strerror(errno)); - die("'%s' is ambiguous - revision name or file/directory name?\n" - "Please put '--' before the list of filenames.", arg); -} - int main(int argc, char **argv) { int i, as_is = 0, verify = 0; @@ -177,14 +169,12 @@ int main(int argc, char **argv) git_config(git_default_config); for (i = 1; i < argc; i++) { - struct stat st; char *arg = argv[i]; char *dotdot; if (as_is) { if (show_file(arg) && as_is < 2) - if (lstat(arg, &st) < 0) - die_badfile(arg); + verify_filename(prefix, arg); continue; } if (!strcmp(arg,"-n")) { @@ -350,8 +340,7 @@ int main(int argc, char **argv) continue; if (verify) die("Needed a single revision"); - if (lstat(arg, &st) < 0) - die_badfile(arg); + verify_filename(prefix, arg); } show_default(); if (verify && revs_count != 1) diff --git a/revision.c b/revision.c index e1f9816..03dd238 100644 --- a/revision.c +++ b/revision.c @@ -675,17 +675,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch arg++; } if (get_sha1(arg, sha1) < 0) { - struct stat st; int j; if (seen_dashdash || local_flags) die("bad revision '%s'", arg); /* If we didn't have a "--", all filenames must exist */ - for (j = i; j < argc; j++) { - if (lstat(argv[j], &st) < 0) - die("'%s': %s", argv[j], strerror(errno)); - } + for (j = i; j < argc; j++) + verify_filename(revs->prefix, argv[j]); + revs->prune_data = get_pathspec(revs->prefix, argv + i); break; } diff --git a/setup.c b/setup.c index 36ede3d..cce9bb8 100644 --- a/setup.c +++ b/setup.c @@ -62,6 +62,29 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) return path; } +/* + * Verify a filename that we got as an argument for a pathspec + * entry. Note that a filename that begins with "-" never verifies + * as true, because even if such a filename were to exist, we want + * it to be preceded by the "--" marker (or we want the user to + * use a format like "./-filename") + */ +void verify_filename(const char *prefix, const char *arg) +{ + const char *name; + struct stat st; + + if (*arg == '-') + die("bad flag '%s' used after filename", arg); + name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : 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("'%s': %s", arg, strerror(errno)); +} + const char **get_pathspec(const char *prefix, const char **pathspec) { const char *entry = *pathspec; -- cgit v0.10.2-6-g49f6 From 5981e09999e90b389a02843671529a0faaf72143 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 16:55:25 -0700 Subject: commit-tree.c: check_valid() microoptimization. There is no point reading the whole object just to make sure it exists and it is of the expected type. We added sha1_object_info() for such need after this code was written, so use it. Signed-off-by: Junio C Hamano diff --git a/commit-tree.c b/commit-tree.c index 2d86518..2595850 100644 --- a/commit-tree.c +++ b/commit-tree.c @@ -45,14 +45,13 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...) static void check_valid(unsigned char *sha1, const char *expect) { - void *buf; char type[20]; - unsigned long size; - buf = read_sha1_file(sha1, type, &size); - if (!buf || strcmp(type, expect)) - die("%s is not a valid '%s' object", sha1_to_hex(sha1), expect); - free(buf); + if (sha1_object_info(sha1, type, NULL)) + die("%s is not a valid object", sha1_to_hex(sha1)); + if (expect && strcmp(type, expect)) + die("%s is not a valid '%s' object", sha1_to_hex(sha1), + expect); } /* -- cgit v0.10.2-6-g49f6 From ea92f41ff92b2fef54c8da4abb03c8101160a034 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 15:09:27 -0700 Subject: 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 diff --git a/cache.h b/cache.h index 4d8fabc..a4f253e 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 f2a9f25..b6ed014 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 cce9bb8..fe7f884 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; -- cgit v0.10.2-6-g49f6 From 031321c654de3c19de45b2dacbdc990b5a694e34 Mon Sep 17 00:00:00 2001 From: sean Date: Wed, 26 Apr 2006 10:49:38 -0400 Subject: 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 diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 4a7e67a..1b482ab 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 f7b2b94..9e25902 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" -- cgit v0.10.2-6-g49f6 From 093b06889104d218e95251176af5cf4aa5dd5cdd Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Wed, 26 Apr 2006 09:10:01 -0700 Subject: 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 diff --git a/git-fetch.sh b/git-fetch.sh index 83143f8..280f62e 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 ;; -- cgit v0.10.2-6-g49f6 From 9af0b8dbe2fb252262412a11254e2bcc6ffb87bb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Apr 2006 18:25:15 -0700 Subject: t0000-basic: more commit-tree tests. Signed-off-by: Junio C Hamano diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index c69706f..cf33989 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -226,4 +226,32 @@ test_expect_success \ 'no diff after checkout and git-update-index --refresh.' \ 'git-diff-files >current && cmp -s current /dev/null' +################################################################ +P=087704a96baf1c2d1c869a8b084481e121c88b5b +test_expect_success \ + 'git-commit-tree records the correct tree in a commit.' \ + 'commit0=$(echo NO | git-commit-tree $P) && + tree=$(git show --pretty=raw $commit0 | + sed -n -e "s/^tree //p" -e "/^author /q") && + test "z$tree" = "z$P"' + +test_expect_success \ + 'git-commit-tree records the correct parent in a commit.' \ + 'commit1=$(echo NO | git-commit-tree $P -p $commit0) && + parent=$(git show --pretty=raw $commit1 | + sed -n -e "s/^parent //p" -e "/^author /q") && + test "z$commit0" = "z$parent"' + +test_expect_success \ + 'git-commit-tree omits duplicated parent in a commit.' \ + 'commit2=$(echo NO | git-commit-tree $P -p $commit0 -p $commit0) && + parent=$(git show --pretty=raw $commit2 | + sed -n -e "s/^parent //p" -e "/^author /q" | + sort -u) && + test "z$commit0" = "z$parent" && + numparent=$(git show --pretty=raw $commit2 | + sed -n -e "s/^parent //p" -e "/^author /q" | + wc -l) && + test $numparent = 1' + test_done -- cgit v0.10.2-6-g49f6 From 61678d87c2aad3bd668196993d8c32f298ddf5ff Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 01:59:00 -0700 Subject: diff-index: fix compilation warnings. Signed-off-by: Junio C Hamano diff --git a/diff-index.c b/diff-index.c index 8694012..8c9f601 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; -- cgit v0.10.2-6-g49f6 From 7fcceed7a024f5e2868c123c4ace5b7894ea3423 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Thu, 27 Apr 2006 19:21:49 +1000 Subject: 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 diff --git a/gitk b/gitk index 7c25d2e..bd205f8 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} -- cgit v0.10.2-6-g49f6 From 83aa18eade572679319406d6dc12a81599a4d57e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 27 Apr 2006 09:02:54 -0700 Subject: 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 diff --git a/builtin-help.c b/builtin-help.c index 10a59cc..7470faa 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 aa2b814..01b7e28 100644 --- a/git.c +++ b/git.c @@ -8,7 +8,6 @@ #include #include #include -#include #include "git-compat-util.h" #include "exec_cmd.h" -- cgit v0.10.2-6-g49f6 From 55e1805dffeb5d2a8ccd717b2d07ca8887436a69 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 15:42:01 -0700 Subject: 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 diff --git a/pack-check.c b/pack-check.c index 84ed90d..e575879 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, -- cgit v0.10.2-6-g49f6 From 9a8b6a0a9d4de3d9c1005b872b9b57a213d3e9f8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 19:31:46 -0700 Subject: 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 diff --git a/pack-objects.c b/pack-objects.c index c0acc46..6604338 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); -- cgit v0.10.2-6-g49f6 From c74320872b445104fe5c265e60785d9d26d94cc5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 27 Apr 2006 15:37:18 -0700 Subject: 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 diff --git a/Makefile b/Makefile index 8ce27a6..14193aa 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 0000000..0256369 --- /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 47408a0..76169e3 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 01b7e28..00fb399 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; -- cgit v0.10.2-6-g49f6 From c1d1128bef97ae16a6740538abe5fb81c67ed239 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 28 Apr 2006 02:32:44 -0700 Subject: 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 diff --git a/git-am.sh b/git-am.sh index eab4aa8..872145b 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 -- cgit v0.10.2-6-g49f6 From d0ad1653662a214baddc1d1ce669a28b91a3ae76 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 28 Apr 2006 10:42:28 +0200 Subject: 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 diff --git a/git-annotate.perl b/git-annotate.perl index 9df72a1..a7aab25 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]$/ ) { -- cgit v0.10.2-6-g49f6 From fe77bb1a02d5b8dd3915cd27a83b660debaecda4 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Fri, 28 Apr 2006 10:41:19 +0200 Subject: annotate: display usage information if no filename was given Signed-off-by: Matthias Kestenholz diff --git a/git-annotate.perl b/git-annotate.perl index a7aab25..a6a7a48 100755 --- a/git-annotate.perl +++ b/git-annotate.perl @@ -10,9 +10,10 @@ use warnings; 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 @@ sub usage() { Use revs from revs-file instead of calling git-rev-list -h, --help This message. -'; +"; exit(1); } @@ -35,7 +36,7 @@ my $rc = GetOptions( "long|l" => \$longrev, "help|h" => \$help, "rename|r" => \$rename, "rev-file|S=s" => \$rev_file); -if (!$rc or $help) { +if (!$rc or $help or !@ARGV) { usage(); } -- cgit v0.10.2-6-g49f6 From 2eaf273d518717c84d748051e05210656f7b7e88 Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:04 -0400 Subject: Update the git-branch man page to include the "-r" option, and fix up asciidoc "callouts" Signed-off-by: Sean Estabrooks diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 71ecd85..72fb2f8 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 663a3a3..ebcc898 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 .' -- cgit v0.10.2-6-g49f6 From 48aeecdcc14684111ddb8ac0ec3bfdc5245ee75e Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:05 -0400 Subject: 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 diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt index 3ab9b91..4b56370 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 985bb2f..0951289 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 890931c..7267bcd 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 aeb1115..8a150d8 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 b7b9798..ebcfe5e 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 0a1b0ad..d4137fc 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 -- cgit v0.10.2-6-g49f6 From 776e994af5b85d77424581bda37fb615897063cc Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:06 -0400 Subject: 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 diff --git a/Documentation/Makefile b/Documentation/Makefile index f4cbf7e..c1af22c 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 0000000..ad03755 --- /dev/null +++ b/Documentation/callouts.xsl @@ -0,0 +1,16 @@ + + + + + + + .sp + + + + + + + .br + + -- cgit v0.10.2-6-g49f6 From aa6bf0eb6489d652c5877d65160ed33c857afa74 Mon Sep 17 00:00:00 2001 From: Sean Estabrooks Date: Fri, 28 Apr 2006 09:15:03 -0400 Subject: Fix trivial typo in git-log man page. Signed-off-by: Sean Estabrooks diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 76cb894..af378ff 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 -- cgit v0.10.2-6-g49f6 From 65056021f2d2dcb7a72f05c5d6cbbd79a79b9d40 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 28 Apr 2006 23:20:52 -0700 Subject: built-in diff. This starts to replace the shell script version of "git diff". Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 8ce27a6..277c1ac 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 0000000..1968212 --- /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 47408a0..52ffa52 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 01b7e28..ff9b87a 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; -- cgit v0.10.2-6-g49f6 From 0fe7c1de16f71312e6adac4b85bddf0d62a47168 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 29 Apr 2006 01:24:49 -0700 Subject: 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 diff --git a/builtin-diff.c b/builtin-diff.c index 1968212..c543105 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 ca36f5d..8a8fe38 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 7150b90..b3b2c4d 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 f2a9f25..4381d45 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; } -- cgit v0.10.2-6-g49f6 From 334b506a3487f762493d3db2d3053114f25590e3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 00:26:41 -0700 Subject: builtin-diff.c: die() formatting type fix. Signed-off-by: Junio C Hamano diff --git a/builtin-diff.c b/builtin-diff.c index c543105..b6114ce 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; -- cgit v0.10.2-6-g49f6 From 262a6ef76a1dde97ab50d79fa5cd6d3f9f125765 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Sat, 29 Apr 2006 15:50:28 +0000 Subject: git-format-patch: Use rfc2822 compliant date. Signed-off-by: Huw Davies Signed-off-by: Junio C Hamano diff --git a/git-format-patch.sh b/git-format-patch.sh index c7133bc..c077f44 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"; -- cgit v0.10.2-6-g49f6 From 755225de6ce4c7b39137afefb35208d0dbc14d9d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 29 Apr 2006 21:22:49 -0700 Subject: 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 diff --git a/Makefile b/Makefile index 8ce27a6..9919992 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 0000000..a0c1caa --- /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 47408a0..94fa9b5 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 01b7e28..fd479e9 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; -- cgit v0.10.2-6-g49f6 From 5c477b972583e96b65ef1613c5d0fb1b90af3d0e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Apr 2006 14:05:55 +0200 Subject: 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 diff --git a/builtin-push.c b/builtin-push.c index a0c1caa..4e659f0 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; -- cgit v0.10.2-6-g49f6 From 7aaf83dafbde7aaa60a1e220e967faaeb28a46f6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 16:03:27 -0700 Subject: 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 diff --git a/builtin-push.c b/builtin-push.c index 4e659f0..9a861b5 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; -- cgit v0.10.2-6-g49f6 From f8b28a4078a29cbf93cac6f9edd8d5c203777313 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 1 May 2006 09:50:57 +1000 Subject: 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 diff --git a/gitk b/gitk index bd205f8..f983dee 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} -- cgit v0.10.2-6-g49f6 From ea4a19e172e8db52779c06723b1e99cc12f228c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 30 Apr 2006 00:54:29 -0700 Subject: 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 diff --git a/revision.c b/revision.c index f2a9f25..d1f85a8 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; -- cgit v0.10.2-6-g49f6 From da7c24dd9c75d014780179f8eb843968919e4c46 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 2 May 2006 11:15:29 +1000 Subject: gitk: Basic support for highlighting one view within another With this, one view can be used as a highlight for another, so that the commits that are in the highlight view are displayed in bold. This required some fairly major changes to how the list of ids, parents, children, and id to row mapping were stored for each view. We can now be reading in several views at once; for all except the current view, we just update the displayorder and the lists of parents and children for the view. This also creates a little bit of infrastructure for handling the watch cursor. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index f983dee..a83a754 100755 --- a/gitk +++ b/gitk @@ -16,75 +16,82 @@ proc gitdir {} { } } -proc start_rev_list {} { +proc start_rev_list {view} { global startmsecs nextupdate ncmupdate global commfd leftover tclencoding datemode - global revtreeargs curview viewfiles + global revtreeargs viewfiles commitidx set startmsecs [clock clicks -milliseconds] set nextupdate [expr {$startmsecs + 100}] set ncmupdate 1 - initlayout + set commitidx($view) 0 set args $revtreeargs - if {$viewfiles($curview) ne {}} { - set args [concat $args "--" $viewfiles($curview)] + if {$viewfiles($view) ne {}} { + set args [concat $args "--" $viewfiles($view)] } set order "--topo-order" if {$datemode} { set order "--date-order" } if {[catch { - set commfd [open [concat | git-rev-list --header $order \ - --parents --boundary --default HEAD $args] r] + set fd [open [concat | git-rev-list --header $order \ + --parents --boundary --default HEAD $args] r] } err]} { puts stderr "Error executing git-rev-list: $err" exit 1 } - set leftover {} - fconfigure $commfd -blocking 0 -translation lf + set commfd($view) $fd + set leftover($view) {} + fconfigure $fd -blocking 0 -translation lf if {$tclencoding != {}} { - fconfigure $commfd -encoding $tclencoding + fconfigure $fd -encoding $tclencoding } - fileevent $commfd readable [list getcommitlines $commfd] - . config -cursor watch - settextcursor watch + fileevent $fd readable [list getcommitlines $fd $view] + nowbusy $view } proc stop_rev_list {} { - global commfd + global commfd curview - if {![info exists commfd]} return + if {![info exists commfd($curview)]} return + set fd $commfd($curview) catch { - set pid [pid $commfd] + set pid [pid $fd] exec kill $pid } - catch {close $commfd} - unset commfd + catch {close $fd} + unset commfd($curview) } proc getcommits {} { - global phase canv mainfont + global phase canv mainfont curview set phase getcommits - start_rev_list + initlayout + start_rev_list $curview $canv delete all $canv create text 3 3 -anchor nw -text "Reading commits..." \ -font $mainfont -tags textitems } -proc getcommitlines {commfd} { +proc getcommitlines {fd view} { global commitlisted nextupdate - global leftover + global leftover commfd global displayorder commitidx commitrow commitdata - global parentlist childlist children + global parentlist childlist children curview hlview + global vparentlist vchildlist vdisporder vcmitlisted - set stuff [read $commfd] + set stuff [read $fd] if {$stuff == {}} { - if {![eof $commfd]} return + if {![eof $fd]} return + unset commfd($view) # set it blocking so we wait for the process to terminate - fconfigure $commfd -blocking 1 - if {![catch {close $commfd} err]} { - after idle finishcommits + fconfigure $fd -blocking 1 + if {![catch {close $fd} err]} { + notbusy $view + if {$view == $curview} { + after idle finishcommits + } return } if {[string range $err 0 4] == "usage"} { @@ -103,13 +110,13 @@ proc getcommitlines {commfd} { while 1 { set i [string first "\0" $stuff $start] if {$i < 0} { - append leftover [string range $stuff $start end] + append leftover($view) [string range $stuff $start end] break } if {$start == 0} { - set cmit $leftover + set cmit $leftover($view) append cmit [string range $stuff 0 [expr {$i - 1}]] - set leftover {} + set leftover($view) {} } else { set cmit [string range $stuff $start [expr {$i - 1}]] } @@ -145,40 +152,49 @@ proc getcommitlines {commfd} { set i 0 foreach p $olds { if {$i == 0 || [lsearch -exact $olds $p] >= $i} { - lappend children($p) $id + lappend children($view,$p) $id } incr i } } else { set olds {} } - lappend parentlist $olds - if {[info exists children($id)]} { - lappend childlist $children($id) - unset children($id) - } else { - lappend childlist {} + if {![info exists children($view,$id)]} { + set children($view,$id) {} } set commitdata($id) [string range $cmit [expr {$j + 1}] end] - set commitrow($id) $commitidx - incr commitidx - lappend displayorder $id - lappend commitlisted $listed + set commitrow($view,$id) $commitidx($view) + incr commitidx($view) + if {$view == $curview} { + lappend parentlist $olds + lappend childlist $children($view,$id) + lappend displayorder $id + lappend commitlisted $listed + } else { + lappend vparentlist($view) $olds + lappend vchildlist($view) $children($view,$id) + lappend vdisporder($view) $id + lappend vcmitlisted($view) $listed + } set gotsome 1 } if {$gotsome} { - layoutmore + if {$view == $curview} { + layoutmore + } elseif {[info exists hlview] && $view == $hlview} { + highlightmore + } } if {[clock clicks -milliseconds] >= $nextupdate} { - doupdate 1 + doupdate } } -proc doupdate {reading} { +proc doupdate {} { global commfd nextupdate numcommits ncmupdate - if {$reading} { - fileevent $commfd readable {} + foreach v [array names commfd] { + fileevent $commfd($v) readable {} } update set nextupdate [expr {[clock clicks -milliseconds] + 100}] @@ -189,8 +205,9 @@ proc doupdate {reading} { } else { set ncmupdate [expr {$numcommits + 100}] } - if {$reading} { - fileevent $commfd readable [list getcommitlines $commfd] + foreach v [array names commfd] { + set fd $commfd($v) + fileevent $fd readable [list getcommitlines $fd $v] } } @@ -200,13 +217,18 @@ proc readcommit {id} { } proc updatecommits {} { - global viewdata curview revtreeargs phase + global viewdata curview revtreeargs phase displayorder + global children commitrow if {$phase ne {}} { stop_rev_list set phase {} } set n $curview + foreach id $displayorder { + catch {unset children($n,$id)} + catch {unset commitrow($n,$id)} + } set curview -1 catch {unset viewdata($n)} readrefs @@ -363,14 +385,23 @@ 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 -font $uifont + menu .bar.view.hl -font $uifont -tearoff 0 .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 "New view..." -command {newview 0} + .bar.view add command -label "Edit view..." -command editview \ + -state disabled .bar.view add command -label "Delete view" -command delview -state disabled + .bar.view add cascade -label "Highlight" -menu .bar.view.hl .bar.view add separator .bar.view add radiobutton -label "All files" -command {showview 0} \ -variable selectedview -value 0 + .bar.view.hl add command -label "New view..." -command {newview 1} + .bar.view.hl add command -label "Remove" -command delhighlight \ + -state disabled + .bar.view.hl add separator + menu .bar.help .bar add cascade -label "Help" -menu .bar.help .bar.help add command -label "About gitk" -command about @@ -1154,9 +1185,10 @@ proc highlight_flist {ll} { # Code to implement multiple views -proc newview {} { - global nextviewnum newviewname newviewperm uifont +proc newview {ishighlight} { + global nextviewnum newviewname newviewperm uifont newishighlight + set newishighlight $ishighlight set top .gitkview if {[winfo exists $top]} { raise $top @@ -1215,19 +1247,23 @@ proc vieweditor {top n title} { focus $top.t } -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 +proc doviewmenu {m first cmd op args} { + set nmenu [$m index end] + for {set i $first} {$i <= $nmenu} {incr i} { + if {[$m entrycget $i -command] eq $cmd} { + eval $m $op $i $args + break } } - return {} +} + +proc allviewmenus {n op args} { + doviewmenu .bar.view 6 [list showview $n] $op $args + doviewmenu .bar.view.hl 3 [list addhighlight $n] $op $args } proc newviewok {top n} { - global nextviewnum newviewperm newviewname + global nextviewnum newviewperm newviewname newishighlight global viewname viewfiles viewperm selectedview curview set files {} @@ -1243,18 +1279,18 @@ proc newviewok {top n} { 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 + addviewmenu $n + if {!$newishighlight} { + after idle showview $n + } else { + after idle addhighlight $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) - } + allviewmenus $n entryconf -label $viewname($n) } if {$files ne $viewfiles($n)} { set viewfiles($n) $files @@ -1270,15 +1306,21 @@ proc delview {} { global curview viewdata viewperm if {$curview == 0} return - set i [viewmenuitem $curview] - if {$i ne {}} { - .bar.view delete $i - } + allviewmenus $curview delete set viewdata($curview) {} set viewperm($curview) 0 showview 0 } +proc addviewmenu {n} { + global viewname + + .bar.view add radiobutton -label $viewname($n) \ + -command [list showview $n] -variable selectedview -value $n + .bar.view.hl add radiobutton -label $viewname($n) \ + -command [list addhighlight $n] -variable selectedhlview -value $n +} + proc flatten {var} { global $var @@ -1301,14 +1343,15 @@ proc unflatten {var l} { proc showview {n} { global curview viewdata viewfiles global displayorder parentlist childlist rowidlist rowoffsets - global colormap rowtextx commitrow + global colormap rowtextx commitrow nextcolor canvxmax global numcommits rowrangelist commitlisted idrowranges global selectedline currentid canv canvy0 global matchinglines treediffs global pending_select phase - global commitidx rowlaidout rowoptim linesegends leftover + global commitidx rowlaidout rowoptim linesegends global commfd nextupdate - global selectedview + global selectedview hlview selectedhlview + global vparentlist vchildlist vdisporder vcmitlisted if {$n == $curview} return set selid {} @@ -1329,20 +1372,19 @@ proc showview {n} { normalline stopfindproc if {$curview >= 0} { + set vparentlist($curview) $parentlist + set vchildlist($curview) $childlist + set vdisporder($curview) $displayorder + set vcmitlisted($curview) $commitlisted 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 {} + [list $phase $rowidlist $rowoffsets $rowrangelist \ + [flatten idrowranges] [flatten idinlist] \ + $rowlaidout $rowoptim $numcommits $linesegends] } elseif {![info exists viewdata($curview)] || [lindex $viewdata($curview) 0] ne {}} { set viewdata($curview) \ - [list {} $displayorder $parentlist $childlist $rowidlist \ - $rowoffsets $rowrangelist $commitlisted] + [list {} $rowidlist $rowoffsets $rowrangelist] } } catch {unset matchinglines} @@ -1351,8 +1393,11 @@ proc showview {n} { set curview $n set selectedview $n + set selectedhlview -1 + .bar.view entryconf 1 -state [expr {$n == 0? "disabled": "normal"}] .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] - .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}] + catch {unset hlview} + .bar.view.hl entryconf 1 -state disabled if {![info exists viewdata($n)]} { set pending_select $selid @@ -1362,46 +1407,36 @@ proc showview {n} { 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] + set displayorder $vdisporder($n) + set parentlist $vparentlist($n) + set childlist $vchildlist($n) + set commitlisted $vcmitlisted($n) + set rowidlist [lindex $v 1] + set rowoffsets [lindex $v 2] + set rowrangelist [lindex $v 3] if {$phase eq {}} { set numcommits [llength $displayorder] 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}] + unflatten idrowranges [lindex $v 4] + unflatten idinlist [lindex $v 5] + set rowlaidout [lindex $v 6] + set rowoptim [lindex $v 7] + set numcommits [lindex $v 8] + set linesegends [lindex $v 9] } catch {unset colormap} catch {unset rowtextx} - catch {unset commitrow} + set nextcolor 0 + set canvxmax [$canv cget -width] 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) + if {$selid ne {} && [info exists commitrow($n,$selid)]} { + set row $commitrow($n,$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}] @@ -1413,21 +1448,98 @@ 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 + if {$phase ne {}} { if {$phase eq "getcommits"} { global mainfont $canv create text 3 3 -anchor nw -text "Reading commits..." \ -font $mainfont -tags textitems } + if {[info exists commfd($n)]} { + layoutmore + } else { + finishcommits + } } } +proc addhighlight {n} { + global hlview curview viewdata highlighted highlightedrows + global selectedhlview + + if {[info exists hlview]} { + delhighlight + } + set hlview $n + set selectedhlview $n + .bar.view.hl entryconf 1 -state normal + set highlighted($n) 0 + set highlightedrows {} + if {$n != $curview && ![info exists viewdata($n)]} { + set viewdata($n) [list getcommits {{}} {{}} {} {} {} 0 0 0 {}] + set vparentlist($n) {} + set vchildlist($n) {} + set vdisporder($n) {} + set vcmitlisted($n) {} + start_rev_list $n + } else { + highlightmore + } +} + +proc delhighlight {} { + global hlview highlightedrows canv linehtag mainfont + global selectedhlview selectedline + + if {![info exists hlview]} return + unset hlview + set selectedhlview {} + .bar.view.hl entryconf 1 -state disabled + foreach l $highlightedrows { + $canv itemconf $linehtag($l) -font $mainfont + if {$l == $selectedline} { + $canv delete secsel + set t [eval $canv create rect [$canv bbox $linehtag($l)] \ + -outline {{}} -tags secsel \ + -fill [$canv cget -selectbackground]] + $canv lower $t + } + } +} + +proc highlightmore {} { + global hlview highlighted commitidx highlightedrows linehtag mainfont + global displayorder vdisporder curview canv commitrow selectedline + + set font [concat $mainfont bold] + set max $commitidx($hlview) + if {$hlview == $curview} { + set disp $displayorder + } else { + set disp $vdisporder($hlview) + } + for {set i $highlighted($hlview)} {$i < $max} {incr i} { + set id [lindex $disp $i] + if {[info exists commitrow($curview,$id)]} { + set row $commitrow($curview,$id) + if {[info exists linehtag($row)]} { + $canv itemconf $linehtag($row) -font $font + lappend highlightedrows $row + if {$row == $selectedline} { + $canv delete secsel + set t [eval $canv create rect \ + [$canv bbox $linehtag($row)] \ + -outline {{}} -tags secsel \ + -fill [$canv cget -selectbackground]] + $canv lower $t + } + } + } + } + set highlighted($hlview) $max +} + +# Graph layout functions + proc shortids {ids} { set res {} foreach id $ids { @@ -1463,19 +1575,19 @@ proc ntimes {n o} { } proc usedinrange {id l1 l2} { - global children commitrow childlist + global children commitrow childlist curview - if {[info exists commitrow($id)]} { - set r $commitrow($id) + if {[info exists commitrow($curview,$id)]} { + set r $commitrow($curview,$id) if {$l1 <= $r && $r <= $l2} { return [expr {$r - $l1 + 1}] } set kids [lindex $childlist $r] } else { - set kids $children($id) + set kids $children($curview,$id) } foreach c $kids { - set r $commitrow($c) + set r $commitrow($curview,$c) if {$l1 <= $r && $r <= $l2} { return [expr {$r - $l1 + 1}] } @@ -1546,20 +1658,18 @@ proc initlayout {} { global rowidlist rowoffsets displayorder commitlisted global rowlaidout rowoptim global idinlist rowchk rowrangelist idrowranges - global commitidx numcommits canvxmax canv + global numcommits canvxmax canv global nextcolor global parentlist childlist children - global colormap rowtextx commitrow + global colormap rowtextx global linesegends - set commitidx 0 set numcommits 0 set displayorder {} set commitlisted {} set parentlist {} set childlist {} set rowrangelist {} - catch {unset children} set nextcolor 0 set rowidlist {{}} set rowoffsets {{}} @@ -1570,7 +1680,6 @@ proc initlayout {} { set canvxmax [$canv cget -width] catch {unset colormap} catch {unset rowtextx} - catch {unset commitrow} catch {unset idrowranges} set linesegends {} } @@ -1605,10 +1714,10 @@ proc visiblerows {} { proc layoutmore {} { global rowlaidout rowoptim commitidx numcommits optim_delay - global uparrowlen + global uparrowlen curview set row $rowlaidout - set rowlaidout [layoutrows $row $commitidx 0] + set rowlaidout [layoutrows $row $commitidx($curview) 0] set orow [expr {$rowlaidout - $uparrowlen - 1}] if {$orow > $rowoptim} { optimize_rows $rowoptim 0 $orow @@ -1622,7 +1731,7 @@ proc layoutmore {} { proc showstuff {canshow} { global numcommits commitrow pending_select selectedline - global linesegends idrowranges idrangedrawn + global linesegends idrowranges idrangedrawn curview if {$numcommits == 0} { global phase @@ -1657,9 +1766,9 @@ proc showstuff {canshow} { incr row } if {[info exists pending_select] && - [info exists commitrow($pending_select)] && - $commitrow($pending_select) < $numcommits} { - selectline $commitrow($pending_select) 1 + [info exists commitrow($curview,$pending_select)] && + $commitrow($curview,$pending_select) < $numcommits} { + selectline $commitrow($curview,$pending_select) 1 } if {![info exists selectedline] && ![info exists pending_select]} { selectline 0 1 @@ -1671,7 +1780,7 @@ proc layoutrows {row endrow last} { global uparrowlen downarrowlen maxwidth mingaplen global childlist parentlist global idrowranges linesegends - global commitidx + global commitidx curview global idinlist rowchk rowrangelist set idlist [lindex $rowidlist $row] @@ -1691,7 +1800,8 @@ proc layoutrows {row endrow last} { set nev [expr {[llength $idlist] + [llength $newolds] + [llength $oldolds] - $maxwidth + 1}] if {$nev > 0} { - if {!$last && $row + $uparrowlen + $mingaplen >= $commitidx} break + if {!$last && + $row + $uparrowlen + $mingaplen >= $commitidx($curview)} break for {set x [llength $idlist]} {[incr x -1] >= 0} {} { set i [lindex $idlist $x] if {![info exists rowchk($i)] || $row >= $rowchk($i)} { @@ -1780,30 +1890,28 @@ proc layoutrows {row endrow last} { proc addextraid {id row} { global displayorder commitrow commitinfo global commitidx commitlisted - global parentlist childlist children + global parentlist childlist children curview - incr commitidx + incr commitidx($curview) lappend displayorder $id lappend commitlisted 0 lappend parentlist {} - set commitrow($id) $row + set commitrow($curview,$id) $row readcommit $id if {![info exists commitinfo($id)]} { set commitinfo($id) {"No commit information available"} } - if {[info exists children($id)]} { - lappend childlist $children($id) - unset children($id) - } else { - lappend childlist {} + if {![info exists children($curview,$id)]} { + set children($curview,$id) {} } + lappend childlist $children($curview,$id) } proc layouttail {} { - global rowidlist rowoffsets idinlist commitidx + global rowidlist rowoffsets idinlist commitidx curview global idrowranges rowrangelist - set row $commitidx + set row $commitidx($curview) set idlist [lindex $rowidlist $row] while {$idlist ne {}} { set col [expr {[llength $idlist] - 1}] @@ -1978,12 +2086,13 @@ proc linewidth {id} { } proc rowranges {id} { - global phase idrowranges commitrow rowlaidout rowrangelist + global phase idrowranges commitrow rowlaidout rowrangelist curview set ranges {} if {$phase eq {} || - ([info exists commitrow($id)] && $commitrow($id) < $rowlaidout)} { - set ranges [lindex $rowrangelist $commitrow($id)] + ([info exists commitrow($curview,$id)] + && $commitrow($curview,$id) < $rowlaidout)} { + set ranges [lindex $rowrangelist $commitrow($curview,$id)] } elseif {[info exists idrowranges($id)]} { set ranges $idrowranges($id) } @@ -1994,11 +2103,12 @@ proc drawlineseg {id i} { global rowoffsets rowidlist global displayorder global canv colormap linespc - global numcommits commitrow + global numcommits commitrow curview set ranges [rowranges $id] set downarrow 1 - if {[info exists commitrow($id)] && $commitrow($id) < $numcommits} { + if {[info exists commitrow($curview,$id)] + && $commitrow($curview,$id) < $numcommits} { set downarrow [expr {$i < [llength $ranges] / 2 - 1}] } else { set downarrow 1 @@ -2122,7 +2232,7 @@ proc drawparentlinks {id row col olds} { proc drawlines {id} { global colormap canv global idrangedrawn - global childlist iddrawn commitrow rowidlist + global children iddrawn commitrow rowidlist curview $canv delete lines.$id set nr [expr {[llength [rowranges $id]] / 2}] @@ -2131,9 +2241,9 @@ proc drawlines {id} { drawlineseg $id $i } } - foreach child [lindex $childlist $commitrow($id)] { + foreach child $children($curview,$id) { if {[info exists iddrawn($child)]} { - set row $commitrow($child) + set row $commitrow($curview,$child) set col [lsearch -exact [lindex $rowidlist $row] $child] if {$col >= 0} { drawparentlinks $child $row $col [list $id] @@ -2147,7 +2257,8 @@ proc drawcmittext {id row col rmx} { global commitlisted commitinfo rowidlist global rowtextx idpos idtags idheads idotherrefs global linehtag linentag linedtag - global mainfont namefont canvxmax + global mainfont canvxmax + global hlview commitrow highlightedrows set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}] set x [xc $row $col] @@ -2172,11 +2283,16 @@ proc drawcmittext {id row col rmx} { set name [lindex $commitinfo($id) 1] set date [lindex $commitinfo($id) 2] set date [formatdate $date] + set font $mainfont + if {[info exists hlview] && [info exists commitrow($hlview,$id)]} { + lappend font bold + lappend highlightedrows $row + } set linehtag($row) [$canv create text $xt $y -anchor w \ - -text $headline -font $mainfont ] + -text $headline -font $font] $canv bind $linehtag($row) "rowmenu %X %Y $id" set linentag($row) [$canv2 create text 3 $y -anchor w \ - -text $name -font $namefont] + -text $name -font $mainfont] set linedtag($row) [$canv3 create text 3 $y -anchor w \ -text $date -font $mainfont] set xr [expr {$xt + [font measure $mainfont $headline]}] @@ -2307,21 +2423,19 @@ proc findcrossings {id} { proc assigncolor {id} { global colormap colors nextcolor - global commitrow parentlist children childlist + global commitrow parentlist children children curview if {[info exists colormap($id)]} return set ncolors [llength $colors] - if {[info exists commitrow($id)]} { - set kids [lindex $childlist $commitrow($id)] - } elseif {[info exists children($id)]} { - set kids $children($id) + if {[info exists children($curview,$id)]} { + set kids $children($curview,$id) } else { set kids {} } if {[llength $kids] == 1} { set child [lindex $kids 0] if {[info exists colormap($child)] - && [llength [lindex $parentlist $commitrow($child)]] == 1} { + && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} { set colormap($id) $colormap($child) return } @@ -2349,7 +2463,7 @@ proc assigncolor {id} { && [lsearch -exact $badcolors $colormap($child)] < 0} { lappend badcolors $colormap($child) } - foreach p [lindex $parentlist $commitrow($child)] { + foreach p [lindex $parentlist $commitrow($curview,$child)] { if {[info exists colormap($p)] && [lsearch -exact $badcolors $colormap($p)] < 0} { lappend badcolors $colormap($p) @@ -2382,7 +2496,7 @@ proc bindline {t id} { proc drawtags {id x xt y1} { global idtags idheads idotherrefs global linespc lthickness - global canv mainfont commitrow rowtextx + global canv mainfont commitrow rowtextx curview set marks {} set ntags 0 @@ -2425,7 +2539,7 @@ proc drawtags {id x xt y1} { $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \ -width 1 -outline black -fill yellow -tags tag.$id] $canv bind $t <1> [list showtag $tag 1] - set rowtextx($commitrow($id)) [expr {$xr + $linespc}] + set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}] } else { # draw a head or other ref if {[incr nheads -1] >= 0} { @@ -2467,21 +2581,17 @@ proc xcoord {i level ln} { } proc finishcommits {} { - global commitidx phase + global commitidx phase curview global canv mainfont ctext maincursor textcursor global findinprogress pending_select - if {$commitidx > 0} { + if {$commitidx($curview) > 0} { drawrest } else { $canv delete all $canv create text 3 3 -anchor nw -text "No commits selected" \ -font $mainfont -tags textitems } - if {![info exists findinprogress]} { - . config -cursor $maincursor - settextcursor $textcursor - } set phase {} catch {unset pending_select} } @@ -2497,18 +2607,38 @@ proc settextcursor {c} { set curtextcursor $c } +proc nowbusy {what} { + global isbusy + + if {[array names isbusy] eq {}} { + . config -cursor watch + settextcursor watch + } + set isbusy($what) 1 +} + +proc notbusy {what} { + global isbusy maincursor textcursor + + catch {unset isbusy($what)} + if {[array names isbusy] eq {}} { + . config -cursor $maincursor + settextcursor $textcursor + } +} + proc drawrest {} { global numcommits global startmsecs global canvy0 numcommits linespc - global rowlaidout commitidx + global rowlaidout commitidx curview global pending_select set row $rowlaidout - layoutrows $rowlaidout $commitidx 1 + layoutrows $rowlaidout $commitidx($curview) 1 layouttail - optimize_rows $row 0 $commitidx - showstuff $commitidx + optimize_rows $row 0 $commitidx($curview) + showstuff $commitidx($curview) if {[info exists pending_select]} { selectline 0 1 } @@ -2540,7 +2670,7 @@ proc findmatches {f} { proc dofind {} { global findtype findloc findstring markedmatches commitinfo global numcommits displayorder linehtag linentag linedtag - global mainfont namefont canv canv2 canv3 selectedline + global mainfont canv canv2 canv3 selectedline global matchinglines foundstring foundstrlen matchstring global commitdata @@ -2601,7 +2731,7 @@ proc dofind {} { markmatches $canv $l $f $linehtag($l) $matches $mainfont } elseif {$ty == "Author"} { drawcmitrow $l - markmatches $canv2 $l $f $linentag($l) $matches $namefont + markmatches $canv2 $l $f $linentag($l) $matches $mainfont } elseif {$ty == "Date"} { drawcmitrow $l markmatches $canv3 $l $f $linedtag($l) $matches $mainfont @@ -2699,13 +2829,8 @@ proc stopfindproc {{done 0}} { catch {close $findprocfile} unset findprocpid } - if {[info exists findinprogress]} { - unset findinprogress - if {$phase eq {}} { - . config -cursor $maincursor - settextcursor $textcursor - } - } + catch {unset findinprogress} + notbusy find } proc findpatches {} { @@ -2745,14 +2870,13 @@ proc findpatches {} { fconfigure $f -blocking 0 fileevent $f readable readfindproc set finddidsel 0 - . config -cursor watch - settextcursor watch + nowbusy find set findinprogress 1 } proc readfindproc {} { global findprocfile finddidsel - global commitrow matchinglines findinsertpos + global commitrow matchinglines findinsertpos curview set n [gets $findprocfile line] if {$n < 0} { @@ -2769,11 +2893,11 @@ proc readfindproc {} { stopfindproc return } - if {![info exists commitrow($id)]} { + if {![info exists commitrow($curview,$id)]} { puts stderr "spurious id: $id" return } - set l $commitrow($id) + set l $commitrow($curview,$id) insertmatch $l $id } @@ -2847,8 +2971,7 @@ proc findfiles {} { set finddidsel 0 set findinsertpos end set id [lindex $displayorder $l] - . config -cursor watch - settextcursor watch + nowbusy find set findinprogress 1 findcont update @@ -3018,7 +3141,7 @@ proc commit_descriptor {p} { # append some text to the ctext widget, and make any SHA1 ID # that we know about be a clickable link. proc appendwithlinks {text} { - global ctext commitrow linknum + global ctext commitrow linknum curview set start [$ctext index "end - 1c"] $ctext insert end $text @@ -3028,11 +3151,12 @@ proc appendwithlinks {text} { set s [lindex $l 0] set e [lindex $l 1] set linkid [string range $text $s $e] - if {![info exists commitrow($linkid)]} continue + if {![info exists commitrow($curview,$linkid)]} continue incr e $ctext tag add link "$start + $s c" "$start + $e c" $ctext tag add link$linknum "$start + $s c" "$start + $e c" - $ctext tag bind link$linknum <1> [list selectline $commitrow($linkid) 1] + $ctext tag bind link$linknum <1> \ + [list selectline $commitrow($curview,$linkid) 1] incr linknum } $ctext tag conf link -foreground blue -underline 1 @@ -3686,11 +3810,10 @@ proc redisplay {} { } proc incrfont {inc} { - global mainfont namefont textfont ctext canv phase + global mainfont textfont ctext canv phase global stopped entries unmarkmatches set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]] - set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]] set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]] setcoords $ctext conf -font $textfont @@ -3729,7 +3852,7 @@ proc sha1change {n1 n2 op} { proc gotocommit {} { global sha1string currentid commitrow tagids headids - global displayorder numcommits + global displayorder numcommits curview if {$sha1string == {} || ([info exists currentid] && $sha1string == $currentid)} return @@ -3755,8 +3878,8 @@ proc gotocommit {} { } } } - if {[info exists commitrow($id)]} { - selectline $commitrow($id) 1 + if {[info exists commitrow($curview,$id)]} { + selectline $commitrow($curview,$id) 1 return } if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} { @@ -3864,7 +3987,7 @@ proc arrowjump {id n y} { } proc lineclick {x y id isnew} { - global ctext commitinfo childlist commitrow canv thickerline + global ctext commitinfo children canv thickerline curview if {![info exists commitinfo($id)] && ![getcommit $id]} return unmarkmatches @@ -3903,7 +4026,7 @@ proc lineclick {x y id isnew} { $ctext insert end "\tAuthor:\t[lindex $info 1]\n" set date [formatdate [lindex $info 2]] $ctext insert end "\tDate:\t$date\n" - set kids [lindex $childlist $commitrow($id)] + set kids $children($curview,$id) if {$kids ne {}} { $ctext insert end "\nChildren:" set i 0 @@ -3934,9 +4057,9 @@ proc normalline {} { } proc selbyid {id} { - global commitrow - if {[info exists commitrow($id)]} { - selectline $commitrow($id) 1 + global commitrow curview + if {[info exists commitrow($curview,$id)]} { + selectline $commitrow($curview,$id) 1 } } @@ -3949,9 +4072,10 @@ proc mstime {} { } proc rowmenu {x y id} { - global rowctxmenu commitrow selectedline rowmenuid + global rowctxmenu commitrow selectedline rowmenuid curview - if {![info exists selectedline] || $commitrow($id) eq $selectedline} { + if {![info exists selectedline] + || $commitrow($curview,$id) eq $selectedline} { set state disabled } else { set state normal @@ -4151,14 +4275,15 @@ proc domktag {} { } proc redrawtags {id} { - global canv linehtag commitrow idpos selectedline + global canv linehtag commitrow idpos selectedline curview - if {![info exists commitrow($id)]} return - drawcmitrow $commitrow($id) + if {![info exists commitrow($curview,$id)]} return + drawcmitrow $commitrow($curview,$id) $canv delete tag.$id set xt [eval drawtags $id $idpos($id)] - $canv coords $linehtag($commitrow($id)) $xt [lindex $idpos($id) 2] - if {[info exists selectedline] && $selectedline == $commitrow($id)} { + $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2] + if {[info exists selectedline] + && $selectedline == $commitrow($curview,$id)} { selectline $selectedline 0 } } @@ -4674,8 +4799,6 @@ set colors {green red blue magenta darkgrey brown orange} catch {source ~/.gitk} -set namefont $mainfont - font create optionfont -family sans-serif -size -12 set revtreeargs {} @@ -4704,6 +4827,7 @@ set optim_delay 16 set nextviewnum 1 set curview 0 set selectedview 0 +set selectedhlview {} set viewfiles(0) {} set viewperm(0) 0 @@ -4733,10 +4857,9 @@ if {$cmdline_files ne {}} { set viewname(1) "Command line" set viewfiles(1) $cmdline_files set viewperm(1) 0 - .bar.view add radiobutton -label $viewname(1) -command {showview 1} \ - -variable selectedview -value 1 + addviewmenu 1 + .bar.view entryconf 1 -state normal .bar.view entryconf 2 -state normal - .bar.view entryconf 3 -state normal } if {[info exists permviews]} { @@ -4746,8 +4869,7 @@ if {[info exists permviews]} { 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 + addviewmenu $n } } getcommits -- cgit v0.10.2-6-g49f6 From 468eb79ed453017151c1230057986a7a79264395 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 23:05:39 -0700 Subject: builtin-count-objects: make it official. Remove the shell-script version, make the hardlink from the git binary, and update the documentation to describe a new option. Signed-off-by: Junio C Hamano diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt index 47216f4..198ce77 100644 --- a/Documentation/git-count-objects.txt +++ b/Documentation/git-count-objects.txt @@ -7,13 +7,23 @@ git-count-objects - Reports on unpacked objects SYNOPSIS -------- -'git-count-objects' +'git-count-objects' [-v] DESCRIPTION ----------- This counts the number of unpacked object files and disk space consumed by them, to help you decide when it is a good time to repack. + +OPTIONS +------- +-v:: + In addition to the number of loose objects and disk + space consumed, it reports the number of in-pack + objects, and number of objects that can be removed by + running `git-prune-packed`. + + Author ------ Written by Junio C Hamano diff --git a/Makefile b/Makefile index 14193aa..f1592fb 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ SCRIPT_SH = \ git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ - git-count-objects.sh git-diff.sh git-fetch.sh \ + git-diff.sh git-fetch.sh \ git-format-patch.sh git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ @@ -167,7 +167,8 @@ PROGRAMS = \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X -BUILT_INS = git-log$X +BUILT_INS = git-log$X \ + git-count-objects$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-count-objects.sh b/git-count-objects.sh deleted file mode 100755 index 40c58ef..0000000 --- a/git-count-objects.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Junio C Hamano -# - -GIT_DIR=`git-rev-parse --git-dir` || exit $? - -dc /dev/null || { - # This is not a real DC at all -- it just knows how - # this script feeds DC and does the computation itself. - dc () { - while read a b - do - case $a,$b in - 0,) acc=0 ;; - *,+) acc=$(($acc + $a)) ;; - p,) echo "$acc" ;; - esac - done - } -} - -echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \ -$({ - echo 0 - # "no-such" is to help Darwin folks by not using xargs -r. - find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | - xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null | - sed -e 's/[ ].*/ +/' - echo p -} | dc) kilobytes -- cgit v0.10.2-6-g49f6 From 8ab99476ec2298a43e94949e0bb7aa5125793a28 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 22:58:18 -0700 Subject: builtin-diff: call it "git-diff", really. Call it "git diff" not "git diffn", remove the shell script version, and hardlink the git binary to it. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 277c1ac..47b165e 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ SCRIPT_SH = \ git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \ git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \ - git-count-objects.sh git-diff.sh git-fetch.sh \ + git-count-objects.sh git-fetch.sh \ git-format-patch.sh git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ @@ -167,7 +167,8 @@ PROGRAMS = \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X -BUILT_INS = git-log$X +BUILT_INS = git-log$X \ + git-diff$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) diff --git a/git-diff.sh b/git-diff.sh deleted file mode 100755 index 0fe6770..0000000 --- a/git-diff.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Linus Torvalds -# Copyright (c) 2005 Junio C Hamano - -USAGE='[ --diff-options ] {0,2} [...]' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit -flags=$(git-rev-parse --no-revs --flags --sq "$@") -files=$(git-rev-parse --no-revs --no-flags --sq "$@") - -# I often say 'git diff --cached -p' and get scolded by git-diff-files, but -# obviously I mean 'git diff --cached -p HEAD' in that case. -case "$rev" in -'') - case " $flags " in - *" '--cached' "*) - rev='HEAD ' - ;; - esac -esac - -# If we have -[123] --ours --theirs --base, don't do --cc by default. -case " $flags " in -*" '-"[123]"' "* | *" '--ours' "* | *" '--base' "* | *" '--theirs' "*) - cc_or_p=-p ;; -*) - cc_or_p=--cc ;; -esac - -# If we do not have --name-status, --name-only, -r, -c or --stat, -# default to --cc. -case " $flags " in -*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \ -*" '--stat' "*) - ;; -*) - flags="$flags'$cc_or_p' " ;; -esac - -# If we do not have -B, -C, -r, nor -p, default to -M. -case " $flags " in -*" '-"[BCMrp]* | *" '--find-copies-harder' "*) - ;; # something like -M50. -*) - flags="$flags'-M' " ;; -esac - -case "$rev" in -?*' '?*' '?*) - usage - ;; -?*' '^?*) - begin=$(expr "$rev" : '.*^.\([0-9a-f]*\).*') && - end=$(expr "$rev" : '.\([0-9a-f]*\). .*') || exit - cmd="git-diff-tree $flags $begin $end -- $files" - ;; -?*' '?*) - cmd="git-diff-tree $flags $rev -- $files" - ;; -?*' ') - cmd="git-diff-index $flags $rev -- $files" - ;; -'') - cmd="git-diff-files $flags -- $files" - ;; -*) - usage - ;; -esac - -eval "$cmd" diff --git a/git.c b/git.c index ff9b87a..e6a960a 100644 --- a/git.c +++ b/git.c @@ -46,7 +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 }, + { "diff", cmd_diff }, }; int i; -- cgit v0.10.2-6-g49f6 From 7b763f7c6536f40090b06c79896a147ba97b0b03 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 23:14:58 -0700 Subject: builtin-log/whatchanged/show: make them official. Remove the shell script version, and hardlink the git binary to them. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 8ce27a6..5d19820 100644 --- a/Makefile +++ b/Makefile @@ -121,7 +121,7 @@ SCRIPT_SH = \ git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \ - git-tag.sh git-verify-tag.sh git-whatchanged.sh \ + git-tag.sh git-verify-tag.sh \ git-applymbox.sh git-applypatch.sh git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh git-grep.sh \ @@ -139,7 +139,7 @@ SCRIPT_PYTHON = \ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ $(patsubst %.perl,%,$(SCRIPT_PERL)) \ $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ - git-cherry-pick git-show git-status + git-cherry-pick git-status # The ones that do not have to link with lcrypto, lz nor xdiff. SIMPLE_PROGRAMS = \ @@ -167,7 +167,7 @@ PROGRAMS = \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X -BUILT_INS = git-log$X +BUILT_INS = git-log$X git-whatchanged$X git-show$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) @@ -505,9 +505,6 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py git-cherry-pick: git-revert cp $< $@ -git-show: git-whatchanged - cp $< $@ - git-status: git-commit cp $< $@ diff --git a/git-whatchanged.sh b/git-whatchanged.sh deleted file mode 100755 index 1fb9feb..0000000 --- a/git-whatchanged.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -USAGE='[-p] [--max-count=] [..] [--pretty=] [-m] [git-diff-tree options] [git-rev-list options]' -SUBDIRECTORY_OK='Yes' -. git-sh-setup - -diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit -case "$0" in -*whatchanged) - count= - test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get whatchanged.difftree) - diff_tree_default_flags='-c -M --abbrev' ;; -*show) - count=-n1 - test -z "$diff_tree_flags" && - diff_tree_flags=$(git-repo-config --get show.difftree) - diff_tree_default_flags='--cc --always' ;; -esac -test -z "$diff_tree_flags" && - diff_tree_flags="$diff_tree_default_flags" - -rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") && -diff_tree_args=$(git-rev-parse --sq --no-revs --no-flags "$@") && - -eval "git-rev-list $count $rev_list_args" | -eval "git-diff-tree --stdin --pretty -r $diff_tree_flags $diff_tree_args" | -LESS="$LESS -S" ${PAGER:-less} -- cgit v0.10.2-6-g49f6 From c24fe420d3e2af6e5faabc6ab7f68c18f939582d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 17:12:26 -0700 Subject: show-branch: omit uninteresting merges. Signed-off-by: Junio C Hamano diff --git a/show-branch.c b/show-branch.c index 24efb65..268c57b 100644 --- a/show-branch.c +++ b/show-branch.c @@ -5,7 +5,7 @@ #include "refs.h" static const char show_branch_usage[] = -"git-show-branch [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...]"; +"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [...]"; static int default_num = 0; static int default_alloc = 0; @@ -527,6 +527,27 @@ static int git_show_branch_config(const char *var, const char *value) return git_default_config(var, value); } +static int omit_in_dense(struct commit *commit, struct commit **rev, int n) +{ + /* If the commit is tip of the named branches, do not + * omit it. + * Otherwise, if it is a merge that is reachable from only one + * tip, it is not that interesting. + */ + int i, flag, count; + for (i = 0; i < n; i++) + if (rev[i] == commit) + return 0; + flag = commit->object.flags; + for (i = count = 0; i < n; i++) { + if (flag & (1u << (i + REV_SHIFT))) + count++; + } + if (count == 1) + return 1; + return 0; +} + int main(int ac, char **av) { struct commit *rev[MAX_REVS], *commit; @@ -548,6 +569,7 @@ int main(int ac, char **av) int with_current_branch = 0; int head_at = -1; int topics = 0; + int dense = 1; setup_git_directory(); git_config(git_show_branch_config); @@ -590,6 +612,8 @@ int main(int ac, char **av) lifo = 1; else if (!strcmp(arg, "--topics")) topics = 1; + else if (!strcmp(arg, "--sparse")) + dense = 0; else if (!strcmp(arg, "--date-order")) lifo = 0; else @@ -732,12 +756,15 @@ int main(int ac, char **av) shown_merge_point |= is_merge_point; if (1 < num_rev) { - int is_merge = !!(commit->parents && commit->parents->next); + int is_merge = !!(commit->parents && + commit->parents->next); if (topics && !is_merge_point && (this_flag & (1u << REV_SHIFT))) continue; - + if (dense && is_merge && + omit_in_dense(commit, rev, num_rev)) + continue; for (i = 0; i < num_rev; i++) { int mark; if (!(this_flag & (1u << (i + REV_SHIFT)))) -- cgit v0.10.2-6-g49f6 From 54eb2d3ff91bf7a914b6e1de9cf8f748d6a9ecb4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 23:40:37 -0700 Subject: builtin-push: make it official. Remove the shell script version, and hardlink the git binary to it. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 9919992..9cb40a8 100644 --- a/Makefile +++ b/Makefile @@ -118,7 +118,7 @@ SCRIPT_SH = \ git-count-objects.sh git-diff.sh git-fetch.sh \ git-format-patch.sh git-ls-remote.sh \ git-merge-one-file.sh git-parse-remote.sh \ - git-prune.sh git-pull.sh git-push.sh git-rebase.sh \ + git-prune.sh git-pull.sh git-rebase.sh \ git-repack.sh git-request-pull.sh git-reset.sh \ git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \ git-tag.sh git-verify-tag.sh git-whatchanged.sh \ @@ -167,7 +167,8 @@ PROGRAMS = \ git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \ git-describe$X git-merge-tree$X git-blame$X git-imap-send$X -BUILT_INS = git-log$X +BUILT_INS = git-log$X \ + git-push$X # what 'all' will build and 'install' will install, in gitexecdir ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) -- cgit v0.10.2-6-g49f6 From 97d4df0b29d262df291985aed486e226b43132d0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Apr 2006 15:23:48 +0200 Subject: builtin-push: also ask config for remote information Now you can store your remote information in the config file like this: [remote.upstream] url = me@company.com:the-project push = master:iceballs [jc: fixed up to adjust a different fix for Push: lines earlier.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-push.c b/builtin-push.c index 9a861b5..06d06ff 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -115,6 +115,41 @@ static int get_remotes_uri(const char *repo, const char *uri[MAX_URI]) return n; } +static const char **config_uri; +static const char *config_repo; +static int config_repo_len; +static int config_current_uri; +static int config_get_refspecs; + +static int get_remote_config(const char* key, const char* value) +{ + if (!strncmp(key, "remote.", 7) && + !strncmp(key + 7, config_repo, config_repo_len)) { + if (!strcmp(key + 7 + config_repo_len, ".url")) { + if (config_current_uri < MAX_URI) + config_uri[config_current_uri++] = strdup(value); + else + error("more than %d URL's specified, ignoring the rest", MAX_URI); + } + else if (config_get_refspecs && + !strcmp(key + 7 + config_repo_len, ".push")) + add_refspec(strdup(value)); + } + return 0; +} + +static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI]) +{ + config_repo_len = strlen(repo); + config_repo = repo; + config_current_uri = 0; + config_uri = uri; + config_get_refspecs = !refspec_nr; + + git_config(get_remote_config); + return config_current_uri; +} + static int get_branches_uri(const char *repo, const char *uri[MAX_URI]) { const char *slash = strchr(repo, '/'); @@ -162,6 +197,10 @@ static int read_config(const char *repo, const char *uri[MAX_URI]) if (n > 0) return n; + n = get_config_remotes_uri(repo, uri); + if (n > 0) + return n; + n = get_branches_uri(repo, uri); if (n > 0) return n; -- cgit v0.10.2-6-g49f6 From 5a223a0d434c874984a0251eca4520ef95718a6d Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 30 Apr 2006 15:24:03 +0200 Subject: fetch, pull: ask config for remote information Now you can say [remote.junio] url = git://git.kernel.org/pub/scm/git/git.git pull = next:next in your .git/config. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/git-parse-remote.sh b/git-parse-remote.sh index c9b899e..8ce57c8 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -4,13 +4,43 @@ # this would fail in that case and would issue an error message. GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :; +if [ -d "$GIT_DIR"/remotes -a "$GIT_REWRITE_REMOTES" = true ]; then + echo "Rewriting $GIT_DIR/remotes" >&2 + error=0 + # rewrite into config + { + cd "$GIT_DIR"/remotes + ls | while read f; do + name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".") + sed -n \ + -e "s/^URL: /remote.$name.url . /p" \ + -e "s/^Pull: /remote.$name.pull ^$ /p" \ + -e "s/^Push: /remote.$name.push ^$ /p" \ + < "$f" + done + echo done + } | while read key regex value; do + case $key in + done) + if [ $error = 0 ]; then + mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old + fi ;; + *) + git-repo-config $key "$value" $regex || error=1 ;; + esac + done +fi + get_data_source () { case "$1" in */*) # Not so fast. This could be the partial URL shorthand... token=$(expr "z$1" : 'z\([^/]*\)/') remainder=$(expr "z$1" : 'z[^/]*/\(.*\)') - if test -f "$GIT_DIR/branches/$token" + if test "$(git-repo-config --get "remote.$token.url")" + then + echo config-partial + elif test -f "$GIT_DIR/branches/$token" then echo branches-partial else @@ -18,7 +48,10 @@ get_data_source () { fi ;; *) - if test -f "$GIT_DIR/remotes/$1" + if test "$(git-repo-config --get "remote.$1.url")" + then + echo config + elif test -f "$GIT_DIR/remotes/$1" then echo remotes elif test -f "$GIT_DIR/branches/$1" @@ -35,6 +68,15 @@ get_remote_url () { case "$data_source" in '') echo "$1" ;; + config-partial) + token=$(expr "z$1" : '\([^/]*\)/') + remainder=$(expr "z$1" : '[^/]*/\(.*\)') + url=$(git-repo-config --get "remote.$token.url") + echo "$url/$remainder" + ;; + config) + git-repo-config --get "remote.$1.url" + ;; remotes) sed -ne '/^URL: */{ s///p @@ -56,8 +98,10 @@ get_remote_url () { get_remote_default_refs_for_push () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches | branches-partial) + '' | config-partial | branches | branches-partial) ;; # no default push mapping, just send matching refs. + config) + git-repo-config --get-all "remote.$1.push" ;; remotes) sed -ne '/^Push: */{ s///p @@ -111,8 +155,11 @@ canon_refs_list_for_fetch () { get_remote_default_refs_for_fetch () { data_source=$(get_data_source "$1") case "$data_source" in - '' | branches-partial) + '' | config-partial | branches-partial) echo "HEAD:" ;; + config) + canon_refs_list_for_fetch \ + $(git-repo-config --get-all "remote.$1.pull") ;; branches) remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1") case "$remote_branch" in '') remote_branch=master ;; esac -- cgit v0.10.2-6-g49f6 From 89b11d3ba5132e44cc04826c297a34e31788d0ab Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 2 May 2006 19:55:31 +1000 Subject: gitk: Fix file list display when files are renamed The conversion of the file list to use a text widget assumed incorrectly that the list of files from git-diff-tree -r would correspond 1-1 with the diff sections in the output of git-diff-tree -r -p -C, which is not true when renames are detected. This fixes it by keeping the elements in the difffilestart list in the order they appear in the file list window. Since this means that the elements of difffilestart are no longer necessarily in ascending order, it's somewhat hard to do the dynamic highlighting in the file list as the diff window is scrolled, so I have taken that out for now. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index a83a754..28f8233 100755 --- a/gitk +++ b/gitk @@ -512,7 +512,7 @@ proc makewindow {} { set ctext .ctop.cdet.left.ctext text $ctext -bg white -state disabled -font $textfont \ -width $geometry(ctextw) -height $geometry(ctexth) \ - -yscrollcommand scrolltext -wrap none + -yscrollcommand {.ctop.cdet.left.sb set} -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 @@ -562,7 +562,8 @@ proc makewindow {} { 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 + $cflist tag configure highlight \ + -background [$cflist cget -selectbackground] .ctop.cdet add .ctop.cdet.right bind .ctop.cdet {resizecdetpanes %W %w} @@ -1092,14 +1093,13 @@ image create bitmap tri-dn -background black -foreground blue -data { } proc init_flist {first} { - global cflist cflist_top cflist_bot selectedline difffilestart + global cflist cflist_top 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} @@ -1126,61 +1126,14 @@ proc sel_flist {w x y} { if {$cmitmode eq "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 } 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 @@ -3561,8 +3514,6 @@ proc getmergediffline {mdf id np} { # start of a new file $ctext insert end "\n" set here [$ctext index "end - 1c"] - $ctext mark set f:$fname $here - $ctext mark gravity f:$fname left lappend difffilestart $here add_flist [list $fname] set l [expr {(78 - [string length $fname]) / 2}] @@ -3693,6 +3644,19 @@ proc getblobdiffs {ids} { set nextupdate [expr {[clock clicks -milliseconds] + 100}] } +proc setinlist {var i val} { + global $var + + while {[llength [set $var]] < $i} { + lappend $var {} + } + if {[llength [set $var]] == $i} { + lappend $var $val + } else { + lset $var $i $val + } +} + proc getblobdiffline {bdf ids} { global diffids blobdifffd ctext curdifftag curtagstart global diffnexthead diffnextnote difffilestart @@ -3719,12 +3683,15 @@ proc getblobdiffline {bdf ids} { set here [$ctext index "end - 1c"] set curtagstart $here set header $newname - lappend difffilestart $here - $ctext mark set f:$fname $here - $ctext mark gravity f:$fname left - if {$newname != $fname} { - $ctext mark set f:$newfname $here - $ctext mark gravity f:$newfname left + set i [lsearch -exact $treediffs($ids) $fname] + if {$i >= 0} { + setinlist difffilestart $i $here + } + if {$newname ne $fname} { + set i [lsearch -exact $treediffs($ids) $newname] + if {$i >= 0} { + setinlist difffilestart $i $here + } } set curdifftag "f:$fname" $ctext tag delete $curdifftag -- cgit v0.10.2-6-g49f6 From ced9456a27197fc038fbc5b5ebad87e55f1920d2 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 2 May 2006 13:32:43 +0100 Subject: Give the user a hint for how to continue in the case that git-am fails because it requires user intervention Give the user a hint for how to continue in the case that git-am fails because it requires user intervention. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano diff --git a/git-am.sh b/git-am.sh index 872145b..507ae4d 100755 --- a/git-am.sh +++ b/git-am.sh @@ -14,6 +14,26 @@ stop_here () { exit 1 } +stop_here_user_resolve () { + cmdline=$(basename $0) + if test '' != "$interactive" + then + cmdline="$cmdline -i" + fi + if test '' != "$threeway" + then + cmdline="$cmdline -3" + fi + if test '.dotest' != "$dotest" + then + cmdline="$cmdline -d=$dotest" + fi + echo "When you have resolved this problem run \"$cmdline --resolved\"." + echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"." + + stop_here $1 +} + go_next () { rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \ "$dotest/patch" "$dotest/info" @@ -374,14 +394,14 @@ do if test '' = "$changed" then echo "No changes - did you forget update-index?" - stop_here $this + stop_here_user_resolve $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 + stop_here_user_resolve $this fi apply_status=0 ;; @@ -407,7 +427,7 @@ do if test $apply_status != 0 then echo Patch failed at $msgnum. - stop_here $this + stop_here_user_resolve $this fi if test -x "$GIT_DIR"/hooks/pre-applypatch -- cgit v0.10.2-6-g49f6 From cfa24e184a67cc36f414d913cf705d31e70a7a1a Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 May 2006 12:54:12 -0700 Subject: repo-config: fix segfault with no argument. An earlier addition of --list feature was carelessly done and caused an invalid access to argv[1] when it was not given. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/repo-config.c b/repo-config.c index fa8aba7..e350630 100644 --- a/repo-config.c +++ b/repo-config.c @@ -102,15 +102,14 @@ int main(int argc, const char **argv) type = T_INT; else if (!strcmp(argv[1], "--bool")) type = T_BOOL; + else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) + return git_config(show_all_config); else break; argc--; 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); -- cgit v0.10.2-6-g49f6 From c1aee1fd8d94da9b3c5d2dc1d4264f7e73a58f80 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 May 2006 16:58:37 +0200 Subject: repo-config: trim white-space before comment Earlier, calling git-repo-config core.hello on a .git/config like this: [core] hello = world ; a comment would yield "world " (i.e. with a trailing space). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c index 4e1f0c2..253c48a 100644 --- a/config.c +++ b/config.c @@ -60,6 +60,12 @@ static char *parse_value(void) space = 1; continue; } + if (!quote) { + if (c == ';' || c == '#') { + comment = 1; + continue; + } + } if (space) { if (len) value[len++] = ' '; @@ -93,12 +99,6 @@ static char *parse_value(void) quote = 1-quote; continue; } - if (!quote) { - if (c == ';' || c == '#') { - comment = 1; - continue; - } - } value[len++] = c; } } -- cgit v0.10.2-6-g49f6 From c9889e40858fa8b9265b58519e7281c07f3632c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 May 2006 14:16:46 -0700 Subject: Revert "fetch, pull: ask config for remote information" This reverts 5a223a0d434c874984a0251eca4520ef95718a6d commit. I asked Johannes to roll an updated version, so let's wait for it. diff --git a/git-parse-remote.sh b/git-parse-remote.sh index 8ce57c8..c9b899e 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -4,43 +4,13 @@ # this would fail in that case and would issue an error message. GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :; -if [ -d "$GIT_DIR"/remotes -a "$GIT_REWRITE_REMOTES" = true ]; then - echo "Rewriting $GIT_DIR/remotes" >&2 - error=0 - # rewrite into config - { - cd "$GIT_DIR"/remotes - ls | while read f; do - name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".") - sed -n \ - -e "s/^URL: /remote.$name.url . /p" \ - -e "s/^Pull: /remote.$name.pull ^$ /p" \ - -e "s/^Push: /remote.$name.push ^$ /p" \ - < "$f" - done - echo done - } | while read key regex value; do - case $key in - done) - if [ $error = 0 ]; then - mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old - fi ;; - *) - git-repo-config $key "$value" $regex || error=1 ;; - esac - done -fi - get_data_source () { case "$1" in */*) # Not so fast. This could be the partial URL shorthand... token=$(expr "z$1" : 'z\([^/]*\)/') remainder=$(expr "z$1" : 'z[^/]*/\(.*\)') - if test "$(git-repo-config --get "remote.$token.url")" - then - echo config-partial - elif test -f "$GIT_DIR/branches/$token" + if test -f "$GIT_DIR/branches/$token" then echo branches-partial else @@ -48,10 +18,7 @@ get_data_source () { fi ;; *) - if test "$(git-repo-config --get "remote.$1.url")" - then - echo config - elif test -f "$GIT_DIR/remotes/$1" + if test -f "$GIT_DIR/remotes/$1" then echo remotes elif test -f "$GIT_DIR/branches/$1" @@ -68,15 +35,6 @@ get_remote_url () { case "$data_source" in '') echo "$1" ;; - config-partial) - token=$(expr "z$1" : '\([^/]*\)/') - remainder=$(expr "z$1" : '[^/]*/\(.*\)') - url=$(git-repo-config --get "remote.$token.url") - echo "$url/$remainder" - ;; - config) - git-repo-config --get "remote.$1.url" - ;; remotes) sed -ne '/^URL: */{ s///p @@ -98,10 +56,8 @@ get_remote_url () { get_remote_default_refs_for_push () { data_source=$(get_data_source "$1") case "$data_source" in - '' | config-partial | branches | branches-partial) + '' | branches | branches-partial) ;; # no default push mapping, just send matching refs. - config) - git-repo-config --get-all "remote.$1.push" ;; remotes) sed -ne '/^Push: */{ s///p @@ -155,11 +111,8 @@ canon_refs_list_for_fetch () { get_remote_default_refs_for_fetch () { data_source=$(get_data_source "$1") case "$data_source" in - '' | config-partial | branches-partial) + '' | branches-partial) echo "HEAD:" ;; - config) - canon_refs_list_for_fetch \ - $(git-repo-config --get-all "remote.$1.pull") ;; branches) remote_branch=$(sed -ne '/#/s/.*#//p' "$GIT_DIR/branches/$1") case "$remote_branch" in '') remote_branch=master ;; esac -- cgit v0.10.2-6-g49f6 From e923effb43fa952f9cb72ffe4c3625fce7655bff Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Wed, 3 May 2006 09:44:36 +1200 Subject: git-send-email: fix version string to be valid perl This makes git-send-email easier to develop and debug, skipping the need to `make git-send-email` every time. Signed-off-by: Junio C Hamano diff --git a/git-send-email.perl b/git-send-email.perl index ecfa347..703dd1f 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -291,6 +291,13 @@ sub send_message my $to = join (",\n\t", @recipients); @recipients = unique_email_list(@recipients,@cc); my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++)); + my $gitversion = '@@GIT_VERSION@@'; + if ($gitversion =~ m/..GIT_VERSION../) { + $gitversion = `git --version`; + chomp $gitversion; + # keep only what's after the last space + $gitversion =~ s/^.* //; + } my $header = "From: $from To: $to @@ -299,7 +306,7 @@ Subject: $subject Reply-To: $from Date: $date Message-Id: $message_id -X-Mailer: git-send-email @@GIT_VERSION@@ +X-Mailer: git-send-email $gitversion "; $header .= "In-Reply-To: $reply_to\n" if $reply_to; -- cgit v0.10.2-6-g49f6 From 098dd8a34b43ea1317527ca1a3354aae9f3704b5 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Wed, 3 May 2006 09:32:53 +1000 Subject: gitk: Allow view to specify arbitrary arguments to git-rev-list The list of arguments to git-rev-list, including arguments that select the range of commits, is now a part of the view specification. If any arguments are given to gitk, they become part of the "Command line" view, and the non-file arguments become the default for any new views created. Getting an error from git-rev-list is no longer fatal; instead the error window pops up, and when you press OK, the main window just shows "No commits selected". The git-rev-list arguments are entered in an entry widget in the view editor window using shell quoting conventions, not Tcl quoting conventions. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index 28f8233..4aa57c0 100755 --- a/gitk +++ b/gitk @@ -19,13 +19,13 @@ proc gitdir {} { proc start_rev_list {view} { global startmsecs nextupdate ncmupdate global commfd leftover tclencoding datemode - global revtreeargs viewfiles commitidx + global viewargs viewfiles commitidx set startmsecs [clock clicks -milliseconds] set nextupdate [expr {$startmsecs + 100}] set ncmupdate 1 set commitidx($view) 0 - set args $revtreeargs + set args $viewargs($view) if {$viewfiles($view) ne {}} { set args [concat $args "--" $viewfiles($view)] } @@ -69,9 +69,7 @@ proc getcommits {} { set phase getcommits initlayout start_rev_list $curview - $canv delete all - $canv create text 3 3 -anchor nw -text "Reading commits..." \ - -font $mainfont -tags textitems + show_status "Reading commits..." } proc getcommitlines {fd view} { @@ -84,26 +82,33 @@ proc getcommitlines {fd view} { set stuff [read $fd] if {$stuff == {}} { if {![eof $fd]} return + global viewname unset commfd($view) + notbusy $view # set it blocking so we wait for the process to terminate fconfigure $fd -blocking 1 - if {![catch {close $fd} err]} { - notbusy $view - if {$view == $curview} { - after idle finishcommits + if {[catch {close $fd} err]} { + set fv {} + if {$view != $curview} { + set fv " for the \"$viewname($view)\" view" } - return + if {[string range $err 0 4] == "usage"} { + set err "Gitk: error reading commits$fv:\ + bad arguments to git-rev-list." + if {$viewname($view) eq "Command line"} { + append err \ + " (Note: arguments to gitk are passed to git-rev-list\ + to allow selection of commits to be displayed.)" + } + } else { + set err "Error reading commits$fv: $err" + } + error_popup $err } - if {[string range $err 0 4] == "usage"} { - set err \ - "Gitk: error reading commits: bad arguments to git-rev-list.\ - (Note: arguments to gitk are passed to git-rev-list\ - to allow selection of commits to be displayed.)" - } else { - set err "Error reading commits: $err" + if {$view == $curview} { + after idle finishcommits } - error_popup $err - exit 1 + return } set start 0 set gotsome 0 @@ -217,7 +222,7 @@ proc readcommit {id} { } proc updatecommits {} { - global viewdata curview revtreeargs phase displayorder + global viewdata curview phase displayorder global children commitrow if {$phase ne {}} { @@ -352,10 +357,7 @@ proc readrefs {} { close $refd } -proc error_popup msg { - set w .error - toplevel $w - wm transient $w . +proc show_error {w msg} { message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 button $w.ok -text OK -command "destroy $w" @@ -365,6 +367,13 @@ proc error_popup msg { tkwait window $w } +proc error_popup msg { + set w .error + toplevel $w + wm transient $w . + show_error $w $msg +} + proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist global textfont mainfont uifont @@ -686,7 +695,7 @@ proc savestuff {w} { global canv canv2 canv3 ctext cflist mainfont textfont uifont global stuffsaved findmergefiles maxgraphpct global maxwidth - global viewname viewfiles viewperm nextviewnum + global viewname viewfiles viewargs viewperm nextviewnum global cmitmode if {$stuffsaved} return @@ -715,7 +724,7 @@ proc savestuff {w} { 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 "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}" } } puts $f "}" @@ -1136,10 +1145,105 @@ proc sel_flist {w x y} { } } +# Functions for adding and removing shell-type quoting + +proc shellquote {str} { + if {![string match "*\['\"\\ \t]*" $str]} { + return $str + } + if {![string match "*\['\"\\]*" $str]} { + return "\"$str\"" + } + if {![string match "*'*" $str]} { + return "'$str'" + } + return "\"[string map {\" \\\" \\ \\\\} $str]\"" +} + +proc shellarglist {l} { + set str {} + foreach a $l { + if {$str ne {}} { + append str " " + } + append str [shellquote $a] + } + return $str +} + +proc shelldequote {str} { + set ret {} + set used -1 + while {1} { + incr used + if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} { + append ret [string range $str $used end] + set used [string length $str] + break + } + set first [lindex $first 0] + set ch [string index $str $first] + if {$first > $used} { + append ret [string range $str $used [expr {$first - 1}]] + set used $first + } + if {$ch eq " " || $ch eq "\t"} break + incr used + if {$ch eq "'"} { + set first [string first "'" $str $used] + if {$first < 0} { + error "unmatched single-quote" + } + append ret [string range $str $used [expr {$first - 1}]] + set used $first + continue + } + if {$ch eq "\\"} { + if {$used >= [string length $str]} { + error "trailing backslash" + } + append ret [string index $str $used] + continue + } + # here ch == "\"" + while {1} { + if {![regexp -start $used -indices "\[\"\\\\]" $str first]} { + error "unmatched double-quote" + } + set first [lindex $first 0] + set ch [string index $str $first] + if {$first > $used} { + append ret [string range $str $used [expr {$first - 1}]] + set used $first + } + if {$ch eq "\""} break + incr used + append ret [string index $str $used] + incr used + } + } + return [list $used $ret] +} + +proc shellsplit {str} { + set l {} + while {1} { + set str [string trimleft $str] + if {$str eq {}} break + set dq [shelldequote $str] + set n [lindex $dq 0] + set word [lindex $dq 1] + set str [string range $str $n end] + lappend l $word + } + return $l +} + # Code to implement multiple views proc newview {ishighlight} { global nextviewnum newviewname newviewperm uifont newishighlight + global newviewargs revtreeargs set newishighlight $ishighlight set top .gitkview @@ -1149,12 +1253,14 @@ proc newview {ishighlight} { } set newviewname($nextviewnum) "View $nextviewnum" set newviewperm($nextviewnum) 0 + set newviewargs($nextviewnum) [shellarglist $revtreeargs] vieweditor $top $nextviewnum "Gitk view definition" } proc editview {} { global curview global viewname viewperm newviewname newviewperm + global viewargs newviewargs set top .gitkvedit-$curview if {[winfo exists $top]} { @@ -1163,6 +1269,7 @@ proc editview {} { } set newviewname($curview) $viewname($curview) set newviewperm($curview) $viewperm($curview) + set newviewargs($curview) [shellarglist $viewargs($curview)] vieweditor $top $curview "Gitk: edit view $viewname($curview)" } @@ -1177,7 +1284,13 @@ proc vieweditor {top n title} { grid $top.nl $top.name -sticky w -pady 5 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) grid $top.perm - -pady 5 -sticky w - message $top.l -aspect 500 -font $uifont \ + message $top.al -aspect 1000 -font $uifont \ + -text "Commits to include (arguments to git-rev-list):" + grid $top.al - -sticky w -pady 5 + entry $top.args -width 50 -textvariable newviewargs($n) \ + -background white + grid $top.args - -sticky ew -padx 5 + message $top.l -aspect 1000 -font $uifont \ -text "Enter files and directories to include, one per line:" grid $top.l - -sticky w text $top.t -width 40 -height 10 -background white @@ -1189,7 +1302,7 @@ proc vieweditor {top n title} { $top.t delete {end - 1c} end $top.t mark set insert 0.0 } - grid $top.t - -sticky w -padx 5 + grid $top.t - -sticky ew -padx 5 frame $top.buts button $top.buts.ok -text "OK" -command [list newviewok $top $n] button $top.buts.can -text "Cancel" -command [list destroy $top] @@ -1211,14 +1324,23 @@ proc doviewmenu {m first cmd op args} { } proc allviewmenus {n op args} { - doviewmenu .bar.view 6 [list showview $n] $op $args + doviewmenu .bar.view 7 [list showview $n] $op $args doviewmenu .bar.view.hl 3 [list addhighlight $n] $op $args } proc newviewok {top n} { global nextviewnum newviewperm newviewname newishighlight global viewname viewfiles viewperm selectedview curview + global viewargs newviewargs + if {[catch { + set newargs [shellsplit $newviewargs($n)] + } err]} { + error_popup "Error in commit selection arguments: $err" + wm raise $top + focus $top + return + } set files {} foreach f [split [$top.t get 0.0 end] "\n"] { set ft [string trim $f] @@ -1232,6 +1354,7 @@ proc newviewok {top n} { set viewname($n) $newviewname($n) set viewperm($n) $newviewperm($n) set viewfiles($n) $files + set viewargs($n) $newargs addviewmenu $n if {!$newishighlight} { after idle showview $n @@ -1245,8 +1368,9 @@ proc newviewok {top n} { set viewname($n) $newviewname($n) allviewmenus $n entryconf -label $viewname($n) } - if {$files ne $viewfiles($n)} { + if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} { set viewfiles($n) $files + set viewargs($n) $newargs if {$curview == $n} { after idle updatecommits } @@ -1347,8 +1471,8 @@ proc showview {n} { set curview $n set selectedview $n set selectedhlview -1 - .bar.view entryconf 1 -state [expr {$n == 0? "disabled": "normal"}] .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}] + .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}] catch {unset hlview} .bar.view.hl entryconf 1 -state disabled @@ -1403,15 +1527,15 @@ proc showview {n} { selectline $row 0 if {$phase ne {}} { if {$phase eq "getcommits"} { - global mainfont - $canv create text 3 3 -anchor nw -text "Reading commits..." \ - -font $mainfont -tags textitems + show_status "Reading commits..." } if {[info exists commfd($n)]} { layoutmore } else { finishcommits } + } elseif {$numcommits == 0} { + show_status "No commits selected" } } @@ -2533,6 +2657,13 @@ proc xcoord {i level ln} { return $x } +proc show_status {msg} { + global canv mainfont + + clear_display + $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems +} + proc finishcommits {} { global commitidx phase curview global canv mainfont ctext maincursor textcursor @@ -2541,9 +2672,7 @@ proc finishcommits {} { if {$commitidx($curview) > 0} { drawrest } else { - $canv delete all - $canv create text 3 3 -anchor nw -text "No commits selected" \ - -font $mainfont -tags textitems + show_status "No commits selected" } set phase {} catch {unset pending_select} @@ -4782,10 +4911,33 @@ foreach arg $argv { # check that we can find a .git directory somewhere... set gitdir [gitdir] if {![file isdirectory $gitdir]} { - error_popup "Cannot find the git directory \"$gitdir\"." + show_error . "Cannot find the git directory \"$gitdir\"." exit 1 } +set cmdline_files {} +set i [lsearch -exact $revtreeargs "--"] +if {$i >= 0} { + set cmdline_files [lrange $revtreeargs [expr {$i + 1}] end] + set revtreeargs [lrange $revtreeargs 0 [expr {$i - 1}]] +} elseif {$revtreeargs ne {}} { + if {[catch { + set f [eval exec git-rev-parse --no-revs --no-flags $revtreeargs] + set cmdline_files [split $f "\n"] + set n [llength $cmdline_files] + set revtreeargs [lrange $revtreeargs 0 end-$n] + } err]} { + # unfortunately we get both stdout and stderr in $err, + # so look for "fatal:". + set i [string first "fatal:" $err] + if {$i > 0} { + set err [string range [expr {$i + 6}] end] + } + show_error . "Bad arguments to gitk:\n$err" + exit 1 + } +} + set history {} set historyindex 0 @@ -4797,7 +4949,9 @@ set selectedview 0 set selectedhlview {} set viewfiles(0) {} set viewperm(0) 0 +set viewargs(0) {} +set cmdlineok 0 set stopped 0 set stuffsaved 0 set patchnum 0 @@ -4805,28 +4959,18 @@ setcoords makewindow readrefs -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 {}} { +if {$cmdline_files ne {} || $revtreeargs 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 + set viewargs(1) $revtreeargs set viewperm(1) 0 addviewmenu 1 - .bar.view entryconf 1 -state normal .bar.view entryconf 2 -state normal + .bar.view entryconf 3 -state normal } if {[info exists permviews]} { @@ -4835,6 +4979,7 @@ if {[info exists permviews]} { incr nextviewnum set viewname($n) [lindex $v 0] set viewfiles($n) [lindex $v 1] + set viewargs($n) [lindex $v 2] set viewperm($n) 1 addviewmenu $n } -- cgit v0.10.2-6-g49f6 From 2fa9a0fb31cbf01e8318a02c3e222d7fd3fd0a83 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 May 2006 14:22:48 +0200 Subject: repo-config: support --get-regexp With --get-regexp, output all key/value pairs where the key matches a regexp. Example: git-repo-config --get-regexp remote.*.url will output something like remote.junio.url git://git.kernel.org/pub/scm/git/git.git remote.gitk.url git://git.kernel.org/pub/scm/gitk/gitk.git Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index 566cfa1..ddcf523 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -49,7 +49,7 @@ OPTIONS --replace-all:: Default behaviour is to replace at most one line. This replaces - all lines matching the key (and optionally the value_regex) + all lines matching the key (and optionally the value_regex). --get:: Get the value for a given key (optionally filtered by a regex @@ -59,6 +59,9 @@ OPTIONS Like get, but does not fail if the number of values for the key is not exactly one. +--get-regexp:: + Like --get-all, but interprets the name as a regular expression. + --unset:: Remove the line matching the key from .git/config. diff --git a/repo-config.c b/repo-config.c index e350630..722153c 100644 --- a/repo-config.c +++ b/repo-config.c @@ -6,7 +6,10 @@ static const char git_config_set_usage[] = static char* key = NULL; static char* value = NULL; +static regex_t* key_regexp = NULL; static regex_t* regexp = NULL; +static int show_keys = 0; +static int use_key_regexp = 0; static int do_all = 0; static int do_not_match = 0; static int seen = 0; @@ -26,16 +29,18 @@ static int show_config(const char* key_, const char* value_) if (value_ == NULL) value_ = ""; - if (!strcmp(key_, key) && + if ((use_key_regexp || !strcmp(key_, key)) && + (!use_key_regexp || + !regexec(key_regexp, key_, 0, NULL, 0)) && (regexp == NULL || (do_not_match ^ !regexec(regexp, value_, 0, NULL, 0)))) { - if (do_all) { - printf("%s\n", value_); - return 0; - } + if (show_keys) + printf("%s ", key_); if (seen > 0) { - fprintf(stderr, "More than one value: %s\n", value); + if (!do_all) + fprintf(stderr, "More than one value: %s\n", + value); free(value); } @@ -50,6 +55,8 @@ static int show_config(const char* key_, const char* value_) value = strdup(value_); } seen++; + if (do_all) + printf("%s\n", value); } return 0; } @@ -63,6 +70,14 @@ static int get_value(const char* key_, const char* regex_) key[i] = tolower(key_[i]); key[i] = 0; + if (use_key_regexp) { + key_regexp = (regex_t*)malloc(sizeof(regex_t)); + if (regcomp(key_regexp, key, REG_EXTENDED)) { + fprintf(stderr, "Invalid key pattern: %s\n", regex_); + return -1; + } + } + if (regex_) { if (regex_[0] == '!') { do_not_match = 1; @@ -78,7 +93,8 @@ static int get_value(const char* key_, const char* regex_) git_config(show_config); if (value) { - printf("%s\n", value); + if (!do_all) + printf("%s\n", value); free(value); } free(key); @@ -123,6 +139,11 @@ int main(int argc, const char **argv) else if (!strcmp(argv[1], "--get-all")) { do_all = 1; return get_value(argv[2], NULL); + } else if (!strcmp(argv[1], "--get-regexp")) { + show_keys = 1; + use_key_regexp = 1; + do_all = 1; + return get_value(argv[2], NULL); } else return git_config_set(argv[1], argv[2]); @@ -136,6 +157,11 @@ int main(int argc, const char **argv) else if (!strcmp(argv[1], "--get-all")) { do_all = 1; return get_value(argv[2], argv[3]); + } else if (!strcmp(argv[1], "--get-regexp")) { + show_keys = 1; + use_key_regexp = 1; + do_all = 1; + return get_value(argv[2], argv[3]); } else if (!strcmp(argv[1], "--replace-all")) return git_config_set_multivar(argv[2], argv[3], NULL, 1); diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index ab4dd5c..1bf728f 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -247,6 +247,24 @@ EOF test_expect_success 'hierarchical section value' 'cmp .git/config expect' +cat > expect << EOF +beta.noindent=sillyValue +nextsection.nonewline=wow2 for me +123456.a123=987 +1.2.3.alpha=beta +EOF + +test_expect_success 'working --list' \ + 'git-repo-config --list > output && cmp output expect' + +cat > expect << EOF +beta.noindent sillyValue +nextsection.nonewline wow2 for me +EOF + +test_expect_success '--get-regexp' \ + 'git-repo-config --get-regexp in > output && cmp output expect' + cat > .git/config << EOF [novalue] variable @@ -255,5 +273,10 @@ EOF test_expect_success 'get variable with no value' \ 'git-repo-config --get novalue.variable ^$' +git-repo-config > output 2>&1 + +test_expect_success 'no arguments, but no crash' \ + "test $? = 129 && grep usage output" + test_done -- cgit v0.10.2-6-g49f6 From 9f0bb90d161edf8c43f5261d12bf83f14eb02ff4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 May 2006 00:40:24 -0700 Subject: core.prefersymlinkrefs: use symlinks for .git/HEAD When inspecting a project whose build infrastructure used to assume that .git/HEAD is a symlink ref, core.prefersymlinkrefs in the config file of such a project would help to bisect its history. Signed-off-by: Junio C Hamano diff --git a/Documentation/config.txt b/Documentation/config.txt index b27b0d5..d1a4bec 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -64,9 +64,11 @@ core.ignoreStat:: slow, such as Microsoft Windows. See gitlink:git-update-index[1]. False by default. -core.onlyUseSymrefs:: - Always use the "symref" format instead of symbolic links for HEAD - and other symbolic reference files. True by default. +core.preferSymlinkRefs:: + Instead of the default "symref" format for HEAD + and other symbolic reference files, use symbolic links. + This is sometimes needed to work with old scripts that + expect HEAD to be a symbolic link. core.repositoryFormatVersion:: Internal variable identifying the repository format and layout diff --git a/Makefile b/Makefile index 8ce27a6..cb6bb72 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ all: # # Define NO_SETENV if you don't have setenv in the C library. # -# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link. -# Don't enable it on Windows. +# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. +# Enable it on Windows. By default, symrefs are still used. # # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. @@ -263,6 +263,7 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease + NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease # There are conflicting reports about this. # On some boxes NO_MMAP is needed, and not so elsewhere. @@ -386,6 +387,9 @@ endif ifdef NO_D_INO_IN_DIRENT ALL_CFLAGS += -DNO_D_INO_IN_DIRENT endif +ifdef NO_SYMLINK_HEAD + ALL_CFLAGS += -DNO_SYMLINK_HEAD +endif ifdef NO_STRCASESTR COMPAT_CFLAGS += -DNO_STRCASESTR COMPAT_OBJS += compat/strcasestr.o diff --git a/cache.h b/cache.h index a4f253e..9d0ddcf 100644 --- a/cache.h +++ b/cache.h @@ -169,7 +169,7 @@ extern void rollback_index_file(struct cache_file *); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int assume_unchanged; -extern int only_use_symrefs; +extern int prefer_symlink_refs; extern int warn_ambiguous_refs; extern int diff_rename_limit_default; extern int shared_repository; diff --git a/config.c b/config.c index 253c48a..4773414 100644 --- a/config.c +++ b/config.c @@ -227,8 +227,8 @@ int git_default_config(const char *var, const char *value) return 0; } - if (!strcmp(var, "core.symrefsonly")) { - only_use_symrefs = git_config_bool(var, value); + if (!strcmp(var, "core.prefersymlinkrefs")) { + prefer_symlink_refs = git_config_bool(var, value); return 0; } diff --git a/environment.c b/environment.c index 6df6478..444c99e 100644 --- a/environment.c +++ b/environment.c @@ -13,7 +13,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int trust_executable_bit = 1; int assume_unchanged = 0; -int only_use_symrefs = 0; +int prefer_symlink_refs = 0; int warn_ambiguous_refs = 1; int repository_format_version = 0; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; diff --git a/refs.c b/refs.c index 03398cc..275b914 100644 --- a/refs.c +++ b/refs.c @@ -76,8 +76,8 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master) char ref[1000]; int fd, len, written; -#ifdef USE_SYMLINK_HEAD - if (!only_use_symrefs) { +#ifndef NO_SYMLINK_HEAD + if (prefer_symlink_refs) { unlink(git_HEAD); if (!symlink(refs_heads_master, git_HEAD)) return 0; -- cgit v0.10.2-6-g49f6 From e098c6f82a28efe1488c7eb1bed4e92f76dd2afd Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 May 2006 21:06:56 -0700 Subject: repo-config: readability fixups. Signed-off-by: Junio C Hamano diff --git a/repo-config.c b/repo-config.c index 722153c..7e06d1a 100644 --- a/repo-config.c +++ b/repo-config.c @@ -2,10 +2,9 @@ #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]] | --list"; +"git-repo-config [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list"; static char* key = NULL; -static char* value = NULL; static regex_t* key_regexp = NULL; static regex_t* regexp = NULL; static int show_keys = 0; @@ -26,6 +25,9 @@ static int show_all_config(const char *key_, const char *value_) static int show_config(const char* key_, const char* value_) { + char value[256]; + const char *vptr = value; + if (value_ == NULL) value_ = ""; @@ -35,28 +37,25 @@ static int show_config(const char* key_, const char* value_) (regexp == NULL || (do_not_match ^ !regexec(regexp, value_, 0, NULL, 0)))) { + int dup_error = 0; if (show_keys) printf("%s ", key_); - if (seen > 0) { - if (!do_all) - fprintf(stderr, "More than one value: %s\n", - value); - free(value); - } - - if (type == T_INT) { - value = malloc(256); + if (seen && !do_all) + dup_error = 1; + if (type == T_INT) sprintf(value, "%d", git_config_int(key_, value_)); - } else if (type == T_BOOL) { - value = malloc(256); + else if (type == T_BOOL) sprintf(value, "%s", git_config_bool(key_, value_) ? "true" : "false"); - } else { - value = strdup(value_); - } + else + vptr = value_; seen++; - if (do_all) - printf("%s\n", value); + if (dup_error) { + error("More than one value for the key %s: %s", + key_, vptr); + } + else + printf("%s\n", vptr); } return 0; } @@ -73,7 +72,7 @@ static int get_value(const char* key_, const char* regex_) if (use_key_regexp) { key_regexp = (regex_t*)malloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { - fprintf(stderr, "Invalid key pattern: %s\n", regex_); + fprintf(stderr, "Invalid key pattern: %s\n", key_); return -1; } } @@ -92,11 +91,6 @@ static int get_value(const char* key_, const char* regex_) } git_config(show_config); - if (value) { - if (!do_all) - printf("%s\n", value); - free(value); - } free(key); if (regexp) { regfree(regexp); @@ -104,9 +98,9 @@ static int get_value(const char* key_, const char* regex_) } if (do_all) - return 0; + return !seen; - return seen == 1 ? 0 : 1; + return (seen == 1) ? 0 : 1; } int main(int argc, const char **argv) -- cgit v0.10.2-6-g49f6 From 80fe7d2b54b1aebde6c067694cdbf3ba40ad0c39 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 May 2006 23:03:15 -0700 Subject: builtin-count-objects: open packs when running -v Otherwise we would report absolutely no objects in a fully packed repository. Signed-off-by: Junio C Hamano diff --git a/builtin-count.c b/builtin-count.c index 0256369..5ee72df 100644 --- a/builtin-count.c +++ b/builtin-count.c @@ -105,6 +105,8 @@ int cmd_count_objects(int ac, const char **av, char **ep) } if (verbose) { struct packed_git *p; + if (!packed_git) + prepare_packed_git(); for (p = packed_git; p; p = p->next) { if (!p->pack_local) continue; -- cgit v0.10.2-6-g49f6 From de5f2bf3612a43af074a98fd1fcdef6a403daf3f Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Wed, 3 May 2006 12:51:40 +0200 Subject: fix various typos in documentation Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt index 2169169..906830d 100644 --- a/Documentation/git-diff-tree.txt +++ b/Documentation/git-diff-tree.txt @@ -92,7 +92,7 @@ separated with a single space are given. Furthermore, it lists only files which were modified from all parents. --cc:: +--cc:: This flag changes the way a merge commit patch is displayed, in a similar way to the '-c' option. It implies the '-c' and '-p' options and further compresses the patch output diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index d4137fc..e01b42f 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -10,8 +10,8 @@ SYNOPSIS -------- [verse] 'git-update-index' - [--add] [--remove | --force-remove] [--replace] - [--refresh [-q] [--unmerged] [--ignore-missing]] + [--add] [--remove | --force-remove] [--replace] + [--refresh] [-q] [--unmerged] [--ignore-missing] [--cacheinfo ]\* [--chmod=(+|-)x] [--assume-unchanged | --no-assume-unchanged] diff --git a/revision.c b/revision.c index 03dd238..5f2f0be 100644 --- a/revision.c +++ b/revision.c @@ -537,7 +537,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->max_count = atoi(arg + 12); continue; } - /* accept -, like traditilnal "head" */ + /* accept -, like traditional "head" */ if ((*arg == '-') && isdigit(arg[1])) { revs->max_count = atoi(arg + 1); continue; -- cgit v0.10.2-6-g49f6 From 060729dd7e4e7d366f09d2e13d796f05ec1734a9 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Wed, 3 May 2006 12:53:19 +0200 Subject: add documentation for update-index --unresolve Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index d4137fc..4bdb2b0 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -15,7 +15,7 @@ SYNOPSIS [--cacheinfo ]\* [--chmod=(+|-)x] [--assume-unchanged | --no-assume-unchanged] - [--really-refresh] + [--really-refresh] [--unresolve] [--info-only] [--index-info] [-z] [--stdin] [--verbose] @@ -80,6 +80,10 @@ OPTIONS filesystem that has very slow lstat(2) system call (e.g. cifs). +--unresolve:: + Restores the 'unmerged' or 'needs updating' state of a + file during a merge if it was cleared by accident. + --info-only:: Do not create objects in the object database for all arguments that follow this flag; just insert diff --git a/update-index.c b/update-index.c index facec8d..9fa3d2b 100644 --- a/update-index.c +++ b/update-index.c @@ -473,7 +473,7 @@ static void read_index_info(int line_termination) } static const char update_index_usage[] = -"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--cacheinfo] [--chmod=(+|-)x] [--info-only] [--force-remove] [--stdin] [--index-info] [--ignore-missing] [-z] [--verbose] [--] ..."; +"git-update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--ignore-missing] [-z] [--verbose] [--] ..."; static unsigned char head_sha1[20]; static unsigned char merge_head_sha1[20]; -- cgit v0.10.2-6-g49f6 From c8c893c62b0d9997e7dfe7bd5f636ddbf29ef619 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 3 May 2006 07:59:00 -0700 Subject: Fix "git-log --parents" breakage post v1.3.0 Post 1.3.0 "git log" forgets to list parent commits on the first line when --parents is given. git-cvsserver relied on it. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/log-tree.c b/log-tree.c index 9634c46..b90ba67 100644 --- a/log-tree.c +++ b/log-tree.c @@ -3,6 +3,15 @@ #include "commit.h" #include "log-tree.h" +static void show_parents(struct commit *commit, int abbrev) +{ + struct commit_list *p; + for (p = commit->parents; p ; p = p->next) { + struct commit *parent = p->item; + printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev)); + } +} + void show_log(struct rev_info *opt, struct log_info *log, const char *sep) { static char this_header[16384]; @@ -14,7 +23,10 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) opt->loginfo = NULL; if (!opt->verbose_header) { - puts(sha1_to_hex(commit->object.sha1)); + fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); + if (opt->parents) + show_parents(commit, abbrev_commit); + putchar('\n'); return; } @@ -40,7 +52,9 @@ void show_log(struct rev_info *opt, struct log_info *log, const char *sep) printf("%s%s", opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ", diff_unique_abbrev(commit->object.sha1, abbrev_commit)); - if (parent) + if (opt->parents) + show_parents(commit, abbrev_commit); + if (parent) printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit)); putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n'); -- cgit v0.10.2-6-g49f6 From 8f5ff31f8e74ac2f72c3c415beb2639bc36f7cb7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 3 May 2006 14:41:03 +0200 Subject: repo-config: deconvolute logics It was rightly noticed that the logic is quite convoluted. Fix that. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/repo-config.c b/repo-config.c index 7e06d1a..63eda1b 100644 --- a/repo-config.c +++ b/repo-config.c @@ -27,36 +27,38 @@ static int show_config(const char* key_, const char* value_) { char value[256]; const char *vptr = value; + int dup_error = 0; if (value_ == NULL) value_ = ""; - if ((use_key_regexp || !strcmp(key_, key)) && - (!use_key_regexp || - !regexec(key_regexp, key_, 0, NULL, 0)) && - (regexp == NULL || + if (!use_key_regexp && strcmp(key_, key)) + return 0; + if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0)) + return 0; + if (regexp != NULL && (do_not_match ^ - !regexec(regexp, value_, 0, NULL, 0)))) { - int dup_error = 0; - if (show_keys) - printf("%s ", key_); - if (seen && !do_all) - dup_error = 1; - if (type == T_INT) - sprintf(value, "%d", git_config_int(key_, value_)); - else if (type == T_BOOL) - sprintf(value, "%s", git_config_bool(key_, value_) - ? "true" : "false"); - else - vptr = value_; - seen++; - if (dup_error) { - error("More than one value for the key %s: %s", - key_, vptr); - } - else - printf("%s\n", vptr); + regexec(regexp, value_, 0, NULL, 0))) + return 0; + + if (show_keys) + printf("%s ", key_); + if (seen && !do_all) + dup_error = 1; + if (type == T_INT) + sprintf(value, "%d", git_config_int(key_, value_)); + else if (type == T_BOOL) + vptr = git_config_bool(key_, value_) ? "true" : "false"; + else + vptr = value_; + seen++; + if (dup_error) { + error("More than one value for the key %s: %s", + key_, vptr); } + else + printf("%s\n", vptr); + return 0; } -- cgit v0.10.2-6-g49f6 From 095acb8a2ac86c003d89886c5df7706c44912196 Mon Sep 17 00:00:00 2001 From: Martin Langhoff Date: Thu, 4 May 2006 10:53:37 +1200 Subject: cvsserver: use git-rev-list instead of git-log Signed-off-by: Martin Langhoff Signed-off-by: Junio C Hamano diff --git a/git-cvsserver.perl b/git-cvsserver.perl index 11d153c..ffd9c66 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -2076,14 +2076,15 @@ sub update # TODO: log processing is memory bound # if we can parse into a 2nd file that is in reverse order # we can probably do something really efficient - my @git_log_params = ('--parents', '--topo-order'); + my @git_log_params = ('--pretty', '--parents', '--topo-order'); if (defined $lastcommit) { push @git_log_params, "$lastcommit..$self->{module}"; } else { push @git_log_params, $self->{module}; } - open(GITLOG, '-|', 'git-log', @git_log_params) or die "Cannot call git-log: $!"; + # git-rev-list is the backend / plumbing version of git-log + open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!"; my @commits; -- cgit v0.10.2-6-g49f6 From 899707dac0c4a29b5b05a88a0017639508d48e7f Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Wed, 3 May 2006 23:28:46 +0200 Subject: blame: Fix path pruning This makes git-blame useable again, it has been totally broken for some time on larger repositories. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/blame.c b/blame.c index 07d2d27..99ceea8 100644 --- a/blame.c +++ b/blame.c @@ -515,9 +515,9 @@ static int compare_tree_path(struct rev_info* revs, paths[1] = NULL; diff_tree_setup_paths(get_pathspec(revs->prefix, paths), - &revs->diffopt); + &revs->pruning); ret = rev_compare_tree(revs, c1->tree, c2->tree); - diff_tree_release_paths(&revs->diffopt); + diff_tree_release_paths(&revs->pruning); return ret; } @@ -531,9 +531,9 @@ static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1, paths[1] = NULL; diff_tree_setup_paths(get_pathspec(revs->prefix, paths), - &revs->diffopt); + &revs->pruning); ret = rev_same_tree_as_empty(revs, t1); - diff_tree_release_paths(&revs->diffopt); + diff_tree_release_paths(&revs->pruning); return ret; } @@ -834,7 +834,7 @@ int main(int argc, const char **argv) args[0] = filename; args[1] = NULL; - diff_tree_setup_paths(args, &rev.diffopt); + diff_tree_setup_paths(args, &rev.pruning); prepare_revision_walk(&rev); process_commits(&rev, filename, &initial); -- cgit v0.10.2-6-g49f6 From dcb3450fd8785d76dd3d25aa49be66190aa5e7f3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 3 May 2006 17:21:08 -0700 Subject: sha1_to_hex() usage cleanup Somebody on the #git channel complained that the sha1_to_hex() thing uses a static buffer which caused an error message to show the same hex output twice instead of showing two different ones. That's pretty easily rectified by making it uses a simple LRU of a few buffers, which also allows some other users (that were aware of the buffer re-use) to be written in a more straightforward manner. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 6762fce..c845c87 100644 --- a/diff.c +++ b/diff.c @@ -1018,14 +1018,12 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) } if (memcmp(one->sha1, two->sha1, 20)) { - char one_sha1[41]; int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; - memcpy(one_sha1, sha1_to_hex(one->sha1), 41); len += snprintf(msg + len, sizeof(msg) - len, "index %.*s..%.*s", - abbrev, one_sha1, abbrev, - sha1_to_hex(two->sha1)); + abbrev, sha1_to_hex(one->sha1), + abbrev, sha1_to_hex(two->sha1)); if (one->mode == two->mode) len += snprintf(msg + len, sizeof(msg) - len, " %06o", one->mode); diff --git a/merge-tree.c b/merge-tree.c index 50528d5..cc7b5bd 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -24,16 +24,14 @@ static const char *sha1_to_hex_zero(const unsigned char *sha1) static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result) { - char branch1_sha1[50]; - /* If it's already branch1, don't bother showing it */ if (!branch1) return; - memcpy(branch1_sha1, sha1_to_hex_zero(branch1->sha1), 41); printf("0 %06o->%06o %s->%s %s%s\n", branch1->mode, result->mode, - branch1_sha1, sha1_to_hex_zero(result->sha1), + sha1_to_hex_zero(branch1->sha1), + sha1_to_hex_zero(result->sha1), base, result->path); } diff --git a/sha1_file.c b/sha1_file.c index f2d33af..5464828 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -108,9 +108,10 @@ int safe_create_leading_directories(char *path) char * sha1_to_hex(const unsigned char *sha1) { - static char buffer[50]; + static int bufno; + static char hexbuffer[4][50]; static const char hex[] = "0123456789abcdef"; - char *buf = buffer; + char *buffer = hexbuffer[3 & ++bufno], *buf = buffer; int i; for (i = 0; i < 20; i++) { -- cgit v0.10.2-6-g49f6 From aa9b1573a5ca93395e8e41651bfc7245f10e0240 Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Wed, 3 May 2006 23:15:46 -0500 Subject: Alphabetize the glossary. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 02a9d9c..aa1648e 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -1,79 +1,37 @@ -object:: - The unit of storage in git. It is uniquely identified by - the SHA1 of its contents. Consequently, an object can not - be changed. - -object name:: - The unique identifier of an object. The hash of the object's contents - using the Secure Hash Algorithm 1 and usually represented by the 40 - character hexadecimal encoding of the hash of the object (possibly - followed by a white space). - -SHA1:: - Synonym for object name. - -object identifier:: - Synonym for object name. - -hash:: - In git's context, synonym to object name. - -object database:: - Stores a set of "objects", and an individual object is identified - by its object name. The objects usually live in `$GIT_DIR/objects/`. +alternate object database:: + Via the alternates mechanism, a repository can inherit part of its + object database from another object database, which is called + "alternate". blob object:: Untyped object, e.g. the contents of a file. -tree object:: - An object containing a list of file names and modes along with refs - to the associated blob and/or tree objects. A tree is equivalent - to a directory. - -tree:: - Either a working tree, or a tree object together with the - dependent blob and tree objects (i.e. a stored representation - of a working tree). - -DAG:: - Directed acyclic graph. The commit objects form a directed acyclic - graph, because they have parents (directed), and the graph of commit - objects is acyclic (there is no chain which begins and ends with the - same object). - -index:: - A collection of files with stat information, whose contents are - stored as objects. The index is a stored version of your working - tree. Truth be told, it can also contain a second, and even a third - version of a working tree, which are used when merging. - -index entry:: - The information regarding a particular file, stored in the index. - An index entry can be unmerged, if a merge was started, but not - yet finished (i.e. if the index contains multiple versions of - that file). - -unmerged index: - An index which contains unmerged index entries. +branch:: + A non-cyclical graph of revisions, i.e. the complete history of + a particular revision, which is called the branch head. The + branch heads are stored in `$GIT_DIR/refs/heads/`. cache:: Obsolete for: index. -working tree:: - The set of files and directories currently being worked on, - i.e. you can work in your working tree without using git at all. - -directory:: - The list you get with "ls" :-) +chain:: + A list of objects, where each object in the list contains a + reference to its successor (for example, the successor of a commit + could be one of its parents). -revision:: - A particular state of files and directories which was stored in - the object database. It is referenced by a commit object. +changeset:: + BitKeeper/cvsps speak for "commit". Since git does not store + changes, but states, it really does not make sense to use + the term "changesets" with git. checkout:: The action of updating the working tree to a revision which was stored in the object database. +clean:: + A working tree is clean, if it corresponds to the revision + referenced by the current head. Also see "dirty". + commit:: As a verb: The action of storing the current state of the index in the object database. The result is a revision. @@ -85,73 +43,72 @@ commit object:: tree object which corresponds to the top directory of the stored revision. -parent:: - A commit object contains a (possibly empty) list of the logical - predecessor(s) in the line of development, i.e. its parents. +core git:: + Fundamental data structures and utilities of git. Exposes only + limited source code management tools. -changeset:: - BitKeeper/cvsps speak for "commit". Since git does not store - changes, but states, it really does not make sense to use - the term "changesets" with git. +DAG:: + Directed acyclic graph. The commit objects form a directed acyclic + graph, because they have parents (directed), and the graph of commit + objects is acyclic (there is no chain which begins and ends with the + same object). -clean:: - A working tree is clean, if it corresponds to the revision - referenced by the current head. +dircache:: + You are *waaaaay* behind. dirty:: A working tree is said to be dirty if it contains modifications which have not been committed to the current branch. -head:: - The top of a branch. It contains a ref to the corresponding - commit object. +directory:: + The list you get with "ls" :-) -branch:: - A non-cyclical graph of revisions, i.e. the complete history of - a particular revision, which is called the branch head. The - branch heads are stored in `$GIT_DIR/refs/heads/`. +ent:: + Favorite synonym to "tree-ish" by some total geeks. See + `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth + explanation. -master:: - The default branch. Whenever you create a git repository, a branch - named "master" is created, and becomes the active branch. In most - cases, this contains the local development. +fetch:: + Fetching a branch means to get the branch's head ref from a + remote repository, to find out which objects are missing from + the local object database, and to get them, too. -origin:: - The default upstream branch. Most projects have one upstream - project which they track, and by default 'origin' is used for - that purpose. New updates from upstream will be fetched into - this branch; you should never commit to it yourself. +file system:: + Linus Torvalds originally designed git to be a user space file + system, i.e. the infrastructure to hold files and directories. + That ensured the efficiency and speed of git. -ref:: - A 40-byte hex representation of a SHA1 pointing to a particular - object. These may be stored in `$GIT_DIR/refs/`. +git archive:: + Synonym for repository (for arch people). + +hash:: + In git's context, synonym to object name. + +head:: + The top of a branch. It contains a ref to the corresponding + commit object. head ref:: A ref pointing to a head. Often, this is abbreviated to "head". Head refs are stored in `$GIT_DIR/refs/heads/`. -tree-ish:: - A ref pointing to either a commit object, a tree object, or a - tag object pointing to a tag or commit or tree object. +index:: + A collection of files with stat information, whose contents are + stored as objects. The index is a stored version of your working + tree. Truth be told, it can also contain a second, and even a third + version of a working tree, which are used when merging. -ent:: - Favorite synonym to "tree-ish" by some total geeks. See - `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth - explanation. +index entry:: + The information regarding a particular file, stored in the index. + An index entry can be unmerged, if a merge was started, but not + yet finished (i.e. if the index contains multiple versions of + that file). -tag object:: - An object containing a ref pointing to another object, which can - contain a message just like a commit object. It can also - contain a (PGP) signature, in which case it is called a "signed - tag object". +master:: + The default branch. Whenever you create a git repository, a branch + named "master" is created, and becomes the active branch. In most + cases, this contains the local development. -tag:: - A ref pointing to a tag or commit object. In contrast to a head, - a tag is not changed by a commit. Tags (not tag objects) are - stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with - a Lisp tag (which is called object type in git's context). - A tag is most typically used to mark a particular point in the - commit ancestry chain. merge:: To merge branches means to try to accumulate the changes since a @@ -159,55 +116,57 @@ merge:: merge uses heuristics to accomplish that. Evidently, an automatic merge can fail. -octopus:: - To merge more than two branches. Also denotes an intelligent - predator. +object:: + The unit of storage in git. It is uniquely identified by + the SHA1 of its contents. Consequently, an object can not + be changed. -resolve:: - The action of fixing up manually what a failed automatic merge - left behind. +object database:: + Stores a set of "objects", and an individual object is identified + by its object name. The objects usually live in `$GIT_DIR/objects/`. -rewind:: - To throw away part of the development, i.e. to assign the head to - an earlier revision. +object identifier:: + Synonym for object name. -rebase:: - To clean a branch by starting from the head of the main line of - development ("master"), and reapply the (possibly cherry-picked) - changes from that branch. +object name:: + The unique identifier of an object. The hash of the object's contents + using the Secure Hash Algorithm 1 and usually represented by the 40 + character hexadecimal encoding of the hash of the object (possibly + followed by a white space). -repository:: - A collection of refs together with an object database containing - all objects, which are reachable from the refs, possibly accompanied - by meta data from one or more porcelains. A repository can - share an object database with other repositories. +object type: + One of the identifiers "commit","tree","tag" and "blob" describing + the type of an object. -git archive:: - Synonym for repository (for arch people). +octopus:: + To merge more than two branches. Also denotes an intelligent + predator. -file system:: - Linus Torvalds originally designed git to be a user space file - system, i.e. the infrastructure to hold files and directories. - That ensured the efficiency and speed of git. +origin:: + The default upstream branch. Most projects have one upstream + project which they track, and by default 'origin' is used for + that purpose. New updates from upstream will be fetched into + this branch; you should never commit to it yourself. -alternate object database:: - Via the alternates mechanism, a repository can inherit part of its - object database from another object database, which is called - "alternate". +pack:: + A set of objects which have been compressed into one file (to save + space or to transmit them efficiently). -reachable:: - An object is reachable from a ref/commit/tree/tag, if there is a - chain leading from the latter to the former. +pack index:: + The list of identifiers, and other information, of the objects in a + pack, to assist in efficiently accessing the contents of a pack. -chain:: - A list of objects, where each object in the list contains a - reference to its successor (for example, the successor of a commit - could be one of its parents). +parent:: + A commit object contains a (possibly empty) list of the logical + predecessor(s) in the line of development, i.e. its parents. -fetch:: - Fetching a branch means to get the branch's head ref from a - remote repository, to find out which objects are missing from - the local object database, and to get them, too. +plumbing:: + Cute name for core git. + +porcelain:: + Cute name for programs and program suites depending on core git, + presenting a high level access to core git. Porcelains expose + more of a SCM interface than the plumbing. pull:: Pulling a branch means to fetch it and merge it. @@ -221,33 +180,75 @@ push:: the remote head ref. If the remote head is not an ancestor to the local head, the push fails. -pack:: - A set of objects which have been compressed into one file (to save - space or to transmit them efficiently). +reachable:: + An object is reachable from a ref/commit/tree/tag, if there is a + chain leading from the latter to the former. -pack index:: - The list of identifiers, and other information, of the objects in a - pack, to assist in efficiently accessing the contents of a pack. +rebase:: + To clean a branch by starting from the head of the main line of + development ("master"), and reapply the (possibly cherry-picked) + changes from that branch. -core git:: - Fundamental data structures and utilities of git. Exposes only - limited source code management tools. +ref:: + A 40-byte hex representation of a SHA1 pointing to a particular + object. These may be stored in `$GIT_DIR/refs/`. -plumbing:: - Cute name for core git. +repository:: + A collection of refs together with an object database containing + all objects, which are reachable from the refs, possibly accompanied + by meta data from one or more porcelains. A repository can + share an object database with other repositories. -porcelain:: - Cute name for programs and program suites depending on core git, - presenting a high level access to core git. Porcelains expose - more of a SCM interface than the plumbing. +resolve:: + The action of fixing up manually what a failed automatic merge + left behind. -object type: - One of the identifiers "commit","tree","tag" and "blob" describing - the type of an object. +revision:: + A particular state of files and directories which was stored in + the object database. It is referenced by a commit object. + +rewind:: + To throw away part of the development, i.e. to assign the head to + an earlier revision. SCM:: Source code management (tool). -dircache:: - You are *waaaaay* behind. +SHA1:: + Synonym for object name. + +tree object:: + An object containing a list of file names and modes along with refs + to the associated blob and/or tree objects. A tree is equivalent + to a directory. + +tree:: + Either a working tree, or a tree object together with the + dependent blob and tree objects (i.e. a stored representation + of a working tree). + +tree-ish:: + A ref pointing to either a commit object, a tree object, or a + tag object pointing to a tag or commit or tree object. + +tag object:: + An object containing a ref pointing to another object, which can + contain a message just like a commit object. It can also + contain a (PGP) signature, in which case it is called a "signed + tag object". + +tag:: + A ref pointing to a tag or commit object. In contrast to a head, + a tag is not changed by a commit. Tags (not tag objects) are + stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with + a Lisp tag (which is called object type in git's context). + A tag is most typically used to mark a particular point in the + commit ancestry chain. + +unmerged index: + An index which contains unmerged index entries. + +working tree:: + The set of files and directories currently being worked on, + i.e. you can work in your working tree without using git at all. -- cgit v0.10.2-6-g49f6 From 9290cd58c374a73cca989879678dc1ee929cdf2e Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Wed, 3 May 2006 23:18:59 -0500 Subject: Added definitions for a few words: fast forward pickaxe refspec tracking branch Wild hack allows "link:git-" prefix to reference commands too. Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index aa1648e..e216eb4 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -68,6 +68,14 @@ ent:: `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth explanation. +fast forward:: + A fast-forward is a special type of merge where you have + a revision and you are "merging" another branch's changes + that happen to be a descendant of what you have. + In such these cases, you do not make a new merge commit but + instead just update to his revision. This will happen + frequently on a tracking branch of a remote repository. + fetch:: Fetching a branch means to get the branch's head ref from a remote repository, to find out which objects are missing from @@ -160,6 +168,12 @@ parent:: A commit object contains a (possibly empty) list of the logical predecessor(s) in the line of development, i.e. its parents. +pickaxe:: The term pickaxe refers to an option to the diffcore routines + that help select changes that add or delete a given text string. + With the --pickaxe-all option, it can be used to view the + full changeset that introduced or removed, say, a particular + line of text. See gitlink:git-diff[1]. + plumbing:: Cute name for core git. @@ -193,6 +207,18 @@ ref:: A 40-byte hex representation of a SHA1 pointing to a particular object. These may be stored in `$GIT_DIR/refs/`. +refspec:: + A refspec is used by fetch and push to describe the mapping + between remote ref and local ref. They are combined with + a colon in the format :, preceded by an optional + plus sign, +. For example: + `git fetch $URL refs/heads/master:refs/heads/origin` + means "grab the master branch head from the $URL and store + it as my origin branch head". + And `git push $URL refs/heads/master:refs/heads/to-upstream` + means "publish my master branch head as to-upstream master head + at $URL". See also gitlink:git-push[1] + repository:: A collection of refs together with an object database containing all objects, which are reachable from the refs, possibly accompanied @@ -217,6 +243,13 @@ SCM:: SHA1:: Synonym for object name. +tracking branch:: + A regular git branch that is used to follow changes from + another repository. A tracking branch should not contain + direct modifications or made commits made locally. + A tracking branch can usually be identified as the + right-hand-side ref in a Pull: refspec. + tree object:: An object containing a list of file names and modes along with refs to the associated blob and/or tree objects. A tree is equivalent diff --git a/Documentation/sort_glossary.pl b/Documentation/sort_glossary.pl index e57dc78..e0bc552 100644 --- a/Documentation/sort_glossary.pl +++ b/Documentation/sort_glossary.pl @@ -48,7 +48,7 @@ This list is sorted alphabetically: '; @keys=sort {uc($a) cmp uc($b)} keys %terms; -$pattern='(\b'.join('\b|\b',reverse @keys).'\b)'; +$pattern='(\b(?>";/eg; print '[[ref_'.no_spaces($key).']]'.$key."::\n" -- cgit v0.10.2-6-g49f6 From 7abd7117ec57b8c3c2a469db62c7811fdac5c655 Mon Sep 17 00:00:00 2001 From: Jon Loeliger Date: Wed, 3 May 2006 23:19:54 -0500 Subject: Add a few more words to the glossary. Clean up a few entries and fix typos. bare repository cherry-picking hook topic branch [jc: removing questionable "symbolic ref -- see 'ref'" for now.] Signed-off-by: Jon Loeliger Signed-off-by: Junio C Hamano diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index e216eb4..39c90ad 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -3,6 +3,17 @@ alternate object database:: object database from another object database, which is called "alternate". +bare repository:: + A bare repository is normally an appropriately named + directory with a `.git` suffix that does not have a + locally checked-out copy of any of the files under revision + control. That is, all of the `git` administrative and + control files that would normally be present in the + hidden `.git` sub-directory are directly present in + the `repository.git` directory instead, and no other files + are present and checked out. Usually publishers of public + repositories make bare repositories available. + blob object:: Untyped object, e.g. the contents of a file. @@ -28,6 +39,15 @@ checkout:: The action of updating the working tree to a revision which was stored in the object database. +cherry-picking:: + In SCM jargon, "cherry pick" means to choose a subset of + changes out of a series of changes (typically commits) + and record them as a new series of changes on top of + different codebase. In GIT, this is performed by + "git cherry-pick" command to extract the change + introduced by an existing commit and to record it based + on the tip of the current branch as a new commit. + clean:: A working tree is clean, if it corresponds to the revision referenced by the current head. Also see "dirty". @@ -100,6 +120,16 @@ head ref:: A ref pointing to a head. Often, this is abbreviated to "head". Head refs are stored in `$GIT_DIR/refs/heads/`. +hook:: + During the normal execution of several git commands, + call-outs are made to optional scripts that allow + a developer to add functionality or checking. + Typically, the hooks allow for a command to be pre-verified + and potentially aborted, and allow for a post-notification + after the operation is done. + The hook scripts are found in the `$GIT_DIR/hooks/` directory, + and are enabled by simply making them executable. + index:: A collection of files with stat information, whose contents are stored as objects. The index is a stored version of your working @@ -113,10 +143,10 @@ index entry:: that file). master:: - The default branch. Whenever you create a git repository, a branch - named "master" is created, and becomes the active branch. In most - cases, this contains the local development. - + The default development branch. Whenever you create a git + repository, a branch named "master" is created, and becomes + the active branch. In most cases, this contains the local + development, though that is purely conventional and not required. merge:: To merge branches means to try to accumulate the changes since a @@ -151,10 +181,11 @@ octopus:: predator. origin:: - The default upstream branch. Most projects have one upstream - project which they track, and by default 'origin' is used for - that purpose. New updates from upstream will be fetched into - this branch; you should never commit to it yourself. + The default upstream tracking branch. Most projects have at + least one upstream project which they track. By default + 'origin' is used for that purpose. New upstream updates + will be fetched into this branch; you should never commit + to it yourself. pack:: A set of objects which have been compressed into one file (to save @@ -168,7 +199,8 @@ parent:: A commit object contains a (possibly empty) list of the logical predecessor(s) in the line of development, i.e. its parents. -pickaxe:: The term pickaxe refers to an option to the diffcore routines +pickaxe:: + The term pickaxe refers to an option to the diffcore routines that help select changes that add or delete a given text string. With the --pickaxe-all option, it can be used to view the full changeset that introduced or removed, say, a particular @@ -204,8 +236,8 @@ rebase:: changes from that branch. ref:: - A 40-byte hex representation of a SHA1 pointing to a particular - object. These may be stored in `$GIT_DIR/refs/`. + A 40-byte hex representation of a SHA1 or a name that denotes + a particular object. These may be stored in `$GIT_DIR/refs/`. refspec:: A refspec is used by fetch and push to describe the mapping @@ -243,10 +275,17 @@ SCM:: SHA1:: Synonym for object name. +topic branch:: + A regular git branch that is used by a developer to + identify a conceptual line of development. Since branches + are very easy and inexpensive, it is often desirable to + have several small branches that each contain very well + defined concepts or small incremental yet related changes. + tracking branch:: A regular git branch that is used to follow changes from another repository. A tracking branch should not contain - direct modifications or made commits made locally. + direct modifications or have local commits made to it. A tracking branch can usually be identified as the right-hand-side ref in a Pull: refspec. -- cgit v0.10.2-6-g49f6 From 5edbcd8d778d81254d2c8a4229c76c6fa7880407 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 4 May 2006 23:18:14 +0200 Subject: builtin-push: --all and --tags _are_ explicit refspecs ... so do not get refspecs from remotes/* or the config if one of them was specified. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/builtin-push.c b/builtin-push.c index 06d06ff..e530022 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -72,7 +72,7 @@ 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; + int has_explicit_refspec = refspec_nr || all || tags; if (!f) return -1; @@ -144,7 +144,7 @@ static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI]) config_repo = repo; config_current_uri = 0; config_uri = uri; - config_get_refspecs = !refspec_nr; + config_get_refspecs = !(refspec_nr || all || tags); git_config(get_remote_config); return config_current_uri; -- cgit v0.10.2-6-g49f6 From fb335158d5a93349e16bc0985e75e93bb9fcd5eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 4 May 2006 17:38:41 -0700 Subject: Makefile: do not link rev-list any specially. We used to depend on bignum from openssl for rev-list to compute merge-order, but there is no reason to use different build recipe from other programs anymore. Just build it with git-%$X rule like everybody else. Noticed by Alexey Dobriyan. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index a3f7e92..45484fc 100644 --- a/Makefile +++ b/Makefile @@ -564,10 +564,6 @@ git-http-push$X: revision.o http.o http-push.o $(LIB_FILE) $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) -git-rev-list$X: rev-list.o $(LIB_FILE) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIBS) $(OPENSSL_LIBSSL) - init-db.o: init-db.c $(CC) -c $(ALL_CFLAGS) \ -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c -- cgit v0.10.2-6-g49f6 From ac4c758adcf52b913b82d481626b0f8648e95475 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 02:35:58 -0700 Subject: delta: stricter constness Signed-off-by: Junio C Hamano diff --git a/delta.h b/delta.h index 9464f3e..09b2e1e 100644 --- a/delta.h +++ b/delta.h @@ -2,11 +2,11 @@ #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, +extern void *diff_delta(const void *from_buf, unsigned long from_size, + const 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, + const void *delta_buf, unsigned long delta_size, unsigned long *dst_size); /* the smallest possible delta size is 4 bytes */ diff --git a/diff-delta.c b/diff-delta.c index 1188b31..5bc2204 100644 --- a/diff-delta.c +++ b/diff-delta.c @@ -131,8 +131,8 @@ 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, +void *diff_delta(const void *from_buf, unsigned long from_size, + const void *to_buf, unsigned long to_size, unsigned long *delta_size, unsigned long max_size) { diff --git a/patch-delta.c b/patch-delta.c index d95f0d9..affd4f6 100644 --- a/patch-delta.c +++ b/patch-delta.c @@ -14,7 +14,7 @@ #include "delta.h" void *patch_delta(void *src_buf, unsigned long src_size, - void *delta_buf, unsigned long delta_size, + const void *delta_buf, unsigned long delta_size, unsigned long *dst_size) { const unsigned char *data, *top; -- cgit v0.10.2-6-g49f6 From 81c5a0e6e5135384a7316b9689a5b0baaf1a2752 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 May 2006 12:35:39 -0700 Subject: git-svn: documentation updates * Clarify that 'init' requires an argument * Remove instances of 'SVN_URL' in the manpage, it's not an environment variable. * Refer to 'Additional Fetch Arguments' when documenting 'fetch' * document --authors-file / -A option Thanks to Pavel Roskin and Seth Falcon for bringing these issues to my attention. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index 7c44450..e003501 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -42,7 +42,8 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext, my %cmd = ( fetch => [ \&fetch, "Download new revisions from SVN", { 'revision|r=s' => \$_revision, %fc_opts } ], - init => [ \&init, "Initialize and fetch (import)", { } ], + init => [ \&init, "Initialize a repo for tracking" . + " (requires URL argument)", { } ], commit => [ \&commit, "Commit git revisions to SVN", { 'stdin|' => \$_stdin, 'edit|e' => \$_edit, @@ -220,7 +221,8 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN } sub init { - $SVN_URL = shift or croak "SVN repository location required\n"; + $SVN_URL = shift or die "SVN repository location required " . + "as a command-line argument\n"; unless (-d $GIT_DIR) { sys('git-init-db'); } diff --git a/contrib/git-svn/git-svn.txt b/contrib/git-svn/git-svn.txt index e18fcaf..f7d3de4 100644 --- a/contrib/git-svn/git-svn.txt +++ b/contrib/git-svn/git-svn.txt @@ -36,17 +36,22 @@ COMMANDS -------- init:: Creates an empty git repository with additional metadata - directories for git-svn. The SVN_URL must be specified - at this point. + directories for git-svn. The Subversion URL must be specified + as a command-line argument. fetch:: - Fetch unfetched revisions from the SVN_URL we are tracking. - refs/heads/remotes/git-svn will be updated to the latest revision. + Fetch unfetched revisions from the Subversion URL we are + tracking. refs/remotes/git-svn will be updated to the + latest revision. - Note: You should never attempt to modify the remotes/git-svn branch - outside of git-svn. Instead, create a branch from remotes/git-svn - and work on that branch. Use the 'commit' command (see below) - to write git commits back to remotes/git-svn. + Note: You should never attempt to modify the remotes/git-svn + branch outside of git-svn. Instead, create a branch from + remotes/git-svn and work on that branch. Use the 'commit' + command (see below) to write git commits back to + remotes/git-svn. + + See 'Additional Fetch Arguments' if you are interested in + manually joining branches on commit. commit:: Commit specified commit or tree objects to SVN. This relies on @@ -62,9 +67,9 @@ rebuild:: tracked with git-svn. Unfortunately, git-clone does not clone git-svn metadata and the svn working tree that git-svn uses for its operations. This rebuilds the metadata so git-svn can - resume fetch operations. SVN_URL may be optionally specified if - the directory/repository you're tracking has moved or changed - protocols. + resume fetch operations. A Subversion URL may be optionally + specified at the command-line if the directory/repository you're + tracking has moved or changed protocols. show-ignore:: Recursively finds and lists the svn:ignore property on @@ -123,6 +128,24 @@ OPTIONS repo-config key: svn.l repo-config key: svn.findcopiesharder +-A:: +--authors-file=:: + + Syntax is compatible with the files used by git-svnimport and + git-cvsimport: + +------------------------------------------------------------------------ +loginname = Joe User +------------------------------------------------------------------------ + + If this option is specified and git-svn encounters an SVN + committer name that does not exist in the authors-file, git-svn + will abort operation. The user will then have to add the + appropriate entry. Re-running the previous git-svn command + after the authors-file is modified should continue operation. + + repo-config key: svn.authors-file + ADVANCED OPTIONS ---------------- -b:: -- cgit v0.10.2-6-g49f6 From 88521450fc4cca1f96d9179ee4f2cbe51da3359c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 May 2006 12:35:40 -0700 Subject: git-svn 1.0.0 Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano diff --git a/contrib/git-svn/git-svn.perl b/contrib/git-svn/git-svn.perl index e003501..de13a96 100755 --- a/contrib/git-svn/git-svn.perl +++ b/contrib/git-svn/git-svn.perl @@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION $GIT_SVN_INDEX $GIT_SVN $GIT_DIR $REV_DIR/; $AUTHOR = 'Eric Wong '; -$VERSION = '0.11.0'; +$VERSION = '1.0.0'; use Cwd qw/abs_path/; $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git'); -- cgit v0.10.2-6-g49f6 From 2b5f3ed3166ea7d93f4138bb2b6c849851ba86ac Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 5 May 2006 15:05:10 -0400 Subject: Several trivial documentation touch ups. Move incorrect asciidoc level 2 titles back to level 1. Show output of git-name-rev in man page example. Reword sentences that begin with a period (.) in asciidoc numbered lists to work around conversion to man page bug. Mention that git-repack now calls git-prune-packed when the -d option is passed to it. [imap] section headers in the config file example need to be contained in a literal block. imap.pass is the proper config file variable to use, not imap.password. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index 131e445..b333f51 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -101,7 +101,7 @@ OPTIONS is not allowed. Examples -~~~~~~~~ +-------- Clone from upstream:: + diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt index cfc0d88..eca9e9c 100644 --- a/Documentation/git-imap-send.txt +++ b/Documentation/git-imap-send.txt @@ -29,6 +29,7 @@ CONFIGURATION git-imap-send requires the following values in the repository configuration file (shown with examples): +.......................... [imap] Folder = "INBOX.Drafts" @@ -38,8 +39,9 @@ configuration file (shown with examples): [imap] Host = imap.server.com User = bob - Password = pwd + Pass = pwd Port = 143 +.......................... BUGS diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt index 6870708..ffaa004 100644 --- a/Documentation/git-name-rev.txt +++ b/Documentation/git-name-rev.txt @@ -41,6 +41,7 @@ Enter git-name-rev: ------------ % git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a +33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940 ------------ Now you are wiser, because you know that it happened 940 revisions before v0.99. diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index d2f9a44..9516227 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -38,6 +38,7 @@ OPTIONS -d:: After packing, if the newly created packs make some existing packs redundant, remove the redundant packs. + Also runs gitlink:git-prune-packed[1]. -l:: Pass the `--local` option to `git pack-objects`, see diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt index ddcf523..fd44f62 100644 --- a/Documentation/git-repo-config.txt +++ b/Documentation/git-repo-config.txt @@ -34,10 +34,10 @@ convert the value to the canonical form (simple decimal number for int, a "true" or "false" string for bool). If no type specifier is passed, no checks or transformations are performed on the value. -This command will fail if +This command will fail if: -. .git/config is invalid, -. .git/config can not be written to, +. The .git/config file is invalid, +. Can not write to .git/config, . no section was provided, . the section or key is invalid, . you try to unset an option which does not exist, or diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index ebcfe5e..b27399d 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -43,7 +43,7 @@ OPTIONS Commit to make the current HEAD. Examples -~~~~~~~~ +-------- Undo a commit and redo:: + -- cgit v0.10.2-6-g49f6 From e994004f93a481414b9065ac24edd0807d1c311e Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 5 May 2006 15:05:24 -0400 Subject: Fix up docs where "--" isn't displayed correctly. A bare "--" doesn't show up in man or html pages correctly as two individual dashes unless backslashed as \-- in the asciidoc source. Note, no backslash is needed inside a literal block. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index ae24547..5e31129 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -26,7 +26,7 @@ OPTIONS -v:: Be verbose. ---:: +\--:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken for command-line options). diff --git a/Documentation/git-checkout-index.txt b/Documentation/git-checkout-index.txt index 09bd6a5..765c173 100644 --- a/Documentation/git-checkout-index.txt +++ b/Documentation/git-checkout-index.txt @@ -63,7 +63,7 @@ OPTIONS Only meaningful with `--stdin`; paths are separated with NUL character instead of LF. ---:: +\--:: Do not interpret any more arguments as options. The order of the flags used to matter, but not anymore. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 0a7365b..38df59c 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -106,7 +106,7 @@ but can be used to amend a merge commit. index and the latest commit does not match on the specified paths to avoid confusion. ---:: +\--:: Do not interpret any more arguments as options. ...:: diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index af378ff..c9ffff7 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -51,7 +51,7 @@ git log v2.6.12.. include/scsi drivers/scsi:: Show all commits since version 'v2.6.12' that changed any file in the include/scsi or drivers/scsi subdirectories -git log --since="2 weeks ago" -- gitk:: +git log --since="2 weeks ago" \-- gitk:: Show the changes during the last two weeks to the file 'gitk'. The "--" is necessary to avoid confusion with the *branch* named diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 796d049..a29c633 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -106,7 +106,7 @@ OPTIONS lines, show only handful hexdigits prefix. Non default number of digits can be specified with --abbrev=. ---:: +\--:: Do not interpret any more arguments as options. :: diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt index fbc986a..332e023 100644 --- a/Documentation/git-merge-index.txt +++ b/Documentation/git-merge-index.txt @@ -8,7 +8,7 @@ git-merge-index - Runs a merge for files needing merging SYNOPSIS -------- -'git-merge-index' [-o] [-q] (-a | -- | \*) +'git-merge-index' [-o] [-q] (-a | \-- | \*) DESCRIPTION ----------- @@ -19,7 +19,7 @@ files are passed as arguments 5, 6 and 7. OPTIONS ------- ---:: +\--:: Do not interpret any more arguments as options. -a:: diff --git a/Documentation/git-prune.txt b/Documentation/git-prune.txt index f694fcb..a11e303 100644 --- a/Documentation/git-prune.txt +++ b/Documentation/git-prune.txt @@ -28,7 +28,7 @@ OPTIONS Do not remove anything; just report what it would remove. ---:: +\--:: Do not interpret any more arguments as options. ...:: diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index c9c3088..66fc478 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -32,7 +32,7 @@ OPTIONS -v:: Be verbose. ---:: +\--:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken for command-line options). diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 23f2b6f..57177c7 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -113,7 +113,7 @@ OPTIONS Only meaningful with `--stdin`; paths are separated with NUL character instead of LF. ---:: +\--:: Do not interpret any more arguments as options. :: diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt index 4962d69..7a6132b 100644 --- a/Documentation/git-verify-pack.txt +++ b/Documentation/git-verify-pack.txt @@ -25,7 +25,7 @@ OPTIONS -v:: After verifying the pack, show list of objects contained in the pack. ---:: +\--:: Do not interpret any more arguments as options. OUTPUT FORMAT diff --git a/Documentation/git-whatchanged.txt b/Documentation/git-whatchanged.txt index 641cb7e..e8f21d0 100644 --- a/Documentation/git-whatchanged.txt +++ b/Documentation/git-whatchanged.txt @@ -58,7 +58,7 @@ git-whatchanged -p v2.6.12.. include/scsi drivers/scsi:: Show as patches the commits since version 'v2.6.12' that changed any file in the include/scsi or drivers/scsi subdirectories -git-whatchanged --since="2 weeks ago" -- gitk:: +git-whatchanged --since="2 weeks ago" \-- gitk:: Show the changes during the last two weeks to the file 'gitk'. The "--" is necessary to avoid confusion with the *branch* named diff --git a/Documentation/gitk.txt b/Documentation/gitk.txt index eb126d7..cb482bf 100644 --- a/Documentation/gitk.txt +++ b/Documentation/gitk.txt @@ -31,7 +31,7 @@ gitk v2.6.12.. include/scsi drivers/scsi:: Show as the changes since version 'v2.6.12' that changed any file in the include/scsi or drivers/scsi subdirectories -gitk --since="2 weeks ago" -- gitk:: +gitk --since="2 weeks ago" \-- gitk:: Show the changes during the last two weeks to the file 'gitk'. The "--" is necessary to avoid confusion with the *branch* named -- cgit v0.10.2-6-g49f6 From ae0b219c8e32aa8f34401252d845916b136a467b Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 5 May 2006 15:05:36 -0400 Subject: Update git-unpack-objects documentation. Document that git-unpack-objects will not produce any results when used on a pack that exists in a repository; move it first. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/Documentation/git-unpack-objects.txt b/Documentation/git-unpack-objects.txt index 1828062..c20b38b 100644 --- a/Documentation/git-unpack-objects.txt +++ b/Documentation/git-unpack-objects.txt @@ -13,9 +13,16 @@ SYNOPSIS DESCRIPTION ----------- -Reads a packed archive (.pack) from the standard input, and -expands the objects contained in the pack into "one-file -one-object" format in $GIT_OBJECT_DIRECTORY. +Read a packed archive (.pack) from the standard input, expanding +the objects contained within and writing them into the repository in +"loose" (one object per file) format. + +Objects that already exist in the repository will *not* be unpacked +from the pack-file. Therefore, nothing will be unpacked if you use +this command on a pack-file that exists within the target repository. + +Please see the `git-repack` documentation for options to generate +new packs and replace existing ones. OPTIONS ------- -- cgit v0.10.2-6-g49f6 From 81ae43cdc4ec4bfba1638aa9786e08322d10b72c Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 5 May 2006 15:06:07 -0400 Subject: Clarify git-cherry documentation. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt index 9a5e371..893baaa 100644 --- a/Documentation/git-cherry.txt +++ b/Documentation/git-cherry.txt @@ -11,11 +11,20 @@ SYNOPSIS DESCRIPTION ----------- -Each commit between the fork-point and is examined, and compared against -the change each commit between the fork-point and introduces. -Commits already included in upstream are prefixed with '-' (meaning "drop from -my local pull"), while commits missing from upstream are prefixed with '+' -(meaning "add to the updated upstream"). +The changeset (or "diff") of each commit between the fork-point and +is compared against each commit between the fork-point and . + +Every commit with a changeset that doesn't exist in the other branch +has its id (sha1) reported, prefixed by a symbol. Those existing only +in the branch are prefixed with a minus (-) sign, and those +that only exist in the branch are prefixed with a plus (+) symbol. + +Because git-cherry compares the changeset rather than the commit id +(sha1), you can use git-cherry to find out if a commit you made locally +has been applied under a different commit id. For example, +this will happen if you're feeding patches via email rather +than pushing or pulling commits directly. + OPTIONS ------- -- cgit v0.10.2-6-g49f6 From 93ddef3e2dd5f7f3238fad9d52e974d03c7844f2 Mon Sep 17 00:00:00 2001 From: sean Date: Fri, 5 May 2006 09:49:15 -0400 Subject: Fix for config file section parsing. Currently, if the target key has a section that matches the initial substring of another section we mistakenly believe we've found the correct section. To avoid this problem, ensure that the section lengths are identical before comparison. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c index 4e1f0c2..a3e14d7 100644 --- a/config.c +++ b/config.c @@ -335,8 +335,9 @@ static int store_aux(const char* key, const char* value) store.offset[store.seen] = ftell(config_file); store.state = KEY_SEEN; store.seen++; - } else if(!strncmp(key, store.key, store.baselen)) - store.state = SECTION_SEEN; + } else if (strrchr(key, '.') - key == store.baselen && + !strncmp(key, store.key, store.baselen)) + store.state = SECTION_SEEN; } return 0; } -- cgit v0.10.2-6-g49f6 From 7ebdba614223f867d3f19963647406df1d0e5ce0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 2 May 2006 16:58:37 +0200 Subject: repo-config: trim white-space before comment Earlier, calling git-repo-config core.hello on a .git/config like this: [core] hello = world ; a comment would yield "world " (i.e. with a trailing space). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano (cherry picked from c1aee1fd8d94da9b3c5d2dc1d4264f7e73a58f80 commit) diff --git a/config.c b/config.c index a3e14d7..2cdf5fc 100644 --- a/config.c +++ b/config.c @@ -60,6 +60,12 @@ static char *parse_value(void) space = 1; continue; } + if (!quote) { + if (c == ';' || c == '#') { + comment = 1; + continue; + } + } if (space) { if (len) value[len++] = ' '; @@ -93,12 +99,6 @@ static char *parse_value(void) quote = 1-quote; continue; } - if (!quote) { - if (c == ';' || c == '#') { - comment = 1; - continue; - } - } value[len++] = c; } } -- cgit v0.10.2-6-g49f6 From e388c7382563b7497397c78bc078d0679dc891a8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 2 May 2006 00:40:24 -0700 Subject: core.prefersymlinkrefs: use symlinks for .git/HEAD When inspecting a project whose build infrastructure used to assume that .git/HEAD is a symlink ref, core.prefersymlinkrefs in the config file of such a project would help to bisect its history. Signed-off-by: Junio C Hamano (cherry picked from 9f0bb90d161edf8c43f5261d12bf83f14eb02ff4 commit) diff --git a/Documentation/config.txt b/Documentation/config.txt index b27b0d5..d1a4bec 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -64,9 +64,11 @@ core.ignoreStat:: slow, such as Microsoft Windows. See gitlink:git-update-index[1]. False by default. -core.onlyUseSymrefs:: - Always use the "symref" format instead of symbolic links for HEAD - and other symbolic reference files. True by default. +core.preferSymlinkRefs:: + Instead of the default "symref" format for HEAD + and other symbolic reference files, use symbolic links. + This is sometimes needed to work with old scripts that + expect HEAD to be a symbolic link. core.repositoryFormatVersion:: Internal variable identifying the repository format and layout diff --git a/Makefile b/Makefile index 8aed3af..3972d10 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,8 @@ all: # # Define NO_SETENV if you don't have setenv in the C library. # -# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link. -# Don't enable it on Windows. +# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. +# Enable it on Windows. By default, symrefs are still used. # # Define PPC_SHA1 environment variable when running make to make use of # a bundled SHA1 routine optimized for PowerPC. @@ -260,6 +260,7 @@ ifeq ($(uname_O),Cygwin) NO_D_TYPE_IN_DIRENT = YesPlease NO_D_INO_IN_DIRENT = YesPlease NO_STRCASESTR = YesPlease + NO_SYMLINK_HEAD = YesPlease NEEDS_LIBICONV = YesPlease # There are conflicting reports about this. # On some boxes NO_MMAP is needed, and not so elsewhere. @@ -383,6 +384,9 @@ endif ifdef NO_D_INO_IN_DIRENT ALL_CFLAGS += -DNO_D_INO_IN_DIRENT endif +ifdef NO_SYMLINK_HEAD + ALL_CFLAGS += -DNO_SYMLINK_HEAD +endif ifdef NO_STRCASESTR COMPAT_CFLAGS += -DNO_STRCASESTR COMPAT_OBJS += compat/strcasestr.o diff --git a/cache.h b/cache.h index 4d8fabc..ae69fde 100644 --- a/cache.h +++ b/cache.h @@ -168,7 +168,7 @@ extern void rollback_index_file(struct cache_file *); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int assume_unchanged; -extern int only_use_symrefs; +extern int prefer_symlink_refs; extern int warn_ambiguous_refs; extern int diff_rename_limit_default; extern int shared_repository; diff --git a/config.c b/config.c index 2cdf5fc..87fb220 100644 --- a/config.c +++ b/config.c @@ -227,8 +227,8 @@ int git_default_config(const char *var, const char *value) return 0; } - if (!strcmp(var, "core.symrefsonly")) { - only_use_symrefs = git_config_bool(var, value); + if (!strcmp(var, "core.prefersymlinkrefs")) { + prefer_symlink_refs = git_config_bool(var, value); return 0; } diff --git a/environment.c b/environment.c index 6df6478..444c99e 100644 --- a/environment.c +++ b/environment.c @@ -13,7 +13,7 @@ char git_default_email[MAX_GITNAME]; char git_default_name[MAX_GITNAME]; int trust_executable_bit = 1; int assume_unchanged = 0; -int only_use_symrefs = 0; +int prefer_symlink_refs = 0; int warn_ambiguous_refs = 1; int repository_format_version = 0; char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8"; diff --git a/refs.c b/refs.c index 03398cc..275b914 100644 --- a/refs.c +++ b/refs.c @@ -76,8 +76,8 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master) char ref[1000]; int fd, len, written; -#ifdef USE_SYMLINK_HEAD - if (!only_use_symrefs) { +#ifndef NO_SYMLINK_HEAD + if (prefer_symlink_refs) { unlink(git_HEAD); if (!symlink(refs_heads_master, git_HEAD)) return 0; -- cgit v0.10.2-6-g49f6 From 86118bcb463e3f34b3df21d550335a40586dfb66 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 03:20:44 -0700 Subject: pack-object: squelch eye-candy on non-tty One of my post-update scripts runs a git-fetch into a separate repository and sends the results back to me (2>&1); I end up getting this in the mail: Generating pack... Done counting 180 objects. Result has 131 objects. Deltifying 131 objects. 0% (0/131) done^M 1% (2/131) done^M... This defaults not to do the progress report when not on a tty. You could give --progress to force the progress report, but let's not bother even documenting it nor mentioning it in the usage string. Signed-off-by: Junio C Hamano diff --git a/pack-objects.c b/pack-objects.c index 6604338..53caed4 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -1239,6 +1239,7 @@ int main(int argc, char **argv) setup_git_directory(); + progress = isatty(2); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -1269,6 +1270,10 @@ int main(int argc, char **argv) usage(pack_usage); continue; } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } if (!strcmp("-q", arg)) { progress = 0; continue; -- cgit v0.10.2-6-g49f6 From 09895c1fa02f77acb2bde3d7e9da675eeb1271cf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 17:50:06 -0700 Subject: update-index --unresolve: work from a subdirectory. It completely forgot to take the prefix into account, so you had to feed the full path even when you start from a subdirectory, which was nonsensical. Signed-off-by: Junio C Hamano diff --git a/update-index.c b/update-index.c index 9fa3d2b..1870ac7 100644 --- a/update-index.c +++ b/update-index.c @@ -576,7 +576,8 @@ static void read_head_pointers(void) } } -static int do_unresolve(int ac, const char **av) +static int do_unresolve(int ac, const char **av, + const char *prefix, int prefix_length) { int i; int err = 0; @@ -588,7 +589,10 @@ static int do_unresolve(int ac, const char **av) for (i = 1; i < ac; i++) { const char *arg = av[i]; - err |= unresolve_one(arg); + const char *p = prefix_path(prefix, prefix_length, arg); + err |= unresolve_one(p); + if (p != arg) + free((char*)p); } return err; } @@ -704,7 +708,8 @@ int main(int argc, const char **argv) break; } if (!strcmp(path, "--unresolve")) { - has_errors = do_unresolve(argc - i, argv + i); + has_errors = do_unresolve(argc - i, argv + i, + prefix, prefix_length); if (has_errors) active_cache_changed = 0; goto finish; -- cgit v0.10.2-6-g49f6 From dc46da2286cf0d42e1e5ebbd272c201dbc3a626a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 22:38:06 -0700 Subject: checkout-index: plug memory leak from prefix_path() prefix_path() sometimes allocates new memory and returns it, and other times returns the incoming path argument intact. The callers need to be a bit careful not to leak memory. Signed-off-by: Junio C Hamano diff --git a/checkout-index.c b/checkout-index.c index dd6a2d8..0b9cabc 100644 --- a/checkout-index.c +++ b/checkout-index.c @@ -269,12 +269,16 @@ int main(int argc, char **argv) /* Check out named files first */ for ( ; i < argc; i++) { const char *arg = argv[i]; + const char *p; if (all) die("git-checkout-index: don't mix '--all' and explicit filenames"); if (read_from_stdin) die("git-checkout-index: don't mix '--stdin' and explicit filenames"); - checkout_file(prefix_path(prefix, prefix_length, arg)); + p = prefix_path(prefix, prefix_length, arg); + checkout_file(p); + if (p != arg) + free((char*)p); } if (read_from_stdin) { @@ -284,6 +288,8 @@ int main(int argc, char **argv) strbuf_init(&buf); while (1) { char *path_name; + const char *p; + read_line(&buf, stdin, line_termination); if (buf.eof) break; @@ -291,7 +297,10 @@ int main(int argc, char **argv) path_name = unquote_c_style(buf.buf, NULL); else path_name = buf.buf; - checkout_file(prefix_path(prefix, prefix_length, path_name)); + p = prefix_path(prefix, prefix_length, path_name); + checkout_file(p); + if (p != path_name) + free((char *)p); if (path_name != buf.buf) free(path_name); } -- cgit v0.10.2-6-g49f6 From fb69a760cc9103d1f8341a3c8ea09bd15a8a412d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 22:53:56 -0700 Subject: update-index: plug memory leak from prefix_path() prefix_path() sometimes allocates new memory and returns it, and other times returns the incoming path argument intact. The callers need to be a bit careful not to leak memory. Signed-off-by: Junio C Hamano diff --git a/update-index.c b/update-index.c index 1870ac7..d63f8ac 100644 --- a/update-index.c +++ b/update-index.c @@ -364,23 +364,26 @@ static void update_one(const char *path, const char *prefix, int prefix_length) const char *p = prefix_path(prefix, prefix_length, path); if (!verify_path(p)) { fprintf(stderr, "Ignoring path %s\n", path); - return; + goto free_return; } if (mark_valid_only) { if (mark_valid(p)) die("Unable to mark file %s", path); - return; + goto free_return; } if (force_remove) { if (remove_file_from_cache(p)) die("git-update-index: unable to remove %s", path); report("remove '%s'", path); - return; + goto free_return; } if (add_file_to_cache(p)) die("Unable to process file %s", path); report("add '%s'", path); + free_return: + if (p != path) + free((char*)p); } static void read_index_info(int line_termination) @@ -735,6 +738,7 @@ int main(int argc, const char **argv) strbuf_init(&buf); while (1) { char *path_name; + const char *p; read_line(&buf, stdin, line_termination); if (buf.eof) break; @@ -742,11 +746,12 @@ int main(int argc, const char **argv) path_name = unquote_c_style(buf.buf, NULL); else path_name = buf.buf; - update_one(path_name, prefix, prefix_length); - if (set_executable_bit) { - const char *p = prefix_path(prefix, prefix_length, path_name); + p = prefix_path(prefix, prefix_length, path_name); + update_one(p, NULL, 0); + if (set_executable_bit) chmod_path(set_executable_bit, p); - } + if (p != path_name) + free((char*) p); if (path_name != buf.buf) free(path_name); } -- cgit v0.10.2-6-g49f6 From 6f81bf16a91c4db5a02b81cbade89e73ca4d49b4 Mon Sep 17 00:00:00 2001 From: sean Date: Sat, 6 May 2006 14:14:02 -0400 Subject: Another config file parsing fix. If the variable we need to store should go into a section that currently only has a single variable (not matching the one we're trying to insert), we will already be into the next section before we notice we've bypassed the correct location to insert the variable. To handle this case we store the current location as soon as we find a variable matching the section of our new variable. This breakage was brought up by Linus. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c index 87fb220..41066e4 100644 --- a/config.c +++ b/config.c @@ -336,8 +336,10 @@ static int store_aux(const char* key, const char* value) store.state = KEY_SEEN; store.seen++; } else if (strrchr(key, '.') - key == store.baselen && - !strncmp(key, store.key, store.baselen)) + !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; + store.offset[store.seen] = ftell(config_file); + } } return 0; } -- cgit v0.10.2-6-g49f6 From bd886fd3ea49b726493255d4adf5d20b31681713 Mon Sep 17 00:00:00 2001 From: sean Date: Sat, 6 May 2006 15:43:43 -0400 Subject: t1300-repo-config: two new config parsing tests. - correctly insert a new variable into a section that only contains a single (different) variable. - correctly insert a new section that matches the initial substring of an existing section. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 1bf728f..7090ea9 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -278,5 +278,36 @@ git-repo-config > output 2>&1 test_expect_success 'no arguments, but no crash' \ "test $? = 129 && grep usage output" +cat > .git/config << EOF +[a.b] + c = d +EOF + +git-repo-config a.x y + +cat > expect << EOF +[a.b] + c = d +[a] + x = y +EOF + +test_expect_success 'new section is partial match of another' 'cmp .git/config expect' + +git-repo-config b.x y +git-repo-config a.b c + +cat > expect << EOF +[a.b] + c = d +[a] + x = y + b = c +[b] + x = y +EOF + +test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect' + test_done -- cgit v0.10.2-6-g49f6