Skip to content

Commit 551a0fb

Browse files
author
Vicent Martí
authored
Merge pull request #788 from repotag/repo_apply
Add Repository#apply
2 parents 35102c0 + 9b25050 commit 551a0fb

File tree

4 files changed

+341
-6
lines changed

4 files changed

+341
-6
lines changed

ext/rugged/rugged.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ VALUE rugged_signature_from_buffer(const char *buffer, const char *encoding_name
104104
void rugged_rb_ary_to_strarray(VALUE rb_array, git_strarray *str_array);
105105
VALUE rugged_strarray_to_rb_ary(git_strarray *str_array);
106106

107+
#define CALLABLE_OR_RAISE(ret, name) \
108+
do { \
109+
if (!rb_respond_to(ret, rb_intern("call"))) \
110+
rb_raise(rb_eArgError, "Expected a Proc or an object that responds to #call (:" name " )."); \
111+
} while (0);
112+
107113
static inline void rugged_set_owner(VALUE object, VALUE owner)
108114
{
109115
rb_iv_set(object, "@owner", owner);
@@ -134,6 +140,13 @@ extern VALUE rb_cRuggedRepo;
134140

135141
VALUE rugged__block_yield_splat(VALUE args);
136142

143+
struct rugged_apply_cb_payload
144+
{
145+
VALUE delta_cb;
146+
VALUE hunk_cb;
147+
int exception;
148+
};
149+
137150
struct rugged_cb_payload
138151
{
139152
VALUE rb_data;

ext/rugged/rugged_remote.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,6 @@ static int credentials_cb(
157157
return payload->exception ? GIT_ERROR : GIT_OK;
158158
}
159159

160-
#define CALLABLE_OR_RAISE(ret, name) \
161-
do { \
162-
if (!rb_respond_to(ret, rb_intern("call"))) \
163-
rb_raise(rb_eArgError, "Expected a Proc or an object that responds to #call (:" name " )."); \
164-
} while (0);
165-
166160
void rugged_remote_init_callbacks_and_payload_from_options(
167161
VALUE rb_options,
168162
git_remote_callbacks *callbacks,

ext/rugged/rugged_repo.c

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <git2/sys/odb_backend.h>
1111
#include <git2/sys/refdb_backend.h>
1212
#include <git2/refs.h>
13+
#include <git2/apply.h>
1314

1415
extern VALUE rb_mRugged;
1516
extern VALUE rb_eRuggedError;
@@ -18,6 +19,7 @@ extern VALUE rb_cRuggedConfig;
1819
extern VALUE rb_cRuggedBackend;
1920
extern VALUE rb_cRuggedRemote;
2021
extern VALUE rb_cRuggedCommit;
22+
extern VALUE rb_cRuggedDiff;
2123
extern VALUE rb_cRuggedTag;
2224
extern VALUE rb_cRuggedTree;
2325
extern VALUE rb_cRuggedReference;
@@ -396,6 +398,98 @@ static VALUE rb_git_repo_init_at(int argc, VALUE *argv, VALUE klass)
396398
return rugged_repo_new(klass, repo);
397399
}
398400

401+
static int apply_cb_result(int exception, VALUE result)
402+
{
403+
if (exception || result == Qnil) {
404+
return GIT_EAPPLYFAIL;
405+
} else {
406+
if (RTEST(result)) {
407+
return 0;
408+
} else {
409+
return 1;
410+
}
411+
}
412+
}
413+
414+
static int apply_delta_cb(const git_diff_delta *delta, void *data)
415+
{
416+
struct rugged_apply_cb_payload *payload = data;
417+
VALUE args = rb_ary_new2(2);
418+
VALUE result;
419+
420+
if (NIL_P(payload->delta_cb))
421+
return 0;
422+
423+
VALUE rb_delta = rugged_diff_delta_new(Qnil, delta);
424+
425+
rb_ary_push(args, payload->delta_cb);
426+
rb_ary_push(args, rb_delta);
427+
428+
result = rb_protect(rugged__block_yield_splat, args, &payload->exception);
429+
430+
return apply_cb_result(payload->exception, result);
431+
}
432+
433+
static int apply_hunk_cb(const git_diff_hunk *hunk, void *data)
434+
{
435+
struct rugged_apply_cb_payload *payload = data;
436+
VALUE args = rb_ary_new2(2);
437+
VALUE result;
438+
439+
if (NIL_P(payload->hunk_cb))
440+
return 0;
441+
442+
VALUE rb_hunk = rugged_diff_hunk_new(Qnil, 0, hunk, 0);
443+
444+
rb_ary_push(args, payload->hunk_cb);
445+
rb_ary_push(args, rb_hunk);
446+
447+
result = rb_protect(rugged__block_yield_splat, args, &payload->exception);
448+
449+
return apply_cb_result(payload->exception, result);
450+
}
451+
452+
static void rugged_parse_apply_options(git_apply_options *opts, git_apply_location_t *location, VALUE rb_options, struct rugged_apply_cb_payload *payload)
453+
{
454+
if (!NIL_P(rb_options)) {
455+
VALUE rb_value;
456+
Check_Type(rb_options, T_HASH);
457+
458+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("location"));
459+
if (!NIL_P(rb_value)) {
460+
ID id_location;
461+
462+
Check_Type(rb_value, T_SYMBOL);
463+
id_location = SYM2ID(rb_value);
464+
465+
if (id_location == rb_intern("both")) {
466+
*location = GIT_APPLY_LOCATION_BOTH;
467+
} else if (id_location == rb_intern("index")) {
468+
*location = GIT_APPLY_LOCATION_INDEX;
469+
} else if (id_location == rb_intern("workdir")) {
470+
*location = GIT_APPLY_LOCATION_WORKDIR;
471+
} else {
472+
rb_raise(rb_eTypeError,
473+
"Invalid location. Expected `:both`, `:index`, or `:workdir`");
474+
}
475+
}
476+
477+
opts->payload = payload;
478+
479+
payload->delta_cb = rb_hash_aref(rb_options, CSTR2SYM("delta_callback"));
480+
if (!NIL_P(payload->delta_cb)) {
481+
CALLABLE_OR_RAISE(payload->delta_cb, "delta_callback");
482+
opts->delta_cb = apply_delta_cb;
483+
}
484+
485+
payload->hunk_cb = rb_hash_aref(rb_options, CSTR2SYM("hunk_callback"));
486+
if (!NIL_P(payload->hunk_cb)) {
487+
CALLABLE_OR_RAISE(payload->hunk_cb, "hunk_callback");
488+
opts->hunk_cb = apply_hunk_cb;
489+
}
490+
}
491+
}
492+
399493
static void parse_clone_options(git_clone_options *ret, VALUE rb_options, struct rugged_remote_cb_payload *remote_payload)
400494
{
401495
VALUE val;
@@ -868,6 +962,69 @@ static VALUE rb_git_repo_revert_commit(int argc, VALUE *argv, VALUE self)
868962
return rugged_index_new(rb_cRuggedIndex, self, index);
869963
}
870964

965+
/*
966+
* call-seq:
967+
* repo.apply(diff, options = {}) -> true or false
968+
*
969+
* Applies the given diff to the repository.
970+
* The following options can be passed in the +options+ Hash:
971+
*
972+
* :location ::
973+
* Whether to apply the changes to the workdir (default for non-bare),
974+
* the index (default for bare) or both. Valid values: +:index+, +:workdir+,
975+
* +:both+.
976+
*
977+
* :delta_callback ::
978+
* While applying the patch, this callback will be executed per delta (file).
979+
* The current +delta+ will be passed to the block. The block's return value
980+
* determines further behavior. When the block evaluates to:
981+
* - +true+: the hunk will be applied and the apply process will continue.
982+
* - +false+: the hunk will be skipped, but the apply process continues.
983+
* - +nil+: the hunk is not applied, and the apply process is aborted.
984+
*
985+
* :hunk_callback ::
986+
* While applying the patch, this callback will be executed per hunk.
987+
* The current +hunk+ will be passed to the block. The block's return value
988+
* determines further behavior, as per +:delta_callback+.
989+
*
990+
*/
991+
static VALUE rb_git_repo_apply(int argc, VALUE *argv, VALUE self)
992+
{
993+
VALUE rb_diff, rb_options;
994+
git_diff *diff;
995+
git_repository *repo;
996+
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
997+
git_apply_location_t location;
998+
struct rugged_apply_cb_payload payload = { Qnil, Qnil, 0 };
999+
int error;
1000+
1001+
Data_Get_Struct(self, git_repository, repo);
1002+
if (git_repository_is_bare(repo)) {
1003+
location = GIT_APPLY_LOCATION_INDEX;
1004+
} else {
1005+
location = GIT_APPLY_LOCATION_WORKDIR;
1006+
}
1007+
1008+
rb_scan_args(argc, argv, "11", &rb_diff, &rb_options);
1009+
1010+
if (!rb_obj_is_kind_of(rb_diff, rb_cRuggedDiff)) {
1011+
rb_raise(rb_eArgError, "Expected a Rugged::Diff.");
1012+
}
1013+
1014+
if (!NIL_P(rb_options)) {
1015+
Check_Type(rb_options, T_HASH);
1016+
rugged_parse_apply_options(&opts, &location, rb_options, &payload);
1017+
}
1018+
1019+
Data_Get_Struct(rb_diff, git_diff, diff);
1020+
1021+
error = git_apply(repo, diff, location, &opts);
1022+
1023+
rugged_exception_check(error);
1024+
1025+
return Qtrue;
1026+
}
1027+
8711028
/*
8721029
* call-seq:
8731030
* repo.merge_commits(our_commit, their_commit, options = {}) -> index
@@ -2608,6 +2765,8 @@ void Init_rugged_repo(void)
26082765
rb_define_method(rb_cRuggedRepo, "merge_analysis", rb_git_repo_merge_analysis, -1);
26092766
rb_define_method(rb_cRuggedRepo, "merge_commits", rb_git_repo_merge_commits, -1);
26102767

2768+
rb_define_method(rb_cRuggedRepo, "apply", rb_git_repo_apply, -1);
2769+
26112770
rb_define_method(rb_cRuggedRepo, "revert_commit", rb_git_repo_revert_commit, -1);
26122771

26132772
rb_define_method(rb_cRuggedRepo, "path_ignored?", rb_git_repo_is_path_ignored, 1);

0 commit comments

Comments
 (0)