Commit 450a78bf by Vicent Martí

Merge pull request #1545 from ethomson/checkout_dirs_in_use

allow checkout to proceed when a dir to be removed is in use (win32)
parents 0ed3fa8a e09d18ee
......@@ -134,6 +134,9 @@ typedef enum {
/** Treat pathspec as simple list of exact match file paths */
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
/** Ignore directories in use, they will be left empty */
GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18),
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
......
......@@ -955,6 +955,9 @@ static int checkout_remove_the_old(
uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES)
flg |= GIT_RMDIR_SKIP_NONEMPTY;
git_buf_truncate(&data->path, data->workdir_len);
git_vector_foreach(&data->diff->deltas, i, delta) {
......
......@@ -444,7 +444,7 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
if (data->error < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST))
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
data->error = 0;
else
futils__error_cannot_rmdir(path->ptr, NULL);
......@@ -480,7 +480,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
if (en == ENOENT || en == ENOTDIR) {
giterr_clear();
error = 0;
} else if (en == ENOTEMPTY || en == EEXIST) {
} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
giterr_clear();
error = GIT_ITEROVER;
} else {
......
......@@ -314,9 +314,20 @@ int p_chmod(const char* path, mode_t mode)
int p_rmdir(const char* path)
{
int error;
wchar_t buf[GIT_WIN_PATH];
git__utf8_to_16(buf, GIT_WIN_PATH, path);
return _wrmdir(buf);
error = _wrmdir(buf);
/* _wrmdir() is documented to return EACCES if "A program has an open
* handle to the directory." This sounds like what everybody else calls
* EBUSY. Let's convert appropriate error codes.
*/
if (GetLastError() == ERROR_SHARING_VIOLATION)
errno = EBUSY;
return error;
}
int p_hide_directory__w32(const char *path)
......
......@@ -526,3 +526,66 @@ void test_checkout_tree__can_write_to_empty_dirs(void)
git_object_free(obj);
}
void test_checkout_tree__fails_when_dir_in_use(void)
{
#ifdef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
git_oid oid;
git_object *obj = NULL;
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_assert(git_path_isfile("testrepo/a/b.txt"));
git_object_free(obj);
cl_git_pass(p_chdir("testrepo/a"));
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
cl_git_pass(p_chdir("../.."));
cl_assert(git_path_is_empty_dir("testrepo/a"));
#endif
}
void test_checkout_tree__can_continue_when_dir_in_use(void)
{
#ifdef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
git_oid oid;
git_object *obj = NULL;
opts.checkout_strategy = GIT_CHECKOUT_FORCE |
GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_assert(git_path_isfile("testrepo/a/b.txt"));
git_object_free(obj);
cl_git_pass(p_chdir("testrepo/a"));
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
cl_git_pass(p_chdir("../.."));
cl_assert(git_path_is_empty_dir("testrepo/a"));
#endif
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment