Skip to content

Commit 1962403

Browse files
author
Vicent Marti
committed
Merge pull request #585 from libgit2/cmn/commit-buffer
Add a way to create a commit as a buffer
2 parents 5a10603 + 8d926c2 commit 1962403

File tree

2 files changed

+196
-78
lines changed

2 files changed

+196
-78
lines changed

ext/rugged/rugged_commit.c

Lines changed: 175 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -338,86 +338,60 @@ static VALUE rb_git_commit_amend(VALUE self, VALUE rb_data)
338338
return rugged_create_oid(&commit_oid);
339339
}
340340

341-
/*
342-
* call-seq:
343-
* Commit.create(repository, data = {}) -> oid
344-
*
345-
* Write a new +Commit+ object to +repository+, with the given +data+
346-
* arguments, passed as a +Hash+:
347-
*
348-
* - +:message+: a string with the full text for the commit's message
349-
* - +:committer+ (optional): a hash with the signature for the committer,
350-
* defaults to the signature from the configuration
351-
* - +:author+ (optional): a hash with the signature for the author,
352-
* defaults to the signature from the configuration
353-
* - +:parents+: an +Array+ with zero or more parents for this commit,
354-
* represented as <tt>Rugged::Commit</tt> instances, or OID +String+.
355-
* - +:tree+: the tree for this commit, represented as a <tt>Rugged::Tree</tt>
356-
* instance or an OID +String+.
357-
* - +:update_ref+ (optional): a +String+ with the name of a reference in the
358-
* repository which should be updated to point to this commit (e.g. "HEAD")
359-
*
360-
* When the commit is successfully written to disk, its +oid+ will be
361-
* returned as a hex +String+.
362-
*
363-
* author = {:email=>"[email protected]", :time=>Time.now, :name=>"Vicent Mart\303\255"}
341+
struct commit_data {
342+
VALUE rb_err_obj;
343+
344+
const char *update_ref;
345+
const char *message;
346+
git_tree *tree;
347+
git_signature *author;
348+
git_signature *committer;
349+
int parent_count;
350+
const git_commit **parents;
351+
};
352+
353+
/**
354+
* Parse the commit options into something we can re-use
364355
*
365-
* Rugged::Commit.create(r,
366-
* :author => author,
367-
* :message => "Hello world\n\n",
368-
* :committer => author,
369-
* :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"],
370-
* :tree => some_tree) #=> "f148106ca58764adc93ad4e2d6b1d168422b9796"
356+
* Note that parents may be set even when the function errors, so make
357+
* sure to free this data.
371358
*/
372-
static VALUE rb_git_commit_create(VALUE self, VALUE rb_repo, VALUE rb_data)
359+
static VALUE parse_commit_options(struct commit_data *out, git_repository *repo, VALUE rb_data)
373360
{
374361
VALUE rb_message, rb_tree, rb_parents, rb_ref;
375-
VALUE rb_err_obj = Qnil;
376-
int parent_count, i, error = 0;
377-
const git_commit **parents = NULL;
378-
git_commit **free_list = NULL;
379-
git_tree *tree;
380-
git_signature *author, *committer;
381-
git_oid commit_oid;
382-
git_repository *repo;
383-
const char *update_ref = NULL;
384-
385-
Check_Type(rb_data, T_HASH);
386-
387-
rugged_check_repo(rb_repo);
388-
Data_Get_Struct(rb_repo, git_repository, repo);
362+
int error = 0, parent_count, i;
389363

390364
rb_ref = rb_hash_aref(rb_data, CSTR2SYM("update_ref"));
391365
if (!NIL_P(rb_ref)) {
392366
Check_Type(rb_ref, T_STRING);
393-
update_ref = StringValueCStr(rb_ref);
367+
out->update_ref = StringValueCStr(rb_ref);
394368
}
395369

396370
rb_message = rb_hash_aref(rb_data, CSTR2SYM("message"));
397371
Check_Type(rb_message, T_STRING);
372+
out->message = StringValueCStr(rb_message);
398373

399-
committer = rugged_signature_get(
374+
out->committer = rugged_signature_get(
400375
rb_hash_aref(rb_data, CSTR2SYM("committer")), repo
401376
);
402377

403-
author = rugged_signature_get(
378+
out->author = rugged_signature_get(
404379
rb_hash_aref(rb_data, CSTR2SYM("author")), repo
405380
);
406381

407382
rb_parents = rb_hash_aref(rb_data, CSTR2SYM("parents"));
408383
Check_Type(rb_parents, T_ARRAY);
409384

410385
rb_tree = rb_hash_aref(rb_data, CSTR2SYM("tree"));
411-
tree = (git_tree *)rugged_object_get(repo, rb_tree, GIT_OBJ_TREE);
386+
out->tree = (git_tree *)rugged_object_get(repo, rb_tree, GIT_OBJ_TREE);
412387

413-
parents = alloca(RARRAY_LEN(rb_parents) * sizeof(void *));
414-
free_list = alloca(RARRAY_LEN(rb_parents) * sizeof(void *));
388+
out->parents = xcalloc(RARRAY_LEN(rb_parents), sizeof(void *));
415389
parent_count = 0;
416390

417391
for (i = 0; i < (int)RARRAY_LEN(rb_parents); ++i) {
418392
VALUE p = rb_ary_entry(rb_parents, i);
419393
git_commit *parent = NULL;
420-
git_commit *free_ptr = NULL;
394+
git_commit *tmp = NULL;
421395

422396
if (NIL_P(p))
423397
continue;
@@ -427,49 +401,106 @@ static VALUE rb_git_commit_create(VALUE self, VALUE rb_repo, VALUE rb_data)
427401

428402
error = git_oid_fromstr(&oid, StringValueCStr(p));
429403
if (error < GIT_OK)
430-
goto cleanup;
404+
goto out;
431405

432406
error = git_commit_lookup(&parent, repo, &oid);
433407
if (error < GIT_OK)
434-
goto cleanup;
435-
436-
free_ptr = parent;
437-
408+
goto out;
438409
} else if (rb_obj_is_kind_of(p, rb_cRuggedCommit)) {
439-
Data_Get_Struct(p, git_commit, parent);
410+
Data_Get_Struct(p, git_commit, tmp);
411+
if ((error = git_object_dup((git_object **) &parent, (git_object *) tmp)) < 0)
412+
goto out;
440413
} else {
441-
rb_err_obj = rb_exc_new2(rb_eTypeError, "Invalid type for parent object");
442-
goto cleanup;
414+
out->rb_err_obj = rb_exc_new2(rb_eTypeError, "Invalid type for parent object");
415+
error = -1;
416+
goto out;
443417
}
444418

445-
parents[parent_count] = parent;
446-
free_list[parent_count] = free_ptr;
419+
out->parents[parent_count] = parent;
447420
parent_count++;
448421
}
449422

423+
out:
424+
out->parent_count = parent_count;
425+
return error;
426+
}
427+
428+
static void free_commit_options(struct commit_data *commit_data)
429+
{
430+
int i;
431+
432+
git_signature_free(commit_data->author);
433+
git_signature_free(commit_data->committer);
434+
435+
git_object_free((git_object *)commit_data->tree);
436+
437+
for (i = 0; i < commit_data->parent_count; ++i)
438+
git_object_free((git_object *) commit_data->parents[i]);
439+
xfree(commit_data->parents);
440+
}
441+
442+
/*
443+
* call-seq:
444+
* Commit.create(repository, data = {}) -> oid
445+
*
446+
* Write a new +Commit+ object to +repository+, with the given +data+
447+
* arguments, passed as a +Hash+:
448+
*
449+
* - +:message+: a string with the full text for the commit's message
450+
* - +:committer+ (optional): a hash with the signature for the committer,
451+
* defaults to the signature from the configuration
452+
* - +:author+ (optional): a hash with the signature for the author,
453+
* defaults to the signature from the configuration
454+
* - +:parents+: an +Array+ with zero or more parents for this commit,
455+
* represented as <tt>Rugged::Commit</tt> instances, or OID +String+.
456+
* - +:tree+: the tree for this commit, represented as a <tt>Rugged::Tree</tt>
457+
* instance or an OID +String+.
458+
* - +:update_ref+ (optional): a +String+ with the name of a reference in the
459+
* repository which should be updated to point to this commit (e.g. "HEAD")
460+
*
461+
* When the commit is successfully written to disk, its +oid+ will be
462+
* returned as a hex +String+.
463+
*
464+
* author = {:email=>"[email protected]", :time=>Time.now, :name=>"Vicent Mart\303\255"}
465+
*
466+
* Rugged::Commit.create(r,
467+
* :author => author,
468+
* :message => "Hello world\n\n",
469+
* :committer => author,
470+
* :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"],
471+
* :tree => some_tree) #=> "f148106ca58764adc93ad4e2d6b1d168422b9796"
472+
*/
473+
static VALUE rb_git_commit_create(VALUE self, VALUE rb_repo, VALUE rb_data)
474+
{
475+
int error = 0;
476+
struct commit_data commit_data = { Qnil };
477+
git_oid commit_oid;
478+
git_repository *repo;
479+
480+
Check_Type(rb_data, T_HASH);
481+
482+
rugged_check_repo(rb_repo);
483+
Data_Get_Struct(rb_repo, git_repository, repo);
484+
485+
if ((error = parse_commit_options(&commit_data, repo, rb_data)) < 0)
486+
goto cleanup;
487+
450488
error = git_commit_create(
451489
&commit_oid,
452490
repo,
453-
update_ref,
454-
author,
455-
committer,
491+
commit_data.update_ref,
492+
commit_data.author,
493+
commit_data.committer,
456494
NULL,
457-
StringValueCStr(rb_message),
458-
tree,
459-
parent_count,
460-
parents);
495+
commit_data.message,
496+
commit_data.tree,
497+
commit_data.parent_count,
498+
commit_data.parents);
461499

462500
cleanup:
463-
git_signature_free(author);
464-
git_signature_free(committer);
465-
466-
git_object_free((git_object *)tree);
467-
468-
for (i = 0; i < parent_count; ++i)
469-
git_object_free((git_object *)free_list[i]);
470-
471-
if (!NIL_P(rb_err_obj))
472-
rb_exc_raise(rb_err_obj);
501+
free_commit_options(&commit_data);
502+
if (!NIL_P(commit_data.rb_err_obj))
503+
rb_exc_raise(commit_data.rb_err_obj);
473504

474505
rugged_exception_check(error);
475506

@@ -662,11 +693,77 @@ static VALUE rb_git_commit_extract_signature(int argc, VALUE *argv, VALUE self)
662693
return ret;
663694
}
664695

