Skip to content

Commit 5634446

Browse files
committed
Merge branch 'kn/reflog-migration' into next
"git refs migrate" learned to also migrate the reflog data across backends. * kn/reflog-migration: 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 d491657 + 246cebe commit 5634446

File tree

7 files changed

+329
-121
lines changed

7 files changed

+329
-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: 124 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,36 @@ 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 *what = flags & REF_LOG_ONLY ? "reflog for pseudoref" : "pseudoref";
1260+
strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
1261+
return 0;
1262+
} else if ((new_oid && !is_null_oid(new_oid)) ?
1263+
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :
1264+
!refname_is_safe(refname)) {
1265+
const char *what = flags & REF_LOG_ONLY ? "reflog with bad name" : "ref with bad name";
1266+
strbuf_addf(err, _("refusing to update %s '%s'"), what, refname);
1267+
return 0;
1268+
}
1269+
1270+
return 1;
1271+
}
1272+
12461273
int ref_transaction_update(struct ref_transaction *transaction,
12471274
const char *refname,
12481275
const struct object_id *new_oid,
@@ -1260,21 +1287,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
12601287
return -1;
12611288
}
12621289

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);
1290+
if (!transaction_refname_valid(refname, new_oid, flags, err))
12691291
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-
}
12781292

12791293
if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS)
12801294
BUG("illegal flags 0x%x passed to ref_transaction_update()", flags);
@@ -1291,7 +1305,38 @@ int ref_transaction_update(struct ref_transaction *transaction,
12911305

12921306
ref_transaction_add_update(transaction, refname, flags,
12931307
new_oid, old_oid, new_target,
1294-
old_target, msg);
1308+
old_target, NULL, msg);
1309+
1310+
return 0;
1311+
}
1312+
1313+
int ref_transaction_update_reflog(struct ref_transaction *transaction,
1314+
const char *refname,
1315+
const struct object_id *new_oid,
1316+
const struct object_id *old_oid,
1317+
const char *committer_info, unsigned int flags,
1318+
const char *msg, unsigned int index,
1319+
struct strbuf *err)
1320+
{
1321+
struct ref_update *update;
1322+
1323+
assert(err);
1324+
1325+
flags |= REF_LOG_ONLY | REF_NO_DEREF;
1326+
1327+
if (!transaction_refname_valid(refname, new_oid, flags, err))
1328+
return -1;
1329+
1330+
update = ref_transaction_add_update(transaction, refname, flags,
1331+
new_oid, old_oid, NULL, NULL,
1332+
committer_info, msg);
1333+
/*
1334+
* While we do set the old_oid value, we unset the flag to skip
1335+
* old_oid verification which only makes sense for refs.
1336+
*/
1337+
update->flags &= ~REF_HAVE_OLD;
1338+
update->index = index;
1339+
12951340
return 0;
12961341
}
12971342

@@ -2711,6 +2756,7 @@ struct migration_data {
27112756
struct ref_store *old_refs;
27122757
struct ref_transaction *transaction;
27132758
struct strbuf *errbuf;
2759+
struct strbuf sb;
27142760
};
27152761

27162762
static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid,
@@ -2743,6 +2789,52 @@ static int migrate_one_ref(const char *refname, const char *referent UNUSED, con
27432789
return ret;
27442790
}
27452791

