Skip to content

Commit e502a15

Browse files
author
Dawa Ometto
committed
rb_git_repo_apply: add support for callback options
1 parent d601d23 commit e502a15

File tree

3 files changed

+128
-36
lines changed

3 files changed

+128
-36
lines changed

ext/rugged/rugged.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ VALUE rb_merge_file_result_fromC(const git_merge_file_result *results);
8585

8686
void rugged_parse_diff_options(git_diff_options *opts, VALUE rb_options);
8787
void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options);
88-
void rugged_parse_apply_options(git_apply_options *opts, git_apply_location_t *location, VALUE rb_options);
88+
8989
void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options);
9090
void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options);
9191

@@ -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: 114 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,98 @@ static VALUE rb_git_repo_init_at(int argc, VALUE *argv, VALUE klass)
398398
return rugged_repo_new(klass, repo);
399399
}
400400

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+
401493
static void parse_clone_options(git_clone_options *ret, VALUE rb_options, struct rugged_remote_cb_payload *remote_payload)
402494
{
403495
VALUE val;
@@ -875,14 +967,34 @@ static VALUE rb_git_repo_revert_commit(int argc, VALUE *argv, VALUE self)
875967
* repo.apply(diff, options = {}) -> true or false
876968
*
877969
* 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),
974+
* the index, or both. Valid values: +:index+, +:workdir+, +:both+.
975+
*
976+
* :delta_callback ::
977+
* While applying the patch, this callback will be executed per delta (file).
978+
* The current +delta+ will be passed to the block. The block's return value
979+
* determines further behavior. When the block evaluates to:
980+
* - +true+: the hunk will be applied and the apply process will continue.
981+
* - +false+: the hunk will be skipped, but the apply process continues.
982+
* - +nil+: the hunk is not applied, and the apply process is aborted.
983+
*
984+
* :hunk_callback ::
985+
* While applying the patch, this callback will be executed per hunk.
986+
* The current +hunk+ will be passed to the block. The block's return value
987+
* determines further behavior, as per :delta_callback.
988+
*
878989
*/
879990
static VALUE rb_git_repo_apply(int argc, VALUE *argv, VALUE self)
880991
{
881992
VALUE rb_diff, rb_options;
882993
git_diff *diff;
883994
git_repository *repo;
884995
git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
885-
git_apply_location_t location = GIT_APPLY_LOCATION_BOTH;
996+
git_apply_location_t location = GIT_APPLY_LOCATION_WORKDIR;
997+
struct rugged_apply_cb_payload payload = { Qnil, Qnil, 0 };
886998
int error;
887999

8881000
rb_scan_args(argc, argv, "11", &rb_diff, &rb_options);
@@ -893,7 +1005,7 @@ static VALUE rb_git_repo_apply(int argc, VALUE *argv, VALUE self)
8931005

8941006
if (!NIL_P(rb_options)) {
8951007
Check_Type(rb_options, T_HASH);
896-
rugged_parse_apply_options(&opts, &location, rb_options);
1008+
rugged_parse_apply_options(&opts, &location, rb_options, &payload);
8971009
}
8981010

8991011
Data_Get_Struct(self, git_repository, repo);
@@ -2581,33 +2693,6 @@ static VALUE rb_git_repo_cherrypick_commit(int argc, VALUE *argv, VALUE self)
25812693
return rugged_index_new(rb_cRuggedIndex, self, index);
25822694
}
25832695

2584-
void rugged_parse_apply_options(git_apply_options *opts, git_apply_location_t *location, VALUE rb_options)
2585-
{
2586-
if (!NIL_P(rb_options)) {
2587-
VALUE rb_value;
2588-
Check_Type(rb_options, T_HASH);
2589-
2590-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("location"));
2591-
if (!NIL_P(rb_value)) {
2592-
ID id_location;
2593-
2594-
Check_Type(rb_value, T_SYMBOL);
2595-
id_location = SYM2ID(rb_value);
2596-
2597-
if (id_location == rb_intern("both")) {
2598-
*location = GIT_APPLY_LOCATION_BOTH;
2599-
} else if (id_location == rb_intern("index")) {
2600-
*location = GIT_APPLY_LOCATION_INDEX;
2601-
} else if (id_location == rb_intern("workdir")) {
2602-
*location = GIT_APPLY_LOCATION_WORKDIR;
2603-
} else {
2604-
rb_raise(rb_eTypeError,
2605-
"Invalid location. Expected `:both`, `:index`, or `:workdir`");
2606-
}
2607-
}
2608-
}
2609-
}
2610-
26112696
void Init_rugged_repo(void)
26122697
{
26132698
id_call = rb_intern("call");

0 commit comments

Comments
 (0)