Skip to content

Commit cb383ec

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 <mjcheetham@outlook.com>
1 parent 1d7817c commit cb383ec

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-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 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 from the repository's object directory in to the
167+
shared volume cache.
168+
161169
OPTIONS
162170
-------
163171
--auto::

builtin/gc.c

Lines changed: 134 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"
@@ -41,6 +42,7 @@
4142
#include "hook.h"
4243
#include "setup.h"
4344
#include "trace2.h"
45+
#include "copy.h"
4446

4547
#define FAILED_RUN "failed to run %s"
4648

@@ -1345,6 +1347,131 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts
13451347
return 0;
13461348
}
13471349

1350+
static void link_or_copy_or_die(const char *src, const char *dst)
1351+
{
1352+
if (!link(src, dst))
1353+
return;
1354+
1355+
warning_errno(_("failed to link '%s' to '%s'... trying copy..."), src,
1356+
dst);
1357+
1358+
if (!copy_file(dst, src, 0644))
1359+
return;
1360+
1361+
die_errno(_("failed to copy '%s' to '%s'"), src, dst);
1362+
}
1363+
1364+
static void migrate_pack(const char *srcdir, const char *dstdir,
1365+
const char *pack_filename)
1366+
{
1367+
struct stat st;
1368+
int has_keep, has_rev, has_idx;
1369+
char *basename, *pack_src, *keep_src, *rev_src, *idx_src,
1370+
*pack_dst, *keep_dst, *rev_dst, *idx_dst;
1371+
1372+
trace2_region_enter("maintenance", "migrate_pack", the_repository);
1373+
1374+
basename = xstrndup(pack_filename, strlen(pack_filename) - 5 /*.pack*/);
1375+
pack_src = xstrfmt("%s/%s", srcdir, pack_filename);
1376+
pack_dst = xstrfmt("%s/%s", dstdir, pack_filename);
1377+
keep_src = xstrfmt("%s/%s.keep", srcdir, basename);
1378+
keep_dst = xstrfmt("%s/%s.keep", dstdir, basename);
1379+
rev_src = xstrfmt("%s/%s.rev", srcdir, basename);
1380+
rev_dst = xstrfmt("%s/%s.rev", dstdir, basename);
1381+
idx_src = xstrfmt("%s/%s.idx", srcdir, basename);
1382+
idx_dst = xstrfmt("%s/%s.idx", dstdir, basename);
1383+
1384+
has_keep = !stat(keep_src, &st);
1385+
has_rev = !stat(rev_src, &st);
1386+
has_idx = !stat(idx_src, &st);
1387+
1388+
/* A pack without an index file is not yet ready to be migrated. */
1389+
if (!has_idx)
1390+
goto cleanup;
1391+
1392+
/*
1393+
* Hard link (or copy if that fails) all but the index file so that
1394+
* other Git processes don't attempt to use the pack file from the new
1395+
* location yet.
1396+
*/
1397+
link_or_copy_or_die(pack_src, pack_dst);
1398+
if (has_keep)
1399+
link_or_copy_or_die(keep_src, keep_dst);
1400+
if (has_rev)
1401+
link_or_copy_or_die(rev_src, rev_dst);
1402+
1403+
/*
1404+
* Move the index file atomically now that the other files can be found
1405+
* at the destination.
1406+
*/
1407+
if (rename(idx_src, idx_dst))
1408+
die_errno(_("failed to move '%s' to '%s'"), idx_src, idx_dst);
1409+
1410+
/*
1411+
* Now the pack and all associated files exist at the destination we can
1412+
* now clean up the files in the source directory.
1413+
*/
1414+
if (unlink(pack_src))
1415+
warning_errno(_("failed to delete '%s'"), pack_src);
1416+
if (has_keep && unlink(keep_src))
1417+
warning_errno(_("failed to delete '%s'"), keep_src);
1418+
if (has_rev & unlink(rev_src))
1419+
warning_errno(_("failed to delete '%s'"), rev_src);
1420+
1421+
cleanup:
1422+
free(idx_src);
1423+
free(idx_dst);
1424+
free(rev_src);
1425+
free(rev_dst);
1426+
free(keep_src);
1427+
free(keep_dst);
1428+
free(pack_src);
1429+
free(pack_dst);
1430+
free(basename);
1431+
1432+
trace2_region_leave("maintenance", "migrate_pack", the_repository);
1433+
}
1434+
1435+
static void move_pack_to_vfs_cache(const char *full_path, size_t full_path_len,
1436+
const char *file_name, UNUSED void *data)
1437+
{
1438+
char *srcdir;
1439+
struct strbuf dstdir = STRBUF_INIT;
1440+
1441+
/* We only care about the actual pack files here.
1442+
* The associated .idx, .keep, .rev files will be copied in tandem
1443+
* with the pack file, with the index file being moved last.
1444+
* The original locations of the non-index files will only deleted
1445+
* once all other files have been copied/moved.
1446+
*/
1447+
if (!ends_with(file_name, ".pack"))
1448+
return;
1449+
1450+
srcdir = xstrndup(full_path, full_path_len - strlen(file_name) - 1);
1451+
1452+
/* No cache or same source + desintation means there's no work to do. */
1453+
if (!object_dir || !fspathcmp(srcdir, object_dir))
1454+
return;
1455+
1456+
strbuf_addf(&dstdir, "%s/pack", object_dir);
1457+
1458+
migrate_pack(srcdir, dstdir.buf, file_name);
1459+
1460+
free(srcdir);
1461+
strbuf_release(&dstdir);
1462+
}
1463+
1464+
static int maintenance_task_vfs_cache_move(UNUSED struct maintenance_run_opts *opts,
1465+
UNUSED struct gc_config *cfg)
1466+
{
1467+
struct repository *r = the_repository;
1468+
1469+
for_each_file_in_pack_dir(r->objects->odb->path, move_pack_to_vfs_cache,
1470+
NULL);
1471+
1472+
return 0;
1473+
}
1474+
13481475
typedef int maintenance_task_fn(struct maintenance_run_opts *opts,
13491476
struct gc_config *cfg);
13501477

