#!/bin/sh test_description='migration of ref storage backends' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh # Migrate the provided repository from one format to the other and # verify that the references and logs are migrated over correctly. # Usage: test_migration # is the relative path to the repo to be migrated. # is the ref format to be migrated to. # (true or false) whether to skip reflog verification. test_migration () { repo=$1 && format=$2 && skip_reflog_verify=${3:-false} && git -C "$repo" for-each-ref --include-root-refs \ --format='%(refname) %(objectname) %(symref)' >expect && if ! $skip_reflog_verify then git -C "$repo" reflog --all >expect_logs && git -C "$repo" reflog list >expect_log_list fi && git -C "$repo" refs migrate --ref-format="$2" && git -C "$repo" for-each-ref --include-root-refs \ --format='%(refname) %(objectname) %(symref)' >actual && test_cmp expect actual && if ! $skip_reflog_verify then git -C "$repo" reflog --all >actual_logs && git -C "$repo" reflog list >actual_log_list && test_cmp expect_logs actual_logs && test_cmp expect_log_list actual_log_list fi && git -C "$repo" rev-parse --show-ref-format >actual && echo "$format" >expect && test_cmp expect actual } test_expect_success 'setup' ' rm -rf .git ' test_expect_success "superfluous arguments" ' test_when_finished "rm -rf repo" && git init repo && test_must_fail git -C repo refs migrate foo 2>err && cat >expect <<-EOF && usage: too many arguments EOF test_cmp expect err ' test_expect_success "missing ref storage format" ' test_when_finished "rm -rf repo" && git init repo && test_must_fail git -C repo refs migrate 2>err && cat >expect <<-EOF && usage: missing --ref-format= EOF test_cmp expect err ' test_expect_success "unknown ref storage format" ' test_when_finished "rm -rf repo" && git init repo && test_must_fail git -C repo refs migrate \ --ref-format=unknown 2>err && cat >expect <<-EOF && error: unknown ref storage format ${SQ}unknown${SQ} EOF test_cmp expect err ' ref_formats="files reftable" for from_format in $ref_formats do for to_format in $ref_formats do if test "$from_format" = "$to_format" then continue fi test_expect_success "$from_format: migration to same format fails" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_must_fail git -C repo refs migrate \ --ref-format=$from_format 2>err && cat >expect <<-EOF && error: repository already uses ${SQ}$from_format${SQ} format EOF test_cmp expect err ' test_expect_success "$from_format -> $to_format: migration with worktree fails" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && git -C repo worktree add wt && test_must_fail git -C repo refs migrate \ --ref-format=$to_format 2>err && cat >expect <<-EOF && error: migrating repositories with worktrees is not supported yet EOF test_cmp expect err ' test_expect_success "$from_format -> $to_format: unborn HEAD" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_migration repo "$to_format" ' test_expect_success "$from_format -> $to_format: single ref" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && test_migration repo "$to_format" ' test_expect_success "$from_format -> $to_format: bare repository" ' test_when_finished "rm -rf repo repo.git" && git init --ref-format=$from_format repo && test_commit -C repo initial && git clone --ref-format=$from_format --mirror repo repo.git && test_migration repo.git "$to_format" ' test_expect_success "$from_format -> $to_format: dangling symref" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && git -C repo symbolic-ref BROKEN_HEAD refs/heads/nonexistent && test_migration repo "$to_format" && echo refs/heads/nonexistent >expect && git -C repo symbolic-ref BROKEN_HEAD >actual && test_cmp expect actual ' test_expect_success "$from_format -> $to_format: broken ref" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && test-tool -C repo ref-store main update-ref "" refs/heads/broken \ "$(test_oid 001)" "$ZERO_OID" REF_SKIP_CREATE_REFLOG,REF_SKIP_OID_VERIFICATION && test_migration repo "$to_format" true && test_oid 001 >expect && git -C repo rev-parse refs/heads/broken >actual && test_cmp expect actual ' test_expect_success "$from_format -> $to_format: pseudo-refs" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && git -C repo update-ref FOO_HEAD HEAD && test_migration repo "$to_format" ' test_expect_success "$from_format -> $to_format: special refs are left alone" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && git -C repo rev-parse HEAD >repo/.git/MERGE_HEAD && git -C repo rev-parse MERGE_HEAD && test_migration repo "$to_format" && test_path_is_file repo/.git/MERGE_HEAD ' test_expect_success "$from_format -> $to_format: a bunch of refs" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && cat >input <<-EOF && create FOO_HEAD HEAD create refs/heads/branch-1 HEAD create refs/heads/branch-2 HEAD create refs/heads/branch-3 HEAD create refs/heads/branch-4 HEAD create refs/tags/tag-1 HEAD create refs/tags/tag-2 HEAD EOF git -C repo update-ref --stdin $to_format: dry-run migration does not modify repository" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && git -C repo refs migrate --dry-run \ --ref-format=$to_format >output && grep "Finished dry-run migration of refs" output && test_path_is_dir repo/.git/ref_migration.* && echo $from_format >expect && git -C repo rev-parse --show-ref-format >actual && test_cmp expect actual ' test_expect_success "$from_format -> $to_format: reflogs of symrefs with target deleted" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit -C repo initial && git -C repo branch branch-1 HEAD && git -C repo symbolic-ref refs/heads/symref refs/heads/branch-1 && cat >input <<-EOF && delete refs/heads/branch-1 EOF git -C repo update-ref --stdin $to_format: reflogs order is retained" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && test_commit --date "100005000 +0700" --no-tag -C repo initial && test_commit --date "100003000 +0700" --no-tag -C repo second && test_migration repo "$to_format" ' test_expect_success "$from_format -> $to_format: stash is retained" ' test_when_finished "rm -rf repo" && git init --ref-format=$from_format repo && ( cd repo && test_commit initial A && echo foo >A && git stash push && echo bar >A && git stash push && git stash list >expect.reflog && test_migration . "$to_format" && git stash list >actual.reflog && test_cmp expect.reflog actual.reflog ) ' done done test_expect_success 'multiple reftable blocks with multiple entries' ' test_when_finished "rm -rf repo" && git init --ref-format=files repo && test_commit -C repo first && printf "create refs/heads/ref-%d HEAD\n" $(test_seq 5000) >stdin && git -C repo update-ref --stdin stdin && git -C repo update-ref --stdin repo/.git/FETCH_HEAD && test_path_is_file repo/.git/HEAD && test_path_is_file repo/.git/ORIG_HEAD && test_path_is_file repo/.git/refs/heads/main && test_path_is_file repo/.git/packed-refs && test_migration repo reftable && echo "ref: refs/heads/.invalid" >expect && test_cmp expect repo/.git/HEAD && echo "this repository uses the reftable format" >expect && test_cmp expect repo/.git/refs/heads && test_path_is_file repo/.git/FETCH_HEAD && test_path_is_missing repo/.git/ORIG_HEAD && test_path_is_missing repo/.git/refs/heads/main && test_path_is_missing repo/.git/logs && test_path_is_missing repo/.git/packed-refs ' test_expect_success 'migrating from reftable format deletes backend files' ' test_when_finished "rm -rf repo" && git init --ref-format=reftable repo && test_commit -C repo first && test_path_is_dir repo/.git/reftable && test_migration repo files && test_path_is_missing repo/.git/reftable && echo "ref: refs/heads/main" >expect && test_cmp expect repo/.git/HEAD && test_path_is_file repo/.git/packed-refs ' test_done