2792+
struct reflog_migration_data {
2793+
unsigned int index;
2794+
const char *refname;
2795+
struct ref_store *old_refs;
2796+
struct ref_transaction *transaction;
2797+
struct strbuf *errbuf;
2798+
struct strbuf *sb;
2799+
};
2800+
2801+
static int migrate_one_reflog_entry(struct object_id *old_oid,
2802+
struct object_id *new_oid,
2803+
const char *committer,
2804+
timestamp_t timestamp, int tz,
2805+
const char *msg, void *cb_data)
2806+
{
2807+
struct reflog_migration_data *data = cb_data;
2808+
const char *date;
2809+
int ret;
2810+
2811+
date = show_date(timestamp, tz, DATE_MODE(NORMAL));
2812+
strbuf_reset(data->sb);
2813+
/* committer contains name and email */
2814+
strbuf_addstr(data->sb, fmt_ident("", committer, WANT_BLANK_IDENT, date, 0));
2815+
2816+
ret = ref_transaction_update_reflog(data->transaction, data->refname,
2817+
new_oid, old_oid, data->sb->buf,
2818+
REF_HAVE_NEW | REF_HAVE_OLD, msg,
2819+
data->index++, data->errbuf);
2820+
return ret;
2821+
}
2822+
2823+
static int migrate_one_reflog(const char *refname, void *cb_data)
2824+
{
2825+
struct migration_data *migration_data = cb_data;
2826+
struct reflog_migration_data data = {
2827+
.refname = refname,
2828+
.old_refs = migration_data->old_refs,
2829+
.transaction = migration_data->transaction,
2830+
.errbuf = migration_data->errbuf,
2831+
.sb = &migration_data->sb,
2832+
};
2833+
2834+
return refs_for_each_reflog_ent(migration_data->old_refs, refname,
2835+
migrate_one_reflog_entry, &data);
2836+
}
2837+
27462838
static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf)
27472839
{
27482840
struct strbuf from_buf = STRBUF_INIT, to_buf = STRBUF_INIT;
@@ -2809,13 +2901,6 @@ static int move_files(const char *from_path, const char *to_path, struct strbuf
28092901
return ret;
28102902
}
28112903

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-
28192904
static int has_worktrees(void)
28202905
{
28212906
struct worktree **worktrees = get_worktrees();
@@ -2840,8 +2925,9 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28402925
struct ref_store *old_refs = NULL, *new_refs = NULL;
28412926
struct ref_transaction *transaction = NULL;
28422927
struct strbuf new_gitdir = STRBUF_INIT;
2843-
struct migration_data data;
2844-
size_t reflog_count = 0;
2928+
struct migration_data data = {
2929+
.sb = STRBUF_INIT,
2930+
};
28452931
int did_migrate_refs = 0;
28462932
int ret;
28472933

@@ -2853,21 +2939,6 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28532939

28542940
old_refs = get_main_ref_store(repo);
28552941

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-
28712942
/*
28722943
* Worktrees complicate the migration because every worktree has a
28732944
* separate ref storage. While it should be feasible to implement, this
@@ -2893,17 +2964,21 @@ int repo_migrate_ref_storage_format(struct repository *repo,
28932964
* This operation is safe as we do not yet modify the main
28942965
* repository.
28952966
*
2896-
* 3. If we're in dry-run mode then we are done and can hand over the
2967+
* 3. Enumerate all reflogs and write them into the new ref storage.
2968+
* This operation is safe as we do not yet modify the main
2969+
* repository.
2970+
*
2971+
* 4. If we're in dry-run mode then we are done and can hand over the
28972972
* directory to the caller for inspection. If not, we now start
28982973
* with the destructive part.
28992974
*
2900-
* 4. Delete the old ref storage from disk. As we have a copy of refs
2975+
* 5. Delete the old ref storage from disk. As we have a copy of refs
29012976
* in the new ref storage it's okay(ish) if we now get interrupted
29022977
* as there is an equivalent copy of all refs available.
29032978
*
2904-
* 5. Move the new ref storage files into place.
2979+
* 6. Move the new ref storage files into place.
29052980
*
2906-
* 6. Change the repository format to the new ref format.
2981+
* 7. Change the repository format to the new ref format.
29072982
*/
29082983
strbuf_addf(&new_gitdir, "%s/%s", old_refs->gitdir, "ref_migration.XXXXXX");
29092984
if (!mkdtemp(new_gitdir.buf)) {
@@ -2945,6 +3020,10 @@ int repo_migrate_ref_storage_format(struct repository *repo,
29453020
if (ret < 0)
29463021
goto done;
29473022

3023+
ret = refs_for_each_reflog(old_refs, migrate_one_reflog, &data);
3024+
if (ret < 0)
3025+
goto done;
3026+
29483027
ret = ref_transaction_commit(transaction, errbuf);
29493028
if (ret < 0)
29503029
goto done;
@@ -3020,6 +3099,7 @@ int repo_migrate_ref_storage_format(struct repository *repo,
30203099
}
30213100
ref_transaction_free(transaction);
30223101
strbuf_release(&new_gitdir);
3102+
strbuf_release(&data.sb);
30233103
return ret;
30243104
}
30253105

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)