summary refs log tree commit diff
path: root/pkgs/build-support/fetchgit/nix-prefetch-git
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/build-support/fetchgit/nix-prefetch-git')
-rwxr-xr-xpkgs/build-support/fetchgit/nix-prefetch-git285
1 files changed, 160 insertions, 125 deletions
diff --git a/pkgs/build-support/fetchgit/nix-prefetch-git b/pkgs/build-support/fetchgit/nix-prefetch-git
index 4f9dd2ac272..486fd3acafb 100755
--- a/pkgs/build-support/fetchgit/nix-prefetch-git
+++ b/pkgs/build-support/fetchgit/nix-prefetch-git
@@ -10,51 +10,51 @@ fetchSubmodules=
 builder=
 
 if test -n "$deepClone"; then
-  deepClone=true
+    deepClone=true
 else
-  deepClone=false
+    deepClone=false
 fi
 
 if test "$leaveDotGit" != 1; then
-  leaveDotGit=
+    leaveDotGit=
 else
-  leaveDotGit=true
+    leaveDotGit=true
 fi
 
 
 argi=0
 argfun=""
 for arg; do
-  if test -z "$argfun"; then
-    case $arg in
-      --out) argfun=set_out;;
-      --url) argfun=set_url;;
-      --rev) argfun=set_rev;;
-      --hash) argfun=set_hashType;;
-      --deepClone) deepClone=true;;
-      --no-deepClone) deepClone=false;;
-      --leave-dotGit) leaveDotGit=true;;
-      --fetch-submodules) fetchSubmodules=true;;
-      --builder) builder=true;;
-      *)
-          argi=$(($argi + 1))
-          case $argi in
-              1) url=$arg;;
-              2) rev=$arg;;
-              3) expHash=$arg;;
-              *) exit 1;;
-          esac
-          ;;
-    esac
-  else
-    case $argfun in
-      set_*)
-        var=$(echo $argfun | sed 's,^set_,,')
-        eval $var=$arg
-        ;;
-    esac
-    argfun=""
-  fi
+    if test -z "$argfun"; then
+        case $arg in
+            --out) argfun=set_out;;
+            --url) argfun=set_url;;
+            --rev) argfun=set_rev;;
+            --hash) argfun=set_hashType;;
+            --deepClone) deepClone=true;;
+            --no-deepClone) deepClone=false;;
+            --leave-dotGit) leaveDotGit=true;;
+            --fetch-submodules) fetchSubmodules=true;;
+            --builder) builder=true;;
+            *)
+                argi=$(($argi + 1))
+                case $argi in
+                    1) url=$arg;;
+                    2) rev=$arg;;
+                    3) expHash=$arg;;
+                    *) exit 1;;
+                esac
+                ;;
+        esac
+    else
+        case $argfun in
+            set_*)
+                var=$(echo $argfun | sed 's,^set_,,')
+                eval $var=$arg
+                ;;
+        esac
+        argfun=""
+    fi
 done
 
 usage(){
@@ -75,19 +75,20 @@ Options:
 }
 
 if test -z "$url"; then
-  usage
+    usage
 fi
 
 
 init_remote(){
-    local url=$1;
-    git init;
-    git remote add origin $url;
+    local url=$1
+    git init
+    git remote add origin $url
+    ( [ -n "$http_proxy" ] && git config http.proxy $http_proxy ) || true
 }
 
 # Return the reference of an hash if it exists on the remote repository.
 ref_from_hash(){
-    local hash=$1;
+    local hash=$1
     git ls-remote origin | sed -n "\,$hash\t, { s,\(.*\)\t\(.*\),\2,; p; q}"
 }
 
