| #!/bin/sh |
| # |
| # Copyright (c) 2021 Jiang Xin |
| # |
| |
| test_description='Test git-bundle' |
| |
| GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME |
| |
| . ./test-lib.sh |
| . "$TEST_DIRECTORY"/lib-bundle.sh |
| |
| # Create a commit or tag and set the variable with the object ID. |
| test_commit_setvar () { |
| notick= |
| signoff= |
| indir= |
| merge= |
| tag= |
| var= |
| |
| while test $# != 0 |
| do |
| case "$1" in |
| --merge) |
| merge=t |
| ;; |
| --tag) |
| tag=t |
| ;; |
| --notick) |
| notick=t |
| ;; |
| --signoff) |
| signoff="$1" |
| ;; |
| -C) |
| shift |
| indir="$1" |
| ;; |
| -*) |
| echo >&2 "error: unknown option $1" |
| return 1 |
| ;; |
| *) |
| break |
| ;; |
| esac |
| shift |
| done |
| if test $# -lt 2 |
| then |
| echo >&2 "error: test_commit_setvar must have at least 2 arguments" |
| return 1 |
| fi |
| var=$1 |
| shift |
| indir=${indir:+"$indir"/} |
| if test -z "$notick" |
| then |
| test_tick |
| fi && |
| if test -n "$merge" |
| then |
| git ${indir:+ -C "$indir"} merge --no-edit --no-ff \ |
| ${2:+-m "$2"} "$1" && |
| oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD) |
| elif test -n "$tag" |
| then |
| git ${indir:+ -C "$indir"} tag -m "$1" "$1" "${2:-HEAD}" && |
| oid=$(git ${indir:+ -C "$indir"} rev-parse "$1") |
| else |
| file=${2:-"$1.t"} && |
| echo "${3-$1}" >"$indir$file" && |
| git ${indir:+ -C "$indir"} add "$file" && |
| git ${indir:+ -C "$indir"} commit $signoff -m "$1" && |
| oid=$(git ${indir:+ -C "$indir"} rev-parse HEAD) |
| fi && |
| eval $var=$oid |
| } |
| |
| get_abbrev_oid () { |
| oid=$1 && |
| suffix=${oid#???????} && |
| oid=${oid%$suffix} && |
| if test -n "$oid" |
| then |
| echo "$oid" |
| else |
| echo "undefined-oid" |
| fi |
| } |
| |
| # Format the output of git commands to make a user-friendly and stable |
| # text. We can easily prepare the expect text without having to worry |
| # about future changes of the commit ID. |
| make_user_friendly_and_stable_output () { |
| sed \ |
| -e "s/$(get_abbrev_oid $A)[0-9a-f]*/<COMMIT-A>/g" \ |
| -e "s/$(get_abbrev_oid $B)[0-9a-f]*/<COMMIT-B>/g" \ |
| -e "s/$(get_abbrev_oid $C)[0-9a-f]*/<COMMIT-C>/g" \ |
| -e "s/$(get_abbrev_oid $D)[0-9a-f]*/<COMMIT-D>/g" \ |
| -e "s/$(get_abbrev_oid $E)[0-9a-f]*/<COMMIT-E>/g" \ |
| -e "s/$(get_abbrev_oid $F)[0-9a-f]*/<COMMIT-F>/g" \ |
| -e "s/$(get_abbrev_oid $G)[0-9a-f]*/<COMMIT-G>/g" \ |
| -e "s/$(get_abbrev_oid $H)[0-9a-f]*/<COMMIT-H>/g" \ |
| -e "s/$(get_abbrev_oid $I)[0-9a-f]*/<COMMIT-I>/g" \ |
| -e "s/$(get_abbrev_oid $J)[0-9a-f]*/<COMMIT-J>/g" \ |
| -e "s/$(get_abbrev_oid $K)[0-9a-f]*/<COMMIT-K>/g" \ |
| -e "s/$(get_abbrev_oid $L)[0-9a-f]*/<COMMIT-L>/g" \ |
| -e "s/$(get_abbrev_oid $M)[0-9a-f]*/<COMMIT-M>/g" \ |
| -e "s/$(get_abbrev_oid $N)[0-9a-f]*/<COMMIT-N>/g" \ |
| -e "s/$(get_abbrev_oid $O)[0-9a-f]*/<COMMIT-O>/g" \ |
| -e "s/$(get_abbrev_oid $P)[0-9a-f]*/<COMMIT-P>/g" \ |
| -e "s/$(get_abbrev_oid $TAG1)[0-9a-f]*/<TAG-1>/g" \ |
| -e "s/$(get_abbrev_oid $TAG2)[0-9a-f]*/<TAG-2>/g" \ |
| -e "s/$(get_abbrev_oid $TAG3)[0-9a-f]*/<TAG-3>/g" |
| } |
| |
| format_and_save_expect () { |
| sed -e 's/Z$//' >expect |
| } |
| |
| HASH_MESSAGE="The bundle uses this hash algorithm: $GIT_DEFAULT_HASH" |
| |
| # (C) (D, pull/1/head, topic/1) |
| # o --- o |
| # / \ (L) |
| # / \ o (H, topic/2) (M, tag:v2) |
| # / (F) \ / (N, tag:v3) |
| # / o --------- o (G, pull/2/head) o --- o --- o (release) |
| # / / \ \ / \ |
| # o --- o --- o -------- o -- o ------------------ o ------- o --- o (main) |
| # (A) (B) (E, tag:v1) (I) (J) (K) (O) (P) |
| # |
| test_expect_success 'setup' ' |
| # Try to make a stable fixed width for abbreviated commit ID, |
| # this fixed-width oid will be replaced with "<OID>". |
| git config core.abbrev 7 && |
| |
| # branch main: commit A & B |
| test_commit_setvar A "Commit A" main.txt && |
| test_commit_setvar B "Commit B" main.txt && |
| |
| # branch topic/1: commit C & D, refs/pull/1/head |
| git checkout -b topic/1 && |
| test_commit_setvar C "Commit C" topic-1.txt && |
| test_commit_setvar D "Commit D" topic-1.txt && |
| git update-ref refs/pull/1/head HEAD && |
| |
| # branch topic/1: commit E, tag v1 |
| git checkout main && |
| test_commit_setvar E "Commit E" main.txt && |
| test_commit_setvar --tag TAG1 v1 && |
| |
| # branch topic/2: commit F & G, refs/pull/2/head |
| git checkout -b topic/2 && |
| test_commit_setvar F "Commit F" topic-2.txt && |
| test_commit_setvar G "Commit G" topic-2.txt && |
| git update-ref refs/pull/2/head HEAD && |
| test_commit_setvar H "Commit H" topic-2.txt && |
| |
| # branch main: merge commit I & J |
| git checkout main && |
| test_commit_setvar --merge I topic/1 "Merge commit I" && |
| test_commit_setvar --merge J refs/pull/2/head "Merge commit J" && |
| |
| # branch main: commit K |
| git checkout main && |
| test_commit_setvar K "Commit K" main.txt && |
| |
| # branch release: |
| git checkout -b release && |
| test_commit_setvar L "Commit L" release.txt && |
| test_commit_setvar M "Commit M" release.txt && |
| test_commit_setvar --tag TAG2 v2 && |
| test_commit_setvar N "Commit N" release.txt && |
| test_commit_setvar --tag TAG3 v3 && |
| |
| # branch main: merge commit O, commit P |
| git checkout main && |
| test_commit_setvar --merge O tags/v2 "Merge commit O" && |
| test_commit_setvar P "Commit P" main.txt |
| ' |
| |
| test_expect_success 'create bundle from special rev: main^!' ' |
| git bundle create special-rev.bdl "main^!" && |
| |
| git bundle list-heads special-rev.bdl | |
| make_user_friendly_and_stable_output >actual && |
| cat >expect <<-\EOF && |
| <COMMIT-P> refs/heads/main |
| EOF |
| test_cmp expect actual && |
| |
| git bundle verify special-rev.bdl | |
| make_user_friendly_and_stable_output >actual && |
| format_and_save_expect <<-EOF && |
| The bundle contains this ref: |
| <COMMIT-P> refs/heads/main |
| The bundle requires this ref: |
| <COMMIT-O> Z |
| $HASH_MESSAGE |
| EOF |
| test_cmp expect actual && |
| |
| test_bundle_object_count special-rev.bdl 3 |
| ' |
| |
| test_expect_success 'create bundle with --max-count option' ' |
| git bundle create max-count.bdl --max-count 1 \ |
| main \ |
| "^release" \ |
| refs/tags/v1 \ |
| refs/pull/1/head \ |
| refs/pull/2/head && |
| |
| git bundle verify max-count.bdl | |
| make_user_friendly_and_stable_output >actual && |
| format_and_save_expect <<-EOF && |
| The bundle contains these 2 refs: |
| <COMMIT-P> refs/heads/main |
| <TAG-1> refs/tags/v1 |
| The bundle requires this ref: |
| <COMMIT-O> Z |
| $HASH_MESSAGE |
| EOF |
| test_cmp expect actual && |
| |
| test_bundle_object_count max-count.bdl 4 |
| ' |
| |
| test_expect_success 'create bundle with --since option' ' |
| git log -1 --pretty="%ad" $M >actual && |
| cat >expect <<-\EOF && |
| Thu Apr 7 15:26:13 2005 -0700 |
| EOF |
| test_cmp expect actual && |
| |
| git bundle create since.bdl \ |
| --since "Thu Apr 7 15:27:00 2005 -0700" \ |
| --all && |
| |
| git bundle verify since.bdl | |
| make_user_friendly_and_stable_output >actual && |
| format_and_save_expect <<-EOF && |
| The bundle contains these 5 refs: |
| <COMMIT-P> refs/heads/main |
| <COMMIT-N> refs/heads/release |
| <TAG-2> refs/tags/v2 |
| <TAG-3> refs/tags/v3 |
| <COMMIT-P> HEAD |
| The bundle requires these 2 refs: |
| <COMMIT-M> Z |
| <COMMIT-K> Z |
| $HASH_MESSAGE |
| EOF |
| test_cmp expect actual && |
| |
| test_bundle_object_count --thin since.bdl 13 |
| ' |
| |
| test_expect_success 'create bundle 1 - no prerequisites' ' |
| # create bundle from args |
| git bundle create 1.bdl topic/1 topic/2 && |
| |
| # create bundle from stdin |
| cat >input <<-\EOF && |
| topic/1 |
| topic/2 |
| EOF |
| git bundle create stdin-1.bdl --stdin <input && |
| |
| format_and_save_expect <<-EOF && |
| The bundle contains these 2 refs: |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| The bundle records a complete history. |
| $HASH_MESSAGE |
| EOF |
| |
| # verify bundle, which has no prerequisites |
| git bundle verify 1.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| git bundle verify stdin-1.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| test_bundle_object_count 1.bdl 24 && |
| test_bundle_object_count stdin-1.bdl 24 |
| ' |
| |
| test_expect_success 'create bundle 2 - has prerequisites' ' |
| # create bundle from args |
| git bundle create 2.bdl \ |
| --ignore-missing \ |
| ^topic/deleted \ |
| ^$D \ |
| ^topic/2 \ |
| release && |
| |
| # create bundle from stdin |
| # input has a non-exist reference: "topic/deleted" |
| cat >input <<-EOF && |
| ^topic/deleted |
| ^$D |
| ^topic/2 |
| EOF |
| git bundle create stdin-2.bdl \ |
| --ignore-missing \ |
| --stdin \ |
| release <input && |
| |
| format_and_save_expect <<-EOF && |
| The bundle contains this ref: |
| <COMMIT-N> refs/heads/release |
| The bundle requires these 3 refs: |
| <COMMIT-D> Z |
| <COMMIT-E> Z |
| <COMMIT-G> Z |
| $HASH_MESSAGE |
| EOF |
| |
| git bundle verify 2.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| git bundle verify stdin-2.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| test_bundle_object_count 2.bdl 16 && |
| test_bundle_object_count stdin-2.bdl 16 |
| ' |
| |
| test_expect_success 'fail to verify bundle without prerequisites' ' |
| git init --bare test1.git && |
| |
| format_and_save_expect <<-\EOF && |
| error: Repository lacks these prerequisite commits: |
| error: <COMMIT-D> Z |
| error: <COMMIT-E> Z |
| error: <COMMIT-G> Z |
| EOF |
| |
| test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual |
| ' |
| |
| test_expect_success 'create bundle 3 - two refs, same object' ' |
| # create bundle from args |
| git bundle create --version=3 3.bdl \ |
| ^release \ |
| ^topic/1 \ |
| ^topic/2 \ |
| main \ |
| HEAD && |
| |
| # create bundle from stdin |
| cat >input <<-\EOF && |
| ^release |
| ^topic/1 |
| ^topic/2 |
| EOF |
| git bundle create --version=3 stdin-3.bdl \ |
| --stdin \ |
| main HEAD <input && |
| |
| format_and_save_expect <<-EOF && |
| The bundle contains these 2 refs: |
| <COMMIT-P> refs/heads/main |
| <COMMIT-P> HEAD |
| The bundle requires these 2 refs: |
| <COMMIT-M> Z |
| <COMMIT-K> Z |
| $HASH_MESSAGE |
| EOF |
| |
| git bundle verify 3.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| git bundle verify stdin-3.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| test_bundle_object_count 3.bdl 4 && |
| test_bundle_object_count stdin-3.bdl 4 |
| ' |
| |
| test_expect_success 'create bundle 4 - with tags' ' |
| # create bundle from args |
| git bundle create 4.bdl \ |
| ^main \ |
| ^release \ |
| ^topic/1 \ |
| ^topic/2 \ |
| --all && |
| |
| # create bundle from stdin |
| cat >input <<-\EOF && |
| ^main |
| ^release |
| ^topic/1 |
| ^topic/2 |
| EOF |
| git bundle create stdin-4.bdl \ |
| --ignore-missing \ |
| --stdin \ |
| --all <input && |
| |
| cat >expect <<-EOF && |
| The bundle contains these 3 refs: |
| <TAG-1> refs/tags/v1 |
| <TAG-2> refs/tags/v2 |
| <TAG-3> refs/tags/v3 |
| The bundle records a complete history. |
| $HASH_MESSAGE |
| EOF |
| |
| git bundle verify 4.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| git bundle verify stdin-4.bdl | |
| make_user_friendly_and_stable_output >actual && |
| test_cmp expect actual && |
| |
| test_bundle_object_count 4.bdl 3 && |
| test_bundle_object_count stdin-4.bdl 3 |
| ' |
| |
| test_expect_success 'clone from bundle' ' |
| git clone --mirror 1.bdl mirror.git && |
| git -C mirror.git show-ref | |
| make_user_friendly_and_stable_output >actual && |
| cat >expect <<-\EOF && |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| EOF |
| test_cmp expect actual && |
| |
| git -C mirror.git fetch ../2.bdl "+refs/*:refs/*" && |
| git -C mirror.git show-ref | |
| make_user_friendly_and_stable_output >actual && |
| cat >expect <<-\EOF && |
| <COMMIT-N> refs/heads/release |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| EOF |
| test_cmp expect actual && |
| |
| git -C mirror.git fetch ../3.bdl "+refs/*:refs/*" && |
| git -C mirror.git show-ref | |
| make_user_friendly_and_stable_output >actual && |
| cat >expect <<-\EOF && |
| <COMMIT-P> refs/heads/main |
| <COMMIT-N> refs/heads/release |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| EOF |
| test_cmp expect actual && |
| |
| git -C mirror.git fetch ../4.bdl "+refs/*:refs/*" && |
| git -C mirror.git show-ref | |
| make_user_friendly_and_stable_output >actual && |
| cat >expect <<-\EOF && |
| <COMMIT-P> refs/heads/main |
| <COMMIT-N> refs/heads/release |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| <TAG-1> refs/tags/v1 |
| <TAG-2> refs/tags/v2 |
| <TAG-3> refs/tags/v3 |
| EOF |
| test_cmp expect actual |
| ' |
| |
| test_expect_success 'unfiltered bundle with --objects' ' |
| git bundle create all-objects.bdl \ |
| --all --objects && |
| git bundle create all.bdl \ |
| --all && |
| |
| # Compare the headers of these files. |
| sed -n -e "/^$/q" -e "p" all.bdl >expect && |
| sed -n -e "/^$/q" -e "p" all-objects.bdl >actual && |
| test_cmp expect actual |
| ' |
| |
| for filter in "blob:none" "tree:0" "tree:1" "blob:limit=100" |
| do |
| test_expect_success "filtered bundle: $filter" ' |
| test_when_finished rm -rf .git/objects/pack cloned unbundled && |
| git bundle create partial.bdl \ |
| --all \ |
| --filter=$filter && |
| |
| git bundle verify partial.bdl >unfiltered && |
| make_user_friendly_and_stable_output <unfiltered >actual && |
| |
| cat >expect <<-EOF && |
| The bundle contains these 10 refs: |
| <COMMIT-P> refs/heads/main |
| <COMMIT-N> refs/heads/release |
| <COMMIT-D> refs/heads/topic/1 |
| <COMMIT-H> refs/heads/topic/2 |
| <COMMIT-D> refs/pull/1/head |
| <COMMIT-G> refs/pull/2/head |
| <TAG-1> refs/tags/v1 |
| <TAG-2> refs/tags/v2 |
| <TAG-3> refs/tags/v3 |
| <COMMIT-P> HEAD |
| The bundle records a complete history. |
| $HASH_MESSAGE |
| The bundle uses this filter: $filter |
| EOF |
| test_cmp expect actual && |
| |
| test_config uploadpack.allowfilter 1 && |
| test_config uploadpack.allowanysha1inwant 1 && |
| git clone --no-local --filter=$filter --bare "file://$(pwd)" cloned && |
| |
| git init unbundled && |
| git -C unbundled bundle unbundle ../partial.bdl >ref-list.txt && |
| ls unbundled/.git/objects/pack/pack-*.promisor >promisor && |
| test_line_count = 1 promisor && |
| |
| # Count the same number of reachable objects. |
| reflist=$(git for-each-ref --format="%(objectname)") && |
| git rev-list --objects --filter=$filter --missing=allow-any \ |
| $reflist >expect && |
| for repo in cloned unbundled |
| do |
| git -C $repo rev-list --objects --missing=allow-any \ |
| $reflist >actual && |
| test_cmp expect actual || return 1 |
| done |
| ' |
| done |
| |
| # NEEDSWORK: 'git clone --bare' should be able to clone from a filtered |
| # bundle, but that requires a change to promisor/filter config options. |
| # For now, we fail gracefully with a helpful error. This behavior can be |
| # changed in the future to succeed as much as possible. |
| test_expect_success 'cloning from filtered bundle has useful error' ' |
| git bundle create partial.bdl \ |
| --all \ |
| --filter=blob:none && |
| test_must_fail git clone --bare partial.bdl partial 2>err && |
| grep "cannot clone from filtered bundle" err |
| ' |
| |
| test_done |