From a6e0f315f5f82babd669d93705059c24a6772084 Mon Sep 17 00:00:00 2001 From: schu Date: Sun, 29 May 2011 16:46:24 +0200 Subject: [PATCH 1/7] Add test case checking renaming of a branch to a new name prefixed with the old name succeeds, e.g. refs/heads/foo -> refs/heads/foo/bar Reported-by: nulltoken Signed-off-by: schu --- tests/t10-refs.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/t10-refs.c b/tests/t10-refs.c index fb252f427..0d1bba3ff 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -686,6 +686,37 @@ BEGIN_TEST(rename6, "can not overwrite name of existing reference") close_temp_repo(repo); END_TEST +static const char *ref_two_name_new = "refs/heads/two/two"; + +BEGIN_TEST(rename7, "can be renamed to a new name prefixed with the old name") + git_reference *ref, *ref_two, *looked_up_ref; + git_repository *repo; + git_oid id; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + must_pass(git_reference_lookup(&ref, repo, ref_master_name)); + must_be_true(ref->type & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id)); + + /* An existing reference... */ + must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name)); + + /* Can be rename to a new name starting with the old name. */ + must_pass(git_reference_rename(looked_up_ref, ref_two_name_new)); + + /* Check we actually renamed it */ + must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new)); + must_be_true(!strcmp(looked_up_ref->name, ref_two_name_new)); + must_fail(git_reference_lookup(&looked_up_ref, repo, ref_two_name)); + + close_temp_repo(repo); +END_TEST + BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove both tracks in the filesystem") git_reference *looked_up_ref, *another_looked_up_ref; git_repository *repo; @@ -968,6 +999,7 @@ BEGIN_SUITE(refs) ADD_TEST(rename4); ADD_TEST(rename5); ADD_TEST(rename6); + ADD_TEST(rename7); ADD_TEST(delete0); ADD_TEST(list0); From e190da78f373511ca999d32f66115bbc75705abf Mon Sep 17 00:00:00 2001 From: schu Date: Thu, 9 Jun 2011 18:11:11 +0200 Subject: [PATCH 2/7] fileops: add convenience function is_dot_or_dotdot() Signed-off-by: schu --- src/fileops.c | 9 ++------- src/fileops.h | 8 ++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 52aeb41a3..edde3aa46 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -231,13 +231,8 @@ int git_futils_direach( size_t de_len; int result; - /* always skip '.' and '..' */ - if (de->d_name[0] == '.') { - if (de->d_name[1] == '\0') - continue; - if (de->d_name[1] == '.' && de->d_name[2] == '\0') - continue; - } + if (is_dot_or_dotdot(de->d_name)) + continue; de_len = strlen(de->d_name); if (path_sz < wd_len + de_len + 1) { diff --git a/src/fileops.h b/src/fileops.h index 4bfebe9d4..cc88682ac 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -102,6 +102,14 @@ extern int git_futils_mv_withpath(const char *from, const char *to); */ extern git_off_t git_futils_filesize(git_file fd); +/* Taken from git.git */ +static inline int is_dot_or_dotdot(const char *name) +{ + return (name[0] == '.' && + (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0'))); +} + /** * Read-only map all or part of a file into memory. * When possible this function should favor a virtual memory From 42b3a460976757dbe7c4755fd60c3f542a9fb1e2 Mon Sep 17 00:00:00 2001 From: schu Date: Thu, 9 Jun 2011 19:51:33 +0200 Subject: [PATCH 3/7] fileops: add git_futils_rmdir_recurs() git_futils_rmdir_recurs() shall remove the given directory and all subdirectories. This happens only if the directories are empty. Signed-off-by: schu --- src/fileops.c | 25 +++++++++++++++++++++++++ src/fileops.h | 3 +++ 2 files changed, 28 insertions(+) diff --git a/src/fileops.c b/src/fileops.c index edde3aa46..afca0329f 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -301,6 +301,31 @@ int git_futils_mkdir_r(const char *path, int mode) return GIT_SUCCESS; } +static int _rmdir_recurs_cb(void *GIT_UNUSED(nil), char *path) +{ + int error = GIT_SUCCESS; + + error = git_futils_isdir(path); + if (error == GIT_SUCCESS) { + size_t root_size = strlen(path); + + if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_cb, NULL)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to remove directory `%s`", path); + + path[root_size] = '\0'; + return p_rmdir(path); + } + + return git__rethrow(error, "Failed to remove directory. `%s` is not a directory", path); +} + +int git_futils_rmdir_recurs(const char *path) +{ + char p[GIT_PATH_MAX]; + strncpy(p, path, GIT_PATH_MAX); + return _rmdir_recurs_cb(NULL, p); +} + int git_futils_cmp_path(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2) { diff --git a/src/fileops.h b/src/fileops.h index cc88682ac..a2f66a5da 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -102,6 +102,9 @@ extern int git_futils_mv_withpath(const char *from, const char *to); */ extern git_off_t git_futils_filesize(git_file fd); +/* Recursively remove an empty directory structure */ +extern int git_futils_rmdir_recurs(const char *path); + /* Taken from git.git */ static inline int is_dot_or_dotdot(const char *name) { From 7ea50f6077d0da28a471e8de2dc32dcdc994c5c6 Mon Sep 17 00:00:00 2001 From: schu Date: Thu, 9 Jun 2011 19:56:42 +0200 Subject: [PATCH 4/7] Add tests for git_futils_rmdir_resurs() Signed-off-by: schu --- tests/t00-core.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/t00-core.c b/tests/t00-core.c index ba5188a43..0042da39c 100644 --- a/tests/t00-core.c +++ b/tests/t00-core.c @@ -474,6 +474,56 @@ BEGIN_TEST(filebuf2, "make sure git_filebuf_write writes large buffer correctly" must_pass(p_unlink(test)); END_TEST +static char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test"; + +static int setup_empty_tmp_dir() +{ + char path[GIT_PATH_MAX]; + + if (mkdir(empty_tmp_dir, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one"); + if (mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_one"); + if (mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_two"); + if (mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_two/three"); + if (mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/two"); + if (mkdir(path, 0755)) + return -1; + + return 0; +} + +BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively") + must_pass(setup_empty_tmp_dir()); + must_pass(git_futils_rmdir_recurs(empty_tmp_dir)); +END_TEST + +BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively") + char file[GIT_PATH_MAX]; + + must_pass(setup_empty_tmp_dir()); + git_path_join(file, empty_tmp_dir, "/two/file.txt"); + must_pass(fd = p_creat(file, 0755)); + must_pass(fd); + must_fail(git_futils_rmdir_recurs(empty_tmp_dir)); + must_pass(p_close(fd)); + must_pass(p_unlink(file)); + must_pass(git_futils_rmdir_recurs(empty_tmp_dir)); +END_TEST + BEGIN_SUITE(core) ADD_TEST(string0); ADD_TEST(string1); @@ -496,4 +546,7 @@ BEGIN_SUITE(core) ADD_TEST(filebuf0); ADD_TEST(filebuf1); ADD_TEST(filebuf2); + + ADD_TEST(rmdir0); + ADD_TEST(rmdir1); END_SUITE From 0ffcf78a30377971d4928439ae387396b3922cbf Mon Sep 17 00:00:00 2001 From: schu Date: Tue, 14 Jun 2011 15:16:43 +0200 Subject: [PATCH 5/7] reference_rename: git compliant reference renaming So far libgit2 didn't handle the following scenarios: * Rename of reference m -> m/m * Rename of reference n/n -> n Fixed. Since we don't write reflogs, we have to delete any old reflog for the renamed reference. Otherwise git.git will possibly fail when it finds invalid logs. Reported-by: nulltoken Signed-off-by: schu --- src/refs.c | 200 +++++++++++++++++++++++++---------------------- src/refs.h | 2 + tests/t10-refs.c | 4 +- 3 files changed, 111 insertions(+), 95 deletions(-) diff --git a/src/refs.c b/src/refs.c index eea96adba..94a7a15c1 100644 --- a/src/refs.c +++ b/src/refs.c @@ -1287,138 +1287,152 @@ int git_reference_set_target(git_reference *ref, const char *target) * Other */ -/* - * Rename a reference - * - * If the reference is packed, we need to rewrite the - * packfile to remove the reference from it and create - * the reference back as a loose one. - * - * If the reference is loose, we just rename it on - * the filesystem. - * - * We also need to re-insert the reference on its corresponding - * in-memory cache, since the caches are indexed by refname. - */ int git_reference_rename(git_reference *ref, const char *new_name, int force) { int error; - char *old_name; - char old_path[GIT_PATH_MAX], new_path[GIT_PATH_MAX], normalized_name[GIT_REFNAME_MAX]; - git_reference *looked_up_ref, *old_ref = NULL; + char *old_name = git__strdup(ref->name); + char new_path[GIT_PATH_MAX]; + char old_path[GIT_PATH_MAX]; + char old_logs[GIT_PATH_MAX]; + char normalized[GIT_REFNAME_MAX]; + const char *target_ref = NULL; + const char *head_target = NULL; + const git_oid *target_oid = NULL; + git_reference *new_ref = NULL, *old_ref = NULL, *head = NULL; assert(ref); - /* Ensure the name is valid */ - error = normalize_name(normalized_name, sizeof(normalized_name), new_name, ref->type & GIT_REF_OID); + error = normalize_name(normalized, sizeof(normalized), new_name, ref->type & GIT_REF_OID); if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to rename reference"); + return git__rethrow(error, "Failed to rename reference. Invalid name"); - new_name = normalized_name; + new_name = normalized; - /* Ensure we're not going to overwrite an existing reference - unless the user has allowed us */ - error = git_reference_lookup(&looked_up_ref, ref->owner, new_name); + error = git_reference_lookup(&new_ref, ref->owner, new_name); if (error == GIT_SUCCESS && !force) return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists"); - if (error < GIT_SUCCESS && - error != GIT_ENOTFOUND) - return git__rethrow(error, "Failed to rename reference"); + if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) + goto cleanup; if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Reference already exists"); + return git__rethrow(error, "Failed to rename reference. Reference already exists"); - old_name = ref->name; - ref->name = git__strdup(new_name); + /* + * First, we backup the reference targets. Just keeping the old + * reference won't work, since we may have to remove it to create + * the new reference, e.g. when renaming foo/bar -> foo. + */ - if (ref->name == NULL) { - ref->name = old_name; - return GIT_ENOMEM; + if (ref->type & GIT_REF_SYMBOLIC) { + if ((target_ref = git_reference_target(ref)) == NULL) + goto cleanup; + } else { + if ((target_oid = git_reference_oid(ref)) == NULL) + goto cleanup; } + /* + * Now delete the old ref and remove an possibly existing directory + * named `new_name`. + */ + if (ref->type & GIT_REF_PACKED) { - /* write the packfile to disk; note - * that the state of the in-memory cache is not - * consistent, because the reference is indexed - * by its old name but it already has the new one. - * This doesn't affect writing, though, and allows - * us to rollback if writing fails - */ - - /* Create the loose ref under its new name */ - error = loose_write(ref); - if (error < GIT_SUCCESS) { - ref->type |= GIT_REF_PACKED; - goto cleanup; - } - ref->type &= ~GIT_REF_PACKED; - /* Remove from the packfile cache in order to avoid packing it back - * Note : we do not rely on git_reference_delete() because this would - * invalidate the reference. - */ git_hashtable_remove(ref->owner->references.packfile, old_name); - - /* Recreate the packed-refs file without the reference */ - error = packed_write(ref->owner); - if (error < GIT_SUCCESS) - goto rename_loose_to_old_name; - + if ((error = packed_write(ref->owner)) < GIT_SUCCESS) + goto rollback; } else { git_path_join(old_path, ref->owner->path_repository, old_name); - git_path_join(new_path, ref->owner->path_repository, ref->name); - - error = git_futils_mv_withpath(old_path, new_path); - if (error < GIT_SUCCESS) + if ((error = p_unlink(old_path)) < GIT_SUCCESS) goto cleanup; - /* Once succesfully renamed, remove from the cache the reference known by its old name*/ git_hashtable_remove(ref->owner->references.loose_cache, old_name); } - /* Store the renamed reference into the loose ref cache */ - error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **) &old_ref); + git_path_join(new_path, ref->owner->path_repository, new_name); - /* If we force-replaced, we need to free the old reference */ - if(old_ref) - reference_free(old_ref); + if (git_futils_exists(new_path) == GIT_SUCCESS) { + if (git_futils_isdir(new_path) == GIT_SUCCESS) { + if ((error = git_futils_rmdir_recurs(new_path, 0)) < GIT_SUCCESS) + goto rollback; + } else goto rollback; + } + /* + * Crude hack: delete any logs till we support proper reflogs. + * Otherwise git.git will possibly fail and leave a mess. git.git + * writes reflogs by default in any repo with a working directory: + * + * "We only enable reflogs in repositories that have a working directory + * associated with them, as shared/bare repositories do not have + * an easy means to prune away old log entries, or may fail logging + * entirely if the user's gecos information is not valid during a push. + * This heuristic was suggested on the mailing list by Junio." + * + * Shawn O. Pearce - 0bee59186976b1d9e6b2dd77332480c9480131d5 + * + * TODO + * + */ + + git_path_join_n(old_logs, 3, ref->owner->path_repository, "logs", old_name); + if (git_futils_exists(old_logs) == GIT_SUCCESS) { + if (git_futils_isfile(old_logs) == GIT_SUCCESS) + if ((error = p_unlink(old_logs)) < GIT_SUCCESS) + goto rollback; + } + + /* + * Finally we can create the new reference. + */ + + if (ref->type & GIT_REF_SYMBOLIC) { + if ((error = git_reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS) + goto rollback; + } else { + if ((error = git_reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS) + goto rollback; + } + + free(ref->name); + ref->name = new_ref->name; + + if ((error = git_hashtable_insert2(ref->owner->references.loose_cache, new_ref->name, new_ref, (void **)&old_ref)) < GIT_SUCCESS) + goto rollback; + + /* + * Check if we have to update HEAD. + */ + + if ((error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS) + goto cleanup; + + head_target = git_reference_target(head); + + if (head_target && !strcmp(head_target, old_name)) + if ((error = git_reference_create_symbolic(&head, new_ref->owner, "HEAD", new_ref->name, 1)) < GIT_SUCCESS) + goto rollback; + +cleanup: free(old_name); return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference"); -cleanup: - /* restore the old name if this failed */ - free(ref->name); - ref->name = old_name; - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference"); +rollback: + /* + * Try to create the old reference again. + */ + if (ref->type & GIT_REF_SYMBOLIC) + error = git_reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0); + else + error = git_reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0); -rename_loose_to_old_name: - /* If we hit this point. Something *bad* happened! Think "Ghostbusters - * crossing the streams" definition of bad. - * Either the packed-refs has been correctly generated and something else - * has gone wrong, or the writing of the new packed-refs has failed, and - * we're stuck with the old one. As a loose ref always takes priority over - * a packed ref, we'll eventually try and rename the generated loose ref to - * its former name. It even that fails, well... we might have lost the reference - * for good. :-/ - */ - - git_path_join(old_path, ref->owner->path_repository, ref->name); - git_path_join(new_path, ref->owner->path_repository, old_name); - - /* No error checking. We'll return the initial error */ - git_futils_mv_withpath(old_path, new_path); - - /* restore the old name */ - free(ref->name); ref->name = old_name; - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference"); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Failed to rollback"); } - /* * Delete a reference. * diff --git a/src/refs.h b/src/refs.h index a0159b091..dfac455e0 100644 --- a/src/refs.h +++ b/src/refs.h @@ -11,6 +11,8 @@ #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" +#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF" + #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled " diff --git a/tests/t10-refs.c b/tests/t10-refs.c index 0d1bba3ff..83ccf300b 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -701,13 +701,13 @@ BEGIN_TEST(rename7, "can be renamed to a new name prefixed with the old name") git_oid_cpy(&id, git_reference_oid(ref)); /* Create loose references */ - must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id)); + must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0)); /* An existing reference... */ must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name)); /* Can be rename to a new name starting with the old name. */ - must_pass(git_reference_rename(looked_up_ref, ref_two_name_new)); + must_pass(git_reference_rename(looked_up_ref, ref_two_name_new, 0)); /* Check we actually renamed it */ must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new)); From 1ee5fd903d00930aab08edecc6b409a34761cf7e Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 3 Jul 2011 14:42:32 +0200 Subject: [PATCH 6/7] Fix windows specific issues - msvc compilation warnings - not released file handle that prevents file removal --- src/fileops.c | 2 ++ src/fileops.h | 2 +- tests/t00-core.c | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index afca0329f..ab3f43cc5 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -305,6 +305,8 @@ static int _rmdir_recurs_cb(void *GIT_UNUSED(nil), char *path) { int error = GIT_SUCCESS; + GIT_UNUSED_ARG(nil) + error = git_futils_isdir(path); if (error == GIT_SUCCESS) { size_t root_size = strlen(path); diff --git a/src/fileops.h b/src/fileops.h index a2f66a5da..cd3ff2f10 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -106,7 +106,7 @@ extern git_off_t git_futils_filesize(git_file fd); extern int git_futils_rmdir_recurs(const char *path); /* Taken from git.git */ -static inline int is_dot_or_dotdot(const char *name) +GIT_INLINE(int) is_dot_or_dotdot(const char *name) { return (name[0] == '.' && (name[1] == '\0' || diff --git a/tests/t00-core.c b/tests/t00-core.c index 0042da39c..1e9f97521 100644 --- a/tests/t00-core.c +++ b/tests/t00-core.c @@ -513,13 +513,14 @@ END_TEST BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively") char file[GIT_PATH_MAX]; + int fd; must_pass(setup_empty_tmp_dir()); git_path_join(file, empty_tmp_dir, "/two/file.txt"); - must_pass(fd = p_creat(file, 0755)); + fd = p_creat(file, 0755); must_pass(fd); - must_fail(git_futils_rmdir_recurs(empty_tmp_dir)); must_pass(p_close(fd)); + must_fail(git_futils_rmdir_recurs(empty_tmp_dir)); must_pass(p_unlink(file)); must_pass(git_futils_rmdir_recurs(empty_tmp_dir)); END_TEST From 1b938a58267f0a11240f0e7da7233607c6b78a2d Mon Sep 17 00:00:00 2001 From: nulltoken Date: Sun, 3 Jul 2011 22:27:05 +0200 Subject: [PATCH 7/7] Remove duplicated recursive directory removal related code --- src/fileops.c | 13 ++++++++----- src/fileops.h | 5 ++--- tests/t00-core.c | 6 +++--- tests/t06-index.c | 2 +- tests/t12-repo.c | 15 +++++++-------- tests/test_helpers.c | 30 +----------------------------- 6 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index ab3f43cc5..9f3a65d27 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -301,7 +301,7 @@ int git_futils_mkdir_r(const char *path, int mode) return GIT_SUCCESS; } -static int _rmdir_recurs_cb(void *GIT_UNUSED(nil), char *path) +static int _rmdir_recurs_foreach(void *force_removal_of_non_empty_directory, char *path) { int error = GIT_SUCCESS; @@ -311,21 +311,24 @@ static int _rmdir_recurs_cb(void *GIT_UNUSED(nil), char *path) if (error == GIT_SUCCESS) { size_t root_size = strlen(path); - if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_cb, NULL)) < GIT_SUCCESS) + if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_foreach, force_removal_of_non_empty_directory)) < GIT_SUCCESS) return git__rethrow(error, "Failed to remove directory `%s`", path); path[root_size] = '\0'; return p_rmdir(path); } - return git__rethrow(error, "Failed to remove directory. `%s` is not a directory", path); + if (*(int *)(force_removal_of_non_empty_directory)) + return p_unlink(path); + else + return git__rethrow(error, "Failed to remove directory. `%s` is not a directory", path); } -int git_futils_rmdir_recurs(const char *path) +int git_futils_rmdir_recurs(const char *path, int force_removal_of_non_empty_directory) { char p[GIT_PATH_MAX]; strncpy(p, path, GIT_PATH_MAX); - return _rmdir_recurs_cb(NULL, p); + return _rmdir_recurs_foreach(&force_removal_of_non_empty_directory, p); } int git_futils_cmp_path(const char *name1, int len1, int isdir1, diff --git a/src/fileops.h b/src/fileops.h index cd3ff2f10..48071d6d1 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -80,6 +80,8 @@ extern int git_futils_mkdir_r(const char *path, int mode); */ extern int git_futils_mkpath2file(const char *path); +extern int git_futils_rmdir_recurs(const char *path, int force_removal_of_non_empty_directory); + /** * Create and open a temporary file with a `_git2_` suffix */ @@ -102,9 +104,6 @@ extern int git_futils_mv_withpath(const char *from, const char *to); */ extern git_off_t git_futils_filesize(git_file fd); -/* Recursively remove an empty directory structure */ -extern int git_futils_rmdir_recurs(const char *path); - /* Taken from git.git */ GIT_INLINE(int) is_dot_or_dotdot(const char *name) { diff --git a/tests/t00-core.c b/tests/t00-core.c index 1e9f97521..5e2f0da17 100644 --- a/tests/t00-core.c +++ b/tests/t00-core.c @@ -508,7 +508,7 @@ static int setup_empty_tmp_dir() BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively") must_pass(setup_empty_tmp_dir()); - must_pass(git_futils_rmdir_recurs(empty_tmp_dir)); + must_pass(git_futils_rmdir_recurs(empty_tmp_dir, 0)); END_TEST BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively") @@ -520,9 +520,9 @@ BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively") fd = p_creat(file, 0755); must_pass(fd); must_pass(p_close(fd)); - must_fail(git_futils_rmdir_recurs(empty_tmp_dir)); + must_fail(git_futils_rmdir_recurs(empty_tmp_dir, 0)); must_pass(p_unlink(file)); - must_pass(git_futils_rmdir_recurs(empty_tmp_dir)); + must_pass(git_futils_rmdir_recurs(empty_tmp_dir, 0)); END_TEST BEGIN_SUITE(core) diff --git a/tests/t06-index.c b/tests/t06-index.c index 3cbb5a9f0..4a111b42e 100644 --- a/tests/t06-index.c +++ b/tests/t06-index.c @@ -210,7 +210,7 @@ BEGIN_TEST(add0, "add a new file to the index") git_index_free(index); git_repository_free(repo); - rmdir_recurs(TEMP_REPO_FOLDER); + must_pass(git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1)); END_TEST BEGIN_SUITE(index) diff --git a/tests/t12-repo.c b/tests/t12-repo.c index 6d897a14e..e1726e07c 100644 --- a/tests/t12-repo.c +++ b/tests/t12-repo.c @@ -141,13 +141,13 @@ static int ensure_repository_init( goto cleanup; git_repository_free(repo); - rmdir_recurs(working_directory); + git_futils_rmdir_recurs(working_directory, 1); return GIT_SUCCESS; cleanup: git_repository_free(repo); - rmdir_recurs(working_directory); + git_futils_rmdir_recurs(working_directory, 1); return GIT_ERROR; } @@ -193,7 +193,7 @@ BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping git_repository_free(repo); must_pass(chdir(current_workdir)); - rmdir_recurs(TEMP_REPO_FOLDER); + must_pass(git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1)); END_TEST #define EMPTY_BARE_REPOSITORY_NAME "empty_bare.git" @@ -210,7 +210,7 @@ BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git" must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) == NULL); git_repository_free(repo); - must_pass(rmdir_recurs(TEMP_REPO_FOLDER)); + must_pass(git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1)); END_TEST #define SOURCE_EMPTY_REPOSITORY_NAME "empty_standard_repo/.gitted" @@ -229,7 +229,7 @@ BEGIN_TEST(open1, "Open a standard repository that has just been initialized by must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) != NULL); git_repository_free(repo); - must_pass(rmdir_recurs(TEMP_REPO_FOLDER)); + must_pass(git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1)); END_TEST @@ -257,7 +257,7 @@ BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of t git_repository_free(repo); must_pass(chdir(current_workdir)); - rmdir_recurs(TEMP_REPO_FOLDER); + must_pass(git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1)); END_TEST BEGIN_TEST(empty0, "test if a repository is empty or not") @@ -392,7 +392,6 @@ BEGIN_TEST(discover0, "test discover") char found_path[GIT_PATH_MAX]; int mode = 0755; - rmdir_recurs(DISCOVER_FOLDER); must_pass(append_ceiling_dir(ceiling_dirs,TEST_RESOURCES)); git_futils_mkdir_r(DISCOVER_FOLDER, mode); @@ -447,7 +446,7 @@ BEGIN_TEST(discover0, "test discover") must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path)); must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path)); - rmdir_recurs(DISCOVER_FOLDER); + must_pass(git_futils_rmdir_recurs(DISCOVER_FOLDER, 1)); git_repository_free(repo); END_TEST diff --git a/tests/test_helpers.c b/tests/test_helpers.c index 5c2ccee15..f2f37a1e4 100644 --- a/tests/test_helpers.c +++ b/tests/test_helpers.c @@ -176,34 +176,6 @@ int cmp_files(const char *a, const char *b) return error; } -static int remove_filesystem_element_recurs(void *GIT_UNUSED(nil), char *path) -{ - int error = GIT_SUCCESS; - - GIT_UNUSED_ARG(nil); - - error = git_futils_isdir(path); - if (error == GIT_SUCCESS) { - size_t root_size = strlen(path); - - error = git_futils_direach(path, GIT_PATH_MAX, remove_filesystem_element_recurs, NULL); - if (error < GIT_SUCCESS) - return error; - - path[root_size] = 0; - return rmdir(path); - } - - return p_unlink(path); -} - -int rmdir_recurs(const char *directory_path) -{ - char buffer[GIT_PATH_MAX]; - strcpy(buffer, directory_path); - return remove_filesystem_element_recurs(NULL, buffer); -} - typedef struct { size_t src_len, dst_len; char *dst; @@ -255,7 +227,7 @@ int open_temp_repo(git_repository **repo, const char *path) void close_temp_repo(git_repository *repo) { git_repository_free(repo); - rmdir_recurs(TEMP_REPO_FOLDER); + git_futils_rmdir_recurs(TEMP_REPO_FOLDER, 1); } static int remove_placeholders_recurs(void *filename, char *path)