Skip to content

Commit 6f8ae95

Browse files
committed
Merge branch 'kn/reflog-migration'
"git refs migrate" learned to also migrate the reflog data across backends. * kn/reflog-migration: refs: mark invalid refname message for translation refs: add support for migrating reflogs refs: allow multiple reflog entries for the same refname refs: introduce the `ref_transaction_update_reflog` function refs: add `committer_info` to `ref_transaction_add_update()` refs: extract out refname verification in transactions refs/files: add count field to ref_lock refs: add `index` field to `struct ref_udpate` refs: include committer info in `ref_update` struct
2 parents f74eae3 + 8ddcdc1 commit 6f8ae95

File tree

7 files changed

+337
-121
lines changed

7 files changed

+337
-121
lines changed

Documentation/git-refs.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ KNOWN LIMITATIONS
5757

5858
The ref format migration has several known limitations in its current form:
5959

60-
* It is not possible to migrate repositories that have reflogs.
61-
6260
* It is not possible to migrate repositories that have worktrees.
6361

6462
* There is no way to block concurrent writes to the repository during an

refs.c

Lines changed: 132 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "date.h"
3232
#include "commit.h"
3333
#include "wildmatch.h"
34+
#include "ident.h"
3435

3536
/*
3637
* List of all available backends
@@ -1199,6 +1200,7 @@ void ref_transaction_free(struct ref_transaction *transaction)
11991200

12001201
for (i = 0; i < transaction->nr; i++) {
12011202
free(transaction->updates[i]->msg);
1203+
free(transaction->updates[i]->committer_info);
12021204
free((char *)transaction->updates[i]->new_target);
12031205
free((char *)transaction->updates[i]->old_target);
12041206
free(transaction->updates[i]);
@@ -1213,6 +1215,7 @@ struct ref_update *ref_transaction_add_update(
12131215
const struct object_id *new_oid,
12141216
const struct object_id *old_oid,
12151217
const char *new_target, const char *old_target,
1218+
const char *committer_info,
12161219
const char *msg)
12171220
{
12181221
struct ref_update *update;
@@ -1237,12 +1240,44 @@ struct ref_update *ref_transaction_add_update(
12371240
oidcpy(&update->new_oid, new_oid);
12381241
if ((flags & REF_HAVE_OLD) && old_oid)
12391242
oidcpy(&update->old_oid, old_oid);
1240-
if (!(flags & REF_SKIP_CREATE_REFLOG))
1243+
if (!(flags & REF_SKIP_CREATE_REFLOG)) {
1244+
update->committer_info = xstrdup_or_null(committer_info);
12411245
update->msg = normalize_reflog_message(msg);
1246+
}
12421247

12431248
return update;
12441249
}
12451250

1251+
static int transaction_refname_valid(const char *refname,
1252+
const struct object_id *new_oid,
1253+
unsigned int flags, struct strbuf *err)
1254+
{
1255+
if (flags & REF_SKIP_REFNAME_VERIFICATION)
1256+
return 1;
1257+
1258+
if (is_pseudo_ref(refname)) {
1259+
const char *refusal_msg;
1260+
if (flags & REF_LOG_ONLY)
1261+
refusal_msg = _("refusing to update reflog for pseudoref '%s'");
1262+
else
1263+
refusal_msg = _("refusing to update pseudoref '%s'");
1264+
strbuf_addf(err, refusal_msg, refname);
1265+
return 0;
1266+
} else if ((new_oid && !is_null_oid(new_oid)) ?
1267+
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1268+
!refname_is_safe(refname)) {
1269+
const char *refusal_msg;
1270+
if (flags & REF_LOG_ONLY)
1271+
refusal_msg = _("refusing to update reflog with bad name '%s'");
1272+
else
1273+
refusal_msg = _("refusing to update ref with bad name '%s'");
1274+
strbuf_addf(err, refusal_msg, refname);
1275+
return 0;
1276+
}
1277+
1278+
return 1;
1279+
}
1280+
12461281
int ref_transaction_update(struct ref_transaction *transaction,
12471282
const char *refname,
12481283
const struct object_id *new_oid,
@@ -1260,21 +1295,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
12601295
return -1;
12611296
}
12621297

1263-
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
1264-
((new_oid && !is_null_oid(new_oid)) ?
1265-
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1266-
!refname_is_safe(refname))) {
1267-
strbuf_addf(err, _("refusing to update ref with bad name '%s'"),
1268-
refname);
1298+
if (!transaction_refname_valid(refname, new_oid, flags, err))
12691299
return -1;
1270-
}
1271-
1272-
if (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&
1273-
is_pseudo_ref(refname)) {
1274-
strbuf_addf(err, _("refusing to update pseudoref '%s'"),
1275-
refname);
1276-
return -1;
1277-
}
12781300

12791301
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
12801302
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1291,7 +1313,38 @@ int ref_transaction_update(struct ref_transaction *transaction,
12911313

12921314
ref_transaction_add_update(transaction, refname, flags,
12931315
new_oid, old_oid, new_target,
1294-
old_target, msg);
1316+
old_target, NULL, msg);
1317+
1318+
return 0;
1319+
}
1320+
1321+
int ref_transaction_update_reflog(struct ref_transaction *transaction,
1322+
const char *refname,
1323+
const struct object_id *new_oid,
1324+
const struct object_id *old_oid,
1325+
const char *committer_info, unsigned int flags,
1326+
const char *msg, unsigned int index,
1327+
struct strbuf *err)
1328+
{
1329+
struct ref_update *update;
1330+
1331+
assert(err);
1332+
1333+
flags |= REF_LOG_ONLY | REF_NO_DEREF;
1334+
1335+
if (!transaction_refname_valid(refname, new_oid, flags, err))
1336+
return -1;
1337+
1338+
update = ref_transaction_add_update(transaction, refname, flags,
1339+
new_oid, old_oid, NULL, NULL,
1340+
committer_info, msg);
1341+
/*
1342+
* While we do set the old_oid value, we unset the flag to skip
1343+
* old_oid verification which only makes sense for refs.
1344+
*/
1345+
update->flags &= ~REF_HAVE_OLD;
1346+
update->index = index;
1347+
12951348
return 0;
12961349
}
12971350