696+
/*
697+
* call-seq:
698+
* Commit.create_to_s(repository, data = {}) -> str
699+
*
700+
* Create a string with the contents of the commit, created with the
701+
* given +data+ arguments, passed as a +Hash+:
702+
*
703+
* - +:message+: a string with the full text for the commit's message
704+
* - +:committer+ (optional): a hash with the signature for the committer,
705+
* defaults to the signature from the configuration
706+
* - +:author+ (optional): a hash with the signature for the author,
707+
* defaults to the signature from the configuration
708+
* - +:parents+: an +Array+ with zero or more parents for this commit,
709+
* represented as <tt>Rugged::Commit</tt> instances, or OID +String+.
710+
* - +:tree+: the tree for this commit, represented as a <tt>Rugged::Tree</tt>
711+
* instance or an OID +String+.
712+
*
713+
* author = {:email=>"[email protected]", :time=>Time.now, :name=>"Vicent Mart\303\255"}
714+
*
715+
* Rugged::Commit.create(r,
716+
* :author => author,
717+
* :message => "Hello world\n\n",
718+
* :committer => author,
719+
* :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"],
720+
* :tree => some_tree) #=> "tree some_tree\nparent 2cb831...."
721+
*/
722+
static VALUE rb_git_commit_create_to_s(VALUE self, VALUE rb_repo, VALUE rb_data)
723+
{
724+
int error = 0;
725+
struct commit_data commit_data = { Qnil };
726+
git_repository *repo;
727+
git_buf buf = { 0 };
728+
729+
Check_Type(rb_data, T_HASH);
730+
731+
rugged_check_repo(rb_repo);
732+
Data_Get_Struct(rb_repo, git_repository, repo);
733+
734+
if ((error = parse_commit_options(&commit_data, repo, rb_data)) < 0)
735+
goto cleanup;
736+
737+
error = git_commit_create_buffer(
738+
&buf,
739+
repo,
740+
commit_data.author,
741+
commit_data.committer,
742+
NULL,
743+
commit_data.message,
744+
commit_data.tree,
745+
commit_data.parent_count,
746+
commit_data.parents);
747+
748+
cleanup:
749+
free_commit_options(&commit_data);
750+
if (!NIL_P(commit_data.rb_err_obj))
751+
rb_exc_raise(commit_data.rb_err_obj);
752+
753+
rugged_exception_check(error);
754+
755+
VALUE ret = rb_str_new_utf8(buf.ptr);
756+
git_buf_free(&buf);
757+
758+
return ret;
759+
}
760+
665761
void Init_rugged_commit(void)
666762
{
667763
rb_cRuggedCommit = rb_define_class_under(rb_mRugged, "Commit", rb_cRuggedObject);
668764

669765
rb_define_singleton_method(rb_cRuggedCommit, "create", rb_git_commit_create, 2);
766+
rb_define_singleton_method(rb_cRuggedCommit, "create_to_s", rb_git_commit_create_to_s, 2);
670767
rb_define_singleton_method(rb_cRuggedCommit, "extract_signature", rb_git_commit_extract_signature, -1);
671768

672769
rb_define_method(rb_cRuggedCommit, "message", rb_git_commit_message_GET, 0);

test/commit_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,27 @@ def test_write_empty_email_fails
369369
:tree => "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b")
370370
end
371371
end
372+
373+
def test_create_commit_to_s
374+
person = {:name => 'Scott', :email => '[email protected]', :time => Time.now }
375+
376+
id = Rugged::Commit.create(@repo,
377+
:message => "This is the commit message\n\nThis commit is created from Rugged",
378+
:committer => person,
379+
:author => person,
380+
:parents => [@repo.head.target],
381+
:tree => "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b")
382+
383+
buffer = Rugged::Commit.create_to_s(@repo,
384+
:message => "This is the commit message\n\nThis commit is created from Rugged",
385+
:committer => person,
386+
:author => person,
387+
:parents => [@repo.head.target],
388+
:tree => "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b")
389+
390+
commit = @repo.lookup(id)
391+
assert_equal buffer, commit.read_raw.data
392+
end
372393
end
373394

374395
class CommitToMboxTest < Rugged::TestCase

0 commit comments

Comments
 (0)