Skip to content

Commit 1c29b72

Browse files
committed
maintenance: add new vfs-cache-move maintenance task
Introduce a new maintenance task, `vfs-cache-move`, that operates on Scalar or VFS for Git repositories with a per-volume, shared object cache (specified by `gvfs.sharedCache`) to migrate packfiles 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. Signed-off-by: Matthew John Cheetham <[email protected]>
1 parent 1d7817c commit 1c29b72

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

Documentation/git-maintenance.txt

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+
* `vfs-cache-move`: 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+
vfs-cache-move::
163+
The `vfs-cache-move` task operates only 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 from the repository's object directory in to the
167+
shared volume cache.
168+
161169
OPTIONS
162170
-------
163171
--auto::

builtin/gc.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,155 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
13451345
return 0;
13461346
}
13471347

1348+
1349+
static void copy_file(const char *srcdir, const char *dstdir, const char *name)
1350+
{
1351+
int ret = 0;
1352+
struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;
1353+
int srcfd = -1, dstfd = -1;
1354+
char buf[1024];
1355+
ssize_t nr;
1356+
1357+
strbuf_addf(&src, "%s/%s", srcdir, name);
1358+
strbuf_addf(&dst, "%s/%s", dstdir, name);
1359+
1360+
srcfd = open(src.buf, O_RDONLY);
1361+
if (srcfd < 0) {
1362+
error("failed to open source file");
1363+
ret = 1;
1364+
goto cleanup;
1365+
}
1366+
1367+
dstfd = open(dst.buf, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1368+
if (dstfd < 0) {
1369+
error("failed to open destination file");
1370+
ret = 1;
1371+
goto cleanup;
1372+
}
1373+
1374+
while ((nr = read(srcfd, buf, sizeof(buf))) > 0) {
1375+
if (write(dstfd, buf, nr) < 0) {
1376+
error("failed to write to destination file");
1377+
ret = 1;
1378+
}
1379+
}
1380+
1381+
cleanup:
1382+
if (srcfd >= 0) close(srcfd);
1383+
if (dstfd >= 0) close(dstfd);
1384+
1385+
if (ret)
1386+
die_errno(_("failed to copy '%s' to '%s'"), src.buf, dst.buf);
1387+
1388+
strbuf_release(&src);
1389+
strbuf_release(&dst);
1390+
}
1391+
1392+
static void move_file(const char *srcdir, const char *dstdir, const char *name)
1393+
{
1394+
struct strbuf src = STRBUF_INIT, dst = STRBUF_INIT;
1395+
1396+
strbuf_addf(&src, "%s/%s", srcdir, name);
1397+
strbuf_addf(&dst, "%s/%s", dstdir, name);
1398+
1399+
if (rename(src.buf, dst.buf))
1400+
die_errno(_("failed to move '%s' to '%s'"), src.buf, dst.buf);
1401+
1402+
strbuf_release(&src);
1403+
strbuf_release(&dst);
1404+
}
1405+
1406+
static void delete_file(const char *dir, const char *name)
1407+
{
1408+
struct strbuf path = STRBUF_INIT;
1409+
1410+
strbuf_addf(&path, "%s/%s", dir, name);
1411+
1412+
if (unlink(path.buf))
1413+
warning_errno(_("failed to delete '%s'"), path.buf);
1414+
1415+
strbuf_release(&path);
1416+
}
1417+
1418+
static void migrate_pack(const char *srcdir, const char *dstdir,
1419+
const char *pack_filename)
1420+
{
1421+
struct strbuf path = STRBUF_INIT;
1422+
struct stat st;
1423+
char *basename, *keep_filename, *rev_filename, *idx_filename;
1424+
int has_keep, has_rev;
1425+
1426+
basename = xstrndup(pack_filename, strlen(pack_filename) - 5 /*.pack*/);
1427+
keep_filename = xstrfmt("%s.keep", basename);
1428+
rev_filename = xstrfmt("%s.rev", basename);
1429+
idx_filename = xstrfmt("%s.idx", basename);
1430+
1431+
strbuf_addf(&path, "%s/%s", srcdir, keep_filename);
1432+
has_keep = !stat(path.buf, &st);
1433+
strbuf_reset(&path);
1434+
strbuf_addf(&path, "%s/%s", srcdir, rev_filename);
1435+
has_rev = !stat(path.buf, &st);
1436+
strbuf_release(&path);
1437+
1438+
/* Copy all but the index file, which we will *move* atomically */
1439+
copy_file(srcdir, dstdir, pack_filename);
1440+
if (has_keep) copy_file(srcdir, dstdir, keep_filename);
1441+
if (has_rev) copy_file(srcdir, dstdir, rev_filename);
1442+
move_file(srcdir, dstdir, idx_filename);
1443+
1444+
/*
1445+
* Now the pack and associated files exist at the destination we
1446+
* we can now clean up files in the source directory.
1447+
*/
1448+
delete_file(srcdir, pack_filename);
1449+
if (has_keep) delete_file(srcdir, keep_filename);
1450+
if (has_rev) delete_file(srcdir, rev_filename);
1451+
1452+
free(idx_filename);
1453+
free(keep_filename);
1454+
free(rev_filename);
1455+
}
1456+
1457+
static void move_pack_to_vfs_cache(const char *full_path, size_t full_path_len,
1458+
const char *file_name, UNUSED void *data)
1459+
{
1460+
char *srcdir;
1461+
struct strbuf dstdir = STRBUF_INIT;
1462+
1463+
/* We only care about the actual pack files here.
1464+
* The associated .idx, .keep, .rev files will be copied in tandem
1465+
* with the pack file, with the index file being moved last.
1466+
* The original locations of the non-index files will only deleted
1467+
* once all other files have been copied/moved.
1468+
*/
1469+
if (!ends_with(file_name, ".pack"))
1470+
return;
1471+
1472+
srcdir = xstrndup(full_path, full_path_len - strlen(file_name) - 1);
1473+
1474+
/* No cache or same source + desintation means there's no work to do. */
1475+
if (!object_dir || !fspathcmp(srcdir, object_dir))
1476+
return;
1477+
1478+
strbuf_addf(&dstdir, "%s/pack", object_dir);
1479+
1480+
migrate_pack(srcdir, dstdir.buf, file_name);
1481+
1482+
free(srcdir);
1483+
strbuf_release(&dstdir);
1484+
}
1485+
1486+
static int maintenance_task_vfs_cache_move(UNUSED struct maintenance_run_opts *opts,
1487+
UNUSED struct gc_config *cfg)
1488+
{
1489+
struct repository *r = the_repository;
1490+
1491+
for_each_file_in_pack_dir(r->objects->odb->path, move_pack_to_vfs_cache,
1492+
NULL);
1493+
1494+
return 0;
1495+
}
1496+
13481497
typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
13491498
struct gc_config *cfg);
13501499

