|
10 | 10 | * Copyright (c) 2006 Shawn O. Pearce
|
11 | 11 | */
|
12 | 12 | #define USE_THE_REPOSITORY_VARIABLE
|
| 13 | +#include "git-compat-util.h" |
13 | 14 | #include "builtin.h"
|
14 | 15 | #include "abspath.h"
|
15 | 16 | #include "date.h"
|
|
41 | 42 | #include "hook.h"
|
42 | 43 | #include "setup.h"
|
43 | 44 | #include "trace2.h"
|
| 45 | +#include "copy.h" |
| 46 | +#include "dir.h" |
44 | 47 |
|
45 | 48 | #define FAILED_RUN "failed to run %s"
|
46 | 49 |
|
@@ -1141,17 +1144,19 @@ static int write_loose_object_to_stdin(const struct object_id *oid,
|
1141 | 1144 | return ++(d->count) > d->batch_size;
|
1142 | 1145 | }
|
1143 | 1146 |
|
1144 |
| -static const char *object_dir = NULL; |
| 1147 | +static const char *shared_object_dir = NULL; |
1145 | 1148 |
|
1146 | 1149 | static int pack_loose(struct maintenance_run_opts *opts)
|
1147 | 1150 | {
|
1148 | 1151 | struct repository *r = the_repository;
|
1149 | 1152 | int result = 0;
|
1150 | 1153 | struct write_loose_object_data data;
|
1151 | 1154 | struct child_process pack_proc = CHILD_PROCESS_INIT;
|
| 1155 | + const char *object_dir = r->objects->odb->path; |
1152 | 1156 |
|
1153 |
| - if (!object_dir) |
1154 |
| - object_dir = r->objects->odb->path; |
| 1157 | + /* If set, use the shared object directory. */ |
| 1158 | + if (shared_object_dir) |
| 1159 | + object_dir = shared_object_dir; |
1155 | 1160 |
|
1156 | 1161 | /*
|
1157 | 1162 | * Do not start pack-objects process
|
@@ -1345,6 +1350,186 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
|
1345 | 1350 | return 0;
|
1346 | 1351 | }
|
1347 | 1352 |
|
| 1353 | +static void link_or_copy_or_die(const char *src, const char *dst) |
| 1354 | +{ |
| 1355 | + if (!link(src, dst)) |
| 1356 | + return; |
| 1357 | + |
| 1358 | + /* Use copy operation if src and dst are on different file systems. */ |
| 1359 | + if (errno != EXDEV) |
| 1360 | + warning_errno(_("failed to link '%s' to '%s'"), src, dst); |
| 1361 | + |
| 1362 | + if (copy_file(dst, src, 0444)) |
| 1363 | + die_errno(_("failed to copy '%s' to '%s'"), src, dst); |
| 1364 | +} |
| 1365 | + |
| 1366 | +static void rename_or_copy_or_die(const char *src, const char *dst) |
| 1367 | +{ |
| 1368 | + if (!rename(src, dst)) |
| 1369 | + return; |
| 1370 | + |
| 1371 | + /* Use copy and delete if src and dst are on different file systems. */ |
| 1372 | + if (errno != EXDEV) |
| 1373 | + warning_errno(_("failed to move '%s' to '%s'"), src, dst); |
| 1374 | + |
| 1375 | + if (copy_file(dst, src, 0444)) |
| 1376 | + die_errno(_("failed to copy '%s' to '%s'"), src, dst); |
| 1377 | + |
| 1378 | + if (unlink(src)) |
| 1379 | + die_errno(_("failed to delete '%s'"), src); |
| 1380 | +} |
| 1381 | + |
| 1382 | +static void migrate_pack(const char *srcdir, const char *dstdir, |
| 1383 | + const char *pack_filename) |
| 1384 | +{ |
| 1385 | + size_t basenamelen, srclen, dstlen; |
| 1386 | + struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT; |
| 1387 | + struct { |
| 1388 | + const char *ext; |
| 1389 | + unsigned move:1; |
| 1390 | + } files[] = { |
| 1391 | + {".pack", 0}, |
| 1392 | + {".keep", 0}, |
| 1393 | + {".rev", 0}, |
| 1394 | + {".idx", 1}, /* The index file must be atomically moved last. */ |
| 1395 | + }; |
| 1396 | + |
| 1397 | + trace2_region_enter("maintenance", "migrate_pack", the_repository); |
| 1398 | + |
| 1399 | + basenamelen = strlen(pack_filename) - 5; /* .pack */ |
| 1400 | + strbuf_addstr(&src, srcdir); |
| 1401 | + strbuf_addch(&src, '/'); |
| 1402 | + strbuf_add(&src, pack_filename, basenamelen); |
| 1403 | + strbuf_addstr(&src, ".idx"); |
| 1404 | + |
| 1405 | + /* A pack without an index file is not yet ready to be migrated. */ |
| 1406 | + if (!file_exists(src.buf)) |
| 1407 | + goto cleanup; |
| 1408 | + |
| 1409 | + strbuf_setlen(&src, src.len - 4 /* .idx */); |
| 1410 | + strbuf_addstr(&dst, dstdir); |
| 1411 | + strbuf_addch(&dst, '/'); |
| 1412 | + strbuf_add(&dst, pack_filename, basenamelen); |
| 1413 | + |
| 1414 | + srclen = src.len; |
| 1415 | + dstlen = dst.len; |
| 1416 | + |
| 1417 | + /* Move or copy files from the source directory to the destination. */ |
| 1418 | + for (size_t i = 0; i < ARRAY_SIZE(files); i++) { |
| 1419 | + strbuf_setlen(&src, srclen); |
| 1420 | + strbuf_addstr(&src, files[i].ext); |
| 1421 | + |
| 1422 | + if (!file_exists(src.buf)) |
| 1423 | + continue; |
| 1424 | + |
| 1425 | + strbuf_setlen(&dst, dstlen); |
| 1426 | + strbuf_addstr(&dst, files[i].ext); |
| 1427 | + |
| 1428 | + if (files[i].move) |
| 1429 | + rename_or_copy_or_die(src.buf, dst.buf); |
| 1430 | + else |
| 1431 | + link_or_copy_or_die(src.buf, dst.buf); |
| 1432 | + } |
| 1433 | + |
| 1434 | + /* |
| 1435 | + * Now the pack and all associated files exist at the destination we can |
| 1436 | + * now clean up the files in the source directory. |
| 1437 | + */ |
| 1438 | + for (size_t i = 0; i < ARRAY_SIZE(files); i++) { |
| 1439 | + /* Files that were moved rather than copied have no clean up. */ |
| 1440 | + if (files[i].move) |
| 1441 | + continue; |
| 1442 | + |
| 1443 | + strbuf_setlen(&src, srclen); |
| 1444 | + strbuf_addstr(&src, files[i].ext); |
| 1445 | + |
| 1446 | + /* Files that never existed in originally have no clean up.*/ |
| 1447 | + if (!file_exists(src.buf)) |
| 1448 | + continue; |
| 1449 | + |
| 1450 | + if (unlink(src.buf)) |
| 1451 | + warning_errno(_("failed to delete '%s'"), src.buf); |
| 1452 | + } |
| 1453 | + |
| 1454 | +cleanup: |
| 1455 | + strbuf_release(&src); |
| 1456 | + strbuf_release(&dst); |
| 1457 | + |
| 1458 | + trace2_region_leave("maintenance", "migrate_pack", the_repository); |
| 1459 | +} |
| 1460 | + |
| 1461 | +static void move_pack_to_shared_cache(const char *full_path, size_t full_path_len, |
| 1462 | + const char *file_name, void *data) |
| 1463 | +{ |
| 1464 | + char *srcdir; |
| 1465 | + const char *dstdir = (const char *)data; |
| 1466 | + |
| 1467 | + /* We only care about the actual pack files here. |
| 1468 | + * The associated .idx, .keep, .rev files will be copied in tandem |
| 1469 | + * with the pack file, with the index file being moved last. |
| 1470 | + * The original locations of the non-index files will only deleted |
| 1471 | + * once all other files have been copied/moved. |
| 1472 | + */ |
| 1473 | + if (!ends_with(file_name, ".pack")) |
| 1474 | + return; |
| 1475 | + |
| 1476 | + srcdir = xstrndup(full_path, full_path_len - strlen(file_name) - 1); |
| 1477 | + |
| 1478 | + migrate_pack(srcdir, dstdir, file_name); |
| 1479 | + |
| 1480 | + free(srcdir); |
| 1481 | +} |
| 1482 | + |
| 1483 | +static int move_loose_object_to_shared_cache(const struct object_id *oid, |
| 1484 | + const char *path, |
| 1485 | + UNUSED void *data) |
| 1486 | +{ |
| 1487 | + struct stat st; |
| 1488 | + struct strbuf dst = STRBUF_INIT; |
| 1489 | + char *hex = oid_to_hex(oid); |
| 1490 | + |
| 1491 | + strbuf_addf(&dst, "%s/%.2s/", shared_object_dir, hex); |
| 1492 | + |
| 1493 | + if (stat(dst.buf, &st)) { |
| 1494 | + if (mkdir(dst.buf, 0777)) |
| 1495 | + die_errno(_("failed to create directory '%s'"), dst.buf); |
| 1496 | + } else if (!S_ISDIR(st.st_mode)) |
| 1497 | + die(_("expected '%s' to be a directory"), dst.buf); |
| 1498 | + |
| 1499 | + strbuf_addstr(&dst, hex+2); |
| 1500 | + rename_or_copy_or_die(path, dst.buf); |
| 1501 | + |
| 1502 | + strbuf_release(&dst); |
| 1503 | + return 0; |
| 1504 | +} |
| 1505 | + |
| 1506 | +static int maintenance_task_cache_local_objs(UNUSED struct maintenance_run_opts *opts, |
| 1507 | + UNUSED struct gc_config *cfg) |
| 1508 | +{ |
| 1509 | + struct strbuf dstdir = STRBUF_INIT; |
| 1510 | + struct repository *r = the_repository; |
| 1511 | + |
| 1512 | + /* This task is only applicable with a VFS/Scalar shared cache. */ |
| 1513 | + if (!shared_object_dir) |
| 1514 | + return 0; |
| 1515 | + |
| 1516 | + /* If the dest is the same as the local odb path then we do nothing. */ |
| 1517 | + if (!fspathcmp(r->objects->odb->path, shared_object_dir)) |
| 1518 | + goto cleanup; |
| 1519 | + |
| 1520 | + strbuf_addf(&dstdir, "%s/pack", shared_object_dir); |
| 1521 | + |
| 1522 | + for_each_file_in_pack_dir(r->objects->odb->path, move_pack_to_shared_cache, |
| 1523 | + dstdir.buf); |
| 1524 | + |
| 1525 | + for_each_loose_object(move_loose_object_to_shared_cache, NULL, |
| 1526 | + FOR_EACH_OBJECT_LOCAL_ONLY); |
| 1527 | + |
| 1528 | +cleanup: |
| 1529 | + strbuf_release(&dstdir); |
| 1530 | + return 0; |
| 1531 | +} |
| 1532 | + |
1348 | 1533 | typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
|
1349 | 1534 | struct gc_config *cfg);
|
1350 | 1535 |
|
@@ -1374,6 +1559,7 @@ enum maintenance_task_label {
|
1374 | 1559 | TASK_GC,
|
1375 | 1560 | TASK_COMMIT_GRAPH,
|
1376 | 1561 | TASK_PACK_REFS,
|
| 1562 | + TASK_CACHE_LOCAL_OBJS, |
1377 | 1563 |
|
1378 | 1564 | /* Leave as final value */
|
1379 | 1565 | TASK__COUNT
|
@@ -1410,6 +1596,10 @@ static struct maintenance_task tasks[] = {
|
1410 | 1596 | maintenance_task_pack_refs,
|
1411 | 1597 | pack_refs_condition,
|
1412 | 1598 | },
|
| 1599 | + [TASK_CACHE_LOCAL_OBJS] = { |
| 1600 | + "cache-local-objects", |
| 1601 | + maintenance_task_cache_local_objs, |
| 1602 | + }, |
1413 | 1603 | };
|
1414 | 1604 |
|
1415 | 1605 | static int compare_tasks_by_selection(const void *a_, const void *b_)
|
@@ -1504,6 +1694,8 @@ static void initialize_maintenance_strategy(void)
|
1504 | 1694 | tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
|
1505 | 1695 | tasks[TASK_PACK_REFS].enabled = 1;
|
1506 | 1696 | tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
|
| 1697 | + tasks[TASK_CACHE_LOCAL_OBJS].enabled = 1; |
| 1698 | + tasks[TASK_CACHE_LOCAL_OBJS].schedule = SCHEDULE_WEEKLY; |
1507 | 1699 | }
|
1508 | 1700 | }
|
1509 | 1701 |
|
@@ -1620,8 +1812,8 @@ static int maintenance_run(int argc, const char **argv, const char *prefix)
|
1620 | 1812 | */
|
1621 | 1813 | if (!git_config_get_value("gvfs.sharedcache", &tmp_obj_dir) &&
|
1622 | 1814 | tmp_obj_dir) {
|
1623 |
| - object_dir = xstrdup(tmp_obj_dir); |
1624 |
| - setenv(DB_ENVIRONMENT, object_dir, 1); |
| 1815 | + shared_object_dir = xstrdup(tmp_obj_dir); |
| 1816 | + setenv(DB_ENVIRONMENT, shared_object_dir, 1); |
1625 | 1817 | }
|
1626 | 1818 |
|
1627 | 1819 | ret = maintenance_run_tasks(&opts, &cfg);
|
|
0 commit comments