From 232475d38231552f04f2e4221936700a923a531e Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Tue, 15 Nov 2005 10:34:03 +1100 Subject: Add -r flag and some speedups The -r flag means "rev-list order", i.e. just display the commits in the order they come from git-rev-list. The speedups include: - don't process the whole commit line-by-line, only the header - don't convert dates when reading the commits, rather do it when needed - don't do the $canv delete lines.$id in drawlines when drawing the graph initially (it was taking a lot of the total time) - cache the date conversion for each hour (more important with tk8.5, since [clock format] is a lot slower in 8.5 than in 8.4). Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index a9d37d9..95b05c0 100755 --- a/gitk +++ b/gitk @@ -60,7 +60,7 @@ proc getcommits {rargs} { proc getcommitlines {commfd} { global commits parents cdate children - global commitlisted phase commitinfo nextupdate + global commitlisted phase nextupdate global stopped redisplaying leftover set stuff [read $commfd] @@ -196,42 +196,41 @@ proc parsecommit {id contents listed olds} { incr ncleft($p) } } - foreach line [split $contents "\n"] { - if {$inhdr} { - if {$line == {}} { - set inhdr 0 - } else { - set tag [lindex $line 0] - if {$tag == "author"} { - set x [expr {[llength $line] - 2}] - set audate [lindex $line $x] - set auname [lrange $line 1 [expr {$x - 1}]] - } elseif {$tag == "committer"} { - set x [expr {[llength $line] - 2}] - set comdate [lindex $line $x] - set comname [lrange $line 1 [expr {$x - 1}]] - } - } - } else { - if {$comment == {}} { - set headline [string trim $line] - } else { - append comment "\n" - } - if {!$listed} { - # git-rev-list indents the comment by 4 spaces; - # if we got this via git-cat-file, add the indentation - append comment " " - } - append comment $line + set hdrend [string first "\n\n" $contents] + if {$hdrend < 0} { + # should never happen... + set hdrend [string length $contents] + } + set header [string range $contents 0 [expr {$hdrend - 1}]] + set comment [string range $contents [expr {$hdrend + 2}] end] + foreach line [split $header "\n"] { + set tag [lindex $line 0] + if {$tag == "author"} { + set audate [lindex $line end-1] + set auname [lrange $line 1 end-2] + } elseif {$tag == "committer"} { + set comdate [lindex $line end-1] + set comname [lrange $line 1 end-2] } } - if {$audate != {}} { - set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"] + set headline {} + # take the first line of the comment as the headline + set i [string first "\n" $comment] + if {$i >= 0} { + set headline [string trim [string range $comment 0 $i]] + } + if {!$listed} { + # git-rev-list indents the comment by 4 spaces; + # if we got this via git-cat-file, add the indentation + set newcomment {} + foreach line [split $comment "\n"] { + append newcomment " " + append newcomment $line + } + set comment $newcomment } if {$comdate != {}} { set cdate($id) $comdate - set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"] } set commitinfo($id) [list $headline $auname $audate \ $comname $comdate $comment] @@ -683,7 +682,7 @@ Use and redistribute under the terms of the GNU General Public License} \ } proc assigncolor {id} { - global commitinfo colormap commcolors colors nextcolor + global colormap commcolors colors nextcolor global parents nparents children nchildren global cornercrossings crossings @@ -783,10 +782,12 @@ proc bindline {t id} { $canv bind $t "lineclick %x %y $id 1" } -proc drawlines {id xtra} { +proc drawlines {id xtra delold} { global mainline mainlinearrow sidelines lthickness colormap canv - $canv delete lines.$id + if {$delold} { + $canv delete lines.$id + } if {[info exists mainline($id)]} { set t [$canv create line $mainline($id) \ -width [expr {($xtra + 1) * $lthickness}] \ @@ -858,7 +859,7 @@ proc drawcommitline {level} { set mainline($id) [trimdiagstart $mainline($id)] } } - drawlines $id 0 + drawlines $id 0 0 set orad [expr {$linespc / 3}] set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \ [expr $x + $orad - 1] [expr $y1 + $orad - 1] \ @@ -878,6 +879,7 @@ proc drawcommitline {level} { set headline [lindex $commitinfo($id) 0] set name [lindex $commitinfo($id) 1] set date [lindex $commitinfo($id) 2] + set date [formatdate $date] set linehtag($lineno) [$canv create text $xt $y1 -anchor w \ -text $headline -font $mainfont ] $canv bind $linehtag($lineno) "rowmenu %X %Y $id" @@ -1446,8 +1448,8 @@ proc decidenext {{noread 0}} { } proc drawcommit {id} { - global phase todo nchildren datemode nextupdate - global numcommits ncmupdate displayorder todo onscreen + global phase todo nchildren datemode nextupdate revlistorder + global numcommits ncmupdate displayorder todo onscreen parents if {$phase != "incrdraw"} { set phase incrdraw @@ -1459,19 +1461,29 @@ proc drawcommit {id} { lappend todo $id set onscreen($id) 0 } - set level [decidenext 1] - if {$level == {} || $id != [lindex $todo $level]} { - return - } - while 1 { - lappend displayorder [lindex $todo $level] - if {[updatetodo $level $datemode]} { - set level [decidenext 1] - if {$level == {}} break + if {$revlistorder} { + set level [lsearch -exact $todo $id] + if {$level < 0} { + error_popup "oops, $id isn't in todo" + return } - set id [lindex $todo $level] - if {![info exists commitlisted($id)]} { - break + lappend displayorder $id + updatetodo $level 0 + } else { + set level [decidenext 1] + if {$level == {} || $id != [lindex $todo $level]} { + return + } + while 1 { + lappend displayorder [lindex $todo $level] + if {[updatetodo $level $datemode]} { + set level [decidenext 1] + if {$level == {}} break + } + set id [lindex $todo $level] + if {![info exists commitlisted($id)]} { + break + } } } drawmore 1 @@ -1523,21 +1535,23 @@ proc drawrest {} { global phase stopped redisplaying selectedline global datemode todo displayorder global numcommits ncmupdate - global nextupdate startmsecs - - set level [decidenext] - if {$level >= 0} { - set phase drawgraph - while 1 { - lappend displayorder [lindex $todo $level] - set hard [updatetodo $level $datemode] - if {$hard} { - set level [decidenext] - if {$level < 0} break + global nextupdate startmsecs revlistorder + + if {!$revlistorder} { + set level [decidenext] + if {$level >= 0} { + set phase drawgraph + while 1 { + lappend displayorder [lindex $todo $level] + set hard [updatetodo $level $datemode] + if {$hard} { + set level [decidenext] + if {$level < 0} break + } } } - drawmore 0 } + drawmore 0 set phase {} set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs] #puts "overall $drawmsecs ms for $numcommits commits" @@ -2146,8 +2160,10 @@ proc selectline {l isnew} { $ctext mark set fmark.0 0.0 $ctext mark gravity fmark.0 left set info $commitinfo($id) - $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n" - $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n" + set date [formatdate [lindex $info 2]] + $ctext insert end "Author: [lindex $info 1] $date\n" + set date [formatdate [lindex $info 4]] + $ctext insert end "Committer: [lindex $info 3] $date\n" if {[info exists idtags($id)]} { $ctext insert end "Tags:" foreach tag $idtags($id) { @@ -3226,7 +3242,7 @@ proc lineclick {x y id isnew} { normalline $canv delete hover # draw this line thicker than normal - drawlines $id 1 + drawlines $id 1 1 set thickerline $id if {$isnew} { set ymax [lindex [$canv cget -scrollregion] 3] @@ -3255,7 +3271,8 @@ proc lineclick {x y id isnew} { set info $commitinfo($id) $ctext insert end "\n\t[lindex $info 0]\n" $ctext insert end "\tAuthor:\t[lindex $info 1]\n" - $ctext insert end "\tDate:\t[lindex $info 2]\n" + set date [formatdate [lindex $info 2]] + $ctext insert end "\tDate:\t$date\n" if {[info exists children($id)]} { $ctext insert end "\nChildren:" set i 0 @@ -3267,7 +3284,8 @@ proc lineclick {x y id isnew} { $ctext tag bind link$i <1> [list selbyid $child] $ctext insert end "\n\t[lindex $info 0]" $ctext insert end "\n\tAuthor:\t[lindex $info 1]" - $ctext insert end "\n\tDate:\t[lindex $info 2]\n" + set date [formatdate [lindex $info 2]] + $ctext insert end "\n\tDate:\t$date\n" } } $ctext conf -state disabled @@ -3278,7 +3296,7 @@ proc lineclick {x y id isnew} { proc normalline {} { global thickerline if {[info exists thickerline]} { - drawlines $thickerline 0 + drawlines $thickerline 0 1 unset thickerline } } @@ -3650,6 +3668,20 @@ proc doquit {} { destroy . } +proc formatdate {d} { + global hours nhours tfd + + set hr [expr {$d / 3600}] + set ms [expr {$d % 3600}] + if {![info exists hours($hr)]} { + set hours($hr) [clock format $d -format "%Y-%m-%d %H"] + set nhours($hr) 0 + } + incr nhours($hr) + set minsec [format "%.2d:%.2d" [expr {$ms/60}] [expr {$ms%60}]] + return "$hours($hr):$minsec" +} + # defaults... set datemode 0 set boldnames 0 @@ -3662,6 +3694,7 @@ set findmergefiles 0 set gaudydiff 0 set maxgraphpct 50 set maxwidth 16 +set revlistorder 0 set colors {green red blue magenta darkgrey brown orange} @@ -3678,6 +3711,7 @@ foreach arg $argv { "^$" { } "^-b" { set boldnames 1 } "^-d" { set datemode 1 } + "^-r" { set revlistorder 1 } default { lappend revtreeargs $arg } -- cgit v0.10.2-6-g49f6 From 36a7cad6e4ae8a331731666d985a7619a76bb4fb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 18 Nov 2005 23:54:17 -0800 Subject: readrefs: grab all refs with one call to ls-remote. Instead of reading refs/heads/* and refs/tags/* files ourselves and missing files in subdirectories of heads/ and tags/, use ls-remote on local repository and grab all of them. This lets us also remove the procedure readotherrefs. Signed-off-by: Junio C Hamano diff --git a/gitk b/gitk index 95b05c0..3ea9434 100755 --- a/gitk +++ b/gitk @@ -238,77 +238,43 @@ proc parsecommit {id contents listed olds} { proc readrefs {} { global tagids idtags headids idheads tagcontents - - set tags [glob -nocomplain -types f [gitdir]/refs/tags/*] - foreach f $tags { - catch { - set fd [open $f r] - set line [read $fd] - if {[regexp {^[0-9a-f]{40}} $line id]} { - set direct [file tail $f] - set tagids($direct) $id - lappend idtags($id) $direct - set tagblob [exec git-cat-file tag $id] - set contents [split $tagblob "\n"] - set obj {} - set type {} - set tag {} - foreach l $contents { - if {$l == {}} break - switch -- [lindex $l 0] { - "object" {set obj [lindex $l 1]} - "type" {set type [lindex $l 1]} - "tag" {set tag [string range $l 4 end]} - } - } - if {$obj != {} && $type == "commit" && $tag != {}} { - set tagids($tag) $obj - lappend idtags($obj) $tag - set tagcontents($tag) $tagblob - } - } - close $fd - } - } - set heads [glob -nocomplain -types f [gitdir]/refs/heads/*] - foreach f $heads { - catch { - set fd [open $f r] - set line [read $fd 40] - if {[regexp {^[0-9a-f]{40}} $line id]} { - set head [file tail $f] - set headids($head) $line - lappend idheads($line) $head - } - close $fd - } - } - readotherrefs refs {} {tags heads} -} - -proc readotherrefs {base dname excl} { global otherrefids idotherrefs - set git [gitdir] - set files [glob -nocomplain -types f [file join $git $base *]] - foreach f $files { - catch { - set fd [open $f r] - set line [read $fd 40] - if {[regexp {^[0-9a-f]{40}} $line id]} { - set name "$dname[file tail $f]" - set otherrefids($name) $id - lappend idotherrefs($id) $name + set refd [open [list | git-ls-remote [gitdir]] r] + while {0 <= [set n [gets $refd line]]} { + if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \ + match id path]} { + continue + } + if {![regexp {^(tags|heads)/(.*)$} $path match type name]} { + set type others + set name $path + } + if {$type == "tags"} { + set tagids($name) $id + lappend idtags($id) $name + set obj {} + set type {} + set tag {} + catch { + set commit [exec git-rev-parse "$id^0"] + if {"$commit" != "$id"} { + set tagids($name) $commit + lappend idtags($commit) $name + } + } + catch { + set tagcontents($name) [exec git-cat-file tag "$id"] } - close $fd + } elseif { $type == "heads" } { + set headids($name) $id + lappend idheads($id) $name + } else { + set otherrefids($name) $id + lappend idotherrefs($id) $name } } - set dirs [glob -nocomplain -types d [file join $git $base *]] - foreach d $dirs { - set dir [file tail $d] - if {[lsearch -exact $excl $dir] >= 0} continue - readotherrefs [file join $base $dir] "$dname$dir/" {} - } + close $refd } proc error_popup msg { -- cgit v0.10.2-6-g49f6 From e246483dc5b56d7deb04a93b0aa08aba4cde0625 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Sun, 30 Oct 2005 02:06:46 -0700 Subject: gitk: Specify line hover font Hovering over a line in gitk displays the commit one-liner in a box, but the text usually overflows the box. The box size is computed with a specified font, so this patch sets the text font as well. Signed-off-by: Frank Sorenson Signed-off-by: Junio C Hamano diff --git a/gitk b/gitk index 3ea9434..5deab53 100755 --- a/gitk +++ b/gitk @@ -3125,7 +3125,7 @@ proc linehover {} { set t [$canv create rectangle $x0 $y0 $x1 $y1 \ -fill \#ffff80 -outline black -width 1 -tags hover] $canv raise $t - set t [$canv create text $x $y -anchor nw -text $text -tags hover] + set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont] $canv raise $t } -- cgit v0.10.2-6-g49f6 From fd913b39101c6ff185e76e459479621c9b475fa1 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Fri, 11 Nov 2005 10:09:12 -0500 Subject: gitk: use git-diff-tree --no-commit-id gitk switched to use git-diff-tree with one argument in gettreediffs and getblobdiffs. git-diff-tree with one argument outputs commit ID in from of the patch. This causes an empty line after "Comments" in the lower right pane. Also, the diff in the lower left pane has the commit ID, which is useless there. This patch makes git use the newly added -no-commit-id option for git-diff-tree to suppress commit ID. It also removes the p variable in both functions, since it has become useless after switching to the one-argument invocation for git-diff-tree. Signed-off-by: Pavel Roskin Signed-off-by: Junio C Hamano diff --git a/gitk b/gitk index 5deab53..29220b1 100755 --- a/gitk +++ b/gitk @@ -2787,8 +2787,7 @@ proc gettreediffs {ids} { set treepending $ids set treediff {} set id [lindex $ids 0] - set p [lindex $ids 1] - if [catch {set gdtf [open "|git-diff-tree -r $id" r]}] return + if [catch {set gdtf [open "|git-diff-tree --no-commit-id -r $id" r]}] return fconfigure $gdtf -blocking 0 fileevent $gdtf readable [list gettreediffline $gdtf $ids] } @@ -2822,9 +2821,8 @@ proc getblobdiffs {ids} { global difffilestart nextupdate diffinhdr treediffs set id [lindex $ids 0] - set p [lindex $ids 1] set env(GIT_DIFF_OPTS) $diffopts - set cmd [list | git-diff-tree -r -p -C $id] + set cmd [list | git-diff-tree --no-commit-id -r -p -C $id] if {[catch {set bdf [open $cmd r]} err]} { puts "error getting diffs: $err" return -- cgit v0.10.2-6-g49f6 From e3fe532ddce7311cfbbdc98d2e157d33f6275231 Mon Sep 17 00:00:00 2001 From: "Stefan-W. Hahn" Date: Sat, 5 Nov 2005 20:55:29 +0100 Subject: gitk: moving all three panes if clicking on an arrow. Signed-off-by: Stefan-W. Hahn Signed-off-by: Junio C Hamano diff --git a/gitk b/gitk index 29220b1..ab2d9b8 100755 --- a/gitk +++ b/gitk @@ -3158,7 +3158,7 @@ proc clickisonarrow {id y} { } proc arrowjump {id dirn y} { - global mainline sidelines canv + global mainline sidelines canv canv2 canv3 set yt {} if {$dirn eq "down"} { @@ -3196,6 +3196,8 @@ proc arrowjump {id dirn y} { set yfrac 0 } $canv yview moveto $yfrac + $canv2 yview moveto $yfrac + $canv3 yview moveto $yfrac } proc lineclick {x y id isnew} { -- cgit v0.10.2-6-g49f6 From a4caa5214058ad7e2bc7b74e989d6b3ffb47bec3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 02:54:07 -0800 Subject: git-count-objects: dc replacement Johannes suggested this earlier but I did not take it so seriously because this command is not that important. But this probably matters on Cygwin which does not seem to come with precompiled dc. It is a mystery for me that anything that mimics UNIX does not offer a dc, though. I did the detection for the lack of dc command a bit differently from the verison Johannes did. Signed-off-by: Junio C Hamano diff --git a/git-count-objects.sh b/git-count-objects.sh index 843d2fd..fc61a1a 100755 --- a/git-count-objects.sh +++ b/git-count-objects.sh @@ -1,6 +1,24 @@ #!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# -. git-sh-setup +. git-sh-setup || die "Not a git repository" + +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, \ $({ -- cgit v0.10.2-6-g49f6 From 6ed64058e1241f9939c4abf5d6a9eaed6a2cc795 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 12:13:53 -0800 Subject: git-repack: do not do complex redundancy check. With "-a", redundant pack removal is trivial, and otherwise redundant pack removal is pointless; do not call git-redundant-pack from this script. Signed-off-by: Junio C Hamano diff --git a/git-repack.sh b/git-repack.sh index 55a7b27..4e16d34 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -32,6 +32,10 @@ case ",$all_into_one," in rev_list= rev_parse='--all' pack_objects= + + # Redundancy check in all-into-one case is trivial. + existing=`cd "$PACKDIR" && \ + find . -type f \( -name '*.pack' -o -name '*.idx' \) -print` ;; esac if [ "$local" ]; then @@ -42,14 +46,6 @@ name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) | exit 1 if [ -z "$name" ]; then echo Nothing new to pack. - if test "$remove_redundant" = t ; then - echo "Removing redundant packs." - sync - redundant=$(git-pack-redundant --all) - if test "$redundant" != "" ; then - echo $redundant | xargs rm - fi - fi exit 0 fi echo "Pack pack-$name created." @@ -62,23 +58,20 @@ exit if test "$remove_redundant" = t then - sync - if test "$all_into_one" = t + # We know $existing are all redundant only when + # all-into-one is used. + if test "$all_into_one" != '' && test "$existing" != '' then - cd "$PACKDIR" - existing=`find . -type f \( -name '*.pack' -o -name '*.idx' \) -print` - for e in $existing - do + sync + ( cd "$PACKDIR" && + for e in $existing + do case "$e" in ./pack-$name.pack | ./pack-$name.idx) ;; - *) rm -f $e ;; + *) rm -f $e ;; esac - done - else - redundant=$(git-pack-redundant --all) - if test "$redundant" != "" ; then - echo $redundant | xargs rm - fi + done + ) fi fi -- cgit v0.10.2-6-g49f6 From 22a06b3c474d256e945dbc0d2a830c3f467288b5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 19:21:11 -0800 Subject: Documentation: rebase-from-internal minor updates. git-commit -v flag has been the default for quite some time, so do not mention it. Also a typofix. Signed-off-by: Junio C Hamano diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt index b2c021d..c2d4a91 100644 --- a/Documentation/howto/rebase-from-internal-branch.txt +++ b/Documentation/howto/rebase-from-internal-branch.txt @@ -40,10 +40,7 @@ So I started from master, made a bunch of edits, and committed: $ git checkout master $ cd Documentation; ed git.txt ... $ cd ..; git add Documentation/*.txt - $ git commit -s -v - -NOTE. The -v flag to commit is a handy way to make sure that -your additions are not introducing bogusly formatted lines. + $ git commit -s After the commit, the ancestry graph would look like this: @@ -98,7 +95,7 @@ to do cherrypicking using only the core GIT tools. Let's go back to the earlier picture, with different labels. You, as an individual developer, cloned upstream repository and -amde a couple of commits on top of it. +made a couple of commits on top of it. *your "master" head upstream --> #1 --> #2 --> #3 -- cgit v0.10.2-6-g49f6 From 397c76697f898e0341699fba8ef783f3342329c7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 19:50:44 -0800 Subject: merge-one-file: remove empty directories When the last file in a directory is removed as the result of a merge, try to rmdir the now-empty directory. [jc: We probably could use "rmdir -p", but for now we do that by hand for portability.] Signed-off-by: Junio C Hamano diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index b08597d..b285990 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -25,7 +25,12 @@ case "${1:-.}${2:-.}${3:-.}" in echo "Removing $4" fi if test -f "$4"; then - rm -f -- "$4" + rm -f -- "$4" && + dn="$4" && + while dn=$(expr "$dn" : '\(.*\)/') && rmdir "$dn" 2>/dev/null + do + :; + done fi && exec git-update-index --remove -- "$4" ;; -- cgit v0.10.2-6-g49f6 From 80e21a9ed809d98788ff6fb705d911bee37d460b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 19:50:44 -0800 Subject: merge-recursive::removeFile: remove empty directories When the last file in a directory is removed as the result of a merge, try to rmdir the now-empty directory. Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index d7d36aa..37258ad 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -293,6 +293,10 @@ def removeFile(clean, path): except OSError, e: if e.errno != errno.ENOENT and e.errno != errno.EISDIR: raise + try: + os.removedirs(os.path.dirname(path)) + except: + pass def uniquePath(path, branch): def fileExists(path): -- cgit v0.10.2-6-g49f6 From 29508e1efb8d5a18f78ca0f9be45fdde49ef6b76 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 11:02:58 -0800 Subject: Isolate shared HTTP request functionality Move shared HTTP request functionality out of http-fetch and http-push, and replace the two fwrite_buffer/fwrite_buffer_dynamic functions with one fwrite_buffer function that does dynamic buffering. Use slot callbacks to process responses to fetch object transfer requests and push transfer requests, and put all of http-push into an #ifdef check for curl multi support. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index d8bb375..285b3ed 100644 --- a/Makefile +++ b/Makefile @@ -387,7 +387,8 @@ $(SIMPLE_PROGRAMS) : git-%$X : %.o $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIB_FILE) $(SIMPLE_LIB) -git-http-fetch$X: fetch.o +git-http-fetch$X: fetch.o http.o +git-http-push$X: http.o git-local-fetch$X: fetch.o git-ssh-fetch$X: rsh.o fetch.o git-ssh-upload$X: rsh.o diff --git a/http-fetch.c b/http-fetch.c index 21cc1b9..6a9eaf3 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -2,44 +2,14 @@ #include "commit.h" #include "pack.h" #include "fetch.h" - -#include -#include - -#if LIBCURL_VERSION_NUM >= 0x070908 -#define USE_CURL_MULTI -#define DEFAULT_MAX_REQUESTS 5 -#endif - -#if LIBCURL_VERSION_NUM < 0x070704 -#define curl_global_cleanup() do { /* nothing */ } while(0) -#endif -#if LIBCURL_VERSION_NUM < 0x070800 -#define curl_global_init(a) do { /* nothing */ } while(0) -#endif - -#if LIBCURL_VERSION_NUM < 0x070c04 -#define NO_CURL_EASY_DUPHANDLE -#endif +#include "http.h" #define PREV_BUF_SIZE 4096 #define RANGE_HEADER_SIZE 30 static int got_alternates = -1; -static int active_requests = 0; -static int data_received; -#ifdef USE_CURL_MULTI -static int max_requests = -1; -static CURLM *curlm; -#endif -#ifndef NO_CURL_EASY_DUPHANDLE -static CURL *curl_default; -#endif -static struct curl_slist *pragma_header; static struct curl_slist *no_pragma_header; -static struct curl_slist *no_range_header; -static char curl_errorstr[CURL_ERROR_SIZE]; struct alt_base { @@ -79,19 +49,6 @@ struct transfer_request struct transfer_request *next; }; -struct active_request_slot -{ - CURL *curl; - FILE *local; - int in_use; - int done; - CURLcode curl_result; - long http_code; - void *callback_data; - void (*callback_func)(void *data); - struct active_request_slot *next; -}; - struct alt_request { char *base; char *url; @@ -101,119 +58,6 @@ struct alt_request { }; static struct transfer_request *request_queue_head = NULL; -static struct active_request_slot *active_queue_head = NULL; - -static int curl_ssl_verify = -1; -static char *ssl_cert = NULL; -#if LIBCURL_VERSION_NUM >= 0x070902 -static char *ssl_key = NULL; -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 -static char *ssl_capath = NULL; -#endif -static char *ssl_cainfo = NULL; -static long curl_low_speed_limit = -1; -static long curl_low_speed_time = -1; - -struct buffer -{ - size_t posn; - size_t size; - void *buffer; -}; - -static int http_options(const char *var, const char *value) -{ - if (!strcmp("http.sslverify", var)) { - if (curl_ssl_verify == -1) { - curl_ssl_verify = git_config_bool(var, value); - } - return 0; - } - - if (!strcmp("http.sslcert", var)) { - if (ssl_cert == NULL) { - ssl_cert = xmalloc(strlen(value)+1); - strcpy(ssl_cert, value); - } - return 0; - } -#if LIBCURL_VERSION_NUM >= 0x070902 - if (!strcmp("http.sslkey", var)) { - if (ssl_key == NULL) { - ssl_key = xmalloc(strlen(value)+1); - strcpy(ssl_key, value); - } - return 0; - } -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - if (!strcmp("http.sslcapath", var)) { - if (ssl_capath == NULL) { - ssl_capath = xmalloc(strlen(value)+1); - strcpy(ssl_capath, value); - } - return 0; - } -#endif - if (!strcmp("http.sslcainfo", var)) { - if (ssl_cainfo == NULL) { - ssl_cainfo = xmalloc(strlen(value)+1); - strcpy(ssl_cainfo, value); - } - return 0; - } - -#ifdef USE_CURL_MULTI - if (!strcmp("http.maxrequests", var)) { - if (max_requests == -1) - max_requests = git_config_int(var, value); - return 0; - } -#endif - - if (!strcmp("http.lowspeedlimit", var)) { - if (curl_low_speed_limit == -1) - curl_low_speed_limit = (long)git_config_int(var, value); - return 0; - } - if (!strcmp("http.lowspeedtime", var)) { - if (curl_low_speed_time == -1) - curl_low_speed_time = (long)git_config_int(var, value); - return 0; - } - - /* Fall back on the default ones */ - return git_default_config(var, value); -} - -static size_t fwrite_buffer(void *ptr, size_t eltsize, size_t nmemb, - struct buffer *buffer) -{ - size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) - size = buffer->size - buffer->posn; - memcpy(buffer->buffer + buffer->posn, ptr, size); - buffer->posn += size; - data_received++; - return size; -} - -static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer) -{ - size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) { - buffer->size = buffer->size * 3 / 2; - if (buffer->size < buffer->posn + size) - buffer->size = buffer->posn + size; - buffer->buffer = xrealloc(buffer->buffer, buffer->size); - } - memcpy(buffer->buffer + buffer->posn, ptr, size); - buffer->posn += size; - data_received++; - return size; -} static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, void *data) @@ -243,168 +87,9 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, return size; } -#ifdef USE_CURL_MULTI -static void process_curl_messages(void); -static void process_request_queue(void); -#endif static void fetch_alternates(char *base); -static CURL* get_curl_handle(void) -{ - CURL* result = curl_easy_init(); - - curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); -#if LIBCURL_VERSION_NUM >= 0x070907 - curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); -#endif - - if (ssl_cert != NULL) - curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); -#if LIBCURL_VERSION_NUM >= 0x070902 - if (ssl_key != NULL) - curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - if (ssl_capath != NULL) - curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); -#endif - if (ssl_cainfo != NULL) - curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); - curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); - - if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { - curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, - curl_low_speed_limit); - curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, - curl_low_speed_time); - } - - curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); - - return result; -} - -static struct active_request_slot *get_active_slot(void) -{ - struct active_request_slot *slot = active_queue_head; - struct active_request_slot *newslot; - -#ifdef USE_CURL_MULTI - int num_transfers; - - /* Wait for a slot to open up if the queue is full */ - while (active_requests >= max_requests) { - curl_multi_perform(curlm, &num_transfers); - if (num_transfers < active_requests) { - process_curl_messages(); - } - } -#endif - - while (slot != NULL && slot->in_use) { - slot = slot->next; - } - if (slot == NULL) { - newslot = xmalloc(sizeof(*newslot)); - newslot->curl = NULL; - newslot->in_use = 0; - newslot->next = NULL; - - slot = active_queue_head; - if (slot == NULL) { - active_queue_head = newslot; - } else { - while (slot->next != NULL) { - slot = slot->next; - } - slot->next = newslot; - } - slot = newslot; - } - - if (slot->curl == NULL) { -#ifdef NO_CURL_EASY_DUPHANDLE - slot->curl = get_curl_handle(); -#else - slot->curl = curl_easy_duphandle(curl_default); -#endif - } - - active_requests++; - slot->in_use = 1; - slot->done = 0; - slot->local = NULL; - slot->callback_data = NULL; - slot->callback_func = NULL; - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header); - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header); - curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); - - return slot; -} - -static int start_active_slot(struct active_request_slot *slot) -{ -#ifdef USE_CURL_MULTI - CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); - - if (curlm_result != CURLM_OK && - curlm_result != CURLM_CALL_MULTI_PERFORM) { - active_requests--; - slot->in_use = 0; - return 0; - } -#endif - return 1; -} - -static void run_active_slot(struct active_request_slot *slot) -{ -#ifdef USE_CURL_MULTI - int num_transfers; - long last_pos = 0; - long current_pos; - fd_set readfds; - fd_set writefds; - fd_set excfds; - int max_fd; - struct timeval select_timeout; - CURLMcode curlm_result; - - while (!slot->done) { - data_received = 0; - do { - curlm_result = curl_multi_perform(curlm, - &num_transfers); - } while (curlm_result == CURLM_CALL_MULTI_PERFORM); - if (num_transfers < active_requests) { - process_curl_messages(); - process_request_queue(); - } - - if (!data_received && slot->local != NULL) { - current_pos = ftell(slot->local); - if (current_pos > last_pos) - data_received++; - last_pos = current_pos; - } - - if (!slot->done && !data_received) { - max_fd = 0; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&excfds); - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 50000; - select(max_fd, &readfds, &writefds, - &excfds, &select_timeout); - } - } -#else - slot->curl_result = curl_easy_perform(slot->curl); - active_requests--; -#endif -} +static void process_object_response(void *callback_data); static void start_request(struct transfer_request *request) { @@ -503,6 +188,10 @@ static void start_request(struct transfer_request *request) } slot = get_active_slot(); + slot->callback_func = process_object_response; + slot->callback_data = request; + request->slot = slot; + curl_easy_setopt(slot->curl, CURLOPT_FILE, request); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); @@ -523,15 +212,13 @@ static void start_request(struct transfer_request *request) } /* Try to get the request started, abort the request on error */ + request->state = ACTIVE; if (!start_active_slot(slot)) { request->state = ABORTED; + request->slot = NULL; close(request->local); request->local = -1; free(request->url); - return; } - - request->slot = slot; - request->state = ACTIVE; } static void finish_request(struct transfer_request *request) @@ -567,6 +254,32 @@ static void finish_request(struct transfer_request *request) pull_say("got %s\n", sha1_to_hex(request->sha1)); } +static void process_object_response(void *callback_data) +{ + struct transfer_request *request = + (struct transfer_request *)callback_data; + + request->curl_result = request->slot->curl_result; + request->http_code = request->slot->http_code; + request->slot = NULL; + request->state = COMPLETE; + + /* Use alternates if necessary */ + if (request->http_code == 404) { + fetch_alternates(alt->base); + if (request->repo->next != NULL) { + request->repo = + request->repo->next; + close(request->local); + request->local = -1; + start_request(request); + return; + } + } + + finish_request(request); +} + static void release_request(struct transfer_request *request) { struct transfer_request *entry = request_queue_head; @@ -587,73 +300,7 @@ static void release_request(struct transfer_request *request) } #ifdef USE_CURL_MULTI -static void process_curl_messages(void) -{ - int num_messages; - struct active_request_slot *slot; - struct transfer_request *request = NULL; - CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); - - while (curl_message != NULL) { - if (curl_message->msg == CURLMSG_DONE) { - int curl_result = curl_message->data.result; - slot = active_queue_head; - while (slot != NULL && - slot->curl != curl_message->easy_handle) - slot = slot->next; - if (slot != NULL) { - curl_multi_remove_handle(curlm, slot->curl); - active_requests--; - slot->done = 1; - slot->in_use = 0; - slot->curl_result = curl_result; - curl_easy_getinfo(slot->curl, - CURLINFO_HTTP_CODE, - &slot->http_code); - request = request_queue_head; - while (request != NULL && - request->slot != slot) - request = request->next; - } else { - fprintf(stderr, "Received DONE message for unknown request!\n"); - } - - /* Process slot callback if appropriate */ - if (slot->callback_func != NULL) { - slot->callback_func(slot->callback_data); - } - - if (request != NULL) { - request->curl_result = curl_result; - request->http_code = slot->http_code; - request->slot = NULL; - request->state = COMPLETE; - - /* Use alternates if necessary */ - if (request->http_code == 404) { - fetch_alternates(alt->base); - if (request->repo->next != NULL) { - request->repo = - request->repo->next; - close(request->local); - request->local = -1; - start_request(request); - } else { - finish_request(request); - } - } else { - finish_request(request); - } - } - } else { - fprintf(stderr, "Unknown CURL message received: %d\n", - (int)curl_message->msg); - } - curl_message = curl_multi_info_read(curlm, &num_messages); - } -} - -static void process_request_queue(void) +void fill_active_slots(void) { struct transfer_request *request = request_queue_head; struct active_request_slot *slot = active_queue_head; @@ -706,9 +353,10 @@ void prefetch(unsigned char *sha1) } tail->next = newreq; } + #ifdef USE_CURL_MULTI - process_request_queue(); - process_curl_messages(); + fill_active_slots(); + step_active_slots(); #endif } @@ -815,12 +463,11 @@ static void process_alternates(void *callback_data) alt_req->url); active_requests++; slot->in_use = 1; - slot->done = 0; if (start_active_slot(slot)) { return; } else { got_alternates = -1; - slot->done = 1; + slot->in_use = 0; return; } } @@ -831,7 +478,7 @@ static void process_alternates(void *callback_data) } } - fwrite_buffer_dynamic(&null_byte, 1, 1, alt_req->buffer); + fwrite_buffer(&null_byte, 1, 1, alt_req->buffer); alt_req->buffer->posn--; data = alt_req->buffer->buffer; @@ -902,16 +549,15 @@ static void fetch_alternates(char *base) char *data; struct active_request_slot *slot; static struct alt_request alt_req; - int num_transfers; /* If another request has already started fetching alternates, wait for them to arrive and return to processing this request's curl message */ +#ifdef USE_CURL_MULTI while (got_alternates == 0) { - curl_multi_perform(curlm, &num_transfers); - process_curl_messages(); - process_request_queue(); + step_active_slots(); } +#endif /* Nothing to do if they've already been fetched */ if (got_alternates == 1) @@ -938,8 +584,7 @@ static void fetch_alternates(char *base) slot->callback_data = &alt_req; curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); alt_req.base = base; @@ -983,8 +628,7 @@ static int fetch_indices(struct alt_base *repo) slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); if (start_active_slot(slot)) { @@ -1115,7 +759,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1) static int fetch_object(struct alt_base *repo, unsigned char *sha1) { char *hex = sha1_to_hex(sha1); - int ret; + int ret = 0; struct transfer_request *request = request_queue_head; while (request != NULL && memcmp(request->sha1, sha1, 20)) @@ -1130,12 +774,7 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) #ifdef USE_CURL_MULTI while (request->state == WAITING) { - int num_transfers; - curl_multi_perform(curlm, &num_transfers); - if (num_transfers < active_requests) { - process_curl_messages(); - process_request_queue(); - } + step_active_slots(); } #else start_request(request); @@ -1143,66 +782,33 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) while (request->state == ACTIVE) { run_active_slot(request->slot); -#ifndef USE_CURL_MULTI - request->curl_result = request->slot->curl_result; - request->http_code = request->slot->http_code; - request->slot = NULL; - - /* Use alternates if necessary */ - if (request->http_code == 404) { - fetch_alternates(alt->base); - if (request->repo->next != NULL) { - request->repo = request->repo->next; - close(request->local); request->local = -1; - start_request(request); - } - } else { - finish_request(request); - request->state = COMPLETE; - } -#endif } if (request->local != -1) { close(request->local); request->local = -1; } if (request->state == ABORTED) { - release_request(request); - return error("Request for %s aborted", hex); - } - - if (request->curl_result != CURLE_OK && request->http_code != 416) { + ret = error("Request for %s aborted", hex); + } else if (request->curl_result != CURLE_OK && + request->http_code != 416) { if (request->http_code == 404) ret = -1; /* Be silent, it is probably in a pack. */ else ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", request->errorstr, request->curl_result, request->http_code, hex); - release_request(request); - return ret; - } - - if (request->zret != Z_STREAM_END) { + } else if (request->zret != Z_STREAM_END) { ret = error("File %s (%s) corrupt\n", hex, request->url); - release_request(request); - return ret; - } - - if (memcmp(request->sha1, request->real_sha1, 20)) { - release_request(request); - return error("File %s has bad hash\n", hex); - } - - if (request->rename < 0) { + } else if (memcmp(request->sha1, request->real_sha1, 20)) { + ret = error("File %s has bad hash\n", hex); + } else if (request->rename < 0) { ret = error("unable to write sha1 filename %s: %s", request->filename, strerror(request->rename)); - release_request(request); - return ret; } release_request(request); - return 0; + return ret; } int fetch(unsigned char *sha1) @@ -1303,10 +909,6 @@ int main(int argc, char **argv) char *commit_id; char *url; int arg = 1; - struct active_request_slot *slot; - char *low_speed_limit; - char *low_speed_time; - char *wait_url; int rc = 0; while (arg < argc && argv[arg][0] == '-') { @@ -1335,58 +937,9 @@ int main(int argc, char **argv) commit_id = argv[arg]; url = argv[arg + 1]; - curl_global_init(CURL_GLOBAL_ALL); - -#ifdef USE_CURL_MULTI - { - char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); - if (http_max_requests != NULL) - max_requests = atoi(http_max_requests); - } - - curlm = curl_multi_init(); - if (curlm == NULL) { - fprintf(stderr, "Error creating curl multi handle.\n"); - return 1; - } -#endif - - if (getenv("GIT_SSL_NO_VERIFY")) - curl_ssl_verify = 0; - - ssl_cert = getenv("GIT_SSL_CERT"); -#if LIBCURL_VERSION_NUM >= 0x070902 - ssl_key = getenv("GIT_SSL_KEY"); -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - ssl_capath = getenv("GIT_SSL_CAPATH"); -#endif - ssl_cainfo = getenv("GIT_SSL_CAINFO"); - - low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); - if (low_speed_limit != NULL) - curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); - low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); - if (low_speed_time != NULL) - curl_low_speed_time = strtol(low_speed_time, NULL, 10); - - git_config(http_options); - - if (curl_ssl_verify == -1) - curl_ssl_verify = 1; - -#ifdef USE_CURL_MULTI - if (max_requests < 1) - max_requests = DEFAULT_MAX_REQUESTS; -#endif + http_init(); - pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache"); no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); - no_range_header = curl_slist_append(no_range_header, "Range:"); - -#ifndef NO_CURL_EASY_DUPHANDLE - curl_default = get_curl_handle(); -#endif alt = xmalloc(sizeof(*alt)); alt->base = url; @@ -1397,30 +950,9 @@ int main(int argc, char **argv) if (pull(commit_id)) rc = 1; - curl_slist_free_all(pragma_header); curl_slist_free_all(no_pragma_header); - curl_slist_free_all(no_range_header); -#ifndef NO_CURL_EASY_DUPHANDLE - curl_easy_cleanup(curl_default); -#endif - slot = active_queue_head; - while (slot != NULL) { - if (slot->in_use) { - if (get_verbosely) { - curl_easy_getinfo(slot->curl, - CURLINFO_EFFECTIVE_URL, - &wait_url); - fprintf(stderr, "Waiting for %s\n", wait_url); - } - run_active_slot(slot); - } - if (slot->curl != NULL) - curl_easy_cleanup(slot->curl); - slot = slot->next; - } -#ifdef USE_CURL_MULTI - curl_multi_cleanup(curlm); -#endif - curl_global_cleanup(); + + http_cleanup(); + return rc; } diff --git a/http-push.c b/http-push.c index 8866189..afdcd8b 100644 --- a/http-push.c +++ b/http-push.c @@ -4,30 +4,15 @@ #include "fetch.h" #include "tag.h" #include "blob.h" +#include "http.h" + +#ifdef USE_CURL_MULTI -#include -#include #include static const char http_push_usage[] = "git-http-push [--complete] [--force] [--verbose] [...]\n"; -#if LIBCURL_VERSION_NUM >= 0x070908 -#define USE_CURL_MULTI -#define DEFAULT_MAX_REQUESTS 5 -#endif - -#if LIBCURL_VERSION_NUM < 0x070704 -#define curl_global_cleanup() do { /* nothing */ } while(0) -#endif -#if LIBCURL_VERSION_NUM < 0x070800 -#define curl_global_init(a) do { /* nothing */ } while(0) -#endif - -#if LIBCURL_VERSION_NUM < 0x070c04 -#define NO_CURL_EASY_DUPHANDLE -#endif - #ifndef XML_STATUS_OK enum XML_Status { XML_STATUS_OK = 1, @@ -52,34 +37,17 @@ enum XML_Status { #define LOCK_TIME 600 #define LOCK_REFRESH 30 -static int active_requests = 0; -static int data_received; static int pushing = 0; static int aborted = 0; static char remote_dir_exists[256]; -#ifdef USE_CURL_MULTI -static int max_requests = -1; -static CURLM *curlm; -#endif -#ifndef NO_CURL_EASY_DUPHANDLE -static CURL *curl_default; -#endif static struct curl_slist *no_pragma_header; static struct curl_slist *default_headers; -static char curl_errorstr[CURL_ERROR_SIZE]; static int push_verbosely = 0; static int push_all = 0; static int force_all = 0; -struct buffer -{ - size_t posn; - size_t size; - void *buffer; -}; - struct repo { char *url; @@ -122,31 +90,7 @@ struct transfer_request struct transfer_request *next; }; -struct active_request_slot -{ - CURL *curl; - FILE *local; - int in_use; - int done; - CURLcode curl_result; - long http_code; - struct active_request_slot *next; -}; - static struct transfer_request *request_queue_head = NULL; -static struct active_request_slot *active_queue_head = NULL; - -static int curl_ssl_verify = -1; -static char *ssl_cert = NULL; -#if LIBCURL_VERSION_NUM >= 0x070902 -static char *ssl_key = NULL; -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 -static char *ssl_capath = NULL; -#endif -static char *ssl_cainfo = NULL; -static long curl_low_speed_limit = -1; -static long curl_low_speed_time = -1; struct active_lock { @@ -174,260 +118,14 @@ struct lockprop int lock_exclusive_write; }; -static int http_options(const char *var, const char *value) -{ - if (!strcmp("http.sslverify", var)) { - if (curl_ssl_verify == -1) { - curl_ssl_verify = git_config_bool(var, value); - } - return 0; - } +static void finish_request(struct transfer_request *request); - if (!strcmp("http.sslcert", var)) { - if (ssl_cert == NULL) { - ssl_cert = xmalloc(strlen(value)+1); - strcpy(ssl_cert, value); - } - return 0; - } -#if LIBCURL_VERSION_NUM >= 0x070902 - if (!strcmp("http.sslkey", var)) { - if (ssl_key == NULL) { - ssl_key = xmalloc(strlen(value)+1); - strcpy(ssl_key, value); - } - return 0; - } -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - if (!strcmp("http.sslcapath", var)) { - if (ssl_capath == NULL) { - ssl_capath = xmalloc(strlen(value)+1); - strcpy(ssl_capath, value); - } - return 0; - } -#endif - if (!strcmp("http.sslcainfo", var)) { - if (ssl_cainfo == NULL) { - ssl_cainfo = xmalloc(strlen(value)+1); - strcpy(ssl_cainfo, value); - } - return 0; - } - -#ifdef USE_CURL_MULTI - if (!strcmp("http.maxrequests", var)) { - if (max_requests == -1) - max_requests = git_config_int(var, value); - return 0; - } -#endif - - if (!strcmp("http.lowspeedlimit", var)) { - if (curl_low_speed_limit == -1) - curl_low_speed_limit = (long)git_config_int(var, value); - return 0; - } - if (!strcmp("http.lowspeedtime", var)) { - if (curl_low_speed_time == -1) - curl_low_speed_time = (long)git_config_int(var, value); - return 0; - } - - /* Fall back on the default ones */ - return git_default_config(var, value); -} - -static size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, - struct buffer *buffer) -{ - size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) - size = buffer->size - buffer->posn; - memcpy(ptr, buffer->buffer + buffer->posn, size); - buffer->posn += size; - return size; -} - -static size_t fwrite_buffer_dynamic(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer) -{ - size_t size = eltsize * nmemb; - if (size > buffer->size - buffer->posn) { - buffer->size = buffer->size * 3 / 2; - if (buffer->size < buffer->posn + size) - buffer->size = buffer->posn + size; - buffer->buffer = xrealloc(buffer->buffer, buffer->size); - } - memcpy(buffer->buffer + buffer->posn, ptr, size); - buffer->posn += size; - data_received++; - return size; -} - -static size_t fwrite_null(const void *ptr, size_t eltsize, - size_t nmemb, struct buffer *buffer) -{ - data_received++; - return eltsize * nmemb; -} - -#ifdef USE_CURL_MULTI -static void process_curl_messages(void); -static void process_request_queue(void); -#endif - -static CURL* get_curl_handle(void) +static void process_response(void *callback_data) { - CURL* result = curl_easy_init(); - - curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); -#if LIBCURL_VERSION_NUM >= 0x070907 - curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); -#endif + struct transfer_request *request = + (struct transfer_request *)callback_data; - if (ssl_cert != NULL) - curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); -#if LIBCURL_VERSION_NUM >= 0x070902 - if (ssl_key != NULL) - curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - if (ssl_capath != NULL) - curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); -#endif - if (ssl_cainfo != NULL) - curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); - curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); - - if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { - curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, - curl_low_speed_limit); - curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, - curl_low_speed_time); - } - - return result; -} - -static struct active_request_slot *get_active_slot(void) -{ - struct active_request_slot *slot = active_queue_head; - struct active_request_slot *newslot; - -#ifdef USE_CURL_MULTI - int num_transfers; - - /* Wait for a slot to open up if the queue is full */ - while (active_requests >= max_requests) { - curl_multi_perform(curlm, &num_transfers); - if (num_transfers < active_requests) { - process_curl_messages(); - } - } -#endif - - while (slot != NULL && slot->in_use) { - slot = slot->next; - } - if (slot == NULL) { - newslot = xmalloc(sizeof(*newslot)); - newslot->curl = NULL; - newslot->in_use = 0; - newslot->next = NULL; - - slot = active_queue_head; - if (slot == NULL) { - active_queue_head = newslot; - } else { - while (slot->next != NULL) { - slot = slot->next; - } - slot->next = newslot; - } - slot = newslot; - } - - if (slot->curl == NULL) { -#ifdef NO_CURL_EASY_DUPHANDLE - slot->curl = get_curl_handle(); -#else - slot->curl = curl_easy_duphandle(curl_default); -#endif - } - - active_requests++; - slot->in_use = 1; - slot->done = 0; - slot->local = NULL; - curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, default_headers); - curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); - - return slot; -} - -static int start_active_slot(struct active_request_slot *slot) -{ -#ifdef USE_CURL_MULTI - CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); - - if (curlm_result != CURLM_OK && - curlm_result != CURLM_CALL_MULTI_PERFORM) { - active_requests--; - slot->in_use = 0; - return 0; - } -#endif - return 1; -} - -static void run_active_slot(struct active_request_slot *slot) -{ -#ifdef USE_CURL_MULTI - int num_transfers; - long last_pos = 0; - long current_pos; - fd_set readfds; - fd_set writefds; - fd_set excfds; - int max_fd; - struct timeval select_timeout; - CURLMcode curlm_result; - - while (!slot->done) { - data_received = 0; - do { - curlm_result = curl_multi_perform(curlm, - &num_transfers); - } while (curlm_result == CURLM_CALL_MULTI_PERFORM); - if (num_transfers < active_requests) { - process_curl_messages(); - process_request_queue(); - } - - if (!data_received && slot->local != NULL) { - current_pos = ftell(slot->local); - if (current_pos > last_pos) - data_received++; - last_pos = current_pos; - } - - if (!slot->done && !data_received) { - max_fd = 0; - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&excfds); - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 50000; - select(max_fd, &readfds, &writefds, - &excfds, &select_timeout); - } - } -#else - slot->curl_result = curl_easy_perform(slot->curl); - active_requests--; -#endif + finish_request(request); } static void start_check(struct transfer_request *request) @@ -447,6 +145,8 @@ static void start_check(struct transfer_request *request) strcpy(posn, hex + 2); slot = get_active_slot(); + slot->callback_func = process_response; + slot->callback_data = request; curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1); @@ -476,6 +176,8 @@ static void start_mkcol(struct transfer_request *request) strcpy(posn, "/"); slot = get_active_slot(); + slot->callback_func = process_response; + slot->callback_data = request; curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ curl_easy_setopt(slot->curl, CURLOPT_URL, request->url); curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); @@ -553,6 +255,8 @@ static void start_put(struct transfer_request *request) strcpy(posn, request->lock->token); slot = get_active_slot(); + slot->callback_func = process_response; + slot->callback_data = request; curl_easy_setopt(slot->curl, CURLOPT_INFILE, &request->buffer); curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, request->buffer.size); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); @@ -578,6 +282,8 @@ static void start_move(struct transfer_request *request) struct curl_slist *dav_headers = NULL; slot = get_active_slot(); + slot->callback_func = process_response; + slot->callback_data = request; curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1); /* undo PUT setup */ curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_MOVE); dav_headers = curl_slist_append(dav_headers, request->dest); @@ -725,48 +431,7 @@ static void release_request(struct transfer_request *request) free(request); } -#ifdef USE_CURL_MULTI -static void process_curl_messages(void) -{ - int num_messages; - struct active_request_slot *slot; - struct transfer_request *request = NULL; - CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); - - while (curl_message != NULL) { - if (curl_message->msg == CURLMSG_DONE) { - slot = active_queue_head; - while (slot != NULL && - slot->curl != curl_message->easy_handle) - slot = slot->next; - if (slot != NULL) { - int curl_result = curl_message->data.result; - curl_multi_remove_handle(curlm, slot->curl); - active_requests--; - slot->done = 1; - slot->in_use = 0; - slot->curl_result = curl_result; - curl_easy_getinfo(slot->curl, - CURLINFO_HTTP_CODE, - &slot->http_code); - request = request_queue_head; - while (request != NULL && - request->slot != slot) - request = request->next; - if (request != NULL) - finish_request(request); - } else { - fprintf(stderr, "Received DONE message for unknown request!\n"); - } - } else { - fprintf(stderr, "Unknown CURL message received: %d\n", - (int)curl_message->msg); - } - curl_message = curl_multi_info_read(curlm, &num_messages); - } -} - -static void process_request_queue(void) +void fill_active_slots(void) { struct transfer_request *request = request_queue_head; struct active_request_slot *slot = active_queue_head; @@ -797,20 +462,6 @@ static void process_request_queue(void) slot = slot->next; } } -#endif - -static void process_waiting_requests(void) -{ - struct active_request_slot *slot = active_queue_head; - - while (slot != NULL) - if (slot->in_use) { - run_active_slot(slot); - slot = active_queue_head; - } else { - slot = slot->next; - } -} static void add_request(unsigned char *sha1, struct active_lock *lock) { @@ -834,10 +485,9 @@ static void add_request(unsigned char *sha1, struct active_lock *lock) request->state = NEED_CHECK; request->next = request_queue_head; request_queue_head = request; -#ifdef USE_CURL_MULTI - process_request_queue(); - process_curl_messages(); -#endif + + fill_active_slots(); + step_active_slots(); } static int fetch_index(unsigned char *sha1) @@ -963,8 +613,7 @@ static int fetch_indices(void) slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); if (start_active_slot(slot)) { @@ -1068,8 +717,7 @@ int fetch_ref(char *ref, unsigned char *sha1) url = quote_ref_url(base, ref); slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL); curl_easy_setopt(slot->curl, CURLOPT_URL, url); if (start_active_slot(slot)) { @@ -1261,8 +909,7 @@ static struct active_lock *lock_remote(char *file, long timeout) curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); @@ -1390,8 +1037,7 @@ static int check_locking(void) curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer); curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer); - curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, - fwrite_buffer_dynamic); + curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer); curl_easy_setopt(slot->curl, CURLOPT_URL, remote->url); curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND); @@ -1560,8 +1206,6 @@ static int update_remote(unsigned char *sha1, struct active_lock *lock) int main(int argc, char **argv) { - struct active_request_slot *slot; - struct active_request_slot *next_slot; struct transfer_request *request; struct transfer_request *next_request; int nr_refspec = 0; @@ -1576,8 +1220,6 @@ int main(int argc, char **argv) unsigned char remote_sha1[20]; struct active_lock *remote_lock; char *remote_path = NULL; - char *low_speed_limit; - char *low_speed_time; int rc = 0; int i; @@ -1617,50 +1259,7 @@ int main(int argc, char **argv) memset(remote_dir_exists, 0, 256); - curl_global_init(CURL_GLOBAL_ALL); - -#ifdef USE_CURL_MULTI - { - char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); - if (http_max_requests != NULL) - max_requests = atoi(http_max_requests); - } - - curlm = curl_multi_init(); - if (curlm == NULL) { - fprintf(stderr, "Error creating curl multi handle.\n"); - return 1; - } -#endif - - if (getenv("GIT_SSL_NO_VERIFY")) - curl_ssl_verify = 0; - - ssl_cert = getenv("GIT_SSL_CERT"); -#if LIBCURL_VERSION_NUM >= 0x070902 - ssl_key = getenv("GIT_SSL_KEY"); -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - ssl_capath = getenv("GIT_SSL_CAPATH"); -#endif - ssl_cainfo = getenv("GIT_SSL_CAINFO"); - - low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); - if (low_speed_limit != NULL) - curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); - low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); - if (low_speed_time != NULL) - curl_low_speed_time = strtol(low_speed_time, NULL, 10); - - git_config(http_options); - - if (curl_ssl_verify == -1) - curl_ssl_verify = 1; - -#ifdef USE_CURL_MULTI - if (max_requests < 1) - max_requests = DEFAULT_MAX_REQUESTS; -#endif + http_init(); no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:"); default_headers = curl_slist_append(default_headers, "Range:"); @@ -1669,10 +1268,6 @@ int main(int argc, char **argv) default_headers = curl_slist_append(default_headers, "Pragma: no-cache"); -#ifndef NO_CURL_EASY_DUPHANDLE - curl_default = get_curl_handle(); -#endif - /* Verify DAV compliance/lock support */ if (check_locking() != 0) { fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url); @@ -1766,13 +1361,13 @@ int main(int argc, char **argv) fetch_indices(); get_delta(push_all ? NULL : remote_sha1, local_object, remote_lock); - process_waiting_requests(); + finish_all_active_slots(); /* Push missing objects to remote, this would be a convenient time to pack them first if appropriate. */ pushing = 1; - process_request_queue(); - process_waiting_requests(); + fill_active_slots(); + finish_all_active_slots(); /* Update the remote branch if all went well */ if (do_remote_update) { @@ -1802,14 +1397,7 @@ int main(int argc, char **argv) curl_slist_free_all(no_pragma_header); curl_slist_free_all(default_headers); - slot = active_queue_head; - while (slot != NULL) { - next_slot = slot->next; - if (slot->curl != NULL) - curl_easy_cleanup(slot->curl); - free(slot); - slot = next_slot; - } + http_cleanup(); request = request_queue_head; while (request != NULL) { @@ -1818,12 +1406,12 @@ int main(int argc, char **argv) request = next_request; } -#ifndef NO_CURL_EASY_DUPHANDLE - curl_easy_cleanup(curl_default); -#endif -#ifdef USE_CURL_MULTI - curl_multi_cleanup(curlm); -#endif - curl_global_cleanup(); return rc; } +#else /* ifdef USE_CURL_MULTI */ +int main(int argc, char **argv) +{ + fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n"); + return 1; +} +#endif diff --git a/http.c b/http.c new file mode 100644 index 0000000..470b56e --- /dev/null +++ b/http.c @@ -0,0 +1,443 @@ +#include "http.h" + +int data_received; +int active_requests = 0; + +#ifdef USE_CURL_MULTI +int max_requests = -1; +CURLM *curlm; +#endif +#ifndef NO_CURL_EASY_DUPHANDLE +CURL *curl_default; +#endif +char curl_errorstr[CURL_ERROR_SIZE]; + +int curl_ssl_verify = -1; +char *ssl_cert = NULL; +#if LIBCURL_VERSION_NUM >= 0x070902 +char *ssl_key = NULL; +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 +char *ssl_capath = NULL; +#endif +char *ssl_cainfo = NULL; +long curl_low_speed_limit = -1; +long curl_low_speed_time = -1; + +struct curl_slist *pragma_header; +struct curl_slist *no_range_header; + +struct active_request_slot *active_queue_head = NULL; + +size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, + struct buffer *buffer) +{ + size_t size = eltsize * nmemb; + if (size > buffer->size - buffer->posn) + size = buffer->size - buffer->posn; + memcpy(ptr, buffer->buffer + buffer->posn, size); + buffer->posn += size; + return size; +} + +size_t fwrite_buffer(const void *ptr, size_t eltsize, + size_t nmemb, struct buffer *buffer) +{ + size_t size = eltsize * nmemb; + if (size > buffer->size - buffer->posn) { + buffer->size = buffer->size * 3 / 2; + if (buffer->size < buffer->posn + size) + buffer->size = buffer->posn + size; + buffer->buffer = xrealloc(buffer->buffer, buffer->size); + } + memcpy(buffer->buffer + buffer->posn, ptr, size); + buffer->posn += size; + data_received++; + return size; +} + +size_t fwrite_null(const void *ptr, size_t eltsize, + size_t nmemb, struct buffer *buffer) +{ + data_received++; + return eltsize * nmemb; +} + +static void finish_active_slot(struct active_request_slot *slot); + +#ifdef USE_CURL_MULTI +static void process_curl_messages(void) +{ + int num_messages; + struct active_request_slot *slot; + CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages); + + while (curl_message != NULL) { + if (curl_message->msg == CURLMSG_DONE) { + int curl_result = curl_message->data.result; + slot = active_queue_head; + while (slot != NULL && + slot->curl != curl_message->easy_handle) + slot = slot->next; + if (slot != NULL) { + curl_multi_remove_handle(curlm, slot->curl); + slot->curl_result = curl_result; + finish_active_slot(slot); + } else { + fprintf(stderr, "Received DONE message for unknown request!\n"); + } + } else { + fprintf(stderr, "Unknown CURL message received: %d\n", + (int)curl_message->msg); + } + curl_message = curl_multi_info_read(curlm, &num_messages); + } +} +#endif + +static int http_options(const char *var, const char *value) +{ + if (!strcmp("http.sslverify", var)) { + if (curl_ssl_verify == -1) { + curl_ssl_verify = git_config_bool(var, value); + } + return 0; + } + + if (!strcmp("http.sslcert", var)) { + if (ssl_cert == NULL) { + ssl_cert = xmalloc(strlen(value)+1); + strcpy(ssl_cert, value); + } + return 0; + } +#if LIBCURL_VERSION_NUM >= 0x070902 + if (!strcmp("http.sslkey", var)) { + if (ssl_key == NULL) { + ssl_key = xmalloc(strlen(value)+1); + strcpy(ssl_key, value); + } + return 0; + } +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 + if (!strcmp("http.sslcapath", var)) { + if (ssl_capath == NULL) { + ssl_capath = xmalloc(strlen(value)+1); + strcpy(ssl_capath, value); + } + return 0; + } +#endif + if (!strcmp("http.sslcainfo", var)) { + if (ssl_cainfo == NULL) { + ssl_cainfo = xmalloc(strlen(value)+1); + strcpy(ssl_cainfo, value); + } + return 0; + } + +#ifdef USE_CURL_MULTI + if (!strcmp("http.maxrequests", var)) { + if (max_requests == -1) + max_requests = git_config_int(var, value); + return 0; + } +#endif + + if (!strcmp("http.lowspeedlimit", var)) { + if (curl_low_speed_limit == -1) + curl_low_speed_limit = (long)git_config_int(var, value); + return 0; + } + if (!strcmp("http.lowspeedtime", var)) { + if (curl_low_speed_time == -1) + curl_low_speed_time = (long)git_config_int(var, value); + return 0; + } + + /* Fall back on the default ones */ + return git_default_config(var, value); +} + +void http_init(void) +{ + char *low_speed_limit; + char *low_speed_time; + + curl_global_init(CURL_GLOBAL_ALL); + + pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache"); + no_range_header = curl_slist_append(no_range_header, "Range:"); + +#ifdef USE_CURL_MULTI + { + char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS"); + if (http_max_requests != NULL) + max_requests = atoi(http_max_requests); + } + + curlm = curl_multi_init(); + if (curlm == NULL) { + fprintf(stderr, "Error creating curl multi handle.\n"); + exit(1); + } +#endif + + if (getenv("GIT_SSL_NO_VERIFY")) + curl_ssl_verify = 0; + + ssl_cert = getenv("GIT_SSL_CERT"); +#if LIBCURL_VERSION_NUM >= 0x070902 + ssl_key = getenv("GIT_SSL_KEY"); +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 + ssl_capath = getenv("GIT_SSL_CAPATH"); +#endif + ssl_cainfo = getenv("GIT_SSL_CAINFO"); + + low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT"); + if (low_speed_limit != NULL) + curl_low_speed_limit = strtol(low_speed_limit, NULL, 10); + low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME"); + if (low_speed_time != NULL) + curl_low_speed_time = strtol(low_speed_time, NULL, 10); + + git_config(http_options); + + if (curl_ssl_verify == -1) + curl_ssl_verify = 1; + +#ifdef USE_CURL_MULTI + if (max_requests < 1) + max_requests = DEFAULT_MAX_REQUESTS; +#endif + +#ifndef NO_CURL_EASY_DUPHANDLE + curl_default = get_curl_handle(); +#endif +} + +void http_cleanup(void) +{ + struct active_request_slot *slot = active_queue_head; +#ifdef USE_CURL_MULTI + char *wait_url; + CURLMcode curlm_result; +#endif + + while (slot != NULL) { +#ifdef USE_CURL_MULTI + if (slot->in_use) { + curl_easy_getinfo(slot->curl, + CURLINFO_EFFECTIVE_URL, + &wait_url); + fprintf(stderr, "Waiting for %s\n", wait_url); + run_active_slot(slot); + } +#endif + if (slot->curl != NULL) + curl_easy_cleanup(slot->curl); + slot = slot->next; + } + +#ifndef NO_CURL_EASY_DUPHANDLE + curl_easy_cleanup(curl_default); +#endif + +#ifdef USE_CURL_MULTI + curl_multi_cleanup(curlm); +#endif + curl_global_cleanup(); + +} + +static CURL* get_curl_handle(void) +{ + CURL* result = curl_easy_init(); + + curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); +#if LIBCURL_VERSION_NUM >= 0x070907 + curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); +#endif + + if (ssl_cert != NULL) + curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); +#if LIBCURL_VERSION_NUM >= 0x070902 + if (ssl_key != NULL) + curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 + if (ssl_capath != NULL) + curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); +#endif + if (ssl_cainfo != NULL) + curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); + curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); + + if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { + curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, + curl_low_speed_limit); + curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, + curl_low_speed_time); + } + + curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); + + return result; +} + +struct active_request_slot *get_active_slot(void) +{ + struct active_request_slot *slot = active_queue_head; + struct active_request_slot *newslot; + +#ifdef USE_CURL_MULTI + int num_transfers; + + /* Wait for a slot to open up if the queue is full */ + while (active_requests >= max_requests) { + curl_multi_perform(curlm, &num_transfers); + if (num_transfers < active_requests) { + process_curl_messages(); + } + } +#endif + + while (slot != NULL && slot->in_use) { + slot = slot->next; + } + if (slot == NULL) { + newslot = xmalloc(sizeof(*newslot)); + newslot->curl = NULL; + newslot->in_use = 0; + newslot->next = NULL; + + slot = active_queue_head; + if (slot == NULL) { + active_queue_head = newslot; + } else { + while (slot->next != NULL) { + slot = slot->next; + } + slot->next = newslot; + } + slot = newslot; + } + + if (slot->curl == NULL) { +#ifdef NO_CURL_EASY_DUPHANDLE + slot->curl = get_curl_handle(); +#else + slot->curl = curl_easy_duphandle(curl_default); +#endif + } + + active_requests++; + slot->in_use = 1; + slot->local = NULL; + slot->callback_data = NULL; + slot->callback_func = NULL; + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header); + curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_range_header); + curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); + + return slot; +} + +int start_active_slot(struct active_request_slot *slot) +{ +#ifdef USE_CURL_MULTI + CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl); + + if (curlm_result != CURLM_OK && + curlm_result != CURLM_CALL_MULTI_PERFORM) { + active_requests--; + slot->in_use = 0; + return 0; + } +#endif + return 1; +} + +#ifdef USE_CURL_MULTI +void step_active_slots(void) +{ + int num_transfers; + CURLMcode curlm_result; + + do { + curlm_result = curl_multi_perform(curlm, &num_transfers); + } while (curlm_result == CURLM_CALL_MULTI_PERFORM); + if (num_transfers < active_requests) { + process_curl_messages(); + fill_active_slots(); + } +} +#endif + +void run_active_slot(struct active_request_slot *slot) +{ +#ifdef USE_CURL_MULTI + long last_pos = 0; + long current_pos; + fd_set readfds; + fd_set writefds; + fd_set excfds; + int max_fd; + struct timeval select_timeout; + + while (slot->in_use) { + data_received = 0; + step_active_slots(); + + if (!data_received && slot->local != NULL) { + current_pos = ftell(slot->local); + if (current_pos > last_pos) + data_received++; + last_pos = current_pos; + } + + if (slot->in_use && !data_received) { + max_fd = 0; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&excfds); + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 50000; + select(max_fd, &readfds, &writefds, + &excfds, &select_timeout); + } + } +#else + while (slot->in_use) { + slot->curl_result = curl_easy_perform(slot->curl); + finish_active_slot(slot); + } +#endif +} + +static void finish_active_slot(struct active_request_slot *slot) +{ + active_requests--; + slot->in_use = 0; + curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code); + + /* Run callback if appropriate */ + if (slot->callback_func != NULL) { + slot->callback_func(slot->callback_data); + } +} + +void finish_all_active_slots(void) +{ + struct active_request_slot *slot = active_queue_head; + + while (slot != NULL) + if (slot->in_use) { + run_active_slot(slot); + slot = active_queue_head; + } else { + slot = slot->next; + } +} diff --git a/http.h b/http.h new file mode 100644 index 0000000..ed4ea33 --- /dev/null +++ b/http.h @@ -0,0 +1,95 @@ +#ifndef HTTP_H +#define HTTP_H + +#include "cache.h" + +#include +#include + +#if LIBCURL_VERSION_NUM >= 0x070908 +#define USE_CURL_MULTI +#define DEFAULT_MAX_REQUESTS 5 +#endif + +#if LIBCURL_VERSION_NUM < 0x070704 +#define curl_global_cleanup() do { /* nothing */ } while(0) +#endif +#if LIBCURL_VERSION_NUM < 0x070800 +#define curl_global_init(a) do { /* nothing */ } while(0) +#endif + +#if LIBCURL_VERSION_NUM < 0x070c04 +#define NO_CURL_EASY_DUPHANDLE +#endif + +struct active_request_slot +{ + CURL *curl; + FILE *local; + int in_use; + CURLcode curl_result; + long http_code; + void *callback_data; + void (*callback_func)(void *data); + struct active_request_slot *next; +}; + +struct buffer +{ + size_t posn; + size_t size; + void *buffer; +}; + +/* Curl request read/write callbacks */ +extern size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, + struct buffer *buffer); +extern size_t fwrite_buffer(const void *ptr, size_t eltsize, + size_t nmemb, struct buffer *buffer); +extern size_t fwrite_null(const void *ptr, size_t eltsize, + size_t nmemb, struct buffer *buffer); + +/* Slot lifecycle functions */ +extern struct active_request_slot *get_active_slot(void); +extern int start_active_slot(struct active_request_slot *slot); +extern void run_active_slot(struct active_request_slot *slot); +extern void finish_all_active_slots(void); + +#ifdef USE_CURL_MULTI +extern void fill_active_slots(void); +extern void step_active_slots(void); +#endif + +extern void http_init(void); +extern void http_cleanup(void); + +extern int data_received; +extern int active_requests; + +#ifdef USE_CURL_MULTI +extern int max_requests; +extern CURLM *curlm; +#endif +#ifndef NO_CURL_EASY_DUPHANDLE +extern CURL *curl_default; +#endif +extern char curl_errorstr[CURL_ERROR_SIZE]; + +extern int curl_ssl_verify; +extern char *ssl_cert; +#if LIBCURL_VERSION_NUM >= 0x070902 +extern char *ssl_key; +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 +extern char *ssl_capath; +#endif +extern char *ssl_cainfo; +extern long curl_low_speed_limit; +extern long curl_low_speed_time; + +extern struct curl_slist *pragma_header; +extern struct curl_slist *no_range_header; + +extern struct active_request_slot *active_queue_head; + +#endif /* HTTP_H */ -- cgit v0.10.2-6-g49f6 From e388ab74dbaea5cdbf07b175be8d6980579392f4 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 11:03:04 -0800 Subject: Make http-fetch request types more clear Rename object request functions and data to make it more clear which type of request is being processed - this is a response to the introduction of slot callbacks and the definition of different types of requests such as alternates_request. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/http-fetch.c b/http-fetch.c index 6a9eaf3..53a7dbf 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -21,14 +21,14 @@ struct alt_base static struct alt_base *alt = NULL; -enum transfer_state { +enum object_request_state { WAITING, ABORTED, ACTIVE, COMPLETE, }; -struct transfer_request +struct object_request { unsigned char sha1[20]; struct alt_base *repo; @@ -36,7 +36,7 @@ struct transfer_request char filename[PATH_MAX]; char tmpfile[PATH_MAX]; int local; - enum transfer_state state; + enum object_request_state state; CURLcode curl_result; char errorstr[CURL_ERROR_SIZE]; long http_code; @@ -46,10 +46,10 @@ struct transfer_request int zret; int rename; struct active_request_slot *slot; - struct transfer_request *next; + struct object_request *next; }; -struct alt_request { +struct alternates_request { char *base; char *url; struct buffer *buffer; @@ -57,7 +57,7 @@ struct alt_request { int http_specific; }; -static struct transfer_request *request_queue_head = NULL; +static struct object_request *object_queue_head = NULL; static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, void *data) @@ -65,24 +65,24 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb, unsigned char expn[4096]; size_t size = eltsize * nmemb; int posn = 0; - struct transfer_request *request = (struct transfer_request *)data; + struct object_request *obj_req = (struct object_request *)data; do { - ssize_t retval = write(request->local, + ssize_t retval = write(obj_req->local, ptr + posn, size - posn); if (retval < 0) return posn; posn += retval; } while (posn < size); - request->stream.avail_in = size; - request->stream.next_in = ptr; + obj_req->stream.avail_in = size; + obj_req->stream.next_in = ptr; do { - request->stream.next_out = expn; - request->stream.avail_out = sizeof(expn); - request->zret = inflate(&request->stream, Z_SYNC_FLUSH); - SHA1_Update(&request->c, expn, - sizeof(expn) - request->stream.avail_out); - } while (request->stream.avail_in && request->zret == Z_OK); + obj_req->stream.next_out = expn; + obj_req->stream.avail_out = sizeof(expn); + obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH); + SHA1_Update(&obj_req->c, expn, + sizeof(expn) - obj_req->stream.avail_out); + } while (obj_req->stream.avail_in && obj_req->zret == Z_OK); data_received++; return size; } @@ -91,9 +91,9 @@ static void fetch_alternates(char *base); static void process_object_response(void *callback_data); -static void start_request(struct transfer_request *request) +static void start_object_request(struct object_request *obj_req) { - char *hex = sha1_to_hex(request->sha1); + char *hex = sha1_to_hex(obj_req->sha1); char prevfile[PATH_MAX]; char *url; char *posn; @@ -105,53 +105,53 @@ static void start_request(struct transfer_request *request) struct curl_slist *range_header = NULL; struct active_request_slot *slot; - snprintf(prevfile, sizeof(prevfile), "%s.prev", request->filename); + snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename); unlink(prevfile); - rename(request->tmpfile, prevfile); - unlink(request->tmpfile); + rename(obj_req->tmpfile, prevfile); + unlink(obj_req->tmpfile); - if (request->local != -1) - error("fd leakage in start: %d", request->local); - request->local = open(request->tmpfile, + if (obj_req->local != -1) + error("fd leakage in start: %d", obj_req->local); + obj_req->local = open(obj_req->tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0666); /* This could have failed due to the "lazy directory creation"; * try to mkdir the last path component. */ - if (request->local < 0 && errno == ENOENT) { - char *dir = strrchr(request->tmpfile, '/'); + if (obj_req->local < 0 && errno == ENOENT) { + char *dir = strrchr(obj_req->tmpfile, '/'); if (dir) { *dir = 0; - mkdir(request->tmpfile, 0777); + mkdir(obj_req->tmpfile, 0777); *dir = '/'; } - request->local = open(request->tmpfile, + obj_req->local = open(obj_req->tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0666); } - if (request->local < 0) { - request->state = ABORTED; + if (obj_req->local < 0) { + obj_req->state = ABORTED; error("Couldn't create temporary file %s for %s: %s\n", - request->tmpfile, request->filename, strerror(errno)); + obj_req->tmpfile, obj_req->filename, strerror(errno)); return; } - memset(&request->stream, 0, sizeof(request->stream)); + memset(&obj_req->stream, 0, sizeof(obj_req->stream)); - inflateInit(&request->stream); + inflateInit(&obj_req->stream); - SHA1_Init(&request->c); + SHA1_Init(&obj_req->c); - url = xmalloc(strlen(request->repo->base) + 50); - request->url = xmalloc(strlen(request->repo->base) + 50); - strcpy(url, request->repo->base); - posn = url + strlen(request->repo->base); + url = xmalloc(strlen(obj_req->repo->base) + 50); + obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50); + strcpy(url, obj_req->repo->base); + posn = url + strlen(obj_req->repo->base); strcpy(posn, "objects/"); posn += 8; memcpy(posn, hex, 2); posn += 2; *(posn++) = '/'; strcpy(posn, hex + 2); - strcpy(request->url, url); + strcpy(obj_req->url, url); /* If a previous temp file is present, process what was already fetched. */ @@ -163,7 +163,7 @@ static void start_request(struct transfer_request *request) if (fwrite_sha1_file(prev_buf, 1, prev_read, - request) == prev_read) { + obj_req) == prev_read) { prev_posn += prev_read; } else { prev_read = -1; @@ -177,24 +177,24 @@ static void start_request(struct transfer_request *request) /* Reset inflate/SHA1 if there was an error reading the previous temp file; also rewind to the beginning of the local file. */ if (prev_read == -1) { - memset(&request->stream, 0, sizeof(request->stream)); - inflateInit(&request->stream); - SHA1_Init(&request->c); + memset(&obj_req->stream, 0, sizeof(obj_req->stream)); + inflateInit(&obj_req->stream); + SHA1_Init(&obj_req->c); if (prev_posn>0) { prev_posn = 0; - lseek(request->local, SEEK_SET, 0); - ftruncate(request->local, 0); + lseek(obj_req->local, SEEK_SET, 0); + ftruncate(obj_req->local, 0); } } slot = get_active_slot(); slot->callback_func = process_object_response; - slot->callback_data = request; - request->slot = slot; + slot->callback_data = obj_req; + obj_req->slot = slot; - curl_easy_setopt(slot->curl, CURLOPT_FILE, request); + curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file); - curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, request->errorstr); + curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr); curl_easy_setopt(slot->curl, CURLOPT_URL, url); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header); @@ -212,109 +212,111 @@ static void start_request(struct transfer_request *request) } /* Try to get the request started, abort the request on error */ - request->state = ACTIVE; + obj_req->state = ACTIVE; if (!start_active_slot(slot)) { - request->state = ABORTED; - request->slot = NULL; - close(request->local); request->local = -1; - free(request->url); + obj_req->state = ABORTED; + obj_req->slot = NULL; + close(obj_req->local); obj_req->local = -1; + free(obj_req->url); + return; } + } -static void finish_request(struct transfer_request *request) +static void finish_object_request(struct object_request *obj_req) { struct stat st; - fchmod(request->local, 0444); - close(request->local); request->local = -1; + fchmod(obj_req->local, 0444); + close(obj_req->local); obj_req->local = -1; - if (request->http_code == 416) { + if (obj_req->http_code == 416) { fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n"); - } else if (request->curl_result != CURLE_OK) { - if (stat(request->tmpfile, &st) == 0) + } else if (obj_req->curl_result != CURLE_OK) { + if (stat(obj_req->tmpfile, &st) == 0) if (st.st_size == 0) - unlink(request->tmpfile); + unlink(obj_req->tmpfile); return; } - inflateEnd(&request->stream); - SHA1_Final(request->real_sha1, &request->c); - if (request->zret != Z_STREAM_END) { - unlink(request->tmpfile); + inflateEnd(&obj_req->stream); + SHA1_Final(obj_req->real_sha1, &obj_req->c); + if (obj_req->zret != Z_STREAM_END) { + unlink(obj_req->tmpfile); return; } - if (memcmp(request->sha1, request->real_sha1, 20)) { - unlink(request->tmpfile); + if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) { + unlink(obj_req->tmpfile); return; } - request->rename = - move_temp_to_file(request->tmpfile, request->filename); + obj_req->rename = + move_temp_to_file(obj_req->tmpfile, obj_req->filename); - if (request->rename == 0) - pull_say("got %s\n", sha1_to_hex(request->sha1)); + if (obj_req->rename == 0) + pull_say("got %s\n", sha1_to_hex(obj_req->sha1)); } static void process_object_response(void *callback_data) { - struct transfer_request *request = - (struct transfer_request *)callback_data; + struct object_request *obj_req = + (struct object_request *)callback_data; - request->curl_result = request->slot->curl_result; - request->http_code = request->slot->http_code; - request->slot = NULL; - request->state = COMPLETE; + obj_req->curl_result = obj_req->slot->curl_result; + obj_req->http_code = obj_req->slot->http_code; + obj_req->slot = NULL; + obj_req->state = COMPLETE; /* Use alternates if necessary */ - if (request->http_code == 404) { + if (obj_req->http_code == 404) { fetch_alternates(alt->base); - if (request->repo->next != NULL) { - request->repo = - request->repo->next; - close(request->local); - request->local = -1; - start_request(request); + if (obj_req->repo->next != NULL) { + obj_req->repo = + obj_req->repo->next; + close(obj_req->local); + obj_req->local = -1; + start_object_request(obj_req); return; } } - finish_request(request); + finish_object_request(obj_req); } -static void release_request(struct transfer_request *request) +static void release_object_request(struct object_request *obj_req) { - struct transfer_request *entry = request_queue_head; + struct object_request *entry = object_queue_head; - if (request->local != -1) - error("fd leakage in release: %d", request->local); - if (request == request_queue_head) { - request_queue_head = request->next; + if (obj_req->local != -1) + error("fd leakage in release: %d", obj_req->local); + if (obj_req == object_queue_head) { + object_queue_head = obj_req->next; } else { - while (entry->next != NULL && entry->next != request) + while (entry->next != NULL && entry->next != obj_req) entry = entry->next; - if (entry->next == request) + if (entry->next == obj_req) entry->next = entry->next->next; } - free(request->url); - free(request); + free(obj_req->url); + free(obj_req); } #ifdef USE_CURL_MULTI void fill_active_slots(void) { - struct transfer_request *request = request_queue_head; + struct object_request *obj_req = object_queue_head; struct active_request_slot *slot = active_queue_head; int num_transfers; - while (active_requests < max_requests && request != NULL) { - if (request->state == WAITING) { - if (has_sha1_file(request->sha1)) - release_request(request); + while (active_requests < max_requests && obj_req != NULL) { + if (obj_req->state == WAITING) { + if (has_sha1_file(obj_req->sha1)) + release_object_request(obj_req); else - start_request(request); + start_object_request(obj_req); curl_multi_perform(curlm, &num_transfers); } - request = request->next; + obj_req = obj_req->next; } while (slot != NULL) { @@ -329,8 +331,8 @@ void fill_active_slots(void) void prefetch(unsigned char *sha1) { - struct transfer_request *newreq; - struct transfer_request *tail; + struct object_request *newreq; + struct object_request *tail; char *filename = sha1_file_name(sha1); newreq = xmalloc(sizeof(*newreq)); @@ -344,10 +346,10 @@ void prefetch(unsigned char *sha1) "%s.temp", filename); newreq->next = NULL; - if (request_queue_head == NULL) { - request_queue_head = newreq; + if (object_queue_head == NULL) { + object_queue_head = newreq; } else { - tail = request_queue_head; + tail = object_queue_head; while (tail->next != NULL) { tail = tail->next; } @@ -441,9 +443,10 @@ static int setup_index(struct alt_base *repo, unsigned char *sha1) return 0; } -static void process_alternates(void *callback_data) +static void process_alternates_response(void *callback_data) { - struct alt_request *alt_req = (struct alt_request *)callback_data; + struct alternates_request *alt_req = + (struct alternates_request *)callback_data; struct active_request_slot *slot = alt_req->slot; struct alt_base *tail = alt; char *base = alt_req->base; @@ -548,7 +551,7 @@ static void fetch_alternates(char *base) char *url; char *data; struct active_request_slot *slot; - static struct alt_request alt_req; + static struct alternates_request alt_req; /* If another request has already started fetching alternates, wait for them to arrive and return to processing this request's @@ -580,7 +583,7 @@ static void fetch_alternates(char *base) /* Use a callback to process the result, since another request may fail and need to have alternates loaded before continuing */ slot = get_active_slot(); - slot->callback_func = process_alternates; + slot->callback_func = process_alternates_response; slot->callback_data = &alt_req; curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer); @@ -760,54 +763,54 @@ static int fetch_object(struct alt_base *repo, unsigned char *sha1) { char *hex = sha1_to_hex(sha1); int ret = 0; - struct transfer_request *request = request_queue_head; + struct object_request *obj_req = object_queue_head; - while (request != NULL && memcmp(request->sha1, sha1, 20)) - request = request->next; - if (request == NULL) + while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20)) + obj_req = obj_req->next; + if (obj_req == NULL) return error("Couldn't find request for %s in the queue", hex); - if (has_sha1_file(request->sha1)) { - release_request(request); + if (has_sha1_file(obj_req->sha1)) { + release_object_request(obj_req); return 0; } #ifdef USE_CURL_MULTI - while (request->state == WAITING) { + while (obj_req->state == WAITING) { step_active_slots(); } #else - start_request(request); + start_object_request(obj_req); #endif - while (request->state == ACTIVE) { - run_active_slot(request->slot); + while (obj_req->state == ACTIVE) { + run_active_slot(obj_req->slot); } - if (request->local != -1) { - close(request->local); request->local = -1; + if (obj_req->local != -1) { + close(obj_req->local); obj_req->local = -1; } - if (request->state == ABORTED) { + if (obj_req->state == ABORTED) { ret = error("Request for %s aborted", hex); - } else if (request->curl_result != CURLE_OK && - request->http_code != 416) { - if (request->http_code == 404) + } else if (obj_req->curl_result != CURLE_OK && + obj_req->http_code != 416) { + if (obj_req->http_code == 404) ret = -1; /* Be silent, it is probably in a pack. */ else ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", - request->errorstr, request->curl_result, - request->http_code, hex); - } else if (request->zret != Z_STREAM_END) { - ret = error("File %s (%s) corrupt\n", hex, request->url); - } else if (memcmp(request->sha1, request->real_sha1, 20)) { + obj_req->errorstr, obj_req->curl_result, + obj_req->http_code, hex); + } else if (obj_req->zret != Z_STREAM_END) { + ret = error("File %s (%s) corrupt\n", hex, obj_req->url); + } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) { ret = error("File %s has bad hash\n", hex); - } else if (request->rename < 0) { + } else if (obj_req->rename < 0) { ret = error("unable to write sha1 filename %s: %s", - request->filename, - strerror(request->rename)); + obj_req->filename, + strerror(obj_req->rename)); } - release_request(request); + release_object_request(obj_req); return ret; } -- cgit v0.10.2-6-g49f6 From 5e3a769186bc41f94c1970579f96f3b68b046ed6 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 11:03:11 -0800 Subject: Improve pack list response handling Better response handling for pack list requests - a 404 means we do have the list but it happens to be empty. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/http-fetch.c b/http-fetch.c index 53a7dbf..4353173 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -637,10 +637,18 @@ static int fetch_indices(struct alt_base *repo) if (start_active_slot(slot)) { run_active_slot(slot); if (slot->curl_result != CURLE_OK) { - free(buffer.buffer); - return error("%s", curl_errorstr); + if (slot->http_code == 404) { + repo->got_indices = 1; + free(buffer.buffer); + return 0; + } else { + repo->got_indices = 0; + free(buffer.buffer); + return error("%s", curl_errorstr); + } } } else { + repo->got_indices = 0; free(buffer.buffer); return error("Unable to start request"); } -- cgit v0.10.2-6-g49f6 From acf59575cac330f3a505247dd7a9d6551a2a9cf8 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 11:03:18 -0800 Subject: Improve XML parsing in http-push Improved XML parsing - replace specialized doc parser callbacks with generic functions that track the parser context and use document-specific callbacks to process that data. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/http-push.c b/http-push.c index afdcd8b..2932693 100644 --- a/http-push.c +++ b/http-push.c @@ -24,13 +24,28 @@ enum XML_Status { #define RANGE_HEADER_SIZE 30 -/* DAV method names and request body templates */ +/* DAV methods */ #define DAV_LOCK "LOCK" #define DAV_MKCOL "MKCOL" #define DAV_MOVE "MOVE" #define DAV_PROPFIND "PROPFIND" #define DAV_PUT "PUT" #define DAV_UNLOCK "UNLOCK" + +/* DAV lock flags */ +#define DAV_PROP_LOCKWR (1u << 0) +#define DAV_PROP_LOCKEX (1u << 1) +#define DAV_LOCK_OK (1u << 2) + +/* DAV XML properties */ +#define DAV_CTX_LOCKENTRY ".multistatus.response.propstat.prop.supportedlock.lockentry" +#define DAV_CTX_LOCKTYPE_WRITE ".multistatus.response.propstat.prop.supportedlock.lockentry.locktype.write" +#define DAV_CTX_LOCKTYPE_EXCLUSIVE ".multistatus.response.propstat.prop.supportedlock.lockentry.lockscope.exclusive" +#define DAV_ACTIVELOCK_OWNER ".prop.lockdiscovery.activelock.owner.href" +#define DAV_ACTIVELOCK_TIMEOUT ".prop.lockdiscovery.activelock.timeout" +#define DAV_ACTIVELOCK_TOKEN ".prop.lockdiscovery.activelock.locktoken.href" + +/* DAV request body templates */ #define PROPFIND_REQUEST "\n\n\n\n\n" #define LOCK_REQUEST "\n\n\n\n\nmailto:%s\n\n" @@ -92,14 +107,17 @@ struct transfer_request static struct transfer_request *request_queue_head = NULL; +struct xml_ctx +{ + char *name; + int len; + char *cdata; + void (*userFunc)(struct xml_ctx *ctx, int tag_closed); + void *userData; +}; + struct active_lock { - int ctx_activelock; - int ctx_owner; - int ctx_owner_href; - int ctx_timeout; - int ctx_locktoken; - int ctx_locktoken_href; char *url; char *owner; char *token; @@ -108,16 +126,6 @@ struct active_lock int refreshing; }; -struct lockprop -{ - int supported_lock; - int lock_entry; - int lock_scope; - int lock_type; - int lock_exclusive; - int lock_exclusive_write; -}; - static void finish_request(struct transfer_request *request); static void process_response(void *callback_data) @@ -734,107 +742,101 @@ int fetch_ref(char *ref, unsigned char *sha1) return 0; } -static void -start_activelock_element(void *userData, const char *name, const char **atts) +static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed) { - struct active_lock *lock = (struct active_lock *)userData; - - if (lock->ctx_activelock && !strcmp(name, "D:timeout")) - lock->ctx_timeout = 1; - else if (lock->ctx_owner && strstr(name, "href")) - lock->ctx_owner_href = 1; - else if (lock->ctx_activelock && strstr(name, "owner")) - lock->ctx_owner = 1; - else if (lock->ctx_locktoken && !strcmp(name, "D:href")) - lock->ctx_locktoken_href = 1; - else if (lock->ctx_activelock && !strcmp(name, "D:locktoken")) - lock->ctx_locktoken = 1; - else if (!strcmp(name, "D:activelock")) - lock->ctx_activelock = 1; + int *lock_flags = (int *)ctx->userData; + + if (tag_closed) { + if (!strcmp(ctx->name, DAV_CTX_LOCKENTRY)) { + if ((*lock_flags & DAV_PROP_LOCKEX) && + (*lock_flags & DAV_PROP_LOCKWR)) { + *lock_flags |= DAV_LOCK_OK; + } + *lock_flags &= DAV_LOCK_OK; + } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_WRITE)) { + *lock_flags |= DAV_PROP_LOCKWR; + } else if (!strcmp(ctx->name, DAV_CTX_LOCKTYPE_EXCLUSIVE)) { + *lock_flags |= DAV_PROP_LOCKEX; + } + } } -static void -end_activelock_element(void *userData, const char *name) +static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) { - struct active_lock *lock = (struct active_lock *)userData; - - if (lock->ctx_timeout && !strcmp(name, "D:timeout")) { - lock->ctx_timeout = 0; - } else if (lock->ctx_owner_href && strstr(name, "href")) { - lock->ctx_owner_href = 0; - } else if (lock->ctx_owner && strstr(name, "owner")) { - lock->ctx_owner = 0; - } else if (lock->ctx_locktoken_href && !strcmp(name, "D:href")) { - lock->ctx_locktoken_href = 0; - } else if (lock->ctx_locktoken && !strcmp(name, "D:locktoken")) { - lock->ctx_locktoken = 0; - } else if (lock->ctx_activelock && !strcmp(name, "D:activelock")) { - lock->ctx_activelock = 0; + struct active_lock *lock = (struct active_lock *)ctx->userData; + + if (tag_closed && ctx->cdata) { + if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) { + lock->owner = xmalloc(strlen(ctx->cdata) + 1); + strcpy(lock->owner, ctx->cdata); + } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) { + if (!strncmp(ctx->cdata, "Second-", 7)) + lock->timeout = + strtol(ctx->cdata + 7, NULL, 10); + } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) { + if (!strncmp(ctx->cdata, "opaquelocktoken:", 16)) { + lock->token = xmalloc(strlen(ctx->cdata - 15)); + strcpy(lock->token, ctx->cdata + 16); + } + } } } static void -activelock_cdata(void *userData, const XML_Char *s, int len) +xml_start_tag(void *userData, const char *name, const char **atts) { - struct active_lock *lock = (struct active_lock *)userData; - char *this = malloc(len+1); - strncpy(this, s, len); - - if (lock->ctx_owner_href) { - lock->owner = malloc(len+1); - strcpy(lock->owner, this); - } else if (lock->ctx_locktoken_href) { - if (!strncmp(this, "opaquelocktoken:", 16)) { - lock->token = malloc(len-15); - strcpy(lock->token, this+16); - } - } else if (lock->ctx_timeout) { - if (!strncmp(this, "Second-", 7)) - lock->timeout = strtol(this+7, NULL, 10); + struct xml_ctx *ctx = (struct xml_ctx *)userData; + const char *c = index(name, ':'); + int new_len; + + if (c == NULL) + c = name; + else + c++; + + new_len = strlen(ctx->name) + strlen(c) + 2; + + if (new_len > ctx->len) { + ctx->name = xrealloc(ctx->name, new_len); + ctx->len = new_len; } + strcat(ctx->name, "."); + strcat(ctx->name, c); - free(this); + if (ctx->cdata) { + free(ctx->cdata); + ctx->cdata = NULL; + } + + ctx->userFunc(ctx, 0); } static void -start_lockprop_element(void *userData, const char *name, const char **atts) +xml_end_tag(void *userData, const char *name) { - struct lockprop *prop = (struct lockprop *)userData; + struct xml_ctx *ctx = (struct xml_ctx *)userData; + const char *c = index(name, ':'); + char *ep; - if (prop->lock_type && !strcmp(name, "D:write")) { - if (prop->lock_exclusive) { - prop->lock_exclusive_write = 1; - } - } else if (prop->lock_scope && !strcmp(name, "D:exclusive")) { - prop->lock_exclusive = 1; - } else if (prop->lock_entry) { - if (!strcmp(name, "D:lockscope")) { - prop->lock_scope = 1; - } else if (!strcmp(name, "D:locktype")) { - prop->lock_type = 1; - } - } else if (prop->supported_lock) { - if (!strcmp(name, "D:lockentry")) { - prop->lock_entry = 1; - } - } else if (!strcmp(name, "D:supportedlock")) { - prop->supported_lock = 1; - } + ctx->userFunc(ctx, 1); + + if (c == NULL) + c = name; + else + c++; + + ep = ctx->name + strlen(ctx->name) - strlen(c) - 1; + *ep = 0; } static void -end_lockprop_element(void *userData, const char *name) +xml_cdata(void *userData, const XML_Char *s, int len) { - struct lockprop *prop = (struct lockprop *)userData; - - if (!strcmp(name, "D:lockentry")) { - prop->lock_entry = 0; - prop->lock_scope = 0; - prop->lock_type = 0; - prop->lock_exclusive = 0; - } else if (!strcmp(name, "D:supportedlock")) { - prop->supported_lock = 0; - } + struct xml_ctx *ctx = (struct xml_ctx *)userData; + if (ctx->cdata) + free(ctx->cdata); + ctx->cdata = xcalloc(len+1, 1); + strncpy(ctx->cdata, s, len); } static struct active_lock *lock_remote(char *file, long timeout) @@ -847,10 +849,11 @@ static struct active_lock *lock_remote(char *file, long timeout) char *url; char *ep; char timeout_header[25]; - struct active_lock *new_lock; + struct active_lock *new_lock = NULL; XML_Parser parser = XML_ParserCreate(NULL); enum XML_Status result; struct curl_slist *dav_headers = NULL; + struct xml_ctx ctx; url = xmalloc(strlen(remote->url) + strlen(file) + 1); sprintf(url, "%s%s", remote->url, file); @@ -894,12 +897,6 @@ static struct active_lock *lock_remote(char *file, long timeout) in_buffer.posn = 0; in_buffer.buffer = in_data; - new_lock = xcalloc(1, sizeof(*new_lock)); - new_lock->owner = NULL; - new_lock->token = NULL; - new_lock->timeout = -1; - new_lock->refreshing = 0; - sprintf(timeout_header, "Timeout: Second-%ld", timeout); dav_headers = curl_slist_append(dav_headers, timeout_header); dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml"); @@ -915,40 +912,41 @@ static struct active_lock *lock_remote(char *file, long timeout) curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_LOCK); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers); + new_lock = xcalloc(1, sizeof(*new_lock)); + new_lock->owner = NULL; + new_lock->token = NULL; + new_lock->timeout = -1; + new_lock->refreshing = 0; + if (start_active_slot(slot)) { run_active_slot(slot); - if (slot->curl_result != CURLE_OK) { - fprintf(stderr, "Got HTTP error %ld\n", slot->http_code); - free(new_lock); - free(url); - free(out_data); - free(in_data); - return NULL; + if (slot->curl_result == CURLE_OK) { + ctx.name = xcalloc(10, 1); + ctx.len = 0; + ctx.cdata = NULL; + ctx.userFunc = handle_new_lock_ctx; + ctx.userData = new_lock; + XML_SetUserData(parser, &ctx); + XML_SetElementHandler(parser, xml_start_tag, + xml_end_tag); + XML_SetCharacterDataHandler(parser, xml_cdata); + result = XML_Parse(parser, in_buffer.buffer, + in_buffer.posn, 1); + free(ctx.name); + if (result != XML_STATUS_OK) { + fprintf(stderr, "XML error: %s\n", + XML_ErrorString( + XML_GetErrorCode(parser))); + new_lock->timeout = -1; + } } } else { - free(new_lock); - free(url); - free(out_data); - free(in_data); fprintf(stderr, "Unable to start request\n"); - return NULL; } + curl_slist_free_all(dav_headers); free(out_data); - - XML_SetUserData(parser, new_lock); - XML_SetElementHandler(parser, start_activelock_element, - end_activelock_element); - XML_SetCharacterDataHandler(parser, activelock_cdata); - result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1); free(in_data); - if (result != XML_STATUS_OK) { - fprintf(stderr, "%s", XML_ErrorString( - XML_GetErrorCode(parser))); - free(url); - free(new_lock); - return NULL; - } if (new_lock->token == NULL || new_lock->timeout <= 0) { if (new_lock->token != NULL) @@ -957,11 +955,12 @@ static struct active_lock *lock_remote(char *file, long timeout) free(new_lock->owner); free(url); free(new_lock); - return NULL; + new_lock = NULL; + } else { + new_lock->url = url; + new_lock->start_time = time(NULL); } - new_lock->url = url; - new_lock->start_time = time(NULL); return new_lock; } @@ -1000,13 +999,15 @@ static int unlock_remote(struct active_lock *lock) if (lock->owner != NULL) free(lock->owner); free(lock->url); +/* Freeing the token causes a segfault... free(lock->token); +*/ free(lock); return rc; } -static int check_locking(void) +static int locking_available(void) { struct active_request_slot *slot; struct buffer in_buffer; @@ -1015,8 +1016,9 @@ static int check_locking(void) char *out_data; XML_Parser parser = XML_ParserCreate(NULL); enum XML_Status result; - struct lockprop supported_lock; struct curl_slist *dav_headers = NULL; + struct xml_ctx ctx; + int lock_flags = 0; out_buffer.size = strlen(PROPFIND_REQUEST) + strlen(remote->url) - 2; out_data = xmalloc(out_buffer.size + 1); @@ -1045,30 +1047,35 @@ static int check_locking(void) if (start_active_slot(slot)) { run_active_slot(slot); - free(out_data); - if (slot->curl_result != CURLE_OK) { - free(in_buffer.buffer); - return -1; + if (slot->curl_result == CURLE_OK) { + ctx.name = xcalloc(10, 1); + ctx.len = 0; + ctx.cdata = NULL; + ctx.userFunc = handle_lockprop_ctx; + ctx.userData = &lock_flags; + XML_SetUserData(parser, &ctx); + XML_SetElementHandler(parser, xml_start_tag, + xml_end_tag); + result = XML_Parse(parser, in_buffer.buffer, + in_buffer.posn, 1); + free(ctx.name); + + if (result != XML_STATUS_OK) { + fprintf(stderr, "XML error: %s\n", + XML_ErrorString( + XML_GetErrorCode(parser))); + lock_flags = 0; + } } - - XML_SetUserData(parser, &supported_lock); - XML_SetElementHandler(parser, start_lockprop_element, - end_lockprop_element); - result = XML_Parse(parser, in_buffer.buffer, in_buffer.posn, 1); - free(in_buffer.buffer); - if (result != XML_STATUS_OK) - return error("%s", XML_ErrorString( - XML_GetErrorCode(parser))); } else { - free(out_data); - free(in_buffer.buffer); - return error("Unable to start request"); + fprintf(stderr, "Unable to start request\n"); } - if (supported_lock.lock_exclusive_write) - return 0; - else - return 1; + free(out_data); + free(in_buffer.buffer); + curl_slist_free_all(dav_headers); + + return lock_flags; } static int is_ancestor(unsigned char *sha1, struct commit *commit) @@ -1269,7 +1276,7 @@ int main(int argc, char **argv) "Pragma: no-cache"); /* Verify DAV compliance/lock support */ - if (check_locking() != 0) { + if (!locking_available()) { fprintf(stderr, "Error: no DAV locking support on remote repo %s\n", remote->url); rc = 1; goto cleanup; -- cgit v0.10.2-6-g49f6 From 7b8999674955b9d6b58de2b8f71b2e2ede795f65 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 11:03:25 -0800 Subject: http-push memory/fd cleanup Clean up memory and file descriptor usage Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/http-push.c b/http-push.c index 2932693..f3c92c9 100644 --- a/http-push.c +++ b/http-push.c @@ -165,6 +165,7 @@ static void start_check(struct transfer_request *request) } else { request->state = ABORTED; free(request->url); + request->url = NULL; } } @@ -198,6 +199,7 @@ static void start_mkcol(struct transfer_request *request) } else { request->state = ABORTED; free(request->url); + request->url = NULL; } } @@ -244,8 +246,6 @@ static void start_put(struct transfer_request *request) request->buffer.size = stream.total_out; request->buffer.posn = 0; - if (request->url != NULL) - free(request->url); request->url = xmalloc(strlen(remote->url) + strlen(request->lock->token) + 51); strcpy(request->url, remote->url); @@ -281,6 +281,7 @@ static void start_put(struct transfer_request *request) } else { request->state = ABORTED; free(request->url); + request->url = NULL; } } @@ -306,6 +307,7 @@ static void start_move(struct transfer_request *request) } else { request->state = ABORTED; free(request->url); + request->url = NULL; } } @@ -370,6 +372,13 @@ static void finish_request(struct transfer_request *request) if (request->headers != NULL) curl_slist_free_all(request->headers); + + /* URL is reused for MOVE after PUT */ + if (request->state != RUN_PUT) { + free(request->url); + request->url = NULL; + } + if (request->state == RUN_HEAD) { if (request->http_code == 404) { request->state = NEED_PUSH; @@ -435,7 +444,8 @@ static void release_request(struct transfer_request *request) entry->next = entry->next->next; } - free(request->url); + if (request->url != NULL) + free(request->url); free(request); } @@ -575,6 +585,7 @@ static int fetch_index(unsigned char *sha1) } } else { free(url); + fclose(indexfile); return error("Unable to start request"); } -- cgit v0.10.2-6-g49f6 From 11979b98adc301fed84122237d0d5a26a360fede Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 18 Nov 2005 17:06:46 -0800 Subject: http.c: reorder to avoid compilation failure. Move the static function get_curl_handle() around to make sure its definition and declarations are seen by the compiler before its first use. Also remove an unused variable. Signed-off-by: Junio C Hamano diff --git a/http.c b/http.c index 470b56e..75e6717 100644 --- a/http.c +++ b/http.c @@ -160,6 +160,41 @@ static int http_options(const char *var, const char *value) return git_default_config(var, value); } +static CURL* get_curl_handle(void) +{ + CURL* result = curl_easy_init(); + + curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); +#if LIBCURL_VERSION_NUM >= 0x070907 + curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); +#endif + + if (ssl_cert != NULL) + curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); +#if LIBCURL_VERSION_NUM >= 0x070902 + if (ssl_key != NULL) + curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); +#endif +#if LIBCURL_VERSION_NUM >= 0x070908 + if (ssl_capath != NULL) + curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); +#endif + if (ssl_cainfo != NULL) + curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); + curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); + + if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { + curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, + curl_low_speed_limit); + curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, + curl_low_speed_time); + } + + curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); + + return result; +} + void http_init(void) { char *low_speed_limit; @@ -223,7 +258,6 @@ void http_cleanup(void) struct active_request_slot *slot = active_queue_head; #ifdef USE_CURL_MULTI char *wait_url; - CURLMcode curlm_result; #endif while (slot != NULL) { @@ -252,41 +286,6 @@ void http_cleanup(void) } -static CURL* get_curl_handle(void) -{ - CURL* result = curl_easy_init(); - - curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify); -#if LIBCURL_VERSION_NUM >= 0x070907 - curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); -#endif - - if (ssl_cert != NULL) - curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); -#if LIBCURL_VERSION_NUM >= 0x070902 - if (ssl_key != NULL) - curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); -#endif -#if LIBCURL_VERSION_NUM >= 0x070908 - if (ssl_capath != NULL) - curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath); -#endif - if (ssl_cainfo != NULL) - curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo); - curl_easy_setopt(result, CURLOPT_FAILONERROR, 1); - - if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) { - curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT, - curl_low_speed_limit); - curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME, - curl_low_speed_time); - } - - curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1); - - return result; -} - struct active_request_slot *get_active_slot(void) { struct active_request_slot *slot = active_queue_head; -- cgit v0.10.2-6-g49f6 From 0890098780f295f2a58658d1f6b6627e40426c72 Mon Sep 17 00:00:00 2001 From: Nick Hengeveld Date: Fri, 18 Nov 2005 17:08:36 -0800 Subject: Decide whether to build http-push in the Makefile The decision about whether to build http-push or not belongs in the Makefile. This follows Junio's suggestion to determine whether curl is new enough to support http-push. Signed-off-by: Nick Hengeveld Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 285b3ed..0efb0b6 100644 --- a/Makefile +++ b/Makefile @@ -242,9 +242,12 @@ ifndef NO_CURL CURL_LIBCURL = -lcurl endif PROGRAMS += git-http-fetch$X - ifndef NO_EXPAT - EXPAT_LIBEXPAT = -lexpat - PROGRAMS += git-http-push$X + curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p) + ifeq "$(curl_check)" "070908" + ifndef NO_EXPAT + EXPAT_LIBEXPAT = -lexpat + PROGRAMS += git-http-push$X + endif endif endif diff --git a/http-push.c b/http-push.c index f3c92c9..76c7886 100644 --- a/http-push.c +++ b/http-push.c @@ -6,8 +6,6 @@ #include "blob.h" #include "http.h" -#ifdef USE_CURL_MULTI - #include static const char http_push_usage[] = @@ -1426,10 +1424,3 @@ int main(int argc, char **argv) return rc; } -#else /* ifdef USE_CURL_MULTI */ -int main(int argc, char **argv) -{ - fprintf(stderr, "http-push requires curl 7.9.8 or higher.\n"); - return 1; -} -#endif -- cgit v0.10.2-6-g49f6 From 10bea152a34b2bf1194ede5e0c9e5595ab2100f3 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Nov 2005 22:32:36 +0100 Subject: Add functions git_config_set() and git_config_set_multivar() The function git_config_set() does exactly what you think it does. Given a key (in the form "core.filemode") and a value, it sets the key to the value. Example: git_config_set("core.filemode", "true"); The function git_config_set_multivar() is meant for setting variables which can have several values for the same key. Example: [diff] twohead = resolve twohead = recarsive the typo in the second line can be replaced by git_config_set_multivar("diff.twohead", "recursive", "^recar"); The third argument of the function is a POSIX extended regex which has to match the value. If there is no key/value pair with a matching value, a new key/value pair is added. These commands are also capable of unsetting (deleting) entries: git_config_set_multivar("diff.twohead", NULL, "sol"); will delete the entry twohead = resolve Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index 99afa2c..f93f516 100644 --- a/cache.h +++ b/cache.h @@ -386,6 +386,8 @@ extern int git_default_config(const char *, const char *); extern int git_config(config_fn_t fn); extern int git_config_int(const char *, const char *); extern int git_config_bool(const char *, const char *); +extern int git_config_set(const char *, const char *); +extern int git_config_set_multivar(const char *, const char *, const char *); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; diff --git a/config.c b/config.c index 915bb97..bbcafff 100644 --- a/config.c +++ b/config.c @@ -1,5 +1,12 @@ - +/* + * GIT - The information manager from hell + * + * Copyright (C) Linus Torvalds, 2005 + * Copyright (C) Johannes Schindelin, 2005 + * + */ #include "cache.h" +#include #define MAXNAME (256) @@ -252,3 +259,288 @@ int git_config(config_fn_t fn) } return ret; } + +/* + * Find all the stuff for git_config_set() below. + */ +static struct { + int baselen; + char* key; + regex_t* value_regex; + off_t offset; + enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; + int seen; +} store; + +static int store_aux(const char* key, const char* value) +{ + switch (store.state) { + case KEY_SEEN: + if (!strcmp(key, store.key) && + (store.value_regex == NULL || + !regexec(store.value_regex, value, 0, NULL, 0))) { + if (store.seen == 1) { + fprintf(stderr, + "Warning: %s has multiple values\n", + key); + } + store.offset = ftell(config_file); + store.seen++; + } + break; + case SECTION_SEEN: + if (strncmp(key, store.key, store.baselen+1)) { + store.state = SECTION_END_SEEN; + break; + } else + store.offset = ftell(config_file); + /* fallthru */ + case SECTION_END_SEEN: + case START: + if (!strcmp(key, store.key) && + (store.value_regex == NULL || + !regexec(store.value_regex, value, 0, NULL, 0))) { + store.offset = ftell(config_file); + store.state = KEY_SEEN; + store.seen++; + } else if(!strncmp(key, store.key, store.baselen)) + store.state = SECTION_SEEN; + } + return 0; +} + +static void store_write_section(int fd, const char* key) +{ + write(fd, "[", 1); + write(fd, key, store.baselen); + write(fd, "]\n", 2); +} + +static void store_write_pair(int fd, const char* key, const char* value) +{ + int i; + + write(fd, "\t", 1); + write(fd, key+store.baselen+1, + strlen(key+store.baselen+1)); + write(fd, " = ", 3); + for (i = 0; value[i]; i++) + switch (value[i]) { + case '\n': write(fd, "\\n", 2); break; + case '\t': write(fd, "\\t", 2); break; + case '"': case '\\': write(fd, "\\", 1); + default: write(fd, value+i, 1); + } + write(fd, "\n", 1); +} + +int git_config_set(const char* key, const char* value) +{ + return git_config_set_multivar(key, value, NULL); +} + +/* + * If value==NULL, unset in (remove from) config, + * if value_regex!=NULL, disregard key/value pairs where value does not match. + * + * Returns 0 on success. + * + * This function does this: + * + * - it locks the config file by creating ".git/config.lock" + * + * - it then parses the config using store_aux() as validator to find + * the position on the key/value pair to replace. If it is to be unset, + * it must be found exactly once. + * + * - the config file is mmap()ed and the part before the match (if any) is + * written to the lock file, then the changed part and the rest. + * + * - the config file is removed and the lock file rename()d to it. + * + */ +int git_config_set_multivar(const char* key, const char* value, + const char* value_regex) +{ + int i; + struct stat st; + int fd; + char* config_file = strdup(git_path("config")); + char* lock_file = strdup(git_path("config.lock")); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + for (store.baselen = 0; + key[store.baselen] != '.' && key[store.baselen]; + store.baselen++); + if (!key[store.baselen] || !key[store.baselen+1]) { + fprintf(stderr, "key does not contain a section: %s\n", key); + return 2; + } + + /* + * Validate the key and while at it, lower case it for matching. + */ + store.key = (char*)malloc(strlen(key)+1); + for (i = 0; key[i]; i++) + if (i != store.baselen && (!isalnum(key[i]) || + (i == store.baselen+1 && !isalpha(key[i])))) { + fprintf(stderr, "invalid key: %s\n", key); + free(store.key); + return 1; + } else + store.key[i] = tolower(key[i]); + + /* + * The lock_file serves a purpose in addition to locking: the new + * contents of .git/config will be written into it. + */ + fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (fd < 0) { + fprintf(stderr, "could not lock config file\n"); + free(store.key); + return -1; + } + + /* + * If .git/config does not exist yet, write a minimal version. + */ + if (stat(config_file, &st)) { + static const char contents[] = + "#\n" + "# This is the config file\n" + "#\n" + "\n"; + + free(store.key); + + /* if nothing to unset, error out */ + if (value == NULL) { + close(fd); + unlink(lock_file); + return 5; + } + + store.key = (char*)key; + + write(fd, contents, sizeof(contents)-1); + store_write_section(fd, key); + store_write_pair(fd, key, value); + } else{ + int in_fd; + char* contents; + int offset, new_line = 0; + + if (value_regex == NULL) + store.value_regex = NULL; + else { + store.value_regex = (regex_t*)malloc(sizeof(regex_t)); + if (regcomp(store.value_regex, value_regex, + REG_EXTENDED)) { + fprintf(stderr, "Invalid pattern: %s", + value_regex); + free(store.value_regex); + return 6; + } + } + + store.offset = 0; + store.state = START; + store.seen = 0; + + /* + * After this, store.offset will contain the *end* offset + * of the last match, or remain at 0 if no match was found. + * As a side effect, we make sure to transform only a valid + * existing config file. + */ + if (git_config(store_aux)) { + fprintf(stderr, "invalid config file\n"); + free(store.key); + if (store.value_regex != NULL) { + regfree(store.value_regex); + free(store.value_regex); + } + return 3; + } + + free(store.key); + if (store.value_regex != NULL) { + regfree(store.value_regex); + free(store.value_regex); + } + + /* if nothing to unset, error out */ + if (store.seen == 0 && value == NULL) { + close(fd); + unlink(lock_file); + return 5; + } + + store.key = (char*)key; + + in_fd = open(config_file, O_RDONLY, 0666); + contents = mmap(NULL, st.st_size, PROT_READ, + MAP_PRIVATE, in_fd, 0); + close(in_fd); + + if (store.offset == 0) { + store.offset = offset = st.st_size; + } else if (store.state != KEY_SEEN) { + offset = store.offset; + } else { + int equal_offset = st.st_size, + bracket_offset = st.st_size; + + if (value == NULL && store.seen > 1) { + fprintf(stderr, "Cannot remove multivar (%s has %d values\n", key, store.seen); + close(fd); + unlink(lock_file); + return 7; + } + for (offset = store.offset-2; offset > 0 + && contents[offset] != '\n'; offset--) + switch (contents[offset]) { + case '=': equal_offset = offset; break; + case ']': bracket_offset = offset; break; + } + if (bracket_offset < equal_offset) { + new_line = 1; + offset = bracket_offset+1; + } else + offset++; + } + + /* write the first part of the config */ + write(fd, contents, offset); + if (new_line) + write(fd, "\n", 1); + + /* write the pair (value == NULL means unset) */ + if (value != NULL) { + if (store.state == START) + store_write_section(fd, key); + store_write_pair(fd, key, value); + } + + /* write the rest of the config */ + if (store.offset < st.st_size) + write(fd, contents + store.offset, + st.st_size - store.offset); + + munmap(contents, st.st_size); + unlink(config_file); + } + + close(fd); + + if (rename(lock_file, config_file) < 0) { + fprintf(stderr, "Could not rename the lock file?\n"); + return 4; + } + + return 0; +} + -- cgit v0.10.2-6-g49f6 From 1b1e59c5084ac8b0af0aa443bb1713179d400211 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Nov 2005 22:44:55 +0100 Subject: Add git-config-set, a simple helper for scripts to set config variables This is meant for the end user, who cannot be expected to edit .git/config by hand. Example: git-config-set core.filemode true will set filemode in the section [core] to true, git-config-set --unset core.filemode will remove the entry (failing if it is not there), and git-config-set --unset diff.twohead ^recar will remove the unique entry whose value matches the regex "^recar" (failing if there is no unique such entry). It is just a light wrapper around git_config_set() and git_config_set_multivar(). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index 0dd7b9c..d17a8b5 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ git-clone git-clone-pack git-commit git-commit-tree +git-config-set git-convert-objects git-count-objects git-cvsexportcommit diff --git a/Makefile b/Makefile index 0efb0b6..092931a 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,8 @@ PROGRAMS = \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ 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-var$X $(SIMPLE_PROGRAMS) + git-name-rev$X git-pack-redundant$X git-config-set$X git-var$X \ + $(SIMPLE_PROGRAMS) # Backward compatibility -- to be removed after 1.0 PROGRAMS += git-ssh-pull$X git-ssh-push$X diff --git a/config-set.c b/config-set.c new file mode 100644 index 0000000..1b1547b --- /dev/null +++ b/config-set.c @@ -0,0 +1,26 @@ +#include "cache.h" + +static const char git_config_set_usage[] = +"git-config-set name [value [value_regex]] | --unset name [value_regex]"; + +int main(int argc, const char **argv) +{ + setup_git_directory(); + switch (argc) { + case 2: + return git_config_set(argv[1], NULL); + case 3: + if (!strcmp(argv[1], "--unset")) + return git_config_set(argv[2], NULL); + else + return git_config_set(argv[1], argv[2]); + case 4: + if (!strcmp(argv[1], "--unset")) + return git_config_set_multivar(argv[2], NULL, argv[3]); + else + return git_config_set_multivar(argv[1], argv[2], argv[3]); + default: + usage(git_config_set_usage); + } + return 0; +} -- cgit v0.10.2-6-g49f6 From 2d2465c01518eb7defa9aa39ae864c9646cfb6f4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Nov 2005 22:49:19 +0100 Subject: Add documentation for git-config-set Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-config-set.txt b/Documentation/git-config-set.txt new file mode 100644 index 0000000..8e897be --- /dev/null +++ b/Documentation/git-config-set.txt @@ -0,0 +1,108 @@ +git-config-set(1) +=============== + +NAME +---- +git-config-set - Set options in .git/config. + + +SYNOPSIS +-------- +'git-config-set' ( name [value [value_regex]] | --unset name [value_regex] ) + +DESCRIPTION +----------- +You can set/replace/unset options with this command. The name is actually +the section and the key separated by a dot, and the value will be escaped. + +If you want to set/unset an option which can occor on multiple lines, you +should provide a POSIX regex for the value. + +This command will fail if + +. .git/config is invalid, +. .git/config can not be written to, +. no section was provided, +. the section or key is invalid, +. you try to unset an option which does not exist, or +. you try to unset/set an option for which multiple lines match. + + +OPTIONS +------- + +--unset:: + Remove the given option from .git/config + + +EXAMPLE +------- + +Given a .git/config like this: + + # + # This is the config file, and + # a '#' or ';' character indicates + # a comment + # + + ; core variables + [core] + ; Don't trust file modes + filemode = false + + ; Our diff algorithm + [diff] + external = "/usr/local/bin/gnu-diff -u" + renames = true + + ; Proxy settings + [proxy] + command="ssh" for "ssh://kernel.org/" + command="proxy-command" for kernel.org + command="myprotocol-command" for "my://" + +you can set the filemode to true with + +------------ +% git config-set core.filemode true +------------ + +The hypothetic proxy command entries actually have a postfix to discern +to what URL they apply. Here is how to change the entry for kernel.org +to "ssh". + +------------ +% git config-set proxy.command '"ssh" for kernel.org' 'for kernel.org$' +------------ + +This makes sure that only the key/value pair for kernel.org is replaced. + +To delete the entry for renames, do + +------------ +% git config-set --unset diff.renames +------------ + +or just + +------------ +% git config-set diff.renames +------------ + +If you want to delete an entry for a multivar (like proxy.command above), +you have to provide a regex matching the value of exactly one line. + + +Author +------ +Written by Johannes Schindelin + +Documentation +-------------- +Documentation by Johannes Schindelin. + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git.txt b/Documentation/git.txt index 338e5ac..694fee8 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -84,6 +84,9 @@ gitlink:git-checkout-index[1]:: gitlink:git-commit-tree[1]:: Creates a new commit object +gitlink:git-config-set[1]:: + Set options in .git/config. + gitlink:git-hash-object[1]:: Computes the object ID from a file. -- cgit v0.10.2-6-g49f6 From 942c1f53aef03cb3d8b5c39b38997a379c3fad20 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 17 Nov 2005 22:50:08 +0100 Subject: Add test case for git-config-set ... includes the mean tests I mentioned on the list. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/t1300-config-set.sh b/t1300-config-set.sh new file mode 100644 index 0000000..df89216 --- /dev/null +++ b/t1300-config-set.sh @@ -0,0 +1,180 @@ +#!/bin/sh +# +# Copyright (c) 2005 Johannes Schindelin +# + +test_description='Test git-config-set in different settings' + +. ./test-lib.sh + +test -f .git/config && rm .git/config + +git-config-set core.penguin "little blue" + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue +EOF + +test_expect_success 'initial' 'cmp .git/config expect' + +git-config-set Core.Movie BadPhysics + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +EOF + +test_expect_success 'mixed case' 'cmp .git/config expect' + +git-config-set Cores.WhatEver Second + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +git-config-set CORE.UPPERCASE true + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics + UPPERCASE = true +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +cat > .git/config << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha ="beta" # last silly comment +[nextSection] noNewline = ouch +EOF + +git-config-set beta.haha alpha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] noNewline = ouch +EOF + +test_expect_success 'really mean test' 'cmp .git/config expect' + +git-config-set nextsection.nonewline wow + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] + nonewline = wow +EOF + +test_expect_success 'really really mean test' 'cmp .git/config expect' + +git-config-set beta.haha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow +EOF + +test_expect_success 'unset' 'cmp .git/config expect' + +git-config-set nextsection.NoNewLine "wow2 for me" "for me$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar' 'cmp .git/config expect' + +git-config-set nextsection.nonewline "wow3" "wow$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow3 + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar replace' 'cmp .git/config expect' + +test_expect_failure 'ambiguous unset' \ + 'git-config-set --unset nextsection.nonewline' + +test_expect_failure 'invalid unset' \ + 'git-config-set --unset somesection.nonewline' + +git-config-set --unset nextsection.nonewline "wow3$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar unset' 'cmp .git/config expect' + +test_expect_failure 'invalid key' 'git-config-set inval.2key blabla' + +test_expect_success 'correct key' 'git-config-set 123456.a123 987' + +test_done + -- cgit v0.10.2-6-g49f6 From 54f4b87454824fedc629219620ce9b7cdcab3d27 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Thu, 17 Nov 2005 20:37:14 +0100 Subject: Library code for user-relative paths, take three. This patch provides the work-horse of the user-relative paths feature, using Linus' idea of a blind chdir() and getcwd() which makes it remarkably simple. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index f93f516..e2be3e7 100644 --- a/cache.h +++ b/cache.h @@ -192,6 +192,7 @@ extern int diff_rename_limit_default; /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern char *enter_repo(char *path, int strict); extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *sha1_file_name(const unsigned char *sha1); extern char *sha1_pack_name(const unsigned char *sha1); diff --git a/path.c b/path.c index 495d17c..5b61709 100644 --- a/path.c +++ b/path.c @@ -11,6 +11,7 @@ * which is what it's designed for. */ #include "cache.h" +#include static char pathname[PATH_MAX]; static char bad_path[] = "/bad-path/"; @@ -89,3 +90,74 @@ char *safe_strncpy(char *dest, const char *src, size_t n) return dest; } + +static char *current_dir() +{ + return getcwd(pathname, sizeof(pathname)); +} + +/* Take a raw path from is_git_repo() and canonicalize it using Linus' + * idea of a blind chdir() and getcwd(). */ +static const char *canonical_path(char *path, int strict) +{ + char *dir = path; + + if(strict && *dir != '/') + return NULL; + + if(*dir == '~') { /* user-relative path */ + struct passwd *pw; + char *slash = strchr(dir, '/'); + + dir++; + /* '~/' and '~' (no slash) means users own home-dir */ + if(!*dir || *dir == '/') + pw = getpwuid(getuid()); + else { + if (slash) { + *slash = '\0'; + pw = getpwnam(dir); + *slash = '/'; + } + else + pw = getpwnam(dir); + } + + /* make sure we got something back that we can chdir() to */ + if(!pw || chdir(pw->pw_dir) < 0) + return NULL; + + if(!slash || !slash[1]) /* no path following username */ + return current_dir(); + + dir = slash + 1; + } + + /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ + if(chdir(dir) < 0) + return NULL; + + return current_dir(); +} + +char *enter_repo(char *path, int strict) +{ + if(!path) + return NULL; + + if(!canonical_path(path, strict)) { + if(strict || !canonical_path(mkpath("%s.git", path), strict)) + return NULL; + } + + /* This is perfectly safe, and people tend to think of the directory + * where they ran git-init-db as their repository, so humour them. */ + (void)chdir(".git"); + + if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0) { + putenv("GIT_DIR=."); + return current_dir(); + } + + return NULL; +} -- cgit v0.10.2-6-g49f6 From 0870ca7fabe6f25095431280e480859f7c66e8ab Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 18 Nov 2005 14:59:34 -0800 Subject: Do not DWIM in userpath library under strict mode. This should force git-daemon administrator's job a bit harder because the exact paths need to be given in the whitelist, but at the same time makes the auditing easier. This moves validate_symref() from refs.c to path.c, because we need to link path.c with git-daemon for its "enter_repo()", but we do not want to link the daemon with the rest of git libraries and its requirements. Signed-off-by: Junio C Hamano diff --git a/path.c b/path.c index 5b61709..d635470 100644 --- a/path.c +++ b/path.c @@ -91,20 +91,55 @@ char *safe_strncpy(char *dest, const char *src, size_t n) return dest; } +int validate_symref(const char *path) +{ + struct stat st; + char *buf, buffer[256]; + int len, fd; + + if (lstat(path, &st) < 0) + return -1; + + /* Make sure it is a "refs/.." symlink */ + if (S_ISLNK(st.st_mode)) { + len = readlink(path, buffer, sizeof(buffer)-1); + if (len >= 5 && !memcmp("refs/", buffer, 5)) + return 0; + return -1; + } + + /* + * Anything else, just open it and try to see if it is a symbolic ref. + */ + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + /* + * Is it a symbolic ref? + */ + if (len < 4 || memcmp("ref:", buffer, 4)) + return -1; + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + if (len >= 5 && !memcmp("refs/", buf, 5)) + return 0; + return -1; +} + static char *current_dir() { return getcwd(pathname, sizeof(pathname)); } -/* Take a raw path from is_git_repo() and canonicalize it using Linus' - * idea of a blind chdir() and getcwd(). */ -static const char *canonical_path(char *path, int strict) +static int user_chdir(char *path) { char *dir = path; - if(strict && *dir != '/') - return NULL; - if(*dir == '~') { /* user-relative path */ struct passwd *pw; char *slash = strchr(dir, '/'); @@ -125,19 +160,19 @@ static const char *canonical_path(char *path, int strict) /* make sure we got something back that we can chdir() to */ if(!pw || chdir(pw->pw_dir) < 0) - return NULL; + return -1; if(!slash || !slash[1]) /* no path following username */ - return current_dir(); + return 0; dir = slash + 1; } /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ if(chdir(dir) < 0) - return NULL; + return -1; - return current_dir(); + return 0; } char *enter_repo(char *path, int strict) @@ -145,16 +180,24 @@ char *enter_repo(char *path, int strict) if(!path) return NULL; - if(!canonical_path(path, strict)) { - if(strict || !canonical_path(mkpath("%s.git", path), strict)) + if (strict) { + if((path[0] != '/') || chdir(path) < 0) return NULL; } + else { + if (!*path) + ; /* happy -- no chdir */ + else if (!user_chdir(path)) + ; /* happy -- as given */ + else if (!user_chdir(mkpath("%s.git", path))) + ; /* happy -- uemacs --> uemacs.git */ + else + return NULL; + (void)chdir(".git"); + } - /* This is perfectly safe, and people tend to think of the directory - * where they ran git-init-db as their repository, so humour them. */ - (void)chdir(".git"); - - if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0) { + if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && + validate_symref("HEAD") == 0) { putenv("GIT_DIR=."); return current_dir(); } diff --git a/refs.c b/refs.c index f324be5..ac26198 100644 --- a/refs.c +++ b/refs.c @@ -10,46 +10,6 @@ #define USE_SYMLINK_HEAD 1 #endif -int validate_symref(const char *path) -{ - struct stat st; - char *buf, buffer[256]; - int len, fd; - - if (lstat(path, &st) < 0) - return -1; - - /* Make sure it is a "refs/.." symlink */ - if (S_ISLNK(st.st_mode)) { - len = readlink(path, buffer, sizeof(buffer)-1); - if (len >= 5 && !memcmp("refs/", buffer, 5)) - return 0; - return -1; - } - - /* - * Anything else, just open it and try to see if it is a symbolic ref. - */ - fd = open(path, O_RDONLY); - if (fd < 0) - return -1; - len = read(fd, buffer, sizeof(buffer)-1); - close(fd); - - /* - * Is it a symbolic ref? - */ - if (len < 4 || memcmp("ref:", buffer, 4)) - return -1; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - if (len >= 5 && !memcmp("refs/", buf, 5)) - return 0; - return -1; -} - const char *resolve_ref(const char *path, unsigned char *sha1, int reading) { int depth = MAXDEPTH, len; -- cgit v0.10.2-6-g49f6 From 8d6301329156e4eeb0d25d6bce14f6e958314986 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Thu, 17 Nov 2005 20:37:14 +0100 Subject: Server-side support for user-relative paths. This patch basically just removes the redundant code from {receive,upload}-pack.c in favour of the library code in path.c. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/receive-pack.c b/receive-pack.c index 8f157bc..1873506 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -248,11 +248,11 @@ static void unpack(void) int main(int argc, char **argv) { int i; - const char *dir = NULL; + char *dir = NULL; argv++; for (i = 1; i < argc; i++) { - const char *arg = *argv++; + char *arg = *argv++; if (*arg == '-') { /* Do flag handling here */ @@ -265,18 +265,9 @@ int main(int argc, char **argv) if (!dir) usage(receive_pack_usage); - /* chdir to the directory. If that fails, try appending ".git" */ - if (chdir(dir) < 0) { - if (chdir(mkpath("%s.git", dir)) < 0) - die("unable to cd to %s", dir); - } - - /* If we have a ".git" directory, chdir to it */ - chdir(".git"); - putenv("GIT_DIR=."); + if(!enter_repo(dir, 0)) + die("'%s': unable to chdir or not a git archive", dir); - if (access("objects", X_OK) < 0 || access("refs/heads", X_OK) < 0) - die("%s doesn't appear to be a git directory", dir); write_head_info(); /* EOF */ diff --git a/upload-pack.c b/upload-pack.c index be63132..1834b6b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -248,7 +248,7 @@ static int upload_pack(void) int main(int argc, char **argv) { - const char *dir; + char *dir; int i; int strict = 0; @@ -275,18 +275,9 @@ int main(int argc, char **argv) usage(upload_pack_usage); dir = argv[i]; - /* chdir to the directory. If that fails, try appending ".git" */ - if (chdir(dir) < 0) { - if (strict || chdir(mkpath("%s.git", dir)) < 0) - die("git-upload-pack unable to chdir to %s", dir); - } - if (!strict) - chdir(".git"); - - if (access("objects", X_OK) || access("refs", X_OK)) - die("git-upload-pack: %s doesn't seem to be a git archive", dir); + if (!enter_repo(dir, strict)) + die("'%s': unable to chdir or not a git archive", dir); - putenv("GIT_DIR=."); upload_pack(); return 0; } -- cgit v0.10.2-6-g49f6 From faea9ccbadf75128a9e394dca80ed9454e19c7a9 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Thu, 17 Nov 2005 20:37:14 +0100 Subject: Client side support for user-relative paths. With this patch, the client side passes identical paths for these two: ssh://host.xz/~junio/repo host.xz:~junio/repo Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index c2badc7..73187a1 100644 --- a/connect.c +++ b/connect.c @@ -454,34 +454,45 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) int git_connect(int fd[2], char *url, const char *prog) { char command[1024]; - char *host, *path; - char *colon; + char *host, *path = url; + char *colon = NULL; int pipefd[2][2]; pid_t pid; - enum protocol protocol; - - host = NULL; - path = url; - colon = strchr(url, ':'); - protocol = PROTO_LOCAL; - if (colon) { - *colon = 0; + enum protocol protocol = PROTO_LOCAL; + + host = strstr(url, "://"); + if(host) { + *host = '\0'; + protocol = get_protocol(url); + host += 3; + path = strchr(host, '/'); + } + else { host = url; - path = colon+1; - protocol = PROTO_SSH; - if (!memcmp(path, "//", 2)) { - char *slash = strchr(path + 2, '/'); - if (slash) { - int nr = slash - path - 2; - memmove(path, path+2, nr); - path[nr] = 0; - protocol = get_protocol(url); - host = path; - path = slash; - } + if ((colon = strchr(host, ':'))) { + protocol = PROTO_SSH; + *colon = '\0'; + path = colon + 1; } } + if (!path || !*path) + die("No path specified. See 'man git-pull' for valid url syntax"); + + /* + * null-terminate hostname and point path to ~ for URL's like this: + * ssh://host.xz/~user/repo + */ + if (protocol != PROTO_LOCAL && host != url) { + char *ptr = path; + if (path[1] == '~') + path++; + else + path = strdup(ptr); + + *ptr = '\0'; + } + if (protocol == PROTO_GIT) return git_tcp_connect(fd, prog, host, path); -- cgit v0.10.2-6-g49f6 From 7b5d895da6d86eda36af84516a359192ca1da469 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Thu, 17 Nov 2005 20:37:14 +0100 Subject: Documentation update for user-relative paths. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index ddd5823..a7628aa 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -5,11 +5,30 @@ to name the remote repository: + =============================================================== -- Rsync URL: rsync://remote.machine/path/to/repo.git/ -- HTTP(s) URL: http://remote.machine/path/to/repo.git/ -- git URL: git://remote.machine/path/to/repo.git/ -- ssh URL: remote.machine:/path/to/repo.git/ -- Local directory: /path/to/repo.git/ +- rsync://host.xz/path/to/repo.git/ +- http://host.xz/path/to/repo.git/ +- https://host.xz/path/to/repo.git/ +- git://host.xz/path/to/repo.git/ +- ssh://host.xz/path/to/repo.git/ +- ssh://host.xz/~user/path/to/repo.git/ +- ssh://host.xz/~/path/to/repo.git +=============================================================== ++ + SSH Is the default transport protocol and also supports an + scp-like syntax. Both syntaxes support username expansion. + The following three are identical to the last three above, + respectively: ++ +=============================================================== +- host.xz:/path/to/repo.git/ +- host.xz:~user/path/to/repo.git/ +- host.xz:path/to/repo.git +=============================================================== ++ + To sync with a local directory, use: + +=============================================================== +- /path/to/repo.git/ =============================================================== + In addition to the above, as a short-hand, the name of a -- cgit v0.10.2-6-g49f6 From 4ddba79db76bd6425f00e99ceb1d82d179319aec Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 20 Nov 2005 06:52:22 +0100 Subject: git-config-set: add more options ... namely --replace-all, to replace any amount of matching lines, not just 0 or 1, --get, to get the value of one key, --get-all, the multivar version of --get, and --unset-all, which deletes all matching lines from .git/config Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-config-set.txt b/Documentation/git-config-set.txt index 8e897be..c707fbc 100644 --- a/Documentation/git-config-set.txt +++ b/Documentation/git-config-set.txt @@ -8,12 +8,18 @@ git-config-set - Set options in .git/config. SYNOPSIS -------- -'git-config-set' ( name [value [value_regex]] | --unset name [value_regex] ) +'git-config-set' name [value [value_regex]] +'git-config-set' --replace-all name [value [value_regex]] +'git-config-set' --get name [value_regex] +'git-config-set' --get-all name [value_regex] +'git-config-set' --unset name [value_regex] +'git-config-set' --unset-all name [value_regex] DESCRIPTION ----------- -You can set/replace/unset options with this command. The name is actually -the section and the key separated by a dot, and the value will be escaped. +You can query/set/replace/unset options with this command. The name is +actually the section and the key separated by a dot, and the value will be +escaped. If you want to set/unset an option which can occor on multiple lines, you should provide a POSIX regex for the value. @@ -31,8 +37,23 @@ This command will fail if OPTIONS ------- +--replace-all:: + Default behaviour is to replace at most one line. This replaces + all lines matching the key (and optionally the value_regex) + +--get:: + Get the value for a given key (optionally filtered by a regex + matching the value). + +--get-all:: + Like get, but does not fail if the number of values for the key + is not exactly one. + --unset:: - Remove the given option from .git/config + Remove the line matching the key from .git/config. + +--unset-all:: + Remove all matching lines from .git/config. EXAMPLE @@ -84,14 +105,39 @@ To delete the entry for renames, do % git config-set --unset diff.renames ------------ -or just +If you want to delete an entry for a multivar (like proxy.command above), +you have to provide a regex matching the value of exactly one line. + +To query the value for a given key, do ------------ -% git config-set diff.renames +% git config-set --get core.filemode ------------ -If you want to delete an entry for a multivar (like proxy.command above), -you have to provide a regex matching the value of exactly one line. +or + +------------ +% git config-set core.filemode +------------ + +or, to query a multivar: + +------------ +% git config-set --get proxy.command "for kernel.org$" +------------ + +If you want to know all the values for a multivar, do: + +------------ +% git config-set --get-all proxy.command +------------ + +If you like to live dangerous, you can replace *all* proxy.commands by a +new one with + +------------ +% git config-set --replace-all proxy.command ssh +------------ Author diff --git a/cache.h b/cache.h index e2be3e7..a7c1bbd 100644 --- a/cache.h +++ b/cache.h @@ -192,7 +192,6 @@ extern int diff_rename_limit_default; /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -extern char *enter_repo(char *path, int strict); extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *sha1_file_name(const unsigned char *sha1); extern char *sha1_pack_name(const unsigned char *sha1); @@ -388,7 +387,7 @@ extern int git_config(config_fn_t fn); extern int git_config_int(const char *, const char *); extern int git_config_bool(const char *, const char *); extern int git_config_set(const char *, const char *); -extern int git_config_set_multivar(const char *, const char *, const char *); +extern int git_config_set_multivar(const char *, const char *, const char *, int); #define MAX_GITNAME (1000) extern char git_default_email[MAX_GITNAME]; diff --git a/config-set.c b/config-set.c index 1b1547b..90a28b3 100644 --- a/config-set.c +++ b/config-set.c @@ -1,24 +1,106 @@ #include "cache.h" +#include static const char git_config_set_usage[] = -"git-config-set name [value [value_regex]] | --unset name [value_regex]"; +"git-config-set [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; + +static char* key = NULL; +static char* value = NULL; +static regex_t* regex = NULL; +static int do_all = 0; +static int seen = 0; + +static int show_config(const char* key_, const char* value_) +{ + if (!strcmp(key_, key) && + (regex == NULL || + !regexec(regex, value_, 0, NULL, 0))) { + if (do_all) { + printf("%s\n", value_); + return 0; + } + if (seen > 0) { + fprintf(stderr, "More than one value: %s\n", value); + free(value); + } + value = strdup(value_); + seen++; + } + return 0; +} + +static int get_value(const char* key_, const char* regex_) +{ + int i; + + key = malloc(strlen(key_)+1); + for (i = 0; key_[i]; i++) + key[i] = tolower(key_[i]); + + if (regex_) { + regex = (regex_t*)malloc(sizeof(regex_t)); + if (regcomp(regex, regex_, REG_EXTENDED)) { + fprintf(stderr, "Invalid pattern: %s\n", regex_); + return -1; + } + } + + i = git_config(show_config); + if (value) { + printf("%s\n", value); + free(value); + } + free(key); + if (regex) { + regfree(regex); + free(regex); + } + + if (do_all) + return 0; + + return seen == 1 ? 0 : 1; +} int main(int argc, const char **argv) { setup_git_directory(); switch (argc) { case 2: - return git_config_set(argv[1], NULL); + return get_value(argv[1], NULL); case 3: if (!strcmp(argv[1], "--unset")) return git_config_set(argv[2], NULL); - else + else if (!strcmp(argv[1], "--unset-all")) + return git_config_set_multivar(argv[2], NULL, NULL, 1); + else if (!strcmp(argv[1], "--get")) + return get_value(argv[2], NULL); + else if (!strcmp(argv[1], "--get-all")) { + do_all = 1; + return get_value(argv[2], NULL); + } else + return git_config_set(argv[1], argv[2]); case 4: if (!strcmp(argv[1], "--unset")) - return git_config_set_multivar(argv[2], NULL, argv[3]); + return git_config_set_multivar(argv[2], NULL, argv[3], 0); + else if (!strcmp(argv[1], "--unset-all")) + return git_config_set_multivar(argv[2], NULL, argv[3], 1); + else if (!strcmp(argv[1], "--get")) + return get_value(argv[2], argv[3]); + else if (!strcmp(argv[1], "--get-all")) { + 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); else - return git_config_set_multivar(argv[1], argv[2], argv[3]); + + return git_config_set_multivar(argv[1], argv[2], argv[3], 0); + case 5: + if (!strcmp(argv[1], "--replace-all")) + return git_config_set_multivar(argv[2], argv[3], argv[4], 1); + case 1: default: usage(git_config_set_usage); } diff --git a/config.c b/config.c index bbcafff..697d79f 100644 --- a/config.c +++ b/config.c @@ -263,11 +263,15 @@ int git_config(config_fn_t fn) /* * Find all the stuff for git_config_set() below. */ + +#define MAX_MATCHES 512 + static struct { int baselen; char* key; regex_t* value_regex; - off_t offset; + int multi_replace; + off_t offset[MAX_MATCHES]; enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state; int seen; } store; @@ -279,12 +283,16 @@ static int store_aux(const char* key, const char* value) if (!strcmp(key, store.key) && (store.value_regex == NULL || !regexec(store.value_regex, value, 0, NULL, 0))) { - if (store.seen == 1) { + if (store.seen == 1 && store.multi_replace == 0) { fprintf(stderr, "Warning: %s has multiple values\n", key); + } else if (store.seen >= MAX_MATCHES) { + fprintf(stderr, "Too many matches\n"); + return 1; } - store.offset = ftell(config_file); + + store.offset[store.seen] = ftell(config_file); store.seen++; } break; @@ -293,14 +301,15 @@ static int store_aux(const char* key, const char* value) store.state = SECTION_END_SEEN; break; } else - store.offset = ftell(config_file); + /* do not increment matches: this is no match */ + store.offset[store.seen] = ftell(config_file); /* fallthru */ case SECTION_END_SEEN: case START: if (!strcmp(key, store.key) && (store.value_regex == NULL || !regexec(store.value_regex, value, 0, NULL, 0))) { - store.offset = ftell(config_file); + store.offset[store.seen] = ftell(config_file); store.state = KEY_SEEN; store.seen++; } else if(!strncmp(key, store.key, store.baselen)) @@ -334,14 +343,38 @@ static void store_write_pair(int fd, const char* key, const char* value) write(fd, "\n", 1); } +static int find_beginning_of_line(const char* contents, int size, + int offset_, int* found_bracket) +{ + int equal_offset = size, bracket_offset = size; + int offset; + + for (offset = offset_-2; offset > 0 + && contents[offset] != '\n'; offset--) + switch (contents[offset]) { + case '=': equal_offset = offset; break; + case ']': bracket_offset = offset; break; + } + if (bracket_offset < equal_offset) { + *found_bracket = 1; + offset = bracket_offset+1; + } else + offset++; + + return offset; +} + int git_config_set(const char* key, const char* value) { - return git_config_set_multivar(key, value, NULL); + return git_config_set_multivar(key, value, NULL, 0); } /* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. + * if multi_replace==0, nothing, or only one matching key/value is replaced, + * else all matching key/values (regardless how many) are removed, + * before the new pair is written. * * Returns 0 on success. * @@ -360,7 +393,7 @@ int git_config_set(const char* key, const char* value) * */ int git_config_set_multivar(const char* key, const char* value, - const char* value_regex) + const char* value_regex, int multi_replace) { int i; struct stat st; @@ -368,6 +401,8 @@ int git_config_set_multivar(const char* key, const char* value, char* config_file = strdup(git_path("config")); char* lock_file = strdup(git_path("config.lock")); + store.multi_replace = multi_replace; + /* * Since "key" actually contains the section name and the real * key name separated by a dot, we have to know where the dot is. @@ -431,7 +466,7 @@ int git_config_set_multivar(const char* key, const char* value, } else{ int in_fd; char* contents; - int offset, new_line = 0; + int i, copy_begin, copy_end, new_line = 0; if (value_regex == NULL) store.value_regex = NULL; @@ -446,7 +481,7 @@ int git_config_set_multivar(const char* key, const char* value, } } - store.offset = 0; + store.offset[0] = 0; store.state = START; store.seen = 0; @@ -472,52 +507,42 @@ int git_config_set_multivar(const char* key, const char* value, free(store.value_regex); } - /* if nothing to unset, error out */ - if (store.seen == 0 && value == NULL) { + /* if nothing to unset, or too many matches, error out */ + if ((store.seen == 0 && value == NULL) || + (store.seen > 1 && multi_replace == 0)) { close(fd); unlink(lock_file); return 5; } - store.key = (char*)key; - in_fd = open(config_file, O_RDONLY, 0666); contents = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in_fd, 0); close(in_fd); - if (store.offset == 0) { - store.offset = offset = st.st_size; - } else if (store.state != KEY_SEEN) { - offset = store.offset; - } else { - int equal_offset = st.st_size, - bracket_offset = st.st_size; - - if (value == NULL && store.seen > 1) { - fprintf(stderr, "Cannot remove multivar (%s has %d values\n", key, store.seen); - close(fd); - unlink(lock_file); - return 7; - } - for (offset = store.offset-2; offset > 0 - && contents[offset] != '\n'; offset--) - switch (contents[offset]) { - case '=': equal_offset = offset; break; - case ']': bracket_offset = offset; break; - } - if (bracket_offset < equal_offset) { - new_line = 1; - offset = bracket_offset+1; + if (store.seen == 0) + store.seen = 1; + + for (i = 0, copy_begin = 0; i < store.seen; i++) { + if (store.offset[i] == 0) { + store.offset[i] = copy_end = st.st_size; + } else if (store.state != KEY_SEEN) { + copy_end = store.offset[i]; } else - offset++; + copy_end = find_beginning_of_line( + contents, st.st_size, + store.offset[i]-2, &new_line); + + /* write the first part of the config */ + if (copy_end > copy_begin) { + write(fd, contents + copy_begin, + copy_end - copy_begin); + if (new_line) + write(fd, "\n", 1); + } + copy_begin = store.offset[i]; } - /* write the first part of the config */ - write(fd, contents, offset); - if (new_line) - write(fd, "\n", 1); - /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) @@ -526,9 +551,9 @@ int git_config_set_multivar(const char* key, const char* value, } /* write the rest of the config */ - if (store.offset < st.st_size) - write(fd, contents + store.offset, - st.st_size - store.offset); + if (copy_begin < st.st_size) + write(fd, contents + copy_begin, + st.st_size - copy_begin); munmap(contents, st.st_size); unlink(config_file); @@ -544,3 +569,4 @@ int git_config_set_multivar(const char* key, const char* value, return 0; } + diff --git a/t/t1300-config-set.sh b/t/t1300-config-set.sh new file mode 100644 index 0000000..717bf4d --- /dev/null +++ b/t/t1300-config-set.sh @@ -0,0 +1,224 @@ +#!/bin/sh +# +# Copyright (c) 2005 Johannes Schindelin +# + +test_description='Test git-config-set in different settings' + +. ./test-lib.sh + +test -f .git/config && rm .git/config + +git-config-set core.penguin "little blue" + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue +EOF + +test_expect_success 'initial' 'cmp .git/config expect' + +git-config-set Core.Movie BadPhysics + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +EOF + +test_expect_success 'mixed case' 'cmp .git/config expect' + +git-config-set Cores.WhatEver Second + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +git-config-set CORE.UPPERCASE true + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics + UPPERCASE = true +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +cat > .git/config << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha ="beta" # last silly comment +haha = hello + haha = bello +[nextSection] noNewline = ouch +EOF + +cp .git/config .git/config2 + +test_expect_success 'multiple unset' \ + 'git-config-set --unset-all beta.haha' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] noNewline = ouch +EOF + +test_expect_success 'multiple unset is correct' 'cmp .git/config expect' + +mv .git/config2 .git/config + +test_expect_success '--replace-all' \ + 'git-config-set --replace-all beta.haha gamma' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = gamma +[nextSection] noNewline = ouch +EOF + +test_expect_success 'all replaced' 'cmp .git/config expect' + +git-config-set beta.haha alpha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] noNewline = ouch +EOF + +test_expect_success 'really mean test' 'cmp .git/config expect' + +git-config-set nextsection.nonewline wow + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] + nonewline = wow +EOF + +test_expect_success 'really really mean test' 'cmp .git/config expect' + +test_expect_success 'get value' 'test alpha = $(git-config-set beta.haha)' +git-config-set --unset beta.haha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow +EOF + +test_expect_success 'unset' 'cmp .git/config expect' + +git-config-set nextsection.NoNewLine "wow2 for me" "for me$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar' 'cmp .git/config expect' + +test_expect_failure 'ambiguous get' \ + 'git-config-set --get nextsection.nonewline' + +test_expect_success 'get multivar' \ + 'git-config-set --get-all nextsection.nonewline' + +git-config-set nextsection.nonewline "wow3" "wow$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow3 + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar replace' 'cmp .git/config expect' + +test_expect_failure 'ambiguous value' 'git-config-set nextsection.nonewline' + +test_expect_failure 'ambiguous unset' \ + 'git-config-set --unset nextsection.nonewline' + +test_expect_failure 'invalid unset' \ + 'git-config-set --unset somesection.nonewline' + +git-config-set --unset nextsection.nonewline "wow3$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar unset' 'cmp .git/config expect' + +test_expect_failure 'invalid key' 'git-config-set inval.2key blabla' + +test_expect_success 'correct key' 'git-config-set 123456.a123 987' + +test_done + diff --git a/t1300-config-set.sh b/t1300-config-set.sh deleted file mode 100644 index df89216..0000000 --- a/t1300-config-set.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Johannes Schindelin -# - -test_description='Test git-config-set in different settings' - -. ./test-lib.sh - -test -f .git/config && rm .git/config - -git-config-set core.penguin "little blue" - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue -EOF - -test_expect_success 'initial' 'cmp .git/config expect' - -git-config-set Core.Movie BadPhysics - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics -EOF - -test_expect_success 'mixed case' 'cmp .git/config expect' - -git-config-set Cores.WhatEver Second - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics -[Cores] - WhatEver = Second -EOF - -test_expect_success 'similar section' 'cmp .git/config expect' - -git-config-set CORE.UPPERCASE true - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics - UPPERCASE = true -[Cores] - WhatEver = Second -EOF - -test_expect_success 'similar section' 'cmp .git/config expect' - -cat > .git/config << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha ="beta" # last silly comment -[nextSection] noNewline = ouch -EOF - -git-config-set beta.haha alpha - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha = alpha -[nextSection] noNewline = ouch -EOF - -test_expect_success 'really mean test' 'cmp .git/config expect' - -git-config-set nextsection.nonewline wow - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha = alpha -[nextSection] - nonewline = wow -EOF - -test_expect_success 'really really mean test' 'cmp .git/config expect' - -git-config-set beta.haha - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow -EOF - -test_expect_success 'unset' 'cmp .git/config expect' - -git-config-set nextsection.NoNewLine "wow2 for me" "for me$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar' 'cmp .git/config expect' - -git-config-set nextsection.nonewline "wow3" "wow$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow3 - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar replace' 'cmp .git/config expect' - -test_expect_failure 'ambiguous unset' \ - 'git-config-set --unset nextsection.nonewline' - -test_expect_failure 'invalid unset' \ - 'git-config-set --unset somesection.nonewline' - -git-config-set --unset nextsection.nonewline "wow3$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar unset' 'cmp .git/config expect' - -test_expect_failure 'invalid key' 'git-config-set inval.2key blabla' - -test_expect_success 'correct key' 'git-config-set 123456.a123 987' - -test_done - -- cgit v0.10.2-6-g49f6 From 13cfdfd5fade73c7784ec45506b446e40e76c742 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 23:50:48 -0800 Subject: Documentation: add hooks/update example. Signed-off-by: Junio C Hamano diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt new file mode 100644 index 0000000..dacaf17 --- /dev/null +++ b/Documentation/howto/update-hook-example.txt @@ -0,0 +1,105 @@ +From: Junio C Hamano +Subject: control access to branches. +Date: Thu, 17 Nov 2005 23:55:32 -0800 +Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net> +Abstract: An example hooks/update script is presented to + implement repository maintenance policies, such as who can push + into which branch and who can make a tag. + +When your developer runs git-push into the repository, +git-receive-pack is run (either locally or over ssh) as that +developer, so is hooks/update script. Quoting from the relevant +section of the documentation: + + Before each ref is updated, if $GIT_DIR/hooks/update file exists + and executable, it is called with three parameters: + + $GIT_DIR/hooks/update refname sha1-old sha1-new + + The refname parameter is relative to $GIT_DIR; e.g. for the + master head this is "refs/heads/master". Two sha1 are the + object names for the refname before and after the update. Note + that the hook is called before the refname is updated, so either + sha1-old is 0{40} (meaning there is no such ref yet), or it + should match what is recorded in refname. + +So if your policy is (1) always require fast-forward push +(i.e. never allow "git-push repo +branch:branch"), (2) you +have a list of users allowed to update each branch, and (3) you +do not let tags to be overwritten, then: + + #!/bin/sh + # This is a sample hooks/update script, written by JC + # in his e-mail buffer, so naturally it is not tested + # but hopefully would convey the idea. + + umask 002 + case "$1" in + refs/tags/*) + # No overwriting an existing tag + if test -f "$GIT_DIR/$1" + then + exit 1 + fi + ;; + refs/heads/*) + # No rebasing or rewinding + if expr "$2" : '0*$' >/dev/null + then + # creating a new branch + ; + else + # updating -- make sure it is a fast forward + mb=`git-merge-base "$2" "$3"` + case "$mb,$2" in + "$2,$mb") + ;; # fast forward -- happy + *) + exit 1 ;; # unhappy + esac + fi + ;; + *) + # No funny refs allowed + exit 1 + ;; + esac + + # Is the user allowed to update it? + me=`id -u -n` ;# e.g. "junio" + while read head_pattern users + do + if expr "$1" : "$head_pattern" >/dev/null + then + case " $users " in + *" $me "*) + exit 0 ;; # happy + ' * ') + exit 0 ;; # anybody + esac + fi + done + exit 1 + +For the sake of simplicity, I assumed that you keep something +like this in $GIT_DIR/info/allowed-pushers file: + + refs/heads/master junio + refs/heads/cogito$ pasky + refs/heads/bw/ linus + refs/heads/tmp/ * + refs/tags/v[0-9]* junio + +With this, Linus can push or create "bw/penguin" or "bw/zebra" +or "bw/panda" branches, Pasky can do only "cogito", and I can do +master branch and make versioned tags. And anybody can do +tmp/blah branches. This assumes all the users are in a single +group that can write into $GIT_DIR/ and underneath. + + + + + + + + -- cgit v0.10.2-6-g49f6 From f6e2869f48f7a9b51a2a867ce955c39a9314ef3b Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 20 Nov 2005 23:08:22 +1100 Subject: gitk: Fix some bugs introduced by speedup changes Commits that weren't read from git-rev-list, i.e. the ones displayed with an open circle, were displayed incorrectly: the headline was null if there was only one line, and the commit comment was put all on one line. Also, the terminal commits weren't displayed when -r was used. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index ab2d9b8..58b4abc 100755 --- a/gitk +++ b/gitk @@ -218,6 +218,8 @@ proc parsecommit {id contents listed olds} { set i [string first "\n" $comment] if {$i >= 0} { set headline [string trim [string range $comment 0 $i]] + } else { + set headline $comment } if {!$listed} { # git-rev-list indents the comment by 4 spaces; @@ -226,6 +228,7 @@ proc parsecommit {id contents listed olds} { foreach line [split $comment "\n"] { append newcomment " " append newcomment $line + append newcomment "\n" } set comment $newcomment } @@ -1503,17 +1506,15 @@ proc drawrest {} { global numcommits ncmupdate global nextupdate startmsecs revlistorder - if {!$revlistorder} { - set level [decidenext] - if {$level >= 0} { - set phase drawgraph - while 1 { - lappend displayorder [lindex $todo $level] - set hard [updatetodo $level $datemode] - if {$hard} { - set level [decidenext] - if {$level < 0} break - } + set level [decidenext] + if {$level >= 0} { + set phase drawgraph + while 1 { + lappend displayorder [lindex $todo $level] + set hard [updatetodo $level $datemode] + if {$hard} { + set level [decidenext] + if {$level < 0} break } } } -- cgit v0.10.2-6-g49f6 From 9ae2172aed289f2706a0e88288909fa47eddd7e7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 20 Nov 2005 10:40:31 -0800 Subject: merge-one-file: use rmdir -p The flag is universally available, even on VMS; use it. Signed-off-by: Junio C Hamano diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh index b285990..c3eca8b 100755 --- a/git-merge-one-file.sh +++ b/git-merge-one-file.sh @@ -26,11 +26,7 @@ case "${1:-.}${2:-.}${3:-.}" in fi if test -f "$4"; then rm -f -- "$4" && - dn="$4" && - while dn=$(expr "$dn" : '\(.*\)/') && rmdir "$dn" 2>/dev/null - do - :; - done + rmdir -p "$(expr "$4" : '\(.*\)/')" 2>/dev/null fi && exec git-update-index --remove -- "$4" ;; -- cgit v0.10.2-6-g49f6 From a6322d079b1d2e2cb72da1271171b35a173f3d22 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Sun, 20 Nov 2005 13:14:37 +0100 Subject: merge-recursive: Replace 'except:' Plain except:s are evil as they will catch all kinds of exceptions including NameError and AttrubiteError. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 37258ad..0129233 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -245,7 +245,7 @@ def updateFileExt(sha, mode, path, updateCache, updateWd): try: createDir = not stat.S_ISDIR(os.lstat(p).st_mode) - except: + except OSError: createDir = True if createDir: @@ -295,7 +295,7 @@ def removeFile(clean, path): raise try: os.removedirs(os.path.dirname(path)) - except: + except OSError: pass def uniquePath(path, branch): -- cgit v0.10.2-6-g49f6 From f98d863d2122e1b8781dfb9889df98876a26f315 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 20 Nov 2005 13:24:18 +0100 Subject: git-config-set: support selecting values by non-matching regex Extend the regex syntax of value_regex so that prepending an exclamation mark means non-match: [core] quetzal = "Dodo" for Brainf*ck quetzal = "T. Rex" for Malbolge quetzal = "cat" You can match the third line with git-config-set --get quetzal '! for ' Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/Documentation/git-config-set.txt b/Documentation/git-config-set.txt index c707fbc..bfbd421 100644 --- a/Documentation/git-config-set.txt +++ b/Documentation/git-config-set.txt @@ -22,7 +22,9 @@ actually the section and the key separated by a dot, and the value will be escaped. If you want to set/unset an option which can occor on multiple lines, you -should provide a POSIX regex for the value. +should provide a POSIX regex for the value. If you want to handle the lines +*not* matching the regex, just prepend a single exlamation mark in front +(see EXAMPLES). This command will fail if @@ -82,6 +84,7 @@ Given a .git/config like this: command="ssh" for "ssh://kernel.org/" command="proxy-command" for kernel.org command="myprotocol-command" for "my://" + command=default-proxy ; for all the rest you can set the filemode to true with @@ -139,6 +142,19 @@ new one with % git config-set --replace-all proxy.command 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 config-set proxy.command ssh '! for ' +------------ + +To actually match only values with an exclamation mark, you have to + +------------ +% git config-set section.key value '[!]' +------------ + Author ------ diff --git a/config-set.c b/config-set.c index 90a28b3..5f654f7 100644 --- a/config-set.c +++ b/config-set.c @@ -8,13 +8,15 @@ static char* key = NULL; static char* value = NULL; static regex_t* regex = NULL; static int do_all = 0; +static int do_not_match = 0; static int seen = 0; static int show_config(const char* key_, const char* value_) { if (!strcmp(key_, key) && (regex == NULL || - !regexec(regex, value_, 0, NULL, 0))) { + (do_not_match ^ + !regexec(regex, value_, 0, NULL, 0)))) { if (do_all) { printf("%s\n", value_); return 0; @@ -38,6 +40,11 @@ static int get_value(const char* key_, const char* regex_) key[i] = tolower(key_[i]); if (regex_) { + if (regex_[0] == '!') { + do_not_match = 1; + regex_++; + } + regex = (regex_t*)malloc(sizeof(regex_t)); if (regcomp(regex, regex_, REG_EXTENDED)) { fprintf(stderr, "Invalid pattern: %s\n", regex_); diff --git a/config.c b/config.c index 697d79f..5d237c8 100644 --- a/config.c +++ b/config.c @@ -269,6 +269,7 @@ int git_config(config_fn_t fn) static struct { int baselen; char* key; + int do_not_match; regex_t* value_regex; int multi_replace; off_t offset[MAX_MATCHES]; @@ -276,13 +277,19 @@ static struct { int seen; } store; +static int matches(const char* key, const char* value) +{ + return !strcmp(key, store.key) && + (store.value_regex == NULL || + (store.do_not_match ^ + !regexec(store.value_regex, value, 0, NULL, 0))); +} + static int store_aux(const char* key, const char* value) { switch (store.state) { case KEY_SEEN: - if (!strcmp(key, store.key) && - (store.value_regex == NULL || - !regexec(store.value_regex, value, 0, NULL, 0))) { + if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { fprintf(stderr, "Warning: %s has multiple values\n", @@ -306,9 +313,7 @@ static int store_aux(const char* key, const char* value) /* fallthru */ case SECTION_END_SEEN: case START: - if (!strcmp(key, store.key) && - (store.value_regex == NULL || - !regexec(store.value_regex, value, 0, NULL, 0))) { + if (matches(key, value)) { store.offset[store.seen] = ftell(config_file); store.state = KEY_SEEN; store.seen++; @@ -471,6 +476,12 @@ int git_config_set_multivar(const char* key, const char* value, if (value_regex == NULL) store.value_regex = NULL; else { + if (value_regex[0] == '!') { + store.do_not_match = 1; + value_regex++; + } else + store.do_not_match = 0; + store.value_regex = (regex_t*)malloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { diff --git a/t/t1300-config-set.sh b/t/t1300-config-set.sh index 717bf4d..59b6c4c 100644 --- a/t/t1300-config-set.sh +++ b/t/t1300-config-set.sh @@ -69,6 +69,28 @@ EOF test_expect_success 'similar section' 'cmp .git/config expect' +test_expect_success 'replace with non-match' \ + 'git-config-set core.penguin kingpin !blue' + +test_expect_success 'replace with non-match (actually matching)' \ + 'git-config-set core.penguin "very blue" !kingpin' + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = very blue + Movie = BadPhysics + UPPERCASE = true + penguin = kingpin +[Cores] + WhatEver = Second +EOF + +test_expect_success 'non-match result' 'cmp .git/config expect' + cat > .git/config << EOF [beta] ; silly comment # another comment noIndent= sillyValue ; 'nother silly comment @@ -173,6 +195,12 @@ EOF test_expect_success 'multivar' 'cmp .git/config expect' +test_expect_success 'non-match' \ + 'git-config-set --get nextsection.nonewline !for' + +test_expect_success 'non-match value' \ + 'test wow = $(git-config-set --get nextsection.nonewline !for)' + test_expect_failure 'ambiguous get' \ 'git-config-set --get nextsection.nonewline' -- cgit v0.10.2-6-g49f6 From 757f17bca50f4a227db2974dd8fd10c8b61a69f1 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 21 Nov 2005 09:56:07 +1100 Subject: gitk: Disable fastdate stuff for now It has a fatal flaw in that it only handles timezones that are a multiple of an hour. It's really only needed with Tk8.5, where the clock format command has been reimplemented in Tcl and is much slower than in Tk8.4. Signed-off-by: Paul Mackerras diff --git a/gitk b/gitk index 58b4abc..3dd97e2 100755 --- a/gitk +++ b/gitk @@ -3636,8 +3636,11 @@ proc doquit {} { } proc formatdate {d} { - global hours nhours tfd + global hours nhours tfd fastdate + if {!$fastdate} { + return [clock format $d -format "%Y-%m-%d %H:%M:%S"] + } set hr [expr {$d / 3600}] set ms [expr {$d % 3600}] if {![info exists hours($hr)]} { @@ -3662,6 +3665,7 @@ set gaudydiff 0 set maxgraphpct 50 set maxwidth 16 set revlistorder 0 +set fastdate 0 set colors {green red blue magenta darkgrey brown orange} -- cgit v0.10.2-6-g49f6 From bd22c904a0f1c88a3a7bfa96bbf690de2f5cb278 Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Mon, 21 Nov 2005 02:52:52 +0200 Subject: Fix sparse warnings Make some functions static and convert func() function prototypes to to func(void). Fix declaration after statement, missing declaration and redundant declaration warnings. Signed-off-by: Timo Hirvonen Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index a7c1bbd..c7c6637 100644 --- a/cache.h +++ b/cache.h @@ -203,6 +203,7 @@ int git_mkstemp(char *path, size_t n, const char *template); int safe_create_leading_directories(char *path); char *safe_strncpy(char *, const char *, size_t); +char *enter_repo(char *path, int strict); /* Read and unpack a sha1 file into memory, write memory to a sha1 file */ extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size); diff --git a/name-rev.c b/name-rev.c index 59194f1..817e36b 100644 --- a/name-rev.c +++ b/name-rev.c @@ -230,8 +230,6 @@ int main(int argc, char **argv) fwrite(p_start, p - p_start, 1, stdout); } } else if (all) { - extern struct object **objs; - extern int nr_objs; int i; for (i = 0; i < nr_objs; i++) diff --git a/pack-redundant.c b/pack-redundant.c index fb6cb48..1519385 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -11,19 +11,19 @@ static const char pack_redundant_usage[] = "git-pack-redundant [ --verbose ] [ --alt-odb ] < --all | <.pack filename> ...>"; -int load_all_packs = 0, verbose = 0, alt_odb = 0; +static int load_all_packs = 0, verbose = 0, alt_odb = 0; struct llist_item { struct llist_item *next; char *sha1; }; -struct llist { +static struct llist { struct llist_item *front; struct llist_item *back; size_t size; } *all_objects; /* all objects which must be present in local packfiles */ -struct pack_list { +static struct pack_list { struct pack_list *next; struct packed_git *pack; struct llist *unique_objects; @@ -36,7 +36,7 @@ struct pll { size_t pl_size; }; -inline void llist_free(struct llist *list) +static inline void llist_free(struct llist *list) { while((list->back = list->front)) { list->front = list->front->next; @@ -45,14 +45,14 @@ inline void llist_free(struct llist *list) free(list); } -inline void llist_init(struct llist **list) +static inline void llist_init(struct llist **list) { *list = xmalloc(sizeof(struct llist)); (*list)->front = (*list)->back = NULL; (*list)->size = 0; } -struct llist * llist_copy(struct llist *list) +static struct llist * llist_copy(struct llist *list) { struct llist *ret; struct llist_item *new, *old, *prev; @@ -79,7 +79,7 @@ struct llist * llist_copy(struct llist *list) return ret; } -inline struct llist_item * llist_insert(struct llist *list, +static inline struct llist_item * llist_insert(struct llist *list, struct llist_item *after, char *sha1) { struct llist_item *new = xmalloc(sizeof(struct llist_item)); @@ -102,12 +102,12 @@ inline struct llist_item * llist_insert(struct llist *list, return new; } -inline struct llist_item * llist_insert_back(struct llist *list, char *sha1) +static inline struct llist_item * llist_insert_back(struct llist *list, char *sha1) { return llist_insert(list, list->back, sha1); } -inline struct llist_item * llist_insert_sorted_unique(struct llist *list, +static inline struct llist_item * llist_insert_sorted_unique(struct llist *list, char *sha1, struct llist_item *hint) { struct llist_item *prev = NULL, *l; @@ -129,7 +129,7 @@ inline struct llist_item * llist_insert_sorted_unique(struct llist *list, } /* returns a pointer to an item in front of sha1 */ -inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1, +static inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1, struct llist_item *hint) { struct llist_item *prev, *l; @@ -164,7 +164,7 @@ redo_from_start: } /* computes A\B */ -void llist_sorted_difference_inplace(struct llist *A, +static void llist_sorted_difference_inplace(struct llist *A, struct llist *B) { struct llist_item *hint, *b; @@ -178,7 +178,7 @@ void llist_sorted_difference_inplace(struct llist *A, } } -inline struct pack_list * pack_list_insert(struct pack_list **pl, +static inline struct pack_list * pack_list_insert(struct pack_list **pl, struct pack_list *entry) { struct pack_list *p = xmalloc(sizeof(struct pack_list)); @@ -188,7 +188,7 @@ inline struct pack_list * pack_list_insert(struct pack_list **pl, return p; } -inline size_t pack_list_size(struct pack_list *pl) +static inline size_t pack_list_size(struct pack_list *pl) { size_t ret = 0; while(pl) { @@ -198,7 +198,7 @@ inline size_t pack_list_size(struct pack_list *pl) return ret; } -struct pack_list * pack_list_difference(struct pack_list *A, +static struct pack_list * pack_list_difference(struct pack_list *A, struct pack_list *B) { struct pack_list *ret, *pl; @@ -218,7 +218,7 @@ struct pack_list * pack_list_difference(struct pack_list *A, return ret; } -void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) +static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) { int p1_off, p2_off; void *p1_base, *p2_base; @@ -250,7 +250,7 @@ void cmp_two_packs(struct pack_list *p1, struct pack_list *p2) } } -void pll_insert(struct pll **pll, struct pll **hint_table) +static void pll_insert(struct pll **pll, struct pll **hint_table) { struct pll *prev; int i = (*pll)->pl_size - 1; @@ -276,7 +276,7 @@ void pll_insert(struct pll **pll, struct pll **hint_table) /* all the permutations have to be free()d at the same time, * since they refer to each other */ -struct pll * get_all_permutations(struct pack_list *list) +static struct pll * get_all_permutations(struct pack_list *list) { struct pll *subset, *pll, *new_pll = NULL; /*silence warning*/ static struct pll **hint = NULL; @@ -323,7 +323,7 @@ struct pll * get_all_permutations(struct pack_list *list) return hint[0]; } -int is_superset(struct pack_list *pl, struct llist *list) +static int is_superset(struct pack_list *pl, struct llist *list) { struct llist *diff; @@ -342,7 +342,7 @@ int is_superset(struct pack_list *pl, struct llist *list) return 0; } -size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) +static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) { size_t ret = 0; int p1_off, p2_off; @@ -373,14 +373,14 @@ size_t sizeof_union(struct packed_git *p1, struct packed_git *p2) } /* another O(n^2) function ... */ -size_t get_pack_redundancy(struct pack_list *pl) +static size_t get_pack_redundancy(struct pack_list *pl) { struct pack_list *subset; + size_t ret = 0; if (pl == NULL) return 0; - size_t ret = 0; while ((subset = pl->next)) { while(subset) { ret += sizeof_union(pl->pack, subset->pack); @@ -391,7 +391,7 @@ size_t get_pack_redundancy(struct pack_list *pl) return ret; } -inline size_t pack_set_bytecount(struct pack_list *pl) +static inline size_t pack_set_bytecount(struct pack_list *pl) { size_t ret = 0; while (pl) { @@ -402,7 +402,7 @@ inline size_t pack_set_bytecount(struct pack_list *pl) return ret; } -void minimize(struct pack_list **min) +static void minimize(struct pack_list **min) { struct pack_list *pl, *unique = NULL, *non_unique = NULL, *min_perm = NULL; @@ -469,7 +469,7 @@ void minimize(struct pack_list **min) } } -void load_all_objects() +static void load_all_objects(void) { struct pack_list *pl = local_packs; struct llist_item *hint, *l; @@ -497,7 +497,7 @@ void load_all_objects() } /* this scales like O(n^2) */ -void cmp_local_packs() +static void cmp_local_packs(void) { struct pack_list *subset, *pl = local_packs; @@ -508,7 +508,7 @@ void cmp_local_packs() } } -void scan_alt_odb_packs() +static void scan_alt_odb_packs(void) { struct pack_list *local, *alt; @@ -524,7 +524,7 @@ void scan_alt_odb_packs() } } -struct pack_list * add_pack(struct packed_git *p) +static struct pack_list * add_pack(struct packed_git *p) { struct pack_list l; size_t off; @@ -550,7 +550,7 @@ struct pack_list * add_pack(struct packed_git *p) return pack_list_insert(&altodb_packs, &l); } -struct pack_list * add_pack_file(char *filename) +static struct pack_list * add_pack_file(char *filename) { struct packed_git *p = packed_git; @@ -565,7 +565,7 @@ struct pack_list * add_pack_file(char *filename) die("Filename %s not found in packed_git\n", filename); } -void load_all() +static void load_all(void) { struct packed_git *p = packed_git; diff --git a/path.c b/path.c index d635470..84b3272 100644 --- a/path.c +++ b/path.c @@ -131,7 +131,7 @@ int validate_symref(const char *path) return -1; } -static char *current_dir() +static char *current_dir(void) { return getcwd(pathname, sizeof(pathname)); } -- cgit v0.10.2-6-g49f6 From 3d32051f4fd657747de00cb9d516dc3512bb377d Mon Sep 17 00:00:00 2001 From: Ryan Anderson Date: Mon, 21 Nov 2005 00:11:22 -0500 Subject: Add Python version checks to the Makefile to automatically set WITH_OWN_SUBPROCESS_PY Also rearrange some path settings in the Makefile in the process. Signed-off-by: Ryan Anderson Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index 092931a..d6dad19 100644 --- a/Makefile +++ b/Makefile @@ -133,11 +133,26 @@ PROGRAMS += git-ssh-pull$X git-ssh-push$X GIT_LIST_TWEAK = +# Set paths to tools early so that they can be used for version tests. +ifndef SHELL_PATH + SHELL_PATH = /bin/sh +endif +ifndef PERL_PATH + PERL_PATH = /usr/bin/perl +endif +ifndef PYTHON_PATH + PYTHON_PATH = /usr/bin/python +endif + PYMODULES = \ gitMergeCommon.py ifdef WITH_OWN_SUBPROCESS_PY PYMODULES += compat/subprocess.py +else + ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK) + PYMODULES += compat/subprocess.py + endif endif ifdef WITH_SEND_EMAIL @@ -252,16 +267,6 @@ ifndef NO_CURL endif endif -ifndef SHELL_PATH - SHELL_PATH = /bin/sh -endif -ifndef PERL_PATH - PERL_PATH = /usr/bin/perl -endif -ifndef PYTHON_PATH - PYTHON_PATH = /usr/bin/python -endif - ifndef NO_OPENSSL LIB_OBJS += epoch.o OPENSSL_LIBSSL = -lssl -- cgit v0.10.2-6-g49f6 From 936a23420c8e1630e013578ba9285cd69cf918f7 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Sun, 20 Nov 2005 22:42:11 +0100 Subject: git-reset.txt: Small fix + clarifications. This basically translates the man-page from 'git-developerish' to plain english, adding some almost-sample output from git-status so users can recognize what will happen. Also mention explicitly that --mixed updates the index, while --soft doesn't. I understood the old text to mean "--mixed is exactly like --soft, but verbose". Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 31ec207..6af3a4f 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -14,19 +14,30 @@ DESCRIPTION Sets the current head to the specified commit and optionally resets the index and working tree to match. +This command is useful if you notice some small error in a recent +commit (or set of commits) and want to redo that part without showing +the undo in the history. + +If you want to undo a commit other than the latest on a branch, +gitlink:git-revert[1] is your friend. + OPTIONS ------- --mixed:: - Like --soft but reports what has not been updated. This is the - default action. + Resets the index but not the working tree (ie, the changed files + are preserved but not marked for commit) and reports what has not + been updated. This is the default action. --soft:: Does not touch the index file nor the working tree at all, but - requires them in a good order. + requires them to be in a good order. This leaves all your changed + files "Updated but not checked in", as gitlink:git-status[1] would + put it. --hard:: Matches the working tree and index to that of the tree being - switched to. + switched to. Any changes to tracked files in the working tree + since are lost. :: Commit to make the current HEAD. -- cgit v0.10.2-6-g49f6 From e0a87193d3f2b78b7b687405c0315e1517d36912 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 20 Nov 2005 23:37:13 -0800 Subject: Fix "do not DWIM" patch to enter_repo" We wanted --strict to mean "do not DWIM", but the code required to see absolute path. daemon does its own path verification and chdirs to the verified repository, so enter_repo() called from upload-pack will always enter ".". Requiring absolute path does not make any sense. Signed-off-by: Junio C Hamano diff --git a/path.c b/path.c index 84b3272..4d88947 100644 --- a/path.c +++ b/path.c @@ -181,7 +181,7 @@ char *enter_repo(char *path, int strict) return NULL; if (strict) { - if((path[0] != '/') || chdir(path) < 0) + if (chdir(path) < 0) return NULL; } else { -- cgit v0.10.2-6-g49f6 From 924215024c72bc359fad920010dd6b356a5140c8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 00:43:12 -0800 Subject: Make sure heads/foo and tags/foo do not confuse things. When both heads/foo and tags/foo exist, get_sha1_basic("foo") picked up the tag without complaining, which is quite confusing. Make sure we require unambiguous form, "heads/foo" or "tags/foo" in such cases. Signed-off-by: Junio C Hamano diff --git a/sha1_name.c b/sha1_name.c index be1755a..faac158 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -236,6 +236,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) NULL }; const char **p; + int found = 0; if (len == 40 && !get_sha1_hex(str, sha1)) return 0; @@ -246,10 +247,20 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) for (p = prefix; *p; p++) { char *pathname = git_path("%s/%.*s", *p, len, str); - if (!read_ref(pathname, sha1)) - return 0; + if (!read_ref(pathname, sha1)) { + /* Must be unique; i.e. when heads/foo and + * tags/foo are both present, reject "foo". + * Note that read_ref() eventually calls + * get_sha1_hex() which can smudge initial + * part of the buffer even if what is read + * is found to be invalid halfway. + */ + if (1 < found++) + return -1; + } } - + if (found == 1) + return 0; return -1; } diff --git a/show-branch.c b/show-branch.c index 631336c..d8808ee 100644 --- a/show-branch.c +++ b/show-branch.c @@ -313,9 +313,16 @@ static int append_ref(const char *refname, const unsigned char *sha1) static int append_head_ref(const char *refname, const unsigned char *sha1) { - if (strncmp(refname, "refs/heads/", 11)) + unsigned char tmp[20]; + int ofs = 11; + if (strncmp(refname, "refs/heads/", ofs)) return 0; - return append_ref(refname + 11, sha1); + /* If both heads/foo and tags/foo exists, get_sha1 would + * get confused. + */ + if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20)) + ofs = 5; + return append_ref(refname + ofs, sha1); } static int append_tag_ref(const char *refname, const unsigned char *sha1) @@ -470,7 +477,7 @@ int main(int ac, char **av) if (MAX_REVS <= num_rev) die("cannot handle more than %d revs.", MAX_REVS); if (get_sha1(ref_name[num_rev], revkey)) - usage(show_branch_usage); + die("'%s' is not a valid ref.\n", ref_name[num_rev]); commit = lookup_commit_reference(revkey); if (!commit) die("cannot find commit %s (%s)", -- cgit v0.10.2-6-g49f6 From 60abce3c0f41952de7dc1a4abefcb988898b71a4 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 10:59:31 -0800 Subject: Fix hooks/update template. Make the example address RFC2606 (aka BCP0032) compliant. Also fix a couple of shell script errors. Noted and fixed by Matthew Wilcox and Andreas Ericsson. Signed-off-by: Junio C Hamano diff --git a/templates/hooks--update b/templates/hooks--update index 3f38b82..6db555f 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -8,14 +8,14 @@ # (2) make this file executable by "chmod +x update". # -recipient="commit-list@mydomain.xz" +recipient="commit-list@example.com" if expr "$2" : '0*$' >/dev/null then echo "Created a new ref, with the following commits:" git-rev-list --pretty "$3" else - $base=$(git-merge-base "$2" "$3") + base=$(git-merge-base "$2" "$3") case "$base" in "$2") echo "New commits:" @@ -24,8 +24,7 @@ else echo "Rebased ref, commits from common ancestor:" ;; esac -fi -git-rev-list --pretty "$3" "^$base" + git-rev-list --pretty "$3" "^$base" fi | mail -s "Changes to ref $1" "$recipient" exit 0 -- cgit v0.10.2-6-g49f6 From 88b5a74883f146eac4b15b30e74bbdeb5ab5a63a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 12:17:22 -0800 Subject: format-patch: fix two-argument special case, and make it easier to pick single commits Luben Tuikov noticed that sometimes being able to say 'git-format-patch ' to format the change a single commit introduces relative to its parent is handy. This patch does not support that directly, but it makes sense to interpret a single argument "rev" to mean "rev^1..rev". With this, the backward compatibility syntaxes still apply: - "format-patch master" means "format-patch master..HEAD" - "format-patch origin master" means "format-patch origin..master" - "format-patch origin.." means "format-patch origin..HEAD" But "format-patch a b c d e" formats the changes these five commits introduce relative to their respective parents. Earlier it rejected these arguments not in "one..two" form. Signed-off-by: Junio C Hamano diff --git a/git-format-patch.sh b/git-format-patch.sh index 7ee5d32..351790c 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -99,7 +99,7 @@ filelist=$tmp-files # Also, "rev1.." should mean "rev1..HEAD"; git-diff users are # familiar with that syntax. -case "$#,$1" in +case "$#,$1$2" in 1,?*..?*) # single "rev1..rev2" ;; @@ -131,7 +131,8 @@ do rev2=`expr "$revpair" : '.*\.\.\(.*\)'` ;; *) - usage + rev1="$revpair^" + rev2="$revpair" ;; esac git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 || -- cgit v0.10.2-6-g49f6 From 9f70b806920e3ea158d7e189a1ec668445c13359 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 12:18:23 -0800 Subject: rename detection with -M100 means "exact renames only". When the user is interested in pure renames, there is no point doing the similarity scores. This changes the score argument parsing to special case -M100 (otherwise, it is a precision scaled value 0 <= v < 1 and would mean 0.1, not 1.0 --- if you do mean 0.1, you can say -M1), and optimizes the diffcore_rename transformation to only look at pure renames in that case. Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 0391e8c..0f839c1 100644 --- a/diff.c +++ b/diff.c @@ -853,6 +853,10 @@ static int parse_num(const char **cp_p) } *cp_p = cp; + /* special case: -M100 would mean 1.0 not 0.1 */ + if (num == 100 && scale == 1000) + return MAX_SCORE; + /* user says num divided by scale and we say internally that * is MAX_SCORE * num / scale. */ diff --git a/diffcore-rename.c b/diffcore-rename.c index 6a9d95d..dba965c 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -307,6 +307,9 @@ void diffcore_rename(struct diff_options *options) if (rename_count == rename_dst_nr) goto cleanup; + if (minimum_score == MAX_SCORE) + goto cleanup; + num_create = (rename_dst_nr - rename_count); num_src = rename_src_nr; mx = xmalloc(sizeof(*mx) * num_create * num_src); -- cgit v0.10.2-6-g49f6 From aa7f412abf605d824c7f72dbfaedd27d5429a025 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 13:42:55 -0800 Subject: tutorial: setting up a tree for subsystem maintainers The "copying over packs" step is to prevent the objects available in upstream repository to get expanted in the subsystem maintainer tree, and is still valid if the upstream repository do not live on the same machine. But if they are on the same machine using objects/info/alternates is cleaner. Signed-off-by: Junio C Hamano diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 03eb421..e2dfb00 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -1534,7 +1534,10 @@ on that project and has an own "public repository" goes like this: the "project lead" person does. 3. Copy over the packed files from "project lead" public - repository to your public repository. + repository to your public repository, unless the "project + lead" repository lives on the same machine as yours. In the + latter case, you can use `objects/info/alternates` file to + point at the repository you are borrowing from. 4. Push into the public repository from your primary repository. Run `git repack`, and possibly `git prune` if the -- cgit v0.10.2-6-g49f6 From 4dbd13527933261076bbed1a5a2daa60a752d096 Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Thu, 17 Nov 2005 20:37:14 +0100 Subject: git-daemon support for user-relative paths. Dropped a fair amount of reundant code in favour of the library code in path.c Added option --strict-paths with documentation, with backwards compatibility for whitelist entries with symlinks. Everything that worked earlier still works insofar as I have remembered testing it. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 3783858..972e0e1 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -29,9 +29,15 @@ This is ideally suited for read-only updates, ie pulling from git repositories. OPTIONS ------- ++--strict-paths:: + Match paths exactly (i.e. don't allow "/foo/repo" when the real path is + "/foo/repo.git" or "/foo/repo/.git") and don't do user-relative paths. + git-daemon will refuse to start when this option is enabled and no + whitelist is specified. + --export-all:: Allow pulling from all directories that look like GIT repositories - (have the 'objects' subdirectory and a 'HEAD' file), even if they + (have the 'objects' and 'refs' subdirectories), even if they do not have the 'git-daemon-export-ok' file. --inetd:: @@ -57,9 +63,15 @@ OPTIONS --verbose:: Log details about the incoming connections and requested files. +:: + A directory to add to the whitelist of allowed directories. Unless + --strict-paths is specified this will also include subdirectories + of each named directory. + Author ------ -Written by Linus Torvalds and YOSHIFUJI Hideaki +Written by Linus Torvalds , YOSHIFUJI Hideaki + and the git-list Documentation -------------- diff --git a/Documentation/pull-fetch-param.txt b/Documentation/pull-fetch-param.txt index a7628aa..6413d52 100644 --- a/Documentation/pull-fetch-param.txt +++ b/Documentation/pull-fetch-param.txt @@ -9,15 +9,16 @@ - http://host.xz/path/to/repo.git/ - https://host.xz/path/to/repo.git/ - git://host.xz/path/to/repo.git/ +- git://host.xz/~user/path/to/repo.git/ - ssh://host.xz/path/to/repo.git/ - ssh://host.xz/~user/path/to/repo.git/ - ssh://host.xz/~/path/to/repo.git =============================================================== + SSH Is the default transport protocol and also supports an - scp-like syntax. Both syntaxes support username expansion. - The following three are identical to the last three above, - respectively: + scp-like syntax. Both syntaxes support username expansion, + as does the native git protocol. The following three are + identical to the last three above, respectively: + =============================================================== - host.xz:/path/to/repo.git/ diff --git a/daemon.c b/daemon.c index 2b81152..ac4c94b 100644 --- a/daemon.c +++ b/daemon.c @@ -15,10 +15,11 @@ static int verbose; static const char daemon_usage[] = "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" -" [--timeout=n] [--init-timeout=n] [directory...]"; +" [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...]"; /* List of acceptable pathname prefixes */ static char **ok_paths = NULL; +static int strict_paths = 0; /* If this is set, git-daemon-export-ok is not required */ static int export_all_trees = 0; @@ -81,69 +82,56 @@ static void loginfo(const char *err, ...) va_end(params); } -static int path_ok(const char *dir) +static char *path_ok(char *dir) { - const char *p = dir; - char **pp; - int sl, ndot; + char *path = enter_repo(dir, strict_paths); - /* The pathname here should be an absolute path. */ - if ( *p++ != '/' ) - return 0; - - sl = 1; ndot = 0; - - for (;;) { - if ( *p == '.' ) { - ndot++; - } else if ( *p == '\0' ) { - /* Reject "." and ".." at the end of the path */ - if ( sl && ndot > 0 && ndot < 3 ) - return 0; - - /* Otherwise OK */ - break; - } else if ( *p == '/' ) { - /* Refuse "", "." or ".." */ - if ( sl && ndot < 3 ) - return 0; - sl = 1; - ndot = 0; - } else { - sl = ndot = 0; - } - p++; + if (!path) { + logerror("'%s': unable to chdir or not a git archive", dir); + return NULL; } if ( ok_paths && *ok_paths ) { - int ok = 0; + char **pp = NULL; int dirlen = strlen(dir); + int pathlen = strlen(path); for ( pp = ok_paths ; *pp ; pp++ ) { int len = strlen(*pp); - if ( len <= dirlen && - !strncmp(*pp, dir, len) && - (dir[len] == '/' || dir[len] == '\0') ) { - ok = 1; - break; + /* because of symlinks we must match both what the + * user passed and the canonicalized path, otherwise + * the user can send a string matching either a whitelist + * entry or an actual directory exactly and still not + * get through */ + if (len <= pathlen && !memcmp(*pp, path, len)) { + if (path[len] == '\0' || (!strict_paths && path[len] == '/')) + return path; + } + if (len <= dirlen && !memcmp(*pp, dir, len)) { + if (dir[len] == '\0' || (!strict_paths && dir[len] == '/')) + return path; } } - - if ( !ok ) - return 0; /* Path not in whitelist */ + } + else { + /* be backwards compatible */ + if (!strict_paths) + return path; } - return 1; /* Path acceptable */ + logerror("'%s': not in whitelist", path); + return NULL; /* Fallthrough. Deny by default */ } -static int set_dir(const char *dir) +static int upload(char *dir) { - if (!path_ok(dir)) { - errno = EACCES; - return -1; - } + /* Timeout as string */ + char timeout_buf[64]; + const char *path; + + loginfo("Request for '%s'", dir); - if ( chdir(dir) ) + if (!(path = path_ok(dir))) return -1; /* @@ -152,45 +140,17 @@ static int set_dir(const char *dir) * We want a readable HEAD, usable "objects" directory, and * a "git-daemon-export-ok" flag that says that the other side * is ok with us doing this. + * + * path_ok() uses enter_repo() and does whitelist checking. + * We only need to make sure the repository is exported. */ + if (!export_all_trees && access("git-daemon-export-ok", F_OK)) { + logerror("'%s': repository not exported.", path); errno = EACCES; return -1; } - if (access("objects/", X_OK) || access("HEAD", R_OK)) { - errno = EINVAL; - return -1; - } - - /* If all this passed, we're OK */ - return 0; -} - -static int upload(char *dir) -{ - /* Try paths in this order */ - static const char *paths[] = { "%s", "%s/.git", "%s.git", "%s.git/.git", NULL }; - const char **pp; - /* Enough for the longest path above including final null */ - int buflen = strlen(dir)+10; - char *dirbuf = xmalloc(buflen); - /* Timeout as string */ - char timeout_buf[64]; - - loginfo("Request for '%s'", dir); - - for ( pp = paths ; *pp ; pp++ ) { - snprintf(dirbuf, buflen, *pp, dir); - if ( !set_dir(dirbuf) ) - break; - } - - if ( !*pp ) { - logerror("Cannot set directory '%s': %s", dir, strerror(errno)); - return -1; - } - /* * We'll ignore SIGTERM from now on, we have a * good client. @@ -200,7 +160,7 @@ static int upload(char *dir) snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout); /* git-upload-pack only ever reads stuff, so this is safe */ - execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL); + execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, path, NULL); return -1; } @@ -216,7 +176,7 @@ static int execute(void) if (len && line[len-1] == '\n') line[--len] = 0; - if (!strncmp("git-upload-pack /", line, 17)) + if (!strncmp("git-upload-pack ", line, 16)) return upload(line+16); logerror("Protocol error: '%s'", line); @@ -617,6 +577,10 @@ int main(int argc, char **argv) init_timeout = atoi(arg+15); continue; } + if (!strcmp(arg, "--strict-paths")) { + strict_paths = 1; + continue; + } if (!strcmp(arg, "--")) { ok_paths = &argv[i+1]; break; @@ -631,6 +595,14 @@ int main(int argc, char **argv) if (log_syslog) openlog("git-daemon", 0, LOG_DAEMON); + if (strict_paths && (!ok_paths || !*ok_paths)) { + if (!inetd_mode) + die("git-daemon: option --strict-paths requires a whitelist"); + + logerror("option --strict-paths requires a whitelist"); + exit (1); + } + if (inetd_mode) { fclose(stderr); //FIXME: workaround return execute(); -- cgit v0.10.2-6-g49f6 From ce335fe04f32261e0204280281989ffbb5d990c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 01:21:18 -0800 Subject: daemon: further tweaks. - Do validation only on canonicalized paths - Run upload-pack with "." as repository argument Signed-off-by: Junio C Hamano diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt index 972e0e1..2a8f371 100644 --- a/Documentation/git-daemon.txt +++ b/Documentation/git-daemon.txt @@ -8,7 +8,7 @@ git-daemon - A really simple server for git repositories. SYNOPSIS -------- 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all] - [--timeout=n] [--init-timeout=n] [directory...] + [--timeout=n] [--init-timeout=n] [--strict-paths] [directory...] DESCRIPTION ----------- @@ -29,7 +29,7 @@ This is ideally suited for read-only updates, ie pulling from git repositories. OPTIONS ------- -+--strict-paths:: +--strict-paths:: Match paths exactly (i.e. don't allow "/foo/repo" when the real path is "/foo/repo.git" or "/foo/repo/.git") and don't do user-relative paths. git-daemon will refuse to start when this option is enabled and no diff --git a/daemon.c b/daemon.c index ac4c94b..d788167 100644 --- a/daemon.c +++ b/daemon.c @@ -92,25 +92,21 @@ static char *path_ok(char *dir) } if ( ok_paths && *ok_paths ) { - char **pp = NULL; - int dirlen = strlen(dir); + char **pp; int pathlen = strlen(path); + /* The validation is done on the paths after enter_repo + * canonicalization, so whitelist should be written in + * terms of real pathnames (i.e. after ~user is expanded + * and symlinks resolved). + */ for ( pp = ok_paths ; *pp ; pp++ ) { int len = strlen(*pp); - /* because of symlinks we must match both what the - * user passed and the canonicalized path, otherwise - * the user can send a string matching either a whitelist - * entry or an actual directory exactly and still not - * get through */ - if (len <= pathlen && !memcmp(*pp, path, len)) { - if (path[len] == '\0' || (!strict_paths && path[len] == '/')) - return path; - } - if (len <= dirlen && !memcmp(*pp, dir, len)) { - if (dir[len] == '\0' || (!strict_paths && dir[len] == '/')) - return path; - } + if (len <= pathlen && + !memcmp(*pp, path, len) && + (path[len] == '\0' || + (!strict_paths && path[len] == '/'))) + return path; } } else { @@ -160,7 +156,7 @@ static int upload(char *dir) snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout); /* git-upload-pack only ever reads stuff, so this is safe */ - execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, path, NULL); + execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL); return -1; } -- cgit v0.10.2-6-g49f6 From f801477645146fd7eff018b4997b86dfa1a0767d Mon Sep 17 00:00:00 2001 From: Paul Collins Date: Fri, 4 Nov 2005 14:57:16 +0000 Subject: proxy-command support for git:// Here is an updated patch that first looks for GIT_PROXY_COMMAND in the environment and then git.proxycommand in the repository's configuration file. I have left the calling convention the same argv[1] is the host and argv[2] is the port. I've taken the hostname parsing verbatim from git_tcp_connect(), so it should now support an explicit port number and whatever that business with the square brackets is. (Should I move this to a helper function?) Regarding internal vs. external hosts, the proxy command can simply run netcat locally to internal hosts, so perhaps that is sufficient. Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index 73187a1..be88ef0 100644 --- a/connect.c +++ b/connect.c @@ -448,6 +448,73 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) #endif /* NO_IPV6 */ +static char *git_proxy_command = NULL; + +static int git_proxy_command_options(const char *var, const char *value) +{ + if (git_proxy_command == NULL) { + if (!strcmp(var, "git.proxycommand")) { + git_proxy_command = xmalloc(strlen(value) + 1); + strcpy(git_proxy_command, value); + return 0; + } + } + + return git_default_config(var, value); +} + +static int git_use_proxy(void) +{ + git_proxy_command = getenv("GIT_PROXY_COMMAND"); + git_config(git_proxy_command_options); + return git_proxy_command != NULL; +} + +static int git_proxy_connect(int fd[2], const char *prog, char *host, char *path) +{ + char *port = STR(DEFAULT_GIT_PORT); + char *colon, *end; + int pipefd[2][2]; + pid_t pid; + + if (host[0] == '[') { + end = strchr(host + 1, ']'); + if (end) { + *end = 0; + end++; + host++; + } else + end = host; + } else + end = host; + colon = strchr(end, ':'); + + if (colon) { + *colon = 0; + port = colon + 1; + } + + if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) + die("unable to create pipe pair for communication"); + pid = fork(); + if (!pid) { + dup2(pipefd[1][0], 0); + dup2(pipefd[0][1], 1); + close(pipefd[0][0]); + close(pipefd[0][1]); + close(pipefd[1][0]); + close(pipefd[1][1]); + execlp(git_proxy_command, git_proxy_command, host, port, NULL); + die("exec failed"); + } + fd[0] = pipefd[0][0]; + fd[1] = pipefd[1][1]; + close(pipefd[0][1]); + close(pipefd[1][0]); + packet_write(fd[1], "%s %s\n", prog, path); + return pid; +} + /* * Yeah, yeah, fixme. Need to pass in the heads etc. */ @@ -493,8 +560,11 @@ int git_connect(int fd[2], char *url, const char *prog) *ptr = '\0'; } - if (protocol == PROTO_GIT) + if (protocol == PROTO_GIT) { + if (git_use_proxy()) + return git_proxy_connect(fd, prog, host, path); return git_tcp_connect(fd, prog, host, path); + } if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) die("unable to create pipe pair for communication"); -- cgit v0.10.2-6-g49f6 From e814bc4d159f62941fafe135c3008b3ff103444d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 19 Nov 2005 03:48:56 -0800 Subject: git-proxy updates. This builds on top of the git-proxy mechanism Paul Collins did, and updates its configuration mechanism. * GIT_PROXY_COMMAND environment variable is used as the catch-all fallback, as in the original. This has not changed. * Renames proxy configuration variables to core.gitproxy; this has become a multi-value variable per list discussion, most notably from suggestion by Linus. [core] ;# matches www.kernel.org as well gitproxy = netcatter for kernel.org gitproxy = netscatter for sample.xz gitproxy = none for mydomain.xz gitproxy = netcatter-default The values are command names, followed by an optional " for " and domainname; the first tail-match of the domainname determines which proxy command is used. An entry without " for " matches any domain and can be used as the default. The command name "none" is special -- it tells the mechanism not to use any proxy command and use the native git:// connection. Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index be88ef0..a4d6d35 100644 --- a/connect.c +++ b/connect.c @@ -449,25 +449,63 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) #endif /* NO_IPV6 */ static char *git_proxy_command = NULL; +static const char *rhost_name = NULL; +static int rhost_len; static int git_proxy_command_options(const char *var, const char *value) { - if (git_proxy_command == NULL) { - if (!strcmp(var, "git.proxycommand")) { - git_proxy_command = xmalloc(strlen(value) + 1); - strcpy(git_proxy_command, value); + if (!strcmp(var, "core.gitproxy")) { + if (git_proxy_command) return 0; + /* [core] + * ;# matches www.kernel.org as well + * gitproxy = netcatter-1 for kernel.org + * gitproxy = netcatter-2 for sample.xz + * gitproxy = netcatter-default + */ + const char *for_pos = strstr(value, " for "); + int matchlen = -1; + int hostlen; + + if (!for_pos) + /* matches everybody */ + matchlen = strlen(value); + else { + hostlen = strlen(for_pos + 5); + if (rhost_len < hostlen) + matchlen = -1; + else if (!strncmp(for_pos + 5, + rhost_name + rhost_len - hostlen, + hostlen) && + ((rhost_len == hostlen) || + rhost_name[rhost_len - hostlen -1] == '.')) + matchlen = for_pos - value; + else + matchlen = -1; } + if (0 <= matchlen) { + /* core.gitproxy = none for kernel.org */ + if (matchlen == 4 && + !memcmp(value, "none", 4)) + matchlen = 0; + git_proxy_command = xmalloc(matchlen + 1); + memcpy(git_proxy_command, value, matchlen); + git_proxy_command[matchlen] = 0; + } + return 0; } return git_default_config(var, value); } -static int git_use_proxy(void) +static int git_use_proxy(const char *host) { + rhost_name = host; + rhost_len = strlen(host); git_proxy_command = getenv("GIT_PROXY_COMMAND"); git_config(git_proxy_command_options); - return git_proxy_command != NULL; + rhost_name = NULL; + return (git_proxy_command && *git_proxy_command); } static int git_proxy_connect(int fd[2], const char *prog, char *host, char *path) @@ -561,7 +599,7 @@ int git_connect(int fd[2], char *url, const char *prog) } if (protocol == PROTO_GIT) { - if (git_use_proxy()) + if (git_use_proxy(host)) return git_proxy_connect(fd, prog, host, path); return git_tcp_connect(fd, prog, host, path); } -- cgit v0.10.2-6-g49f6 From 3dd94e3b2e2f5b00512273f96ab5628c742c112c Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 21 Nov 2005 11:18:20 +0100 Subject: git-config-set: Properly terminate strings with '\0' When a lowercase version of the key was generated, it was not terminated. Strangely enough, it worked on Linux and macosx anyway. Just cygwin barfed. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/config-set.c b/config-set.c index 5f654f7..d938f96 100644 --- a/config-set.c +++ b/config-set.c @@ -38,6 +38,7 @@ static int get_value(const char* key_, const char* regex_) key = malloc(strlen(key_)+1); for (i = 0; key_[i]; i++) key[i] = tolower(key_[i]); + key[i] = 0; if (regex_) { if (regex_[0] == '!') { diff --git a/config.c b/config.c index 5d237c8..e78520b 100644 --- a/config.c +++ b/config.c @@ -432,6 +432,7 @@ int git_config_set_multivar(const char* key, const char* value, return 1; } else store.key[i] = tolower(key[i]); + store.key[i] = 0; /* * The lock_file serves a purpose in addition to locking: the new -- cgit v0.10.2-6-g49f6 From b17e659dd4007cb1d3eb5ac32b524c0c5ab59601 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 20 Nov 2005 21:22:19 +0100 Subject: Allow hierarchical section names A .git/config like follows becomes valid with this patch: [remote.junio] url = git://git.kernel.org/pub/scm/git/git.git pull = master:junio todo:todo +pu:pu [remote.ibook] url = ibook:git/ pull = master:ibook push = master:quetzal (This patch only does the ini file thing, git-fetch and friends still ignore these values). Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c index e78520b..357c1ca 100644 --- a/config.c +++ b/config.c @@ -143,7 +143,7 @@ static int get_base_var(char *name) return -1; if (c == ']') return baselen; - if (!isalnum(c)) + if (!isalnum(c) && c != '.') return -1; if (baselen > MAXNAME / 2) return -1; @@ -405,28 +405,29 @@ int git_config_set_multivar(const char* key, const char* value, int fd; char* config_file = strdup(git_path("config")); char* lock_file = strdup(git_path("config.lock")); - - store.multi_replace = multi_replace; + const char* last_dot = strrchr(key, '.'); /* * Since "key" actually contains the section name and the real * key name separated by a dot, we have to know where the dot is. */ - for (store.baselen = 0; - key[store.baselen] != '.' && key[store.baselen]; - store.baselen++); - if (!key[store.baselen] || !key[store.baselen+1]) { + + if (last_dot == NULL) { fprintf(stderr, "key does not contain a section: %s\n", key); return 2; } + store.baselen = last_dot - key; + + store.multi_replace = multi_replace; /* * Validate the key and while at it, lower case it for matching. */ store.key = (char*)malloc(strlen(key)+1); for (i = 0; key[i]; i++) - if (i != store.baselen && (!isalnum(key[i]) || - (i == store.baselen+1 && !isalpha(key[i])))) { + if (i != store.baselen && + ((!isalnum(key[i]) && key[i] != '.') || + (i == store.baselen+1 && !isalpha(key[i])))) { fprintf(stderr, "invalid key: %s\n", key); free(store.key); return 1; diff --git a/t/t1300-config-set.sh b/t/t1300-config-set.sh index 59b6c4c..7a5849a 100644 --- a/t/t1300-config-set.sh +++ b/t/t1300-config-set.sh @@ -248,5 +248,24 @@ test_expect_failure 'invalid key' 'git-config-set inval.2key blabla' test_expect_success 'correct key' 'git-config-set 123456.a123 987' +test_expect_success 'hierarchical section' \ + 'git-config-set 1.2.3.alpha beta' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + NoNewLine = wow2 for me +[123456] + a123 = 987 +[1.2.3] + alpha = beta +EOF + +test_expect_success 'hierarchical section value' 'cmp .git/config expect' + test_done -- cgit v0.10.2-6-g49f6 From ef07618fdd660057b80fe75007405c62502a5820 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 21 Nov 2005 12:38:31 -0800 Subject: git-repack: Properly abort in corrupt repository In a corrupt repository, git-repack produces a pack that does not contain needed objects without complaining, and the result of this combined with -d flag can be very painful -- e.g. a lossage of one tree object can lead to lossage of blobs reachable only through that tree. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/git-repack.sh b/git-repack.sh index 4e16d34..c0f271d 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -41,7 +41,7 @@ esac if [ "$local" ]; then pack_objects="$pack_objects --local" fi -name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) | +name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) 2>&1 | git-pack-objects --non-empty $pack_objects .tmp-pack) || exit 1 if [ -z "$name" ]; then diff --git a/pack-objects.c b/pack-objects.c index 4e941e7..8864a31 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -524,7 +524,7 @@ int main(int argc, char **argv) unsigned char sha1[20]; if (get_sha1_hex(line, sha1)) - die("expected sha1, got garbage"); + die("expected sha1, got garbage:\n %s", line); hash = 0; p = line+40; while (*p) { -- cgit v0.10.2-6-g49f6 From f35230fb1123a0db4776c574a874aecec1f38db8 Mon Sep 17 00:00:00 2001 From: Paul Serice Date: Mon, 21 Nov 2005 11:07:23 -0600 Subject: git-daemon not listening when compiled with -DNO_IPV6 git-daemon was not listening when compiled with -DNO_IPV6. socksetup() was not returning socket count when compiled with -DNO_IPV6. Signed-off-by: Paul Serice Signed-off-by: Junio C Hamano diff --git a/daemon.c b/daemon.c index d788167..91b9656 100644 --- a/daemon.c +++ b/daemon.c @@ -466,8 +466,14 @@ static int socksetup(int port, int **socklist_p) return 0; } + if (listen(sockfd, 5) < 0) { + close(sockfd); + return 0; + } + *socklist_p = xmalloc(sizeof(int)); **socklist_p = sockfd; + return 1; } #endif -- cgit v0.10.2-6-g49f6 From 1b1480ff6acda6e53325961e20fb13ae6895ee69 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 21 Nov 2005 14:17:12 -0800 Subject: rename/copy score parsing updates. Better variant, which handles stuff like "4.5%" and rejects "192.168.0.1". Additionally, make sure numbers are unsigned (I'm making them unsigned long just for the hell of it), to make sure that artificial wraparound scenarios don't cause harm. -hpa [jc: with this, -M100 changes its meaning back to 10%. People wanting to say "pure renames only" should now say -M100% or -M1.0; sounds a bit like an earthquake, but arguably things are more consistent this way ;-)] Signed-off-by: Junio C Hamano diff --git a/diff.c b/diff.c index 0f839c1..ffe8a55 100644 --- a/diff.c +++ b/diff.c @@ -838,29 +838,38 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) static int parse_num(const char **cp_p) { - int num, scale, ch, cnt; + unsigned long num, scale; + int ch, dot; const char *cp = *cp_p; - cnt = num = 0; + num = 0; scale = 1; - while ('0' <= (ch = *cp) && ch <= '9') { - if (cnt++ < 5) { - /* We simply ignore more than 5 digits precision. */ - scale *= 10; - num = num * 10 + ch - '0'; + 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; - /* special case: -M100 would mean 1.0 not 0.1 */ - if (num == 100 && scale == 1000) - return MAX_SCORE; - /* user says num divided by scale and we say internally that * is MAX_SCORE * num / scale. */ - return (MAX_SCORE * num / scale); + return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale); } int diff_scoreopt_parse(const char *opt) -- cgit v0.10.2-6-g49f6 From d6ebd2590cb427a564a7bc21f893b238ce26e26f Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Tue, 22 Nov 2005 00:44:15 +0100 Subject: Introduce $(ALL_PROGRAMS) for 'all:' and 'install:' to operate on. Remove $(SIMPLE_PROGRAMS) from $(PROGRAMS) so buildrules don't have to be overridden. Put $(SCRIPTS) with the other target-macros so it doesn't get lonely. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index d6dad19..ecbb888 100644 --- a/Makefile +++ b/Makefile @@ -102,6 +102,11 @@ SCRIPT_PERL = \ SCRIPT_PYTHON = \ git-merge-recursive.py +SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ + $(patsubst %.perl,%,$(SCRIPT_PERL)) \ + $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ + gitk git-cherry-pick + # The ones that do not have to link with lcrypto nor lz. SIMPLE_PROGRAMS = \ git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \ @@ -125,8 +130,10 @@ PROGRAMS = \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ 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-config-set$X git-var$X \ - $(SIMPLE_PROGRAMS) + git-name-rev$X git-pack-redundant$X git-config-set$X git-var$X + +# what 'all' will build and 'install' will install. +ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X # Backward compatibility -- to be removed after 1.0 PROGRAMS += git-ssh-pull$X git-ssh-push$X @@ -339,25 +346,20 @@ endif ALL_CFLAGS += -DSHA1_HEADER=$(call shellquote,$(SHA1_HEADER)) -SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \ - $(patsubst %.perl,%,$(SCRIPT_PERL)) \ - $(patsubst %.py,%,$(SCRIPT_PYTHON)) \ - gitk git-cherry-pick - export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir ### Build rules -all: $(PROGRAMS) $(SCRIPTS) git +all: $(ALL_PROGRAMS) all: $(MAKE) -C templates # Only use $(CFLAGS). We don't need anything else. -git: git.c Makefile +git$(X): git.c Makefile $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \ - $(CFLAGS) $@.c -o $@ + $(CFLAGS) $< -o $@ -$(filter-out git,$(patsubst %.sh,%,$(SCRIPT_SH))) : % : %.sh +$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh rm -f $@ sed -e '1s|#!.*/sh|#!$(call shq,$(SHELL_PATH))|' \ -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \ @@ -441,9 +443,9 @@ check: ### Installation rules -install: $(PROGRAMS) $(SCRIPTS) git +install: all $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir)) - $(INSTALL) git $(PROGRAMS) $(SCRIPTS) $(call shellquote,$(DESTDIR)$(bindir)) + $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(bindir)) $(MAKE) -C templates install $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR)) -- cgit v0.10.2-6-g49f6 From c3df8568424684bbcc7df7722eb3ec34bdae8b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Tue, 22 Nov 2005 12:18:23 +0900 Subject: GIT: Fix compilation error in connect.c Fix compilation error for gcc-2.95. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index a4d6d35..7a417e5 100644 --- a/connect.c +++ b/connect.c @@ -455,6 +455,10 @@ static int rhost_len; static int git_proxy_command_options(const char *var, const char *value) { if (!strcmp(var, "core.gitproxy")) { + const char *for_pos; + int matchlen = -1; + int hostlen; + if (git_proxy_command) return 0; /* [core] @@ -463,10 +467,7 @@ static int git_proxy_command_options(const char *var, const char *value) * gitproxy = netcatter-2 for sample.xz * gitproxy = netcatter-default */ - const char *for_pos = strstr(value, " for "); - int matchlen = -1; - int hostlen; - + for_pos = strstr(value, " for "); if (!for_pos) /* matches everybody */ matchlen = strlen(value); -- cgit v0.10.2-6-g49f6 From 07f92477227e2a1770dfbb6318caedc4f091f6c1 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 18 Nov 2005 16:29:04 -0500 Subject: max-count in terms of intersection When a path designation is given, max-count counts the number of commits therein (intersection), not globally. This avoids the case where in case path has been inactive for the last N commits, --max-count=N and path designation at git-rev-list is given, would give no commits. Signed-off-by: Luben Tuikov Signed-off-by: Junio C Hamano diff --git a/rev-list.c b/rev-list.c index 6e6ffde..e17f928 100644 --- a/rev-list.c +++ b/rev-list.c @@ -124,8 +124,6 @@ static int filter_commit(struct commit * commit) stop_traversal=1; return CONTINUE; } - if (max_count != -1 && !max_count--) - return STOP; if (no_merges && (commit->parents && commit->parents->next)) return CONTINUE; if (paths && dense) { @@ -148,6 +146,9 @@ static int process_commit(struct commit * commit) return CONTINUE; } + if (max_count != -1 && !max_count--) + return STOP; + show_commit(commit); return CONTINUE; -- cgit v0.10.2-6-g49f6 From 9ce392f4826558357af2b2c7eb6ad876fdb53a91 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 22:52:37 -0800 Subject: Move diff.renamelimit out of default configuration. Otherwise we would end up linking all the unneeded stuff into git-daemon only to link with git_default_config. Signed-off-by: Junio C Hamano diff --git a/Makefile b/Makefile index ecbb888..a97a5d9 100644 --- a/Makefile +++ b/Makefile @@ -482,7 +482,8 @@ deb: dist ### Cleaning rules clean: - rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o git $(PROGRAMS) $(LIB_FILE) + rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE) + rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X rm -f $(filter-out gitk,$(SCRIPTS)) rm -f *.spec *.pyc *.pyo rm -rf $(GIT_TARNAME) diff --git a/config.c b/config.c index 357c1ca..18d59ee 100644 --- a/config.c +++ b/config.c @@ -236,11 +236,6 @@ int git_default_config(const char *var, const char *value) return 0; } - if (!strcmp(var, "diff.renamelimit")) { - diff_rename_limit_default = git_config_int(var, value); - return 0; - } - /* Add other config variables here.. */ return 0; } diff --git a/diff-files.c b/diff-files.c index 1789939..38599b5 100644 --- a/diff-files.c +++ b/diff-files.c @@ -38,7 +38,7 @@ int main(int argc, const char **argv) const char *prefix = setup_git_directory(); int entries, i; - git_config(git_default_config); + git_config(git_diff_config); diff_setup(&diff_options); while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "--")) { diff --git a/diff-index.c b/diff-index.c index c9a9f4c..0054883 100644 --- a/diff-index.c +++ b/diff-index.c @@ -180,7 +180,7 @@ int main(int argc, const char **argv) int allow_options = 1; int i; - git_config(git_default_config); + git_config(git_diff_config); diff_setup(&diff_options); for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/diff-stages.c b/diff-stages.c index 85170b2..9968d6c 100644 --- a/diff-stages.c +++ b/diff-stages.c @@ -55,6 +55,9 @@ int main(int ac, const char **av) { int stage1, stage2; + setup_git_directory(); + + git_config(git_diff_config); read_cache(); diff_setup(&diff_options); while (1 < ac && av[1][0] == '-') { diff --git a/diff-tree.c b/diff-tree.c index 09d16ad..da9c68c 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -164,7 +164,7 @@ int main(int argc, const char **argv) unsigned char sha1[2][20]; const char *prefix = setup_git_directory(); - git_config(git_default_config); + git_config(git_diff_config); nr_sha1 = 0; diff_setup(&diff_options); diff --git a/diff.c b/diff.c index ffe8a55..2e0797b 100644 --- a/diff.c +++ b/diff.c @@ -15,6 +15,16 @@ 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; diff --git a/diff.h b/diff.h index 9b2e1e6..32b4780 100644 --- a/diff.h +++ b/diff.h @@ -77,6 +77,7 @@ extern int diff_scoreopt_parse(const char *opt); #define DIFF_SETUP_USE_CACHE 2 #define DIFF_SETUP_USE_SIZE_CACHE 4 +extern int git_diff_config(const char *var, const char *value); extern void diff_setup(struct diff_options *); extern int diff_opt_parse(struct diff_options *, const char **, int); extern int diff_setup_done(struct diff_options *); -- cgit v0.10.2-6-g49f6 From c7d77dab9333112cfce8cce51e56005b5e8b4076 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 23:44:35 -0800 Subject: git-var: constness and globalness cleanup. var.c::git_var read function did not have to return writable strings; make it and the functions it points at return const char * instead. ident.c::get_ident() did not need to be global, so make it static. Signed-off-by: Junio C Hamano diff --git a/cache.h b/cache.h index c7c6637..6ac94c5 100644 --- a/cache.h +++ b/cache.h @@ -263,9 +263,8 @@ void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); extern int setup_ident(void); -extern char *get_ident(const char *name, const char *email, const char *date_str); -extern char *git_author_info(void); -extern char *git_committer_info(void); +extern const char *git_author_info(void); +extern const char *git_committer_info(void); static inline void *xmalloc(size_t size) { diff --git a/ident.c b/ident.c index bc89e1d..ac1c27f 100644 --- a/ident.c +++ b/ident.c @@ -156,7 +156,8 @@ static int copy(char *buf, int size, int offset, const char *src) return offset; } -char *get_ident(const char *name, const char *email, const char *date_str) +static const char *get_ident(const char *name, const char *email, + const char *date_str) { static char buffer[1000]; char date[50]; @@ -181,12 +182,16 @@ char *get_ident(const char *name, const char *email, const char *date_str) return buffer; } -char *git_author_info(void) +const char *git_author_info(void) { - return get_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE")); + return get_ident(getenv("GIT_AUTHOR_NAME"), + getenv("GIT_AUTHOR_EMAIL"), + getenv("GIT_AUTHOR_DATE")); } -char *git_committer_info(void) +const char *git_committer_info(void) { - return get_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE")); + return get_ident(getenv("GIT_COMMITTER_NAME"), + getenv("GIT_COMMITTER_EMAIL"), + getenv("GIT_COMMITTER_DATE")); } diff --git a/var.c b/var.c index 51cf86a..9804459 100644 --- a/var.c +++ b/var.c @@ -12,7 +12,7 @@ static const char var_usage[] = "git-var [-l | ]"; struct git_var { const char *name; - char *(*read)(void); + const char *(*read)(void); }; static struct git_var git_vars[] = { { "GIT_COMMITTER_IDENT", git_committer_info }, -- cgit v0.10.2-6-g49f6 From bb931cf9d73d94d9940b6d0ee56b6c13ad42f1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= Date: Fri, 18 Nov 2005 23:17:50 +0100 Subject: Make git-pack-redundant take a list of unimportant objs on stdin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us do "git-fsck-objects --full --unreachable | cut -d ' ' -f3 | git-pack-redundant --all", which will keep git-pack-redundant from keeping packs just because they contain unreachable objects. Also add some more --verbose output. Signed-off-by: Lukas Sandström Signed-off-by: Junio C Hamano diff --git a/pack-redundant.c b/pack-redundant.c index 1519385..9851c29 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -579,6 +579,8 @@ int main(int argc, char **argv) { int i; struct pack_list *min, *red, *pl; + struct llist *ignore; + char *sha1, buf[42]; /* 40 byte sha1 + \n + \0 */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -621,6 +623,23 @@ int main(int argc, char **argv) if (alt_odb) scan_alt_odb_packs(); + /* ignore objects given on stdin */ + llist_init(&ignore); + if (!isatty(0)) { + while (fgets(buf, sizeof(buf), stdin)) { + sha1 = xmalloc(20); + if (get_sha1_hex(buf, sha1)) + die("Bad sha1 on stdin: %s", buf); + llist_insert_sorted_unique(ignore, sha1, NULL); + } + } + llist_sorted_difference_inplace(all_objects, ignore); + pl = local_packs; + while (pl) { + llist_sorted_difference_inplace(pl->unique_objects, ignore); + pl = pl->next; + } + minimize(&min); if (verbose) { @@ -647,6 +666,8 @@ int main(int argc, char **argv) pl->pack->pack_name); pl = pl->next; } + if (verbose) + fprintf(stderr, "%luMB of redundant packs in total.\n", pack_set_bytecount(red)/(1024*1024)); return 0; } -- cgit v0.10.2-6-g49f6 From 60435f68bb1ff319f9a718c91c0efff7c495dcc4 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 22 Nov 2005 15:56:35 +0100 Subject: speedup allocation in pack-redundant.c Reuse discarded nodes of llists Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano diff --git a/pack-redundant.c b/pack-redundant.c index 9851c29..3b91838 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -36,11 +36,31 @@ struct pll { size_t pl_size; }; -static inline void llist_free(struct llist *list) +static struct llist_item *free_nodes = NULL; + +static inline struct llist_item *llist_item_get() +{ + struct llist_item *new; + if ( free_nodes ) { + new = free_nodes; + free_nodes = free_nodes->next; + } else + new = xmalloc(sizeof(struct llist_item)); + + return new; +} + +static inline void llist_item_put(struct llist_item *item) +{ + item->next = free_nodes; + free_nodes = item; +} + +static void llist_free(struct llist *list) { while((list->back = list->front)) { list->front = list->front->next; - free(list->back); + llist_item_put(list->back); } free(list); } @@ -62,13 +82,13 @@ static struct llist * llist_copy(struct llist *list) if ((ret->size = list->size) == 0) return ret; - new = ret->front = xmalloc(sizeof(struct llist_item)); + new = ret->front = llist_item_get(); new->sha1 = list->front->sha1; old = list->front->next; while (old) { prev = new; - new = xmalloc(sizeof(struct llist_item)); + new = llist_item_get(); prev->next = new; new->sha1 = old->sha1; old = old->next; @@ -82,7 +102,7 @@ static struct llist * llist_copy(struct llist *list) static inline struct llist_item * llist_insert(struct llist *list, struct llist_item *after, char *sha1) { - struct llist_item *new = xmalloc(sizeof(struct llist_item)); + struct llist_item *new = llist_item_get(); new->sha1 = sha1; new->next = NULL; @@ -153,7 +173,7 @@ redo_from_start: prev->next = l->next; if (l == list->back) list->back = prev; - free(l); + llist_item_put(l); list->size--; return prev; } -- cgit v0.10.2-6-g49f6 From 2b6e34c1a3c5d787f478c32a359cfe50cb8114b8 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 22 Nov 2005 15:58:41 +0100 Subject: remove unused variable It is just assigned, nothing more. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano diff --git a/pack-redundant.c b/pack-redundant.c index 3b91838..59375f1 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -493,12 +493,10 @@ static void load_all_objects(void) { struct pack_list *pl = local_packs; struct llist_item *hint, *l; - int i; llist_init(&all_objects); while (pl) { - i = 0; hint = NULL; l = pl->all_objects->front; while (l) { -- cgit v0.10.2-6-g49f6 From d1ab157759f0738a13bfb04bca8c436814913d4b Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Tue, 22 Nov 2005 15:59:22 +0100 Subject: arguments cleanup and some formatting Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano diff --git a/pack-redundant.c b/pack-redundant.c index 59375f1..b38baa9 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -149,8 +149,9 @@ static inline struct llist_item * llist_insert_sorted_unique(struct llist *list, } /* returns a pointer to an item in front of sha1 */ -static inline struct llist_item * llist_sorted_remove(struct llist *list, char *sha1, - struct llist_item *hint) +static inline struct llist_item * llist_sorted_remove(struct llist *list, + const char *sha1, + struct llist_item *hint) { struct llist_item *prev, *l; @@ -218,10 +219,11 @@ static inline size_t pack_list_size(struct pack_list *pl) return ret; } -static struct pack_list * pack_list_difference(struct pack_list *A, - struct pack_list *B) +static struct pack_list * pack_list_difference(const struct pack_list *A, + const struct pack_list *B) { - struct pack_list *ret, *pl; + struct pack_list *ret; + const struct pack_list *pl; if (A == NULL) return NULL; @@ -350,8 +352,7 @@ static int is_superset(struct pack_list *pl, struct llist *list) diff = llist_copy(list); while (pl) { - llist_sorted_difference_inplace(diff, - pl->all_objects); + llist_sorted_difference_inplace(diff, pl->all_objects); if (diff->size == 0) { /* we're done */ llist_free(diff); return 1; -- cgit v0.10.2-6-g49f6 From c61642185d411e5e3350566a68483e358ca392b9 Mon Sep 17 00:00:00 2001 From: Paul Serice Date: Tue, 22 Nov 2005 07:54:23 -0600 Subject: Fixed git:// IPv4 address problem when compiled with -DNO_IPV6. Failure to dereference a pointer caused incorrect initialization of the IPv4 address when calling connect() when compiled with -DNO_IPV6. With this patch and yesterday's patch for git-daemon, it should now be possible to use the native git protocol for both the client and server on Cygwin. Signed-off-by: Paul Serice Signed-off-by: Junio C Hamano diff --git a/connect.c b/connect.c index 7a417e5..93f6f80 100644 --- a/connect.c +++ b/connect.c @@ -427,7 +427,7 @@ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path) memset(&sa, 0, sizeof sa); sa.sin_family = he->h_addrtype; sa.sin_port = htons(nport); - memcpy(&sa.sin_addr, ap, he->h_length); + memcpy(&sa.sin_addr, *ap, he->h_length); if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { close(sockfd); -- cgit v0.10.2-6-g49f6 From b99a394cd1e7042ad219046a6ea81ad3f93004ca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Nov 2005 16:08:36 -0800 Subject: pack-redundant: type cleanups. Binary representation of object names are unsigned char[20], not signed. Also verbose output had %lu format printing size_t without (unsigned long) cast other places already had, so match that. Using format %zu was suggested but might not be supported as widely. Noted by Morten Welinder, fixed with input from H. Peter Anvin and Hideaki Yoshifuji. Signed-off-by: Junio C Hamano diff --git a/pack-redundant.c b/pack-redundant.c index b38baa9..793fa08 100644 --- a/pack-redundant.c +++ b/pack-redundant.c @@ -15,7 +15,7 @@ static int load_all_packs = 0, verbose = 0, alt_odb = 0; struct llist_item { struct llist_item *next; - char *sha1; + unsigned char *sha1; }; static struct llist { struct llist_item *front; @@ -100,7 +100,8 @@ static struct llist * llist_copy(struct llist *list) } static inline struct llist_item * llist_insert(struct llist *list, - struct llist_item *after, char *sha1) + struct llist_item *after, + unsigned char *sha1) { struct llist_item *new = llist_item_get(); new->sha1 = sha1; @@ -122,13 +123,12 @@ static inline struct llist_item * llist_insert(struct llist *list, return new; } -static inline struct llist_item * llist_insert_back(struct llist *list, char *sha1) +static inline struct llist_item *llist_insert_back(struct llist *list, unsigned char *sha1) { return llist_insert(list, list->back, sha1); } -static inline struct llist_item * llist_insert_sorted_unique(struct llist *list, - char *sha1, struct llist_item *hint) +static inline struct llist_item *llist_insert_sorted_unique(struct llist *list, unsigned char *sha1, struct llist_item *hint) { struct llist_item *prev = NULL, *l; @@ -149,9 +149,7 @@ static inline struct llist_item * llist_insert_sorted_unique(struct llist *list, } /* returns a pointer to an item in front of sha1 */ -static inline struct llist_item * llist_sorted_remove(struct llist *list, - const char *sha1, - struct llist_item *hint) +static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *sha1, struct llist_item *hint) { struct llist_item *prev, *l; @@ -599,7 +597,8 @@ int main(int argc, char **argv) int i; struct pack_list *min, *red, *pl; struct llist *ignore; - char *sha1, buf[42]; /* 40 byte sha1 + \n + \0 */ + unsigned char *sha1; + char buf[42]; /* 40 byte sha1 + \n + \0 */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -686,7 +685,8 @@ int main(int argc, char **argv) pl = pl->next; } if (verbose) - fprintf(stderr, "%luMB of redundant packs in total.\n", pack_set_bytecount(red)/(1024*1024)); + fprintf(stderr, "%luMB of redundant packs in total.\n", + (unsigned long)pack_set_bytecount(red)/(1024*1024)); return 0; } -- cgit v0.10.2-6-g49f6 From fbf8ac212caa74fc506434da83f8e9630b09ed12 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Nov 2005 16:23:11 -0800 Subject: git-merge: make recursive the default strategy git-pull invoked merge with recursive as the default strategy for some time now; match it in the git-merge itself. Also avoid listing more than one strategy on default because we have only one strategy that can resolve an octopus and we are already counting heads here. This reduces the need to stash away local modifications. Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index 7f481e4..255476e 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -12,10 +12,8 @@ usage () { die "git-merge [-n] [--no-commit] [-s ]... +" } -# all_strategies='resolve recursive stupid octopus' - all_strategies='recursive octopus resolve stupid ours' -default_strategies='resolve octopus' +default_strategies='recursive' use_strategies= dropsave() { @@ -90,11 +88,6 @@ do shift done -case "$use_strategies" in -'') - use_strategies=$default_strategies - ;; -esac test "$#" -le 2 && usage ;# we need at least two heads. merge_msg="$1" @@ -185,6 +178,17 @@ case "$#,$common,$no_commit" in ;; esac +case "$use_strategies" in +'') + case "$#" in + 1) + use_strategies="$default_strategies" ;; + *) + use_strategies=octopus ;; + esac + ;; +esac + # At this point, we need a real merge. No matter what strategy # we use, it would operate on the index, possibly affecting the # working tree, and when resolved cleanly, have the desired tree -- cgit v0.10.2-6-g49f6 From 073dab137a746a44606e8e11629e05ff8343346f Mon Sep 17 00:00:00 2001 From: Josef Weidendorfer Date: Wed, 23 Nov 2005 12:04:23 +0100 Subject: Fix git-mv for moving directories with 1 file This is fixed by putting the file into @changedfiles/@addedfiles, and not the directory this file is in. Additionally, this fixes the behavior for attempting to overwrite a file with a directory, and gives a message for all cases where overwriting is not possible (file->dir,dir->file,dir->dir). Thanks for Alexander Litvinov for noting this problem. Signed-off-by: Josef Weidendorfer Signed-off-by: Junio C Hamano diff --git a/git-mv.perl b/git-mv.perl index a21d87e..bf54c38 100755 --- a/git-mv.perl +++ b/git-mv.perl @@ -103,13 +103,22 @@ while(scalar @srcArgs > 0) { $bad = "bad source '$src'"; } + $safesrc = quotemeta($src); + @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; + $overwritten{$dst} = 0; if (($bad eq "") && -e $dst) { $bad = "destination '$dst' already exists"; - if (-f $dst && $opt_f) { - print "Warning: $bad; will overwrite!\n"; - $bad = ""; - $overwritten{$dst} = 1; + if ($opt_f) { + # only files can overwrite each other: check both source and destination + if (-f $dst && (scalar @srcfiles == 1)) { + print "Warning: $bad; will overwrite!\n"; + $bad = ""; + $overwritten{$dst} = 1; + } + else { + $bad = "Can not overwrite '$src' with '$dst'"; + } } } @@ -118,8 +127,6 @@ while(scalar @srcArgs > 0) { } if ($bad eq "") { - $safesrc = quotemeta($src); - @srcfiles = grep /^$safesrc(\/|$)/, @allfiles; if (scalar @srcfiles == 0) { $bad = "'$src' not under version control"; } @@ -166,10 +173,12 @@ while(scalar @srcs > 0) { push @deletedfiles, @srcfiles; if (scalar @srcfiles == 1) { + # $dst can be a directory with 1 file inside if ($overwritten{$dst} ==1) { - push @changedfiles, $dst; + push @changedfiles, $dstfiles[0]; + } else { - push @addedfiles, $dst; + push @addedfiles, $dstfiles[0]; } } else { -- cgit v0.10.2-6-g49f6 From 30c7bb249e9b2038dd05271e055d347f84336576 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 23 Nov 2005 09:57:54 -0800 Subject: Allow editing of a revert-message I think all commit operations should allow editing of the message (ie we should do this for merges too), but that's _particularly_ true of doing a "git revert". We should always explain why we needed to revert something. This patch adds a "-e" or "--edit" flag to "git revert", although I actually suspect it should be on by default (and we should have a "--no-edit" flag to disable it, probably together with an automatic disable if stdin isn't a terminal). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano diff --git a/git-revert.sh b/git-revert.sh index 4154fe0..05bd854 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -25,7 +25,7 @@ usage () { esac } -no_commit= replay= +no_commit= replay= edit= while case "$#" in 0) break ;; esac do case "$1" in @@ -36,6 +36,9 @@ do -r|--r|--re|--rep|--repl|--repla|--replay) replay=t ;; + -e|--edit) + edit=t + ;; -*) usage ;; @@ -161,6 +164,7 @@ echo >&2 "Finished one $me." # If we are revert, or if our cherry-pick results in a hand merge, # we had better say that the current user is responsible for that. +[ "$edit" ] && ${EDITOR:-${VISUAL:-vi}} .msg case "$no_commit" in '') git-commit -n -F .msg -- cgit v0.10.2-6-g49f6 From b7884981f8f2a063f577df12c65e9af8b1a4f1b8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Nov 2005 16:56:55 -0800 Subject: git-revert: make --edit default. Revert always should explain why, so make --edit the default, unless stdin is not a terminal. If you really don't want to say anything, you can say "git-revert --no-edit $commit", or if you are really sick, you could also say "git-revert $commit diff --git a/git-revert.sh b/git-revert.sh index 05bd854..4ba6912 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -7,11 +7,13 @@ case "$0" in *-revert* ) + test -t 0 && edit=-e me=revert ;; *-cherry-pick* ) + edit= me=cherry-pick ;; * ) - die "What are ou talking about?" ;; + die "What are you talking about?" ;; esac usage () { @@ -25,7 +27,7 @@ usage () { esac } -no_commit= replay= edit= +no_commit= replay= while case "$#" in 0) break ;; esac do case "$1" in @@ -33,12 +35,15 @@ do --no-commi|--no-commit) no_commit=t ;; + -e|--e|--ed|--edi|--edit) + edit=-e + ;; + -n|--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) + edit= + ;; -r|--r|--re|--rep|--repl|--repla|--replay) replay=t ;; - -e|--edit) - edit=t - ;; -*) usage ;; @@ -164,10 +169,9 @@ echo >&2 "Finished one $me." # If we are revert, or if our cherry-pick results in a hand merge, # we had better say that the current user is responsible for that. -[ "$edit" ] && ${EDITOR:-${VISUAL:-vi}} .msg case "$no_commit" in '') - git-commit -n -F .msg + git-commit -n -F .msg $edit rm -f .msg ;; esac -- cgit v0.10.2-6-g49f6 From 9c20a4706767e6003e62f6500ed94ccddcbe322f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 21:46:57 -0800 Subject: Teach update-index to read from ls-tree. git-update-index --index-info can almost be usable to read from ls-tree output to update the index (and not the working tree file) to HEAD commit, but not quite. It was designed to read from git-apply --index-info output, and does not want " blob " in ls-tree output. Accept that as well. This lets us update "git-checkout " that used to filter the extra " blob " string out. Noted by Luben. Signed-off-by: Junio C Hamano diff --git a/git-checkout.sh b/git-checkout.sh index 4c08f36..9509ab4 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -82,7 +82,6 @@ then # rescuing paths and is never meant to remove what # is not in the named tree-ish. git-ls-tree -r "$new" "$@" | - sed -ne 's/^\([0-7]*\) blob \(.*\)$/\1 \2/p' | git-update-index --index-info || exit $? fi git-checkout-index -f -u -- "$@" diff --git a/update-index.c b/update-index.c index 5bbc3de..11b7f6a 100644 --- a/update-index.c +++ b/update-index.c @@ -338,7 +338,7 @@ static void read_index_info(int line_termination) struct strbuf buf; strbuf_init(&buf); while (1) { - char *ptr; + char *ptr, *tab; char *path_name; unsigned char sha1[20]; unsigned int mode; @@ -348,12 +348,15 @@ static void read_index_info(int line_termination) break; mode = strtoul(buf.buf, &ptr, 8); - if (ptr == buf.buf || *ptr != ' ' || - get_sha1_hex(ptr + 1, sha1) || - ptr[41] != '\t') + if (ptr == buf.buf || *ptr != ' ') goto bad_line; - ptr += 42; + tab = strchr(ptr, '\t'); + if (!tab || tab - ptr < 41) + goto bad_line; + if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ') + goto bad_line; + ptr = tab + 1; if (line_termination && ptr[0] == '"') path_name = unquote_c_style(ptr, NULL); -- cgit v0.10.2-6-g49f6 From a50b870aa18738ec10ed31fd4a32a284ee1cfe50 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 21 Nov 2005 21:49:06 -0800 Subject: diff-tree: teach single-commit diff-tree to honour grafts We used to read the commit objects by hand and ignored the grafts. Rewrite it using lookup_commit() API, to make it grafts-aware. Signed-off-by: Junio C Hamano diff --git a/diff-tree.c b/diff-tree.c index da9c68c..d56d921 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -69,52 +69,50 @@ static int diff_root_tree(const unsigned char *new, const char *base) return retval; } -static const char *generate_header(const char *commit, const char *parent, const char *msg, unsigned long len) +static const char *generate_header(const char *commit, const char *parent, const char *msg) { static char this_header[16384]; int offset; + unsigned long len; if (!verbose_header) return commit; + len = strlen(msg); offset = sprintf(this_header, "%s%s (from %s)\n", header_prefix, commit, parent); offset += pretty_print_commit(commit_format, msg, len, this_header + offset, sizeof(this_header) - offset); return this_header; } -static int diff_tree_commit(const unsigned char *commit, const char *name) +static int diff_tree_commit(const unsigned char *commit_sha1) { - unsigned long size, offset; - char *buf = read_object_with_reference(commit, "commit", &size, NULL); + struct commit *commit; + struct commit_list *parents; + char name[50]; + unsigned char sha1[20]; - if (!buf) + sprintf(name, "%s^0", sha1_to_hex(commit_sha1)); + if (get_sha1(name, sha1)) return -1; - - if (!name) { - static char commit_name[60]; - strcpy(commit_name, sha1_to_hex(commit)); - name = commit_name; - } - + name[40] = 0; + commit = lookup_commit(sha1); + /* Root commit? */ - if (show_root_diff && memcmp(buf + 46, "parent ", 7)) { - header = generate_header(name, "root", buf, size); - diff_root_tree(commit, ""); + if (show_root_diff && !commit->parents) { + header = generate_header(name, "root", commit->buffer); + diff_root_tree(commit_sha1, ""); } /* More than one parent? */ - if (ignore_merges) { - if (!memcmp(buf + 46 + 48, "parent ", 7)) + if (ignore_merges && commit->parents && commit->parents->next) return 0; - } - offset = 46; - while (offset + 48 < size && !memcmp(buf + offset, "parent ", 7)) { - unsigned char parent[20]; - if (get_sha1_hex(buf + offset + 7, parent)) - return -1; - header = generate_header(name, sha1_to_hex(parent), buf, size); - diff_tree_sha1_top(parent, commit, ""); + for (parents = commit->parents; parents; parents = parents->next) { + struct commit *parent = parents->item; + header = generate_header(name, + sha1_to_hex(parent->object.sha1), + commit->buffer); + diff_tree_sha1_top(parent->object.sha1, commit_sha1, ""); if (!header && verbose_header) { header_prefix = "\ndiff-tree "; /* @@ -122,9 +120,7 @@ static int diff_tree_commit(const unsigned char *commit, const char *name) * don't print the diffs. */ } - offset += 48; } - free(buf); return 0; } @@ -147,7 +143,7 @@ static int diff_tree_stdin(char *line) return diff_tree_sha1_top(parent, commit, ""); } line[40] = 0; - return diff_tree_commit(commit, line); + return diff_tree_commit(commit); } static const char diff_tree_usage[] = @@ -250,7 +246,7 @@ int main(int argc, const char **argv) usage(diff_tree_usage); break; case 1: - diff_tree_commit(sha1[0], NULL); + diff_tree_commit(sha1[0]); break; case 2: diff_tree_sha1_top(sha1[0], sha1[1], ""); -- cgit v0.10.2-6-g49f6 From ee72aeaf009417156a3599b0eb69da3f7023ca07 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 24 Nov 2005 11:36:01 +0100 Subject: Rename git-config-set to git-repo-config ... and adjust all references. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano diff --git a/.gitignore b/.gitignore index d17a8b5..8a6bd02 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ git-clone git-clone-pack git-commit git-commit-tree -git-config-set git-convert-objects git-count-objects git-cvsexportcommit @@ -76,6 +75,7 @@ git-rebase git-receive-pack git-relink git-repack +git-repo-config git-request-pull git-reset git-resolve diff --git a/Documentation/git-config-set.txt b/Documentation/git-config-set.txt deleted file mode 100644 index bfbd421..0000000 --- a/Documentation/git-config-set.txt +++ /dev/null @@ -1,170 +0,0 @@ -git-config-set(1) -=============== - -NAME ----- -git-config-set - Set options in .git/config. - - -SYNOPSIS --------- -'git-config-set' name [value [value_regex]] -'git-config-set' --replace-all name [value [value_regex]] -'git-config-set' --get name [value_regex] -'git-config-set' --get-all name [value_regex] -'git-config-set' --unset name [value_regex] -'git-config-set' --unset-all name [value_regex] - -DESCRIPTION ------------ -You can query/set/replace/unset options with this command. The name is -actually the section and the key separated by a dot, and the value will be -escaped. - -If you want to set/unset an option which can occor on multiple lines, you -should provide a POSIX regex for the value. If you want to handle the lines -*not* matching the regex, just prepend a single exlamation mark in front -(see EXAMPLES). - -This command will fail if - -. .git/config is invalid, -. .git/config can not be written to, -. no section was provided, -. the section or key is invalid, -. you try to unset an option which does not exist, or -. you try to unset/set an option for which multiple lines match. - - -OPTIONS -------- - ---replace-all:: - Default behaviour is to replace at most one line. This replaces - all lines matching the key (and optionally the value_regex) - ---get:: - Get the value for a given key (optionally filtered by a regex - matching the value). - ---get-all:: - Like get, but does not fail if the number of values for the key - is not exactly one. - ---unset:: - Remove the line matching the key from .git/config. - ---unset-all:: - Remove all matching lines from .git/config. - - -EXAMPLE -------- - -Given a .git/config like this: - - # - # This is the config file, and - # a '#' or ';' character indicates - # a comment - # - - ; core variables - [core] - ; Don't trust file modes - filemode = false - - ; Our diff algorithm - [diff] - external = "/usr/local/bin/gnu-diff -u" - 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 - -you can set the filemode to true with - ------------- -% git config-set core.filemode true ------------- - -The hypothetic proxy command entries actually have a postfix to discern -to what URL they apply. Here is how to change the entry for kernel.org -to "ssh". - ------------- -% git config-set proxy.command '"ssh" for kernel.org' 'for kernel.org$' ------------- - -This makes sure that only the key/value pair for kernel.org is replaced. - -To delete the entry for renames, do - ------------- -% git config-set --unset diff.renames ------------- - -If you want to delete an entry for a multivar (like proxy.command above), -you have to provide a regex matching the value of exactly one line. - -To query the value for a given key, do - ------------- -% git config-set --get core.filemode ------------- - -or - ------------- -% git config-set core.filemode ------------- - -or, to query a multivar: - ------------- -% git config-set --get proxy.command "for kernel.org$" ------------- - -If you want to know all the values for a multivar, do: - ------------- -% git config-set --get-all proxy.command ------------- - -If you like to live dangerous, you can replace *all* proxy.commands by a -new one with - ------------- -% git config-set --replace-all proxy.command 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 config-set proxy.command ssh '! for ' ------------- - -To actually match only values with an exclamation mark, you have to - ------------- -% git config-set section.key value '[!]' ------------- - - -Author ------- -Written by Johannes Schindelin - -Documentation --------------- -Documentation by Johannes Schindelin. - -GIT ---- -Part of the gitlink:git[7] suite - diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt new file mode 100644 index 0000000..5eefe02 --- /dev/null +++ b/Documentation/git-repo-config.txt @@ -0,0 +1,170 @@ +git-repo-config(1) +================== + +NAME +---- +git-repo-config - Get and set options in .git/config. + + +SYNOPSIS +-------- +'git-repo-config' name [value [value_regex]] +'git-repo-config' --replace-all name [value [value_regex]] +'git-repo-config' --get name [value_regex] +'git-repo-config' --get-all name [value_regex] +'git-repo-config' --unset name [value_regex] +'git-repo-config' --unset-all name [value_regex] + +DESCRIPTION +----------- +You can query/set/replace/unset options with this command. The name is +actually the section and the key separated by a dot, and the value will be +escaped. + +If you want to set/unset an option which can occor on multiple lines, you +should provide a POSIX regex for the value. If you want to handle the lines +*not* matching the regex, just prepend a single exlamation mark in front +(see EXAMPLES). + +This command will fail if + +. .git/config is invalid, +. .git/config can not be written to, +. no section was provided, +. the section or key is invalid, +. you try to unset an option which does not exist, or +. you try to unset/set an option for which multiple lines match. + + +OPTIONS +------- + +--replace-all:: + Default behaviour is to replace at most one line. This replaces + all lines matching the key (and optionally the value_regex) + +--get:: + Get the value for a given key (optionally filtered by a regex + matching the value). + +--get-all:: + Like get, but does not fail if the number of values for the key + is not exactly one. + +--unset:: + Remove the line matching the key from .git/config. + +--unset-all:: + Remove all matching lines from .git/config. + + +EXAMPLE +------- + +Given a .git/config like this: + + # + # This is the config file, and + # a '#' or ';' character indicates + # a comment + # + + ; core variables + [core] + ; Don't trust file modes + filemode = false + + ; Our diff algorithm + [diff] + external = "/usr/local/bin/gnu-diff -u" + 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 + +you can set the filemode to true with + +------------ +% git repo-config core.filemode true +------------ + +The hypothetic proxy command entries actually have a postfix to discern +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$' +------------ + +This makes sure that only the key/value pair for kernel.org is replaced. + +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), +you have to provide a regex matching the value of exactly one line. + +To query the value for a given key, do + +------------ +% git repo-config --get core.filemode +------------ + +or + +------------ +% git repo-config core.filemode +------------ + +or, to query a multivar: + +------------ +% git repo-config --get proxy.command "for kernel.org$" +------------ + +If you want to know all the values for a multivar, do: + +------------ +% git repo-config --get-all proxy.command +------------ + +If you like to live dangerous, you can replace *all* proxy.commands by a +new one with + +------------ +% git repo-config --replace-all proxy.command 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 ' +------------ + +To actually match only values with an exclamation mark, you have to + +------------ +% git repo-config section.key value '[!]' +------------ + + +Author +------ +Written by Johannes Schindelin + +Documentation +-------------- +Documentation by Johannes Schindelin. + +GIT +--- +Part of the gitlink:git[7] suite + diff --git a/Documentation/git.txt b/Documentation/git.txt index 694fee8..a518249 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -84,9 +84,6 @@ gitlink:git-checkout-index[1]:: gitlink:git-commit-tree[1]:: Creates a new commit object -gitlink:git-config-set[1]:: - Set options in .git/config. - gitlink:git-hash-object[1]:: Computes the object ID from a file. @@ -111,6 +108,9 @@ gitlink:git-prune-packed[1]:: gitlink:git-read-tree[1]:: Reads tree information into the directory index +gitlink:git-repo-config[1]:: + Get and set options in .git/config. + gitlink:git-unpack-objects[1]:: Unpacks objects out of a packed archive. diff --git a/Makefile b/Makefile index a97a5d9..984d167 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ PROGRAMS = \ git-unpack-objects$X git-update-index$X git-update-server-info$X \ 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-config-set$X git-var$X + git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X # what 'all' will build and 'install' will install. ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X diff --git a/config-set.c b/config-set.c deleted file mode 100644 index d938f96..0000000 --- a/config-set.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "cache.h" -#include - -static const char git_config_set_usage[] = -"git-config-set [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; - -static char* key = NULL; -static char* value = NULL; -static regex_t* regex = NULL; -static int do_all = 0; -static int do_not_match = 0; -static int seen = 0; - -static int show_config(const char* key_, const char* value_) -{ - if (!strcmp(key_, key) && - (regex == NULL || - (do_not_match ^ - !regexec(regex, value_, 0, NULL, 0)))) { - if (do_all) { - printf("%s\n", value_); - return 0; - } - if (seen > 0) { - fprintf(stderr, "More than one value: %s\n", value); - free(value); - } - value = strdup(value_); - seen++; - } - return 0; -} - -static int get_value(const char* key_, const char* regex_) -{ - int i; - - key = malloc(strlen(key_)+1); - for (i = 0; key_[i]; i++) - key[i] = tolower(key_[i]); - key[i] = 0; - - if (regex_) { - if (regex_[0] == '!') { - do_not_match = 1; - regex_++; - } - - regex = (regex_t*)malloc(sizeof(regex_t)); - if (regcomp(regex, regex_, REG_EXTENDED)) { - fprintf(stderr, "Invalid pattern: %s\n", regex_); - return -1; - } - } - - i = git_config(show_config); - if (value) { - printf("%s\n", value); - free(value); - } - free(key); - if (regex) { - regfree(regex); - free(regex); - } - - if (do_all) - return 0; - - return seen == 1 ? 0 : 1; -} - -int main(int argc, const char **argv) -{ - setup_git_directory(); - switch (argc) { - case 2: - return get_value(argv[1], NULL); - case 3: - if (!strcmp(argv[1], "--unset")) - return git_config_set(argv[2], NULL); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, NULL, 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], NULL); - else if (!strcmp(argv[1], "--get-all")) { - do_all = 1; - return get_value(argv[2], NULL); - } else - - return git_config_set(argv[1], argv[2]); - case 4: - if (!strcmp(argv[1], "--unset")) - return git_config_set_multivar(argv[2], NULL, argv[3], 0); - else if (!strcmp(argv[1], "--unset-all")) - return git_config_set_multivar(argv[2], NULL, argv[3], 1); - else if (!strcmp(argv[1], "--get")) - return get_value(argv[2], argv[3]); - else if (!strcmp(argv[1], "--get-all")) { - 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); - else - - return git_config_set_multivar(argv[1], argv[2], argv[3], 0); - case 5: - if (!strcmp(argv[1], "--replace-all")) - return git_config_set_multivar(argv[2], argv[3], argv[4], 1); - case 1: - default: - usage(git_config_set_usage); - } - return 0; -} diff --git a/repo-config.c b/repo-config.c new file mode 100644 index 0000000..b2569b7 --- /dev/null +++ b/repo-config.c @@ -0,0 +1,116 @@ +#include "cache.h" +#include + +static const char git_config_set_usage[] = +"git-repo-config [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]"; + +static char* key = NULL; +static char* value = NULL; +static regex_t* regex = NULL; +static int do_all = 0; +static int do_not_match = 0; +static int seen = 0; + +static int show_config(const char* key_, const char* value_) +{ + if (!strcmp(key_, key) && + (regex == NULL || + (do_not_match ^ + !regexec(regex, value_, 0, NULL, 0)))) { + if (do_all) { + printf("%s\n", value_); + return 0; + } + if (seen > 0) { + fprintf(stderr, "More than one value: %s\n", value); + free(value); + } + value = strdup(value_); + seen++; + } + return 0; +} + +static int get_value(const char* key_, const char* regex_) +{ + int i; + + key = malloc(strlen(key_)+1); + for (i = 0; key_[i]; i++) + key[i] = tolower(key_[i]); + key[i] = 0; + + if (regex_) { + if (regex_[0] == '!') { + do_not_match = 1; + regex_++; + } + + regex = (regex_t*)malloc(sizeof(regex_t)); + if (regcomp(regex, regex_, REG_EXTENDED)) { + fprintf(stderr, "Invalid pattern: %s\n", regex_); + return -1; + } + } + + i = git_config(show_config); + if (value) { + printf("%s\n", value); + free(value); + } + free(key); + if (regex) { + regfree(regex); + free(regex); + } + + if (do_all) + return 0; + + return seen == 1 ? 0 : 1; +} + +int main(int argc, const char **argv) +{ + setup_git_directory(); + switch (argc) { + case 2: + return get_value(argv[1], NULL); + case 3: + if (!strcmp(argv[1], "--unset")) + return git_config_set(argv[2], NULL); + else if (!strcmp(argv[1], "--unset-all")) + return git_config_set_multivar(argv[2], NULL, NULL, 1); + else if (!strcmp(argv[1], "--get")) + return get_value(argv[2], NULL); + else if (!strcmp(argv[1], "--get-all")) { + do_all = 1; + return get_value(argv[2], NULL); + } else + + return git_config_set(argv[1], argv[2]); + case 4: + if (!strcmp(argv[1], "--unset")) + return git_config_set_multivar(argv[2], NULL, argv[3], 0); + else if (!strcmp(argv[1], "--unset-all")) + return git_config_set_multivar(argv[2], NULL, argv[3], 1); + else if (!strcmp(argv[1], "--get")) + return get_value(argv[2], argv[3]); + else if (!strcmp(argv[1], "--get-all")) { + 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); + else + + return git_config_set_multivar(argv[1], argv[2], argv[3], 0); + case 5: + if (!strcmp(argv[1], "--replace-all")) + return git_config_set_multivar(argv[2], argv[3], argv[4], 1); + case 1: + default: + usage(git_config_set_usage); + } + return 0; +} diff --git a/t/t1300-config-set.sh b/t/t1300-config-set.sh deleted file mode 100644 index 7a5849a..0000000 --- a/t/t1300-config-set.sh +++ /dev/null @@ -1,271 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2005 Johannes Schindelin -# - -test_description='Test git-config-set in different settings' - -. ./test-lib.sh - -test -f .git/config && rm .git/config - -git-config-set core.penguin "little blue" - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue -EOF - -test_expect_success 'initial' 'cmp .git/config expect' - -git-config-set Core.Movie BadPhysics - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics -EOF - -test_expect_success 'mixed case' 'cmp .git/config expect' - -git-config-set Cores.WhatEver Second - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics -[Cores] - WhatEver = Second -EOF - -test_expect_success 'similar section' 'cmp .git/config expect' - -git-config-set CORE.UPPERCASE true - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = little blue - Movie = BadPhysics - UPPERCASE = true -[Cores] - WhatEver = Second -EOF - -test_expect_success 'similar section' 'cmp .git/config expect' - -test_expect_success 'replace with non-match' \ - 'git-config-set core.penguin kingpin !blue' - -test_expect_success 'replace with non-match (actually matching)' \ - 'git-config-set core.penguin "very blue" !kingpin' - -cat > expect << EOF -# -# This is the config file -# - -[core] - penguin = very blue - Movie = BadPhysics - UPPERCASE = true - penguin = kingpin -[Cores] - WhatEver = Second -EOF - -test_expect_success 'non-match result' 'cmp .git/config expect' - -cat > .git/config << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha ="beta" # last silly comment -haha = hello - haha = bello -[nextSection] noNewline = ouch -EOF - -cp .git/config .git/config2 - -test_expect_success 'multiple unset' \ - 'git-config-set --unset-all beta.haha' - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] noNewline = ouch -EOF - -test_expect_success 'multiple unset is correct' 'cmp .git/config expect' - -mv .git/config2 .git/config - -test_expect_success '--replace-all' \ - 'git-config-set --replace-all beta.haha gamma' - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha = gamma -[nextSection] noNewline = ouch -EOF - -test_expect_success 'all replaced' 'cmp .git/config expect' - -git-config-set beta.haha alpha - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha = alpha -[nextSection] noNewline = ouch -EOF - -test_expect_success 'really mean test' 'cmp .git/config expect' - -git-config-set nextsection.nonewline wow - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment - haha = alpha -[nextSection] - nonewline = wow -EOF - -test_expect_success 'really really mean test' 'cmp .git/config expect' - -test_expect_success 'get value' 'test alpha = $(git-config-set beta.haha)' -git-config-set --unset beta.haha - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow -EOF - -test_expect_success 'unset' 'cmp .git/config expect' - -git-config-set nextsection.NoNewLine "wow2 for me" "for me$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar' 'cmp .git/config expect' - -test_expect_success 'non-match' \ - 'git-config-set --get nextsection.nonewline !for' - -test_expect_success 'non-match value' \ - 'test wow = $(git-config-set --get nextsection.nonewline !for)' - -test_expect_failure 'ambiguous get' \ - 'git-config-set --get nextsection.nonewline' - -test_expect_success 'get multivar' \ - 'git-config-set --get-all nextsection.nonewline' - -git-config-set nextsection.nonewline "wow3" "wow$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - nonewline = wow3 - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar replace' 'cmp .git/config expect' - -test_expect_failure 'ambiguous value' 'git-config-set nextsection.nonewline' - -test_expect_failure 'ambiguous unset' \ - 'git-config-set --unset nextsection.nonewline' - -test_expect_failure 'invalid unset' \ - 'git-config-set --unset somesection.nonewline' - -git-config-set --unset nextsection.nonewline "wow3$" - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - NoNewLine = wow2 for me -EOF - -test_expect_success 'multivar unset' 'cmp .git/config expect' - -test_expect_failure 'invalid key' 'git-config-set inval.2key blabla' - -test_expect_success 'correct key' 'git-config-set 123456.a123 987' - -test_expect_success 'hierarchical section' \ - 'git-config-set 1.2.3.alpha beta' - -cat > expect << EOF -[beta] ; silly comment # another comment -noIndent= sillyValue ; 'nother silly comment - -# empty line - ; comment -[nextSection] - NoNewLine = wow2 for me -[123456] - a123 = 987 -[1.2.3] - alpha = beta -EOF - -test_expect_success 'hierarchical section value' 'cmp .git/config expect' - -test_done - diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh new file mode 100644 index 0000000..5e994ff --- /dev/null +++ b/t/t1300-repo-config.sh @@ -0,0 +1,271 @@ +#!/bin/sh +# +# Copyright (c) 2005 Johannes Schindelin +# + +test_description='Test git-repo-config in different settings' + +. ./test-lib.sh + +test -f .git/config && rm .git/config + +git-repo-config core.penguin "little blue" + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue +EOF + +test_expect_success 'initial' 'cmp .git/config expect' + +git-repo-config Core.Movie BadPhysics + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +EOF + +test_expect_success 'mixed case' 'cmp .git/config expect' + +git-repo-config Cores.WhatEver Second + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +git-repo-config CORE.UPPERCASE true + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = little blue + Movie = BadPhysics + UPPERCASE = true +[Cores] + WhatEver = Second +EOF + +test_expect_success 'similar section' 'cmp .git/config expect' + +test_expect_success 'replace with non-match' \ + 'git-repo-config core.penguin kingpin !blue' + +test_expect_success 'replace with non-match (actually matching)' \ + 'git-repo-config core.penguin "very blue" !kingpin' + +cat > expect << EOF +# +# This is the config file +# + +[core] + penguin = very blue + Movie = BadPhysics + UPPERCASE = true + penguin = kingpin +[Cores] + WhatEver = Second +EOF + +test_expect_success 'non-match result' 'cmp .git/config expect' + +cat > .git/config << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha ="beta" # last silly comment +haha = hello + haha = bello +[nextSection] noNewline = ouch +EOF + +cp .git/config .git/config2 + +test_expect_success 'multiple unset' \ + 'git-repo-config --unset-all beta.haha' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] noNewline = ouch +EOF + +test_expect_success 'multiple unset is correct' 'cmp .git/config expect' + +mv .git/config2 .git/config + +test_expect_success '--replace-all' \ + 'git-repo-config --replace-all beta.haha gamma' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = gamma +[nextSection] noNewline = ouch +EOF + +test_expect_success 'all replaced' 'cmp .git/config expect' + +git-repo-config beta.haha alpha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] noNewline = ouch +EOF + +test_expect_success 'really mean test' 'cmp .git/config expect' + +git-repo-config nextsection.nonewline wow + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment + haha = alpha +[nextSection] + nonewline = wow +EOF + +test_expect_success 'really really mean test' 'cmp .git/config expect' + +test_expect_success 'get value' 'test alpha = $(git-repo-config beta.haha)' +git-repo-config --unset beta.haha + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow +EOF + +test_expect_success 'unset' 'cmp .git/config expect' + +git-repo-config nextsection.NoNewLine "wow2 for me" "for me$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar' 'cmp .git/config expect' + +test_expect_success 'non-match' \ + 'git-repo-config --get nextsection.nonewline !for' + +test_expect_success 'non-match value' \ + 'test wow = $(git-repo-config --get nextsection.nonewline !for)' + +test_expect_failure 'ambiguous get' \ + 'git-repo-config --get nextsection.nonewline' + +test_expect_success 'get multivar' \ + 'git-repo-config --get-all nextsection.nonewline' + +git-repo-config nextsection.nonewline "wow3" "wow$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + nonewline = wow3 + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar replace' 'cmp .git/config expect' + +test_expect_failure 'ambiguous value' 'git-repo-config nextsection.nonewline' + +test_expect_failure 'ambiguous unset' \ + 'git-repo-config --unset nextsection.nonewline' + +test_expect_failure 'invalid unset' \ + 'git-repo-config --unset somesection.nonewline' + +git-repo-config --unset nextsection.nonewline "wow3$" + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + NoNewLine = wow2 for me +EOF + +test_expect_success 'multivar unset' 'cmp .git/config expect' + +test_expect_failure 'invalid key' 'git-repo-config inval.2key blabla' + +test_expect_success 'correct key' 'git-repo-config 123456.a123 987' + +test_expect_success 'hierarchical section' \ + 'git-repo-config 1.2.3.alpha beta' + +cat > expect << EOF +[beta] ; silly comment # another comment +noIndent= sillyValue ; 'nother silly comment + +# empty line + ; comment +[nextSection] + NoNewLine = wow2 for me +[123456] + a123 = 987 +[1.2.3] + alpha = beta +EOF + +test_expect_success 'hierarchical section value' 'cmp .git/config expect' + +test_done + -- cgit v0.10.2-6-g49f6 From 2c52a42dd720ed3cf1eadbc4a4e92be4cdf52f1d Mon Sep 17 00:00:00 2001 From: Andreas Ericsson Date: Fri, 25 Nov 2005 02:37:09 +0100 Subject: cvsimport: Don't let local settings hinder cvs user-migration. Avoid this by passing "--norc" to cvsps. Signed-off-by: Andreas Ericsson Signed-off-by: Junio C Hamano diff --git a/git-cvsimport.perl b/git-cvsimport.perl index efe1934..08a890c 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -502,7 +502,7 @@ unless($pid) { if ($opt_P) { exec("cat", $opt_P); } else { - exec("cvsps",@opt,"-u","-A",'--root',$opt_d,$cvs_tree); + exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree); die "Could not start cvsps: $!\n"; } } -- cgit v0.10.2-6-g49f6 From 874fbc332349bb77d8da596d3fef534ad9afc0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Fri, 25 Nov 2005 19:03:05 +0900 Subject: Fix a warning about unused value. Fix a warning: git.c:276: warning: value computed is not used Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: Junio C Hamano diff --git a/git.c b/git.c index bdd3f8d..0b10b6e 100644 --- a/git.c +++ b/git.c @@ -273,7 +273,7 @@ int main(int argc, char **argv, char **envp) while (!strncmp(exec_path, "./", 2)) { exec_path += 2; while (*exec_path == '/') - *exec_path++; + exec_path++; } snprintf(git_command + len, sizeof(git_command) - len, "/%s", exec_path); -- cgit v0.10.2-6-g49f6 From 3a2f2bb354d920e3f1b7f4334d3656c77e9fb423 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 11:10:49 -0800 Subject: config.c: avoid shadowing global. This is purely cosmetic, but avoid shadowing "FILE *config_file" global in git_config_set_multivar() function. Signed-off-by: Junio C Hamano diff --git a/config.c b/config.c index 18d59ee..5cc8535 100644 --- a/config.c +++ b/config.c @@ -398,7 +398,7 @@ int git_config_set_multivar(const char* key, const char* value, int i; struct stat st; int fd; - char* config_file = strdup(git_path("config")); + char* config_filename = strdup(git_path("config")); char* lock_file = strdup(git_path("config.lock")); const char* last_dot = strrchr(key, '.'); @@ -444,7 +444,7 @@ int git_config_set_multivar(const char* key, const char* value, /* * If .git/config does not exist yet, write a minimal version. */ - if (stat(config_file, &st)) { + if (stat(config_filename, &st)) { static const char contents[] = "#\n" "# This is the config file\n" @@ -523,7 +523,7 @@ int git_config_set_multivar(const char* key, const char* value, return 5; } - in_fd = open(config_file, O_RDONLY, 0666); + in_fd = open(config_filename, O_RDONLY, 0666); contents = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in_fd, 0); close(in_fd); @@ -564,12 +564,12 @@ int git_config_set_multivar(const char* key, const char* value, st.st_size - copy_begin); munmap(contents, st.st_size); - unlink(config_file); + unlink(config_filename); } close(fd); - if (rename(lock_file, config_file) < 0) { + if (rename(lock_file, config_filename) < 0) { fprintf(stderr, "Could not rename the lock file?\n"); return 4; } -- cgit v0.10.2-6-g49f6 From f678dd180a037283decb76201b537dca24de848f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 13:33:14 -0800 Subject: Prevent "git-commit -a path1 path2..." When you want to create a partial commit, giving -a by mistake would ignore the given path. Prevent it. Signed-off-by: Junio C Hamano diff --git a/git-commit.sh b/git-commit.sh index 41955e8..27aea80 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -92,10 +92,13 @@ tt*) esac case "$all,$#" in -t,*) +t,0) git-diff-files --name-only -z | git-update-index --remove -z --stdin ;; +t,*) + die "Cannot use -a and explicit files at the same time." + ;; ,0) ;; *) -- cgit v0.10.2-6-g49f6 From e8cc80d03934cc607e3a4d89a05350c238dbf9c5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 23 Nov 2005 23:46:13 -0800 Subject: parse-remote and ls-remote clean-up. There is no reason to use git-sh-setup from git-ls-remote. git-parse-remote can help the caller to use .git/remotes shortcut if it is run inside a git repository, but can still be useful outside a git repositoryas long as the caller does not use any shortcut. Use "git-rev-parse --git-dir" to figure out where the GIT_DIR is, instead of using git-sh-setup. This also makes "git-ls-remote origin" to work from inside a subdirectory of a git managed repository as a side effect. Signed-off-by: Junio C Hamano diff --git a/git-ls-remote.sh b/git-ls-remote.sh index f0f0b07..dc6a775 100755 --- a/git-ls-remote.sh +++ b/git-ls-remote.sh @@ -1,6 +1,5 @@ #!/bin/sh # -. git-sh-setup usage () { echo >&2 "usage: $0 [--heads] [--tags] ..." diff --git a/git-parse-remote.sh b/git-parse-remote.sh index aea7b0e..5f158c6 100755 --- a/git-parse-remote.sh +++ b/git-parse-remote.sh @@ -1,6 +1,8 @@ #!/bin/sh -. git-sh-setup +# git-ls-remote could be called from outside a git managed repository; +# this would fail in that case and would issue an error message. +GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :; get_data_source () { case "$1" in -- cgit v0.10.2-6-g49f6 From ae2b0f15180d4b044828c836bcab6a990efa5c8e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 24 Nov 2005 00:12:11 -0800 Subject: git-sh-setup: die if outside git repository. Now all the users of this script detect its exit status and die, complaining that it is outside git repository. So move the code that dies from all callers to git-sh-setup script. Signed-off-by: Junio C Hamano diff --git a/git-am.sh b/git-am.sh index 8f073c9..660b3a4 100755 --- a/git-am.sh +++ b/git-am.sh @@ -1,7 +1,7 @@ #!/bin/sh # # -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { echo >&2 "usage: $0 [--signoff] [--dotest=] [--utf8] [--binary] [--3way] " diff --git a/git-applymbox.sh b/git-applymbox.sh index 6de6932..24d4a8c 100755 --- a/git-applymbox.sh +++ b/git-applymbox.sh @@ -18,7 +18,7 @@ ## ## git-am is supposed to be the newer and better tool for this job. -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { echo >&2 "applymbox [-u] [-k] [-q] [-m] (-c .dotest/ | mbox) [signoff]" diff --git a/git-applypatch.sh b/git-applypatch.sh index 66fd19a..f0549960 100755 --- a/git-applypatch.sh +++ b/git-applypatch.sh @@ -10,7 +10,7 @@ ## $3 - "info" file with Author, email and subject ## $4 - optional file containing signoff to add ## -. git-sh-setup || die "Not a git archive." +. git-sh-setup final=.dotest/final-commit ## diff --git a/git-bisect.sh b/git-bisect.sh index 1ab2f18..d92993b 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,5 +1,5 @@ #!/bin/sh -. git-sh-setup || dir "Not a git archive" +. git-sh-setup usage() { echo >&2 'usage: git bisect [start|bad|good|next|reset|visualize] diff --git a/git-branch.sh b/git-branch.sh index 2594518..4cd5da1 100755 --- a/git-branch.sh +++ b/git-branch.sh @@ -1,6 +1,6 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { echo >&2 "usage: $(basename $0)"' [-d ] | [[-f] [start-point]] diff --git a/git-checkout.sh b/git-checkout.sh index 9509ab4..4cf30e2 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -1,5 +1,5 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die "usage: git checkout [-f] [-b ] [] [...]" diff --git a/git-cherry.sh b/git-cherry.sh index aad2e61..867522b 100755 --- a/git-cherry.sh +++ b/git-cherry.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -. git-sh-setup || die "Not a git archive." +. git-sh-setup usage="usage: $0 "'[-v] [] diff --git a/git-commit.sh b/git-commit.sh index 27aea80..3d250ec 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Linus Torvalds # -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die 'git commit [-a] [-s] [-v | --no-verify] [-m | -F | (-C|-c) ] [-e] [...]' diff --git a/git-count-objects.sh b/git-count-objects.sh index fc61a1a..d6e9a32 100755 --- a/git-count-objects.sh +++ b/git-count-objects.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -. git-sh-setup || die "Not a git repository" +. git-sh-setup dc /dev/null || { # This is not a real DC at all -- it just knows how diff --git a/git-fetch.sh b/git-fetch.sh index 6586e77..14ea295 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -1,6 +1,6 @@ #!/bin/sh # -. git-sh-setup || die "Not a git archive" +. git-sh-setup . git-parse-remote _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" diff --git a/git-format-patch.sh b/git-format-patch.sh index 351790c..bc56876 100755 --- a/git-format-patch.sh +++ b/git-format-patch.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -. git-sh-setup || die "Not a git archive." +. git-sh-setup usage () { echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox] diff --git a/git-lost-found.sh b/git-lost-found.sh index 3892f52..9dd7430 100755 --- a/git-lost-found.sh +++ b/git-lost-found.sh @@ -1,6 +1,6 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive." +. git-sh-setup laf="$GIT_DIR/lost-found" rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit diff --git a/git-merge.sh b/git-merge.sh index 255476e..d352a3c 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano # -. git-sh-setup || die "Not a git archive" +. git-sh-setup LF=' ' diff --git a/git-octopus.sh b/git-octopus.sh index d2471af..2edbf52 100755 --- a/git-octopus.sh +++ b/git-octopus.sh @@ -4,7 +4,7 @@ # # Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD. # -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die "usage: git octopus" diff --git a/git-prune.sh b/git-prune.sh index c4de7f5..1fd8c73 100755 --- a/git-prune.sh +++ b/git-prune.sh @@ -1,6 +1,6 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup dryrun= echo= diff --git a/git-pull.sh b/git-pull.sh index 3b875ad..3a13984 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -4,7 +4,7 @@ # # Fetch one or more remote refs and merge it/them into the current HEAD. -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { echo >&2 "usage: $0"' [-n] [--no-commit] [--no-summary] [--help] diff --git a/git-push.sh b/git-push.sh index edc0b83..140c8f8 100755 --- a/git-push.sh +++ b/git-push.sh @@ -1,5 +1,5 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die "Usage: git push [--all] [--force] []" diff --git a/git-rebase.sh b/git-rebase.sh index 5289762..2bc3a12 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Junio C Hamano. # -. git-sh-setup || die "Not a git archive." +. git-sh-setup # The other head is given other=$(git-rev-parse --verify "$1^0") || exit diff --git a/git-repack.sh b/git-repack.sh index c0f271d..430ddc5 100755 --- a/git-repack.sh +++ b/git-repack.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Linus Torvalds # -. git-sh-setup || die "Not a git archive" +. git-sh-setup no_update_info= all_into_one= remove_redundant= local= while case "$#" in 0) break ;; esac diff --git a/git-reset.sh b/git-reset.sh index 2086d26..72ef303 100755 --- a/git-reset.sh +++ b/git-reset.sh @@ -1,5 +1,5 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die 'Usage: git reset [--mixed | --soft | --hard] []' diff --git a/git-resolve.sh b/git-resolve.sh index 7d8fb54..fcc5ad7 100755 --- a/git-resolve.sh +++ b/git-resolve.sh @@ -4,7 +4,7 @@ # # Resolve two trees. # -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { die "git-resolve " diff --git a/git-revert.sh b/git-revert.sh index 4ba6912..c1aebb1 100755 --- a/git-revert.sh +++ b/git-revert.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Linus Torvalds # Copyright (c) 2005 Junio C Hamano # -. git-sh-setup || die "Not a git archive" +. git-sh-setup case "$0" in *-revert* ) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index dbb9884..e343349 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -1,10 +1,9 @@ #!/bin/sh # -# Set up GIT_DIR and GIT_OBJECT_DIRECTORY -# and return true if everything looks ok -# -: ${GIT_DIR=.git} -: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"} +# This is included in commands that either have to be run from the toplevel +# of the repository, or with GIT_DIR environment variable properly. +# If the GIT_DIR does not look like the right correct git-repository, +# it dies. # Having this variable in your environment would break scripts because # you would cause "cd" to be be taken to unexpected places. If you @@ -12,6 +11,9 @@ # exporting it. unset CDPATH +: ${GIT_DIR=.git} +: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"} + die() { echo >&2 "$@" exit 1 @@ -22,4 +24,5 @@ refs/*) : ;; *) false ;; esac && [ -d "$GIT_DIR/refs" ] && -[ -d "$GIT_OBJECT_DIRECTORY/" ] +[ -d "$GIT_OBJECT_DIRECTORY/" ] || + die "Not a git repository." diff --git a/git-status.sh b/git-status.sh index 837f334..b90ffc1 100755 --- a/git-status.sh +++ b/git-status.sh @@ -2,7 +2,7 @@ # # Copyright (c) 2005 Linus Torvalds # -. git-sh-setup || die "Not a git archive" +GIT_DIR=$(git-rev-parse --git-dir) || exit report () { header="# diff --git a/git-tag.sh b/git-tag.sh index 1375945..16efc5b 100755 --- a/git-tag.sh +++ b/git-tag.sh @@ -1,7 +1,7 @@ #!/bin/sh # Copyright (c) 2005 Linus Torvalds -. git-sh-setup || die "Not a git archive" +. git-sh-setup usage () { echo >&2 "Usage: git-tag [-a | -s | -u ] [-f | -d] [-m ] []" diff --git a/git-verify-tag.sh b/git-verify-tag.sh index ed4c893..3c65f4a 100755 --- a/git-verify-tag.sh +++ b/git-verify-tag.sh @@ -1,5 +1,5 @@ #!/bin/sh -. git-sh-setup || die "Not a git archive" +. git-sh-setup type="$(git-cat-file -t "$1" 2>/dev/null)" || die "$1: no such object." -- cgit v0.10.2-6-g49f6 From 5d318ce6a9fb18448e4a1a9b61d355d391ddc5ec Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 24 Nov 2005 01:31:01 -0800 Subject: git-var: make it work in subdirectory. Use setup_git_directory() so that it can find its .git directory. Signed-off-by: Junio C Hamano diff --git a/var.c b/var.c index 9804459..59da56d 100644 --- a/var.c +++ b/var.c @@ -57,6 +57,8 @@ int main(int argc, char **argv) if (argc != 2) { usage(var_usage); } + + setup_git_directory(); setup_ident(); val = NULL; -- cgit v0.10.2-6-g49f6 From 5e7bfe256755ef2862e960864db1edb829c82b77 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 15:43:41 -0800 Subject: setup_git_directory: make sure GIT_DIR is a valid repository. setup_git_directory() always trusted what the user told where GIT_DIR was, and assumed that is a valid .git/ directory. This commit changes it to at least do the same level validation as is_toplevel_directory() does -- has refs/, has objects/ unless GIT_OBJECT_DIRECTORY is set, and has valid HEAD symlink or symref. Signed-off-by: Junio C Hamano diff --git a/setup.c b/setup.c index c487d7e..ab3c778 100644 --- a/setup.c +++ b/setup.c @@ -73,8 +73,8 @@ const char **get_pathspec(const char *prefix, const char **pathspec) } /* - * Test it it looks like we're at the top - * level git directory. We want to see a + * Test if it looks like we're at the top level git directory. + * We want to see: * * - either a .git/objects/ directory _or_ the proper * GIT_OBJECT_DIRECTORY environment variable @@ -92,17 +92,43 @@ static int is_toplevel_directory(void) return 1; } -const char *setup_git_directory(void) +static const char *setup_git_directory_1(void) { static char cwd[PATH_MAX+1]; int len, offset; /* * If GIT_DIR is set explicitly, we're not going - * to do any discovery + * to do any discovery, but we still do repository + * validation. */ - if (getenv(GIT_DIR_ENVIRONMENT)) + if (getenv(GIT_DIR_ENVIRONMENT)) { + char path[PATH_MAX]; + int len = strlen(getenv(GIT_DIR_ENVIRONMENT)); + if (sizeof(path) - 40 < len) + die("'$%s' too big", GIT_DIR_ENVIRONMENT); + memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len); + + strcpy(path + len, "/refs"); + if (access(path, X_OK)) + goto bad_dir_environ; + strcpy(path + len, "/HEAD"); + if (validate_symref(path)) + goto bad_dir_environ; + if (getenv(DB_ENVIRONMENT)) { + if (access(DB_ENVIRONMENT, X_OK)) + goto bad_dir_environ; + } + else { + strcpy(path + len, "/objects"); + if (access(path, X_OK)) + goto bad_dir_environ; + } return NULL; + bad_dir_environ: + path[len] = 0; + die("Not a git repository: '%s'", path); + } if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/') die("Unable to read current working directory"); @@ -127,3 +153,9 @@ const char *setup_git_directory(void) cwd[len] = 0; return cwd + offset; } + +const char *setup_git_directory(void) +{ + const char *retval = setup_git_directory_1(); + return retval; +} -- cgit v0.10.2-6-g49f6 From 2a1ddc58e4d2220feea7eba4623dac07a0f83f94 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 25 Nov 2005 15:52:57 -0800 Subject: git-sh-setup: move the repository check to a core program. Any core commands that use setup_git_directory() now check if given GIT_DIR is really a valid repository, so the same check in git-sh-setup can use it without reimplementing it in shell. This commit changes git-sh-setup to use git-var command for that, although any other commands would do. Note that we export GIT_DIR explicitly when calling git-var; without it, the caller of this script would use GIT_DIR that we return (which is to assume ./.git unless the caller has it elsewhere) while git-var would go up to find a .git directory in our parent directories, which would be checking a different directory from what our callers will be using. Signed-off-by: Junio C Hamano diff --git a/git-sh-setup.sh b/git-sh-setup.sh index e343349..b4f1022 100755 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -19,10 +19,5 @@ die() { exit 1 } -case "$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD 2>/dev/null)" in -refs/*) : ;; -*) false ;; -esac && -[ -d "$GIT_DIR/refs" ] && -[ -d "$GIT_OBJECT_DIRECTORY/" ] || - die "Not a git repository." +# Make sure we are in a valid repository of a vintage we understand. +GIT_DIR="$GIT_DIR" git-var GIT_AUTHOR_IDENT >/dev/null || exit -- cgit v0.10.2-6-g49f6