diff options
| author | Vicent Martà <vicent@github.com> | 2013-06-21 14:29:21 -0700 |
|---|---|---|
| committer | Vicent Martà <vicent@github.com> | 2013-06-21 14:29:21 -0700 |
| commit | 5d669f0a96ab858750f4fdd90f138225946934c2 (patch) | |
| tree | 08a22c76140d116551afe73a7f996ae53a1753e8 | |
| parent | 00197c34d6385a2de3970c9f820714064c5bf27f (diff) | |
| parent | d4f98ba4f124a836ed964a71137a6dae28358704 (diff) | |
| download | libgit2-5d669f0a96ab858750f4fdd90f138225946934c2.tar.gz | |
Merge pull request #1665 from arrbee/checkout-target-directory
Add target directory to checkout options
| -rw-r--r-- | include/git2/checkout.h | 2 | ||||
| -rw-r--r-- | src/checkout.c | 58 | ||||
| -rw-r--r-- | src/iterator.c | 12 | ||||
| -rw-r--r-- | src/iterator.h | 15 | ||||
| -rw-r--r-- | tests-clar/checkout/index.c | 99 | ||||
| -rw-r--r-- | tests-clar/checkout/tree.c | 44 |
6 files changed, 211 insertions, 19 deletions
diff --git a/include/git2/checkout.h b/include/git2/checkout.h index f49e87566..a086408c7 100644 --- a/include/git2/checkout.h +++ b/include/git2/checkout.h @@ -236,6 +236,8 @@ typedef struct git_checkout_opts { git_strarray paths; git_tree *baseline; /** expected content of workdir, defaults to HEAD */ + + const char *target_directory; /** alternative checkout path to workdir */ } git_checkout_opts; #define GIT_CHECKOUT_OPTS_VERSION 1 diff --git a/src/checkout.c b/src/checkout.c index 065bb50fd..8f9ec64e4 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -858,7 +858,7 @@ static int checkout_submodule( return 0; if ((error = git_futils_mkdir( - file->path, git_repository_workdir(data->repo), + file->path, data->opts.target_directory, data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) return error; @@ -1030,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path) { #if 0 int error = git_futils_rmdir_r( - path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS); + path, data->opts.target_directory, GIT_RMDIR_EMPTY_PARENTS); if (error == GIT_ENOTFOUND) { error = 0; @@ -1163,7 +1163,8 @@ static int checkout_data_init( return -1; } - if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0) + if ((!proposed || !proposed->target_directory) && + (error = git_repository__ensure_not_bare(repo, "checkout")) < 0) return error; data->repo = repo; @@ -1176,6 +1177,13 @@ static int checkout_data_init( else memmove(&data->opts, proposed, sizeof(git_checkout_opts)); + if (!data->opts.target_directory) + data->opts.target_directory = git_repository_workdir(repo); + else if (!git_path_isdir(data->opts.target_directory) && + (error = git_futils_mkdir(data->opts.target_directory, NULL, + GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0) + goto cleanup; + /* refresh config and index content unless NO_REFRESH is given */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { git_config *cfg; @@ -1238,7 +1246,8 @@ static int checkout_data_init( if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 || - (error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0) + (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || + (error = git_path_to_dir(&data->path)) < 0) goto cleanup; data->workdir_len = git_buf_len(&data->path); @@ -1286,11 +1295,13 @@ int git_checkout_iterator( GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || - (error = git_iterator_for_workdir( - &workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, + (error = git_iterator_for_workdir_ext( + &workdir, data.repo, data.opts.target_directory, + iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_tree( - &baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0) + &baseline, data.opts.baseline, + iterflags, data.pfx, data.pfx)) < 0) goto cleanup; /* Should not have case insensitivity mismatch */ @@ -1358,8 +1369,19 @@ int git_checkout_index( int error; git_iterator *index_i; - if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0) - return error; + if (!index && !repo) { + giterr_set(GITERR_CHECKOUT, + "Must provide either repository or index to checkout"); + return -1; + } + if (index && repo && git_index_owner(index) != repo) { + giterr_set(GITERR_CHECKOUT, + "Index to checkout does not match repository"); + return -1; + } + + if (!repo) + repo = git_index_owner(index); if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0) return error; @@ -1383,8 +1405,19 @@ int git_checkout_tree( git_tree *tree = NULL; git_iterator *tree_i = NULL; - if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0) - return error; + if (!treeish && !repo) { + giterr_set(GITERR_CHECKOUT, + "Must provide either repository or tree to checkout"); + return -1; + } + if (treeish && repo && git_object_owner(treeish) != repo) { + giterr_set(GITERR_CHECKOUT, + "Object to checkout does not match repository"); + return -1; + } + + if (!repo) + repo = git_object_owner(treeish); if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) { giterr_set( @@ -1409,8 +1442,7 @@ int git_checkout_head( git_tree *head = NULL; git_iterator *head_i = NULL; - if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0) - return error; + assert(repo); if (!(error = checkout_lookup_head_tree(&head, repo)) && !(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL))) diff --git a/src/iterator.c b/src/iterator.c index 76b0e41d0..5917f63fd 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1321,9 +1321,10 @@ static void workdir_iterator__free(git_iterator *self) git_ignore__free(&wi->ignores); } -int git_iterator_for_workdir( +int git_iterator_for_workdir_ext( git_iterator **out, git_repository *repo, + const char *repo_workdir, git_iterator_flag_t flags, const char *start, const char *end) @@ -1331,8 +1332,11 @@ int git_iterator_for_workdir( int error; workdir_iterator *wi; - if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) - return GIT_EBAREREPO; + if (!repo_workdir) { + if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) + return GIT_EBAREREPO; + repo_workdir = git_repository_workdir(repo); + } /* initialize as an fs iterator then do overrides */ wi = git__calloc(1, sizeof(workdir_iterator)); @@ -1352,7 +1356,7 @@ int git_iterator_for_workdir( return error; } - return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo)); + return fs_iterator__initialize(out, &wi->fi, repo_workdir); } diff --git a/src/iterator.h b/src/iterator.h index 493ff4b2a..ea88fa6a2 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -79,15 +79,26 @@ extern int git_iterator_for_index( const char *start, const char *end); +extern int git_iterator_for_workdir_ext( + git_iterator **out, + git_repository *repo, + const char *repo_workdir, + git_iterator_flag_t flags, + const char *start, + const char *end); + /* workdir iterators will match the ignore_case value from the index of the * repository, unless you override with a non-zero flag value */ -extern int git_iterator_for_workdir( +GIT_INLINE(int) git_iterator_for_workdir( git_iterator **out, git_repository *repo, git_iterator_flag_t flags, const char *start, - const char *end); + const char *end) +{ + return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end); +} /* for filesystem iterators, you have to explicitly pass in the ignore_case * behavior that you desire diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c index a3a0f8fda..c7500db1d 100644 --- a/tests-clar/checkout/index.c +++ b/tests-clar/checkout/index.c @@ -506,3 +506,102 @@ void test_checkout_index__issue_1397(void) check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf"); } + +void test_checkout_index__target_directory(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* create some files that *would* conflict if we were using the wd */ + cl_git_mkfile("testrepo/README", "I'm in the way!\n"); + cl_git_mkfile("testrepo/new.txt", "my new file\n"); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(4, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_index__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_index *index; + git_object *head = NULL; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_index__cleanup(); + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}")); + cl_git_pass(git_index_read_tree(index, (const git_tree *)head)); + cl_git_pass(git_index_write(index)); + git_index_free(index); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + /* fail to checkout a bare repo */ + cl_git_fail(git_checkout_index(g_repo, NULL, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_checkout_index__can_get_repo_from_index(void) +{ + git_index *index; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + + cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); + cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt")); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_checkout_index(NULL, index, &opts)); + + check_file_contents("./testrepo/README", "hey there\n"); + check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./testrepo/new.txt", "my new file\n"); + + git_index_free(index); +} diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index 5835ab05b..0e65f28c8 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -596,6 +596,8 @@ void test_checkout_tree__fails_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } @@ -628,5 +630,47 @@ void test_checkout_tree__can_continue_when_dir_in_use(void) cl_git_pass(p_chdir("../..")); cl_assert(git_path_is_empty_dir("testrepo/a")); + + git_object_free(obj); #endif } + +void test_checkout_tree__target_directory_from_bare(void) +{ + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_oid oid; + checkout_counts cts; + memset(&cts, 0, sizeof(cts)); + + test_checkout_tree__cleanup(); /* cleanup default checkout */ + + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert(git_repository_is_bare(g_repo)); + + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL; + opts.notify_cb = checkout_count_callback; + opts.notify_payload = &cts; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD")); + cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_fail(git_checkout_tree(g_repo, g_object, &opts)); + + opts.target_directory = "alternative"; + cl_assert(!git_path_isdir("alternative")); + + cl_git_pass(git_checkout_tree(g_repo, g_object, &opts)); + + cl_assert_equal_i(0, cts.n_untracked); + cl_assert_equal_i(0, cts.n_ignored); + cl_assert_equal_i(3, cts.n_updates); + + check_file_contents("./alternative/README", "hey there\n"); + check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n"); + check_file_contents("./alternative/new.txt", "my new file\n"); + + cl_git_pass(git_futils_rmdir_r( + "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); +} |
