diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2017-06-04 22:46:07 +0100 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2017-06-04 22:46:07 +0100 |
commit | dd0aa811dd329f5040c70f7aac514b3757eb2453 (patch) | |
tree | 1684ec2ac5f1b549489409e25d65f3c9a71d6572 | |
parent | 82e929a88bc79dc5ebd9a8484a40319ac6768c9b (diff) | |
parent | f0848dd7e0d137939eb969af03bd75f9bf318286 (diff) | |
download | libgit2-dd0aa811dd329f5040c70f7aac514b3757eb2453.tar.gz |
Merge branch 'pr/4228'
-rw-r--r-- | include/git2/worktree.h | 67 | ||||
-rw-r--r-- | src/worktree.c | 73 | ||||
-rw-r--r-- | tests/worktree/refs.c | 5 | ||||
-rw-r--r-- | tests/worktree/submodule.c | 2 | ||||
-rw-r--r-- | tests/worktree/worktree.c | 76 |
5 files changed, 192 insertions, 31 deletions
diff --git a/include/git2/worktree.h b/include/git2/worktree.h index 4c4f9284d..d3fa88e3f 100644 --- a/include/git2/worktree.h +++ b/include/git2/worktree.h @@ -74,6 +74,27 @@ GIT_EXTERN(void) git_worktree_free(git_worktree *wt); */ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt); +typedef struct git_worktree_add_options { + unsigned int version; + + int lock; /**< lock newly created worktree */ +} git_worktree_add_options; + +#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1 +#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0} + +/** + * Initializes a `git_worktree_add_options` with default vaules. + * Equivalent to creating an instance with + * GIT_WORKTREE_ADD_OPTIONS_INIT. + * + * @param opts the struct to initialize + * @param version Verison of struct; pass `GIT_WORKTREE_ADD_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +int git_worktree_add_init_options(git_worktree_add_options *opts, + unsigned int version); + /** * Add a new working tree * @@ -85,9 +106,12 @@ GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt); * @param repo Repository to create working tree for * @param name Name of the working tree * @param path Path to create working tree at + * @param opts Options to modify default behavior. May be NULL * @return 0 or an error code */ -GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *path); +GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo, + const char *name, const char *path, + const git_worktree_add_options *opts); /** * Lock worktree if not already locked @@ -137,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} + +/** + * 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 set of flags? + * 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 @@ -163,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 55fbf5204..f224b2333 100644 --- a/src/worktree.c +++ b/src/worktree.c @@ -269,15 +269,32 @@ out: return err; } -int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, const char *worktree) +int git_worktree_add_init_options(git_worktree_add_options *opts, + unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version, + git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT); + return 0; +} + +int git_worktree_add(git_worktree **out, git_repository *repo, + const char *name, const char *worktree, + const git_worktree_add_options *opts) { git_buf gitdir = GIT_BUF_INIT, wddir = GIT_BUF_INIT, buf = GIT_BUF_INIT; git_reference *ref = NULL, *head = NULL; git_commit *commit = NULL; git_repository *wt = NULL; git_checkout_options coopts = GIT_CHECKOUT_OPTIONS_INIT; + git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT; int err; + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options"); + + if (opts) + memcpy(&wtopts, opts, sizeof(wtopts)); + assert(out && repo && name && worktree); *out = NULL; @@ -301,6 +318,21 @@ int git_worktree_add(git_worktree **out, git_repository *repo, const char *name, if ((err = git_path_prettify_dir(&wddir, worktree, NULL)) < 0) goto out; + if (wtopts.lock) { + int fd; + + if ((err = git_buf_joinpath(&buf, gitdir.ptr, "locked")) < 0) + goto out; + + if ((fd = p_creat(buf.ptr, 0644)) < 0) { + err = fd; + goto out; + } + + p_close(fd); + git_buf_clear(&buf); + } + /* Create worktree .git file */ if ((err = git_buf_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0) goto out; @@ -424,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; + + GITERR_CHECK_VERSION( + opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION, + "git_worktree_prune_options"); + + if (opts) + memcpy(&popts, opts, sizeof(popts)); - if ((flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && + if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0 && git_worktree_is_locked(&reason, wt)) { if (!reason.size) @@ -439,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"); @@ -449,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; } @@ -474,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/submodule.c b/tests/worktree/submodule.c index 562077597..294385226 100644 --- a/tests/worktree/submodule.c +++ b/tests/worktree/submodule.c @@ -74,7 +74,7 @@ void test_worktree_submodule__resolve_relative_url(void) cl_git_pass(git_repository_open(&child.repo, WORKTREE_CHILD)); /* Create worktree of submodule repository */ - cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr)); + cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr, NULL)); cl_git_pass(git_repository_open_from_worktree(&repo, wt)); cl_git_pass(git_submodule_resolve_url(&sm_relative_path, repo, diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c index 73991bff7..4ac3b8bba 100644 --- a/tests/worktree/worktree.c +++ b/tests/worktree/worktree.c @@ -215,7 +215,7 @@ void test_worktree_worktree__init(void) git_buf path = GIT_BUF_INIT; cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); - cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr)); + cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); /* Open and verify created repo */ cl_git_pass(git_repository_open(&repo, path.ptr)); @@ -228,6 +228,31 @@ void test_worktree_worktree__init(void) git_repository_free(repo); } +void test_worktree_worktree__add_locked(void) +{ + git_worktree *wt; + git_repository *repo; + git_reference *branch; + git_buf path = GIT_BUF_INIT; + git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT; + + opts.lock = 1; + + cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-locked")); + cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-locked", path.ptr, &opts)); + + /* Open and verify created repo */ + cl_assert(git_worktree_is_locked(NULL, wt)); + cl_git_pass(git_repository_open(&repo, path.ptr)); + cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-locked/") == 0); + cl_git_pass(git_branch_lookup(&branch, repo, "worktree-locked", GIT_BRANCH_LOCAL)); + + git_buf_free(&path); + git_worktree_free(wt); + git_reference_free(branch); + git_repository_free(repo); +} + void test_worktree_worktree__init_existing_branch(void) { git_reference *head, *branch; @@ -240,7 +265,7 @@ void test_worktree_worktree__init_existing_branch(void) cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false)); cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); - cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr)); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); git_buf_free(&path); git_commit_free(commit); @@ -254,7 +279,7 @@ void test_worktree_worktree__init_existing_worktree(void) git_buf path = GIT_BUF_INIT; cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../worktree-new")); - cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr)); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr, NULL)); cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree")); cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink); @@ -279,7 +304,7 @@ void test_worktree_worktree__init_existing_path(void) } cl_git_pass(git_buf_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree")); - cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr)); + cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL)); /* Verify files have not been re-created */ for (i = 0; i < ARRAY_SIZE(wtfiles); i++) { @@ -303,7 +328,7 @@ void test_worktree_worktree__init_submodule(void) cl_git_pass(git_buf_joinpath(&path, repo->workdir, "sm_unchanged")); cl_git_pass(git_repository_open(&sm, path.ptr)); cl_git_pass(git_buf_joinpath(&path, repo->workdir, "../worktree/")); - cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr)); + cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr, NULL)); cl_git_pass(git_repository_open_from_worktree(&wt, worktree)); cl_git_pass(git_path_prettify_dir(&path, path.ptr, NULL)); @@ -429,13 +454,31 @@ void test_worktree_worktree__unlock_locked_worktree(void) git_worktree_free(wt); } +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_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, GIT_WORKTREE_PRUNE_VALID)); + 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)); @@ -446,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)); @@ -474,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)); |