| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='git ls-remote' |
| 4 | |
| 5 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 6 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 7 | |
| 8 | . ./test-lib.sh |
| 9 | |
| 10 | generate_references () { |
| 11 | for ref |
| 12 | do |
| 13 | oid=$(git rev-parse "$ref") && |
| 14 | printf '%s\t%s\n' "$oid" "$ref" || return 1 |
| 15 | done |
| 16 | } |
| 17 | |
| 18 | test_expect_success 'set up fake upload-pack' ' |
| 19 | # This can be used to simulate an upload-pack that just shows the |
| 20 | # contents of the "input" file (prepared with the test-tool pkt-line |
| 21 | # helper), and does not do any negotiation (since ls-remote does not |
| 22 | # need it). |
| 23 | write_script cat-input <<-\EOF |
| 24 | # send our initial advertisement/response |
| 25 | cat input |
| 26 | # soak up the flush packet from the client |
| 27 | cat |
| 28 | EOF |
| 29 | ' |
| 30 | |
| 31 | test_expect_success 'dies when no remote found' ' |
| 32 | test_must_fail git ls-remote |
| 33 | ' |
| 34 | |
| 35 | test_expect_success setup ' |
| 36 | >file && |
| 37 | git add file && |
| 38 | test_tick && |
| 39 | git commit -m initial && |
| 40 | git tag mark && |
| 41 | git tag mark1.1 && |
| 42 | git tag mark1.2 && |
| 43 | git tag mark1.10 && |
| 44 | git show-ref --tags -d >expected.tag.raw && |
| 45 | sed -e "s/ / /" expected.tag.raw >expected.tag && |
| 46 | generate_references HEAD >expected.all && |
| 47 | git show-ref -d >refs && |
| 48 | sed -e "s/ / /" refs >>expected.all && |
| 49 | |
| 50 | grep refs/heads/ expected.all >expected.branches && |
| 51 | git remote add self "$(pwd)/.git" && |
| 52 | git remote add self2 "." |
| 53 | ' |
| 54 | |
| 55 | test_expect_success 'ls-remote --tags .git' ' |
| 56 | git ls-remote --tags .git >actual && |
| 57 | test_cmp expected.tag actual |
| 58 | ' |
| 59 | |
| 60 | test_expect_success 'ls-remote .git' ' |
| 61 | git ls-remote .git >actual && |
| 62 | test_cmp expected.all actual |
| 63 | ' |
| 64 | |
| 65 | test_expect_success 'ls-remote --tags self' ' |
| 66 | git ls-remote --tags self >actual && |
| 67 | test_cmp expected.tag actual |
| 68 | ' |
| 69 | |
| 70 | test_expect_success 'ls-remote self' ' |
| 71 | git ls-remote self >actual && |
| 72 | test_cmp expected.all actual |
| 73 | ' |
| 74 | |
| 75 | test_expect_success 'ls-remote --branches self' ' |
| 76 | git ls-remote --branches self >actual && |
| 77 | test_cmp expected.branches actual && |
| 78 | git ls-remote -b self >actual && |
| 79 | test_cmp expected.branches actual |
| 80 | ' |
| 81 | |
| 82 | test_expect_success 'ls-remote -h is deprecated w/o warning' ' |
| 83 | git ls-remote -h self >actual 2>warning && |
| 84 | test_cmp expected.branches actual && |
| 85 | test_grep ! deprecated warning |
| 86 | ' |
| 87 | |
| 88 | test_expect_success 'ls-remote --heads is deprecated and hidden w/o warning' ' |
| 89 | git ls-remote -h >short-help && |
| 90 | test_grep ! -e --head short-help && |
| 91 | git ls-remote --heads self >actual 2>warning && |
| 92 | test_cmp expected.branches actual && |
| 93 | test_grep ! deprecated warning |
| 94 | ' |
| 95 | |
| 96 | test_expect_success 'ls-remote --sort="version:refname" --tags self' ' |
| 97 | generate_references \ |
| 98 | refs/tags/mark \ |
| 99 | refs/tags/mark1.1 \ |
| 100 | refs/tags/mark1.2 \ |
| 101 | refs/tags/mark1.10 >expect && |
| 102 | git ls-remote --sort="version:refname" --tags self >actual && |
| 103 | test_cmp expect actual |
| 104 | ' |
| 105 | |
| 106 | test_expect_success 'ls-remote --sort="-version:refname" --tags self' ' |
| 107 | generate_references \ |
| 108 | refs/tags/mark1.10 \ |
| 109 | refs/tags/mark1.2 \ |
| 110 | refs/tags/mark1.1 \ |
| 111 | refs/tags/mark >expect && |
| 112 | git ls-remote --sort="-version:refname" --tags self >actual && |
| 113 | test_cmp expect actual |
| 114 | ' |
| 115 | |
| 116 | test_expect_success 'ls-remote --sort="-refname" --tags self' ' |
| 117 | generate_references \ |
| 118 | refs/tags/mark1.2 \ |
| 119 | refs/tags/mark1.10 \ |
| 120 | refs/tags/mark1.1 \ |
| 121 | refs/tags/mark >expect && |
| 122 | git ls-remote --sort="-refname" --tags self >actual && |
| 123 | test_cmp expect actual |
| 124 | ' |
| 125 | |
| 126 | test_expect_success 'dies when no remote specified, multiple remotes found, and no default specified' ' |
| 127 | test_must_fail git ls-remote |
| 128 | ' |
| 129 | |
| 130 | test_expect_success 'succeeds when no remote specified but only one found' ' |
| 131 | test_when_finished git remote add self2 "." && |
| 132 | git remote remove self2 && |
| 133 | git ls-remote |
| 134 | ' |
| 135 | |
| 136 | test_expect_success 'use "origin" when no remote specified and multiple found' ' |
| 137 | URL="$(pwd)/.git" && |
| 138 | echo "From $URL" >exp_err && |
| 139 | |
| 140 | git remote add origin "$URL" && |
| 141 | git ls-remote 2>actual_err >actual && |
| 142 | |
| 143 | test_cmp exp_err actual_err && |
| 144 | test_cmp expected.all actual |
| 145 | ' |
| 146 | |
| 147 | test_expect_success 'suppress "From <url>" with -q' ' |
| 148 | git ls-remote -q 2>actual_err && |
| 149 | ! test_cmp exp_err actual_err |
| 150 | ' |
| 151 | |
| 152 | test_expect_success 'use branch.<name>.remote if possible' ' |
| 153 | # |
| 154 | # Test that we are indeed using branch.<name>.remote, not "origin", even |
| 155 | # though the "origin" remote has been set. |
| 156 | # |
| 157 | |
| 158 | # setup a new remote to differentiate from "origin" |
| 159 | git clone . other.git && |
| 160 | ( |
| 161 | cd other.git && |
| 162 | echo "$(git rev-parse HEAD) HEAD" && |
| 163 | git show-ref | sed -e "s/ / /" |
| 164 | ) >exp && |
| 165 | |
| 166 | URL="other.git" && |
| 167 | echo "From $URL" >exp_err && |
| 168 | |
| 169 | git remote add other $URL && |
| 170 | git config branch.main.remote other && |
| 171 | |
| 172 | git ls-remote 2>actual_err >actual && |
| 173 | test_cmp exp_err actual_err && |
| 174 | test_cmp exp actual |
| 175 | ' |
| 176 | |
| 177 | test_expect_success 'confuses pattern as remote when no remote specified' ' |
| 178 | if test_have_prereq MINGW |
| 179 | then |
| 180 | # Windows does not like asterisks in pathname |
| 181 | does_not_exist=main |
| 182 | else |
| 183 | does_not_exist="refs*main" |
| 184 | fi && |
| 185 | cat >exp <<-EOF && |
| 186 | fatal: '\''$does_not_exist'\'' does not appear to be a git repository |
| 187 | fatal: Could not read from remote repository. |
| 188 | |
| 189 | Please make sure you have the correct access rights |
| 190 | and the repository exists. |
| 191 | EOF |
| 192 | # |
| 193 | # Do not expect "git ls-remote <pattern>" to work; ls-remote needs |
| 194 | # <remote> if you want to feed <pattern>, just like you cannot say |
| 195 | # fetch <branch>. |
| 196 | # We could just as easily have used "main"; the "*" emphasizes its |
| 197 | # role as a pattern. |
| 198 | test_must_fail git ls-remote "$does_not_exist" >actual 2>&1 && |
| 199 | test_cmp exp actual |
| 200 | ' |
| 201 | |
| 202 | test_expect_success 'die with non-2 for wrong repository even with --exit-code' ' |
| 203 | { |
| 204 | git ls-remote --exit-code ./no-such-repository |
| 205 | status=$? |
| 206 | } && |
| 207 | test $status != 2 && test $status != 0 |
| 208 | ' |
| 209 | |
| 210 | test_expect_success 'Report success even when nothing matches' ' |
| 211 | git ls-remote other.git "refs/nsn/*" >actual && |
| 212 | test_must_be_empty actual |
| 213 | ' |
| 214 | |
| 215 | test_expect_success 'Report no-match with --exit-code' ' |
| 216 | test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual && |
| 217 | test_must_be_empty actual |
| 218 | ' |
| 219 | |
| 220 | test_expect_success 'Report match with --exit-code' ' |
| 221 | git ls-remote --exit-code other.git "refs/tags/*" >actual && |
| 222 | git ls-remote . tags/mark* >expect && |
| 223 | test_cmp expect actual |
| 224 | ' |
| 225 | |
| 226 | test_expect_success 'set up some extra tags for ref hiding' ' |
| 227 | git tag magic/one && |
| 228 | git tag magic/two |
| 229 | ' |
| 230 | |
| 231 | for configsection in transfer uploadpack |
| 232 | do |
| 233 | test_expect_success "Hide some refs with $configsection.hiderefs" ' |
| 234 | test_config $configsection.hiderefs refs/tags && |
| 235 | git ls-remote . >actual && |
| 236 | test_unconfig $configsection.hiderefs && |
| 237 | git ls-remote . >expect.raw && |
| 238 | sed -e "/ refs\/tags\//d" expect.raw >expect && |
| 239 | test_cmp expect actual |
| 240 | ' |
| 241 | |
| 242 | test_expect_success "Override hiding of $configsection.hiderefs" ' |
| 243 | test_when_finished "test_unconfig $configsection.hiderefs" && |
| 244 | git config --add $configsection.hiderefs refs/tags && |
| 245 | git config --add $configsection.hiderefs "!refs/tags/magic" && |
| 246 | git config --add $configsection.hiderefs refs/tags/magic/one && |
| 247 | git ls-remote . >actual && |
| 248 | test_grep refs/tags/magic/two actual && |
| 249 | test_grep ! refs/tags/magic/one actual |
| 250 | ' |
| 251 | |
| 252 | done |
| 253 | |
| 254 | test_expect_success 'overrides work between mixed transfer/upload-pack hideRefs' ' |
| 255 | test_config uploadpack.hiderefs refs/tags && |
| 256 | test_config transfer.hiderefs "!refs/tags/magic" && |
| 257 | git ls-remote . >actual && |
| 258 | test_grep refs/tags/magic actual |
| 259 | ' |
| 260 | |
| 261 | test_expect_success 'protocol v2 supports hiderefs' ' |
| 262 | test_config uploadpack.hiderefs refs/tags && |
| 263 | git -c protocol.version=2 ls-remote . >actual && |
| 264 | test_grep ! refs/tags actual |
| 265 | ' |
| 266 | |
| 267 | test_expect_success 'ls-remote --symref' ' |
| 268 | git fetch origin && |
| 269 | echo "ref: refs/heads/main HEAD" >expect.v2 && |
| 270 | generate_references \ |
| 271 | HEAD \ |
| 272 | refs/heads/main >>expect.v2 && |
| 273 | echo "ref: refs/remotes/origin/main refs/remotes/origin/HEAD" >>expect.v2 && |
| 274 | oid=$(git rev-parse HEAD) && |
| 275 | echo "$oid refs/remotes/origin/HEAD" >>expect.v2 && |
| 276 | generate_references \ |
| 277 | refs/remotes/origin/main \ |
| 278 | refs/tags/mark \ |
| 279 | refs/tags/mark1.1 \ |
| 280 | refs/tags/mark1.10 \ |
| 281 | refs/tags/mark1.2 >>expect.v2 && |
| 282 | # v0 does not show non-HEAD symrefs |
| 283 | grep -v "ref: refs/remotes" <expect.v2 >expect.v0 && |
| 284 | git -c protocol.version=0 ls-remote --symref >actual.v0 && |
| 285 | test_cmp expect.v0 actual.v0 && |
| 286 | git -c protocol.version=2 ls-remote --symref >actual.v2 && |
| 287 | test_cmp expect.v2 actual.v2 |
| 288 | ' |
| 289 | |
| 290 | test_expect_success 'ls-remote with filtered symref (refname)' ' |
| 291 | rev=$(git rev-parse HEAD) && |
| 292 | cat >expect <<-EOF && |
| 293 | ref: refs/heads/main HEAD |
| 294 | $rev HEAD |
| 295 | ref: refs/remotes/origin/main refs/remotes/origin/HEAD |
| 296 | $rev refs/remotes/origin/HEAD |
| 297 | EOF |
| 298 | git ls-remote --symref . HEAD >actual && |
| 299 | test_cmp expect actual |
| 300 | ' |
| 301 | |
| 302 | test_expect_success 'ls-remote with filtered symref (--branches)' ' |
| 303 | git symbolic-ref refs/heads/foo refs/tags/mark && |
| 304 | cat >expect.v2 <<-EOF && |
| 305 | ref: refs/tags/mark refs/heads/foo |
| 306 | $rev refs/heads/foo |
| 307 | $rev refs/heads/main |
| 308 | EOF |
| 309 | grep -v "^ref: refs/tags/" <expect.v2 >expect.v0 && |
| 310 | git -c protocol.version=0 ls-remote --symref --branches . >actual.v0 && |
| 311 | test_cmp expect.v0 actual.v0 && |
| 312 | git -c protocol.version=2 ls-remote --symref --branches . >actual.v2 && |
| 313 | test_cmp expect.v2 actual.v2 |
| 314 | ' |
| 315 | |
| 316 | test_expect_success 'indicate no refs in v0 standards-compliant empty remote' ' |
| 317 | # Git does not produce an output like this, but it does match the |
| 318 | # standard and is produced by other implementations like JGit. So |
| 319 | # hard-code the case we care about. |
| 320 | # |
| 321 | # The actual capabilities do not matter; there are none that would |
| 322 | # change how ls-remote behaves. |
| 323 | oid=0000000000000000000000000000000000000000 && |
| 324 | test-tool pkt-line pack >input.q <<-EOF && |
| 325 | $oid capabilities^{}Qcaps-go-here |
| 326 | 0000 |
| 327 | EOF |
| 328 | q_to_nul <input.q >input && |
| 329 | |
| 330 | # --exit-code asks the command to exit with 2 when no |
| 331 | # matching refs are found. |
| 332 | test_expect_code 2 git ls-remote --exit-code --upload-pack=./cat-input . |
| 333 | ' |
| 334 | |
| 335 | test_expect_success 'ls-remote works outside repository' ' |
| 336 | # It is important for this repo to be inside the nongit |
| 337 | # area, as we want a repo name that does not include |
| 338 | # slashes (because those inhibit some of our configuration |
| 339 | # lookups). |
| 340 | nongit git init --bare dst.git && |
| 341 | nongit git ls-remote dst.git |
| 342 | ' |
| 343 | |
| 344 | test_expect_success 'ls-remote --sort fails gracefully outside repository' ' |
| 345 | # Use a sort key that requires access to the referenced objects. |
| 346 | nongit test_must_fail git ls-remote --sort=authordate "$TRASH_DIRECTORY" 2>err && |
| 347 | test_grep "^fatal: not a git repository, but the field '\''authordate'\'' requires access to object data" err |
| 348 | ' |
| 349 | |
| 350 | test_expect_success 'ls-remote patterns work with all protocol versions' ' |
| 351 | git for-each-ref --format="%(objectname) %(refname)" \ |
| 352 | refs/heads/main refs/remotes/origin/main >expect && |
| 353 | git -c protocol.version=0 ls-remote . main >actual.v0 && |
| 354 | test_cmp expect actual.v0 && |
| 355 | git -c protocol.version=2 ls-remote . main >actual.v2 && |
| 356 | test_cmp expect actual.v2 |
| 357 | ' |
| 358 | |
| 359 | test_expect_success 'ls-remote prefixes work with all protocol versions' ' |
| 360 | git for-each-ref --format="%(objectname) %(refname)" \ |
| 361 | refs/heads/ refs/tags/ >expect && |
| 362 | git -c protocol.version=0 ls-remote --branches --tags . >actual.v0 && |
| 363 | test_cmp expect actual.v0 && |
| 364 | git -c protocol.version=2 ls-remote --branches --tags . >actual.v2 && |
| 365 | test_cmp expect actual.v2 |
| 366 | ' |
| 367 | |
| 368 | test_expect_success 'v0 clients can handle multiple symrefs' ' |
| 369 | # Modern versions of Git will not return multiple symref capabilities |
| 370 | # for v0, so we have to hard-code the response. Note that we will |
| 371 | # always use both v0 and object-format=sha1 here, as the hard-coded |
| 372 | # response reflects a server that only supports those. |
| 373 | oid=1234567890123456789012345678901234567890 && |
| 374 | symrefs="symref=refs/remotes/origin/HEAD:refs/remotes/origin/main" && |
| 375 | symrefs="$symrefs symref=HEAD:refs/heads/main" && |
| 376 | |
| 377 | # Likewise we want to make sure our parser is not fooled by the string |
| 378 | # "symref" appearing as part of an earlier cap. But there is no way to |
| 379 | # do that via upload-pack, as arbitrary strings can appear only in a |
| 380 | # "symref" value itself (where we skip past the values as a whole) |
| 381 | # and "agent" (which always appears after "symref", so putting our |
| 382 | # parser in a confused state is less interesting). |
| 383 | caps="some other caps including a-fake-symref-cap" && |
| 384 | |
| 385 | test-tool pkt-line pack >input.q <<-EOF && |
| 386 | $oid HEADQ$caps $symrefs |
| 387 | $oid refs/heads/main |
| 388 | $oid refs/remotes/origin/HEAD |
| 389 | $oid refs/remotes/origin/main |
| 390 | 0000 |
| 391 | EOF |
| 392 | q_to_nul <input.q >input && |
| 393 | |
| 394 | cat >expect <<-EOF && |
| 395 | ref: refs/heads/main HEAD |
| 396 | $oid HEAD |
| 397 | $oid refs/heads/main |
| 398 | ref: refs/remotes/origin/main refs/remotes/origin/HEAD |
| 399 | $oid refs/remotes/origin/HEAD |
| 400 | $oid refs/remotes/origin/main |
| 401 | EOF |
| 402 | |
| 403 | git ls-remote --symref --upload-pack=./cat-input . >actual && |
| 404 | test_cmp expect actual |
| 405 | ' |
| 406 | |
| 407 | test_expect_success 'helper with refspec capability fails gracefully' ' |
| 408 | mkdir test-bin && |
| 409 | write_script test-bin/git-remote-foo <<-EOF && |
| 410 | read capabilities |
| 411 | echo import |
| 412 | echo refspec ${SQ}*:*${SQ} |
| 413 | EOF |
| 414 | ( |
| 415 | PATH="$PWD/test-bin:$PATH" && |
| 416 | export PATH && |
| 417 | test_must_fail nongit git ls-remote foo::bar |
| 418 | ) |
| 419 | ' |
| 420 | |
| 421 | test_done |