Skip to content

Commit 4829785

Browse files
committed
maintenance: add new cache-local-objects maintenance task (#720)
Introduce a new maintenance task, `cache-local-objects`, that operates on Scalar or VFS for Git repositories with a per-volume, shared object cache (specified by `gvfs.sharedCache`) to migrate packfiles and loose objects from the repository object directory to the shared cache. Older versions of `microsoft/git` incorrectly placed packfiles in the repository object directory instead of the shared cache; this task will help clean up existing clones impacted by that issue. Fixes #716
2 parents a13eb91 + 5a738b0 commit 4829785

File tree

4 files changed

+329
-0
lines changed

4 files changed

+329
-0
lines changed

Documentation/git-maintenance.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ task:
6969
* `prefetch`: hourly.
7070
* `loose-objects`: daily.
7171
* `incremental-repack`: daily.
72+
* `cache-local-objects`: weekly.
7273
--
7374
+
7475
`git maintenance register` will also disable foreground maintenance by
@@ -158,6 +159,13 @@ pack-refs::
158159
need to iterate across many references. See linkgit:git-pack-refs[1]
159160
for more information.
160161

162+
cache-local-objects::
163+
The `cache-local-objects` task only operates on Scalar or VFS for Git
164+
repositories (cloned with either `scalar clone` or `gvfs clone`) that
165+
have the `gvfs.sharedCache` configuration setting present. This task
166+
migrates pack files and loose objects from the repository's object
167+
directory in to the shared volume cache.
168+
161169
OPTIONS
162170
-------
163171
--auto::

builtin/gc.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define USE_THE_REPOSITORY_VARIABLE
1414
#define DISABLE_SIGN_COMPARE_WARNINGS
1515

16+
#include "git-compat-util.h"
1617
#include "builtin.h"
1718
#include "abspath.h"
1819
#include "date.h"
@@ -44,6 +45,8 @@
4445
#include "hook.h"
4546
#include "setup.h"
4647
#include "trace2.h"
48+
#include "copy.h"
49+
#include "dir.h"
4750

4851
#define FAILED_RUN "failed to run %s"
4952

@@ -1374,6 +1377,186 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
13741377
return 0;
13751378
}
13761379

