about summary refs log tree commit diff
path: root/pkgs/build-support/fetchgit/nix-prefetch-git
blob: 198ab5c65fedf3431092965e62143ab8900b4fff (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#! /bin/sh -e

url=$1
rev=$2
expHash=$3

hashType=$NIX_HASH_ALGO
if test -z "$hashType"; then
    hashType=sha256
fi

if test -z "$url"; then
    echo "syntax: nix-prefetch-git URL [REVISION [EXPECTED-HASH]]" >&2
    exit 1
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

init_remote(){
    local url=$1;
    git init;
    git remote add origin $url;
}

# Return the reference of an hash if it exists on the remote repository.
ref_from_hash(){
    local hash=$1;
    git ls-remote origin | sed -n "\,$hash\t, { s,\(.*\)\t\(.*\),\2,; p; q}"
}

# Return the hash of a reference if it exists on the remote repository.
hash_from_ref(){
    local ref=$1
    git ls-remote origin | sed -n "\,\t$ref, { s,\(.*\)\t\(.*\),\1,; p; q}"
}

# Fetch everything and checkout the right sha1
checkout_hash(){
    local hash="$1";
    local ref="$2";

    if test -z "$hash"; then
        hash=$(hash_from_ref $ref);
    fi;

    git fetch origin || return 1
    git checkout $hash || return 1
}

# Fetch only a branch/tag and checkout it.
checkout_ref(){
    local hash="$1";
    local ref="$2";

    if test -n "$NIX_PREFETCH_GIT_DEEP_CLONE"; 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
    fi

    if test -z "$ref"; then
        ref=$(ref_from_hash $hash);
    fi;

    if test -n "$ref"; then
        # --depth option is ignored on http repository.
        git fetch --depth 1 origin +"$ref" || return 1
        git checkout FETCH_HEAD || return 1
    else
        return 1;
    fi;
}

# Update submodules
init_submodules(){
    # Add urls into .git/config file
    git submodule init

    # list submodule directories and their hashes
    git submodule status |
    while read l; do
        # checkout each submodule
        local hash=$(echo $l | sed 's,^-\([0-9a-f]*\) \(.*\)$,\1,');
        local dir=$(echo $l | sed 's,^-\([0-9a-f]*\) \(.*\)$,\2,');
        local url=$(sed -n "\,$dir, { :loop; n; s,^.*url = ,,; T loop; p; q }" .git/config);

        clone "$dir" "$url" "$hash" "";
    done;
}

clone(){
    local top=$(pwd)
    local dir="$1"
    local url="$2"
    local hash="$3"
    local ref="$4"

    cd $dir;

    # Initialize the repository.
    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;
    )

    # Checkout linked sources.
    init_submodules;

    if [ -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
    fi

    cd $top;
}

# 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=/tmp/git-checkout-tmp-$$
    tmpFile=$tmpPath/git-export
    mkdir $tmpPath $tmpFile

    trap "rm -rf $tmpPath" EXIT

    # Perform the checkout.
    case "$rev" in
        HEAD|refs/*)
            clone "$tmpFile" "$url" "" "$rev" 1>&2;;
        [0-9a-f]*)
            if test -z "$(echo $rev | tr -d 0123456789abcdef)"; then
                clone "$tmpFile" "$url" "$rev" "" 1>&2;
            else
                echo 1>&2 "Bad commit hash or bad reference.";
                exit 1;
            fi;;
        "")
            clone "$tmpFile" "$url" "" "HEAD" 1>&2;;
    esac

    # Allow doing additional processing before .git removal
    eval "$NIX_PREFETCH_GIT_CHECKOUT_HOOK"
    if test "$NIX_PREFETCH_GIT_LEAVE_DOT_GIT" != 1
    then
	echo "removing \`.git'..." >&2
	rm -rf $tmpFile/.git
    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)

    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

echo $hash

if test -n "$PRINT_PATH"; then
    echo $finalPath
fi