@@ -2711,6 +2764,7 @@ struct migration_data {
27112764
struct ref_store *old_refs;
27122765
struct ref_transaction *transaction;
27132766
struct strbuf *errbuf;
2767+
struct strbuf sb;
27142768
};
27152769

27162770
static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
@@ -2743,6 +2797,52 @@ static int migrate_one_ref(const char *refname, const char *referent UNUSED, con
27432797
return ret;
27442798
}
27452799

2800+
struct reflog_migration_data {
2801+
unsigned int index;
2802+
const char *refname;
2803+
struct ref_store *old_refs;
2804+
struct ref_transaction *transaction;
2805+
struct strbuf *errbuf;
2806+
struct strbuf *sb;
2807+
};
2808+
2809+
static int migrate_one_reflog_entry(struct object_id *old_oid,
2810+
struct object_id *new_oid,
2811+
const char *committer,
2812+
timestamp_t timestamp, int tz,
2813+
const char *msg, void *cb_data)
2814+
{
2815+
struct reflog_migration_data *data = cb_data;
2816+
const char *date;
2817+
int ret;
2818+
2819+
date = show_date(timestamp, tz, DATE_MODE(NORMAL));
2820+
strbuf_reset(data->sb);
2821+
/* committer contains name and email */
2822+
strbuf_addstr(data->sb, fmt_ident("", committer, WANT_BLANK_IDENT, date, 0));
2823+
2824+
ret = ref_transaction_update_reflog(data->transaction, data->refname,
2825+
new_oid, old_oid, data->sb->buf,
2826+
REF_HAVE_NEW | REF_HAVE_OLD, msg,
2827+
data->index++, data->errbuf);
2828+
return ret;
2829+
}
2830+
2831+
static int migrate_one_reflog(const char *refname, void *cb_data)
2832+
{
2833+
struct migration_data *migration_data = cb_data;
2834+
struct reflog_migration_data data = {
2835+
.refname = refname,
2836+
.old_refs = migration_data->old_refs,
2837+
.transaction = migration_data->transaction,
2838+
.errbuf = migration_data->errbuf,
2839+
.sb = &migration_data->sb,
2840+
};
2841+
2842+
return refs_for_each_reflog_ent(migration_data->old_refs, refname,
2843+
migrate_one_reflog_entry, &data);
2844+
}
2845+
27462846
static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
27472847
{
27482848
struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
@@ -2809,13 +2909,6 @@ static int move_files(const char *from_path, const char *to_path, struct strbuf
28092909
return ret;
28102910
}
28112911

2812-
static int count_reflogs(const char *reflog UNUSED, void *payload)
2813-
{
2814-
size_t *reflog_count = payload;
2815-
(*reflog_count)++;
2816-
return 0;
2817-
}
2818-
28192912
static int has_worktrees(void)
28202913
{
28212914
struct worktree **worktrees = get_worktrees();
@@ -2840,8 +2933,9 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28402933
struct ref_store *old_refs = NULL, *new_refs = NULL;
28412934
struct ref_transaction *transaction = NULL;
28422935
struct strbuf new_gitdir = STRBUF_INIT;
2843-
struct migration_data data;
2844-
size_t reflog_count = 0;
2936+
struct migration_data data = {
2937+
.sb = STRBUF_INIT,
2938+
};
28452939
int did_migrate_refs = 0;
28462940
int ret;
28472941

@@ -2853,21 +2947,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28532947

28542948
old_refs = get_main_ref_store(repo);
28552949

2856-
/*
2857-
* We do not have any interfaces that would allow us to write many
2858-
* reflog entries. Once we have them we can remove this restriction.
2859-
*/
2860-
if (refs_for_each_reflog(old_refs, count_reflogs, &reflog_count) < 0) {
2861-
strbuf_addstr(errbuf, "cannot count reflogs");
2862-
ret = -1;
2863-
goto done;
2864-
}
2865-
if (reflog_count) {
2866-
strbuf_addstr(errbuf, "migrating reflogs is not supported yet");
2867-
ret = -1;
2868-
goto done;
2869-
}
2870-
28712950
/*
28722951
* Worktrees complicate the migration because every worktree has a
28732952
* separate ref storage. While it should be feasible to implement, this
@@ -2893,17 +2972,21 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28932972
* This operation is safe as we do not yet modify the main
28942973
* repository.
28952974
*
2896-
* 3. If we're in dry-run mode then we are done and can hand over the
2975+
* 3. Enumerate all reflogs and write them into the new ref storage.
2976+
* This operation is safe as we do not yet modify the main
2977+
* repository.
2978+
*
2979+
* 4. If we're in dry-run mode then we are done and can hand over the
28972980
* directory to the caller for inspection. If not, we now start
28982981
* with the destructive part.
28992982
*
2900-
* 4. Delete the old ref storage from disk. As we have a copy of refs
2983+
* 5. Delete the old ref storage from disk. As we have a copy of refs
29012984
* in the new ref storage it's okay(ish) if we now get interrupted
29022985
* as there is an equivalent copy of all refs available.
29032986
*
2904-
* 5. Move the new ref storage files into place.
2987+
* 6. Move the new ref storage files into place.
29052988
*
2906-
* 6. Change the repository format to the new ref format.
2989+
* 7. Change the repository format to the new ref format.
29072990
*/
29082991
strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
29092992
if (!mkdtemp(new_gitdir.buf)) {
@@ -2945,6 +3028,10 @@ int repo_migrate_ref_storage_format(struct repository *repo,
29453028
if (ret < 0)
29463029
goto done;
29473030

3031+
ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
3032+
if (ret < 0)
3033+
goto done;
3034+
29483035
ret = ref_transaction_commit(transaction, errbuf);
29493036
if (ret < 0)
29503037
goto done;
@@ -3020,6 +3107,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
30203107
}
30213108
ref_transaction_free(transaction);
30223109
strbuf_release(&new_gitdir);
3110+
strbuf_release(&data.sb);
30233111
return ret;
30243112
}
30253113

refs.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,20 @@ int ref_transaction_update(struct ref_transaction *transaction,
771771
unsigned int flags, const char *msg,
772772
struct strbuf *err);
773773

774+
/*
775+
* Similar to`ref_transaction_update`, but this function is only for adding
776+
* a reflog update. Supports providing custom committer information. The index
777+
* field can be utiltized to order updates as desired. When not used, the
778+
* updates default to being ordered by refname.
779+
*/
780+
int ref_transaction_update_reflog(struct ref_transaction *transaction,
781+
const char *refname,
782+
const struct object_id *new_oid,
783+
const struct object_id *old_oid,
784+
const char *committer_info, unsigned int flags,
785+
const char *msg, unsigned int index,
786+
struct strbuf *err);
787+
774788
/*
775789
* Add a reference creation to transaction. new_oid is the value that
776790
* the reference should have after the update; it must not be

0 commit comments

Comments
 (0)