@@ -1374,6 +1501,7 @@ enum maintenance_task_label {
13741501
TASK_GC,
13751502
TASK_COMMIT_GRAPH,
13761503
TASK_PACK_REFS,
1504+
TASK_VFS_CACHE_MOVE,
13771505

13781506
/* Leave as final value */
13791507
TASK__COUNT
@@ -1410,6 +1538,10 @@ static struct maintenance_task tasks[] = {
14101538
maintenance_task_pack_refs,
14111539
pack_refs_condition,
14121540
},
1541+
[TASK_VFS_CACHE_MOVE] = {
1542+
"vfs-cache-move",
1543+
maintenance_task_vfs_cache_move,
1544+
},
14131545
};
14141546

14151547
static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1504,6 +1636,8 @@ static void initialize_maintenance_strategy(void)
15041636
tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
15051637
tasks[TASK_PACK_REFS].enabled = 1;
15061638
tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
1639+
tasks[TASK_VFS_CACHE_MOVE].enabled = 1;
1640+
tasks[TASK_VFS_CACHE_MOVE].schedule = SCHEDULE_WEEKLY;
15071641
}
15081642
}
15091643

t/t7900-maintenance.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,4 +1011,39 @@ 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+
printf "blob\ndata <<END\n%s\nEND\n\n" 1 2 3 4 5 | \
1027+
git -c fastimport.unpackLimit=0 fast-import &&
1028+
find .git/objects/pack \
1029+
-type f \
1030+
\( -name "*.pack" \
1031+
-o -name "*.idx" \
1032+
-o -name "*.keep" \
1033+
-o -name "*.rev" \) >src.txt &&
1034+
1035+
sed "s|.*/|../cache/pack/|" src.txt >dst.txt &&
1036+
mkdir -p ../cache/pack &&
1037+
1038+
git maintenance run &&
1039+
while IFS= read -r f; do
1040+
test_path_is_missing $f || exit 1
1041+
done <src.txt &&
1042+
1043+
while IFS= read -r f; do
1044+
test_path_exists $f || exit 1
1045+
done <dst.txt
1046+
)
1047+
'
1048+
10141049
test_done

0 commit comments

Comments
 (0)