/*
* Copyright 2013, Tim Branyen @tbranyen
* @author Michael Robinson @codeofinterest
*
* Dual licensed under the MIT and GPL licenses.
*/
#include
#include
#include
#include "git2.h"
#include "cvv8/v8-convert.hpp"
#include "../include/reference.h"
#include "../include/signature.h"
#include "../include/repo.h"
#include "../include/oid.h"
#include "../include/tree.h"
#include "../include/commit.h"
#include "../include/error.h"
#include "../include/functions/utilities.h"
#include "../include/functions/string.h"
using namespace v8;
using namespace cvv8;
using namespace node;
void GitCommit::Initialize(Handle target) {
HandleScope scope;
Local tpl = FunctionTemplate::New(New);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
tpl->SetClassName(String::NewSymbol("Commit"));
NODE_SET_PROTOTYPE_METHOD(tpl, "lookup", Lookup);
NODE_SET_PROTOTYPE_METHOD(tpl, "oid", Oid);
NODE_SET_PROTOTYPE_METHOD(tpl, "message", Message);
NODE_SET_PROTOTYPE_METHOD(tpl, "time", Time);
NODE_SET_PROTOTYPE_METHOD(tpl, "offset", Offset);
NODE_SET_PROTOTYPE_METHOD(tpl, "author", Author);
NODE_SET_PROTOTYPE_METHOD(tpl, "committer", Committer);
NODE_SET_PROTOTYPE_METHOD(tpl, "tree", Tree);
NODE_SET_PROTOTYPE_METHOD(tpl, "parents", Parents);
NODE_SET_PROTOTYPE_METHOD(tpl, "free", Free);
constructor_template = Persistent::New(tpl->GetFunction());
target->Set(String::NewSymbol("Commit"), constructor_template);
}
git_commit* GitCommit::GetValue() {
return this->commit;
}
void GitCommit::SetValue(git_commit* commit) {
this->commit = commit;
}
void GitCommit::SetOid(git_oid* oid) {
this->oid = oid;
}
Handle GitCommit::New(const Arguments& args) {
HandleScope scope;
GitCommit *commit = new GitCommit();
commit->Wrap(args.This());
return scope.Close(args.This());
}
Handle GitCommit::Free(const Arguments& args) {
HandleScope scope;
GitCommit *commit = ObjectWrap::Unwrap(args.This());
git_commit_free(commit->commit);
commit->commit = NULL;
return Undefined();
}
Handle GitCommit::Lookup(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsObject()) {
return ThrowException(Exception::Error(String::New("Repo is required and must be an Object.")));
}
if(args.Length() == 1 || !(args[1]->IsObject() || args[1]->IsString())) {
return ThrowException(Exception::Error(String::New("Oid is required and must be an Object or String")));
}
if(args.Length() == 2 || !args[2]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
LookupBaton *baton = new LookupBaton;
baton->request.data = baton;
baton->error = NULL;
baton->repo = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue();
if (args[1]->IsObject()) {
baton->rawOid = ObjectWrap::Unwrap(args[1]->ToObject())->GetValue();
} else {
baton->sha = stringArgToString(args[1]->ToString());
}
baton->callback = Persistent::New(Local::Cast(args[2]));
uv_queue_work(uv_default_loop(), &baton->request, LookupWork, (uv_after_work_cb)LookupAfterWork);
return Undefined();
}
void GitCommit::LookupWork(uv_work_t *req) {
LookupBaton *baton = static_cast(req->data);
if (!baton->sha.empty()) {
int returnCode = git_oid_fromstr(&baton->rawOid, baton->sha.c_str());
if (returnCode != GIT_OK) {
baton->error = giterr_last();
return;
}
}
baton->rawCommit = NULL;
int returnCode = git_commit_lookup(&baton->rawCommit, baton->repo, &baton->rawOid);
if (returnCode != GIT_OK) {
baton->error = giterr_last();
}
}
void GitCommit::LookupAfterWork(uv_work_t *req) {
HandleScope scope;
LookupBaton *baton = static_cast(req->data);
if (success(baton->error, baton->callback)) {
Local commit = GitCommit::constructor_template->NewInstance();
GitCommit *commitInstance = ObjectWrap::Unwrap(commit);
commitInstance->SetValue(baton->rawCommit);
commitInstance->SetOid(&baton->rawOid);
Handle argv[2] = {
Local::New(Null()),
commit
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
delete req;
}
Handle GitCommit::Oid(const Arguments& args) {
HandleScope scope;
Local oid = GitOid::constructor_template->NewInstance();
GitOid *oidInstance = ObjectWrap::Unwrap(oid);
oidInstance->SetValue(*const_cast(ObjectWrap::Unwrap(args.This())->oid));
return scope.Close(oid);
}
Handle GitCommit::Message(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
MessageBaton* baton = new MessageBaton;
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, MessageWork, (uv_after_work_cb)MessageAfterWork);
return Undefined();
}
void GitCommit::MessageWork(uv_work_t* req) {
MessageBaton *baton = static_cast(req->data);
baton->message = git_commit_message(baton->rawCommit);
}
void GitCommit::MessageAfterWork(uv_work_t* req) {
HandleScope scope;
MessageBaton *baton = static_cast(req->data);
Handle argv[2] = {
Local::New(Null()),
String::New(baton->message.c_str())
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
delete req;
}
Handle GitCommit::Time(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
TimeBaton* baton = new TimeBaton;
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, TimeWork, (uv_after_work_cb)TimeAfterWork);
return Undefined();
}
void GitCommit::TimeWork(uv_work_t* req) {
TimeBaton *baton = static_cast(req->data);
baton->time = git_commit_time(baton->rawCommit);
}
void GitCommit::TimeAfterWork(uv_work_t* req) {
HandleScope scope;
TimeBaton *baton = static_cast(req->data);
Handle argv[2] = {
Local::New(Null()),
Integer::New(baton->time)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
delete req;
}
Handle GitCommit::Offset(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
OffsetBaton* baton = new OffsetBaton;
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, OffsetWork, (uv_after_work_cb)OffsetAfterWork);
return Undefined();
}
void GitCommit::OffsetWork(uv_work_t* req) {
OffsetBaton *baton = static_cast(req->data);
baton->offset = git_commit_time_offset(baton->rawCommit);
}
void GitCommit::OffsetAfterWork(uv_work_t* req) {
HandleScope scope;
OffsetBaton *baton = static_cast(req->data);
Handle argv[2] = {
Local::New(Null()),
Integer::New(baton->offset)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
delete req;
}
Handle GitCommit::Author(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
SignatureBaton* baton = new SignatureBaton;
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, AuthorWork, (uv_after_work_cb)AuthorAfterWork);
return Undefined();
}
void GitCommit::AuthorWork(uv_work_t* req) {
SignatureBaton *baton = static_cast(req->data);
baton->rawSignature = git_commit_author(baton->rawCommit);
}
void GitCommit::AuthorAfterWork(uv_work_t* req) {
HandleScope scope;
SignatureBaton *baton = static_cast(req->data);
Local signature = GitSignature::constructor_template->NewInstance();
GitSignature *signatureInstance = ObjectWrap::Unwrap(signature);
signatureInstance->SetValue(const_cast(baton->rawSignature));
Handle argv[2] = {
Local::New(Null()),
signature
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
delete req;
}
Handle GitCommit::Committer(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
SignatureBaton* baton = new SignatureBaton;
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, CommitterWork, (uv_after_work_cb)CommitterAfterWork);
return Undefined();
}
void GitCommit::CommitterWork(uv_work_t* req) {
SignatureBaton *baton = static_cast(req->data);
baton->rawSignature = git_commit_committer(baton->rawCommit);
}
void GitCommit::CommitterAfterWork(uv_work_t* req) {
HandleScope scope;
SignatureBaton *baton = static_cast(req->data);
Local signature = GitSignature::constructor_template->NewInstance();
GitSignature *signatureInstance = ObjectWrap::Unwrap(signature);
signatureInstance->SetValue(const_cast(baton->rawSignature));
Handle argv[2] = {
Local::New(Null()),
signature
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
delete req;
}
Handle GitCommit::Tree(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
TreeBaton* baton = new TreeBaton;
baton->request.data = baton;
baton->error = NULL;
baton->rawTree = NULL;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, TreeWork, (uv_after_work_cb)TreeAfterWork);
return Undefined();
}
void GitCommit::TreeWork(uv_work_t* req) {
TreeBaton* baton = static_cast(req->data);
int returnCode = git_commit_tree(&baton->rawTree, baton->rawCommit);
if (returnCode != GIT_OK) {
baton->error = giterr_last();
}
}
void GitCommit::TreeAfterWork(uv_work_t* req) {
HandleScope scope;
TreeBaton* baton = static_cast(req->data);
if (success(baton->error, baton->callback)) {
Local tree = GitTree::constructor_template->NewInstance();
GitTree *treeInstance = ObjectWrap::Unwrap(tree);
treeInstance->SetValue(baton->rawTree);
Handle argv[2] = {
Local::New(Null()),
tree
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
delete req;
}
Handle GitCommit::Parents(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 && !args[0]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
ParentsBaton* baton = new ParentsBaton;
baton->request.data = baton;
baton->error = NULL;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue();
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, ParentsWork, (uv_after_work_cb)ParentsAfterWork);
return Undefined();
}
void GitCommit::ParentsWork(uv_work_t* req) {
ParentsBaton* baton = static_cast(req->data);
int parentCount = git_commit_parentcount(baton->rawCommit);
while (parentCount > 0) {
git_commit* rawParentCommit = NULL;
int parentIndex = parentCount -1;
int returnCode = git_commit_parent(&rawParentCommit, baton->rawCommit, parentIndex);
if (returnCode != GIT_OK) {
baton->error = giterr_last();
return;
}
const git_oid* rawParentOid = git_commit_id(rawParentCommit);
Parent* parent = new Parent;
parent->rawCommit = rawParentCommit;
parent->rawOid = rawParentOid;
baton->parents.push_back(parent);
parentCount--;
}
}
void GitCommit::ParentsAfterWork(uv_work_t* req) {
HandleScope scope;
ParentsBaton* baton = static_cast(req->data);
if (success(baton->error, baton->callback)) {
std::vector parents;
for(std::vector::iterator parentIterator = baton->parents.begin(); parentIterator != baton->parents.end(); ++parentIterator) {
Parent *parentData = (*parentIterator);
Local parent = GitCommit::constructor_template->NewInstance();
GitCommit *parentInstance = ObjectWrap::Unwrap(parent);
parentInstance->SetValue(parentData->rawCommit);
parentInstance->SetOid(const_cast(parentData->rawOid));
parents.push_back(parent);
}
Handle argv[2] = {
Local::New(Null()),
cvv8::CastToJS(parents)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
delete req;
}
Persistent GitCommit::constructor_template;