Merge branch 'mh/ref-remove-empty-directory'
Deletion of a branch "foo/bar" could remove .git/refs/heads/foo once there no longer is any other branch whose name begins with "foo/", but we didn't do so so far. Now we do. * mh/ref-remove-empty-directory: (23 commits) files_transaction_commit(): clean up empty directories try_remove_empty_parents(): teach to remove parents of reflogs, too try_remove_empty_parents(): don't trash argument contents try_remove_empty_parents(): rename parameter "name" -> "refname" delete_ref_loose(): inline function delete_ref_loose(): derive loose reference path from lock log_ref_write_1(): inline function log_ref_setup(): manage the name of the reflog file internally log_ref_write_1(): don't depend on logfile argument log_ref_setup(): pass the open file descriptor back to the caller log_ref_setup(): improve robustness against races log_ref_setup(): separate code for create vs non-create log_ref_write(): inline function rename_tmp_log(): improve error reporting rename_tmp_log(): use raceproof_create_file() lock_ref_sha1_basic(): use raceproof_create_file() lock_ref_sha1_basic(): inline constant raceproof_create_file(): new function safe_create_leading_directories(): set errno on SCLD_EXISTS safe_create_leading_directories_const(): preserve errno ...
This commit is contained in:
76
sha1_file.c
76
sha1_file.c
@ -129,8 +129,10 @@ enum scld_error safe_create_leading_directories(char *path)
|
||||
*slash = '\0';
|
||||
if (!stat(path, &st)) {
|
||||
/* path exists */
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
errno = ENOTDIR;
|
||||
ret = SCLD_EXISTS;
|
||||
}
|
||||
} else if (mkdir(path, 0777)) {
|
||||
if (errno == EEXIST &&
|
||||
!stat(path, &st) && S_ISDIR(st.st_mode))
|
||||
@ -158,13 +160,85 @@ enum scld_error safe_create_leading_directories(char *path)
|
||||
|
||||
enum scld_error safe_create_leading_directories_const(const char *path)
|
||||
{
|
||||
int save_errno;
|
||||
/* path points to cache entries, so xstrdup before messing with it */
|
||||
char *buf = xstrdup(path);
|
||||
enum scld_error result = safe_create_leading_directories(buf);
|
||||
|
||||
save_errno = errno;
|
||||
free(buf);
|
||||
errno = save_errno;
|
||||
return result;
|
||||
}
|
||||
|
||||
int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
|
||||
{
|
||||
/*
|
||||
* The number of times we will try to remove empty directories
|
||||
* in the way of path. This is only 1 because if another
|
||||
* process is racily creating directories that conflict with
|
||||
* us, we don't want to fight against them.
|
||||
*/
|
||||
int remove_directories_remaining = 1;
|
||||
|
||||
/*
|
||||
* The number of times that we will try to create the
|
||||
* directories containing path. We are willing to attempt this
|
||||
* more than once, because another process could be trying to
|
||||
* clean up empty directories at the same time as we are
|
||||
* trying to create them.
|
||||
*/
|
||||
int create_directories_remaining = 3;
|
||||
|
||||
/* A scratch copy of path, filled lazily if we need it: */
|
||||
struct strbuf path_copy = STRBUF_INIT;
|
||||
|
||||
int ret, save_errno;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(*path);
|
||||
|
||||
retry_fn:
|
||||
ret = fn(path, cb);
|
||||
save_errno = errno;
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (errno == EISDIR && remove_directories_remaining-- > 0) {
|
||||
/*
|
||||
* A directory is in the way. Maybe it is empty; try
|
||||
* to remove it:
|
||||
*/
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
|
||||
goto retry_fn;
|
||||
} else if (errno == ENOENT && create_directories_remaining-- > 0) {
|
||||
/*
|
||||
* Maybe the containing directory didn't exist, or
|
||||
* maybe it was just deleted by a process that is
|
||||
* racing with us to clean up empty directories. Try
|
||||
* to create it:
|
||||
*/
|
||||
enum scld_error scld_result;
|
||||
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
do {
|
||||
scld_result = safe_create_leading_directories(path_copy.buf);
|
||||
if (scld_result == SCLD_OK)
|
||||
goto retry_fn;
|
||||
} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&path_copy);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
|
||||
{
|
||||
int i;
|
||||
|
Reference in New Issue
Block a user