summaryrefslogtreecommitdiffstats
path: root/object-file.c
diff options
context:
space:
mode:
Diffstat (limited to 'object-file.c')
-rw-r--r--object-file.c25
1 files changed, 21 insertions, 4 deletions
diff --git a/object-file.c b/object-file.c
index c1bd746d9e..008ddf59a5 100644
--- a/object-file.c
+++ b/object-file.c
@@ -1974,6 +1974,8 @@ static void write_object_file_prepare_literally(const struct git_hash_algo *algo
hash_object_body(algo, &c, buf, len, oid, hdr, hdrlen);
}
+#define CHECK_COLLISION_DEST_VANISHED -2
+
static int check_collision(const char *source, const char *dest)
{
char buf_source[4096], buf_dest[4096];
@@ -1990,6 +1992,8 @@ static int check_collision(const char *source, const char *dest)
if (fd_dest < 0) {
if (errno != ENOENT)
ret = error_errno(_("unable to open %s"), dest);
+ else
+ ret = CHECK_COLLISION_DEST_VANISHED;
goto out;
}
@@ -2037,8 +2041,11 @@ int finalize_object_file(const char *tmpfile, const char *filename)
int finalize_object_file_flags(const char *tmpfile, const char *filename,
enum finalize_object_file_flags flags)
{
- struct stat st;
- int ret = 0;
+ unsigned retries = 0;
+ int ret;
+
+retry:
+ ret = 0;
if (object_creation_mode == OBJECT_CREATION_USES_RENAMES)
goto try_rename;
@@ -2059,6 +2066,8 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
* left to unlink.
*/
if (ret && ret != EEXIST) {
+ struct stat st;
+
try_rename:
if (!stat(filename, &st))
ret = EEXIST;
@@ -2074,9 +2083,17 @@ int finalize_object_file_flags(const char *tmpfile, const char *filename,
errno = saved_errno;
return error_errno(_("unable to write file %s"), filename);
}
- if (!(flags & FOF_SKIP_COLLISION_CHECK) &&
- check_collision(tmpfile, filename))
+ if (!(flags & FOF_SKIP_COLLISION_CHECK)) {
+ ret = check_collision(tmpfile, filename);
+ if (ret == CHECK_COLLISION_DEST_VANISHED) {
+ if (retries++ > 5)
+ return error(_("unable to write repeatedly vanishing file %s"),
+ filename);
+ goto retry;
+ }
+ else if (ret)
return -1;
+ }
unlink_or_warn(tmpfile);
}