at v192 9.0 kB view raw
1#! /bin/sh -e 2 3url= 4rev= 5expHash= 6hashType=$NIX_HASH_ALGO 7deepClone=$NIX_PREFETCH_GIT_DEEP_CLONE 8leaveDotGit=$NIX_PREFETCH_GIT_LEAVE_DOT_GIT 9fetchSubmodules= 10builder= 11branchName=$NIX_PREFETCH_GIT_BRANCH_NAME 12 13if test -n "$deepClone"; then 14 deepClone=true 15else 16 deepClone=false 17fi 18 19if test "$leaveDotGit" != 1; then 20 leaveDotGit= 21else 22 leaveDotGit=true 23fi 24 25 26argi=0 27argfun="" 28for arg; do 29 if test -z "$argfun"; then 30 case $arg in 31 --out) argfun=set_out;; 32 --url) argfun=set_url;; 33 --rev) argfun=set_rev;; 34 --hash) argfun=set_hashType;; 35 --branch-name) argfun=set_branchName;; 36 --deepClone) deepClone=true;; 37 --no-deepClone) deepClone=false;; 38 --leave-dotGit) leaveDotGit=true;; 39 --fetch-submodules) fetchSubmodules=true;; 40 --builder) builder=true;; 41 *) 42 argi=$(($argi + 1)) 43 case $argi in 44 1) url=$arg;; 45 2) rev=$arg;; 46 3) expHash=$arg;; 47 *) exit 1;; 48 esac 49 ;; 50 esac 51 else 52 case $argfun in 53 set_*) 54 var=$(echo $argfun | sed 's,^set_,,') 55 eval $var=$arg 56 ;; 57 esac 58 argfun="" 59 fi 60done 61 62usage(){ 63 echo >&2 "syntax: nix-prefetch-git [options] [URL [REVISION [EXPECTED-HASH]]] 64 65Options: 66 --out path Path where the output would be stored. 67 --url url Any url understand by 'git clone'. 68 --rev ref Any sha1 or references (such as refs/heads/master) 69 --hash h Expected hash. 70 --deepClone Clone submodules recursively. 71 --no-deepClone Do not clone submodules. 72 --leave-dotGit Keep the .git directories. 73 --fetch-submodules Fetch submodules. 74 --builder Clone as fetchgit does, but url, rev, and out option are mandatory. 75" 76 exit 1 77} 78 79if test -z "$url"; then 80 usage 81fi 82 83 84init_remote(){ 85 local url=$1 86 git init 87 git remote add origin $url 88 ( [ -n "$http_proxy" ] && git config http.proxy $http_proxy ) || true 89} 90 91# Return the reference of an hash if it exists on the remote repository. 92ref_from_hash(){ 93 local hash=$1 94 git ls-remote origin | sed -n "\,$hash\t, { s,\(.*\)\t\(.*\),\2,; p; q}" 95} 96 97# Return the hash of a reference if it exists on the remote repository. 98hash_from_ref(){ 99 local ref=$1 100 git ls-remote origin | sed -n "\,\t$ref, { s,\(.*\)\t\(.*\),\1,; p; q}" 101} 102 103# Fetch everything and checkout the right sha1 104checkout_hash(){ 105 local hash="$1" 106 local ref="$2" 107 108 if test -z "$hash"; then 109 hash=$(hash_from_ref $ref) 110 fi 111 112 git fetch ${builder:+--progress} origin || return 1 113 git checkout -b $branchName $hash || return 1 114} 115 116# Fetch only a branch/tag and checkout it. 117checkout_ref(){ 118 local hash="$1" 119 local ref="$2" 120 121 if "$deepClone"; then 122 # The caller explicitly asked for a deep clone. Deep clones 123 # allow "git describe" and similar tools to work. See 124 # http://thread.gmane.org/gmane.linux.distributions.nixos/3569 125 # for a discussion. 126 return 1 127 fi 128 129 if test -z "$ref"; then 130 ref=$(ref_from_hash $hash) 131 fi 132 133 if test -n "$ref"; then 134 # --depth option is ignored on http repository. 135 git fetch ${builder:+--progress} --depth 1 origin +"$ref" || return 1 136 git checkout -b $branchName FETCH_HEAD || return 1 137 else 138 return 1 139 fi 140} 141 142# Update submodules 143init_submodules(){ 144 # Add urls into .git/config file 145 git submodule init 146 147 # list submodule directories and their hashes 148 git submodule status | 149 while read l; do 150 # checkout each submodule 151 local hash=$(echo $l | awk '{print substr($1,2)}') 152 local dir=$(echo $l | awk '{print $2}') 153 local name=$( 154 git config -f .gitmodules --get-regexp submodule\..*\.path | 155 sed -n "s,^\(.*\)\.path $dir\$,\\1,p") 156 local url=$(git config --get ${name}.url) 157 158 clone "$dir" "$url" "$hash" "" 159 done 160} 161 162clone(){ 163 local top=$(pwd) 164 local dir="$1" 165 local url="$2" 166 local hash="$3" 167 local ref="$4" 168 169 cd $dir 170 171 # Initialize the repository. 172 init_remote "$url" 173 174 # Download data from the repository. 175 checkout_ref "$hash" "$ref" || 176 checkout_hash "$hash" "$ref" || ( 177 echo 1>&2 "Unable to checkout $hash$ref from $url." 178 exit 1 179 ) 180 181 # Checkout linked sources. 182 if test -n "$fetchSubmodules"; then 183 init_submodules 184 fi 185 186 if [ -z "$builder" -a -f .topdeps ]; then 187 if tg help 2>&1 > /dev/null 188 then 189 echo "populating TopGit branches..." 190 tg remote --populate origin 191 else 192 echo "WARNING: would populate TopGit branches but TopGit is not available" >&2 193 echo "WARNING: install TopGit to fix the problem" >&2 194 fi 195 fi 196 197 cd $top 198} 199 200# Remove all remote branches, remove tags not reachable from HEAD, do a full 201# repack and then garbage collect unreferenced objects. 202make_deterministic_repo(){ 203 local repo="$1" 204 205 # run in sub-shell to not touch current working directory 206 ( 207 cd "$repo" 208 # Remove files that contain timestamps or otherwise have non-deterministic 209 # properties. 210 rm -rf .git/logs/ .git/hooks/ .git/index .git/FETCH_HEAD .git/ORIG_HEAD \ 211 .git/refs/remotes/origin/HEAD .git/config 212 213 # Remove all remote branches. 214 git branch -r | while read branch; do 215 git branch -rD "$branch" >&2 216 done 217 218 # Remove tags not reachable from HEAD. If we're exactly on a tag, don't 219 # delete it. 220 maybe_tag=$(git tag --points-at HEAD) 221 git tag --contains HEAD | while read tag; do 222 if [ "$tag" != "$maybe_tag" ]; then 223 git tag -d "$tag" >&2 224 fi 225 done 226 227 # Do a full repack. Must run single-threaded, or else we lose determinism. 228 git config pack.threads 1 229 git repack -A -d -f 230 rm -f .git/config 231 232 # Garbage collect unreferenced objects. 233 git gc --prune=all 234 ) 235} 236 237 238clone_user_rev() { 239 local dir="$1" 240 local url="$2" 241 local rev="${3:-HEAD}" 242 243 # Perform the checkout. 244 case "$rev" in 245 HEAD|refs/*) 246 clone "$dir" "$url" "" "$rev" 1>&2;; 247 *) 248 if test -z "$(echo $rev | tr -d 0123456789abcdef)"; then 249 clone "$dir" "$url" "$rev" "" 1>&2 250 else 251 echo 1>&2 "Bad commit hash or bad reference." 252 exit 1 253 fi;; 254 esac 255 256 local full_revision=$(cd $dir && (git rev-parse $rev 2> /dev/null || git rev-parse refs/heads/$branchName) | tail -n1) 257 echo "git revision is $full_revision" 258 echo "git human-readable version is $(cd $dir && (git describe $full_revision 2> /dev/null || git describe --tags $full_revision 2> /dev/null || echo -- none --))" >&2 259 echo "Commit date is $(cd $dir && git show --no-patch --pretty=%ci $full_revision)" 260 261 # Allow doing additional processing before .git removal 262 eval "$NIX_PREFETCH_GIT_CHECKOUT_HOOK" 263 if test -z "$leaveDotGit"; then 264 echo "removing \`.git'..." >&2 265 find $dir -name .git\* | xargs rm -rf 266 else 267 find $dir -name .git | while read gitdir; do 268 make_deterministic_repo "$(readlink -f "$gitdir/..")" 269 done 270 fi 271} 272 273if test -z "$branchName"; then 274 branchName=fetchgit 275fi 276 277if test -n "$builder"; then 278 test -n "$out" -a -n "$url" -a -n "$rev" || usage 279 mkdir $out 280 clone_user_rev "$out" "$url" "$rev" 281else 282 if test -z "$hashType"; then 283 hashType=sha256 284 fi 285 286 # If the hash was given, a file with that hash may already be in the 287 # store. 288 if test -n "$expHash"; then 289 finalPath=$(nix-store --print-fixed-path --recursive "$hashType" "$expHash" git-export) 290 if ! nix-store --check-validity "$finalPath" 2> /dev/null; then 291 finalPath= 292 fi 293 hash=$expHash 294 fi 295 296 # If we don't know the hash or a path with that hash doesn't exist, 297 # download the file and add it to the store. 298 if test -z "$finalPath"; then 299 300 tmpPath="$(mktemp -d "${TMPDIR:-/tmp}/git-checkout-tmp-XXXXXXXX")" 301 trap "rm -rf \"$tmpPath\"" EXIT 302 303 tmpFile="$tmpPath/git-export" 304 mkdir "$tmpFile" 305 306 # Perform the checkout. 307 clone_user_rev "$tmpFile" "$url" "$rev" 308 309 # Compute the hash. 310 hash=$(nix-hash --type $hashType $hashFormat $tmpFile) 311 if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi 312 313 # Add the downloaded file to the Nix store. 314 finalPath=$(nix-store --add-fixed --recursive "$hashType" $tmpFile) 315 316 if test -n "$expHash" -a "$expHash" != "$hash"; then 317 echo "hash mismatch for URL \`$url'" 318 exit 1 319 fi 320 fi 321 322 if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi 323 324 echo $hash 325 326 if test -n "$PRINT_PATH"; then 327 echo $finalPath 328 fi 329fi