1380+
static void link_or_copy_or_die(const char *src, const char *dst)
1381+
{
1382+
if (!link(src, dst))
1383+
return;
1384+
1385+
/* Use copy operation if src and dst are on different file systems. */
1386+
if (errno != EXDEV)
1387+
warning_errno(_("failed to link '%s' to '%s'"), src, dst);
1388+
1389+
if (copy_file(dst, src, 0444))
1390+
die_errno(_("failed to copy '%s' to '%s'"), src, dst);
1391+
}
1392+
1393+
static void rename_or_copy_or_die(const char *src, const char *dst)
1394+
{
1395+
if (!rename(src, dst))
1396+
return;
1397+
1398+
/* Use copy and delete if src and dst are on different file systems. */
1399+
if (errno != EXDEV)
1400+
warning_errno(_("failed to move '%s' to '%s'"), src, dst);
1401+
1402+
if (copy_file(dst, src, 0444))
1403+
die_errno(_("failed to copy '%s' to '%s'"), src, dst);
1404+
1405+
if (unlink(src))
1406+
die_errno(_("failed to delete '%s'"), src);
1407+
}
1408+
1409+
static void migrate_pack(const char *srcdir, const char *dstdir,
1410+
const char *pack_filename)
1411+
{
1412+
size_t basenamelen, srclen, dstlen;
1413+
struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;
1414+
struct {
1415+
const char *ext;
1416+
unsigned move:1;
1417+
} files[] = {
1418+
{".pack", 0},
1419+
{".keep", 0},
1420+
{".rev", 0},
1421+
{".idx", 1}, /* The index file must be atomically moved last. */
1422+
};
1423+
1424+
trace2_region_enter("maintenance", "migrate_pack", the_repository);
1425+
1426+
basenamelen = strlen(pack_filename) - 5; /* .pack */
1427+
strbuf_addstr(&src, srcdir);
1428+
strbuf_addch(&src, '/');
1429+
strbuf_add(&src, pack_filename, basenamelen);
1430+
strbuf_addstr(&src, ".idx");
1431+
1432+
/* A pack without an index file is not yet ready to be migrated. */
1433+
if (!file_exists(src.buf))
1434+
goto cleanup;
1435+
1436+
strbuf_setlen(&src, src.len - 4 /* .idx */);
1437+
strbuf_addstr(&dst, dstdir);
1438+
strbuf_addch(&dst, '/');
1439+
strbuf_add(&dst, pack_filename, basenamelen);
1440+
1441+
srclen = src.len;
1442+
dstlen = dst.len;
1443+
1444+
/* Move or copy files from the source directory to the destination. */
1445+
for (size_t i = 0; i < ARRAY_SIZE(files); i++) {
1446+
strbuf_setlen(&src, srclen);
1447+
strbuf_addstr(&src, files[i].ext);
1448+
1449+
if (!file_exists(src.buf))
1450+
continue;
1451+
1452+
strbuf_setlen(&dst, dstlen);
1453+
strbuf_addstr(&dst, files[i].ext);
1454+
1455+
if (files[i].move)
1456+
rename_or_copy_or_die(src.buf, dst.buf);
1457+
else
1458+
link_or_copy_or_die(src.buf, dst.buf);
1459+
}
1460+
1461+
/*
1462+
* Now the pack and all associated files exist at the destination we can
1463+
* now clean up the files in the source directory.
1464+
*/
1465+
for (size_t i = 0; i < ARRAY_SIZE(files); i++) {
1466+
/* Files that were moved rather than copied have no clean up. */
1467+
if (files[i].move)
1468+
continue;
1469+
1470+
strbuf_setlen(&src, srclen);
1471+
strbuf_addstr(&src, files[i].ext);
1472+
1473+
/* Files that never existed in originally have no clean up.*/
1474+
if (!file_exists(src.buf))
1475+
continue;
1476+
1477+
if (unlink(src.buf))
1478+
warning_errno(_("failed to delete '%s'"), src.buf);
1479+
}
1480+
1481+
cleanup:
1482+
strbuf_release(&src);
1483+
strbuf_release(&dst);
1484+
1485+
trace2_region_leave("maintenance", "migrate_pack", the_repository);
1486+
}
1487+
1488+
static void move_pack_to_shared_cache(const char *full_path, size_t full_path_len,
1489+
const char *file_name, void *data)
1490+
{
1491+
char *srcdir;
1492+
const char *dstdir = (const char *)data;
1493+
1494+
/* We only care about the actual pack files here.
1495+
* The associated .idx, .keep, .rev files will be copied in tandem
1496+
* with the pack file, with the index file being moved last.
1497+
* The original locations of the non-index files will only deleted
1498+
* once all other files have been copied/moved.
1499+
*/
1500+
if (!ends_with(file_name, ".pack"))
1501+
return;
1502+
1503+
srcdir = xstrndup(full_path, full_path_len - strlen(file_name) - 1);
1504+
1505+
migrate_pack(srcdir, dstdir, file_name);
1506+
1507+
free(srcdir);
1508+
}
1509+
1510+
static int move_loose_object_to_shared_cache(const struct object_id *oid,
1511+
const char *path,
1512+
UNUSED void *data)
1513+
{
1514+
struct stat st;
1515+
struct strbuf dst = STRBUF_INIT;
1516+
char *hex = oid_to_hex(oid);
1517+
1518+
strbuf_addf(&dst, "%s/%.2s/", shared_object_dir, hex);
1519+
1520+
if (stat(dst.buf, &st)) {
1521+
if (mkdir(dst.buf, 0777))
1522+
die_errno(_("failed to create directory '%s'"), dst.buf);
1523+
} else if (!S_ISDIR(st.st_mode))
1524+
die(_("expected '%s' to be a directory"), dst.buf);
1525+
1526+
strbuf_addstr(&dst, hex+2);
1527+
rename_or_copy_or_die(path, dst.buf);
1528+
1529+
strbuf_release(&dst);
1530+
return 0;
1531+
}
1532+
1533+
static int maintenance_task_cache_local_objs(UNUSED struct maintenance_run_opts *opts,
1534+
UNUSED struct gc_config *cfg)
1535+
{
1536+
struct strbuf dstdir = STRBUF_INIT;
1537+
struct repository *r = the_repository;
1538+
1539+
/* This task is only applicable with a VFS/Scalar shared cache. */
1540+
if (!shared_object_dir)
1541+
return 0;
1542+
1543+
/* If the dest is the same as the local odb path then we do nothing. */
1544+
if (!fspathcmp(r->objects->odb->path, shared_object_dir))
1545+
goto cleanup;
1546+
1547+
strbuf_addf(&dstdir, "%s/pack", shared_object_dir);
1548+
1549+
for_each_file_in_pack_dir(r->objects->odb->path, move_pack_to_shared_cache,
1550+
dstdir.buf);
1551+
1552+
for_each_loose_object(move_loose_object_to_shared_cache, NULL,
1553+
FOR_EACH_OBJECT_LOCAL_ONLY);
1554+
1555+
cleanup:
1556+
strbuf_release(&dstdir);
1557+
return 0;
1558+
}
1559+
13771560
typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
13781561
struct gc_config *cfg);
13791562

@@ -1403,6 +1586,7 @@ enum maintenance_task_label {
14031586
TASK_GC,
14041587
TASK_COMMIT_GRAPH,
14051588
TASK_PACK_REFS,
1589+
TASK_CACHE_LOCAL_OBJS,
14061590

14071591
/* Leave as final value */
14081592
TASK__COUNT
@@ -1439,6 +1623,10 @@ static struct maintenance_task tasks[] = {
14391623
maintenance_task_pack_refs,
14401624
pack_refs_condition,
14411625
},
1626+
[TASK_CACHE_LOCAL_OBJS] = {
1627+
"cache-local-objects",
1628+
maintenance_task_cache_local_objs,
1629+
},
14421630
};
14431631

14441632
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1533,6 +1721,8 @@ static void initialize_maintenance_strategy(void)
15331721
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
15341722
tasks[TASK_PACK_REFS].enabled = 1;
15351723
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1724+
tasks[TASK_CACHE_LOCAL_OBJS].enabled = 1;
1725+
tasks[TASK_CACHE_LOCAL_OBJS].schedule = SCHEDULE_WEEKLY;
15361726
}
15371727
}
15381728

scalar.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@ static int cmd_run(int argc, const char **argv)
11741174
{ "fetch", "prefetch" },
11751175
{ "loose-objects", "loose-objects" },
11761176
{ "pack-files", "incremental-repack" },
1177+
{ "cache-local-objects", "cache-local-objects" },
11771178
{ NULL, NULL }
11781179
};
11791180
struct strbuf buf = STRBUF_INIT;

0 commit comments

Comments
 (0)