@@ -99,12 +100,12 @@ hash_from_ref(){
 
 # Fetch everything and checkout the right sha1
 checkout_hash(){
-    local hash="$1";
-    local ref="$2";
+    local hash="$1"
+    local ref="$2"
 
     if test -z "$hash"; then
-        hash=$(hash_from_ref $ref);
-    fi;
+        hash=$(hash_from_ref $ref)
+    fi
 
     git fetch ${builder:+--progress} origin || return 1
     git checkout -b fetchgit $hash || return 1
@@ -112,28 +113,28 @@ checkout_hash(){
 
 # Fetch only a branch/tag and checkout it.
 checkout_ref(){
-    local hash="$1";
-    local ref="$2";
+    local hash="$1"
+    local ref="$2"
 
     if "$deepClone"; then
-	# The caller explicitly asked for a deep clone.  Deep clones
-	# allow "git describe" and similar tools to work.  See
-	# http://thread.gmane.org/gmane.linux.distributions.nixos/3569
-	# for a discussion.
-	return 1
+        # The caller explicitly asked for a deep clone.  Deep clones
+        # allow "git describe" and similar tools to work.  See
+        # http://thread.gmane.org/gmane.linux.distributions.nixos/3569
+        # for a discussion.
+        return 1
     fi
 
     if test -z "$ref"; then
-        ref=$(ref_from_hash $hash);
-    fi;
+        ref=$(ref_from_hash $hash)
+    fi
 
     if test -n "$ref"; then
         # --depth option is ignored on http repository.
         git fetch ${builder:+--progress} --depth 1 origin +"$ref" || return 1
         git checkout -b fetchgit FETCH_HEAD || return 1
     else
-        return 1;
-    fi;
+        return 1
+    fi
 }
 
 # Update submodules
@@ -145,20 +146,15 @@ init_submodules(){
     git submodule status |
     while read l; do
         # checkout each submodule
-        local hash=$(echo $l | awk '{print substr($1,2)}');
-        local dir=$(echo $l | awk '{print $2}');
+        local hash=$(echo $l | awk '{print substr($1,2)}')
+        local dir=$(echo $l | awk '{print $2}')
         local name=$(
-            git config -f .gitmodules --get-regexp submodule\.[^.]*\.path |
+            git config -f .gitmodules --get-regexp submodule\..*\.path |
             sed -n "s,^\(.*\)\.path $dir\$,\\1,p")
-        local url=$(git config -f .gitmodules --get ${name}.url);
-
-        # Get Absolute URL if we have a relative URL
-        if ! echo "$url" | grep '^[a-zA-Z]\+://' >/dev/null 2>&1; then
-          url="$(git config --get remote.origin.url)/$url"
-        fi
+        local url=$(git config --get ${name}.url)
 
-        clone "$dir" "$url" "$hash" "";
-    done;
+        clone "$dir" "$url" "$hash" ""
+    done
 }
 
 clone(){
@@ -168,37 +164,75 @@ clone(){
     local hash="$3"
     local ref="$4"
 
-    cd $dir;
+    cd $dir
 
     # Initialize the repository.
-    init_remote "$url";
+    init_remote "$url"
 
     # Download data from the repository.
     checkout_ref "$hash" "$ref" ||
     checkout_hash "$hash" "$ref" || (
-        echo 1>&2 "Unable to checkout $hash$ref from $url.";
-        exit 1;
+        echo 1>&2 "Unable to checkout $hash$ref from $url."
+        exit 1
     )
 
     # Checkout linked sources.
     if test -n "$fetchSubmodules"; then
-        init_submodules;
+        init_submodules
     fi
 
     if [ -z "$builder" -a -f .topdeps ]; then
-	if tg help 2>&1 > /dev/null
-	then
-	    echo "populating TopGit branches..."
-	    tg remote --populate origin
-	else
-	    echo "WARNING: would populate TopGit branches but TopGit is not available" >&2
-	    echo "WARNING: install TopGit to fix the problem" >&2
-	fi
+        if tg help 2>&1 > /dev/null
+        then
+            echo "populating TopGit branches..."
+            tg remote --populate origin
+        else
+            echo "WARNING: would populate TopGit branches but TopGit is not available" >&2
+            echo "WARNING: install TopGit to fix the problem" >&2
+        fi
     fi
 
-    cd $top;
+    cd $top
 }
 
+# Remove all remote branches, remove tags not reachable from HEAD, do a full
+# repack and then garbage collect unreferenced objects.
+make_deterministic_repo(){
+    local repo="$1"
+
+    # run in sub-shell to not touch current working directory
+    (
+    cd "$repo"
+    # Remove files that contain timestamps or otherwise have non-deterministic
+    # properties.
+    rm -rf .git/logs/ .git/hooks/ .git/index .git/FETCH_HEAD .git/ORIG_HEAD \
+        .git/refs/remotes/origin/HEAD .git/config
+
+    # Remove all remote branches.
+    git branch -r | while read branch; do
+        git branch -rD "$branch" >&2
+    done
+
+    # Remove tags not reachable from HEAD. If we're exactly on a tag, don't
+    # delete it.
+    maybe_tag=$(git tag --points-at HEAD)
+    git tag --contains HEAD | while read tag; do
+        if [ "$tag" != "$maybe_tag" ]; then
+            git tag -d "$tag" >&2
+        fi
+    done
+
+    # Do a full repack. Must run single-threaded, or else we loose determinism.
+    git config pack.threads 1
+    git repack -A -d -f
+    rm -f .git/config
+
+    # Garbage collect unreferenced objects.
+    git gc --prune=all
+    )
+}
+
+
 clone_user_rev() {
     local dir="$1"
     local url="$2"
@@ -210,79 +244,80 @@ clone_user_rev() {
             clone "$dir" "$url" "" "$rev" 1>&2;;
         *)
             if test -z "$(echo $rev | tr -d 0123456789abcdef)"; then
-                clone "$dir" "$url" "$rev" "" 1>&2;
+                clone "$dir" "$url" "$rev" "" 1>&2
             else
-                echo 1>&2 "Bad commit hash or bad reference.";
-                exit 1;
+                echo 1>&2 "Bad commit hash or bad reference."
+                exit 1
             fi;;
     esac
 
     local full_revision=$(cd $dir && (git rev-parse $rev 2> /dev/null || git rev-parse refs/heads/fetchgit) | tail -n1)
     echo "git revision is $full_revision"
     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
+    echo "Commit date is $(cd $dir && git show --no-patch --pretty=%ci $full_revision)"
 
     # Allow doing additional processing before .git removal
     eval "$NIX_PREFETCH_GIT_CHECKOUT_HOOK"
     if test -z "$leaveDotGit"; then
-	echo "removing \`.git'..." >&2
+        echo "removing \`.git'..." >&2
         find $dir -name .git\* | xargs rm -rf
     else
-        # The logs and index contain timestamps, and the hooks contain
-        # the nix path of git's bash
-        find $dir -name .git | xargs -I {} rm -rf {}/logs {}/index {}/hooks
+        find $dir -name .git | while read gitdir; do
+            make_deterministic_repo "$(readlink -f "$gitdir/..")"
+        done
     fi
 }
 
 if test -n "$builder"; then
-  test -n "$out" -a -n "$url" -a -n "$rev" || usage
-  mkdir $out
-  clone_user_rev "$out" "$url" "$rev"
+    test -n "$out" -a -n "$url" -a -n "$rev" || usage
+    mkdir $out
+    clone_user_rev "$out" "$url" "$rev"
 else
-  if test -z "$hashType"; then
-      hashType=sha256
-  fi
+    if test -z "$hashType"; then
+        hashType=sha256
+    fi
 
-  # If the hash was given, a file with that hash may already be in the
-  # store.
-  if test -n "$expHash"; then
-      finalPath=$(nix-store --print-fixed-path --recursive "$hashType" "$expHash" git-export)
-      if ! nix-store --check-validity "$finalPath" 2> /dev/null; then
-          finalPath=
-      fi
-      hash=$expHash
-  fi
+    # If the hash was given, a file with that hash may already be in the
+    # store.
+    if test -n "$expHash"; then
+        finalPath=$(nix-store --print-fixed-path --recursive "$hashType" "$expHash" git-export)
+        if ! nix-store --check-validity "$finalPath" 2> /dev/null; then
+            finalPath=
+        fi
+        hash=$expHash
+    fi
 
-  # If we don't know the hash or a path with that hash doesn't exist,
-  # download the file and add it to the store.
-  if test -z "$finalPath"; then
+    # If we don't know the hash or a path with that hash doesn't exist,
+    # download the file and add it to the store.
+    if test -z "$finalPath"; then
 
-      tmpPath="$(mktemp -d "${TMPDIR:-/tmp}/git-checkout-tmp-XXXXXXXX")"
-      trap "rm -rf \"$tmpPath\"" EXIT
+        tmpPath="$(mktemp -d "${TMPDIR:-/tmp}/git-checkout-tmp-XXXXXXXX")"
+        trap "rm -rf \"$tmpPath\"" EXIT
 
-      tmpFile="$tmpPath/git-export"
-      mkdir "$tmpFile"
+        tmpFile="$tmpPath/git-export"
+        mkdir "$tmpFile"
 
-      # Perform the checkout.
-      clone_user_rev "$tmpFile" "$url" "$rev"
+        # Perform the checkout.
+        clone_user_rev "$tmpFile" "$url" "$rev"
 
-      # Compute the hash.
-      hash=$(nix-hash --type $hashType $hashFormat $tmpFile)
-      if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi
+        # Compute the hash.
+        hash=$(nix-hash --type $hashType $hashFormat $tmpFile)
+        if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi
 
-      # Add the downloaded file to the Nix store.
-      finalPath=$(nix-store --add-fixed --recursive "$hashType" $tmpFile)
+        # Add the downloaded file to the Nix store.
+        finalPath=$(nix-store --add-fixed --recursive "$hashType" $tmpFile)
 
-      if test -n "$expHash" -a "$expHash" != "$hash"; then
-          echo "hash mismatch for URL \`$url'"
-          exit 1
-      fi
-  fi
+        if test -n "$expHash" -a "$expHash" != "$hash"; then
+            echo "hash mismatch for URL \`$url'"
+            exit 1
+        fi
+    fi
 
-  if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi
+    if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi
 
-  echo $hash
+    echo $hash
 
-  if test -n "$PRINT_PATH"; then
-      echo $finalPath
-  fi
+    if test -n "$PRINT_PATH"; then
+        echo $finalPath
+    fi
 fi