Skip to content

Commit 4a5255d

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 4a5255d

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-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: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* Copyright (c) 2006 Shawn O. Pearce
1111
*/
1212
#define USE_THE_REPOSITORY_VARIABLE
13+
#include "git-compat-util.h"
1314
#include "builtin.h"
1415
#include "abspath.h"
1516
#include "date.h"
@@ -1345,6 +1346,155 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
13451346
return 0;
13461347
}
13471348

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

@@ -1374,6 +1524,7 @@ enum maintenance_task_label {
13741524
TASK_GC,
13751525
TASK_COMMIT_GRAPH,
13761526
TASK_PACK_REFS,
1527+
TASK_VFS_CACHE_MOVE,
13771528

13781529
/* Leave as final value */
13791530
TASK__COUNT
@@ -1410,6 +1561,10 @@ static struct maintenance_task tasks[] = {
14101561
maintenance_task_pack_refs,
14111562
pack_refs_condition,
14121563
},
1564+
[TASK_VFS_CACHE_MOVE] = {
1565+
"vfs-cache-move",
1566+
maintenance_task_vfs_cache_move,
1567+
},
14131568
};
14141569

14151570
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1504,6 +1659,8 @@ static void initialize_maintenance_strategy(void)
15041659
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
15051660
tasks[TASK_PACK_REFS].enabled = 1;
15061661
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1662+
tasks[TASK_VFS_CACHE_MOVE].enabled = 1;
1663+
tasks[TASK_VFS_CACHE_MOVE].schedule = SCHEDULE_WEEKLY;
15071664
}
15081665
}
15091666

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)