/*
* Copyright 2011, Tim Branyen @tbranyen
* @author Michael Robinson @codeofinterest
*
* Dual licensed under the MIT and GPL licenses.
*/
#include
#include
#include
#include "../vendor/libgit2/include/git2.h"
#include "cvv8/v8-convert.hpp"
#include "../include/reference.h"
#include "../include/sig.h"
#include "../include/repo.h"
#include "../include/oid.h"
#include "../include/tree.h"
#include "../include/commit.h"
#include "../include/error.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, "close", Close);
NODE_SET_PROTOTYPE_METHOD(tpl, "tree", Tree);
NODE_SET_PROTOTYPE_METHOD(tpl, "parent", Parent);
NODE_SET_PROTOTYPE_METHOD(tpl, "parentSync", ParentSync);
NODE_SET_PROTOTYPE_METHOD(tpl, "fetchDetails", FetchDetails);
NODE_SET_PROTOTYPE_METHOD(tpl, "fetchDetailsSync", FetchDetailsSync);
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::Close() {
git_commit_free(this->commit);
this->commit = NULL;
}
int GitCommit::Tree(git_tree** tree) {
return git_commit_tree(tree, this->commit);
}
Handle GitCommit::New(const Arguments& args) {
HandleScope scope;
GitCommit *commit = new GitCommit();
commit->Wrap(args.This());
return scope.Close(args.This());
}
Handle GitCommit::FetchDetailsSync(const Arguments& args) {
HandleScope scope;
GitCommit *commit = ObjectWrap::Unwrap(args.This());
Local details = Object::New();
Handle oid = GitOid::constructor_template->NewInstance();
char sha[GIT_OID_HEXSZ + 1];
sha[GIT_OID_HEXSZ] = '\0';
GitOid *oidInstance = ObjectWrap::Unwrap(oid);
const git_oid* rawOid = git_commit_id(commit->commit);
oidInstance->SetValue(*const_cast(rawOid));
git_oid_fmt(sha, rawOid);
details->Set(String::NewSymbol("id"), oid);
details->Set(String::NewSymbol("sha"), String::New(sha));
details->Set(String::NewSymbol("message"), cvv8::CastToJS(git_commit_message(commit->commit)));
details->Set(String::NewSymbol("time"), cvv8::CastToJS(git_commit_time(commit->commit)));
details->Set(String::NewSymbol("timeOffset"), cvv8::CastToJS(git_commit_time_offset(commit->commit)));
const git_signature *rawCommitter = git_commit_committer(commit->commit);
Local committer = Object::New();
committer->Set(String::NewSymbol("name"), cvv8::CastToJS(rawCommitter->name));
committer->Set(String::NewSymbol("email"), cvv8::CastToJS(rawCommitter->email));
Local committerWhen = Object::New();
committerWhen->Set(String::NewSymbol("when"), cvv8::CastToJS(rawCommitter->when.time));
committerWhen->Set(String::NewSymbol("offset"), cvv8::CastToJS(rawCommitter->when.offset));
committer->Set(String::NewSymbol("when"), cvv8::CastToJS(committerWhen));
details->Set(String::NewSymbol("committer"), committer);
const git_signature* rawAuthor = git_commit_author(commit->commit);
Local author = Object::New();
author->Set(String::NewSymbol("name"), cvv8::CastToJS(rawAuthor->name));
author->Set(String::NewSymbol("email"), cvv8::CastToJS(rawAuthor->email));
Local authorWhen = Object::New();
authorWhen->Set(String::NewSymbol("when"), cvv8::CastToJS(rawAuthor->when.time));
authorWhen->Set(String::NewSymbol("offset"), cvv8::CastToJS(rawAuthor->when.offset));
author->Set(String::NewSymbol("when"), authorWhen);
details->Set(String::NewSymbol("author"), author);
int parentCount = git_commit_parentcount(commit->commit);
std::vector parentShas;
while (parentCount > 0) {
int parentIndex = parentCount -1;
char sha[GIT_OID_HEXSZ + 1];
sha[GIT_OID_HEXSZ] = '\0';
const git_oid *parentOid = git_commit_parent_id(commit->commit, parentIndex);
git_oid_fmt(sha, parentOid);
parentShas.push_back(sha);
parentCount--;
}
details->Set(String::NewSymbol("parentCount"), cvv8::CastToJS(parentCount));
details->Set(String::NewSymbol("parentShas"), cvv8::CastToJS(parentShas));
return scope.Close(details);
}
Handle GitCommit::FetchDetails(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.")));
}
FetchDetailsBaton* baton = new FetchDetailsBaton();
baton->request.data = baton;
baton->rawCommit = ObjectWrap::Unwrap(args.This())->commit;
baton->callback = Persistent::New(Local::Cast(args[0]));
uv_queue_work(uv_default_loop(), &baton->request, FetchDetailsWork, (uv_after_work_cb)FetchDetailsAfterWork);
return Undefined();
}
void GitCommit::FetchDetailsWork(uv_work_t *req) {
FetchDetailsBaton* baton = static_cast(req->data);
baton->oid = git_commit_id(baton->rawCommit);
baton->sha[GIT_OID_HEXSZ] = '\0';
git_oid_fmt(baton->sha, baton->oid);
baton->message = git_commit_message(baton->rawCommit);
baton->time = git_commit_time(baton->rawCommit);
baton->timeOffset = git_commit_time_offset(baton->rawCommit);
baton->committer = git_commit_committer(baton->rawCommit);
baton->author = git_commit_author(baton->rawCommit);
baton->parentCount = git_commit_parentcount(baton->rawCommit);
int parentCount = baton->parentCount;
while (parentCount > 0) {
int parentIndex = parentCount -1;
char sha[GIT_OID_HEXSZ + 1];
sha[GIT_OID_HEXSZ] = '\0';
const git_oid *parentOid = git_commit_parent_id(baton->rawCommit, parentIndex);
git_oid_fmt(sha, parentOid);
baton->parentShas.push_back(sha);
parentCount--;
}
}
void GitCommit::FetchDetailsAfterWork(uv_work_t *req) {
HandleScope scope;
FetchDetailsBaton* baton = static_cast(req->data);
if (baton->error) {
Local argv[1] = {
GitError::WrapError(baton->error)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 1, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
} else {
Local details = Object::New();
Handle oid = GitOid::constructor_template->NewInstance();
GitOid *oidInstance = ObjectWrap::Unwrap(oid);
oidInstance->SetValue(*const_cast(baton->oid));
details->Set(String::NewSymbol("id"), oid);
details->Set(String::NewSymbol("sha"), String::New(baton->sha));
details->Set(String::NewSymbol("message"), cvv8::CastToJS(baton->message));
details->Set(String::NewSymbol("time"), cvv8::CastToJS(baton->time));
details->Set(String::NewSymbol("timeOffset"), cvv8::CastToJS(baton->timeOffset));
Local committer = Object::New();
committer->Set(String::NewSymbol("name"), cvv8::CastToJS(baton->committer->name));
committer->Set(String::NewSymbol("email"), cvv8::CastToJS(baton->committer->email));
Local committerWhen = Object::New();
committerWhen->Set(String::NewSymbol("time"), cvv8::CastToJS(baton->committer->when.time));
committerWhen->Set(String::NewSymbol("offset"), cvv8::CastToJS(baton->committer->when.offset));
committer->Set(String::NewSymbol("when"), cvv8::CastToJS(committerWhen));
details->Set(String::NewSymbol("committer"), committer);
Local author = Object::New();
author->Set(String::NewSymbol("name"), cvv8::CastToJS(baton->author->name));
author->Set(String::NewSymbol("email"), cvv8::CastToJS(baton->author->email));
Local authorWhen = Object::New();
authorWhen->Set(String::NewSymbol("time"), cvv8::CastToJS(baton->author->when.time));
authorWhen->Set(String::NewSymbol("offset"), cvv8::CastToJS(baton->author->when.offset));
author->Set(String::NewSymbol("when"), authorWhen);
details->Set(String::NewSymbol("author"), author);
details->Set(String::NewSymbol("parentCount"), cvv8::CastToJS(baton->parentCount));
details->Set(String::NewSymbol("parentShas"), cvv8::CastToJS(baton->parentShas));
Handle argv[2] = {
Local::New(Null()),
details
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
delete req;
}
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);
git_oid rawOid = baton->rawOid;
if (!baton->sha.empty()) {
int returnCode = git_oid_fromstr(&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, &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 (baton->error) {
Local argv[1] = {
GitError::WrapError(baton->error)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 1, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
} else {
Local commit = GitCommit::constructor_template->NewInstance();
GitCommit *commitInstance = ObjectWrap::Unwrap(commit);
commitInstance->SetValue(baton->rawCommit);
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::Close(const Arguments& args) {
HandleScope scope;
GitCommit *commit = ObjectWrap::Unwrap(args.This());
commit->Close();
return scope.Close(Undefined());
}
/**
* @todo asynchronize
*/
Handle GitCommit::Tree(const Arguments& args) {
HandleScope scope;
GitCommit *commit = ObjectWrap::Unwrap(args.This());
if(args.Length() == 0 || !args[0]->IsObject()) {
return ThrowException(Exception::Error(String::New("Tree is required and must be an Object.")));
}
GitTree* g_tree = ObjectWrap::Unwrap(args[0]->ToObject());
git_tree* tree;
int err = commit->Tree(&tree);
g_tree->SetValue(tree);
return scope.Close( Integer::New(err) );
}
Handle GitCommit::ParentSync(const Arguments& args) {
HandleScope scope;
if(args.Length() == 0 || !args[0]->IsNumber()) {
return ThrowException(Exception::Error(String::New("Position must be a Number.")));
}
GitCommit *commit = ObjectWrap::Unwrap(args.This());
git_commit *parentCommitValue ;
int returnCode = git_commit_parent(&parentCommitValue, commit->commit, args[0]->ToInteger()->Value());
if (returnCode != GIT_OK) {
return ThrowException(Exception::Error(String::New(giterr_last()->message)));
}
Local parent = GitCommit::constructor_template->NewInstance();
GitCommit *parentCommit = ObjectWrap::Unwrap(parent);
parentCommit->SetValue(parentCommitValue);
return scope.Close(parent);
}
Handle GitCommit::Parent(const Arguments& args) {
HandleScope scope;
if(args.Length() != 2) {
return ThrowException(Exception::Error(String::New("Position and callback are required")));
}
if(!args[0]->IsNumber()) {
return ThrowException(Exception::Error(String::New("Position is required and must be a Number.")));
}
if(!args[1]->IsFunction()) {
return ThrowException(Exception::Error(String::New("Callback is required and must be a Function.")));
}
ParentBaton* baton = new ParentBaton();
baton->request.data = baton;
baton->commit = ObjectWrap::Unwrap(args.This());
baton->commit->Ref();
baton->error = NULL;
baton->index = args[0]->ToInteger()->Value();
baton->callback = Persistent::New(Local::Cast(args[1]));
uv_queue_work(uv_default_loop(), &baton->request, ParentWork, (uv_after_work_cb)ParentAfterWork);
return Undefined();
}
void GitCommit::ParentWork(uv_work_t* req) {
ParentBaton* baton = static_cast(req->data);
baton->rawParentCommit = NULL;
int returnCode = git_commit_parent(&baton->rawParentCommit, baton->commit->commit, baton->index);
if (returnCode != GIT_OK) {
baton->error = giterr_last();
}
}
void GitCommit::ParentAfterWork(uv_work_t* req) {
HandleScope scope;
ParentBaton* baton = static_cast(req->data);
if (baton->error) {
Local argv[1] = {
GitError::WrapError(baton->error)
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 1, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
} else {
Local parent = GitCommit::constructor_template->NewInstance();
GitCommit *parentInstance = ObjectWrap::Unwrap(parent);
parentInstance->SetValue(baton->rawParentCommit);
Handle argv[2] = {
Local::New(Null()),
parent
};
TryCatch try_catch;
baton->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught()) {
node::FatalException(try_catch);
}
}
baton->commit->Unref();
delete req;
}
Persistent GitCommit::constructor_template;