Skip to content

Commit 3e30f9e

Browse files
author
Edward Thomson
committed
Introduce Rugged::Blob.merge_files
Provide an interface to `git_merge_files` that allows a caller to provide the file inputs directly and receive the merged file results.
1 parent 37e0c11 commit 3e30f9e

File tree

5 files changed

+162
-80
lines changed

5 files changed

+162
-80
lines changed

ext/rugged/rugged.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,87 @@ void rugged_rb_ary_to_strarray(VALUE rb_array, git_strarray *str_array)
428428
}
429429
}
430430

431+
void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options)
432+
{
433+
VALUE rb_value;
434+
435+
Check_Type(rb_options, T_HASH);
436+
437+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("ancestor_label"));
438+
if (!NIL_P(rb_value)) {
439+
Check_Type(rb_value, T_STRING);
440+
opts->ancestor_label = StringValueCStr(rb_value);
441+
}
442+
443+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("our_label"));
444+
if (!NIL_P(rb_value)) {
445+
Check_Type(rb_value, T_STRING);
446+
opts->our_label = StringValueCStr(rb_value);
447+
}
448+
449+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("their_label"));
450+
if (!NIL_P(rb_value)) {
451+
Check_Type(rb_value, T_STRING);
452+
opts->their_label = StringValueCStr(rb_value);
453+
}
454+
455+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("favor"));
456+
if (!NIL_P(rb_value)) {
457+
ID id_favor;
458+
459+
Check_Type(rb_value, T_SYMBOL);
460+
id_favor = SYM2ID(rb_value);
461+
462+
if (id_favor == rb_intern("normal")) {
463+
opts->favor = GIT_MERGE_FILE_FAVOR_NORMAL;
464+
} else if (id_favor == rb_intern("ours")) {
465+
opts->favor = GIT_MERGE_FILE_FAVOR_OURS;
466+
} else if (id_favor == rb_intern("theirs")) {
467+
opts->favor = GIT_MERGE_FILE_FAVOR_THEIRS;
468+
} else if (id_favor == rb_intern("union")) {
469+
opts->favor = GIT_MERGE_FILE_FAVOR_UNION;
470+
} else {
471+
rb_raise(rb_eTypeError,
472+
"Invalid favor mode. Expected `:normal`, `:ours`, `:theirs` or `:union`");
473+
}
474+
}
475+
476+
rb_value = rb_hash_aref(rb_options, CSTR2SYM("style"));
477+
if (!NIL_P(rb_value)) {
478+
ID id_style;
479+
480+
Check_Type(rb_value, T_SYMBOL);
481+
id_style = SYM2ID(rb_value);
482+
483+
if (id_style == rb_intern("standard")) {
484+
opts->flags |= GIT_MERGE_FILE_STYLE_MERGE;
485+
} else if (id_style == rb_intern("diff3")) {
486+
opts->flags |= GIT_MERGE_FILE_STYLE_DIFF3;
487+
} else {
488+
rb_raise(rb_eTypeError,
489+
"Invalid style mode. Expected `:standard`, or `:diff3`");
490+
}
491+
} else {
492+
opts->flags |= GIT_MERGE_FILE_STYLE_MERGE;
493+
}
494+
495+
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("simplify")))) {
496+
opts->flags |= GIT_MERGE_FILE_SIMPLIFY_ALNUM;
497+
}
498+
}
499+
500+
VALUE rb_merge_file_result_fromC(const git_merge_file_result *result)
501+
{
502+
VALUE rb_result = rb_hash_new();
503+
504+
rb_hash_aset(rb_result, CSTR2SYM("automergeable"), result->automergeable ? Qtrue : Qfalse);
505+
rb_hash_aset(rb_result, CSTR2SYM("path"), result->path ? rb_str_new_utf8(result->path) : Qnil);
506+
rb_hash_aset(rb_result, CSTR2SYM("filemode"), INT2FIX(result->mode));
507+
rb_hash_aset(rb_result, CSTR2SYM("data"), rb_str_new(result->ptr, result->len));
508+
509+
return rb_result;
510+
}
511+
431512
void Init_rugged(void)
432513
{
433514
rb_mRugged = rb_define_module("Rugged");

ext/rugged/rugged.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,12 @@ VALUE rugged_diff_hunk_new(VALUE owner, size_t hunk_idx, const git_diff_hunk *hu
9696
VALUE rugged_diff_line_new(const git_diff_line *line);
9797
VALUE rugged_remote_new(VALUE owner, git_remote *remote);
9898
VALUE rb_git_delta_file_fromC(const git_diff_file *file);
99+
VALUE rb_merge_file_result_fromC(const git_merge_file_result *results);
99100

100101
void rugged_parse_diff_options(git_diff_options *opts, VALUE rb_options);
101102
void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options);
102103
void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options);
104+
void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options);
103105

104106
void rugged_cred_extract(git_cred **cred, int allowed_types, VALUE rb_credential);
105107

