10
10
#include <git2/sys/odb_backend.h>
11
11
#include <git2/sys/refdb_backend.h>
12
12
#include <git2/refs.h>
13
+ #include <git2/apply.h>
13
14
14
15
extern VALUE rb_mRugged ;
15
16
extern VALUE rb_eRuggedError ;
@@ -18,6 +19,7 @@ extern VALUE rb_cRuggedConfig;
18
19
extern VALUE rb_cRuggedBackend ;
19
20
extern VALUE rb_cRuggedRemote ;
20
21
extern VALUE rb_cRuggedCommit ;
22
+ extern VALUE rb_cRuggedDiff ;
21
23
extern VALUE rb_cRuggedTag ;
22
24
extern VALUE rb_cRuggedTree ;
23
25
extern VALUE rb_cRuggedReference ;
@@ -396,6 +398,98 @@ static VALUE rb_git_repo_init_at(int argc, VALUE *argv, VALUE klass)
396
398
return rugged_repo_new (klass , repo );
397
399
}
398
400
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
+
399
493
static void parse_clone_options (git_clone_options * ret , VALUE rb_options , struct rugged_remote_cb_payload * remote_payload )
400
494
{
401
495
VALUE val ;
@@ -868,6 +962,69 @@ static VALUE rb_git_repo_revert_commit(int argc, VALUE *argv, VALUE self)
868
962
return rugged_index_new (rb_cRuggedIndex , self , index );
869
963
}
870
964
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
+
871
1028
/*
872
1029
* call-seq:
873
1030
* repo.merge_commits(our_commit, their_commit, options = {}) -> index
@@ -2608,6 +2765,8 @@ void Init_rugged_repo(void)
2608
2765
rb_define_method (rb_cRuggedRepo , "merge_analysis" , rb_git_repo_merge_analysis , -1 );
2609
2766
rb_define_method (rb_cRuggedRepo , "merge_commits" , rb_git_repo_merge_commits , -1 );
2610
2767
2768
+ rb_define_method (rb_cRuggedRepo , "apply" , rb_git_repo_apply , -1 );
2769
+
2611
2770
rb_define_method (rb_cRuggedRepo , "revert_commit" , rb_git_repo_revert_commit , -1 );
2612
2771
2613
2772
rb_define_method (rb_cRuggedRepo , "path_ignored?" , rb_git_repo_is_path_ignored , 1 );
0 commit comments