refs: implement removal of ref storages

We're about to introduce logic to migrate ref storages. One part of the migration will be to delete the files that are part of the old ref storage format. We don't yet have a way to delete such data generically across ref backends though. Implement a new `delete` callback and expose it via a new `ref_storage_delete()` function. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 6, 2024 at 07:29 UTC 64a6dd8ffc2f120fc13e438af3236aa00cebc241
6 files changed +146
refs.c
+5
@@ -1861,6 +1861,11 @@ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *e
1861 return refs->be->create_on_disk(refs, flags, err);
1862 }
1863
1864 +int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err)
1865 +{
1866 + return refs->be->remove_on_disk(refs, err);
1867 +}
1868 +
1869 int repo_resolve_gitlink_ref(struct repository *r,
1870 const char *submodule, const char *refname,
1871 struct object_id *oid)
refs.h
+5
@@ -129,6 +129,11 @@ int ref_store_create_on_disk(struct ref_store *refs, int flags, struct strbuf *e
129 */
130 void ref_store_release(struct ref_store *ref_store);
131
132 +/*
133 + * Remove the ref store from disk. This deletes all associated data.
134 + */
135 +int ref_store_remove_on_disk(struct ref_store *refs, struct strbuf *err);
136 +
137 /*
138 * Return the peeled value of the oid currently being iterated via
139 * for_each_ref(), etc. This is equivalent to calling:
refs/files-backend.c
+62
@@ -3342,11 +3342,73 @@ static int files_ref_store_create_on_disk(struct ref_store *ref_store,
3342 return 0;
3343 }
3344
3345 +struct remove_one_root_ref_data {
3346 + const char *gitdir;
3347 + struct strbuf *err;
3348 +};
3349 +
3350 +static int remove_one_root_ref(const char *refname,
3351 + void *cb_data)
3352 +{
3353 + struct remove_one_root_ref_data *data = cb_data;
3354 + struct strbuf buf = STRBUF_INIT;
3355 + int ret = 0;
3356 +
3357 + strbuf_addf(&buf, "%s/%s", data->gitdir, refname);
3358 +
3359 + ret = unlink(buf.buf);
3360 + if (ret < 0)
3361 + strbuf_addf(data->err, "could not delete %s: %s\n",
3362 + refname, strerror(errno));
3363 +
3364 + strbuf_release(&buf);
3365 + return ret;
3366 +}
3367 +
3368 +static int files_ref_store_remove_on_disk(struct ref_store *ref_store,
3369 + struct strbuf *err)
3370 +{
3371 + struct files_ref_store *refs =
3372 + files_downcast(ref_store, REF_STORE_WRITE, "remove");
3373 + struct remove_one_root_ref_data data = {
3374 + .gitdir = refs->base.gitdir,
3375 + .err = err,
3376 + };
3377 + struct strbuf sb = STRBUF_INIT;
3378 + int ret = 0;
3379 +
3380 + strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
3381 + if (remove_dir_recursively(&sb, 0) < 0) {
3382 + strbuf_addf(err, "could not delete refs: %s",
3383 + strerror(errno));
3384 + ret = -1;
3385 + }
3386 + strbuf_reset(&sb);
3387 +
3388 + strbuf_addf(&sb, "%s/logs", refs->base.gitdir);
3389 + if (remove_dir_recursively(&sb, 0) < 0) {
3390 + strbuf_addf(err, "could not delete logs: %s",
3391 + strerror(errno));
3392 + ret = -1;
3393 + }
3394 + strbuf_reset(&sb);
3395 +
3396 + if (for_each_root_ref(refs, remove_one_root_ref, &data) < 0)
3397 + ret = -1;
3398 +
3399 + if (ref_store_remove_on_disk(refs->packed_ref_store, err) < 0)
3400 + ret = -1;
3401 +
3402 + strbuf_release(&sb);
3403 + return ret;
3404 +}
3405 +
3406 struct ref_storage_be refs_be_files = {
3407 .name = "files",
3408 .init = files_ref_store_init,
3409 .release = files_ref_store_release,
3410 .create_on_disk = files_ref_store_create_on_disk,
3411 + .remove_on_disk = files_ref_store_remove_on_disk,
3412
3413 .transaction_prepare = files_transaction_prepare,
3414 .transaction_finish = files_transaction_finish,
refs/packed-backend.c
+15
@@ -1,5 +1,6 @@
1 #include "../git-compat-util.h"
2 #include "../config.h"
3 +#include "../dir.h"
4 #include "../gettext.h"
5 #include "../hash.h"
6 #include "../hex.h"
@@ -1266,6 +1267,19 @@ static int packed_ref_store_create_on_disk(struct ref_store *ref_store UNUSED,
1267 return 0;
1268 }
1269
1270 +static int packed_ref_store_remove_on_disk(struct ref_store *ref_store,
1271 + struct strbuf *err)
1272 +{
1273 + struct packed_ref_store *refs = packed_downcast(ref_store, 0, "remove");
1274 +
1275 + if (remove_path(refs->path) < 0) {
1276 + strbuf_addstr(err, "could not delete packed-refs");
1277 + return -1;
1278 + }
1279 +
1280 + return 0;
1281 +}
1282 +
1283 /*
1284 * Write the packed refs from the current snapshot to the packed-refs
1285 * tempfile, incorporating any changes from `updates`. `updates` must
@@ -1724,6 +1738,7 @@ struct ref_storage_be refs_be_packed = {
1738 .init = packed_ref_store_init,
1739 .release = packed_ref_store_release,
1740 .create_on_disk = packed_ref_store_create_on_disk,
1741 + .remove_on_disk = packed_ref_store_remove_on_disk,
1742
1743 .transaction_prepare = packed_transaction_prepare,
1744 .transaction_finish = packed_transaction_finish,
refs/refs-internal.h
+7
@@ -517,6 +517,12 @@ typedef int ref_store_create_on_disk_fn(struct ref_store *refs,
517 int flags,
518 struct strbuf *err);
519
520 +/*
521 + * Remove the reference store from disk.
522 + */
523 +typedef int ref_store_remove_on_disk_fn(struct ref_store *refs,
524 + struct strbuf *err);
525 +
526 typedef int ref_transaction_prepare_fn(struct ref_store *refs,
527 struct ref_transaction *transaction,
528 struct strbuf *err);
@@ -649,6 +655,7 @@ struct ref_storage_be {
655 ref_store_init_fn *init;
656 ref_store_release_fn *release;
657 ref_store_create_on_disk_fn *create_on_disk;
658 + ref_store_remove_on_disk_fn *remove_on_disk;
659
660 ref_transaction_prepare_fn *transaction_prepare;
661 ref_transaction_finish_fn *transaction_finish;
refs/reftable-backend.c
+52
@@ -1,6 +1,7 @@
1 #include "../git-compat-util.h"
2 #include "../abspath.h"
3 #include "../chdir-notify.h"
4 +#include "../dir.h"
5 #include "../environment.h"
6 #include "../gettext.h"
7 #include "../hash.h"
@@ -343,6 +344,56 @@ static int reftable_be_create_on_disk(struct ref_store *ref_store,
344 return 0;
345 }
346
347 +static int reftable_be_remove_on_disk(struct ref_store *ref_store,
348 + struct strbuf *err)
349 +{
350 + struct reftable_ref_store *refs =
351 + reftable_be_downcast(ref_store, REF_STORE_WRITE, "remove");
352 + struct strbuf sb = STRBUF_INIT;
353 + int ret = 0;
354 +
355 + /*
356 + * Release the ref store such that all stacks are closed. This is
357 + * required so that the "tables.list" file is not open anymore, which
358 + * would otherwise make it impossible to remove the file on Windows.
359 + */
360 + reftable_be_release(ref_store);
361 +
362 + strbuf_addf(&sb, "%s/reftable", refs->base.gitdir);
363 + if (remove_dir_recursively(&sb, 0) < 0) {
364 + strbuf_addf(err, "could not delete reftables: %s",
365 + strerror(errno));
366 + ret = -1;
367 + }
368 + strbuf_reset(&sb);
369 +
370 + strbuf_addf(&sb, "%s/HEAD", refs->base.gitdir);
371 + if (unlink(sb.buf) < 0) {
372 + strbuf_addf(err, "could not delete stub HEAD: %s",
373 + strerror(errno));
374 + ret = -1;
375 + }
376 + strbuf_reset(&sb);
377 +
378 + strbuf_addf(&sb, "%s/refs/heads", refs->base.gitdir);
379 + if (unlink(sb.buf) < 0) {
380 + strbuf_addf(err, "could not delete stub heads: %s",
381 + strerror(errno));
382 + ret = -1;
383 + }
384 + strbuf_reset(&sb);
385 +
386 + strbuf_addf(&sb, "%s/refs", refs->base.gitdir);
387 + if (rmdir(sb.buf) < 0) {
388 + strbuf_addf(err, "could not delete refs directory: %s",
389 + strerror(errno));
390 + ret = -1;
391 + }
392 +
393 + strbuf_release(&sb);
394 + return ret;
395 +}
396 +
397 struct reftable_ref_iterator {
398 struct ref_iterator base;
399 struct reftable_ref_store *refs;
@@ -2196,6 +2247,7 @@ struct ref_storage_be refs_be_reftable = {
2247 .init = reftable_be_init,
2248 .release = reftable_be_release,
2249 .create_on_disk = reftable_be_create_on_disk,
2250 + .remove_on_disk = reftable_be_remove_on_disk,
2251
2252 .transaction_prepare = reftable_be_transaction_prepare,
2253 .transaction_finish = reftable_be_transaction_finish,