diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2018-03-19 19:50:45 +0000 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2018-11-04 09:21:48 +0000 |
commit | f83bbe0a882cb141c636ebf7657462b3be85dea8 (patch) | |
tree | 1b848c7bb1fc3fbe2b7baa3e2a67e7a35f4eb9e6 /src/apply.c | |
parent | 664cda6f6b5a29b486b231d77c707ad54a2f58ce (diff) | |
download | libgit2-f83bbe0a882cb141c636ebf7657462b3be85dea8.tar.gz |
apply: introduce `git_apply`
Introduce `git_apply`, which will take a `git_diff` and apply it to the
working directory (akin to `git apply`), the index (akin to `git apply
--cached`), or both (akin to `git apply --index`).
Diffstat (limited to 'src/apply.c')
-rw-r--r-- | src/apply.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/src/apply.c b/src/apply.c index 134a98f81..e761b270c 100644 --- a/src/apply.c +++ b/src/apply.c @@ -9,10 +9,13 @@ #include <assert.h> +#include "git2/apply.h" #include "git2/patch.h" #include "git2/filter.h" #include "git2/blob.h" #include "git2/index.h" +#include "git2/checkout.h" +#include "git2/repository.h" #include "array.h" #include "patch.h" #include "fileops.h" @@ -481,3 +484,110 @@ done: return error; } + +/* normal: apply to workdir: ignore index + * --cached: apply to index: ignore workdir + * --index: apply to both: validate index == workdir + */ + +int git_apply( + git_repository *repo, + git_diff *diff, + git_apply_options *given_opts) +{ + git_index *contents = NULL, *repo_index = NULL; + git_reader *pre_reader = NULL; + const git_diff_delta *delta; + git_vector paths = GIT_VECTOR_INIT; + git_apply_options opts = GIT_APPLY_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + bool do_checkout; + size_t i; + int error; + + assert(repo && diff); + + GITERR_CHECK_VERSION( + given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options"); + + if (given_opts) + memcpy(&opts, given_opts, sizeof(git_apply_options)); + + do_checkout = (opts.location != GIT_APPLY_LOCATION_INDEX); + + /* + * by default, we apply a patch directly to the working directory; + * in `--cached` or `--index` mode, we apply to the contents already + * in the index. + */ + if (opts.location == GIT_APPLY_LOCATION_WORKDIR) + error = git_reader_for_workdir(&pre_reader, repo); + else + error = git_reader_for_index(&pre_reader, repo, NULL); + + if (error < 0) + goto done; + + /* if we're not checking out, we're writing to the repo's index */ + if (do_checkout) + error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL); + else + error = git_repository_index(&repo_index, repo); + + if (error < 0) + goto done; + + /* + * note: this is not the full postimage, this only contains the + * new files created during the diffing process. we will limit + * checkout to only write the files affected by this diff. + */ + if ((error = git_index_new(&contents)) < 0) + goto done; + + for (i = 0; i < git_diff_num_deltas(diff); i++) { + delta = git_diff_get_delta(diff, i); + + if ((error = apply_one(repo, pre_reader, contents, diff, i)) < 0) + goto done; + + if (do_checkout) { + git_vector_insert(&paths, (void *)delta->old_file.path); + + if (strcmp(delta->old_file.path, delta->new_file.path)) + git_vector_insert(&paths, (void *)delta->new_file.path); + } + } + + if (do_checkout) { + checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE; + checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH; + + if (opts.location == GIT_APPLY_LOCATION_WORKDIR) + checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX; + + checkout_opts.paths.strings = (char **)paths.contents; + checkout_opts.paths.count = paths.length; + + error = git_checkout_index(repo, contents, &checkout_opts); + } else { + const git_index_entry *entry; + + for (i = 0; i < git_index_entrycount(contents); i++) { + entry = git_index_get_byindex(contents, i); + + if ((error = git_index_add(repo_index, entry)) < 0) + goto done; + } + + error = git_index_write(repo_index); + } + +done: + git_vector_free(&paths); + git_index_free(contents); + git_reader_free(pre_reader); + git_index_free(repo_index); + + return error; +} |