diff --git a/include/git2/worktree.h b/include/git2/worktree.h index 84e2bc92e..e3bafc3d0 100644 --- a/include/git2/worktree.h +++ b/include/git2/worktree.h @@ -161,23 +161,44 @@ typedef enum { GIT_WORKTREE_PRUNE_WORKING_TREE = 1u << 2, } git_worktree_prune_t; +typedef struct git_worktree_prune_options { + unsigned int version; + + uint32_t flags; +} git_worktree_prune_options; + +#define GIT_WORKTREE_PRUNE_OPTIONS_VERSION 1 +#define GIT_WORKTREE_PRUNE_OPTIONS_INIT {GIT_WORKTREE_PRUNE_OPTIONS_VERSION,0} + /** - * Is the worktree prunable with the given set of flags? + * Initializes a `git_worktree_prune_options` with default vaules. + * Equivalent to creating an instance with + * GIT_WORKTREE_PRUNE_OPTIONS_INIT. + * + * @param opts the struct to initialize + * @param version Verison of struct; pass `GIT_WORKTREE_PRUNE_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_worktree_prune_init_options( + git_worktree_prune_options *opts, + unsigned int version); + +/** + * Is the worktree prunable with the given options? * * A worktree is not prunable in the following scenarios: * * - the worktree is linking to a valid on-disk worktree. The - * GIT_WORKTREE_PRUNE_VALID flag will cause this check to be - * ignored. - * - the worktree is not valid but locked. The - * GIT_WORKRTEE_PRUNE_LOCKED flag will cause this check to be - * ignored. + * `valid` member will cause this check to be ignored. + * - the worktree is locked. The `locked` flag will cause this + * check to be ignored. * * If the worktree is not valid and not locked or if the above * flags have been passed in, this function will return a * positive value. */ -GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, unsigned flags); +GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, + git_worktree_prune_options *opts); /** * Prune working tree @@ -187,10 +208,12 @@ GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt, unsigned flags); * `git_worktree_is_prunable` succeeds. * * @param wt Worktree to prune - * @param flags git_worktree_prune_t flags + * @param opts Specifies which checks to override. See + * `git_worktree_is_prunable`. May be NULL * @return 0 or an error code */ -GIT_EXTERN(int) git_worktree_prune(git_worktree *wt, unsigned flags); +GIT_EXTERN(int) git_worktree_prune(git_worktree *wt, + git_worktree_prune_options *opts); /** @} */ GIT_END_DECL diff --git a/src/worktree.c b/src/worktree.c index b9ed75991..f224b2333 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -456,11 +456,29 @@ out: return ret; } -int git_worktree_is_prunable(git_worktree *wt, unsigned flags) +int git_worktree_prune_init_options( + git_worktree_prune_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT); + return 0; +} + +int git_worktree_is_prunable(git_worktree *wt, + git_worktree_prune_options *opts) { git_buf reason = GIT_BUF_INIT; + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; - if ((flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && git_worktree_is_locked(&reason, wt)) { if (!reason.size) @@ -471,7 +489,7 @@ int git_worktree_is_prunable(git_worktree *wt, unsigned flags) return 0; } - if ((flags & GIT_WORKTREE_PRUNE_VALID) == 0 && + if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 && git_worktree_validate(wt) == 0) { giterr_set(GITERR_WORKTREE, "Not pruning valid working tree"); @@ -481,13 +499,22 @@ int git_worktree_is_prunable(git_worktree *wt, unsigned flags) return 1; } -int git_worktree_prune(git_worktree *wt, unsigned flags) +int git_worktree_prune(git_worktree *wt, + git_worktree_prune_options *opts) { + git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_buf path = GIT_BUF_INIT; char *wtpath; int err; - if (!git_worktree_is_prunable(wt, flags)) { + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); + + if (!git_worktree_is_prunable(wt, &popts)) { err = -1; goto out; } @@ -506,7 +533,7 @@ int git_worktree_prune(git_worktree *wt, unsigned flags) /* Skip deletion of the actual working tree if it does * not exist or deletion was not requested */ - if ((flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || + if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 || !git_path_exists(wt->gitlink_path)) { goto out; diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c index 95b173ef5..a10f50a2c 100644 --- a/tests/worktree/refs.c +++ b/tests/worktree/refs.c @@ -118,11 +118,14 @@ void test_worktree_refs__delete_fails_for_checked_out_branch(void) void test_worktree_refs__delete_succeeds_after_pruning_worktree(void) { + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_reference *branch; git_worktree *worktree; + opts.flags = GIT_WORKTREE_PRUNE_VALID; + cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename)); - cl_git_pass(git_worktree_prune(worktree, GIT_WORKTREE_PRUNE_VALID)); + cl_git_pass(git_worktree_prune(worktree, &opts)); git_worktree_free(worktree); cl_git_pass(git_branch_lookup(&branch, fixture.repo, diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c index 7ab86cceb..4ac3b8bba 100644 --- a/tests/worktree/worktree.c +++ b/tests/worktree/worktree.c @@ -454,13 +454,31 @@ void test_worktree_worktree__unlock_locked_worktree(void) git_worktree_free(wt); } -void test_worktree_worktree__prune_valid(void) +void test_worktree_worktree__prune_without_opts_fails(void) { git_worktree *wt; git_repository *repo; cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); - cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID)); + cl_git_fail(git_worktree_prune(wt, NULL)); + + /* Assert the repository is still valid */ + cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + + git_worktree_free(wt); + git_repository_free(repo); +} + +void test_worktree_worktree__prune_valid(void) +{ + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; + git_worktree *wt; + git_repository *repo; + + opts.flags = GIT_WORKTREE_PRUNE_VALID; + + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); + cl_git_pass(git_worktree_prune(wt, &opts)); /* Assert the repository is not valid anymore */ cl_git_fail(git_repository_open_from_worktree(&repo, wt)); @@ -471,27 +489,33 @@ void test_worktree_worktree__prune_valid(void) void test_worktree_worktree__prune_locked(void) { + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_worktree *wt; git_repository *repo; cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_git_pass(git_worktree_lock(wt, NULL)); - cl_git_fail(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID)); - cl_git_fail(git_worktree_prune(wt, ~GIT_WORKTREE_PRUNE_LOCKED)); + opts.flags = GIT_WORKTREE_PRUNE_VALID; + cl_git_fail(git_worktree_prune(wt, &opts)); /* Assert the repository is still valid */ cl_git_pass(git_repository_open_from_worktree(&repo, wt)); + opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_LOCKED; + cl_git_pass(git_worktree_prune(wt, &opts)); + git_worktree_free(wt); git_repository_free(repo); } -void test_worktree_worktree__prune_gitdir(void) +void test_worktree_worktree__prune_gitdir_only(void) { + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_worktree *wt; + opts.flags = GIT_WORKTREE_PRUNE_VALID; cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); - cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_VALID)); + cl_git_pass(git_worktree_prune(wt, &opts)); cl_assert(!git_path_exists(wt->gitdir_path)); cl_assert(git_path_exists(wt->gitlink_path)); @@ -499,12 +523,15 @@ void test_worktree_worktree__prune_gitdir(void) git_worktree_free(wt); } -void test_worktree_worktree__prune_both(void) +void test_worktree_worktree__prune_worktree(void) { + git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT; git_worktree *wt; + opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE; + cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); - cl_git_pass(git_worktree_prune(wt, GIT_WORKTREE_PRUNE_WORKING_TREE | GIT_WORKTREE_PRUNE_VALID)); + cl_git_pass(git_worktree_prune(wt, &opts)); cl_assert(!git_path_exists(wt->gitdir_path)); cl_assert(!git_path_exists(wt->gitlink_path));