ext/rugged/rugged_blob.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,58 @@ static VALUE rb_git_blob_to_buffer(int argc, VALUE *argv, VALUE self)
551551
return rb_ret;
552552
}
553553

554+
static void rugged_parse_merge_file_input(git_merge_file_input *input, VALUE rb_input)
555+
{
556+
VALUE rb_value;
557+
558+
Check_Type(rb_input, T_HASH);
559+
560+
rb_value = rb_hash_aref(rb_input, CSTR2SYM("content"));
561+
if (NIL_P(rb_value))
562+
rb_raise(rb_eArgError, "File input must have `:content`.");
563+
564+
input->ptr = RSTRING_PTR(rb_value);
565+
input->size = RSTRING_LEN(rb_value);
566+
567+
rb_value = rb_hash_aref(rb_input, CSTR2SYM("filemode"));
568+
if (!NIL_P(rb_value))
569+
input->mode = FIX2UINT(rb_value);
570+
571+
rb_value = rb_hash_aref(rb_input, CSTR2SYM("path"));
572+
if (!NIL_P(rb_value)) {
573+
Check_Type(rb_value, T_STRING);
574+
input->path = RSTRING_PTR(rb_value);
575+
}
576+
}
577+
578+
static VALUE rb_git_blob_merge_files(int argc, VALUE *argv, VALUE klass)
579+
{
580+
VALUE rb_ancestor, rb_ours, rb_theirs, rb_options, rb_result;
581+
582+
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
583+
ours = GIT_MERGE_FILE_INPUT_INIT,
584+
theirs = GIT_MERGE_FILE_INPUT_INIT;
585+
git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
586+
git_merge_file_result result = {0};
587+
int error;
588+
589+
rb_scan_args(argc, argv, "31", &rb_ancestor, &rb_ours, &rb_theirs, &rb_options);
590+
591+
rugged_parse_merge_file_input(&ancestor, rb_ancestor);
592+
rugged_parse_merge_file_input(&ours, rb_ours);
593+
rugged_parse_merge_file_input(&theirs, rb_theirs);
594+
595+
if (!NIL_P(rb_options))
596+
rugged_parse_merge_file_options(&opts, rb_options);
597+
598+
rugged_exception_check(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
599+
600+
rb_result = rb_merge_file_result_fromC(&result);
601+
git_merge_file_result_free(&result);
602+
603+
return rb_result;
604+
}
605+
554606
static VALUE rb_git_blob_sig_new(int argc, VALUE *argv, VALUE klass)
555607
{
556608
int error, opts = 0;
@@ -622,6 +674,7 @@ void Init_rugged_blob(void)
622674
rb_define_singleton_method(rb_cRuggedBlob, "from_io", rb_git_blob_from_io, -1);
623675

624676
rb_define_singleton_method(rb_cRuggedBlob, "to_buffer", rb_git_blob_to_buffer, -1);
677+
rb_define_singleton_method(rb_cRuggedBlob, "merge_files", rb_git_blob_merge_files, -1);
625678

626679
rb_cRuggedBlobSig = rb_define_class_under(rb_cRuggedBlob, "HashSignature", rb_cObject);
627680
rb_define_singleton_method(rb_cRuggedBlobSig, "new", rb_git_blob_sig_new, -1);

ext/rugged/rugged_index.c

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -952,76 +952,6 @@ static VALUE rb_git_conflict_get(VALUE self, VALUE rb_path)
952952
return rb_result;
953953
}
954954

955-
void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options)
956-
{
957-
if (!NIL_P(rb_options)) {
958-
VALUE rb_value;
959-
Check_Type(rb_options, T_HASH);
960-
961-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("ancestor_label"));
962-
if (!NIL_P(rb_value)) {
963-
Check_Type(rb_value, T_STRING);
964-
opts->ancestor_label = StringValueCStr(rb_value);
965-
}
966-
967-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("our_label"));
968-
if (!NIL_P(rb_value)) {
969-
Check_Type(rb_value, T_STRING);
970-
opts->our_label = StringValueCStr(rb_value);
971-
}
972-
973-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("their_label"));
974-
if (!NIL_P(rb_value)) {
975-
Check_Type(rb_value, T_STRING);
976-
opts->their_label = StringValueCStr(rb_value);
977-
}
978-
979-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("favor"));
980-
if (!NIL_P(rb_value)) {
981-
ID id_favor;
982-
983-
Check_Type(rb_value, T_SYMBOL);
984-
id_favor = SYM2ID(rb_value);
985-
986-
if (id_favor == rb_intern("normal")) {
987-
opts->favor = GIT_MERGE_FILE_FAVOR_NORMAL;
988-
} else if (id_favor == rb_intern("ours")) {
989-
opts->favor = GIT_MERGE_FILE_FAVOR_OURS;
990-
} else if (id_favor == rb_intern("theirs")) {
991-
opts->favor = GIT_MERGE_FILE_FAVOR_THEIRS;
992-
} else if (id_favor == rb_intern("union")) {
993-
opts->favor = GIT_MERGE_FILE_FAVOR_UNION;
994-
} else {
995-
rb_raise(rb_eTypeError,
996-
"Invalid favor mode. Expected `:normal`, `:ours`, `:theirs` or `:union`");
997-
}
998-
}
999-
1000-
rb_value = rb_hash_aref(rb_options, CSTR2SYM("style"));
1001-
if (!NIL_P(rb_value)) {
1002-
ID id_style;
1003-
1004-
Check_Type(rb_value, T_SYMBOL);
1005-
id_style = SYM2ID(rb_value);
1006-
1007-
if (id_style == rb_intern("standard")) {
1008-
opts->flags |= GIT_MERGE_FILE_STYLE_MERGE;
1009-
} else if (id_style == rb_intern("diff3")) {
1010-
opts->flags |= GIT_MERGE_FILE_STYLE_DIFF3;
1011-
} else {
1012-
rb_raise(rb_eTypeError,
1013-
"Invalid style mode. Expected `:standard`, or `:diff3`");
1014-
}
1015-
} else {
1016-
opts->flags |= GIT_MERGE_FILE_STYLE_MERGE;
1017-
}
1018-
1019-
if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("simplify")))) {
1020-
opts->flags |= GIT_MERGE_FILE_SIMPLIFY_ALNUM;
1021-
}
1022-
}
1023-
}
1024-
1025955
/*
1026956
* call-seq:
1027957
* index.merge_file(path[, options]) -> merge_file or nil
@@ -1061,8 +991,7 @@ void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_opti
1061991

1062992
static VALUE rb_git_merge_file(int argc, VALUE *argv, VALUE self)
1063993
{
1064-
VALUE rb_path, rb_options;
1065-
VALUE rb_result = rb_hash_new();
994+
VALUE rb_path, rb_options, rb_result;
1066995
VALUE rb_repo = rugged_owner(self);
1067996

1068997
git_repository *repo;
@@ -1074,10 +1003,8 @@ static VALUE rb_git_merge_file(int argc, VALUE *argv, VALUE self)
10741003

10751004
rb_scan_args(argc, argv, "1:", &rb_path, &rb_options);
10761005

1077-
if (!NIL_P(rb_options)) {
1078-
Check_Type(rb_options, T_HASH);
1006+
if (!NIL_P(rb_options))
10791007
rugged_parse_merge_file_options(&opts, rb_options);
1080-
}
10811008

10821009
Check_Type(rb_path, T_STRING);
10831010

@@ -1100,11 +1027,7 @@ static VALUE rb_git_merge_file(int argc, VALUE *argv, VALUE self)
11001027
error = git_merge_file_from_index(&merge_file_result, repo, ancestor, ours, theirs, &opts);
11011028
rugged_exception_check(error);
11021029

1103-
rb_hash_aset(rb_result, CSTR2SYM("automergeable"), merge_file_result.automergeable ? Qtrue : Qfalse);
1104-
rb_hash_aset(rb_result, CSTR2SYM("path"), rb_path);
1105-
rb_hash_aset(rb_result, CSTR2SYM("filemode"), INT2FIX(merge_file_result.mode));
1106-
rb_hash_aset(rb_result, CSTR2SYM("data"), rb_str_new(merge_file_result.ptr, merge_file_result.len));
1107-
1030+
rb_result = rb_merge_file_result_fromC(&merge_file_result);
11081031
git_merge_file_result_free(&merge_file_result);
11091032

11101033
return rb_result;

test/blob_test.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,29 @@ def test_blob_text_set_encoding
122122
blob = @repo.lookup(oid)
123123
assert_equal Encoding::ASCII_8BIT, blob.text(0, Encoding::ASCII_8BIT).encoding
124124
end
125+
126+
def test_blob_merge_files
127+
ancestor = { :content => "Common ancestor", :path => "file.txt", :filemode => 0100644 }
128+
ours = { :content => "Ours side", :path => "newfile.txt", :filemode => 0100644 }
129+
theirs = { :content => "Theirs side", :path => "file.txt", :filemode => 0100755 }
130+
131+
result = Rugged::Blob.merge_files(ancestor, ours, theirs,
132+
{ :ancestor_label => "ANCESTOR",
133+
:our_label => "OURS",
134+
:their_label => "THEIRS",
135+
:style => :diff3 })
136+
137+
assert_equal false, result[:automergeable]
138+
assert_equal "newfile.txt", result[:path]
139+
assert_equal 0100755, result[:filemode]
140+
assert_equal "<<<<<<< OURS\n" +
141+
"Ours side\n" +
142+
"||||||| ANCESTOR\n" +
143+
"Common ancestor\n" +
144+
"=======\n" +
145+
"Theirs side\n" +
146+
">>>>>>> THEIRS\n", result[:data]
147+
end
125148
end
126149

127150
class BlobWriteTest < Rugged::TestCase

0 commit comments

Comments
 (0)