summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-01-28 23:25:07 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2018-11-03 16:44:26 +0000
commit02b1083ab779e4d8d2279dea1a4ae38ec1d2e47b (patch)
tree66998d89fe22cf2609b296ceca742f6d47bdeae4 /src
parent2b12dcf6d6971a622abe9730472dc373c0856d98 (diff)
downloadlibgit2-02b1083ab779e4d8d2279dea1a4ae38ec1d2e47b.tar.gz
apply: introduce `git_apply_tree`
Introduce `git_apply_tree`, which will apply a `git_diff` to a given `git_tree`, allowing an in-memory patch application for a repository.
Diffstat (limited to 'src')
-rw-r--r--src/apply.c100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/apply.c b/src/apply.c
index 8c7bb6bf3..be8c7b66a 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -11,6 +11,8 @@
#include "git2/patch.h"
#include "git2/filter.h"
+#include "git2/blob.h"
+#include "git2/index.h"
#include "array.h"
#include "patch.h"
#include "fileops.h"
@@ -376,3 +378,101 @@ done:
return error;
}
+
+static int apply_one(
+ git_repository *repo,
+ git_tree *preimage,
+ git_index *postimage,
+ git_diff *diff,
+ size_t i)
+{
+ git_patch *patch = NULL;
+ git_tree_entry *tree_entry = NULL;
+ git_blob *blob = NULL;
+ git_buf contents = GIT_BUF_INIT;
+ const git_diff_delta *delta;
+ char *filename = NULL;
+ unsigned int mode;
+ git_oid blob_id;
+ git_index_entry index_entry;
+ int error;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
+ goto done;
+
+ delta = git_patch_get_delta(patch);
+
+ if ((error = git_tree_entry_bypath(&tree_entry, preimage,
+ delta->old_file.path)) < 0 ||
+ (error = git_blob_lookup(&blob, repo,
+ git_tree_entry_id(tree_entry))) < 0 ||
+ (error = git_apply__patch(&contents, &filename, &mode,
+ git_blob_rawcontent(blob), git_blob_rawsize(blob), patch)) < 0 ||
+ (error = git_blob_create_frombuffer(&blob_id, repo,
+ contents.ptr, contents.size)) < 0)
+ goto done;
+
+ memset(&index_entry, 0, sizeof(git_index_entry));
+ index_entry.path = filename;
+ index_entry.mode = mode;
+ git_oid_cpy(&index_entry.id, &blob_id);
+
+ if ((error = git_index_add(postimage, &index_entry)) < 0)
+ goto done;
+
+done:
+ git_buf_free(&contents);
+ git__free(filename);
+ git_blob_free(blob);
+ git_tree_entry_free(tree_entry);
+ git_patch_free(patch);
+
+ return error;
+}
+
+int git_apply_to_tree(
+ git_index **out,
+ git_repository *repo,
+ git_tree *preimage,
+ git_diff *diff)
+{
+ git_index *postimage = NULL;
+ const git_diff_delta *delta;
+ size_t i;
+ int error = 0;
+
+ assert(out && repo && preimage && diff);
+
+ *out = NULL;
+
+ if ((error = git_index_new(&postimage)) < 0 ||
+ (error = git_index_read_tree(postimage, preimage)) < 0)
+ goto done;
+
+ /*
+ * Remove the old paths from the index before applying diffs -
+ * we need to do a full pass to remove them before adding deltas,
+ * in order to handle rename situations.
+ */
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ if ((error = git_index_remove(postimage,
+ delta->old_file.path, 0)) < 0)
+ goto done;
+ }
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ if ((error = apply_one(repo, preimage, postimage, diff, i)) < 0)
+ goto done;
+ }
+
+ *out = postimage;
+
+done:
+ if (error < 0)
+ git_index_free(postimage);
+
+ return error;
+}
+