When migrating reflog entries between different storage formats we end up with invalid old object IDs for the migrated entries: instead of writing the old object ID of the to-be-migrated entry, we end up with the all-zeroes object ID. The root cause of this issue is that we don't know to use the old object ID provided by the caller. Instead, we manually resolve the old object ID by resolving the current value of its matching reference. But as that reference does not yet exist in the target ref storage we always end up resolving it to all-zeroes. This issue got unnoticed as there is no user-facing command that would even show the old object ID. While `git log -g` knows to show the new object ID, we don't have any formatting directive to show the old object ID. Fix the bug by introducing a new flag `REF_LOG_USE_PROVIDED_OIDS`. If set, backends are instructed to use the old and new object IDs provided by the caller, without doing any manual resolving. Set this flag in `ref_transaction_update_reflog()`. Amend our tests in t1460-refs-migrate to use our test tool to read reflog entries. This test tool prints out both old and new object ID of each reflog entry, which fixes the test gap. Furthermore it also prints the full identity used to write the reflog, which provides test coverage for the previous commit in this patch series that fixed the identity for migrated reflogs. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
329 lines
10 KiB
Bash
Executable File
329 lines
10 KiB
Bash
Executable File
#!/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
|
|
|
|
print_all_reflog_entries () {
|
|
repo=$1 &&
|
|
test-tool -C "$repo" ref-store main for-each-reflog >reflogs &&
|
|
while read reflog
|
|
do
|
|
echo "REFLOG: $reflog" &&
|
|
test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" ||
|
|
return 1
|
|
done <reflogs
|
|
}
|
|
|
|
# Migrate the provided repository from one format to the other and
|
|
# verify that the references and logs are migrated over correctly.
|
|
# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
|
|
# <repo> is the relative path to the repo to be migrated.
|
|
# <format> is the ref format to be migrated to.
|
|
# <skip_reflog_verify> (default: false) whether to skip reflog verification.
|
|
# <options...> are other options be passed directly to 'git refs migrate'.
|
|
test_migration () {
|
|
repo=$1 &&
|
|
format=$2 &&
|
|
shift 2 &&
|
|
skip_reflog_verify=false &&
|
|
if test $# -ge 1
|
|
then
|
|
skip_reflog_verify=$1
|
|
shift
|
|
fi &&
|
|
git -C "$repo" for-each-ref --include-root-refs \
|
|
--format='%(refname) %(objectname) %(symref)' >expect &&
|
|
if ! $skip_reflog_verify
|
|
then
|
|
print_all_reflog_entries "$repo" >expect_logs
|
|
fi &&
|
|
|
|
git -C "$repo" refs migrate --ref-format="$format" "$@" &&
|
|
|
|
git -C "$repo" for-each-ref --include-root-refs \
|
|
--format='%(refname) %(objectname) %(symref)' >actual &&
|
|
test_cmp expect actual &&
|
|
if ! $skip_reflog_verify
|
|
then
|
|
print_all_reflog_entries "$repo" >actual_logs &&
|
|
test_cmp expect_logs actual_logs
|
|
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=<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 <input &&
|
|
test_migration repo "$to_format"
|
|
'
|
|
|
|
test_expect_success "$from_format -> $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 <input &&
|
|
test_migration repo "$to_format"
|
|
'
|
|
|
|
test_expect_success "$from_format -> $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
|
|
)
|
|
'
|
|
|
|
test_expect_success "$from_format -> $to_format: skip reflog with --skip-reflog" '
|
|
test_when_finished "rm -rf repo" &&
|
|
git init --ref-format=$from_format repo &&
|
|
test_commit -C repo initial &&
|
|
# we see that the repository contains reflogs.
|
|
git -C repo reflog --all >reflogs &&
|
|
test_line_count = 2 reflogs &&
|
|
test_migration repo "$to_format" true --no-reflog &&
|
|
# there should be no reflogs post migration.
|
|
git -C repo reflog --all >reflogs &&
|
|
test_must_be_empty reflogs
|
|
'
|
|
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 &&
|
|
test_commit -C repo second &&
|
|
printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
|
|
git -C repo update-ref --stdin <stdin &&
|
|
test_migration repo reftable true
|
|
'
|
|
|
|
test_expect_success 'migrating from files format deletes backend files' '
|
|
test_when_finished "rm -rf repo" &&
|
|
git init --ref-format=files repo &&
|
|
test_commit -C repo first &&
|
|
git -C repo pack-refs --all &&
|
|
test_commit -C repo second &&
|
|
git -C repo update-ref ORIG_HEAD HEAD &&
|
|
git -C repo rev-parse HEAD >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
|