|
13 | 13 | #define USE_THE_REPOSITORY_VARIABLE
|
14 | 14 | #define DISABLE_SIGN_COMPARE_WARNINGS
|
15 | 15 |
|
| 16 | +#include "git-compat-util.h" |
16 | 17 | #include "builtin.h"
|
17 | 18 | #include "abspath.h"
|
| 19 | +#include "copy.h" |
18 | 20 | #include "date.h"
|
19 | 21 | #include "dir.h"
|
20 | 22 | #include "environment.h"
|
@@ -1524,6 +1526,186 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
|
1524 | 1526 | return 0;
|
1525 | 1527 | }
|
1526 | 1528 |
|
| 1529 | +static void link_or_copy_or_die(const char *src, const char *dst) |
| 1530 | +{ |
| 1531 | + if (!link(src, dst)) |
| 1532 | + return; |
| 1533 | + |
| 1534 | + /* Use copy operation if src and dst are on different file systems. */ |
| 1535 | + if (errno != EXDEV) |
| 1536 | + warning_errno(_("failed to link '%s' to '%s'"), src, dst); |
| 1537 | + |
| 1538 | + if (copy_file(dst, src, 0444)) |
| 1539 | + die_errno(_("failed to copy '%s' to '%s'"), src, dst); |
| 1540 | +} |
| 1541 | + |
| 1542 | +static void rename_or_copy_or_die(const char *src, const char *dst) |
| 1543 | +{ |
| 1544 | + if (!rename(src, dst)) |
| 1545 | + return; |
| 1546 | + |
| 1547 | + /* Use copy and delete if src and dst are on different file systems. */ |
| 1548 | + if (errno != EXDEV) |
| 1549 | + warning_errno(_("failed to move '%s' to '%s'"), src, dst); |
| 1550 | + |
| 1551 | + if (copy_file(dst, src, 0444)) |
| 1552 | + die_errno(_("failed to copy '%s' to '%s'"), src, dst); |
| 1553 | + |
| 1554 | + if (unlink(src)) |
| 1555 | + die_errno(_("failed to delete '%s'"), src); |
| 1556 | +} |
| 1557 | + |
| 1558 | +static void migrate_pack(const char *srcdir, const char *dstdir, |
| 1559 | + const char *pack_filename) |
| 1560 | +{ |
| 1561 | + size_t basenamelen, srclen, dstlen; |
| 1562 | + struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT; |
| 1563 | + struct { |
| 1564 | + const char *ext; |
| 1565 | + unsigned move:1; |
| 1566 | + } files[] = { |
| 1567 | + {".pack", 0}, |
| 1568 | + {".keep", 0}, |
| 1569 | + {".rev", 0}, |
| 1570 | + {".idx", 1}, /* The index file must be atomically moved last. */ |
| 1571 | + }; |
| 1572 | + |
| 1573 | + trace2_region_enter("maintenance", "migrate_pack", the_repository); |
| 1574 | + |
| 1575 | + basenamelen = strlen(pack_filename) - 5; /* .pack */ |
| 1576 | + strbuf_addstr(&src, srcdir); |
| 1577 | + strbuf_addch(&src, '/'); |
| 1578 | + strbuf_add(&src, pack_filename, basenamelen); |
| 1579 | + strbuf_addstr(&src, ".idx"); |
| 1580 | + |
| 1581 | + /* A pack without an index file is not yet ready to be migrated. */ |
| 1582 | + if (!file_exists(src.buf)) |
| 1583 | + goto cleanup; |
| 1584 | + |
| 1585 | + strbuf_setlen(&src, src.len - 4 /* .idx */); |
| 1586 | + strbuf_addstr(&dst, dstdir); |
| 1587 | + strbuf_addch(&dst, '/'); |
| 1588 | + strbuf_add(&dst, pack_filename, basenamelen); |
| 1589 | + |
| 1590 | + srclen = src.len; |
| 1591 | + dstlen = dst.len; |
| 1592 | + |
| 1593 | + /* Move or copy files from the source directory to the destination. */ |
| 1594 | + for (size_t i = 0; i < ARRAY_SIZE(files); i++) { |
| 1595 | + strbuf_setlen(&src, srclen); |
| 1596 | + strbuf_addstr(&src, files[i].ext); |
| 1597 | + |
| 1598 | + if (!file_exists(src.buf)) |
| 1599 | + continue; |
| 1600 | + |
| 1601 | + strbuf_setlen(&dst, dstlen); |
| 1602 | + strbuf_addstr(&dst, files[i].ext); |
| 1603 | + |
| 1604 | + if (files[i].move) |
| 1605 | + rename_or_copy_or_die(src.buf, dst.buf); |
| 1606 | + else |
| 1607 | + link_or_copy_or_die(src.buf, dst.buf); |
| 1608 | + } |
| 1609 | + |
| 1610 | + /* |
| 1611 | + * Now the pack and all associated files exist at the destination we can |
| 1612 | + * now clean up the files in the source directory. |
| 1613 | + */ |
| 1614 | + for (size_t i = 0; i < ARRAY_SIZE(files); i++) { |
| 1615 | + /* Files that were moved rather than copied have no clean up. */ |
| 1616 | + if (files[i].move) |
| 1617 | + continue; |
| 1618 | + |
| 1619 | + strbuf_setlen(&src, srclen); |
| 1620 | + strbuf_addstr(&src, files[i].ext); |
| 1621 | + |
| 1622 | + /* Files that never existed in originally have no clean up.*/ |
| 1623 | + if (!file_exists(src.buf)) |
| 1624 | + continue; |
| 1625 | + |
| 1626 | + if (unlink(src.buf)) |
| 1627 | + warning_errno(_("failed to delete '%s'"), src.buf); |
| 1628 | + } |
| 1629 | + |
| 1630 | +cleanup: |
| 1631 | + strbuf_release(&src); |
| 1632 | + strbuf_release(&dst); |
| 1633 | + |
| 1634 | + trace2_region_leave("maintenance", "migrate_pack", the_repository); |
| 1635 | +} |
| 1636 | + |
| 1637 | +static void move_pack_to_shared_cache(const char *full_path, size_t full_path_len, |
| 1638 | + const char *file_name, void *data) |
| 1639 | +{ |
| 1640 | + char *srcdir; |
| 1641 | + const char *dstdir = (const char *)data; |
| 1642 | + |
| 1643 | + /* We only care about the actual pack files here. |
| 1644 | + * The associated .idx, .keep, .rev files will be copied in tandem |
| 1645 | + * with the pack file, with the index file being moved last. |
| 1646 | + * The original locations of the non-index files will only deleted |
| 1647 | + * once all other files have been copied/moved. |
| 1648 | + */ |
| 1649 | + if (!ends_with(file_name, ".pack")) |
| 1650 | + return; |
| 1651 | + |
| 1652 | + srcdir = xstrndup(full_path, full_path_len - strlen(file_name) - 1); |
| 1653 | + |
| 1654 | + migrate_pack(srcdir, dstdir, file_name); |
| 1655 | + |
| 1656 | + free(srcdir); |
| 1657 | +} |
| 1658 | + |
| 1659 | +static int move_loose_object_to_shared_cache(const struct object_id *oid, |
| 1660 | + const char *path, |
| 1661 | + UNUSED void *data) |
| 1662 | +{ |
| 1663 | + struct stat st; |
| 1664 | + struct strbuf dst = STRBUF_INIT; |
| 1665 | + char *hex = oid_to_hex(oid); |
| 1666 | + |
| 1667 | + strbuf_addf(&dst, "%s/%.2s/", shared_object_dir, hex); |
| 1668 | + |
| 1669 | + if (stat(dst.buf, &st)) { |
| 1670 | + if (mkdir(dst.buf, 0777)) |
| 1671 | + die_errno(_("failed to create directory '%s'"), dst.buf); |
| 1672 | + } else if (!S_ISDIR(st.st_mode)) |
| 1673 | + die(_("expected '%s' to be a directory"), dst.buf); |
| 1674 | + |
| 1675 | + strbuf_addstr(&dst, hex+2); |
| 1676 | + rename_or_copy_or_die(path, dst.buf); |
| 1677 | + |
| 1678 | + strbuf_release(&dst); |
| 1679 | + return 0; |
| 1680 | +} |
| 1681 | + |
| 1682 | +static int maintenance_task_cache_local_objs(UNUSED struct maintenance_run_opts *opts, |
| 1683 | + UNUSED struct gc_config *cfg) |
| 1684 | +{ |
| 1685 | + struct strbuf dstdir = STRBUF_INIT; |
| 1686 | + struct repository *r = the_repository; |
| 1687 | + |
| 1688 | + /* This task is only applicable with a VFS/Scalar shared cache. */ |
| 1689 | + if (!shared_object_dir) |
| 1690 | + return 0; |
| 1691 | + |
| 1692 | + /* If the dest is the same as the local odb path then we do nothing. */ |
| 1693 | + if (!fspathcmp(r->objects->odb->path, shared_object_dir)) |
| 1694 | + goto cleanup; |
| 1695 | + |
| 1696 | + strbuf_addf(&dstdir, "%s/pack", shared_object_dir); |
| 1697 | + |
| 1698 | + for_each_file_in_pack_dir(r->objects->odb->path, move_pack_to_shared_cache, |
| 1699 | + dstdir.buf); |
| 1700 | + |
| 1701 | + for_each_loose_object(move_loose_object_to_shared_cache, NULL, |
| 1702 | + FOR_EACH_OBJECT_LOCAL_ONLY); |
| 1703 | + |
| 1704 | +cleanup: |
| 1705 | + strbuf_release(&dstdir); |
| 1706 | + return 0; |
| 1707 | +} |
| 1708 | + |
1527 | 1709 | typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
|
1528 | 1710 | struct gc_config *cfg);
|
1529 | 1711 |
|
@@ -1556,6 +1738,7 @@ enum maintenance_task_label {
|
1556 | 1738 | TASK_REFLOG_EXPIRE,
|
1557 | 1739 | TASK_WORKTREE_PRUNE,
|
1558 | 1740 | TASK_RERERE_GC,
|
| 1741 | + TASK_CACHE_LOCAL_OBJS, |
1559 | 1742 |
|
1560 | 1743 | /* Leave as final value */
|
1561 | 1744 | TASK__COUNT
|
@@ -1607,6 +1790,10 @@ static struct maintenance_task tasks[] = {
|
1607 | 1790 | maintenance_task_rerere_gc,
|
1608 | 1791 | rerere_gc_condition,
|
1609 | 1792 | },
|
| 1793 | + [TASK_CACHE_LOCAL_OBJS] = { |
| 1794 | + "cache-local-objects", |
| 1795 | + maintenance_task_cache_local_objs, |
| 1796 | + }, |
1610 | 1797 | };
|
1611 | 1798 |
|
1612 | 1799 | static int compare_tasks_by_selection(const void *a_, const void *b_)
|
@@ -1701,6 +1888,8 @@ static void initialize_maintenance_strategy(void)
|
1701 | 1888 | tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
|
1702 | 1889 | tasks[TASK_PACK_REFS].enabled = 1;
|
1703 | 1890 | tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
|
| 1891 | + tasks[TASK_CACHE_LOCAL_OBJS].enabled = 1; |
| 1892 | + tasks[TASK_CACHE_LOCAL_OBJS].schedule = SCHEDULE_WEEKLY; |
1704 | 1893 | }
|
1705 | 1894 | }
|
1706 | 1895 |
|
|
0 commit comments