| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='pack-objects --stdin' |
| 4 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 5 | export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| 6 | |
| 7 | . ./test-lib.sh |
| 8 | |
| 9 | packed_objects () { |
| 10 | git show-index <"$1" >tmp-object-list && |
| 11 | cut -d' ' -f2 tmp-object-list | sort && |
| 12 | rm tmp-object-list |
| 13 | } |
| 14 | |
| 15 | test_expect_success 'setup for --stdin-packs tests' ' |
| 16 | git init stdin-packs && |
| 17 | git -C stdin-packs config set maintenance.auto false && |
| 18 | ( |
| 19 | cd stdin-packs && |
| 20 | |
| 21 | test_commit A && |
| 22 | test_commit B && |
| 23 | test_commit C && |
| 24 | |
| 25 | for id in A B C |
| 26 | do |
| 27 | git pack-objects .git/objects/pack/pack-$id \ |
| 28 | --incremental --revs <<-EOF || exit 1 |
| 29 | refs/tags/$id |
| 30 | EOF |
| 31 | done && |
| 32 | |
| 33 | ls -la .git/objects/pack |
| 34 | ) |
| 35 | ' |
| 36 | |
| 37 | test_expect_success '--stdin-packs with excluded packs' ' |
| 38 | ( |
| 39 | cd stdin-packs && |
| 40 | |
| 41 | PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && |
| 42 | PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && |
| 43 | PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && |
| 44 | |
| 45 | git pack-objects test --stdin-packs <<-EOF && |
| 46 | $PACK_A |
| 47 | ^$PACK_B |
| 48 | $PACK_C |
| 49 | EOF |
| 50 | |
| 51 | ( |
| 52 | git show-index <$(ls .git/objects/pack/pack-A-*.idx) && |
| 53 | git show-index <$(ls .git/objects/pack/pack-C-*.idx) |
| 54 | ) >expect.raw && |
| 55 | git show-index <$(ls test-*.idx) >actual.raw && |
| 56 | |
| 57 | cut -d" " -f2 <expect.raw | sort >expect && |
| 58 | cut -d" " -f2 <actual.raw | sort >actual && |
| 59 | test_cmp expect actual |
| 60 | ) |
| 61 | ' |
| 62 | |
| 63 | test_expect_success '--stdin-packs is incompatible with --filter' ' |
| 64 | ( |
| 65 | cd stdin-packs && |
| 66 | test_must_fail git pack-objects --stdin-packs --stdout \ |
| 67 | --filter=blob:none </dev/null 2>err && |
| 68 | test_grep "options .--stdin-packs. and .--filter. cannot be used together" err |
| 69 | ) |
| 70 | ' |
| 71 | |
| 72 | test_expect_success '--stdin-packs is incompatible with --revs' ' |
| 73 | ( |
| 74 | cd stdin-packs && |
| 75 | test_must_fail git pack-objects --stdin-packs --revs out \ |
| 76 | </dev/null 2>err && |
| 77 | test_grep "cannot use internal rev list with --stdin-packs" err |
| 78 | ) |
| 79 | ' |
| 80 | |
| 81 | test_expect_success '--stdin-packs with loose objects' ' |
| 82 | ( |
| 83 | cd stdin-packs && |
| 84 | |
| 85 | PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && |
| 86 | PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && |
| 87 | PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && |
| 88 | |
| 89 | test_commit D && # loose |
| 90 | |
| 91 | git pack-objects test2 --stdin-packs --unpacked <<-EOF && |
| 92 | $PACK_A |
| 93 | ^$PACK_B |
| 94 | $PACK_C |
| 95 | EOF |
| 96 | |
| 97 | ( |
| 98 | git show-index <$(ls .git/objects/pack/pack-A-*.idx) && |
| 99 | git show-index <$(ls .git/objects/pack/pack-C-*.idx) && |
| 100 | git rev-list --objects --no-object-names \ |
| 101 | refs/tags/C..refs/tags/D |
| 102 | |
| 103 | ) >expect.raw && |
| 104 | ls -la . && |
| 105 | git show-index <$(ls test2-*.idx) >actual.raw && |
| 106 | |
| 107 | cut -d" " -f2 <expect.raw | sort >expect && |
| 108 | cut -d" " -f2 <actual.raw | sort >actual && |
| 109 | test_cmp expect actual |
| 110 | ) |
| 111 | ' |
| 112 | |
| 113 | test_expect_success '--stdin-packs with broken links' ' |
| 114 | ( |
| 115 | cd stdin-packs && |
| 116 | |
| 117 | # make an unreachable object with a bogus parent |
| 118 | git cat-file -p HEAD >commit && |
| 119 | sed "s/$(git rev-parse HEAD^)/$(test_oid zero)/" <commit | |
| 120 | git hash-object -w -t commit --stdin >in && |
| 121 | |
| 122 | git pack-objects .git/objects/pack/pack-D <in && |
| 123 | |
| 124 | PACK_A="$(basename .git/objects/pack/pack-A-*.pack)" && |
| 125 | PACK_B="$(basename .git/objects/pack/pack-B-*.pack)" && |
| 126 | PACK_C="$(basename .git/objects/pack/pack-C-*.pack)" && |
| 127 | PACK_D="$(basename .git/objects/pack/pack-D-*.pack)" && |
| 128 | |
| 129 | git pack-objects test3 --stdin-packs --unpacked <<-EOF && |
| 130 | $PACK_A |
| 131 | ^$PACK_B |
| 132 | $PACK_C |
| 133 | $PACK_D |
| 134 | EOF |
| 135 | |
| 136 | ( |
| 137 | git show-index <$(ls .git/objects/pack/pack-A-*.idx) && |
| 138 | git show-index <$(ls .git/objects/pack/pack-C-*.idx) && |
| 139 | git show-index <$(ls .git/objects/pack/pack-D-*.idx) && |
| 140 | git rev-list --objects --no-object-names \ |
| 141 | refs/tags/C..refs/tags/D |
| 142 | ) >expect.raw && |
| 143 | git show-index <$(ls test3-*.idx) >actual.raw && |
| 144 | |
| 145 | cut -d" " -f2 <expect.raw | sort >expect && |
| 146 | cut -d" " -f2 <actual.raw | sort >actual && |
| 147 | test_cmp expect actual |
| 148 | ) |
| 149 | ' |
| 150 | |
| 151 | test_expect_success 'pack-objects --stdin with duplicate packfile' ' |
| 152 | test_when_finished "rm -fr repo" && |
| 153 | |
| 154 | git init repo && |
| 155 | ( |
| 156 | cd repo && |
| 157 | test_commit "commit" && |
| 158 | git repack -ad && |
| 159 | |
| 160 | { |
| 161 | basename .git/objects/pack/pack-*.pack && |
| 162 | basename .git/objects/pack/pack-*.pack |
| 163 | } >packfiles && |
| 164 | |
| 165 | git pack-objects --stdin-packs generated-pack <packfiles && |
| 166 | packed_objects .git/objects/pack/pack-*.idx >expect && |
| 167 | packed_objects generated-pack-*.idx >actual && |
| 168 | test_cmp expect actual |
| 169 | ) |
| 170 | ' |
| 171 | |
| 172 | test_expect_success 'pack-objects --stdin with same packfile excluded and included' ' |
| 173 | test_when_finished "rm -fr repo" && |
| 174 | |
| 175 | git init repo && |
| 176 | ( |
| 177 | cd repo && |
| 178 | test_commit "commit" && |
| 179 | git repack -ad && |
| 180 | |
| 181 | { |
| 182 | basename .git/objects/pack/pack-*.pack && |
| 183 | printf "^%s\n" "$(basename .git/objects/pack/pack-*.pack)" |
| 184 | } >packfiles && |
| 185 | |
| 186 | git pack-objects --stdin-packs generated-pack <packfiles && |
| 187 | packed_objects generated-pack-*.idx >packed-objects && |
| 188 | test_must_be_empty packed-objects |
| 189 | ) |
| 190 | ' |
| 191 | |
| 192 | test_expect_success 'pack-objects --stdin with packfiles from alternate object database' ' |
| 193 | test_when_finished "rm -fr shared member" && |
| 194 | |
| 195 | # Set up a shared repository with a single packfile. |
| 196 | git init shared && |
| 197 | test_commit -C shared "shared-objects" && |
| 198 | git -C shared repack -ad && |
| 199 | basename shared/.git/objects/pack/pack-*.pack >packfile && |
| 200 | |
| 201 | # Set up a repository that is connected to the shared repository. This |
| 202 | # repository has no objects on its own, but we still expect to be able |
| 203 | # to pack objects from its alternate. |
| 204 | git clone --shared shared member && |
| 205 | git -C member pack-objects --stdin-packs generated-pack <packfile && |
| 206 | test_cmp shared/.git/objects/pack/pack-*.pack member/generated-pack-*.pack |
| 207 | ' |
| 208 | |
| 209 | test_expect_success 'pack-objects --stdin with packfiles from main and alternate object database' ' |
| 210 | test_when_finished "rm -fr shared member" && |
| 211 | |
| 212 | # Set up a shared repository with a single packfile. |
| 213 | git init shared && |
| 214 | test_commit -C shared "shared-commit" && |
| 215 | git -C shared repack -ad && |
| 216 | |
| 217 | # Set up a repository that is connected to the shared repository. This |
| 218 | # repository has a second packfile so that we can verify that it is |
| 219 | # possible to write packs that include packfiles from different object |
| 220 | # databases. |
| 221 | git clone --shared shared member && |
| 222 | test_commit -C member "local-commit" && |
| 223 | git -C member repack -dl && |
| 224 | |
| 225 | { |
| 226 | basename shared/.git/objects/pack/pack-*.pack && |
| 227 | basename member/.git/objects/pack/pack-*.pack |
| 228 | } >packfiles && |
| 229 | |
| 230 | { |
| 231 | packed_objects shared/.git/objects/pack/pack-*.idx && |
| 232 | packed_objects member/.git/objects/pack/pack-*.idx |
| 233 | } | sort >expected-objects && |
| 234 | |
| 235 | git -C member pack-objects --stdin-packs generated-pack <packfiles && |
| 236 | packed_objects member/generated-pack-*.idx >actual-objects && |
| 237 | test_cmp expected-objects actual-objects |
| 238 | ' |
| 239 | |
| 240 | objdir=.git/objects |
| 241 | packdir=$objdir/pack |
| 242 | |
| 243 | objects_in_packs () { |
| 244 | for p in "$@" |
| 245 | do |
| 246 | git show-index <"$packdir/pack-$p.idx" || return 1 |
| 247 | done >objects.raw && |
| 248 | |
| 249 | cut -d' ' -f2 objects.raw | sort && |
| 250 | rm -f objects.raw |
| 251 | } |
| 252 | |
| 253 | test_expect_success '--stdin-packs=follow walks into unknown packs' ' |
| 254 | test_when_finished "rm -fr repo" && |
| 255 | |
| 256 | git init repo && |
| 257 | ( |
| 258 | cd repo && |
| 259 | git config set maintenance.auto false && |
| 260 | |
| 261 | for c in A B C D |
| 262 | do |
| 263 | test_commit "$c" || return 1 |
| 264 | done && |
| 265 | |
| 266 | A="$(echo A | git pack-objects --revs $packdir/pack)" && |
| 267 | B="$(echo A..B | git pack-objects --revs $packdir/pack)" && |
| 268 | C="$(echo B..C | git pack-objects --revs $packdir/pack)" && |
| 269 | D="$(echo C..D | git pack-objects --revs $packdir/pack)" && |
| 270 | test_commit E && |
| 271 | |
| 272 | git prune-packed && |
| 273 | |
| 274 | cat >in <<-EOF && |
| 275 | pack-$B.pack |
| 276 | ^pack-$C.pack |
| 277 | pack-$D.pack |
| 278 | EOF |
| 279 | |
| 280 | # With just --stdin-packs, pack "A" is unknown to us, so |
| 281 | # only objects from packs "B" and "D" are included in |
| 282 | # the output pack. |
| 283 | P=$(git pack-objects --stdin-packs $packdir/pack <in) && |
| 284 | objects_in_packs $B $D >expect && |
| 285 | objects_in_packs $P >actual && |
| 286 | test_cmp expect actual && |
| 287 | |
| 288 | # But with --stdin-packs=follow, objects from both |
| 289 | # included packs reach objects from the unknown pack, so |
| 290 | # objects from pack "A" is included in the output pack |
| 291 | # in addition to the above. |
| 292 | P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) && |
| 293 | objects_in_packs $A $B $D >expect && |
| 294 | objects_in_packs $P >actual && |
| 295 | test_cmp expect actual && |
| 296 | |
| 297 | # And with --unpacked, we will pick up objects from unknown |
| 298 | # packs that are reachable from loose objects. Loose object E |
| 299 | # reaches objects in pack A, but there are three excluded packs |
| 300 | # in between. |
| 301 | # |
| 302 | # The resulting pack should include objects reachable from E |
| 303 | # that are not present in packs B, C, or D, along with those |
| 304 | # present in pack A. |
| 305 | cat >in <<-EOF && |
| 306 | ^pack-$B.pack |
| 307 | ^pack-$C.pack |
| 308 | ^pack-$D.pack |
| 309 | EOF |
| 310 | |
| 311 | P=$(git pack-objects --stdin-packs=follow --unpacked \ |
| 312 | $packdir/pack <in) && |
| 313 | |
| 314 | { |
| 315 | objects_in_packs $A && |
| 316 | git rev-list --objects --no-object-names D..E |
| 317 | }>expect.raw && |
| 318 | sort expect.raw >expect && |
| 319 | objects_in_packs $P >actual && |
| 320 | test_cmp expect actual |
| 321 | ) |
| 322 | ' |
| 323 | |
| 324 | test_expect_success '--stdin-packs with promisors' ' |
| 325 | test_when_finished "rm -fr repo" && |
| 326 | git init repo && |
| 327 | ( |
| 328 | cd repo && |
| 329 | git config set maintenance.auto false && |
| 330 | git remote add promisor garbage && |
| 331 | git config set remote.promisor.promisor true && |
| 332 | |
| 333 | for c in A B C D |
| 334 | do |
| 335 | echo "$c" >file && |
| 336 | git add file && |
| 337 | git commit --message "$c" && |
| 338 | git tag "$c" || return 1 |
| 339 | done && |
| 340 | |
| 341 | A="$(echo A | git pack-objects --revs $packdir/pack)" && |
| 342 | B="$(echo A..B | git pack-objects --revs $packdir/pack --filter=blob:none)" && |
| 343 | C="$(echo B..C | git pack-objects --revs $packdir/pack)" && |
| 344 | D="$(echo C..D | git pack-objects --revs $packdir/pack)" && |
| 345 | touch $packdir/pack-$B.promisor && |
| 346 | |
| 347 | test_must_fail git pack-objects --stdin-packs --exclude-promisor-objects pack- 2>err <<-EOF && |
| 348 | pack-$B.pack |
| 349 | EOF |
| 350 | test_grep "is a promisor but --exclude-promisor-objects was given" err && |
| 351 | |
| 352 | PACK=$(git pack-objects --stdin-packs=follow --exclude-promisor-objects $packdir/pack <<-EOF |
| 353 | pack-$D.pack |
| 354 | EOF |
| 355 | ) && |
| 356 | objects_in_packs $C $D >expect && |
| 357 | objects_in_packs $PACK >actual && |
| 358 | test_cmp expect actual && |
| 359 | rm -f $packdir/pack-$PACK.* |
| 360 | ) |
| 361 | ' |
| 362 | |
| 363 | test_expect_success '--stdin-packs does not perform backfill fetch' ' |
| 364 | test_when_finished "rm -rf remote client" && |
| 365 | |
| 366 | git init remote && |
| 367 | test_commit_bulk -C remote 10 && |
| 368 | git -C remote config set --local uploadpack.allowfilter 1 && |
| 369 | git -C remote config set --local uploadpack.allowanysha1inwant 1 && |
| 370 | |
| 371 | git clone --filter=tree:0 "file://$(pwd)/remote" client && |
| 372 | ( |
| 373 | cd client && |
| 374 | ls .git/objects/pack/*.promisor | sed "s|.*/||; s/\.promisor$/.pack/" >packs && |
| 375 | test_line_count -gt 1 packs && |
| 376 | GIT_TRACE2_EVENT="$(pwd)/event.log" git pack-objects --stdin-packs pack <packs && |
| 377 | test_grep ! "\"event\":\"child_start\"" event.log |
| 378 | ) |
| 379 | ' |
| 380 | |
| 381 | stdin_packs__follow_with_only () { |
| 382 | rm -fr stdin_packs__follow_with_only && |
| 383 | git init stdin_packs__follow_with_only && |
| 384 | ( |
| 385 | cd stdin_packs__follow_with_only && |
| 386 | |
| 387 | test_commit A && |
| 388 | test_commit B && |
| 389 | |
| 390 | git rev-parse "$@" >B.objects && |
| 391 | |
| 392 | echo A | git pack-objects --revs $packdir/pack && |
| 393 | B="$(git pack-objects $packdir/pack <B.objects)" && |
| 394 | |
| 395 | git cat-file --batch-check="%(objectname)" --batch-all-objects >objs && |
| 396 | for obj in $(cat objs) |
| 397 | do |
| 398 | rm -f $objdir/$(test_oid_to_path $obj) || return 1 |
| 399 | done && |
| 400 | |
| 401 | ( cd $packdir && ls pack-*.pack ) >in && |
| 402 | git pack-objects --stdin-packs=follow --stdout >/dev/null <in |
| 403 | ) |
| 404 | } |
| 405 | |
| 406 | test_expect_success '--stdin-packs=follow tolerates missing blobs' ' |
| 407 | stdin_packs__follow_with_only HEAD HEAD^{tree} |
| 408 | ' |
| 409 | |
| 410 | test_expect_success '--stdin-packs=follow tolerates missing trees' ' |
| 411 | stdin_packs__follow_with_only HEAD HEAD:B.t |
| 412 | ' |
| 413 | |
| 414 | test_expect_success '--stdin-packs=follow tolerates missing commits' ' |
| 415 | stdin_packs__follow_with_only HEAD HEAD^{tree} |
| 416 | ' |
| 417 | |
| 418 | test_expect_success '--stdin-packs=follow with open-excluded packs' ' |
| 419 | test_when_finished "rm -fr repo" && |
| 420 | |
| 421 | git init repo && |
| 422 | ( |
| 423 | cd repo && |
| 424 | git config set maintenance.auto false && |
| 425 | |
| 426 | git branch -M main && |
| 427 | |
| 428 | # Create the following commit structure: |
| 429 | # |
| 430 | # A <-- B <-- D (main) |
| 431 | # ^ |
| 432 | # \ |
| 433 | # C (other) |
| 434 | test_commit A && |
| 435 | test_commit B && |
| 436 | git checkout -B other && |
| 437 | test_commit C && |
| 438 | git checkout main && |
| 439 | test_commit D && |
| 440 | |
| 441 | A="$(echo A | git pack-objects --revs $packdir/pack)" && |
| 442 | B="$(echo A..B | git pack-objects --revs $packdir/pack)" && |
| 443 | C="$(echo B..C | git pack-objects --revs $packdir/pack)" && |
| 444 | D="$(echo B..D | git pack-objects --revs $packdir/pack)" && |
| 445 | |
| 446 | C_ONLY="$(git rev-parse other | git pack-objects $packdir/pack)" && |
| 447 | |
| 448 | git prune-packed && |
| 449 | |
| 450 | # Create a pack using --stdin-packs=follow where: |
| 451 | # |
| 452 | # - pack D is included, |
| 453 | # - pack C_ONLY is excluded, but open, |
| 454 | # - pack B is excluded, but closed, and |
| 455 | # - packs A and C are unknown |
| 456 | # |
| 457 | # The resulting pack should therefore contain: |
| 458 | # |
| 459 | # - objects from the included pack D, |
| 460 | # - A.t (rescued via D^{tree}), and |
| 461 | # - C^{tree} and C.t (rescued via pack C_ONLY) |
| 462 | # |
| 463 | # , but should omit: |
| 464 | # |
| 465 | # - C (excluded via C_ONLY), |
| 466 | # - objects from pack B (trivially excluded-closed) |
| 467 | # - A and A^{tree} (ancestors of B) |
| 468 | P=$(git pack-objects --stdin-packs=follow $packdir/pack <<-EOF |
| 469 | pack-$D.pack |
| 470 | !pack-$C_ONLY.pack |
| 471 | ^pack-$B.pack |
| 472 | EOF |
| 473 | ) && |
| 474 | |
| 475 | { |
| 476 | objects_in_packs $D && |
| 477 | git rev-parse A:A.t "C^{tree}" C:C.t |
| 478 | } >expect.raw && |
| 479 | sort expect.raw >expect && |
| 480 | |
| 481 | objects_in_packs $P >actual && |
| 482 | test_cmp expect actual |
| 483 | ) |
| 484 | ' |
| 485 | |
| 486 | test_expect_success '--stdin-packs with !-delimited pack without follow' ' |
| 487 | test_when_finished "rm -fr repo" && |
| 488 | |
| 489 | git init repo && |
| 490 | ( |
| 491 | test_commit A && |
| 492 | test_commit B && |
| 493 | test_commit C && |
| 494 | |
| 495 | A="$(echo A | git pack-objects --revs $packdir/pack)" && |
| 496 | B="$(echo A..B | git pack-objects --revs $packdir/pack)" && |
| 497 | C="$(echo B..C | git pack-objects --revs $packdir/pack)" && |
| 498 | |
| 499 | cat >in <<-EOF && |
| 500 | !pack-$A.pack |
| 501 | pack-$B.pack |
| 502 | pack-$C.pack |
| 503 | EOF |
| 504 | |
| 505 | # Without --stdin-packs=follow, we treat the first |
| 506 | # line of input as a literal packfile name, and thus |
| 507 | # expect pack-objects to complain of a missing pack |
| 508 | test_must_fail git pack-objects --stdin-packs --stdout \ |
| 509 | >/dev/null <in 2>err && |
| 510 | test_grep "could not find pack .!pack-$A.pack." err && |
| 511 | |
| 512 | # With --stdin-packs=follow, we treat the second line |
| 513 | # of input as indicating pack-$A.pack is an excluded |
| 514 | # open pack, and thus expect pack-objects to succeed |
| 515 | P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) && |
| 516 | |
| 517 | objects_in_packs $B $C >expect && |
| 518 | objects_in_packs $P >actual && |
| 519 | test_cmp expect actual |
| 520 | ) |
| 521 | ' |
| 522 | |
| 523 | test_done |