[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/nodegit/nodegit/v0.0.75/src/commit.cc [Back]  [Original]

/*
 * 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;

Web Proxy Viewer  |  New URL  |  Original Page