Commit 9094ae5a by Russell Belfer

Add target directory to checkout

This adds the ability for checkout to write to a target directory
instead of having to use the working directory of the repository.
This makes it easier to do exports of repository data and the like.

This is similar to, but not quite the same as, the --prefix option
to `git checkout-index` (this will always be treated as a directory
name, not just as a simple text prefix).

As part of this, the workdir iterator was extended to take the
path to the working directory as a parameter and fallback on the
git_repository_workdir result only if it's not specified.

Fixes #1332
parent 00197c34
...@@ -236,6 +236,8 @@ typedef struct git_checkout_opts { ...@@ -236,6 +236,8 @@ typedef struct git_checkout_opts {
git_strarray paths; git_strarray paths;
git_tree *baseline; /** expected content of workdir, defaults to HEAD */ git_tree *baseline; /** expected content of workdir, defaults to HEAD */
const char *target_directory; /** alternative checkout path to workdir */
} git_checkout_opts; } git_checkout_opts;
#define GIT_CHECKOUT_OPTS_VERSION 1 #define GIT_CHECKOUT_OPTS_VERSION 1
......
...@@ -858,7 +858,7 @@ static int checkout_submodule( ...@@ -858,7 +858,7 @@ static int checkout_submodule(
return 0; return 0;
if ((error = git_futils_mkdir( 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) data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
return error; return error;
...@@ -1030,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path) ...@@ -1030,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path)
{ {
#if 0 #if 0
int error = git_futils_rmdir_r( 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) { if (error == GIT_ENOTFOUND) {
error = 0; error = 0;
...@@ -1163,7 +1163,8 @@ static int checkout_data_init( ...@@ -1163,7 +1163,8 @@ static int checkout_data_init(
return -1; 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; return error;
data->repo = repo; data->repo = repo;
...@@ -1176,6 +1177,13 @@ static int checkout_data_init( ...@@ -1176,6 +1177,13 @@ static int checkout_data_init(
else else
memmove(&data->opts, proposed, sizeof(git_checkout_opts)); 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 */ /* refresh config and index content unless NO_REFRESH is given */
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
git_config *cfg; git_config *cfg;
...@@ -1238,7 +1246,8 @@ static int checkout_data_init( ...@@ -1238,7 +1246,8 @@ static int checkout_data_init(
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 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; goto cleanup;
data->workdir_len = git_buf_len(&data->path); data->workdir_len = git_buf_len(&data->path);
...@@ -1286,11 +1295,13 @@ int git_checkout_iterator( ...@@ -1286,11 +1295,13 @@ int git_checkout_iterator(
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE; GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_workdir( (error = git_iterator_for_workdir_ext(
&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, &workdir, data.repo, data.opts.target_directory,
iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
data.pfx, data.pfx)) < 0 || data.pfx, data.pfx)) < 0 ||
(error = git_iterator_for_tree( (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; goto cleanup;
/* Should not have case insensitivity mismatch */ /* Should not have case insensitivity mismatch */
......
...@@ -1321,9 +1321,10 @@ static void workdir_iterator__free(git_iterator *self) ...@@ -1321,9 +1321,10 @@ static void workdir_iterator__free(git_iterator *self)
git_ignore__free(&wi->ignores); git_ignore__free(&wi->ignores);
} }
int git_iterator_for_workdir( int git_iterator_for_workdir_ext(
git_iterator **out, git_iterator **out,
git_repository *repo, git_repository *repo,
const char *repo_workdir,
git_iterator_flag_t flags, git_iterator_flag_t flags,
const char *start, const char *start,
const char *end) const char *end)
...@@ -1331,8 +1332,11 @@ int git_iterator_for_workdir( ...@@ -1331,8 +1332,11 @@ int git_iterator_for_workdir(
int error; int error;
workdir_iterator *wi; workdir_iterator *wi;
if (!repo_workdir) {
if (git_repository__ensure_not_bare(repo, "scan working directory") < 0) if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
return GIT_EBAREREPO; return GIT_EBAREREPO;
repo_workdir = git_repository_workdir(repo);
}
/* initialize as an fs iterator then do overrides */ /* initialize as an fs iterator then do overrides */
wi = git__calloc(1, sizeof(workdir_iterator)); wi = git__calloc(1, sizeof(workdir_iterator));
...@@ -1352,7 +1356,7 @@ int git_iterator_for_workdir( ...@@ -1352,7 +1356,7 @@ int git_iterator_for_workdir(
return error; 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( ...@@ -79,15 +79,26 @@ extern int git_iterator_for_index(
const char *start, const char *start,
const char *end); 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 /* workdir iterators will match the ignore_case value from the index of the
* repository, unless you override with a non-zero flag value * 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_iterator **out,
git_repository *repo, git_repository *repo,
git_iterator_flag_t flags, git_iterator_flag_t flags,
const char *start, 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 /* for filesystem iterators, you have to explicitly pass in the ignore_case
* behavior that you desire * behavior that you desire
......
...@@ -506,3 +506,31 @@ void test_checkout_index__issue_1397(void) ...@@ -506,3 +506,31 @@ void test_checkout_index__issue_1397(void)
check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf"); 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";
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");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment