Skip to content

Commit f0fde56

Browse files
pks-tgitster
authored andcommitted
refs: fix invalid old object IDs when migrating reflogs
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent ec4d8b7 commit f0fde56

File tree

6 files changed

+55
-13
lines changed

6 files changed

+55
-13
lines changed

refs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,8 @@ int ref_transaction_update_reflog(struct ref_transaction *transaction,
13851385

13861386
assert(err);
13871387

1388-
flags = REF_HAVE_OLD | REF_HAVE_NEW | REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF;
1388+
flags = REF_HAVE_OLD | REF_HAVE_NEW | REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF |
1389+
REF_LOG_USE_PROVIDED_OIDS;
13891390

13901391
if (!transaction_refname_valid(refname, new_oid, flags, err))
13911392
return -1;

refs.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,13 +760,20 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
760760
*/
761761
#define REF_SKIP_CREATE_REFLOG (1 << 12)
762762

763+
/*
764+
* When writing a REF_LOG_ONLY record, use the old and new object IDs provided
765+
* in the update instead of resolving the old object ID. The caller must also
766+
* set both REF_HAVE_OLD and REF_HAVE_NEW.
767+
*/
768+
#define REF_LOG_USE_PROVIDED_OIDS (1 << 13)
769+
763770
/*
764771
* Bitmask of all of the flags that are allowed to be passed in to
765772
* ref_transaction_update() and friends:
766773
*/
767774
#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
768775
(REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \
769-
REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG)
776+
REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG | REF_LOG_USE_PROVIDED_OIDS)
770777

771778
/*
772779
* Add a reference update to transaction. `new_oid` is the value that

refs/files-backend.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3010,6 +3010,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
30103010
struct ref_lock *lock,
30113011
struct strbuf *err)
30123012
{
3013+
struct object_id *old_oid = &lock->old_oid;
3014+
3015+
if (update->flags & REF_LOG_USE_PROVIDED_OIDS) {
3016+
if (!(update->flags & REF_HAVE_OLD) ||
3017+
!(update->flags & REF_HAVE_NEW) ||
3018+
!(update->flags & REF_LOG_ONLY)) {
3019+
strbuf_addf(err, _("trying to write reflog for '%s'"
3020+
"with incomplete values"), update->refname);
3021+
return REF_TRANSACTION_ERROR_GENERIC;
3022+
}
3023+
3024+
old_oid = &update->old_oid;
3025+
}
3026+
30133027
if (update->new_target) {
30143028
/*
30153029
* We want to get the resolved OID for the target, to ensure
@@ -3027,7 +3041,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs,
30273041
}
30283042
}
30293043

3030-
if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid,
3044+
if (files_log_ref_write(refs, lock->ref_name, old_oid,
30313045
&update->new_oid, update->committer_info,
30323046
update->msg, update->flags, err)) {
30333047
char *old_msg = strbuf_detach(err, NULL);

refs/reftable-backend.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,20 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor
10961096
if (ret)
10971097
return REF_TRANSACTION_ERROR_GENERIC;
10981098

1099+
if (u->flags & REF_LOG_USE_PROVIDED_OIDS) {
1100+
if (!(u->flags & REF_HAVE_OLD) ||
1101+
!(u->flags & REF_HAVE_NEW) ||
1102+
!(u->flags & REF_LOG_ONLY)) {
1103+
strbuf_addf(err, _("trying to write reflog for '%s'"
1104+
"with incomplete values"), u->refname);
1105+
return REF_TRANSACTION_ERROR_GENERIC;
1106+
}
1107+
1108+
if (queue_transaction_update(refs, tx_data, u, &u->old_oid, err))
1109+
return REF_TRANSACTION_ERROR_GENERIC;
1110+
return 0;
1111+
}
1112+
10991113
/* Verify that the new object ID is valid. */
11001114
if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) &&
11011115
!(u->flags & REF_SKIP_OID_VERIFICATION) &&

t/t1421-reflog-write.sh

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,9 @@ test_expect_success 'simple writes' '
9999
EOF
100100
101101
git reflog write refs/heads/something $COMMIT_OID $COMMIT_OID second &&
102-
# Note: the old object ID of the second reflog entry is broken.
103-
# This will be fixed in subsequent commits.
104102
test_reflog_matches . refs/heads/something <<-EOF
105103
$ZERO_OID $COMMIT_OID $SIGNATURE first
106-
$ZERO_OID $COMMIT_OID $SIGNATURE second
104+
$COMMIT_OID $COMMIT_OID $SIGNATURE second
107105
EOF
108106
)
109107
'

t/t1460-refs-migrate.sh

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
77

88
. ./test-lib.sh
99

10+
print_all_reflog_entries () {
11+
repo=$1 &&
12+
test-tool -C "$repo" ref-store main for-each-reflog >reflogs &&
13+
while read reflog
14+
do
15+
echo "REFLOG: $reflog" &&
16+
test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" ||
17+
return 1
18+
done <reflogs
19+
}
20+
1021
# Migrate the provided repository from one format to the other and
1122
# verify that the references and logs are migrated over correctly.
1223
# Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]]
@@ -28,8 +39,7 @@ test_migration () {
2839
--format='%(refname) %(objectname) %(symref)' >expect &&
2940
if ! $skip_reflog_verify
3041
then
31-
git -C "$repo" reflog --all >expect_logs &&
32-
git -C "$repo" reflog list >expect_log_list
42+
print_all_reflog_entries "$repo" >expect_logs
3343
fi &&
3444

3545
git -C "$repo" refs migrate --ref-format="$format" "$@" &&
@@ -39,10 +49,8 @@ test_migration () {
3949
test_cmp expect actual &&
4050
if ! $skip_reflog_verify
4151
then
42-
git -C "$repo" reflog --all >actual_logs &&
43-
git -C "$repo" reflog list >actual_log_list &&
44-
test_cmp expect_logs actual_logs &&
45-
test_cmp expect_log_list actual_log_list
52+
print_all_reflog_entries "$repo" >actual_logs &&
53+
test_cmp expect_logs actual_logs
4654
fi &&
4755

4856
git -C "$repo" rev-parse --show-ref-format >actual &&
@@ -273,7 +281,7 @@ test_expect_success 'multiple reftable blocks with multiple entries' '
273281
test_commit -C repo second &&
274282
printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin &&
275283
git -C repo update-ref --stdin <stdin &&
276-
test_migration repo reftable
284+
test_migration repo reftable true
277285
'
278286

279287
test_expect_success 'migrating from files format deletes backend files' '

0 commit comments

Comments
 (0)