Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9094ae5

Browse files
committed
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 libgit2#1332
1 parent 00197c3 commit 9094ae5

File tree

5 files changed

+69
-13
lines changed

5 files changed

+69
-13
lines changed

include/git2/checkout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,8 @@ typedef struct git_checkout_opts {
236236
git_strarray paths;
237237

238238
git_tree *baseline; /** expected content of workdir, defaults to HEAD */
239+
240+
const char *target_directory; /** alternative checkout path to workdir */
239241
} git_checkout_opts;
240242

241243
#define GIT_CHECKOUT_OPTS_VERSION 1

src/checkout.c

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ static int checkout_submodule(
858858
return 0;
859859

860860
if ((error = git_futils_mkdir(
861-
file->path, git_repository_workdir(data->repo),
861+
file->path, data->opts.target_directory,
862862
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
863863
return error;
864864

@@ -1030,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path)
10301030
{
10311031
#if 0
10321032
int error = git_futils_rmdir_r(
1033-
path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS);
1033+
path, data->opts.target_directory, GIT_RMDIR_EMPTY_PARENTS);
10341034

10351035
if (error == GIT_ENOTFOUND) {
10361036
error = 0;
@@ -1163,7 +1163,8 @@ static int checkout_data_init(
11631163
return -1;
11641164
}
11651165

1166-
if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
1166+
if ((!proposed || !proposed->target_directory) &&
1167+
(error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
11671168
return error;
11681169

11691170
data->repo = repo;
@@ -1176,6 +1177,13 @@ static int checkout_data_init(
11761177
else
11771178
memmove(&data->opts, proposed, sizeof(git_checkout_opts));
11781179

1180+
if (!data->opts.target_directory)
1181+
data->opts.target_directory = git_repository_workdir(repo);
1182+
else if (!git_path_isdir(data->opts.target_directory) &&
1183+
(error = git_futils_mkdir(data->opts.target_directory, NULL,
1184+
GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0)
1185+
goto cleanup;
1186+
11791187
/* refresh config and index content unless NO_REFRESH is given */
11801188
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
11811189
git_config *cfg;
@@ -1238,7 +1246,8 @@ static int checkout_data_init(
12381246

12391247
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
12401248
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
1241-
(error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0)
1249+
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
1250+
(error = git_path_to_dir(&data->path)) < 0)
12421251
goto cleanup;
12431252

12441253
data->workdir_len = git_buf_len(&data->path);
@@ -1286,11 +1295,13 @@ int git_checkout_iterator(
12861295
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
12871296

12881297
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
1289-
(error = git_iterator_for_workdir(
1290-
&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
1298+
(error = git_iterator_for_workdir_ext(
1299+
&workdir, data.repo, data.opts.target_directory,
1300+
iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
12911301
data.pfx, data.pfx)) < 0 ||
12921302
(error = git_iterator_for_tree(
1293-
&baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
1303+
&baseline, data.opts.baseline,
1304+
iterflags, data.pfx, data.pfx)) < 0)
12941305
goto cleanup;
12951306

12961307
/* Should not have case insensitivity mismatch */

src/iterator.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,18 +1321,22 @@ static void workdir_iterator__free(git_iterator *self)
13211321
git_ignore__free(&wi->ignores);
13221322
}
13231323

1324-
int git_iterator_for_workdir(
1324+
int git_iterator_for_workdir_ext(
13251325
git_iterator **out,
13261326
git_repository *repo,
1327+
const char *repo_workdir,
13271328
git_iterator_flag_t flags,
13281329
const char *start,
13291330
const char *end)
13301331
{
13311332
int error;
13321333
workdir_iterator *wi;
13331334

1334-
if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1335-
return GIT_EBAREREPO;
1335+
if (!repo_workdir) {
1336+
if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
1337+
return GIT_EBAREREPO;
1338+
repo_workdir = git_repository_workdir(repo);
1339+
}
13361340

13371341
/* initialize as an fs iterator then do overrides */
13381342
wi = git__calloc(1, sizeof(workdir_iterator));
@@ -1352,7 +1356,7 @@ int git_iterator_for_workdir(
13521356
return error;
13531357
}
13541358

1355-
return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo));
1359+
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
13561360
}
13571361

13581362

src/iterator.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,26 @@ extern int git_iterator_for_index(
7979
const char *start,
8080
const char *end);
8181

82+
extern int git_iterator_for_workdir_ext(
83+
git_iterator **out,
84+
git_repository *repo,
85+
const char *repo_workdir,
86+
git_iterator_flag_t flags,
87+
const char *start,
88+
const char *end);
89+
8290
/* workdir iterators will match the ignore_case value from the index of the
8391
* repository, unless you override with a non-zero flag value
8492
*/
85-
extern int git_iterator_for_workdir(
93+
GIT_INLINE(int) git_iterator_for_workdir(
8694
git_iterator **out,
8795
git_repository *repo,
8896
git_iterator_flag_t flags,
8997
const char *start,
90-
const char *end);
98+
const char *end)
99+
{
100+
return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end);
101+
}
91102

92103
/* for filesystem iterators, you have to explicitly pass in the ignore_case
93104
* behavior that you desire

tests-clar/checkout/index.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,31 @@ void test_checkout_index__issue_1397(void)
506506

507507
check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
508508
}
509+
510+
void test_checkout_index__target_directory(void)
511+
{
512+
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
513+
checkout_counts cts;
514+
memset(&cts, 0, sizeof(cts));
515+
516+
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
517+
opts.target_directory = "alternative";
518+
519+
opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
520+
opts.notify_cb = checkout_count_callback;
521+
opts.notify_payload = &cts;
522+
523+
/* create some files that *would* conflict if we were using the wd */
524+
cl_git_mkfile("testrepo/README", "I'm in the way!\n");
525+
cl_git_mkfile("testrepo/new.txt", "my new file\n");
526+
527+
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
528+
529+
cl_assert_equal_i(0, cts.n_untracked);
530+
cl_assert_equal_i(0, cts.n_ignored);
531+
cl_assert_equal_i(4, cts.n_updates);
532+
533+
check_file_contents("./alternative/README", "hey there\n");
534+
check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
535+
check_file_contents("./alternative/new.txt", "my new file\n");
536+
}

0 commit comments

Comments
 (0)