Skip to content

Commit 8f09a98

Browse files
author
Edward Thomson
committed
odb: freshen existing objects when writing
When writing an object, we calculate its OID and see if it exists in the object database. If it does, we need to freshen the file that contains it.
1 parent d2794b0 commit 8f09a98

File tree

8 files changed

+193
-3
lines changed

8 files changed

+193
-3
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ v0.24 + 1
6363
* `git_diff_file` now includes an `id_abbrev` field that reflects the
6464
number of nibbles set in the `id` field.
6565

66+
* `git_odb_backend` now has a `freshen` function pointer. This optional
67+
function pointer is similar to the `exists` function, but it will update
68+
a last-used marker. For filesystem-based object databases, this updates
69+
the timestamp of the file containing the object, to indicate "freshness".
70+
If this is `NULL`, then it will not be called and the `exists` function
71+
will be used instead.
72+
6673
v0.24
6774
-------
6875

include/git2/sys/odb_backend.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ struct git_odb_backend {
8383
git_odb_writepack **, git_odb_backend *, git_odb *odb,
8484
git_transfer_progress_cb progress_cb, void *progress_payload);
8585

86+
/**
87+
* "Freshens" an already existing object, updating its last-used
88+
* time. This occurs when `git_odb_write` was called, but the
89+
* object already existed (and will not be re-written). The
90+
* underlying implementation may want to update last-used timestamps.
91+
*
92+
* If callers implement this, they should return `0` if the object
93+
* exists and was freshened, and non-zero otherwise.
94+
*/
95+
int (* freshen)(git_odb_backend *, const git_oid *);
96+
8697
/**
8798
* Frees any resources held by the odb (including the `git_odb_backend`
8899
* itself). An odb backend implementation must provide this function.

src/fileops.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,22 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
837837
return cp_by_fd(ifd, ofd, true);
838838
}
839839

840+
int git_futils_touch(const char *path)
841+
{
842+
struct p_timeval times[2];
843+
time_t now = time(NULL);
844+
int ret;
845+
846+
times[0].tv_sec = now;
847+
times[0].tv_usec = 0;
848+
times[1].tv_sec = now;
849+
times[1].tv_usec = 0;
850+
851+
ret = p_utimes(path, times);
852+
853+
return (ret < 0) ? git_path_set_error(errno, path, "touch") : 0;
854+
}
855+
840856
static int cp_link(const char *from, const char *to, size_t link_size)
841857
{
842858
int error = 0;

src/fileops.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ extern int git_futils_cp(
184184
const char *to,
185185
mode_t filemode);
186186

187+
/**
188+
* Set the files atime and mtime to the current time.
189+
*/
190+
extern int git_futils_touch(const char *path);
191+
187192
/**
188193
* Flags that can be passed to `git_futils_cp_r`.
189194
*

src/odb.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -654,7 +654,10 @@ void git_odb_free(git_odb *db)
654654
GIT_REFCOUNT_DEC(db, odb_free);
655655
}
656656

657-
static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed)
657+
static int odb_exists_1(
658+
git_odb *db,
659+
const git_oid *id,
660+
bool only_refreshed)
658661
{
659662
size_t i;
660663
bool found = false;
@@ -673,6 +676,44 @@ static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed)
673676
return (int)found;
674677
}
675678

679+
static int odb_freshen_1(
680+
git_odb *db,
681+
const git_oid *id,
682+
bool only_refreshed)
683+
{
684+
size_t i;
685+
bool found = false;
686+
687+
for (i = 0; i < db->backends.length && !found; ++i) {
688+
backend_internal *internal = git_vector_get(&db->backends, i);
689+
git_odb_backend *b = internal->backend;
690+
691+
if (only_refreshed && !b->refresh)
692+
continue;
693+
694+
if (b->freshen != NULL)
695+
found = !b->freshen(b, id);
696+
else if (b->exists != NULL)
697+
found = b->exists(b, id);
698+
}
699+
700+
return (int)found;
701+
}
702+
703+
static int odb_freshen(git_odb *db, const git_oid *id)
704+
{
705+
assert(db && id);
706+
707+
if (odb_freshen_1(db, id, false))
708+
return 1;
709+
710+
if (!git_odb_refresh(db))
711+
return odb_freshen_1(db, id, true);
712+
713+
/* Failed to refresh, hence not found */
714+
return 0;
715+
}
716+
676717
int git_odb_exists(git_odb *db, const git_oid *id)
677718
{
678719
git_odb_object *object;
@@ -1131,7 +1172,7 @@ int git_odb_write(
11311172
assert(oid && db);
11321173

11331174
git_odb_hash(oid, data, len, type);
1134-
if (git_odb_exists(db, oid))
1175+
if (odb_freshen(db, oid))
11351176
return 0;
11361177

11371178
for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -1257,7 +1298,7 @@ int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
12571298

12581299
git_hash_final(out, stream->hash_ctx);
12591300

1260-
if (git_odb_exists(stream->backend->odb, out))
1301+
if (odb_freshen(stream->backend->odb, out))
12611302
return 0;
12621303

12631304
return stream->finalize_write(stream, out);

src/odb_loose.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,23 @@ static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, c
918918
return error;
919919
}
920920

921+
static int loose_backend__freshen(
922+
git_odb_backend *_backend,
923+
const git_oid *oid)
924+
{
925+
loose_backend *backend = (loose_backend *)_backend;
926+
git_buf path = GIT_BUF_INIT;
927+
int error;
928+
929+
if (object_file_name(&path, backend, oid) < 0)
930+
return -1;
931+
932+
error = git_futils_touch(path.ptr);
933+
git_buf_free(&path);
934+
935+
return error;
936+
}
937+
921938
static void loose_backend__free(git_odb_backend *_backend)
922939
{
923940
loose_backend *backend;
@@ -975,6 +992,7 @@ int git_odb_backend_loose(
975992
backend->parent.exists = &loose_backend__exists;
976993
backend->parent.exists_prefix = &loose_backend__exists_prefix;
977994
backend->parent.foreach = &loose_backend__foreach;
995+
backend->parent.freshen = &loose_backend__freshen;
978996
backend->parent.free = &loose_backend__free;
979997

980998
*backend_out = (git_odb_backend *)backend;

src/odb_pack.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,18 @@ static int pack_backend__read_header(
363363
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
364364
}
365365

366+
static int pack_backend__freshen(
367+
git_odb_backend *backend, const git_oid *oid)
368+
{
369+
struct git_pack_entry e;
370+
int error;
371+
372+
if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
373+
return error;
374+
375+
return git_futils_touch(e.p->pack_name);
376+
}
377+
366378
static int pack_backend__read(
367379
void **buffer_p, size_t *len_p, git_otype *type_p,
368380
git_odb_backend *backend, const git_oid *oid)
@@ -560,6 +572,7 @@ static int pack_backend__alloc(struct pack_backend **out, size_t initial_size)
560572
backend->parent.refresh = &pack_backend__refresh;
561573
backend->parent.foreach = &pack_backend__foreach;
562574
backend->parent.writepack = &pack_backend__writepack;
575+
backend->parent.freshen = &pack_backend__freshen;
563576
backend->parent.free = &pack_backend__free;
564577

565578
*out = backend;

tests/odb/freshen.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "clar_libgit2.h"
2+
#include "odb.h"
3+
#include "posix.h"
4+
5+
static git_repository *repo;
6+
static git_odb *odb;
7+
8+
void test_odb_freshen__initialize(void)
9+
{
10+
repo = cl_git_sandbox_init("testrepo.git");
11+
cl_git_pass(git_repository_odb(&odb, repo));
12+
}
13+
14+
void test_odb_freshen__cleanup(void)
15+
{
16+
git_odb_free(odb);
17+
cl_git_sandbox_cleanup();
18+
}
19+
20+
#define LOOSE_STR "hey\n"
21+
#define LOOSE_ID "1385f264afb75a56a5bec74243be9b367ba4ca08"
22+
#define LOOSE_FN "13/85f264afb75a56a5bec74243be9b367ba4ca08"
23+
24+
void test_odb_freshen__loose_object(void)
25+
{
26+
git_oid expected_id, id;
27+
struct stat before, after;
28+
struct p_timeval old_times[2];
29+
30+
cl_git_pass(git_oid_fromstr(&expected_id, LOOSE_ID));
31+
32+
old_times[0].tv_sec = 1234567890;
33+
old_times[0].tv_usec = 0;
34+
old_times[1].tv_sec = 1234567890;
35+
old_times[1].tv_usec = 0;
36+
37+
/* set time to way back */
38+
cl_must_pass(p_utimes("testrepo.git/objects/" LOOSE_FN, old_times));
39+
cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &before));
40+
41+
cl_git_pass(git_odb_write(&id, odb, LOOSE_STR, CONST_STRLEN(LOOSE_STR),
42+
GIT_OBJ_BLOB));
43+
cl_assert_equal_oid(&expected_id, &id);
44+
cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_FN, &after));
45+
46+
cl_assert(before.st_atime < after.st_atime);
47+
cl_assert(before.st_mtime < after.st_mtime);
48+
}
49+
50+
#define PACKED_STR "Testing a readme.txt\n"
51+
#define PACKED_ID "6336846bd5c88d32f93ae57d846683e61ab5c530"
52+
#define PACKED_FN "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack"
53+
54+
void test_odb_freshen__packed_object(void)
55+
{
56+
git_oid expected_id, id;
57+
struct stat before, after;
58+
struct p_timeval old_times[2];
59+
60+
cl_git_pass(git_oid_fromstr(&expected_id, PACKED_ID));
61+
62+
old_times[0].tv_sec = 1234567890;
63+
old_times[0].tv_usec = 0;
64+
old_times[1].tv_sec = 1234567890;
65+
old_times[1].tv_usec = 0;
66+
67+
/* set time to way back */
68+
cl_must_pass(p_utimes("testrepo.git/objects/pack/" PACKED_FN, old_times));
69+
cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &before));
70+
71+
cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
72+
CONST_STRLEN(PACKED_STR), GIT_OBJ_BLOB));
73+
cl_assert_equal_oid(&expected_id, &id);
74+
cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));
75+
76+
cl_assert(before.st_atime < after.st_atime);
77+
cl_assert(before.st_mtime < after.st_mtime);
78+
}
79+

0 commit comments

Comments
 (0)