Skip to content

Commit e5cb41d

Browse files
mjcheethamdscho
authored andcommitted
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 b8386da + bbbe8fa commit e5cb41d

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

@@ -1365,6 +1368,186 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
13651368
return 0;
13661369
}
13671370

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

@@ -1394,6 +1577,7 @@ enum maintenance_task_label {
13941577
TASK_GC,
13951578
TASK_COMMIT_GRAPH,
13961579
TASK_PACK_REFS,
1580+
TASK_CACHE_LOCAL_OBJS,
13971581

13981582
/* Leave as final value */
13991583
TASK__COUNT
@@ -1430,6 +1614,10 @@ static struct maintenance_task tasks[] = {
14301614
maintenance_task_pack_refs,
14311615
pack_refs_condition,
14321616
},
1617+
[TASK_CACHE_LOCAL_OBJS] = {
1618+
"cache-local-objects",
1619+
maintenance_task_cache_local_objs,
1620+
},
14331621
};
14341622

14351623
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1524,6 +1712,8 @@ static void initialize_maintenance_strategy(void)
15241712
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
15251713
tasks[TASK_PACK_REFS].enabled = 1;
15261714
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1715+
tasks[TASK_CACHE_LOCAL_OBJS].enabled = 1;
1716+
tasks[TASK_CACHE_LOCAL_OBJS].schedule = SCHEDULE_WEEKLY;
15271717
}
15281718
}
15291719

scalar.c

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

0 commit comments

Comments
 (0)