Skip to content

Commit 33d4221

Browse files
peffgitster
authored andcommitted
write_sha1_file: freshen existing objects
When we try to write a loose object file, we first check whether that object already exists. If so, we skip the write as an optimization. However, this can interfere with prune's strategy of using mtimes to mark files in progress. For example, if a branch contains a particular tree object and is deleted, that tree object may become unreachable, and have an old mtime. If a new operation then tries to write the same tree, this ends up as a noop; we notice we already have the object and do nothing. A prune running simultaneously with this operation will see the object as old, and may delete it. We can solve this by "freshening" objects that we avoid writing by updating their mtime. The algorithm for doing so is essentially the same as that of has_sha1_file. Therefore we provide a new (static) interface "check_and_freshen", which finds and optionally freshens the object. It's trivial to implement freshening and simple checking by tweaking a single parameter. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent abcb865 commit 33d4221

File tree

2 files changed

+71
-7
lines changed

2 files changed

+71
-7
lines changed

sha1_file.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,27 +442,53 @@ void prepare_alt_odb(void)
442442
read_info_alternates(get_object_directory(), 0);
443443
}
444444

445-
static int has_loose_object_local(const unsigned char *sha1)
445+
static int freshen_file(const char *fn)
446446
{
447-
return !access(sha1_file_name(sha1), F_OK);
447+
struct utimbuf t;
448+
t.actime = t.modtime = time(NULL);
449+
return !utime(fn, &t);
448450
}
449451

450-
int has_loose_object_nonlocal(const unsigned char *sha1)
452+
static int check_and_freshen_file(const char *fn, int freshen)
453+
{
454+
if (access(fn, F_OK))
455+
return 0;
456+
if (freshen && freshen_file(fn))
457+
return 0;
458+
return 1;
459+
}
460+
461+
static int check_and_freshen_local(const unsigned char *sha1, int freshen)
462+
{
463+
return check_and_freshen_file(sha1_file_name(sha1), freshen);
464+
}
465+
466+
static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
451467
{
452468
struct alternate_object_database *alt;
453469
prepare_alt_odb();
454470
for (alt = alt_odb_list; alt; alt = alt->next) {
455471
fill_sha1_path(alt->name, sha1);
456-
if (!access(alt->base, F_OK))
472+
if (check_and_freshen_file(alt->base, freshen))
457473
return 1;
458474
}
459475
return 0;
460476
}
461477

478+
static int check_and_freshen(const unsigned char *sha1, int freshen)
479+
{
480+
return check_and_freshen_local(sha1, freshen) ||
481+
check_and_freshen_nonlocal(sha1, freshen);
482+
}
483+
484+
int has_loose_object_nonlocal(const unsigned char *sha1)
485+
{
486+
return check_and_freshen_nonlocal(sha1, 0);
487+
}
488+
462489
static int has_loose_object(const unsigned char *sha1)
463490
{
464-
return has_loose_object_local(sha1) ||
465-
has_loose_object_nonlocal(sha1);
491+
return check_and_freshen(sha1, 0);
466492
}
467493

468494
static unsigned int pack_used_ctr;
@@ -2965,6 +2991,17 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
29652991
return move_temp_to_file(tmp_file, filename);
29662992
}
29672993

2994+
static int freshen_loose_object(const unsigned char *sha1)
2995+
{
2996+
return check_and_freshen(sha1, 1);
2997+
}
2998+
2999+
static int freshen_packed_object(const unsigned char *sha1)
3000+
{
3001+
struct pack_entry e;
3002+
return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
3003+
}
3004+
29683005
int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
29693006
{
29703007
unsigned char sha1[20];
@@ -2977,7 +3014,7 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
29773014
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
29783015
if (returnsha1)
29793016
hashcpy(returnsha1, sha1);
2980-
if (has_sha1_file(sha1))
3017+
if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
29813018
return 0;
29823019
return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
29833020
}

t/t6501-freshen-objects.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ for repack in '' true; do
100100
test_expect_success "repository passes fsck ($title)" '
101101
git fsck
102102
'
103+
104+
test_expect_success "abandon objects again ($title)" '
105+
git reset --hard HEAD^ &&
106+
find .git/objects -type f |
107+
xargs test-chmtime -v -86400
108+
'
109+
110+
test_expect_success "start writing new commit with same tree ($title)" '
111+
tree=$(
112+
GIT_INDEX_FILE=index.tmp &&
113+
export GIT_INDEX_FILE &&
114+
git read-tree HEAD &&
115+
add abandon &&
116+
add unrelated &&
117+
git write-tree
118+
)
119+
'
120+
121+
test_expect_success "simultaneous gc ($title)" '
122+
git gc --prune=12.hours.ago
123+
'
124+
125+
# tree should have been refreshed by write-tree
126+
test_expect_success "finish writing out commit ($title)" '
127+
commit=$(echo foo | git commit-tree -p HEAD $tree) &&
128+
git update-ref HEAD $commit
129+
'
103130
done
104131

105132
test_done

0 commit comments

Comments
 (0)