mirror of
https://git.proxmox.com/git/libgit2
synced 2026-02-01 17:27:18 +00:00
Merge pull request #1665 from arrbee/checkout-target-directory
Add target directory to checkout options
This commit is contained in:
commit
5d669f0a96
@ -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
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user