@@ -1374,6 +1523,7 @@ enum maintenance_task_label {
13741523
TASK_GC,
13751524
TASK_COMMIT_GRAPH,
13761525
TASK_PACK_REFS,
1526+
TASK_VFS_CACHE_MOVE,
13771527

13781528
/* Leave as final value */
13791529
TASK__COUNT
@@ -1410,6 +1560,10 @@ static struct maintenance_task tasks[] = {
14101560
maintenance_task_pack_refs,
14111561
pack_refs_condition,
14121562
},
1563+
[TASK_VFS_CACHE_MOVE] = {
1564+
"vfs-cache-move",
1565+
maintenance_task_vfs_cache_move,
1566+
},
14131567
};
14141568

14151569
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1504,6 +1658,8 @@ static void initialize_maintenance_strategy(void)
15041658
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
15051659
tasks[TASK_PACK_REFS].enabled = 1;
15061660
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1661+
tasks[TASK_VFS_CACHE_MOVE].enabled = 1;
1662+
tasks[TASK_VFS_CACHE_MOVE].schedule = SCHEDULE_WEEKLY;
15071663
}
15081664
}
15091665

t/t7900-maintenance.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,4 +1011,34 @@ test_expect_success 'repacking loose objects is quiet' '
10111011
)
10121012
'
10131013

1014+
test_expect_success 'vfs-cache-move task' '
1015+
#test_when_finished "rm -rf repo" &&
1016+
git init repo &&
1017+
(
1018+
cd repo &&
1019+
1020+
test_commit something &&
1021+
git config set gvfs.sharedcache ../cache &&
1022+
git config set maintenance.gc.enabled false &&
1023+
git config set maintenance.vfs-cache-move.enabled true &&
1024+
git config set maintenance.vfs-cache-move.auto 1 &&
1025+
1026+
touch .git/objects/pack/vfs-12345678.pack &&
1027+
touch .git/objects/pack/vfs-12345678.keep &&
1028+
touch .git/objects/pack/vfs-12345678.rev &&
1029+
touch .git/objects/pack/vfs-12345678.idx &&
1030+
mkdir -p ../cache/pack &&
1031+
1032+
git maintenance run &&
1033+
test_path_is_missing .git/objects/pack/vfs-12345678.pack &&
1034+
test_path_is_missing .git/objects/pack/vfs-12345678.keep &&
1035+
test_path_is_missing .git/objects/pack/vfs-12345678.rev &&
1036+
test_path_is_missing .git/objects/pack/vfs-12345678.idx &&
1037+
test_path_exists ../cache/pack/vfs-12345678.pack &&
1038+
test_path_exists ../cache/pack/vfs-12345678.keep &&
1039+
test_path_exists ../cache/pack/vfs-12345678.rev &&
1040+
test_path_exists ../cache/pack/vfs-12345678.idx
1041+
)
1042+
'
1043+
10141044
test_done

0 commit comments

Comments
 (0)