diff --git a/.gitignore b/.gitignore index 1f63036f9..42cebca29 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ /doc !doc/Theme.css /node_modules - +/testing.js +/out +/test.git diff --git a/.gitmodules b/.gitmodules index c7bc5e0e7..ca6a48f0b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "example/stress/jquery"] - path = example/stress/jquery - url = https://github.com/jquery/jquery.git [submodule "vendor/libgit2"] path = vendor/libgit2 url = git://github.com/libgit2/libgit2.git diff --git a/README.md b/README.md index cd3769037..383e938c9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ nodegit > Node.js libgit2 bindings -**v0.0.74** [![Build +**v0.0.78** [![Build Status](https://travis-ci.org/tbranyen/nodegit.png)](https://travis-ci.org/tbranyen/nodegit) Maintained by Tim Branyen [@tbranyen](http://twitter.com/tbranyen) and Michael @@ -11,6 +11,11 @@ Robinson [@codeofinterest](http://twitter.com/codeofinterest), with help from [awesome contributors](https://github.com/tbranyen/nodegit/contributors)! +API Documentation +------------------------ + +Documentation may be found here: [`nodegit` documentation](http://tbranyen.github.com/nodegit/). + Contributing ------------ @@ -70,123 +75,56 @@ API Example Usage #### Convenience API #### ```JavaScript -var git = require("nodegit"); - -// Read a repository -var git = require('nodegit'); - -// nodegit follows the error, values ... callback argument convention -git.repo('.git', function(error, repo) { - if (error) { throw error; } - - // Use the master branch - repo.branch('master', function(error, branch) { - if (error) { throw error; } - - // Print out `git log` emulation for each commit in the branch's history - branch.history().on('commit', function(error, commit) { - console.log('commit ' + commit.sha); - console.log(commit.author.name + '<' + commit.author.email + '>'); - console.log(new Date(commit.time * 1000)); - console.log("\n"); - console.log(commit.message); - console.log("\n"); - }); - }); -}); -``` - -#### Raw API #### - -```` javascript -var git = require('nodegit').raw; - -// Create instance of Repo constructor -var repo = new git.Repo(); - -// Read a repository -repo.open('.git', function(err) { - // Err is an integer, success is 0, use strError for string representation - if (err) { - var error = new git.Error(); - throw error.strError(err); +// Load in the module. +var git = require('nodegit'), + async = require('async'); + +// Open the repository in the current directory. +git.repo('.git', function(error, repository) { + if (error) { + throw error; } - // Create instance of Ref constructor with this repository - var ref = new git.Ref(repo); - - // Find the master branch - repo.lookupRef(ref, '/refs/heads/master', function(err) { - if (err) { - var error = new git.Error(); - throw error.strError(err); + // Use the master branch. + repository.branch('master', function(error, branch) { + if (error) { + throw error; } - // Create instance of Commit constructor with this repository - var commit = new git.Commit(repo); - - // Create instance of Oid constructor - var oid = new git.Oid(); - - // Set the oid constructor internal reference to this branch reference - ref.oid(oid); - - // Lookup the commit for this oid - commit.lookup(oid, function(err) { - if (err) { - var error = new git.Error(); - throw error.strError(err); - } - - // Create instance of RevWalk constructor with this repository - var revwalk = new git.RevWalk(repo); - - // Push the commit as the start to walk - revwalk.push(commit); - - // Recursive walk - function walk() { - // Each revision walk iteration yields a commit - var revisionCommit = new git.Commit(repo); - - revwalk.next(revisionCommit, function(err) { - // Finish recursion once no more revision commits are left - if (err) { return; } - - // Create instance of Oid for sha - var oid = new git.Oid(); - - // Set oid to the revision commit - revisionCommit.id(oid); - - // Create instance of Sig for author - var author = new git.Sig(); - - // Set the author to the revision commit author - revisionCommit.author(author); - - // Convert timestamp to milliseconds and set new Date object - var time = new Date( revisionCommit.time() * 1000 ); - - // Print out `git log` emulation - console.log(oid.toString( 40 )); - console.log(author.name() + '<' + author.email() + '>'); - console.log(time); - console.log('\n'); - console.log(revisionCommit.message()); - console.log('\n'); + // Iterate over the revision history. + branch.history().on('commit', function(error, commit) { - // Recurse! - walk(); + // Print out `git log` emulation. + async.series([ + function(callback) { + commit.sha(callback); + }, + function(callback) { + commit.time(callback); + }, + function(callback) { + commit.author(function(error, author) { + author.name(callback); + }); + }, + function(callback) { + commit.author(function(error, author) { + author.email(callback); + }); + }, + function(callback) { + commit.message(callback); + } + ], function printCommit(error, results) { + console.log('SHA ' + results[0]); + console.log(new Date(results[1] * 1000)); + console.log(results[2] + ' <' + results[3] + '>'); + console.log(results[4]); }); - } - - // Initiate recursion - walk(): }); }); }); -```` +``` Running tests ------------- @@ -197,19 +135,6 @@ __To run unit tests ensure to update the submodules with `git submodule update - Then simply run `npm test` in the project root. -Documentation ------------------------- - -Recent documentation may be found here: [`nodegit` documentation](http://tbranyen.github.com/nodegit/) - -__`nodegit` native and library code is documented to be built with `Natural Docs`.__ - -To create the documentation, `cd` into the `nodegit` dir and run the following: - $ cd nodegit - $ make doc - -The documentation will then generate in the `doc/` subfolder as HTML. - Release information ------------------- diff --git a/binding.gyp b/binding.gyp index ad556410f..b683da9ea 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,17 +7,17 @@ 'src/blob.cc', 'src/commit.cc', 'src/error.cc', - 'src/object.cc', 'src/oid.cc', 'src/reference.cc', 'src/repo.cc', 'src/revwalk.cc', - 'src/sig.cc', + 'src/signature.cc', 'src/tree.cc', 'src/tree_entry.cc', 'src/diff_list.cc', 'src/threads.cc', - 'src/functions/string.cc' + 'src/functions/string.cc', + 'src/functions/utilities.cc' ], 'include_dirs': [ diff --git a/example/convenience-commit.js b/example/convenience-commit.js index 235e39d04..1e2320a85 100644 --- a/example/convenience-commit.js +++ b/example/convenience-commit.js @@ -1,43 +1,48 @@ -var git = require( '../' ); - -git.repo( '../.git', function( err, repo ) { - if( err ) { throw new Error( err ); } - - repo.commit( '59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5', function( err, commit ) { - if( err ) { throw new Error( err ); } - - var history = commit.history(); - history.on( 'commit', function() { - // console.log(arguments); - }); - - history.on( 'end', function( commits ) { - // Read first commit tree - var tree = commits[0].tree(); - - // Synchronous - tree.walk().on('entry', function( i, entry ) { - console.log( entry.content ); - return false; - }); - - // Asynchronous - not yet implemented - //tree.walk().on( 'entry', function( err, i, entry ) { - // console.log( entry ); - //}); +// Load in the module. +var git = require('nodegit'), + async = require('async'); + +// Open the repository in the current directory. +git.repo('.git', function(error, repository) { + if (error) throw error; + + // Use the master branch (a branch is the HEAD commit) + repository.branch('master', function(error, branch) { + if (error) throw error; + + // History returns an event, and begins walking the history + var history = branch.history(); + + // History emits 'commit' event for each commit in the branch's history + history.on('commit', function(error, commit) { + // Print out `git log` emulation. + async.series([ + function(callback) { + commit.sha(callback); + }, + function(callback) { + commit.date(callback); + }, + function(callback) { + commit.author(function(error, author) { + author.name(callback); + }); + }, + function(callback) { + commit.author(function(error, author) { + author.email(callback); + }); + }, + function(callback) { + commit.message(callback); + } + ], function printCommit(error, results) { + if (error) throw error; + console.log('SHA ' + results[0]); + console.log(results[1] * 1000); + console.log(results[2] + ' <' + results[3] + '>'); + console.log(results[4]); + }); }); }); - - //repo.branch( 'master', function( err, branch ) { - // if( err ) { throw new Error( err ); } - - // var history = branch.history(); - // console.log( history ); - - // //branch.tree().each( function( i, entry ) { - // // console.log( entry.name ); - // // console.log( entry.contents ); - - // //}); - //}); }); diff --git a/example/convenience-tree.js b/example/convenience-tree.js index dbec82cc3..efe4cb928 100644 --- a/example/convenience-tree.js +++ b/example/convenience-tree.js @@ -1,24 +1,23 @@ -var git = require( '../' ); +// Load in the module. +var git = require('nodegit'); -git.repo( '../.git', function( err, repo ) { - if( err ) { throw err; } +// Open the repository in the current directory. +git.repo('.git', function(error, repository) { + if (error) throw error; - repo.branch( 'master', function( err, branch ) { - if( err ) { throw err; } + // Use the master branch. + repository.branch('master', function(error, branch) { + if (error) throw error; - branch.tree().walk().on('entry', function( idx, entry ) { - //console.log(entry.entry); - console.log( entry.name, entry.attributes ); - //console.log( entry.content ); + // Iterate over the revision history. + branch.tree(function(error, tree) { + console.log(tree); + if (error) throw error; + tree.walk().on('entry', function(error, entry) { + entry.name(function(error, name) { + console.log(name); + }); + }); }); - - //branch.tree().entry('example/raw-blob.js', function( entry ) { - // if( entry ) { - // console.log(entry.name); - // } - // else { - // console.log('not found'); - // } - //}); }); }); diff --git a/example/stress/commit.js b/example/stress/commit.js deleted file mode 100644 index d1dd0b2a6..000000000 --- a/example/stress/commit.js +++ /dev/null @@ -1,44 +0,0 @@ -var git = require( '../../' ).raw; - -//* Stress test basic commit - setInterval(function() { - for(var i=0; i<10000; i++) { - (function() { - - var start = new Date; - - var repo = new git.Repo(); - repo.open( 'jquery/.git', function() { - var commit = new git.Commit(); - - //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); - }); - - })(); - } - }, 0); -//*/ - -//* Stress test repo open - setInterval(function() { - for(var i=0; i<10000; i++) { - - (function() { - var start = new Date; - - var repo = new git.Repo(); - repo.open( 'jquery/.git', function() { - var oid = new git.Oid(); - oid.mkstr( 'cf702496ee28830f3488ed3f1c3940cfbb2dfa8f' ); - - var commit = new git.Commit(); - commit.lookup( repo, oid, function( err ) { - //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); - }); - }); - - })(); - - } - }, 0); -//*/ diff --git a/example/stress/jquery b/example/stress/jquery deleted file mode 160000 index cf702496e..000000000 --- a/example/stress/jquery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cf702496ee28830f3488ed3f1c3940cfbb2dfa8f diff --git a/example/stress/repo.js b/example/stress/repo.js deleted file mode 100644 index d32ed765a..000000000 --- a/example/stress/repo.js +++ /dev/null @@ -1,50 +0,0 @@ -var git = require( 'nodegit' ).raw; - -//* Stress test basic repo - setInterval(function() { - for(var i=0; i<10000; i++) { - - (function() { - var start = new Date; - var repo = new git.Repo(); - - //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); - })(); - - } - }, 0); -//*/ - - -//* Stress test repo open - setInterval(function() { - for(var i=0; i<10000; i++) { - - (function() { - var start = new Date; - var repo = new git.Repo(); - repo.open( 'jquery/.git', function() { - - //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); - }); - })(); - - } - }, 0); -//*/ - -//* Init stress test - setInterval(function() { - for(var i=0; i<10000; i++) { - - (function() { - var start = new Date; - var repo = new git.Repo(); - repo.init( './test/'+ i +'.git', true, function() { - //console.log( 'Time taken: ' + (+new Date-start) + 'ms' ); - }); - })(); - - } - }, 0); -//*/ diff --git a/example/stress/revwalk.js b/example/stress/revwalk.js deleted file mode 100644 index c4e733fe0..000000000 --- a/example/stress/revwalk.js +++ /dev/null @@ -1,58 +0,0 @@ -//var git = require( '../../' ).raw; -var git = require( 'nodegit' ); - - -//* Stress test revision walking - //setInterval(function() { -// for(var i=0; i<10000; i++) { - - (function() { - - git.repo( 'jquery/.git', function() { - //console.log( 'Repo opened' ); - - this.branch( 'master', function() { - this.history().on( 'commit', function( i, commit ) { - console.log( commit.id.toString(40) ); - }); - }); - }); - - //var repo = new git.Repo(); - //repo.open( 'jquery/.git', function( err ) { - - // var commit = new git.Commit( repo ) - // , ref = new git.Ref( repo ); - - // ref.lookup( repo, '/refs/heads/master', function( err ) { - // if( err ) { throw new Error( err ); } - - // var oid = new git.Oid(); - // ref.oid( oid ); - - - //commit.lookup( repo, oid, function( err ) { - - // var revwalk = new git.RevWalk( repo ); - // revwalk.push( commit ); - - // function walk() { - // var _oid = new git.Oid(); - // revwalk.next( _oid, function( err ) { - // if( !err ) { - // walk(); - // } - // }); - // } - - // walk(); - - //}); - // }); - //}); - - })(); - -// } - //}, 0); -//*/ diff --git a/include/blob.h b/include/blob.h index 0426e8c88..f857d23e1 100755 --- a/include/blob.h +++ b/include/blob.h @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -8,211 +10,102 @@ #include #include +#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "repo.h" +#include "oid.h" +using namespace v8; using namespace node; /** - * Class: GitBlob * Wrapper for libgit2 git_blob. */ class GitBlob : public ObjectWrap { public: - /** - * Variable: constructor_template - * Used to create Node.js constructor. - */ - static v8::Persistent constructor_template; - /** - * Function: Initialize - * Used to intialize the EventEmitter from Node.js - * - * Parameters: - * target - v8::Object the Node.js global module object - */ - static void Initialize(v8::Handle target); - /** - * Accessor for GitBlob - * - * @return the internal git_blob reference - */ + + static Persistent constructor_template; + + static void Initialize(Handle target); + git_blob* GetValue(); - /** - * Mutator for Object - * - * @param obj a git_object object - */ void SetValue(git_blob* blob); - /** - * Function: Lookup - * Lookup a blob object from a repository. - * - * Parameters: - * repo the repo to use when locating the blob. - * id identity of the blob to locate. - * - * Returns: - * 0 on success; error code otherwise - */ - int Lookup(git_repository* repo, const git_oid *id); - /** - * Function: RawContent - * Get a read-only buffer with the raw content of a blob. - * - * Returns: - * raw content buffer; NULL if the blob has no contents - */ - const void* RawContent(); - /** - * Function: RawSize - * Lookup a blob object from a repository. - * - * Returns: - * size in bytes - */ - int RawSize(); - /** - * - * Function: Close - * Free a blob object. - */ - void Close(); - /** - * - * Function: CreateFromFile - * Read a file into the ODB. - * - * Returns: - * 0 on success, error code otherwise - */ - int CreateFromFile(git_oid* oid, git_repository* repo, const char* path); - /** - * - * Function: CreateFromBuffer - * Read a buffer into the ODB. - * - * Returns: - * 0 on success, error code otherwise - */ - int CreateFromBuffer(git_oid* oid, git_repository* repo, const void* buffer, size_t len); protected: - /** - * Constructor: GitBlob - */ GitBlob() {}; - /** - * Deconstructor: GitBlob - */ ~GitBlob() {}; - /** - * Function: New - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle New(const v8::Arguments& args); - /** - * Function: Lookup - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle Lookup(const v8::Arguments& args); - /** - * Function: EIO_Lookup - * - * Parameters: - * req - an uv_work_t pointer - * - */ - static void EIO_Lookup(uv_work_t* req); - /** - * Function: EIO_AfterLookup - * - * Parameters: - * req - an uv_work_t pointer - */ - static void EIO_AfterLookup(uv_work_t* req); - /** - * Function: RawContent - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle RawContent(const v8::Arguments& args); - /** - * Function: RawSize - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle RawSize(const v8::Arguments& args); - /** - * Function: Close - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle Close(const v8::Arguments& args); - /** - * Function: CreateFromFile - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle CreateFromFile(const v8::Arguments& args); - /** - * Function: CreateFromBuffer - * - * Parameters: - * args v8::Arguments function call - * - * Returns: - * v8::Object args.This() - */ - static v8::Handle CreateFromBuffer(const v8::Arguments& args); + + static Handle New(const Arguments& args); + static Handle Free(const Arguments& args); + + static Handle Lookup(const Arguments& args); + static void LookupWork(uv_work_t* req); + static void LookupAfterWork(uv_work_t* req); + + static Handle RawContent(const Arguments& args); + static void RawContentWork(uv_work_t* req); + static void RawContentAfterWork(uv_work_t* req); + + static Handle CreateFromFile(const Arguments& args); + static void CreateFromFileWork(uv_work_t* req); + static void CreateFromFileAfterWork(uv_work_t* req); + + static Handle CreateFromBuffer(const Arguments& args); + static void CreateFromBufferWork(uv_work_t* req); + static void CreateFromBufferAfterWork(uv_work_t* req); private: - /** - * Variable: blob - * Internal reference to git_blob object - */ + git_blob* blob; - /** - * Struct: lookup_request - * Contains references to the current blob, repo, and oid for a - * commit lookup, also contains references to an error code post - * lookup, and a callback function to execute. - */ - struct lookup_request { + struct LookupBaton { + uv_work_t request; + const git_error* error; + GitBlob* blob; - GitRepo* repo; - GitOid* oid; - int err; - v8::Persistent callback; + git_blob* rawBlob; + git_repository* rawRepo; + git_oid rawOid; + + Persistent callback; + }; + + struct RawContentBaton { + uv_work_t request; + + GitBlob* blob; + git_blob* rawBlob; + std::string rawContent; + int rawSize; + + Persistent callback; + }; + + struct CreateFromFileBaton { + uv_work_t request; + const git_error* error; + + GitBlob* blob; + git_blob* rawBlob; + git_repository* rawRepo; + std::string path; + + Persistent callback; + }; + + struct CreateFromBufferBaton { + uv_work_t request; + const git_error* error; + + GitBlob* blob; + git_blob* rawBlob; + git_repository* rawRepo; + const void* data; + size_t dataLength; + + Persistent callback; }; }; diff --git a/include/commit.h b/include/commit.h index 84e20a829..961d2ed82 100755 --- a/include/commit.h +++ b/include/commit.h @@ -9,7 +9,7 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "reference.h" #include "repo.h" @@ -38,35 +38,48 @@ class GitCommit : public ObjectWrap { git_commit* GetValue(); void SetValue(git_commit* commit); - void Close(); - - int Tree(git_tree** tree); + void SetOid(git_oid* oid); protected: GitCommit() {} ~GitCommit() {} static Handle New(const Arguments& args); - - static Handle FetchDetailsSync(const Arguments& args); - static Handle FetchDetails(const Arguments& args); - static void FetchDetailsWork(uv_work_t *req); - static void FetchDetailsAfterWork(uv_work_t *req); + static Handle Free(const Arguments& args); static Handle Lookup(const Arguments& args); static void LookupWork(uv_work_t *req); static void LookupAfterWork(uv_work_t *req); - static Handle Close(const Arguments& args); + static Handle Oid(const Arguments& args); + + static Handle Message(const Arguments& args); + static void MessageWork(uv_work_t* req); + static void MessageAfterWork(uv_work_t* req); + + static Handle Time(const Arguments& args); + static void TimeWork(uv_work_t* req); + static void TimeAfterWork(uv_work_t* req); + + static Handle Offset(const Arguments& args); + static void OffsetWork(uv_work_t* req); + static void OffsetAfterWork(uv_work_t* req); + + static Handle Author(const Arguments& args); + static void AuthorWork(uv_work_t* req); + static void AuthorAfterWork(uv_work_t* req); + + static Handle Committer(const Arguments& args); + static void CommitterWork(uv_work_t* req); + static void CommitterAfterWork(uv_work_t* req); static Handle Tree(const Arguments& args); static void TreeWork(uv_work_t* req); static void TreeAfterWork(uv_work_t* req); - static Handle ParentSync(const Arguments& args); - static Handle Parent(const Arguments& args); - static void ParentWork(uv_work_t* req); - static void ParentAfterWork(uv_work_t* req); + static Handle Parents(const Arguments& args); + static void ParentsWork(uv_work_t* req); + static void ParentsAfterWork(uv_work_t* req); private: git_commit* commit; @@ -84,41 +97,66 @@ class GitCommit : public ObjectWrap { Persistent callback; }; - /** - * Struct containing details for a commit. - */ - struct FetchDetailsBaton { + struct MessageBaton { + uv_work_t request; + + git_commit* rawCommit; + std::string message; + + Persistent callback; + }; + + struct TimeBaton { + uv_work_t request; + + git_commit* rawCommit; + git_time_t time; + + Persistent callback; + }; + + struct SignatureBaton { + uv_work_t request; + + git_commit* rawCommit; + const git_signature* rawSignature; + + Persistent callback; + }; + + struct OffsetBaton { + uv_work_t request; + + git_commit* rawCommit; + int offset; + + Persistent callback; + }; + + + struct TreeBaton { uv_work_t request; const git_error* error; git_commit* rawCommit; - const git_oid *oid; - char sha[GIT_OID_HEXSZ + 1]; - const char* message; - time_t time; - int timeOffset; - const git_signature* committer; - const git_signature* author; - unsigned int parentCount; - std::vector parentShas; + git_tree* rawTree; Persistent callback; }; - /** - * Contains references to the current commit, parent commit (output) - * parent commit's index, also contains references to an error code post - * lookup, and a callback function to execute. - */ - struct ParentBaton { + struct Parent { + const git_oid* rawOid; + git_commit* rawCommit; + }; + + struct ParentsBaton { uv_work_t request; const git_error* error; int index; - GitCommit* commit; - git_commit* rawParentCommit; + git_commit* rawCommit; + std::vector parents; Persistent callback; }; - }; diff --git a/include/diff_list.h b/include/diff_list.h index 429c96145..58913866e 100644 --- a/include/diff_list.h +++ b/include/diff_list.h @@ -11,9 +11,10 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" -#include "../include/repo.h" +#include "repo.h" +#include "oid.h" using namespace node; using namespace v8; @@ -24,30 +25,21 @@ using namespace v8; class GitDiffList : public ObjectWrap { public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ static Persistent constructor_template; - static const int WALK_DELTA_THRESHHOLD = 10; + static const int WALK_DELTA_SEND_THRESHHOLD = 10; - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ static void Initialize (Handle target); git_diff_list* GetValue(); void SetValue(git_diff_list* diffList); - void Close(); protected: GitDiffList() {} ~GitDiffList() {} static Handle New(const Arguments& args); - static Handle Close(const Arguments& args); + static Handle Free(const Arguments& args); static Handle TreeToTree(const Arguments& args); static void TreeToTreeWork(uv_work_t *req); @@ -81,8 +73,6 @@ class GitDiffList : public ObjectWrap { private: git_diff_list* diffList; - // git_oid* oldOid; - // git_oid* newOid; struct TreeToTreeBaton { uv_work_t request; diff --git a/include/error.h b/include/error.h index 87c3e593a..101a51122 100755 --- a/include/error.h +++ b/include/error.h @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -8,54 +10,29 @@ #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace v8; using namespace node; /** - * Class: GitError * Wrapper for libgit2 git_error. */ class GitError : public ObjectWrap { public: - /** - * Variable: constructor_template - * Used to create Node.js constructor. - */ + static Persistent constructor_template; - /** - * Function: Initialize - * Used to intialize the EventEmitter from Node.js - * - * Parameters: - * target - Object the Node.js global module object - */ + static void Initialize(Handle target); static Local WrapError(const git_error* error); protected: - const git_error* error; - - /** - * Constructor: GitError - */ GitError() {}; - /** - * Deconstructor: GitError - */ ~GitError() {}; - /** - * Function: New - * - * Parameters: - * args Arguments function call - * - * Returns: - * Object args.This() - */ + const git_error* error; + static Handle New(const Arguments& args); }; diff --git a/include/functions/utilities.h b/include/functions/utilities.h new file mode 100644 index 000000000..ad9ec84c2 --- /dev/null +++ b/include/functions/utilities.h @@ -0,0 +1,15 @@ +#ifndef UTILITY_FUNCTIONS +#define UTILITY_FUNCTIONS + +#include +#include + +#include "git2.h" + +#include "../../include/error.h" + +using namespace v8; + +bool success(const git_error* error, Persistent callback); + +#endif diff --git a/include/index.h b/include/index.h index 03d479da9..cf58288e2 100755 --- a/include/index.h +++ b/include/index.h @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -8,7 +10,7 @@ #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace node; @@ -52,5 +54,5 @@ class GitIndex : public ObjectWrap { */ static v8::Handle New(const v8::Arguments& args); }; - + #endif diff --git a/include/object.h b/include/object.h deleted file mode 100755 index d84de5c05..000000000 --- a/include/object.h +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#ifndef OBJ_H -#define OBJ_H - -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -#include "repo.h" -#include "oid.h" - -using namespace node; - -/** - * Class wrapper for libgit2 git_object - */ -class GitObject : public ObjectWrap { - public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ - static Persistent constructor_template; - - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ - static void Initialize(Handle target); - - /** - * Accessor for Object - * - * @return the internal git_object reference - */ - git_object* GetValue(); - - /** - * Mutator for Object - * - * @param obj a git_object object - */ - void SetValue(git_object* obj); - - const git_oid* Id(); - git_otype Type(); - - git_repository* Owner(); - const char* Type2String(git_otype type); - git_otype String2Type(const char* type); - int TypeIsLoose(git_otype type); - size_t Size(git_otype type); - - protected: - /** - * Constructor - */ - GitObject() {}; - - /** - * Deconstructor - */ - ~GitObject() {}; - - /** - * Mutator for GitObject - * - * @param args v8::Arguments function call arguments from Node.js - * - * @return v8::Object args.This() - */ - static Handle New(const Arguments& args); - static Handle Id(const Arguments& args); - static Handle Type(const Arguments& args); - static Handle Owner(const Arguments& args); - static Handle Type2String(const Arguments& args); - static Handle String2Type(const Arguments& args); - static Handle TypeIsLoose(const Arguments& args); - static Handle Size(const Arguments& args); - - private: - /** - * Internal reference to git_object object - */ - git_object* obj; -}; - -#endif diff --git a/include/odb.h b/include/odb.h index a0d9692cc..19c07e900 100755 --- a/include/odb.h +++ b/include/odb.h @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -8,7 +10,7 @@ #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace node; @@ -52,5 +54,5 @@ class GitOdb : public ObjectWrap { */ static v8::Handle New(const v8::Arguments& args); }; - + #endif diff --git a/include/oid.h b/include/oid.h index cfab6db64..4f61e8665 100755 --- a/include/oid.h +++ b/include/oid.h @@ -1,14 +1,18 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #ifndef OID_H #define OID_H #include #include +#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace node; using namespace v8; @@ -19,35 +23,34 @@ class GitOid : public ObjectWrap { static Persistent constructor_template; static void Initialize (Handle target); - Handle WrapObj(Local obj); git_oid GetValue(); void SetValue(git_oid oid); - int Mkstr(const char* str); - void Mkraw(const unsigned char* raw); - void Fmt(char* buffer); - void PathFmt(char *str); - char* AllocFmt(); - char* ToString(char* buffer, size_t bufferSize); - void Cpy(git_oid* out); - int Cmp(const git_oid* a, const git_oid* b); - + protected: GitOid() {} ~GitOid() {} - protected: static Handle New(const Arguments& args); - static Handle Mkstr(const Arguments& args); - static Handle Mkraw(const Arguments& args); - static Handle Fmt(const Arguments& args); - static Handle PathFmt(const Arguments& args); - static Handle AllocFmt(const Arguments& args); - static Handle ToString(const Arguments& args); - static Handle Cpy(const Arguments& args); - static Handle Cmp(const Arguments& args); + + static Handle Sha(const Arguments& args); + + static Handle FromString(const Arguments& args); + static void FromStringWork(uv_work_t* req); + static void FromStringAfterWork(uv_work_t* req); private: git_oid oid; + + struct FromStringBaton { + uv_work_t request; + const git_error* error; + + std::string fromString; + GitOid* oid; + git_oid rawOid; + + Persistent callback; + }; }; #endif diff --git a/include/reference.h b/include/reference.h index 46226573a..4f28bcca4 100644 --- a/include/reference.h +++ b/include/reference.h @@ -12,7 +12,7 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "repo.h" #include "oid.h" diff --git a/include/repo.h b/include/repo.h index e5493b11d..55eb04d38 100755 --- a/include/repo.h +++ b/include/repo.h @@ -1,6 +1,9 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #ifndef REPO_H #define REPO_H @@ -9,9 +12,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include "../vendor/libgit2/include/git2.h" - -#include "object.h" +#include "git2.h" using namespace node; using namespace v8; @@ -24,7 +25,6 @@ class GitRepo : public ObjectWrap { git_repository* GetValue(); void SetValue(git_repository* repo); - // Synchronous void Free(); protected: diff --git a/include/revwalk.h b/include/revwalk.h index 09a0c0588..46ef8db07 100755 --- a/include/revwalk.h +++ b/include/revwalk.h @@ -1,13 +1,16 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #ifndef REVWALK_H #define REVWALK_H #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "repo.h" @@ -16,65 +19,70 @@ using namespace v8; class GitRevWalk : public ObjectWrap { public: - static Persistent constructor_template; + static Persistent constructor_template; static void Initialize(Handle target); git_revwalk* GetValue(); void SetValue(git_revwalk* revwalk); - int New(git_repository* repo); - void Reset(); - int Push(git_oid* oid); - int Hide(); - int Sorting(int sort); - void Free(); - git_repository* Repository(); + git_repository* GetRepo(); + void SetRepo(git_repository* repository); protected: GitRevWalk() {} ~GitRevWalk() {} + static Handle New(const Arguments& args); + static Handle Free(const Arguments& args); + static Handle Reset(const Arguments& args); - /** - * Although git_revwalk_next is not blocking when iterating with a - * time-sorting mode, options may be added later to allow different sort - * modes, hence the async implementation. - */ + static Handle Allocate(const Arguments& args); + static void AllocateWork(uv_work_t *req); + static void AllocateAfterWork(uv_work_t *req); + static Handle Push(const Arguments& args); static void PushWork(uv_work_t *req); static void PushAfterWork(uv_work_t *req); - static Handle Hide(const Arguments& args); - static Handle Next(const Arguments& args); static void NextWork(uv_work_t* req); static void NextAfterWork(uv_work_t* req); static Handle Sorting(const Arguments& args); - static Handle Free(const Arguments& args); - static Handle Repository(const Arguments& args); private: git_revwalk* revwalk; git_repository* repo; + struct AllocateBaton { + uv_work_t request; + const git_error* error; + + GitRevWalk* revwalk; + git_revwalk *rawRevwalk; + git_repository* rawRepo; + + Persistent callback; + }; + struct PushBaton { uv_work_t request; const git_error* error; - git_revwalk *revwalk; - git_oid oid; + git_revwalk *rawRevwalk; + git_oid rawOid; Persistent callback; }; struct NextBaton { uv_work_t request; + const git_error* error; bool walkOver; - git_revwalk *revwalk; - git_oid oid; + git_revwalk *rawRevwalk; + git_oid rawOid; Persistent callback; }; diff --git a/include/sig.h b/include/sig.h deleted file mode 100755 index c01778e20..000000000 --- a/include/sig.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#ifndef GitSig_H -#define GitSig_H - -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -#include "repo.h" - -using namespace v8; -using namespace node; - -class GitSig : public ObjectWrap { - public: - static Persistent constructor_template; - static void Initialize(Handle target); - - void New(const char *name, const char *email, time_t time, int offset); - git_signature* GetValue(); - void SetValue(git_signature* GitSig); - git_signature* Dup(); - void Free(); - - char* Name(); - char* Email(); - - protected: - GitSig() {}; - ~GitSig() {}; - - static Handle New(const Arguments& args); - static Handle Dup(const Arguments& args); - static Handle Free(const Arguments& args); - - static Handle Name(const Arguments& args); - static Handle Email(const Arguments& args); - - private: - git_signature* sig; - - char* name; - char* email; -}; - -#endif diff --git a/include/signature.h b/include/signature.h new file mode 100755 index 000000000..bc83ad529 --- /dev/null +++ b/include/signature.h @@ -0,0 +1,45 @@ +/* + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ + +#ifndef GitSignature_H +#define GitSignature_H + +#include +#include + +#include "git2.h" + +#include "repo.h" + +using namespace v8; +using namespace node; + +class GitSignature : public ObjectWrap { + public: + static Persistent constructor_template; + + static void Initialize(Handle target); + + git_signature* GetValue(); + void SetValue(git_signature* signature); + + protected: + GitSignature() {}; + ~GitSignature() {}; + + static Handle New(const Arguments& args); + static Handle Duplicate(const Arguments& args); + static Handle Free(const Arguments& args); + + static Handle Name(const Arguments& args); + static Handle Email(const Arguments& args); + + private: + git_signature* signature; +}; + +#endif diff --git a/include/tag.h b/include/tag.h index db8560d44..f75b28f90 100755 --- a/include/tag.h +++ b/include/tag.h @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -8,7 +10,7 @@ #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace node; @@ -52,5 +54,5 @@ class GitTag : public ObjectWrap { */ static v8::Handle New(const v8::Arguments& args); }; - + #endif diff --git a/include/threads.h b/include/threads.h index 469911d8c..bf9f8c392 100755 --- a/include/threads.h +++ b/include/threads.h @@ -8,7 +8,7 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" using namespace node; using namespace v8; diff --git a/include/tree.h b/include/tree.h index 3309c88d6..4cc84c457 100755 --- a/include/tree.h +++ b/include/tree.h @@ -1,6 +1,9 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #ifndef GITTREE_H #define GITTREE_H @@ -8,8 +11,9 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include #include +#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "repo.h" #include "tree_entry.h" @@ -22,110 +26,71 @@ using namespace node; */ class GitTree : public ObjectWrap { public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ + static Persistent constructor_template; - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ + static const int WALK_ENTRY_SEND_THRESHOLD = 10; + static void Initialize(Handle target); - /** - * Accessor for GitTree - * - * @return the internal git_tree reference - */ + git_tree* GetValue(); - /** - * Mutator for GitTree - * - * @param obj a git_tree object - */ void SetValue(git_tree* tree); - /** - * Lookup a tree object from a repository. - * - * @param repo the repo to use when locating the tree. - * @param id identity of the tree to locate. - * - * @return 0 on success; error code otherwise - */ - int Lookup(git_repository* repo, const git_oid* id); - /** - * Get number of entries in the looked up tree. - * - * @return number of entries - */ - size_t EntryCount(); - /** - * Get entry by index in the looked up tree. - * - * @param idx index of the entry - * - * @return git tree entry - */ - git_tree_entry* EntryByIndex(int idx); - - int SortEntries(); - void ClearEntries(); protected: - /** - * Constructor - */ GitTree() {}; - /** - * Deconstructor - */ ~GitTree() {}; - /** - * Creates a new instance of GitTree to Node.js - * - * @param args v8::Arguments function call arguments from Node.js - * - * @return v8::Object args.This() - */ + static Handle New(const Arguments& args); static Handle Lookup(const Arguments& args); - static void EIO_Lookup(uv_work_t *req); - static void EIO_AfterLookup(uv_work_t *req); - static Handle EntryCount(const Arguments& args); - static Handle EntryByIndex(const Arguments& args); - static void EIO_EntryByIndex(uv_work_t *req); - static void EIO_AfterEntryByIndex(uv_work_t *req); + static void LookupWork(uv_work_t* req); + static void LookupAfterWork(uv_work_t* req); + + static Handle Walk(const Arguments& args); + static void WalkWork(void* payload); + static int WalkWorkEntry(const char *root, const git_tree_entry *entry, void *payload); + static void WalkWorkSendEntry(uv_async_t *handle, int status /*UNUSED*/); + static void WalkWorkSendEnd(uv_async_t *handle, int status /*UNUSED*/); + static Handle EntryByPath(const Arguments& args); static void EntryByPathWork(uv_work_t *req); static void EntryByPathAfterWork(uv_work_t *req); - static Handle SortEntries(const Arguments& args); - static Handle ClearEntries(const Arguments& args); private: - /** - * Internal reference to git_tree object - */ + git_tree* tree; - /** - * Structure to handle async lookups - */ - struct lookup_request { - GitTree* tree; - GitRepo* repo; - GitOid* oid; - int err; + + struct LookupBaton { + uv_work_t request; + const git_error* error; + + git_oid rawOid; + git_repository* rawRepo; + git_tree* rawTree; + Persistent callback; }; - /** - * Structure to handle async entryByIndex - */ - struct entryindex_request { - GitTree* tree; - GitTreeEntry* entry; - int idx; - Persistent callback; + + struct WalkEntry { + git_tree_entry* rawEntry; + std::string root; + }; + + struct WalkBaton { + uv_thread_t threadId; + uv_mutex_t mutex; + uv_async_t asyncEntry; + uv_async_t asyncEnd; + + const git_error* error; + + std::vector rawTreeEntries; + + git_tree* rawTree; + bool blobsOnly; + + Persistent entryCallback; + Persistent endCallback; }; struct EntryByPathBaton { diff --git a/include/tree_entry.h b/include/tree_entry.h index c028444bd..eb97ad6a6 100755 --- a/include/tree_entry.h +++ b/include/tree_entry.h @@ -1,19 +1,22 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #ifndef GITTREEENTRY_H #define GITTREEENTRY_H #include #include +#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "repo.h" #include "tree.h" #include "oid.h" -#include "object.h" using namespace v8; using namespace node; @@ -23,45 +26,77 @@ using namespace node; */ class GitTreeEntry : ObjectWrap { public: - /** - * v8::FunctionTemplate used to create Node.js constructor - */ + static Persistent constructor_template; - /** - * Used to intialize the EventEmitter from Node.js - * - * @param target v8::Object the Node.js module object - */ static void Initialize(Handle target); - - /** - * Accessor for GitTreeEntry - * - * @return the internal git_tree_entry reference - */ git_tree_entry* GetValue(); - - /** - * Mutator for GitTreeEntry - * - * @param obj a git_tree_entry object - */ void SetValue(git_tree_entry* tree); - const char* Name(); - int Attributes(); - const git_oid* Id(); - int ToObject(git_repository* repo, git_object** obj); + void SetRoot(std::string root); + std::string GetRoot(); protected: static Handle New(const Arguments& args); + + static Handle Root(const Arguments& args); + static Handle Name(const Arguments& args); - static Handle Attributes(const Arguments& args); - static Handle Id(const Arguments& args); - static Handle ToObject(const Arguments& args); + static void NameWork(uv_work_t* req); + static void NameAfterWork(uv_work_t* req); + + static Handle FileMode(const Arguments& args); + static void FileModeWork(uv_work_t* req); + static void FileModeAfterWork(uv_work_t* req); + + static Handle Oid(const Arguments& args); + static void OidWork(uv_work_t* req); + static void OidAfterWork(uv_work_t* req); + + static Handle ToBlob(const Arguments& args); + static void ToBlobWork(uv_work_t *req); + static void ToBlobAfterWork(uv_work_t *req); private: git_tree_entry* entry; + std::string root; + + struct NameBaton { + uv_work_t request; + + git_tree_entry* rawEntry; + const char* name; + + Persistent callback; + }; + + struct FileModeBaton { + uv_work_t request; + + git_tree_entry* rawEntry; + git_filemode_t fileMode; + + Persistent callback; + }; + + struct OidBaton { + uv_work_t request; + + git_tree_entry* rawEntry; + const git_oid* rawOid; + + Persistent callback; + }; + + struct ToBlobBaton { + uv_work_t request; + const git_error* error; + + git_repository* rawRepo; + git_tree_entry* rawEntry; + git_blob* rawBlob; + + Persistent callback; + }; }; #endif diff --git a/install.js b/install.js index 893daaf90..5434079fb 100644 --- a/install.js +++ b/install.js @@ -97,13 +97,13 @@ async.series([ }); }, function deleteExistingLibgit2BuildFolder(callback) { - fs.exists(libgit2BuildDirectory, function(exists) { - if (exists) { - fs.remove(libgit2BuildDirectory, callback); - } else { + // fs.exists(libgit2BuildDirectory, function(exists) { + // if (exists) { + // fs.remove(libgit2BuildDirectory, callback); + // } else { callback(); - } - }); + // } + // }); }, function createLibgit2BuildDirectory(callback) { console.log('[nodegit] Building libgit2 dependency.'); diff --git a/lib/blob.js b/lib/blob.js index 2808a5e89..c3d368008 100644 --- a/lib/blob.js +++ b/lib/blob.js @@ -1,33 +1,124 @@ -var git = require( '../' ); +var git = require('../'), + success = require('./utilities').success; -var _Blob = function( obj ) { - var self = {}; - - if( obj instanceof git.raw.Repo ) { - self.repo = obj; - self.blob = new git.raw.Blob( obj ); +/** + * Blob convenience class constructor. + * + * @constructor + * @param {git.raw.Repo} rawRepo Raw repository object. + * @param {git.raw.Blob} [rawBlob = new git.raw.Blob(rawRepo)] Raw blob object. + */ +var Blob = function(rawRepo, rawBlob) { + if(!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for Blob must be a raw repo'); } - else if( obj instanceof git.raw.Blob ) { - self.blob = obj; + this.rawRepo = rawRepo; + + if(typeof rawBlob !== 'undefined' && + rawBlob instanceof git.raw.Blob) { + this.rawBlob = rawBlob; + } else { + this.rawBlob = new git.raw.Blob(this.rawRepo); } +}; + +/** + * Retrieve the blob represented by the oid. + * + * @param {git.raw.Oid} oid The OID representing the blob to lookup. + * @param {Blob~lookupCallback} callback + */ +Blob.prototype.lookup = function(oid, callback) { + /** + * @callback Blob~lookupCallback Callback executed on lookup completion. + * @param {GitError|null} error An Error or null if successful. + * @param {Blob|null} blob Retrieved blob object or null. + */ + var self = this; + self.rawBlob.lookup(self.rawRepo, oid, function blobLookup(error, rawBlob) { + if (success(error, callback)) { + self.rawBlob = rawBlob; + callback(null, self); + } + }); +}; - Object.defineProperty( self, 'raw', { - get: function() { - return self.blob.rawContent().toString(); - }, - enumerable: true +/** + * Retrieve the blob's raw content buffer. + * + * @param {Blob~rawContentCallback} callback + */ +Blob.prototype.rawContent = function(callback) { + /** + * @callback Blob~rawContentCallback Callback executed after raw content is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {Buffer|null} content The raw content of the blob or null. + */ + this.rawBlob.rawContent(function(error, content) { + if (success(error, callback)) { + callback(null, content); + } }); +}; - self.lookup = function( oid ) { - self.blob.lookup( self.repo, oid, function() { - var args = Array.prototype.slice.call( arguments ); - args[0] = git.util().error( args[0] ); +/** + * Retrieve the blob's content. + * + * @param {Blob~contentCallback} callback + */ +Blob.prototype.content = function(callback) { + /** + * @callback Blob~contentCallback Callback executed after content is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} content The content of the blob or null. + */ + this.rawContent(function(error, content) { + if (success(error, callback)) { + callback(null, content.toString()); + } + }); +}; - callback.apply( self, args.concat( self ) ); - }); - }; +/** + * Create a new blob from the file at the given path. + * + * @param {String} path Full path to the file. + * @param {Blob~createFromFileCallback} callback + */ +Blob.prototype.createFromFile = function(path, callback) { + /** + * @callback Blob~createFromFileCallback Callback executed after blob is created. + * @param {GitError|null} error An Error or null if successful. + * @param {Blob|null} blob The new blob or null. + */ + var self = this; + self.rawBlob.createFromFile(path, self.rawRepo, function blobCreateFromFileCallback(error, rawBlob) { + if (success(error, callback)) { + self.rawBlob = rawBlob; + callback(null, self); + } + }); +}; - return self; +/** + * Create a new blob from the given buffer. + * + * @param {Buffer} buffer Buffer used to create blob. + * @param {Blob~createFromBufferCallback} callback + */ +Blob.prototype.createFromFile = function(path, callback) { + /** + * @callback Blob~createFromBufferCallback Callback executed after blob is created. + * @param {GitError|null} error An Error or null if successful. + * @param {Blob|null} content The new blob or null. + */ + var self = this; + self.rawBlob.createFromBuffer(buffer, self.rawRepo, function blobCreateFromBufferCallback(error, rawBlob) { + if (success(error, callback)) { + self.rawBlob = rawBlob; + callback(null, self); + } + }); }; -exports.blob = _Blob; +exports.blob = Blob; diff --git a/lib/commit.js b/lib/commit.js index 3c726faae..75f2fc18e 100644 --- a/lib/commit.js +++ b/lib/commit.js @@ -1,195 +1,357 @@ var git = require( '../' ), - events = require( 'events' ); + success = require('./utilities').success, + events = require('events'); /** - * Apply given details to the context. + * Convenience commit constructor. * - * @param {Object} details - * @param {Object} context - * @return {Object} The modified context. + * @constructor + * @param {git.raw.Repo} rawRepo Raw repository object. + * @param {git.raw.Commit} [rawCommit = new git.raw.Commit(rawRepo)] Raw commit object. */ -function applyDetails(details, context) { - if (details) { - for (var detailKey in details) { - if (detailKey === 'id') { - context[detailKey] = git.oid(details[detailKey]); - continue; - } - context[detailKey] = details[detailKey]; - } +var Commit = function(rawRepo, rawCommit) { + if (!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for Commit must be a raw repo'); } - return context; -} -/** - * Convenience commit constructor. - * - * @param {RawCommit|Null} rawCommit - * @return {Commit} - */ -var Commit = function(rawCommit) { - var self = {}; + this.rawRepo = rawRepo; - if(rawCommit && rawCommit instanceof git.raw.Commit) { - self.commit = rawCommit; + if (rawCommit instanceof git.raw.Commit) { + this.rawCommit = rawCommit; } else { - self.commit = new git.raw.Commit(); + this.rawCommit = new git.raw.Commit(); } +}; +/** + * Look up the commit referenced by oid, replace this.commit with the result. + * + * @param {Oid|git.raw.Oid|String} oid A representation of an OID used to lookup the commit. + * @param {Commit~lookupCallback} callback + */ +Commit.prototype.lookup = function(oid, callback) { /** - * Fetch the commit's details asynchronously. - * - * @param {Function} callback + * @callback Commit~lookupCallback Callback executed on lookup completion. + * @param {GitError|null} error An Error or null if successful. + * @param {Commit|null} commit Retrieved commit object or null. */ - self.fetchDetails = function(callback) { - var error = null; - self.commit.fetchDetails(function(error, details) { - if (error) { - error = git.error(error); - } - applyDetails(details, self); - callback(error); - }); - }; + if (typeof oid !== 'undefined' && + typeof oid !== 'string' && + !(oid instanceof git.raw.Oid)) { + oid = oid.getRawOid(); + } + var self = this; + self.rawCommit.lookup(self.rawRepo, oid, function commitLookup(error, rawCommit) { + if (success(error, callback)) { + self.rawCommit = rawCommit; + callback(null, self); + } + }); +}; +/** + * Retrieve the commit's OID. + * + * @param {Commit~oidCallback} callback + */ +Commit.prototype.oid = function(callback) { /** - * Fetch the commit's details synchronously. + * @callback Commit~oidCallback Callback executed on OID retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Oid|null} commit Retrieved OID object or null. */ - self.fetchDetailsSync = function(callback) { - var details = self.commit.fetchDetailsSync(); - return applyDetails(details, self); - }; + callback(null, new git.oid(this.rawCommit.oid())); +}; +/** + * Retrieve the SHA. + * + * @param {Commit~shaCallback} callback + */ +Commit.prototype.sha = function(callback) { /** - * Look up the commit referenced by oid, replace self.commit - * with the result. - * - * @param {Repo} repo - * @param {Oid|String|RawOid} oid Raw or convenience OID object or SHA string - * @param {Function} callback + * @callback Commit~shaCallback Callback executed on SHA retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} sha Retrieved SHA. */ - self.lookup = function(repo, oid, callback) { - self.repo = repo; - if (typeof oid !== 'string' && !(oid instanceof git.raw.Oid)) { - oid = oid.getRawOid(); + this.oid(function(error, oid) { + if (!success(error, callback)) { + return; } - self.commit.lookup(repo, oid, function(error, commit) { - if (error) { - callback(git.error(error), null); + oid.sha(function(error, sha) { + if (!success(error, callback)) { return; } - self.commit = commit; - self.fetchDetails(function(error) { - if (error) { - callback(git.error(error), null); - return; - } - callback(null, self); - }); + callback(null, sha); }); - }; - - self.tree = function() { - var tree = new git.raw.Tree(self.repo); - if(tree.error) { - return git.error(tree.error); - } else { - self.commit.tree(tree); + }); +}; + +/** + * Retrieve the message + * + * @param {Commit~messageCallback} callback + */ +Commit.prototype.message = function(callback) { + /** + * @callback Commit~messageCallback Callback executed on message retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} message Retrieved message. + */ + this.rawCommit.message(function(error, message) { + if (success(error, callback)) { + callback(null, message); } - return git.tree(self.repo, tree); - }; + }); +}; - self.file = function(path, callback) { - self.tree().entry(path, callback); - }; +/** + * Retrieve the commit time as a unix timestamp. + * + * @param {Commit~timeCallback} callback + */ +Commit.prototype.time = function(callback) { + /** + * @callback Commit~timeCallback Callback executed on time retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Integer|null} time Retrieved time in seconds. + */ + this.rawCommit.time(function(error, time) { + if (success(error, callback)) { + // git_commit_time returns timestamp in s, converting to ms here + callback(null, time * 1000); + } + }); +}; +/** + * Retrieve the commit time as a Date object. + * + * @param {Commit~dateCallback} callback + */ +Commit.prototype.date = function(callback) { /** - * Walk the history of this commit. - * - * @return {Event} Event emits 'commit', with error, commit and 'end', with - * error, commits[] + * @callback Commit~dateCallback Callback executed on date retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Date|null} time Retrieved time as a Date object. */ - self.history = function() { - var revwalk = git.revwalk(self.repo), - event = new events.EventEmitter(), - commits = []; - - revwalk.walk(self.id, function(error, index, commit, noMoreCommits) { - if(error) { - event.emit('end', error, commits); - return false; - } + this.time(function(error, time) { + if (success(error, callback)) { + callback(null, new Date(time)); + } + }); +}; - if (noMoreCommits) { - event.emit('end', null, commits); - return; - } - event.emit('commit', null, commit); - commits.push(commit); - }); +/** + * Retrieve the commit's positive or negative timezone offset, in minutes from UTC. + * + * @param {Commit~offsetCallback} callback + */ +Commit.prototype.offset = function(callback) { + /** + * @callback Commit~offsetCallback Callback executed on offset retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Integer|null} offset Retrieved offset in in minutes from UTC. + */ + this.rawCommit.offset(function(error, offset) { + if (success(error, callback)) { + callback(null, offset); + } + }); +}; - return event; - }; +/** + * Retrieve the commit's author signature. + * + * @param {Commit~authorCallback} callback + */ +Commit.prototype.author = function(callback) { + /** + * @callback Commit~authorCallback Callback executed on author retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Signature|null} author Retrieved author signature. + */ + this.rawCommit.author(function(error, rawSignature) { + if (success(error, callback)) { + callback(null, new git.signature(rawSignature)); + } + }); +}; +/** + * Retrieve the commit's committer. + * + * @param {Commit~committerCallback} callback + */ +Commit.prototype.committer = function(callback) { /** - * Retrieve the commit's parent at the given position asynchronously. - * - * @param {Integer} position + * @callback Commit~committerCallback Callback executed on committer retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Signature|null} committer Retrieved committer signature. */ - self.parent = function(position, callback) { - var parent = null; - self.commit.parent(position, function processParent(errorCode, parent) { - var error = null; - if (errorCode) { - error = git.error(errorCode); - return callback(error, null); - } - var parentCommit = new Commit(parent); - parentCommit.fetchDetails(function returnParent(error) { - callback(error, parentCommit); - }); - }); - }; + this.rawCommit.committer(function(error, rawSignature) { + if (success(error, callback)) { + callback(null, new git.signature(rawSignature)); + } + }); +}; +/** + * Retrieve the tree for this commit. + * + * @param {Commit~treeCallback} callback + */ +Commit.prototype.tree = function(callback) { /** - * Retrieve the commit's parent at the given positino synchronously. - * - * @param {Integer} position - * @return {Commit} + * @callback Commit~treeCallback Callback executed on tree retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Tree|null} tree Retrieved tree. */ - self.parentSync = function(position) { - var parent = new Commit(self.commit.parentSync(position)); - return parent.fetchDetailsSync(); - }; + var self = this; + self.rawCommit.tree(function commitTree(error, rawTree) { + if (success(error, callback)) { + callback(null, new git.tree(self.rawRepo, rawTree)); + } + }); +}; +/** + * Retrieve the file represented by path for this commit. + * Path must be relative to repository root. + * + * @param {String} path + * @param {Commit~fileCallback} callback + */ +Commit.prototype.file = function(path, callback) { /** - * Get a diff tree showing changes between this commit and its parent(s). - * Assumes commit has been populated with fetchDetails|fetchDetailsSync - * - * @param {Function} callback Called with error (null if no error) and the - * diff tree + * @callback Commit~fileCallback Callback executed on file retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Entry|null} file Retrieved file entry. */ - self.parentsDiffTrees = function(callback) { - if (!self.parentCount) { - callback(null, []); + this.tree(function commitFileCallback(error, tree) { + if (!success(error, callback)) { return; } - var parentDiffLists = []; - self.parentShas.forEach(function eachParentSha(parentSha) { - git.diffList(self.repo).treeToTree(parentSha, self.sha, function walkDiffList(error, diffList) { - if (error) { - callback(error, null); - return; + tree.entry(path, function(error, entry) { + if (success(error, callback)) { + callback(null, entry); + } + }); + }); +}; + +/** + * Walk the history from this commit backwards. + * An EventEmitter is returned that will emit a 'commit' event for each + * commit in the history, and one 'end' event when the walk is completed. + * + * @fires Commit#commit + * @fires Commit#end + * + * @return {EventEmitter} historyWalkEmitter + */ +Commit.prototype.history = function() { + var event = new events.EventEmitter(), + self = this; + + self.oid(function commitOid(error, oid) { + (new git.revwalk(self.rawRepo)).allocate(function createRevwalk(error, revwalk) { + var commits = []; + revwalk.walk(oid, function commitRevWalk(error, index, commit, noMoreCommits) { + if(error) { + event.emit('end', error, commits); + return false; } - parentDiffLists.push(diffList); - if (parentDiffLists.length === self.parentShas.length) { - callback(null, parentDiffLists); + + if (noMoreCommits) { + /** + * End event. + * + * @event Commit#end + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {Commit[]} commits The commits. + */ + event.emit('end', null, commits); + return; } + /** + * Commit event. + * + * @event Commit#commit + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {Commit} commit The commit. + */ + event.emit('commit', null, commit); + commits.push(commit); }); }); - }; + }); + + return event; +}; + +/** + * Retrieve the commit's parents. + * + * @param {Commit~parentsCallback} callback + */ +Commit.prototype.parents = function(callback) { + /** + * @callback Commit~parentsCallback Callback executed on parents retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Commit[]|null} parents Commit's parent(s). + */ + var self = this; + self.rawCommit.parents(function processParent(error, rawParents) { + if (success(error, callback)) { + var parents = []; + rawParents.forEach(function eachParent(rawParent) { + parents.push(new Commit(self.rawRepo, rawParent)); + }); + callback(null, parents); + } + }); +}; - return self; +/** + * Generate an array of diff trees showing changes between this commit + * and its parent(s). + * + * @param {Commit~parentsDiffTreesCallback} callback + */ +Commit.prototype.parentsDiffTrees = function(callback) { + /** + * @callback Commit~parentsDiffTreesCallback Callback executed on diff trees retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {DiffList[]|null} diffLists Array of DiffTrees showing changes between this commit and its parent(s) + */ + var self = this; + self.sha(function(error, commitSha) { + if (!success(error, callback)) { + return; + } + self.parents(function commitParents(error, parents) { + if (!success(error, callback)) { + return; + } + var parentDiffLists = []; + parents.forEach(function commitEachParent(parent) { + parent.sha(function commitParentSha(error, parentSha) { + (new git.diffList(self.rawRepo)).treeToTree(parentSha, commitSha, function walkDiffList(error, diffList) { + if (!success(error, callback)) { + return; + } + parentDiffLists.push(diffList); + if (parentDiffLists.length === parents.length) { + callback(null, parentDiffLists); + } + }); + }); + }); + }); + }); }; exports.commit = Commit; diff --git a/lib/diff_list.js b/lib/diff_list.js index caf1a055b..56a269314 100644 --- a/lib/diff_list.js +++ b/lib/diff_list.js @@ -1,67 +1,165 @@ var git = require('../'), events = require('events'); -var GitDiffList = function(rawRepo, rawDiffList) { - var self = { - repo: rawRepo, - diffList: rawDiffList || new git.raw.DiffList() - }; +/** + * Convenience diff list class. + * + * @constructor + * @param {git.raw.Repo} rawRepo + * @param {git.raw.DiffList} [rawDiffList = new git.raw.DiffList] + */ +var DiffList = function(rawRepo, rawDiffList) { + if (!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for DiffList must be a raw repo'); + } + this.rawRepo = rawRepo; - self.walk = function() { - var event = new events.EventEmitter(), - allFileDeltas = []; + if (rawDiffList instanceof git.raw.DiffList) { + this.rawDiffList = rawDiffList; + } else { + this.rawDiffList = new git.raw.DiffList(); + } +}; - process.nextTick(function() { - self.diffList.walk(function fileCallback(error, fileDeltas) { - if (error) { - event.emit('end', error, null); - } - fileDeltas.forEach(function(fileDelta) { - event.emit('file', null, fileDelta); - allFileDeltas.push(fileDelta); - }); - }, function hunkCallback(error, diffHunk) { - // @todo implement? - }, function lineCallback(error, diffLine) { - // @todo implement? - }, function endCallback(error) { - event.emit('end', error, allFileDeltas); - }); - }); +/** + * Refer to vendor/libgit2/include/git2/diff.h for delta type definitions. + * + * @readonly + * @enum {Integer} + */ +DiffList.prototype.deltaTypes = { + /** 0 */ GIT_DELTA_UNMODIFIED: git.raw.DiffList.deltaTypes.GIT_DELTA_UNMODIFIED, + /** 1 */ GIT_DELTA_ADDED: git.raw.DiffList.deltaTypes.GIT_DELTA_ADDED, + /** 2 */ GIT_DELTA_DELETED: git.raw.DiffList.deltaTypes.GIT_DELTA_DELETED, + /** 3 */ GIT_DELTA_MODIFIED: git.raw.DiffList.deltaTypes.GIT_DELTA_MODIFIED, + /** 4 */ GIT_DELTA_RENAMED: git.raw.DiffList.deltaTypes.GIT_DELTA_RENAMED, + /** 5 */ GIT_DELTA_COPIED: git.raw.DiffList.deltaTypes.GIT_DELTA_COPIED, + /** 6 */ GIT_DELTA_IGNORED: git.raw.DiffList.deltaTypes.GIT_DELTA_IGNORED, + /** 7 */ GIT_DELTA_UNTRACKED: git.raw.DiffList.deltaTypes.GIT_DELTA_UNTRACKED, + /** 8 */ GIT_DELTA_TYPECHANGE: git.raw.DiffList.deltaTypes.GIT_DELTA_TYPECHANGE +}; - return event; - }; +/** + * Refer to vendor/libgit2/include/git2/diff.h for line origin type definitions. + * + * @readOnly + * @enum {String} + */ +DiffList.prototype.lineOriginTypes = { + /** ' ' */ GIT_DIFF_LINE_CONTEXT: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, + /** '+' */ GIT_DIFF_LINE_ADDITION: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_ADDITION, + /** '-' */ GIT_DIFF_LINE_DELETION: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_DELETION, + /** '\n' */ GIT_DIFF_LINE_ADD_EOFNL: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_ADD_EOFNL, + /** '' */ GIT_DIFF_LINE_DEL_EOFNL: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_DEL_EOFNL, + /** 'F' */ GIT_DIFF_LINE_FILE_HDR: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_FILE_HDR, + /** 'H' */ GIT_DIFF_LINE_HUNK_HDR: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_HUNK_HDR, + /** 'B' */ GIT_DIFF_LINE_BINARY: git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_BINARY +}; - self.treeToTree = function(oldSha, newSha, callback) { - self.diffList.treeToTree(self.repo, oldSha, newSha, function(error, rawDifflist) { - if (error) { - callback(error, null); - return; - } - self.diffList = rawDifflist; - callback(null, self); +/** + * Walk the current diff list tree. + * + * @fires DiffList#delta + * @fires DiffList#end + * + * @return {EventEmitter} diffListWalkEmitter + */ +DiffList.prototype.walk = function() { + var event = new events.EventEmitter(), + allFileDeltas = [], + self = this; + + self.rawDiffList.walk(function fileCallback(error, fileDeltas) { + if (error) { + event.emit('end', new git.error(error.message, error.code), null); + } + fileDeltas.forEach(function(fileDelta) { + /** + * Delta event. + * + * @event DiffList#delta + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {FileDelta} fileDelta The file delta object. + */ + event.emit('delta', null, fileDelta); + allFileDeltas.push(fileDelta); }); - }; + }, function hunkCallback(error, diffHunk) { + /** TO BE IMPLEMENTED */ + }, function lineCallback(error, diffLine) { + /** TO BE IMPLEMENTED */ + }, function endCallback(error) { + /** + * End event. + * + * @event DiffList#end + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {FileDelta[]} fileDeltas The file delta objects. + */ + event.emit('end', error ? new git.error(error.message, error.code) : null, allFileDeltas); + }); - /** - * Add libgit2 delta types to git.diffList object. - * - * Refer to vendor/libgit2/include/git2/diff.h for definitions. - */ - for (var deltaType in git.raw.DiffList.deltaTypes) { - self[deltaType] = git.raw.DiffList.deltaTypes[deltaType]; - } - - /** - * Add libgit2 line origin types to git.diffList object. - * - * Refer to vendor/libgit2/include/git2/diff.h for definitions. - */ - for (var lineOriginType in git.raw.DiffList.lineOriginTypes) { - self[lineOriginType] = git.raw.DiffList.lineOriginTypes[lineOriginType]; - } + return event; +}; - return self; +DiffList.prototype.treeToTree = function(oldSha, newSha, callback) { + var self = this; + self.rawDiffList.treeToTree(self.rawRepo, oldSha, newSha, function(error, rawDifflist) { + if (error) { + callback(new git.error(error.message, error.code), null); + return; + } + self.rawDiffList = rawDifflist; + callback(null, self); + }); }; -exports.diffList = GitDiffList; +exports.diffList = DiffList; + +/** + * @namespace + * @property {Object} oldFile Contains details for the old file state + * @property {String} oldFile.path The path to the old file, relative to the repository + * @property {Object} newFile Contains details for the new file state + * @property {String} newFile.path The path to the new file, relative to the repository + * @property {Object[]} content Array of context & differences + * @property {Object} content[].range + * @property {Object} content[].range.old + * @property {Integer} content[].range.old.start + * @property {Integer} content[].range.old.lines + * @property {Object} content[].range.new + * @property {Integer} content[].range.new.start + * @property {Integer} content[].range.new.lines + * @property {Object} content[].content Content of the delta + * @property {DiffList.lineOriginTypes} content[].lineOrigin + * @property {Integer} content[].contentLength + * @property {Integer} status Type of delta + */ +var FileDelta = { + oldFile: { + path: String + }, + newFile: { + path: String + }, + content: [ + { + range: { + old: { + start: Number, + lines: Number + }, + 'new': { + start: Number, + lines: Number + } + }, + content: String, + lineOrigin: String, + contentLength: Number + } + ], + status: Number +}; diff --git a/lib/error.js b/lib/error.js index 3edd99493..9fbeac5a4 100644 --- a/lib/error.js +++ b/lib/error.js @@ -4,38 +4,59 @@ var git = require('../'), /** * GitError constructor. * - * @param {String} message giterr_last->message - * @param {Integer} code giterr_last->klass + * @constructor + * @param {String} [message = 'No message'] The error description. Set from giterr_last->message. + * @param {Integer} [code = git.raw.Error.codes.GITERR_INVALID] The error code. Set from giterr_last->klass */ var GitError = function(message, code) { Error.call(this); Error.captureStackTrace(this, exports.error); this.name = 'GitError'; - this.message = message; - this.code = code; + this.message = message || 'No message'; + this.code = code || git.raw.Error.codes.GITERR_INVALID; }; util.inherits(GitError, Error); /** - * Add libgit2 error codes to git.error object. - * * Refer to vendor/libgit2/include/git2/errors.h for error code definitions. + * + * @readonly + * @enum {Integer} */ -for (var errorName in git.raw.Error.codes) { - GitError.prototype[errorName] = git.raw.Error.codes[errorName]; -} +GitError.prototype.codes = { + /** 0 */ GITERR_NOMEMORY: git.raw.Error.codes.GITERR_NOMEMORY, + /** 1 */ GITERR_OS: git.raw.Error.codes.GITERR_OS, + /** 2 */ GITERR_INVALID: git.raw.Error.codes.GITERR_INVALID, + /** 3 */ GITERR_REFERENCE: git.raw.Error.codes.GITERR_REFERENCE, + /** 4 */ GITERR_ZLIB: git.raw.Error.codes.GITERR_ZLIB, + /** 5 */ GITERR_REPOSITORY: git.raw.Error.codes.GITERR_REPOSITORY, + /** 6 */ GITERR_CONFIG: git.raw.Error.codes.GITERR_CONFIG, + /** 7 */ GITERR_REGEX: git.raw.Error.codes.GITERR_REGEX, + /** 8 */ GITERR_ODB: git.raw.Error.codes.GITERR_ODB, + /** 9 */ GITERR_INDEX: git.raw.Error.codes.GITERR_INDEX, + /** 10 */ GITERR_OBJECT: git.raw.Error.codes.GITERR_OBJECT, + /** 11 */ GITERR_NET: git.raw.Error.codes.GITERR_NET, + /** 12 */ GITERR_TAG: git.raw.Error.codes.GITERR_TAG, + /** 13 */ GITERR_TREE: git.raw.Error.codes.GITERR_TREE +}; /** - * Add libgit2 return codes to git.error object. - * * Refer to vendor/libgit2/include/git2/errors.h for return code definitions. + * + * @readonly + * @enum {Integer} */ -for (var errorName in git.raw.Error.returnCodes) { - GitError.prototype[errorName] = git.raw.Error.returnCodes[errorName]; -} - -exports.error = function(error) { - return new GitError(error.message, error.code); +GitError.prototype.returnCodes = { + /** 0 */ GIT_OK: git.raw.Error.returnCodes.GIT_OK, + /** -1 */ GIT_ERROR: git.raw.Error.returnCodes.GIT_ERROR, + /** -3 */ GIT_ENOTFOUND: git.raw.Error.returnCodes.GIT_ENOTFOUND, + /** -4 */ GIT_EEXISTS: git.raw.Error.returnCodes.GIT_EEXISTS, + /** -5 */ GIT_EAMBIGUOUS: git.raw.Error.returnCodes.GIT_EAMBIGUOUS, + /** -6 */ GIT_EBUFS: git.raw.Error.returnCodes.GIT_EBUFS, + /** -30 */ GIT_PASSTHROUGH: git.raw.Error.returnCodes.GIT_PASSTHROUGH, + /** -31 */ GIT_ITEROVER: git.raw.Error.returnCodes.GIT_ITEROVER }; + +exports.error = GitError; diff --git a/lib/index.js b/lib/index.js index 6d37ebac3..197f77abb 100755 --- a/lib/index.js +++ b/lib/index.js @@ -12,14 +12,12 @@ if (~os.type().indexOf('CYGWIN') && !~path.indexOf(root)) { // Import libraries exports.blob = require('./blob.js').blob; exports.repo = require('./repo.js').repo; -exports.sig = require('./sig.js').sig; +exports.signature = require('./signature.js').signature; exports.oid = require('./oid.js').oid; -exports.object = require('./object.js').object; exports.reference = require('./reference.js').reference; exports.revwalk = require('./revwalk.js').revwalk; exports.commit = require('./commit.js').commit; exports.tree = require('./tree.js').tree; -exports.entry = require('./tree_entry.js').entry; // Assign raw api to module try { @@ -31,6 +29,7 @@ try { // Initialize objects that need to access their raw counterparts exports.diffList = require('./diff_list.js').diffList; exports.error = require('./error.js').error; +exports.entry = require('./tree_entry.js').entry; // Set version exports.version = require('../package').version; diff --git a/lib/object.js b/lib/object.js deleted file mode 100644 index 1c99d9fa7..000000000 --- a/lib/object.js +++ /dev/null @@ -1,71 +0,0 @@ -var git = require( '../' ); - -var _Object = function( obj ) { - var self = {}; - - if( object instanceof git.raw.Object ) { - self.object = obj; - } - else { - self.object = new git.raw.Object(); - } - - Object.defineProperty( self, 'id', { - get: function() { - }, - enumerable: true - }); - - Object.defineProperty( self, 'type', { - get: function() { - return self.object.type(); - }, - enumerable: true - }); - - Object.defineProperty( self, 'length', { - get: function() { - return self.object.size(); - }, - enumerable: true - }); - - Object.defineProperty( self, 'isLoose', { - get: function() { - return self.object.typeIsLoose(); - }, - enumerable: true - }); - - self.id = function() { - var oid = git.oid(); - - self.object.id( oid.oid ); - - return oid; - }; - - self.owner = function() { - var repo = git.repo(); - - self.object.owner( repo.repo ); - - return repo; - }; - - self.toString = function() { - return self.object.type2String(); - }; - - self.toType = function( type ) { - return self.object.toType( type ); - }; - - self.free = function() { - return self.object.free(); - }; - - return self; -}; - -exports.object = _Object; diff --git a/lib/oid.js b/lib/oid.js index 972933392..c32b58a3c 100644 --- a/lib/oid.js +++ b/lib/oid.js @@ -1,19 +1,60 @@ -var git = require( '../' ); +var git = require('../'), + success = require('./utilities').success; +/** + * Convenience Oid constructor. + * + * @constructor + * @param {git.raw.Oid} [rawOid = new git.rawOid] Raw Oid object. + */ var Oid = function(rawOid) { - var self = {}; - if(rawOid instanceof git.raw.Oid) { - self.oid = rawOid; + this.rawOid = rawOid; } else { - self.oid = new git.raw.Oid(); + this.rawOid = new git.raw.Oid(); } +}; - self.getRawOid = function() { - return self.oid; - }; +/** + * @return {git.raw.Oid} The wrapped raw Oid object. + */ +Oid.prototype.getRawOid = function() { + return this.rawOid; +}; + +/** + * Create Oid object from string. + * + * @param {String} sha + * @param {Oid~fromStringCallback} callback + */ +Oid.prototype.fromString = function(sha, callback) { + /** + * @callback Oid~fromStringCallback Callback executed after raw Oid is created. + * @param {GitError|null} error An Error or null if successful. + * @param {Oid|null} oid The new Oid object. + */ + var self = this; + self.rawOid.fromString(sha, function(error, rawOid) { + if (success(error, callback)) { + self.rawOid = rawOid; + callback(null, self); + } + }); +}; - return self; +/** + * Convert the raw Oid to a SHA + * + * @param {Oid~shaCallback} callback + */ +Oid.prototype.sha = function(callback) { + /** + * @callback Oid~shaCallback Callback executed after SHA is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} sha The SHA. + */ + callback(null, this.rawOid.sha()); }; exports.oid = Oid; diff --git a/lib/reference.js b/lib/reference.js index 94e8231d9..51e7d5a2f 100644 --- a/lib/reference.js +++ b/lib/reference.js @@ -1,37 +1,64 @@ -var git = require('../'); +var git = require('../'), + success = require('./utilities').success; -var Reference = function(rawReference) { - var self = {}; - - if(rawReference instanceof git.raw.Reference) { - self.reference = rawReference; - } else if(rawReference instanceof git.raw.Repo) { - self.repo = rawReference; - self.reference = new git.raw.Reference(rawReference); +/** + * Convenience reference constructor. + * + * @constructor + * @param {git.raw.Repo} rawRepo + * @param {git.raw.Reference} [rawReference = new git.raw.Reference()] + */ +var Reference = function(rawRepo, rawReference) { + if (!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for Reference must be a raw repo'); } + this.rawRepo = rawRepo; - self.lookup = function(name, callback) { - self.reference.lookup(self.repo, name, function(error, reference) { - if (error) { - callback(git.error(error), self); - return; - } - self.reference = reference; - callback(null, self); - }); - }; + if (rawReference instanceof git.raw.Reference) { + this.rawReference = rawReference; + } else { + this.rawReference = new git.raw.Reference(this.rawRepo); + } +}; - self.oid = function(callback) { - self.reference.oid(function(error, rawOid) { - if (error) { - callback(git.error(error, self)); - return; - } - callback(null, git.oid(rawOid)); - }); - }; +/** + * Lookup the reference with the given name. + * + * @param {String} name + * @param {Reference~lookupCallback} callback + */ +Reference.prototype.lookup = function(name, callback) { + /** + * @callback Reference~lookupCallback Callback executed on lookup completion. + * @param {GitError|null} error An Error or null if successful. + * @param {Reference|null} reference Retrieved reference object or null. + */ + var self = this; + self.rawReference.lookup(self.rawRepo, name, function referenceLookup(error, rawReference) { + if (!success(error, callback)) { + return; + } + self.rawReference = rawReference; + callback(null, self); + }); +}; - return self; +/** + * Get the Oid representing this reference. + * + * @param {Reference~oidCallback} callback + */ +Reference.prototype.oid = function(callback) { + /** + * @callback Reference~oidCallback Callback executed on oid retrieval. + * @param {GitError|null} error An Error or null if successful. + * @param {Oid|null} oid Retrieved Oid object or null. + */ + this.rawReference.oid(function referenceOid(error, rawOid) { + if (success(error, callback)) { + callback(null, new git.oid(rawOid)); + } + }); }; exports.reference = Reference; diff --git a/lib/repo.js b/lib/repo.js index b6541b427..343af1dd4 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -1,79 +1,132 @@ var git = require('../'), - fs = require('fs'); + success = require('./utilities').success; -/* Module: Repo - * Work with a repository. +/** + * Convenience repository class. + * + * @constructor */ -exports.repo = function(directory, callback) { - var self = { - // Assign a new repo object - repo: new git.raw.Repo() - }; +var Repo = function() { + this.rawRepo = new git.raw.Repo(); +}; - if (directory) { - if (!callback || typeof callback !== 'function') { - throw new Error('If directory is provided, callback function is required'); - } - self.repo.open(directory, function(error, rawRepo) { - if (error) { - callback(git.error(error), null); - return; - } - self.repo = rawRepo; - callback(null, self); - }); +/** + * Open the git repository at directory. + * + * @example + * git.repo('/path/to/repository/.git', function(error, repo) { }); + * + * @param {String} directory The .git directory for the repository to open. + * @param {Repo~openCallback} callback + */ +Repo.prototype.open = function(directory, callback) { + /** + * @callback Repo~openCallback Callback executed when repository is opened. + * @param {GitError|null} error An Error or null if successful. + * @param {Repo|null} repo Opened repository. + */ + if (typeof callback !== 'function') { + throw new git.error('Callback is required and must be a Function'); } + var self = this; + self.rawRepo.open(directory, function openRepository(error, rawRepo) { + if (success(error, callback)) { + self.rawRepo = rawRepo; + callback(null, self); + } + }); +}; +/** + * Look up a branch's most recent commit. + * + * @param {String} name Branch name, e.g. 'master' + * @param {Repo~branchCallback} callback + */ +Repo.prototype.branch = function(name, callback) { /** - * Look up a branch and find its tree. - * - * @param {String} name Branch name, e.g. 'master' - * @param {Function} + * @callback Repo~branchCallback Callback executed when the branch is checked out. + * @param {GitError|null} error An Error or null if successful. + * @param {Commit|null} repo HEAD commit for the branch. */ - self.branch = function(name, callback) { - git.reference(self.repo).lookup('refs/heads/' + name, function referenceLookupCallback(error, reference) { - if (error) { - callback(git.error(error), null); + var self = this; + (new git.reference(self.rawRepo)).lookup('refs/heads/' + name, function referenceLookupCallback(error, reference) { + if (!success(error, callback)) { + return; + } + reference.oid(function oidCallback(error, oid) { + if (!success(error, callback)) { return; } - reference.oid(function oidCallback(error, oid) { - if (error) { - callback(git.error(error), null); + self.commit(oid, function commitLookupCallback(error, commit) { + if (!success(error, callback)) { return; } - self.commit(oid, function commitLookupCallback(error, commit) { - if (error) { - callback(git.error(error), null); - return; - } - callback(null, commit); - }); + callback(null, commit); }); }); - }; - - // Find a single commit - self.commit = function(oid, callback) { - git.commit().lookup(self.repo, oid, function(error, commit) { - callback(error, commit); - }); - }; - - self.commitSync = function(sha) { - throw new Error('commitSync not yet implemented'); - // return git.commit().lookupSync(sha); - }; + }); +}; - self.init = function(directory, isBare, callback) { - self.repo.init(directory, isBare, function(error) { - callback.call(this, error, self); - }); - }; +/** + * Retrieve the commit identified by oid. + * + * @param {String|Oid|git.raw.Oid} sha + * @param {Repo~commitCallback} callback + */ +Repo.prototype.commit = function(sha, callback) { + /** + * @callback Repo~commitCallback Callback executed when the commit is looked up. + * @param {GitError|null} error An Error or null if successful. + * @param {Commit|null} commit Commit represented by sha. + */ + (new git.commit(this.rawRepo)).lookup(sha, function(error, commit) { + if (success(error, callback)) { + callback(null, commit); + } + }); +}; - self.free = function() { - self.repo.free(); - delete self.repo; - }; +/** + * Initialise a git repository at directory. + * + * @param {String} directory + * @param {Boolean} isBare True if the repository is to be bare, false otherwise. + * @param {Repo~initCallback} callback + */ +Repo.prototype.init = function(directory, isBare, callback) { + /** + * @callback Repo~initCallback Callback executed when repository is initialized. + * @param {GitError|null} error An Error or null if successful. + * @param {Repo|null} repo Initialized repository. + */ + var self = this; + self.rawRepo.init(directory, isBare, function(error, rawRepo) { + if (success(error, callback)) { + self.rawRepo = rawRepo; + callback(null, self); + } + }); +}; - return self; +/** + * Create a new Repo object. If directory is not provided, simply return it. + * Otherwise open the repo asynchronously. + * + * @param {String|null} directory The directory for the git repo to open. Null + * if one does not want to open a repo. + * @param {repoCallback|null} callback + * @return {Repo|null} The Repo if no directory is provided, else undefined. + */ +exports.repo = function(directory, callback) { + /** + * @callback repoCallback Callback executed if repository is opened. + * @param {GitError|null} error An Error or null if successful. + * @param {Repo|null} repo Opened repository. + */ + var repo = new Repo(); + if (typeof directory === 'undefined') { + return repo; + } + repo.open(directory, callback); }; diff --git a/lib/revwalk.js b/lib/revwalk.js index 8958f9e89..30fc2b4b4 100644 --- a/lib/revwalk.js +++ b/lib/revwalk.js @@ -1,72 +1,90 @@ -var git = require( '../' ); +var git = require('../'), + success = require('./utilities').success; -var _RevWalk = function( obj ) { - var self = {}; +/** + * Convenience revision walking class + * + * @constructor + * @param {git.raw.Repo} rawRepo + * @param {git.raw.RevWalk|null} rawRevWalk + */ +var RevWalk = function(rawRepo, rawRevWalk) { + if (!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for RevWalk must be a raw repo'); + } + this.rawRepo = rawRepo; - if (obj instanceof git.raw.Repo) { - self.repo = obj; - self.revwalk = new git.raw.RevWalk(obj); - } else if (obj instanceof git.raw.RevWalk) { - self.revwalk = obj; + if (typeof rawRevWalk !== 'undefined' && + rawRevWalk instanceof git.raw.RevWalk) { + this.rawRevWalk = rawRevWalk; + } else { + this.rawRevWalk = new git.raw.RevWalk(rawRepo); } +}; + +/** + * Allocate new revwalker. + * + * @param {Function} callback + */ +RevWalk.prototype.allocate = function(callback) { + var self = this; + self.rawRevWalk.allocate(function revwalkAllocate(error, rawRevwalk) { + if (success(error, callback)) { + self.rawRevwalk = rawRevwalk; + callback(null, self); + } + }); +}; - /** - * Walk the history from the given oid. - * - * @param {Oid} oid - * @param {Function} callback Callback accepting the following arguments: - * error, index, commit, noMoreCommits - */ - self.walk = function(oid, callback) { - if(!callback) { return; } +/** + * Walk the history from the given oid. + * + * @param {Oid} oid + * @param {Function} callback Callback accepting the following arguments: + * error, index, commit, noMoreCommits + */ +RevWalk.prototype.walk = function(oid, callback) { + var self = this; + self.rawRevWalk.push(oid.getRawOid(), function revWalkPush(error) { + if (!success(error, callback)) { + return; + } - self.revwalk.push(oid.getRawOid(), function(error) { - if (error) { - callback.apply(this, [git.error(error)]); + var shouldContinue = true, index = 0; + + function walk() { + if(!shouldContinue) { return; } - var shouldContinue = true, index = 0; + self.rawRevWalk.next(function revWalkNext(error, oid, walkOver) { + if(error) { + callback(new git.error(error.message, error.code), index, commit); + return; + } - function walk() { - if(!shouldContinue) { + // Finished walking history, apply callback with noMoreCommits = true + if (walkOver) { + callback(null, index, null, walkOver); return; } - self.revwalk.next(function(error, oid, walkOver) { + (new git.commit(self.rawRepo)).lookup(oid, function revWalkCommitLookup(error, commit) { if(error) { - callback.apply(this, [git.error(error), index, commit]); + callback(new git.error(error.message, error.code), index, commit); return; } - - // Finished walking history, apply callback with noMoreCommits = true - if (walkOver) { - callback.apply(this, [null, index, null, walkOver]); - return; + if(callback(null, index, commit) === false) { + shouldContinue = false; } - - git.commit(self.repo).lookup(self.repo, oid, function(error, commit) { - - if(error) { - callback.apply(this, [git.error(error), index, commit]); - return; - } - - if(callback.apply(this, [null, index, commit]) === false) { - shouldContinue = false; - } - - index++; - walk(); - }); + index++; + walk(); }); - } - - walk(); - }); - }; - - return self; + }); + } + walk(); + }); }; -exports.revwalk = _RevWalk; +exports.revwalk = RevWalk; diff --git a/lib/sig.js b/lib/sig.js deleted file mode 100644 index 494de94ca..000000000 --- a/lib/sig.js +++ /dev/null @@ -1,31 +0,0 @@ -var git = require( '../' ); - -var _Sig = function( obj ) { - var self = {}; - - Object.defineProperty( self, 'name', { - get: function() { - return self.sig.name(); - }, - enumerable: true - }); - - Object.defineProperty( self, 'email', { - get: function() { - return self.sig.email(); - }, - enumerable: true - }); - - // Internal references to Git references - if( obj instanceof git.raw.Repo ) { - // TODO: Add support for creation - } - else if ( obj instanceof git.raw.Sig ) { - self.sig = obj; - } - - return self; -}; - -exports.sig = _Sig; diff --git a/lib/signature.js b/lib/signature.js new file mode 100644 index 000000000..c33f91b13 --- /dev/null +++ b/lib/signature.js @@ -0,0 +1,23 @@ +var git = require('../'); + +var Signature = function(rawSignature) { + if (rawSignature instanceof git.raw.Signature) { + this.rawSignature = rawSignature; + } else { + this.rawSignature = new git.raw.Signature(); + } +}; + +Signature.prototype.name = function(callback) { + callback(null, this.rawSignature.name()); +}; + +Signature.prototype.email = function(callback) { + callback(null, this.rawSignature.email()); +}; + +Signature.prototype.duplicate = function(callback) { + callback(null, new Signature(git.rawSignature.duplicate())); +}; + +exports.signature = Signature; diff --git a/lib/tree.js b/lib/tree.js index 35198dab4..d56d11eff 100644 --- a/lib/tree.js +++ b/lib/tree.js @@ -1,110 +1,114 @@ var git = require('../'), + success = require('./utilities').success, events = require('events'); -var Tree = function(rawObject, tree) { - var self = {}; +/** + * Tree convenience class constructor. + * + * @constructor + * @param {git.raw.Repo} rawRepo Raw repository object. + * @param {git.raw.Tree} [rawTree = new git.raw.Tree(rawRepo)] Raw tree object. + */ +var Tree = function(rawRepo, rawTree) { + if(!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for Tree must be a raw repo'); + } + this.rawRepo = rawRepo; - if (rawObject instanceof git.raw.Repo && - tree instanceof git.raw.Tree) { - self.repo = rawObject; - self.tree = tree; - } else if(rawObject instanceof git.raw.Repo) { - self.repo = rawObject; - self.tree = new git.raw.Tree(tree); - } else if (rawObject instanceof git.raw.Tree) { - self.tree = rawObject; + if(rawTree instanceof git.raw.Tree) { + this.rawTree = rawTree; } else { - self.tree = new git.raw.Tree(); + this.rawTree = new git.raw.Tree(rawRepo); } +}; - Object.defineProperty(self, 'length', { - get: function() { - return self.tree.entryCount(); - }, - enumerable: true - }); - - self.walk = function(repo) { - repo = repo || self.repo; - - var entry, - index, - length = self.length, - event = new events.EventEmitter(), - entries = []; - - function next(index) { - var dir; - var tree; - var prerequisites = 0; - - function complete(error) { - if (index < length-1) { - next(index = index+1); - } else { - event.emit('end', error, entries); - } - } - - entry = git.entry(repo); - - self.tree.entryByIndex(entry.entry, index, function() { - if (entry.isFile()) { - entries.push(entry); - event.emit('entry', null, index, entry); - } else { - dir = entry.name; - tree = entry.tree(); - prerequisites++; - if (tree.error) { - event.emit('end', tree.error); - return; - } - - tree.walk(repo).on('entry', function(error, index, entry) { - if (error) { - event.emit('entry', error, index, entry); - } - entry.dir = dir + '/' + entry.dir; - event.emit('entry', null, index, entry); - }).on('end', function(error, endEntries) { - if (error) { - complete(error); - } - prerequisites--; - entries = entries.concat(endEntries); - if (prerequisites === 0) { - complete(error); - } - }); - } - - if (prerequisites === 0) { - complete(); - } - }); +/** + * Retrieve the raw tree identified by the given Oid. + * + * @param {Oid} oid The Oid identifying a tree. + * @param {Tree~lookupCallback} callback + */ +Tree.prototype.lookup = function(oid, callback) { + /** + * @callback Tree~lookupCallback Callback executed when the tree is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {Tree|null} tree The tree object or null. + */ + var self = this; + self.rawTree.lookup(oid.getRawOid(), self.rawRepo, function(error, rawTree) { + if (success(error, callback)) { + self.rawTree = rawTree; + callback(null, self); } + }); +}; - next(0); - - return event; - }; - - self.entry = function(path, callback) { - if(!callback) { - throw new Error('Tree entry function requires a callback'); +/** + * Retrieve the entry by path. + * + * @param {String} path Path to the tree entry, relative to repository root. + * @param {Tree~entryCallback} callback + */ +Tree.prototype.entry = function(path, callback) { + /** + * @callback Tree~entryCallback Callback executed when an entry is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {Entry|null} entry The tree entry object or null. + */ + var self = this; + self.rawTree.entryByPath(path, function(error, rawEntry) { + if (success(error, callback)) { + callback(null, new git.entry(self.rawRepo, rawEntry)); } + }); +}; - self.tree.entryByPath(path, function(error, entry) { - if (error) { - callback(git.error(error), null); - } else { - git.entry(entry).fetchDetails(callback); - } +/** + * Walk the tree. + * + * @fires Tree#entry + * @fires Tree#end + * + * @param {Boolean} [blobsOnly = true] True to emit only blob & blob executable entries. + * + * @return {EventEmitter} + */ +Tree.prototype.walk = function(blobsOnly) { + blobsOnly = typeof blobsOnly === 'undefined' ? true : blobsOnly; + + var self = this, + event = new events.EventEmitter(), + entries = []; + + var total = 0; + + self.rawTree.walk(blobsOnly, function treeWalkEntries(error, rawEntries) { + rawEntries.forEach(function treeWalkEntryEmitter(rawEntry) { + var entry = new git.entry(self.rawRepo, rawEntry); + entries.push(entry); + /** + * Entry event. + * + * @event Tree#entry + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {Entry} entry The tree entry. + */ + event.emit('entry', null, entry); }); - }; + }, function treeWalkEnd(error) { + /** + * End event. + * + * @event Tree#end + * + * @param {GitError|null} error An error object if there was an issue, null otherwise. + * @param {Entry[]} entries The tree entries. + */ + event.emit('end', error ? new git.error(error.message, error.code) : null, entries); + }); - return self; + return event; }; exports.tree = Tree; diff --git a/lib/tree_entry.js b/lib/tree_entry.js index 22dfdeeb8..a78de137b 100644 --- a/lib/tree_entry.js +++ b/lib/tree_entry.js @@ -1,100 +1,266 @@ -var git = require( '../' ); +var git = require('../'), + path = require('path'), + success = require('./utilities').success; -var _TreeEntry = function(rawObject) { - var self = {}; +var fileModeIsFile = function(fileMode, instance) { + return fileMode === instance.fileModes.GIT_FILEMODE_BLOB || + fileMode === instance.fileModes.GIT_FILEMODE_BLOB_EXECUTABLE; +}; + +var fileModeIsDirectory = function(fileMode, instance) { + return fileMode === instance.fileModes.GIT_FILEMODE_TREE; +}; - if(rawObject instanceof git.raw.TreeEntry) { - self.entry = rawObject; - } else { - self.entry = new git.raw.TreeEntry(); - self.repo = rawObject; +/** + * Convenience tree entry constructor. + * + * @constructor + * @param {git.raw.Repo} rawRepo Raw repository object. + * @param {git.raw.TreeEntry} rawTreeEntry Raw tree entry object. + */ +var TreeEntry = function(rawRepo, rawTreeEntry) { + if(!(rawRepo instanceof git.raw.Repo)) { + throw new git.error('First parameter for Tree Entry must be a raw repo', 0); } - self.dir = ''; + if(!(rawTreeEntry instanceof git.raw.TreeEntry)) { + throw new git.error('Second parameter for Tree Entry must be a raw tree entry', 0); + } - Object.defineProperty( self, 'name', { - get: function() { - return self.dir + self.entry.name(); - }, - enumerable: true - }); + this.rawRepo = rawRepo; + this.rawEntry = rawTreeEntry; + this._cache = {}; +}; - Object.defineProperty( self, 'attributes', { - get: function() { - return self.entry.attributes(); - }, - enumerable: true - }); +/** + * Refer to vendor/libgit2/include/git2/types.h for filemode definitions. + * + * @readonly + * @enum {Integer} + */ +TreeEntry.prototype.fileModes = { + /** 0 (0000000) */ GIT_FILEMODE_NEW: git.raw.TreeEntry.fileModes.GIT_FILEMODE_NEW, + /** 16384 (0040000) */ GIT_FILEMODE_TREE: git.raw.TreeEntry.fileModes.GIT_FILEMODE_TREE, + /** 33188 (0100644) */ GIT_FILEMODE_BLOB: git.raw.TreeEntry.fileModes.GIT_FILEMODE_BLOB, + /** 33261 (0100755) */ GIT_FILEMODE_BLOB_EXECUTABLE: git.raw.TreeEntry.fileModes.GIT_FILEMODE_BLOB_EXECUTABLE, + /** 40960 (0120000) */ GIT_FILEMODE_LINK: git.raw.TreeEntry.fileModes.GIT_FILEMODE_LINK, + /** 57344 (0160000) */ GIT_FILEMODE_COMMIT: git.raw.TreeEntry.fileModes.GIT_FILEMODE_COMMIT +}; - Object.defineProperty( self, 'object', { - get: function() { - return self.entry.toObject(); - }, - enumerable: true +/** + * Retrieve the Oid for this TreeEntry. + * + * @param {TreeEntry~oidCallback} callback + */ +TreeEntry.prototype.oid = function(callback) { + /** + * @callback TreeEntry~oidCallback Callback executed after the Oid is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {TreeEntry|null} oid The Oid object or null. + */ + this.rawEntry.oid(function(error, rawOid) { + if (success(error, callback)) { + callback(null, new git.oid(rawOid)); + } }); +}; - Object.defineProperty( self, 'id', { - get: function() { - var oid = git.oid(); - self.entry.id( oid.oid ); - - return oid; - }, - enumerable: true +/** + * Retrieve the SHA for this TreeEntry. + * + * @param {TreeEntry~shaCallback} callback + */ +TreeEntry.prototype.sha = function(callback) { + /** + * @callback TreeEntry~shaCallback Callback executed after the SHA is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} sha The SHA object or null. + */ + this.rawEntry.oid(function(error, oid) { + if (!success(error, callback)) { + return; + } + (new git.oid(oid)).sha(function(error, sha) { + if (success(error, callback)) { + callback(null, sha); + } + }); }); +}; - Object.defineProperty( self, 'sha', { - get: function() { - var oid = self.id; +/** + * Determine whether this TreeEntry is a file (blob or blob executable). + * + * @param {TreeEntry~isFileCallback} callback + */ +TreeEntry.prototype.isFile = function(callback) { + /** + * @callback TreeEntry~isFileCallback Callback executed after type is determined. + * @param {GitError|null} error An Error or null if successful. + * @param {Boolean|null} content True if the entry is a blob or blob executable, false otherwise. + */ + var self = this; + if (typeof self._cache.fileMode !== 'undefined') { + callback(null, fileModeIsFile(self._cache.fileMode, self)); + return; + } + self.rawEntry.fileMode(function(error, fileMode) { + if (success(error, callback)) { + self._cache.fileMode = fileMode; + callback(null, fileModeIsFile(self._cache.fileMode, self)); + } + }); +}; - return oid.oid.toString( 40 ); - }, - enumerable: true +/** + * Determine whether this Tree Entry is a directory. + * + * @param {TreeEntry~isDirectoryCallback} callback + */ +TreeEntry.prototype.isDirectory = function(callback) { + /** + * @callback TreeEntry~isDirectoryCallback Callback executed after type is determined. + * @param {GitError|null} error An Error or null if successful. + * @param {Boolean|null} content True if the entry is a directory, false otherwise. + */ + var self = this; + if (typeof self._cache.fileMode !== 'undefined') { + callback(null, fileModeIsDirectory(self._cache.fileMode, self)); + return; + } + self.rawEntry.fileMode(function(error, fileMode) { + if (success(error, callback)) { + self._cache.fileMode = fileMode; + callback(null, fileModeIsDirectory(self._cache.fileMode, self)); + } }); +}; - Object.defineProperty( self, 'content', { - get: function() { - if( self.isFile() ) { - var blob = git.blob( self.repo ); +/** + * Retrieve the name for this TreeEntry. + * + * @param {TreeEntry~nameCallback} callback + */ +TreeEntry.prototype.name = function(callback) { + /** + * @callback TreeEntry~nameCallback Callback executed after name is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} name the entry's name. + */ + this.rawEntry.name(function treeEntryName(error, name) { + if (success(error, callback)) { + callback(null, name); + } + }); +}; - self.entry.toObject( self.repo, blob.blob ); +/** + * Retrieve the entry's root path. + * + * @param {TreeEntry~rootCallback} callback + */ +TreeEntry.prototype.root = function(callback) { + /** + * @callback TreeEntry~rootCallback Callback executed after root path is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} root the entry's root path, relative to repository. + */ + this.rawEntry.root(function treeEntryRoot(error, root) { + if (success(error, callback)) { + callback(null, root); + } + }); +}; - return blob.raw; +/** + * Retrieve the path relative to the repository root for this TreeEntry. + * + * @param {TreeEntry~pathCallback} callback + */ +TreeEntry.prototype.path = function(callback) { + /** + * @callback TreeEntry~pathCallback Callback executed after path is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} path the entry's full path relative to repository. + */ + var self = this; + self.root(function treeEntryRoot(error, root) { + if (!success(error, callback)) { + return; + } + self.rawEntry.name(function treeEntryName(error, name) { + if (success(error, callback)) { + callback(null, path.join(root, name)); } - - return null; - }, - enumerable: true + }); }); +}; - self.isFile = function() { - return self.attributes !== 16384; - }; - - self.isDir = function() { - return self.attributes === 16384; - }; - - self.fetchDetails = function(callback) { - console.log('tree fetch details'); - callback(null, self); - }; - - self.tree = function() { - var tree = new git.raw.Tree( self.repo ); - if( tree.error ) { - return git.error( tree.error ); +/** + * Retrieve the TreeEntry's content. + * + * @param {TreeEntry~contentCallback} callback + */ +TreeEntry.prototype.content = function(callback) { + /** + * @callback TreeEntry~contentCallback Callback executed after content is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {String|null} content the entry's content. + */ + this.toBlob(function convertBlob(error, blob) { + if (!success(error, callback)) { + return; } - else { - if( tree.lookup(self.repo, self.id.oid) ) { - return git.error( tree.error ); + blob.content(function blobContent(error, content) { + if (success(error, callback)) { + callback(null, content); } - } + }); + }); +}; - return git.tree( tree ); - }; +/** + * Convert the TreeEntry to a blob. + * + * @param {TreeEntry~blobCallback} callback + */ +TreeEntry.prototype.toBlob = function(callback) { + /** + * @callback TreeEntry~blobCallback Callback executed after blob is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {Blob|null} blob the blob representation of the entry. + */ + var self = this; + self.rawEntry.toBlob(self.rawRepo, function blobCallback(error, rawBlob) { + if (success(error, callback)) { + callback(null, new git.blob(self.rawRepo, rawBlob)); + } + }); +}; - return self; +/** + * Retrieve the TreeEntry's Tree. + * + * @param {TreeEntry~treeCallback} callback + */ +TreeEntry.prototype.tree = function(callback) { + /** + * @callback TreeEntry~treeCallback Callback executed after tree is retrieved. + * @param {GitError|null} error An Error or null if successful. + * @param {Tree|null} tree the entry's tree. + */ + var self = this; + self.oid(function treeEntryOid(error, oid) { + if (!success(error, callback)) { + return; + } + (new git.tree(self.rawRepo)).lookup(oid, function(error, tree) { + if (!success(error, callback)) { + return; + } + callback(null, tree); + }); + }); }; -exports.entry = _TreeEntry; +exports.entry = TreeEntry; diff --git a/lib/utilities.js b/lib/utilities.js new file mode 100755 index 000000000..f7d62b3c0 --- /dev/null +++ b/lib/utilities.js @@ -0,0 +1,31 @@ +var git = require('../'); + +/** + * @namespace + */ +var utilities = { + /** + * Check if error is null, if it is not, convert it to a GitError and call + * the callback. + * + * @param {Object} error + * @param {Function} callback + * + * @return {Boolean} True if the error was null, false otherwise. + */ + success: function(error, callback) { + if (typeof callback !== 'function') { + throw new Error('Callback must be provided'); + } + if (error) { + if (error instanceof git.error) { + callback(error); + } else { + callback(new git.error(error.message, error.code)); + } + return false; + } + return true; + } +}; +exports.success = utilities.success; diff --git a/package.json b/package.json index 025bec00e..314dd6df0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nodegit", "description": "Node.js libgit2 asynchronous native bindings", - "version": "0.0.77", + "version": "0.0.79", "homepage": "https://github.com/tbranyen/nodegit", "keywords": [ "libgit2", diff --git a/src/base.cc b/src/base.cc index 5a73900a2..725303bec 100755 --- a/src/base.cc +++ b/src/base.cc @@ -1,19 +1,21 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "../include/reference.h" -#include "../include/sig.h" +#include "../include/signature.h" #include "../include/error.h" #include "../include/blob.h" #include "../include/repo.h" #include "../include/oid.h" -#include "../include/object.h" #include "../include/commit.h" #include "../include/revwalk.h" #include "../include/tree.h" @@ -27,10 +29,9 @@ extern "C" void init(Handle target) { GitError::Initialize(target); GitReference::Initialize(target); - GitSig::Initialize(target); + GitSignature::Initialize(target); GitBlob::Initialize(target); GitOid::Initialize(target); - GitObject::Initialize(target); GitRepo::Initialize(target); GitCommit::Initialize(target); GitRevWalk::Initialize(target); diff --git a/src/blob.cc b/src/blob.cc index ef2b9b7ac..391e7534d 100755 --- a/src/blob.cc +++ b/src/blob.cc @@ -1,73 +1,49 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ -#include -#include #include -#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "../include/utils.h" #include "../include/repo.h" #include "../include/blob.h" +#include "../include/functions/string.h" +#include "../include/functions/utilities.h" + using namespace v8; using namespace node; void GitBlob::Initialize (Handle target) { HandleScope scope; - Local t = FunctionTemplate::New(New); + Local tpl = FunctionTemplate::New(New); - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Blob")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->SetClassName(String::NewSymbol("Blob")); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawContent", RawContent); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "rawSize", RawSize); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createFromFile", CreateFromFile); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "createFromBuffer", CreateFromBuffer); + NODE_SET_PROTOTYPE_METHOD(tpl, "lookup", Lookup); + NODE_SET_PROTOTYPE_METHOD(tpl, "rawContent", RawContent); + NODE_SET_PROTOTYPE_METHOD(tpl, "free", Free); + NODE_SET_PROTOTYPE_METHOD(tpl, "createFromFile", CreateFromFile); + NODE_SET_PROTOTYPE_METHOD(tpl, "createFromBuffer", CreateFromBuffer); - target->Set(String::NewSymbol("Blob"), constructor_template->GetFunction()); + constructor_template = Persistent::New(tpl->GetFunction()); + target->Set(String::NewSymbol("Blob"), constructor_template); } git_blob* GitBlob::GetValue() { return this->blob; } - void GitBlob::SetValue(git_blob* blob) { this->blob = blob; } -int GitBlob::Lookup(git_repository* repo, const git_oid* id) { - return git_blob_lookup(&this->blob, repo, id); -} - -const void* GitBlob::RawContent() { - return git_blob_rawcontent(this->blob); -} - -int GitBlob::RawSize() { - return git_blob_rawsize(this->blob); -} - -void GitBlob::Close() { - git_blob_free(this->blob); -} - -int CreateFromFile(git_oid* oid, git_repository* repo, const char* path) { - return git_blob_create_fromdisk(oid, repo, path); -} - -int CreateFromBuffer(git_oid* oid, git_repository* repo, const void* buffer, size_t len) { - return git_blob_create_frombuffer(oid, repo, buffer, len); -} - Handle GitBlob::New(const Arguments& args) { HandleScope scope; @@ -76,6 +52,14 @@ Handle GitBlob::New(const Arguments& args) { return scope.Close( args.This() ); } +Handle GitBlob::Free(const Arguments& args) { + HandleScope scope; + + GitBlob* blob = ObjectWrap::Unwrap(args.This()); + git_blob_free(blob->blob); + + return scope.Close( Undefined() ); +} Handle GitBlob::Lookup(const Arguments& args) { HandleScope scope; @@ -92,144 +76,215 @@ Handle GitBlob::Lookup(const Arguments& args) { return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - GitBlob* blob = ObjectWrap::Unwrap(args.This()); - Local callback = Local::Cast(args[2]); - - lookup_request* ar = new lookup_request(); - ar->blob = blob; - ar->repo = ObjectWrap::Unwrap(args[0]->ToObject()); - ar->oid = ObjectWrap::Unwrap(args[1]->ToObject()); - ar->callback = Persistent::New(callback); - - blob->Ref(); + LookupBaton* baton = new LookupBaton; + baton->request.data = baton; + baton->blob = ObjectWrap::Unwrap(args.This()); + baton->blob->Ref(); + baton->rawBlob = baton->blob->GetValue(); + baton->rawRepo = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + baton->rawOid = ObjectWrap::Unwrap(args[1]->ToObject())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[2])); - uv_work_t *req = new uv_work_t; - req->data = ar; - uv_queue_work(uv_default_loop(), req, EIO_Lookup, (uv_after_work_cb)EIO_AfterLookup); + uv_queue_work(uv_default_loop(), &baton->request, LookupWork, (uv_after_work_cb)LookupAfterWork); - return scope.Close( Undefined() ); + return Undefined(); } - -void GitBlob::EIO_Lookup(uv_work_t* req) { - lookup_request* ar = static_cast(req->data); - - git_oid oid = ar->oid->GetValue(); - ar->err = ar->blob->Lookup(ar->repo->GetValue(), &oid); - +void GitBlob::LookupWork(uv_work_t* req) { + LookupBaton* baton = static_cast(req->data); + int returnCode = git_blob_lookup(&baton->rawBlob, baton->rawRepo, &baton->rawOid); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } } +void GitBlob::LookupAfterWork(uv_work_t* req) { + HandleScope scope; + LookupBaton* baton = static_cast(req->data); -void GitBlob::EIO_AfterLookup(uv_work_t* req) { - lookup_request* ar = static_cast(req->data); - delete req; - ar->blob->Unref(); - - Local argv[1]; - argv[0] = Integer::New(ar->err); - - TryCatch try_catch; + if (success(baton->error, baton->callback)) { + baton->blob->SetValue(baton->rawBlob); - ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); + Handle argv[2] = { + Local::New(Null()), + baton->blob->handle_ + }; - if(try_catch.HasCaught()) { - FatalException(try_catch); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } } - - ar->callback.Dispose(); - - delete ar; + delete req; } Handle GitBlob::RawContent(const Arguments& args) { HandleScope scope; - GitBlob* blob = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } - int rawSize = blob->RawSize(); - std::string contents = (const char *)const_cast(blob->RawContent()); + RawContentBaton* baton = new RawContentBaton; + baton->request.data = baton; + baton->rawBlob = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); - int bufferLength = rawSize; - Buffer* buffer = Buffer::New(const_cast(contents.c_str()), bufferLength); + uv_queue_work(uv_default_loop(), &baton->request, RawContentWork, (uv_after_work_cb)RawContentAfterWork); - Local fastBuffer; - MAKE_FAST_BUFFER(buffer, fastBuffer); - - return scope.Close( fastBuffer ); + return Undefined(); } +void GitBlob::RawContentWork(uv_work_t* req) { + RawContentBaton* baton = static_cast(req->data); -Handle GitBlob::RawSize(const Arguments& args) { - HandleScope scope; - - GitBlob* blob = ObjectWrap::Unwrap(args.This()); - - return scope.Close( Integer::New(blob->RawSize()) ); + baton->rawContent = (const char *)const_cast(git_blob_rawcontent(baton->rawBlob)); + baton->rawSize = git_blob_rawsize(baton->rawBlob); } - -Handle GitBlob::Close(const Arguments& args) { +void GitBlob::RawContentAfterWork(uv_work_t* req) { HandleScope scope; + RawContentBaton* baton = static_cast(req->data); - GitBlob* blob = ObjectWrap::Unwrap(args.This()); + Local fastBuffer; + Buffer* buffer = Buffer::New(const_cast(baton->rawContent.c_str()), baton->rawSize); + MAKE_FAST_BUFFER(buffer, fastBuffer); - blob->Close(); + Handle argv[2] = { + Local::New(Null()), + fastBuffer + }; - return scope.Close( Undefined() ); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + delete req; } Handle GitBlob::CreateFromFile(const Arguments& args) { HandleScope scope; - GitBlob* blob = ObjectWrap::Unwrap(args.This()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); - } - - if(args.Length() == 1 || !args[1]->IsObject()) { return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); } - if(args.Length() == 2 || !args[2]->IsString()) { + if(args.Length() == 1 || !args[1]->IsString()) { return ThrowException(Exception::Error(String::New("Path is required and must be an String."))); } - GitOid* oid = ObjectWrap::Unwrap(args[0]->ToObject()); - GitRepo* repo = ObjectWrap::Unwrap(args[1]->ToObject()); + if(args.Length() == 2 || !args[2]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be an Function."))); + } - String::Utf8Value path(args[2]); + CreateFromFileBaton* baton = new CreateFromFileBaton; + baton->request.data = baton; + baton->error = NULL; + baton->blob = ObjectWrap::Unwrap(args.This()); + baton->blob->Ref(); + baton->rawBlob = baton->blob->GetValue(); + baton->rawRepo = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + baton->path = stringArgToString(args[1]->ToString()); + baton->callback = Persistent::New(Local::Cast(args[2])); - git_oid tmp_oid = oid->GetValue(); - int err = blob->CreateFromFile(&tmp_oid, repo->GetValue(), *path); + uv_queue_work(uv_default_loop(), &baton->request, CreateFromFileWork, (uv_after_work_cb)CreateFromFileAfterWork); - return scope.Close( Integer::New(err) ); + return Undefined(); } +void GitBlob::CreateFromFileWork(uv_work_t* req) { + CreateFromFileBaton* baton = static_cast(req->data); + + git_oid* rawOid = NULL; + int returnCode = git_blob_create_fromdisk(rawOid, baton->rawRepo, baton->path.c_str()); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + return; + } -Handle GitBlob::CreateFromBuffer(const Arguments& args) { + returnCode = git_blob_lookup(&baton->rawBlob, baton->rawRepo, rawOid); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } +} +void GitBlob::CreateFromFileAfterWork(uv_work_t* req) { HandleScope scope; + CreateFromFileBaton* baton = static_cast(req->data); - GitBlob* blob = ObjectWrap::Unwrap(args.This()); + baton->blob->SetValue(baton->rawBlob); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + Handle argv[2] = { + Local::New(Null()), + baton->blob->handle_ + }; + + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); } + delete req; +} - if(args.Length() == 1 || !args[1]->IsObject()) { +Handle GitBlob::CreateFromBuffer(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() == 2 || !Buffer::HasInstance(args[2])) { - return ThrowException(Exception::Error(String::New("Buffer is required and must be a Buffer."))); + if(args.Length() == 1 || !Buffer::HasInstance(args[1])) { + return ThrowException(Exception::Error(String::New("Buffer is required and must be an Buffer."))); + } + + if(args.Length() == 2 || !args[2]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be an Function."))); } - GitOid* oid = ObjectWrap::Unwrap(args[0]->ToObject()); - GitRepo* repo = ObjectWrap::Unwrap(args[1]->ToObject()); - Local buffer = args[2]->ToObject(); + CreateFromBufferBaton* baton = new CreateFromBufferBaton; + baton->request.data = baton; + baton->error = NULL; + baton->blob = ObjectWrap::Unwrap(args.This()); + baton->blob->Ref(); + baton->rawBlob = baton->blob->GetValue(); + baton->rawRepo = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + Local buffer = args[1]->ToObject(); + baton->data = Buffer::Data(buffer); + baton->dataLength = Buffer::Length(buffer); + baton->callback = Persistent::New(Local::Cast(args[2])); + + uv_queue_work(uv_default_loop(), &baton->request, CreateFromFileWork, (uv_after_work_cb)CreateFromFileAfterWork); + + return Undefined(); +} +void GitBlob::CreateFromBufferWork(uv_work_t* req) { + CreateFromBufferBaton* baton = static_cast(req->data); - const void* data = Buffer::Data(buffer); - size_t length = Buffer::Length(buffer); + git_oid* rawOid = NULL; + int returnCode = git_blob_create_frombuffer(rawOid, baton->rawRepo, baton->data, baton->dataLength); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } - git_oid tmp_oid = oid->GetValue(); - int err = blob->CreateFromBuffer(&tmp_oid, repo->GetValue(), data, length); + returnCode = git_blob_lookup(&baton->rawBlob, baton->rawRepo, rawOid); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } +} +void GitBlob::CreateFromBufferAfterWork(uv_work_t* req) { + HandleScope scope; + CreateFromBufferBaton* baton = static_cast(req->data); - return scope.Close( Integer::New(err) ); + baton->blob->SetValue(baton->rawBlob); + + Handle argv[2] = { + Local::New(Null()), + baton->blob->handle_ + }; + + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + delete req; } -Persistent GitBlob::constructor_template; +Persistent GitBlob::constructor_template; diff --git a/src/commit.cc b/src/commit.cc index 2c88d001b..d4eecd555 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -1,25 +1,26 @@ /* - * Copyright 2011, Tim Branyen @tbranyen + * Copyright 2013, Tim Branyen @tbranyen * @author Michael Robinson @codeofinterest * * Dual licensed under the MIT and GPL licenses. */ -#include #include #include +#include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "cvv8/v8-convert.hpp" #include "../include/reference.h" -#include "../include/sig.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; @@ -35,13 +36,16 @@ void GitCommit::Initialize(Handle target) { tpl->SetClassName(String::NewSymbol("Commit")); NODE_SET_PROTOTYPE_METHOD(tpl, "lookup", Lookup); - NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close); - + 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, "parent", Parent); - NODE_SET_PROTOTYPE_METHOD(tpl, "parentSync", ParentSync); - NODE_SET_PROTOTYPE_METHOD(tpl, "fetchDetails", FetchDetails); - NODE_SET_PROTOTYPE_METHOD(tpl, "fetchDetailsSync", FetchDetailsSync); + 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); @@ -50,209 +54,31 @@ void GitCommit::Initialize(Handle target) { 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); +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::FetchDetailsSync(const Arguments& args) { +Handle GitCommit::Free(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); + git_commit_free(commit->commit); + commit->commit = NULL; 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; @@ -285,13 +111,11 @@ Handle GitCommit::Lookup(const Arguments& args) { 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()); + int returnCode = git_oid_fromstr(&baton->rawOid, baton->sha.c_str()); if (returnCode != GIT_OK) { baton->error = giterr_last(); return; @@ -299,33 +123,20 @@ void GitCommit::LookupWork(uv_work_t *req) { } baton->rawCommit = NULL; - int returnCode = git_commit_lookup(&baton->rawCommit, baton->repo, &rawOid); + 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 (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 { - + 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()), @@ -341,123 +152,327 @@ void GitCommit::LookupAfterWork(uv_work_t *req) { delete req; } -Handle GitCommit::Close(const Arguments& args) { +Handle GitCommit::Oid(const Arguments& args) { HandleScope scope; - GitCommit *commit = ObjectWrap::Unwrap(args.This()); - commit->Close(); + Local oid = GitOid::constructor_template->NewInstance(); + GitOid *oidInstance = ObjectWrap::Unwrap(oid); + oidInstance->SetValue(*const_cast(ObjectWrap::Unwrap(args.This())->oid)); - return scope.Close(Undefined()); + return scope.Close(oid); } -/** - * @todo asynchronize - */ -Handle GitCommit::Tree(const Arguments& args) { +Handle GitCommit::Message(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."))); + if(args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - GitTree* g_tree = ObjectWrap::Unwrap(args[0]->ToObject()); + MessageBaton* baton = new MessageBaton; + baton->request.data = baton; + baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); - git_tree* tree; - int err = commit->Tree(&tree); - g_tree->SetValue(tree); + uv_queue_work(uv_default_loop(), &baton->request, MessageWork, (uv_after_work_cb)MessageAfterWork); - return scope.Close( Integer::New(err) ); + return Undefined(); } +void GitCommit::MessageWork(uv_work_t* req) { + MessageBaton *baton = static_cast(req->data); -Handle GitCommit::ParentSync(const Arguments& args) { + baton->message = git_commit_message(baton->rawCommit); +} +void GitCommit::MessageAfterWork(uv_work_t* req) { HandleScope scope; + MessageBaton *baton = static_cast(req->data); - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Position must be a Number."))); + 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; +} - GitCommit *commit = ObjectWrap::Unwrap(args.This()); +Handle GitCommit::Time(const Arguments& args) { + HandleScope scope; - git_commit *parentCommitValue ; - int returnCode = git_commit_parent(&parentCommitValue, commit->commit, args[0]->ToInteger()->Value()); + if(args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } - if (returnCode != GIT_OK) { - return ThrowException(Exception::Error(String::New(giterr_last()->message))); + 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."))); } - Local parent = GitCommit::constructor_template->NewInstance(); - GitCommit *parentCommit = ObjectWrap::Unwrap(parent); - parentCommit->SetValue(parentCommitValue); + 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 scope.Close(parent); + return Undefined(); } +void GitCommit::OffsetWork(uv_work_t* req) { + OffsetBaton *baton = static_cast(req->data); -Handle GitCommit::Parent(const Arguments& args) { + 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) + }; - if(args.Length() != 2) { - return ThrowException(Exception::Error(String::New("Position and callback are required"))); + 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[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Position is required and must be a Number."))); + if(args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - if(!args[1]->IsFunction()) { + 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."))); } - ParentBaton* baton = new ParentBaton(); + SignatureBaton* baton = new SignatureBaton; 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])); + baton->rawCommit = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); - uv_queue_work(uv_default_loop(), &baton->request, ParentWork, (uv_after_work_cb)ParentAfterWork); + 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); -void GitCommit::ParentWork(uv_work_t* req) { - ParentBaton* baton = static_cast(req->data); + Local signature = GitSignature::constructor_template->NewInstance(); + GitSignature *signatureInstance = ObjectWrap::Unwrap(signature); + signatureInstance->SetValue(const_cast(baton->rawSignature)); - baton->rawParentCommit = NULL; - int returnCode = git_commit_parent(&baton->rawParentCommit, baton->commit->commit, baton->index); + 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::ParentAfterWork(uv_work_t* req) { +void GitCommit::TreeAfterWork(uv_work_t* req) { HandleScope scope; - ParentBaton* baton = static_cast(req->data); + 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); - if (baton->error) { - Local argv[1] = { - GitError::WrapError(baton->error) + Handle argv[2] = { + Local::New(Null()), + tree }; TryCatch try_catch; - - baton->callback->Call(Context::GetCurrent()->Global(), 1, argv); - + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); if (try_catch.HasCaught()) { node::FatalException(try_catch); } - } else { + } + 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."))); + } - Local parent = GitCommit::constructor_template->NewInstance(); - GitCommit *parentInstance = ObjectWrap::Unwrap(parent); - parentInstance->SetValue(baton->rawParentCommit); + 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()), - parent + cvv8::CastToJS(parents) }; TryCatch try_catch; @@ -466,7 +481,6 @@ void GitCommit::ParentAfterWork(uv_work_t* req) { node::FatalException(try_catch); } } - baton->commit->Unref(); delete req; } diff --git a/src/diff_list.cc b/src/diff_list.cc index 46d14db47..1204f6399 100755 --- a/src/diff_list.cc +++ b/src/diff_list.cc @@ -15,9 +15,11 @@ #include "../include/error.h" #include "../include/functions/string.h" +#include "../include/functions/utilities.h" using namespace v8; using namespace node; +using namespace cvv8; namespace cvv8 { template <> @@ -34,7 +36,7 @@ void GitDiffList::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(tpl, "treeToTree", TreeToTree); NODE_SET_PROTOTYPE_METHOD(tpl, "walk", Walk); - NODE_SET_PROTOTYPE_METHOD(tpl, "close", Close); + NODE_SET_PROTOTYPE_METHOD(tpl, "free", Free); // Add libgit2 delta types to diff_list object Local libgit2DeltaTypes = Object::New(); @@ -114,17 +116,12 @@ Handle GitDiffList::New(const Arguments& args) { return scope.Close(args.This()); } - -void GitDiffList::Close() { - git_diff_list_free(this->diffList); - this->diffList = NULL; -} - -Handle GitDiffList::Close(const Arguments& args) { +Handle GitDiffList::Free(const Arguments& args) { HandleScope scope; GitDiffList *diffList = ObjectWrap::Unwrap(args.This()); - diffList->Close(); + git_diff_list_free(diffList->diffList); + diffList->diffList = NULL; return scope.Close(Undefined()); } @@ -148,7 +145,7 @@ Handle GitDiffList::TreeToTree(const Arguments& args) { return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - TreeToTreeBaton *baton = new TreeToTreeBaton(); + TreeToTreeBaton *baton = new TreeToTreeBaton; baton->request.data = baton; baton->error = NULL; baton->diffList = ObjectWrap::Unwrap(args.This()); @@ -265,7 +262,7 @@ void GitDiffList::TreeToTreeAfterWork(uv_work_t *req) { Handle GitDiffList::Walk(const Arguments& args) { HandleScope scope; - GitDiffList* diffList = ObjectWrap::Unwrap(args.This()); + GitDiffList* diffList = ObjectWrap::Unwrap(args.This()); if (diffList->GetValue() == NULL) { return ThrowException(Exception::Error(String::New("No diff list to Walk."))); @@ -288,7 +285,7 @@ Handle GitDiffList::Walk(const Arguments& args) { } - WalkBaton* baton = new WalkBaton(); + WalkBaton* baton = new WalkBaton; uv_async_init(uv_default_loop(), &baton->asyncFile, WalkWorkSendFile); uv_async_init(uv_default_loop(), &baton->asyncHunk, WalkWorkSendHunk); uv_async_init(uv_default_loop(), &baton->asyncData, WalkWorkSendData); @@ -298,7 +295,7 @@ Handle GitDiffList::Walk(const Arguments& args) { baton->rawDiffList = diffList->GetValue(); diffList->Ref(); - + baton->error = NULL; baton->fileCallback = Persistent::New(Local::Cast(args[0])); baton->hunkCallback = Persistent::New(Local::Cast(args[1])); baton->lineCallback = Persistent::New(Local::Cast(args[2])); @@ -308,7 +305,6 @@ Handle GitDiffList::Walk(const Arguments& args) { return Undefined(); } - void GitDiffList::WalkWork(void *payload) { WalkBaton *baton = static_cast(payload); @@ -326,7 +322,6 @@ void GitDiffList::WalkWork(void *payload) { baton->asyncEnd.data = baton; uv_async_send(&baton->asyncEnd); } - int GitDiffList::WalkWorkFile(const git_diff_delta *delta, float progress, void *payload) { WalkBaton *baton = static_cast(payload); @@ -348,7 +343,7 @@ int GitDiffList::WalkWorkFile(const git_diff_delta *delta, float progress, uv_mutex_lock(&baton->mutex); - GitDiffList::Delta* newDelta = new GitDiffList::Delta(); + Delta* newDelta = new Delta; newDelta->raw = (git_diff_delta*)malloc(sizeof(git_diff_delta)); memcpy(newDelta->raw, delta, sizeof(git_diff_delta)); @@ -358,17 +353,16 @@ int GitDiffList::WalkWorkFile(const git_diff_delta *delta, float progress, baton->fileDeltas[key] = newDelta; uv_mutex_unlock(&baton->mutex); - if ((unsigned int)baton->fileDeltas.size() == (unsigned int)GitDiffList::WALK_DELTA_THRESHHOLD) { + if ((unsigned int)baton->fileDeltas.size() == (unsigned int)GitDiffList::WALK_DELTA_SEND_THRESHHOLD) { + baton->asyncFile.data = baton; uv_async_send(&baton->asyncFile); } return GIT_OK; } - int GitDiffList::WalkWorkHunk(const git_diff_delta *delta, const git_diff_range *range, const char *header, size_t header_len, void *payload) { return GIT_OK; } - int GitDiffList::WalkWorkData(const git_diff_delta *delta, const git_diff_range *range, char line_origin, const char *content, size_t content_len, void *payload) { @@ -393,25 +387,13 @@ int GitDiffList::WalkWorkData(const git_diff_delta *delta, const git_diff_range return GIT_OK; } - void GitDiffList::WalkWorkSendFile(uv_async_t *handle, int status /*UNUSED*/) { HandleScope scope; WalkBaton *baton = static_cast(handle->data); - if (baton->error) { - Local argv[1] = { - GitError::WrapError(baton->error) - }; - - TryCatch try_catch; - baton->fileCallback->Call(Context::GetCurrent()->Global(), 1, argv); - if (try_catch.HasCaught()) { - node::FatalException(try_catch); - } - } else { - - uv_mutex_lock(&baton->mutex); + uv_mutex_lock(&baton->mutex); + if (success(baton->error, baton->fileCallback)) { std::vector > fileDeltasArray; @@ -453,8 +435,12 @@ void GitDiffList::WalkWorkSendFile(uv_async_t *handle, int status /*UNUSED*/) { content->Set(String::New("range"), range); content->Set(String::New("content"), String::New(rawContent->content.c_str())); + // char lineOrigin[2]; + // strncpy(lineOrigin, &rawContent->lineOrigin, 1); char lineOrigin[2]; - strncpy(lineOrigin, &rawContent->lineOrigin, 1); + lineOrigin[0] = rawContent->lineOrigin; + lineOrigin[1] = '\0'; + // std::string lineOrigin(rawContent->lineOrigin); content->Set(String::New("lineOrigin"), String::New(lineOrigin)); content->Set(String::New("contentLength"), Integer::New(rawContent->contentLength)); @@ -469,7 +455,6 @@ void GitDiffList::WalkWorkSendFile(uv_async_t *handle, int status /*UNUSED*/) { baton->fileDeltas.clear(); - uv_mutex_unlock(&baton->mutex); Handle argv[2] = { Local::New(Null()), @@ -482,14 +467,18 @@ void GitDiffList::WalkWorkSendFile(uv_async_t *handle, int status /*UNUSED*/) { node::FatalException(try_catch); } } + uv_mutex_unlock(&baton->mutex); } - void GitDiffList::WalkWorkSendHunk(uv_async_t *handle, int status /*UNUSED*/) { } void GitDiffList::WalkWorkSendData(uv_async_t *handle, int status /*UNUSED*/) { } void GitDiffList::WalkWorkSendEnd(uv_async_t *handle, int status /*UNUSED*/) { WalkBaton *baton = static_cast(handle->data); uv_mutex_destroy(&baton->mutex); + uv_close((uv_handle_t*) &baton->asyncFile, NULL); + uv_close((uv_handle_t*) &baton->asyncHunk, NULL); + uv_close((uv_handle_t*) &baton->asyncData, NULL); + uv_close((uv_handle_t*) &baton->asyncEnd, NULL); Local argv[1]; if (baton->error) { diff --git a/src/error.cc b/src/error.cc index 9a7565607..5ecee568a 100755 --- a/src/error.cc +++ b/src/error.cc @@ -1,5 +1,7 @@ -/* - * Copyright 2011, Tim Branyen @tbranyen +/** + * Copyright (c) 2011, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * * Dual licensed under the MIT and GPL licenses. */ @@ -7,13 +9,14 @@ #include #include "cvv8/v8-convert.hpp" + #include "git2.h" #include "../include/error.h" using namespace v8; -using namespace cvv8; using namespace node; +using namespace cvv8; /** * Copied from libgit2/include/errors.h, to allow exporting to JS @@ -49,33 +52,33 @@ void GitError::Initialize (Handle target) { // Add libgit2 error codes to error object Local libgit2Errors = Object::New(); - libgit2Errors->Set(String::NewSymbol("GITERR_NOMEMORY"), cvv8::CastToJS(GITERR_NOMEMORY), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_OS"), cvv8::CastToJS(GITERR_OS), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_INVALID"), cvv8::CastToJS(GITERR_INVALID), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_REFERENCE"), cvv8::CastToJS(GITERR_REFERENCE), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_ZLIB"), cvv8::CastToJS(GITERR_ZLIB), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_REPOSITORY"), cvv8::CastToJS(GITERR_REPOSITORY), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_CONFIG"), cvv8::CastToJS(GITERR_CONFIG), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_REGEX"), cvv8::CastToJS(GITERR_REGEX), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_ODB"), cvv8::CastToJS(GITERR_ODB), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_INDEX"), cvv8::CastToJS(GITERR_INDEX), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_OBJECT"), cvv8::CastToJS(GITERR_OBJECT), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_NET"), cvv8::CastToJS(GITERR_NET), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_TAG"), cvv8::CastToJS(GITERR_TAG), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_TREE"), cvv8::CastToJS(GITERR_TREE), ReadOnly); - libgit2Errors->Set(String::NewSymbol("GITERR_INDEXER"), cvv8::CastToJS(GITERR_INDEXER), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_NOMEMORY"), CastToJS(GITERR_NOMEMORY), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_OS"), CastToJS(GITERR_OS), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_INVALID"), CastToJS(GITERR_INVALID), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_REFERENCE"), CastToJS(GITERR_REFERENCE), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_ZLIB"), CastToJS(GITERR_ZLIB), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_REPOSITORY"), CastToJS(GITERR_REPOSITORY), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_CONFIG"), CastToJS(GITERR_CONFIG), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_REGEX"), CastToJS(GITERR_REGEX), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_ODB"), CastToJS(GITERR_ODB), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_INDEX"), CastToJS(GITERR_INDEX), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_OBJECT"), CastToJS(GITERR_OBJECT), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_NET"), CastToJS(GITERR_NET), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_TAG"), CastToJS(GITERR_TAG), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_TREE"), CastToJS(GITERR_TREE), ReadOnly); + libgit2Errors->Set(String::NewSymbol("GITERR_INDEXER"), CastToJS(GITERR_INDEXER), ReadOnly); // Add libgit2 error codes to error object Local libgit2ReturnCodes = Object::New(); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_OK"), cvv8::CastToJS(_GIT_OK), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_ERROR"), cvv8::CastToJS(_GIT_ERROR), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_ENOTFOUND"), cvv8::CastToJS(_GIT_ENOTFOUND), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_EEXISTS"), cvv8::CastToJS(_GIT_EEXISTS), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_EAMBIGUOUS"), cvv8::CastToJS(_GIT_EAMBIGUOUS), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_EBUFS"), cvv8::CastToJS(_GIT_EBUFS), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_PASSTHROUGH"), cvv8::CastToJS(_GIT_PASSTHROUGH), ReadOnly); - libgit2ReturnCodes->Set(String::NewSymbol("GIT_ITEROVER"), cvv8::CastToJS(_GIT_ITEROVER), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_OK"), CastToJS(_GIT_OK), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_ERROR"), CastToJS(_GIT_ERROR), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_ENOTFOUND"), CastToJS(_GIT_ENOTFOUND), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_EEXISTS"), CastToJS(_GIT_EEXISTS), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_EAMBIGUOUS"), CastToJS(_GIT_EAMBIGUOUS), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_EBUFS"), CastToJS(_GIT_EBUFS), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_PASSTHROUGH"), CastToJS(_GIT_PASSTHROUGH), ReadOnly); + libgit2ReturnCodes->Set(String::NewSymbol("GIT_ITEROVER"), CastToJS(_GIT_ITEROVER), ReadOnly); constructor_template = Persistent::New(tpl->GetFunction()); constructor_template->Set(String::NewSymbol("codes"), libgit2Errors, ReadOnly); @@ -90,7 +93,7 @@ Local GitError::WrapError(const git_error* error) { Local gitError = GitError::constructor_template->NewInstance(); Local stackTrace = StackTrace::CurrentStackTrace(10); - gitError->Set(String::NewSymbol("stackTrace"), cvv8::CastToJS(stackTrace->AsArray())); + gitError->Set(String::NewSymbol("stackTrace"), CastToJS(stackTrace->AsArray())); gitError->Set(String::NewSymbol("message"), String::New(error->message)); gitError->Set(String::NewSymbol("code"), Integer::New(error->klass)); diff --git a/src/functions/utilities.cc b/src/functions/utilities.cc new file mode 100644 index 000000000..ec5904dcc --- /dev/null +++ b/src/functions/utilities.cc @@ -0,0 +1,21 @@ +#include "../../include/functions/utilities.h" + +using namespace v8; + +bool success(const git_error* error, Persistent callback) { + HandleScope scope; + + if (error) { + Local argv[1] = { + GitError::WrapError(error) + }; + + TryCatch try_catch; + callback->Call(Context::GetCurrent()->Global(), 1, argv); + if (try_catch.HasCaught()) { + FatalException(try_catch); + } + return false; + } + return true; +} diff --git a/src/object.cc b/src/object.cc deleted file mode 100755 index 9a15102ec..000000000 --- a/src/object.cc +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -#include "../include/object.h" -#include "../include/repo.h" -#include "../include/oid.h" - -using namespace v8; -using namespace node; - -void GitObject::Initialize (Handle target) { - HandleScope scope; - - Local t = FunctionTemplate::New(New); - - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("Object")); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "type", Type); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "owner", Owner); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "type2String", Type2String); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "string2Type", String2Type); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "typeIsLoose", TypeIsLoose); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "size", Size); - - Local type = Object::New(); - - type->Set(String::New("ANY"), Integer::New(-2)); - type->Set(String::New("BAD"), Integer::New(-1)); - type->Set(String::New("_EXT1"), Integer::New(0)); - type->Set(String::New("COMMIT"), Integer::New(1)); - type->Set(String::New("TREE"), Integer::New(2)); - type->Set(String::New("BLOB"), Integer::New(3)); - type->Set(String::New("TAG"), Integer::New(4)); - type->Set(String::New("_EXT2"), Integer::New(5)); - type->Set(String::New("OFS_DELTA"), Integer::New(6)); - type->Set(String::New("REF_DELTA"), Integer::New(7)); - - constructor_template->Set(String::New("type"), type); - - target->Set(String::NewSymbol("Object"), constructor_template->GetFunction()); -} - -git_object* GitObject::GetValue() { - return this->obj; -} - -void GitObject::SetValue(git_object* obj) { - this->obj = obj; -} - -const git_oid* GitObject::Id() { - return git_object_id(this->obj); -} - -git_otype GitObject::Type() { - return git_object_type(this->obj); -} - -git_repository* GitObject::Owner() { - return git_object_owner(this->obj); -} - -const char* GitObject::Type2String(git_otype type) { - return git_object_type2string(type); -} - -git_otype GitObject::String2Type(const char* type) { - return git_object_string2type(type); -} - -int GitObject::TypeIsLoose(git_otype type) { - return git_object_typeisloose(type); -} - -size_t GitObject::Size(git_otype type) { - return git_object__size(type); -} - -Handle GitObject::New(const Arguments& args) { - HandleScope scope; - - GitObject *obj = new GitObject(); - - obj->Wrap(args.This()); - - return scope.Close( args.This() ); -} - -Handle GitObject::Id(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); - } - - GitOid *oid = ObjectWrap::Unwrap(args[0]->ToObject()); - - oid->SetValue(*const_cast(obj->Id())); - - return scope.Close( Undefined() ); -} - -Handle GitObject::Type(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - return scope.Close( Integer::New(obj->Type()) ); -} - -Handle GitObject::Owner(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); - } - - GitRepo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - - repo->SetValue(obj->Owner()); - - return scope.Close( Undefined() ); -} - -Handle GitObject::Type2String(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Type is required and must be a Number."))); - } - - git_otype type = (git_otype)args[0]->ToInteger()->Value(); - - return scope.Close( String::New(obj->Type2String(type)) ); -} - -Handle GitObject::String2Type(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(Exception::Error(String::New("Type is required and must be a String."))); - } - - String::Utf8Value type(args[0]); - - return scope.Close( Integer::New(obj->String2Type(*type)) ); -} - -Handle GitObject::TypeIsLoose(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Type is required and must be a Number."))); - } - - git_otype type = (git_otype)args[0]->ToInteger()->Value(); - - return scope.Close( Integer::New(obj->TypeIsLoose(type)) ); -} - -Handle GitObject::Size(const Arguments& args) { - HandleScope scope; - - GitObject *obj = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Type is required and must be a Number."))); - } - - git_otype type = (git_otype)args[0]->ToInteger()->Value(); - - return scope.Close( Integer::New(obj->Size(type)) ); -} - -Persistent GitObject::constructor_template; diff --git a/src/oid.cc b/src/oid.cc index 016246d38..fc68b7dea 100755 --- a/src/oid.cc +++ b/src/oid.cc @@ -1,14 +1,21 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, 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 "git2.h" #include "../include/oid.h" +#include "../include/functions/utilities.h" +#include "../include/functions/string.h" + using namespace v8; using namespace node; @@ -20,14 +27,8 @@ void GitOid::Initialize(Handle target) { tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->SetClassName(String::NewSymbol("Oid")); - NODE_SET_PROTOTYPE_METHOD(tpl, "mkstr", Mkstr); - NODE_SET_PROTOTYPE_METHOD(tpl, "mkraw", Mkraw); - NODE_SET_PROTOTYPE_METHOD(tpl, "fmt", Fmt); - NODE_SET_PROTOTYPE_METHOD(tpl, "pathFmt", PathFmt); - NODE_SET_PROTOTYPE_METHOD(tpl, "allocFmt", AllocFmt); - NODE_SET_PROTOTYPE_METHOD(tpl, "toString", ToString); - NODE_SET_PROTOTYPE_METHOD(tpl, "cpy", Cpy); - NODE_SET_PROTOTYPE_METHOD(tpl, "cmp", Cmp); + NODE_SET_PROTOTYPE_METHOD(tpl, "sha", Sha); + NODE_SET_PROTOTYPE_METHOD(tpl, "fromString", FromString); constructor_template = Persistent::New(tpl->GetFunction()); target->Set(String::NewSymbol("Oid"), constructor_template); @@ -36,43 +37,10 @@ void GitOid::Initialize(Handle target) { git_oid GitOid::GetValue() { return this->oid; } - void GitOid::SetValue(git_oid oid) { this->oid = oid; } -int GitOid::Mkstr(const char* id) { - return git_oid_fromstr(&this->oid, id); -} - -void GitOid::Mkraw(const unsigned char* raw) { - git_oid_fromraw(&this->oid, raw); -} - -void GitOid::Fmt(char* buffer) { - git_oid_fmt(buffer, &this->oid); -} - -void GitOid::PathFmt(char* str) { - git_oid_pathfmt(str, &this->oid); -} - -char* GitOid::AllocFmt() { - return git_oid_allocfmt(&this->oid); -} - -char* GitOid::ToString(char* buffer, size_t bufferSize) { - return git_oid_tostr(buffer, bufferSize, &this->oid); -} - -void GitOid::Cpy(git_oid* out) { - git_oid_cpy(out, &this->oid); -} - -int GitOid::Cmp(const git_oid* a, const git_oid* b) { - return git_oid_cmp(a, b); -} - Handle GitOid::New(const Arguments& args) { HandleScope scope; @@ -82,117 +50,70 @@ Handle GitOid::New(const Arguments& args) { return scope.Close(args.This()); } -Handle GitOid::Mkstr(const Arguments& args) { +Handle GitOid::Sha(const Arguments& args) { HandleScope scope; GitOid* oid = ObjectWrap::Unwrap(args.This()); - if(args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(Exception::Error(String::New("Object id is required and must be a hex formatted String."))); - } - - String::Utf8Value id(args[0]); + char sha[GIT_OID_HEXSZ + 1]; + sha[GIT_OID_HEXSZ] = '\0'; + git_oid rawOid = oid->GetValue(); + git_oid_fmt(sha, const_cast(&rawOid)); - return scope.Close(Integer::New(oid->Mkstr(*id))); + return scope.Close(String::New(sha)); } -Handle GitOid::Mkraw(const Arguments& args) { +Handle GitOid::FromString(const Arguments& args) { HandleScope scope; - GitOid* oid = ObjectWrap::Unwrap(args.This()); - if(args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(Exception::Error(String::New("Raw object id is required."))); + return ThrowException(Exception::Error(String::New("String is required and must be a String."))); } - String::Utf8Value raw(args[0]); - oid->Mkraw((const unsigned char*)*raw); - - return scope.Close( args.This() ); -} - -Handle GitOid::Fmt(const Arguments& args) { - HandleScope scope; - - GitOid *oid = ObjectWrap::Unwrap(args.This()); - - char buffer[40]; - oid->Fmt(buffer); - - return scope.Close(String::New(buffer)); -} - -Handle GitOid::PathFmt(const Arguments& args) { - HandleScope scope; - - GitOid *oid = ObjectWrap::Unwrap(args.This()); - - char buffer[41]; - oid->PathFmt(buffer); - - return scope.Close( String::New(buffer) ); -} + if(args.Length() == 1 || !args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } -Handle GitOid::AllocFmt(const Arguments& args) { - HandleScope scope; + FromStringBaton* baton = new FromStringBaton; + baton->request.data = baton; + baton->error = NULL; + baton->oid = ObjectWrap::Unwrap(args.This()); + baton->rawOid = baton->oid->GetValue(); + baton->fromString = stringArgToString(args[0]->ToString()); + baton->callback = Persistent::New(Local::Cast(args[1])); - GitOid *oid = ObjectWrap::Unwrap(args.This()); + uv_queue_work(uv_default_loop(), &baton->request, FromStringWork, (uv_after_work_cb)FromStringAfterWork); - return scope.Close( String::New(oid->AllocFmt()) ); + return Undefined(); } +void GitOid::FromStringWork(uv_work_t* req) { + FromStringBaton *baton = static_cast(req->data); -Handle GitOid::ToString(const Arguments& args) { - HandleScope scope; - - GitOid *oid = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Length argument is required and must be a Number."))); + int returnCode = git_oid_fromstr(&baton->rawOid, baton->fromString.c_str()); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); } - - char buffer[Int32::Cast(*args[0])->Value()+1]; - oid->ToString(buffer, sizeof(buffer)); - - return scope.Close( String::New(buffer) ); } - -Handle GitOid::Cpy(const Arguments& args) { +void GitOid::FromStringAfterWork(uv_work_t* req) { HandleScope scope; + FromStringBaton *baton = static_cast(req->data); - GitOid *oid = ObjectWrap::Unwrap(args.This()); - - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object."))); - } - - GitOid *clone = ObjectWrap::Unwrap(args[0]->ToObject()); - - git_oid *out = NULL; - oid->Cpy(out); - clone->SetValue(*out); - - return scope.Close( Undefined() ); -} - -Handle GitOid::Cmp(const Arguments& args) { - HandleScope scope; + if (success(baton->error, baton->callback)) { - // GitOid *oid = ObjectWrap::Unwrap(args.This()); + baton->oid->SetValue(baton->rawOid); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object."))); - } + Handle argv[2] = { + Local::New(Null()), + baton->oid->handle_ + }; - if(args.Length() == 1 || !args[1]->IsObject()) { - return ThrowException(Exception::Error(String::New("GitOid argument is required and must be a Object."))); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } } - - // GitOid* a = ObjectWrap::Unwrap(args[0]->ToObject()); - // GitOid* b = ObjectWrap::Unwrap(args[1]->ToObject()); - - //int cmp = oid->Cmp(&a->GetValue(), &b->GetValue()); - int cmp = 0; - - return scope.Close( Integer::New(cmp) ); + delete req; } + Persistent GitOid::constructor_template; diff --git a/src/reference.cc b/src/reference.cc index e9c72b403..a80628ca9 100755 --- a/src/reference.cc +++ b/src/reference.cc @@ -9,7 +9,7 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "../include/repo.h" #include "../include/reference.h" @@ -17,6 +17,7 @@ #include "../include/error.h" #include "../include/functions/string.h" +#include "../include/functions/utilities.h" using namespace v8; using namespace node; @@ -39,7 +40,6 @@ void GitReference::Initialize(Handle target) { git_reference* GitReference::GetValue() { return this->ref; } - void GitReference::SetValue(git_reference *ref) { this->ref = ref; } @@ -72,45 +72,32 @@ Handle GitReference::Oid(const Arguments& args) { return Undefined(); } - void GitReference::OidWork(uv_work_t* req) { OidBaton *baton = static_cast(req->data); git_ref_t referenceType = git_reference_type(baton->rawRef); if (referenceType == GIT_REF_INVALID) { - printf("invalid\n"); giterr_set_str(GITERR_INVALID, "Invalid reference type"); baton->error = giterr_last(); return; } if (referenceType == GIT_REF_SYMBOLIC) { - printf("symbolic\n"); int returnCode = git_reference_resolve(&baton->rawRef, baton->rawRef); if (returnCode != GIT_OK) { baton->error = giterr_last(); return; } } + baton->rawOid = git_reference_target(baton->rawRef); } void GitReference::OidAfterWork(uv_work_t* req) { HandleScope scope; OidBaton *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 { - + if (success(baton->error, baton->callback)) { Handle oid = GitOid::constructor_template->NewInstance(); GitOid *oidInstance = ObjectWrap::Unwrap(oid); oidInstance->SetValue(*const_cast(baton->rawOid)); @@ -158,7 +145,6 @@ Handle GitReference::Lookup(const Arguments& args) { return Undefined(); } - void GitReference::LookupWork(uv_work_t *req) { LookupBaton *baton = static_cast(req->data); @@ -168,23 +154,11 @@ void GitReference::LookupWork(uv_work_t *req) { baton->error = giterr_last(); } } - void GitReference::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 { - + if (success(baton->error, baton->callback)) { baton->ref->SetValue(baton->rawRef); Handle argv[2] = { diff --git a/src/repo.cc b/src/repo.cc index b1d6d5db9..6644803a4 100755 --- a/src/repo.cc +++ b/src/repo.cc @@ -8,9 +8,8 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" -#include "../include/object.h" #include "../include/repo.h" #include "../include/commit.h" #include "../include/error.h" @@ -39,15 +38,10 @@ void GitRepo::Initialize(Handle target) { git_repository* GitRepo::GetValue() { return this->repo; } - void GitRepo::SetValue(git_repository* repo) { this->repo = repo; } -void GitRepo::Free() { - git_repository_free(this->repo); -} - Handle GitRepo::New(const Arguments& args) { HandleScope scope; @@ -57,6 +51,16 @@ Handle GitRepo::New(const Arguments& args) { return scope.Close(args.This()); } +Handle GitRepo::Free(const Arguments& args) { + HandleScope scope; + + GitRepo *repo = ObjectWrap::Unwrap(args.This()); + git_repository_free(repo->repo); + + return scope.Close( Undefined() ); +} + + Handle GitRepo::Open(const Arguments& args) { HandleScope scope; @@ -125,20 +129,9 @@ void GitRepo::OpenAfterWork(uv_work_t *req) { baton->repo->Unref(); } -Handle GitRepo::Free(const Arguments& args) { - HandleScope scope; - - GitRepo *repo = ObjectWrap::Unwrap(args.This()); - - repo->Free(); - - return scope.Close( Undefined() ); -} - Handle GitRepo::Init(const Arguments& args) { HandleScope scope; - if(args.Length() == 0 || !args[0]->IsString()) { return ThrowException(Exception::Error(String::New("path is required and must be a String."))); } @@ -151,7 +144,7 @@ Handle GitRepo::Init(const Arguments& args) { return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - InitBaton *baton = new InitBaton(); + InitBaton *baton = new InitBaton; baton->request.data = baton; baton->error = NULL; baton->repo = ObjectWrap::Unwrap(args.This()); diff --git a/src/revwalk.cc b/src/revwalk.cc index 23a9d96c6..c8817bfde 100755 --- a/src/revwalk.cc +++ b/src/revwalk.cc @@ -8,94 +8,142 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "../include/revwalk.h" #include "../include/repo.h" #include "../include/commit.h" #include "../include/error.h" +#include "../include/functions/utilities.h" + using namespace v8; using namespace node; void GitRevWalk::Initialize(Handle target) { HandleScope scope; - Local t = FunctionTemplate::New(New); + Local tpl = FunctionTemplate::New(New); - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(1); - constructor_template->SetClassName(String::NewSymbol("RevWalk")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->SetClassName(String::NewSymbol("RevWalk")); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "reset", Reset); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "push", Push); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "next", Next); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "repository", Repository); + NODE_SET_PROTOTYPE_METHOD(tpl, "allocate", Allocate); + NODE_SET_PROTOTYPE_METHOD(tpl, "reset", Reset); + NODE_SET_PROTOTYPE_METHOD(tpl, "push", Push); + NODE_SET_PROTOTYPE_METHOD(tpl, "next", Next); + NODE_SET_PROTOTYPE_METHOD(tpl, "free", Free); - Local sort = Object::New(); + // Local sort = Object::New(); - sort->Set(String::New("NONE"), Integer::New(0)); - sort->Set(String::New("TOPOLOGICAL"), Integer::New(1)); - sort->Set(String::New("TIME"), Integer::New(2)); - sort->Set(String::New("REVERSE"), Integer::New(4)); + // sort->Set(String::New("NONE"), Integer::New(0)); + // sort->Set(String::New("TOPOLOGICAL"), Integer::New(1)); + // sort->Set(String::New("TIME"), Integer::New(2)); + // sort->Set(String::New("REVERSE"), Integer::New(4)); - constructor_template->Set(String::New("sort"), sort); + // tpl->Set(String::New("sort"), sort); - target->Set(String::NewSymbol("RevWalk"), constructor_template->GetFunction()); + constructor_template = Persistent::New(tpl->GetFunction()); + target->Set(String::NewSymbol("RevWalk"), constructor_template); } git_revwalk* GitRevWalk::GetValue() { return this->revwalk; } - void GitRevWalk::SetValue(git_revwalk* revwalk) { this->revwalk = revwalk; } - -int GitRevWalk::New(git_repository* repo) { +git_repository* GitRevWalk::GetRepo() { + return this->repo; +} +void GitRevWalk::SetRepo(git_repository* repo) { this->repo = repo; - - return git_revwalk_new(&this->revwalk, this->repo); } -void GitRevWalk::Reset() { - git_revwalk_reset(this->revwalk); -} +Handle GitRevWalk::Free(const Arguments& args) { + HandleScope scope; -void GitRevWalk::Free() { - git_revwalk_free(this->revwalk); -} + GitRevWalk *revwalk = ObjectWrap::Unwrap(args.This()); + + git_revwalk_free(revwalk->revwalk); -git_repository* GitRevWalk::Repository() { - return git_revwalk_repository(this->revwalk); + return Undefined(); } Handle GitRevWalk::New(const Arguments& args) { HandleScope scope; GitRevWalk *revwalk = new GitRevWalk(); - if(args.Length() == 0 || !args[0]->IsObject()) { return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); } GitRepo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - revwalk->New(repo->GetValue()); + revwalk->SetRepo(repo->GetValue()); revwalk->Wrap(args.This()); - return scope.Close( args.This() ); + return scope.Close(args.This()); } Handle GitRevWalk::Reset(const Arguments& args) { HandleScope scope; GitRevWalk *revwalk = ObjectWrap::Unwrap(args.This()); + git_revwalk_reset(revwalk->revwalk); + + return Undefined(); +} - revwalk->Reset(); +Handle GitRevWalk::Allocate(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."))); + } + + AllocateBaton *baton = new AllocateBaton; + baton->request.data = baton; + baton->error = NULL; + baton->revwalk = ObjectWrap::Unwrap(args.This()); + baton->rawRevwalk = NULL; + baton->rawRepo = baton->revwalk->GetRepo(); + baton->callback = Persistent::New(Local::Cast(args[0])); + + uv_queue_work(uv_default_loop(), &baton->request, AllocateWork, (uv_after_work_cb)AllocateAfterWork); + + return Undefined(); +} +void GitRevWalk::AllocateWork(uv_work_t *req) { + AllocateBaton *baton = static_cast(req->data); + + int returnCode = git_revwalk_new(&baton->rawRevwalk, baton->rawRepo); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } +} + +void GitRevWalk::AllocateAfterWork(uv_work_t *req) { + HandleScope scope; + AllocateBaton *baton = static_cast(req->data); + + + if (success(baton->error, baton->callback)) { + + baton->revwalk->SetValue(baton->rawRevwalk); + + Handle argv[2] = { + Local::New(Null()), + baton->revwalk->handle_ + }; - return scope.Close( Undefined() ); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + delete req; } Handle GitRevWalk::Push(const Arguments& args) { @@ -109,29 +157,28 @@ Handle GitRevWalk::Push(const Arguments& args) { return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - PushBaton* baton = new PushBaton(); + PushBaton* baton = new PushBaton; baton->request.data = baton; - baton->revwalk = ObjectWrap::Unwrap(args.This())->GetValue(); - baton->oid = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + baton->error = NULL; + baton->rawRevwalk = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->rawOid = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); baton->callback = Persistent::New(Local::Cast(args[1])); uv_queue_work(uv_default_loop(), &baton->request, PushWork, (uv_after_work_cb)PushAfterWork); return Undefined(); } - void GitRevWalk::PushWork(uv_work_t *req) { PushBaton *baton = static_cast(req->data); - git_revwalk_sorting(baton->revwalk, GIT_SORT_TIME | GIT_SORT_REVERSE); + git_revwalk_sorting(baton->rawRevwalk, GIT_SORT_TIME | GIT_SORT_REVERSE); - int returnCode = git_revwalk_push(baton->revwalk, &baton->oid); + int returnCode = git_revwalk_push(baton->rawRevwalk, &baton->rawOid); if (returnCode) { baton->error = giterr_last(); } } - void GitRevWalk::PushAfterWork(uv_work_t *req) { HandleScope scope; PushBaton *baton = static_cast(req->data); @@ -158,10 +205,11 @@ Handle GitRevWalk::Next(const Arguments& args) { return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - NextBaton* baton = new NextBaton(); + NextBaton* baton = new NextBaton; baton->request.data = baton; - baton->revwalk = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->error = NULL; + baton->rawRevwalk = ObjectWrap::Unwrap(args.This())->GetValue(); baton->walkOver = false; baton->callback = Persistent::New(Local::Cast(args[0])); @@ -169,42 +217,27 @@ Handle GitRevWalk::Next(const Arguments& args) { return Undefined(); } - void GitRevWalk::NextWork(uv_work_t *req) { NextBaton *baton = static_cast(req->data); - // baton->oid = NULL; - int returnCode = git_revwalk_next(&baton->oid, baton->revwalk); + int returnCode = git_revwalk_next(&baton->rawOid, baton->rawRevwalk); if (returnCode != GIT_OK) { if (returnCode == GIT_ITEROVER) { baton->walkOver = true; } else { + git_revwalk_reset(baton->rawRevwalk); baton->error = giterr_last(); } } } - void GitRevWalk::NextAfterWork(uv_work_t *req) { HandleScope scope; NextBaton *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 { - + if (success(baton->error, baton->callback)) { Local oid = GitOid::constructor_template->NewInstance(); GitOid *oidInstance = ObjectWrap::Unwrap(oid); - oidInstance->SetValue(baton->oid); + oidInstance->SetValue(baton->rawOid); Local argv[3] = { Local::New(Null()), @@ -213,9 +246,7 @@ void GitRevWalk::NextAfterWork(uv_work_t *req) { }; TryCatch try_catch; - baton->callback->Call(Context::GetCurrent()->Global(), 3, argv); - if(try_catch.HasCaught()) { FatalException(try_catch); } @@ -223,28 +254,4 @@ void GitRevWalk::NextAfterWork(uv_work_t *req) { delete req; } -Handle GitRevWalk::Free(const Arguments& args) { - HandleScope scope; - - GitRevWalk *revwalk = ObjectWrap::Unwrap(args.This()); - - revwalk->Free(); - - return scope.Close( Undefined() ); -} - -Handle GitRevWalk::Repository(const Arguments& args) { - HandleScope scope; - - GitRevWalk *revwalk = new GitRevWalk(); - - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); - } - - GitRepo *repo = ObjectWrap::Unwrap(args[0]->ToObject()); - repo->SetValue(revwalk->Repository()); - - return scope.Close( Undefined() ); -} -Persistent GitRevWalk::constructor_template; +Persistent GitRevWalk::constructor_template; diff --git a/src/sig.cc b/src/sig.cc deleted file mode 100755 index 0b33e1089..000000000 --- a/src/sig.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ - -#include -#include - -#include "../vendor/libgit2/include/git2.h" - -#include "../include/repo.h" -#include "../include/sig.h" - -using namespace v8; -using namespace node; - -void GitSig::Initialize (Handle target) { - HandleScope scope; - - Local t = FunctionTemplate::New(New); - - constructor_template = Persistent::New(t); - constructor_template->InstanceTemplate()->SetInternalFieldCount(3); - constructor_template->SetClassName(String::NewSymbol("Sig")); - - NODE_SET_PROTOTYPE_METHOD(constructor_template, "dup", Dup); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "free", Free); - - // FIXME: This is an irresponsible way to accomplish fetching properties from the struct - NODE_SET_PROTOTYPE_METHOD(constructor_template, "name", Name); - NODE_SET_PROTOTYPE_METHOD(constructor_template, "email", Email); - - target->Set(String::NewSymbol("Sig"), constructor_template->GetFunction()); -} - -git_signature* GitSig::GetValue() { - return this->sig; -} - -void GitSig::SetValue(git_signature* sig) { - this->sig = sig; - this->name = sig->name; - this->email = sig->email; -} - -void GitSig::New(const char *name, const char *email, time_t time, int offset) { - git_signature_new(&this->sig, name, email, time, offset); - //this->sig = git_signature_new(name, email, time, offset); -} - -git_signature* GitSig::Dup() { - return git_signature_dup(this->sig); -} - -void GitSig::Free() { - git_signature_free(this->sig); -} - -char* GitSig::Name() { - return this->name; -} - -char* GitSig::Email() { - return this->email; -} - -Handle GitSig::New(const Arguments& args) { - HandleScope scope; - - GitSig *sig = new GitSig(); - sig->Wrap(args.This()); - - return scope.Close( args.This() ); -} - -Handle GitSig::Dup(const Arguments& args) { - HandleScope scope; - - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("GitSignature is required and must be an Object."))); - } - - GitSig* sig = ObjectWrap::Unwrap(args[0]->ToObject()); - sig->SetValue(sig->Dup()); - - return scope.Close( Undefined() ); -} - -Handle GitSig::Free(const Arguments& args) { - HandleScope scope; - - GitSig *sig = ObjectWrap::Unwrap(args.This()); - sig->Free(); - - return scope.Close( Undefined() ); -} - -Handle GitSig::Name(const Arguments& args) { - HandleScope scope; - - GitSig *sig = ObjectWrap::Unwrap(args.This()); - - return scope.Close( String::New(sig->Name()) ); -} - -Handle GitSig::Email(const Arguments& args) { - HandleScope scope; - - GitSig *sig = ObjectWrap::Unwrap(args.This()); - - return scope.Close( String::New(sig->Email()) ); -} -Persistent GitSig::constructor_template; diff --git a/src/signature.cc b/src/signature.cc new file mode 100755 index 000000000..49b775403 --- /dev/null +++ b/src/signature.cc @@ -0,0 +1,90 @@ +/* + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ + +#include +#include + +#include "git2.h" + +#include "../include/repo.h" +#include "../include/signature.h" + +using namespace v8; +using namespace node; + +void GitSignature::Initialize(Handle target) { + Local tpl = FunctionTemplate::New(New); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->SetClassName(String::NewSymbol("Signature")); + + NODE_SET_PROTOTYPE_METHOD(tpl, "duplicate", Duplicate); + NODE_SET_PROTOTYPE_METHOD(tpl, "name", Name); + NODE_SET_PROTOTYPE_METHOD(tpl, "email", Email); + + NODE_SET_PROTOTYPE_METHOD(tpl, "free", Free); + + constructor_template = Persistent::New(tpl->GetFunction()); + target->Set(String::NewSymbol("Signature"), constructor_template); +} + +git_signature* GitSignature::GetValue() { + return this->signature; +} + +void GitSignature::SetValue(git_signature* signature) { + this->signature = signature; +} + +Handle GitSignature::New(const Arguments& args) { + HandleScope scope; + + GitSignature *signature = new GitSignature(); + signature->Wrap(args.This()); + + return scope.Close(args.This()); +} + +Handle GitSignature::Free(const Arguments& args) { + HandleScope scope; + + GitSignature *signature = ObjectWrap::Unwrap(args.This()); + git_signature_free(signature->signature); + signature->signature = NULL; + + return Undefined(); +} + +Handle GitSignature::Duplicate(const Arguments& args) { + HandleScope scope; + + Local duplicateSignature = GitSignature::constructor_template->NewInstance(); + GitSignature *duplicateSignatureInstance = ObjectWrap::Unwrap(duplicateSignature); + + GitSignature* signature = ObjectWrap::Unwrap(args.This()); + duplicateSignatureInstance->SetValue(git_signature_dup(signature->GetValue())); + + return duplicateSignature; +} + +Handle GitSignature::Name(const Arguments& args) { + HandleScope scope; + + GitSignature *signature = ObjectWrap::Unwrap(args.This()); + + return scope.Close(String::New(signature->GetValue()->name)); +} + +Handle GitSignature::Email(const Arguments& args) { + HandleScope scope; + + GitSignature *signature = ObjectWrap::Unwrap(args.This()); + + return scope.Close(String::New(signature->GetValue()->email)); +} + +Persistent GitSignature::constructor_template; diff --git a/src/threads.cc b/src/threads.cc index 14c4b7f1c..847e38e3a 100755 --- a/src/threads.cc +++ b/src/threads.cc @@ -9,7 +9,7 @@ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "git2.h" #include "../include/threads.h" #include "../include/error.h" diff --git a/src/tree.cc b/src/tree.cc index 46ed0f8b2..915164e36 100755 --- a/src/tree.cc +++ b/src/tree.cc @@ -1,11 +1,16 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, 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 "git2.h" #include "../include/repo.h" #include "../include/oid.h" @@ -14,9 +19,11 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include "../include/error.h" #include "../include/functions/string.h" +#include "../include/functions/utilities.h" using namespace v8; using namespace node; +using namespace cvv8; void GitTree::Initialize (Handle target) { HandleScope scope; @@ -27,10 +34,8 @@ void GitTree::Initialize (Handle target) { tpl->SetClassName(String::NewSymbol("Tree")); NODE_SET_PROTOTYPE_METHOD(tpl, "lookup", Lookup); - NODE_SET_PROTOTYPE_METHOD(tpl, "entryCount", EntryCount); - NODE_SET_PROTOTYPE_METHOD(tpl, "entryByIndex", EntryByIndex); + NODE_SET_PROTOTYPE_METHOD(tpl, "walk", Walk); NODE_SET_PROTOTYPE_METHOD(tpl, "entryByPath", EntryByPath); - NODE_SET_PROTOTYPE_METHOD(tpl, "sortEntries", EntryCount); constructor_template = Persistent::New(tpl->GetFunction()); target->Set(String::NewSymbol("Tree"), constructor_template); @@ -39,28 +44,10 @@ void GitTree::Initialize (Handle target) { git_tree* GitTree::GetValue() { return this->tree; } - void GitTree::SetValue(git_tree* tree) { this->tree = tree; } -int GitTree::Lookup(git_repository* repo, const git_oid* id) { - return git_tree_lookup(&this->tree, repo, id); -} - -size_t GitTree::EntryCount() { - return git_tree_entrycount(this->tree); -} - -git_tree_entry* GitTree::EntryByIndex(int idx) { - return const_cast(git_tree_entry_byindex(this->tree, idx)); -} - -int GitTree::SortEntries() { - //return git_tree_sort_entries(this->tree); - return 0; -} - Handle GitTree::New(const Arguments& args) { HandleScope scope; @@ -74,152 +61,214 @@ Handle GitTree::New(const Arguments& args) { Handle GitTree::Lookup(const Arguments& args) { HandleScope scope; - GitTree *tree = ObjectWrap::Unwrap(args.This()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); + return ThrowException(Exception::Error(String::New("Oid is required and must be a Object."))); } if(args.Length() == 1 || !args[1]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + return ThrowException(Exception::Error(String::New("Repository is required and must be a Object."))); + } + + if(args.Length() == 2 || !args[2]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - GitRepo* repo = ObjectWrap::Unwrap(args[0]->ToObject()); - GitOid* oid = ObjectWrap::Unwrap(args[1]->ToObject()); - - git_oid ref_oid = oid->GetValue(); - int err = tree->Lookup(repo->GetValue(), &ref_oid); - - return scope.Close( Integer::New(err) ); - -// if(args.Length() == 2 || !args[2]->IsFunction()) { -// return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); -// } -// -// callback = Local::Cast(args[2]); -// -// lookup_request *lr = new lookup_request(); -// lr->tree = tree; -// lr->repo = ObjectWrap::Unwrap(args[0]->ToObject()); -// lr->oid = ObjectWrap::Unwrap(args[1]->ToObject()); -// lr->callback = Persistent::New(callback); -// -// tree->Ref(); -// -// eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, lr); -// ev_ref(EV_DEFAULT_UC); -// -// return scope.Close( Undefined() ); + LookupBaton* baton = new LookupBaton; + baton->request.data = baton; + baton->error = NULL; + baton->rawTree = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->rawOid = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + baton->rawRepo = ObjectWrap::Unwrap(args[1]->ToObject())->GetValue(); + 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 GitTree::LookupWork(uv_work_t* req) { + LookupBaton* baton = static_cast(req->data); -//void GitTree::EIO_Lookup(uv_work_t *req) { -// lookup_request *lr = static_cast(req->data); -// -// git_oid oid = lr->oid->GetValue(); -// lr->err = lr->tree->Lookup(lr->repo->GetValue(), &oid); -// -// return 0; -//} - -//void GitTree::EIO_AfterLookup(uv_work_t *req) { -// lookup_request *lr = static_cast(req->data); -// -// ev_unref(EV_DEFAULT_UC); -// lr->tree->Unref(); -// -// Handle argv[1]; -// argv[0] = Integer::New(lr->err); -// -// TryCatch try_catch; -// -// lr->callback->Call(Context::GetCurrent()->Global(), 1, argv); -// -// if(try_catch.HasCaught()) -// FatalException(try_catch); -// -// lr->callback.Dispose(); -// -// delete lr; -// -// return 0; -//} - -Handle GitTree::EntryCount(const Arguments& args) { + int returnCode = git_tree_lookup(&baton->rawTree, baton->rawRepo, &baton->rawOid); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } +} +void GitTree::LookupAfterWork(uv_work_t* req) { HandleScope scope; + LookupBaton* baton = static_cast(req->data); - GitTree *tree = ObjectWrap::Unwrap(args.This()); + if (success(baton->error, baton->callback)) { + Local tree = GitTree::constructor_template->NewInstance(); + GitTree *treeInstance = ObjectWrap::Unwrap(tree); + treeInstance->SetValue(baton->rawTree); - int count = tree->EntryCount(); + Handle argv[2] = { + Local::New(Null()), + tree + }; - return scope.Close( Integer::New(count) ); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + delete req; } -Handle GitTree::EntryByIndex(const Arguments& args) { +Handle GitTree::Walk(const Arguments& args) { HandleScope scope; - GitTree *tree = ObjectWrap::Unwrap(args.This()); - Local callback; + GitTree* tree = ObjectWrap::Unwrap(args.This()); - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("TreeEntry is required and must be a Object."))); + if (tree->GetValue() == NULL) { + return ThrowException(Exception::Error(String::New("No tree list to Walk."))); + } + + if(args.Length() == 0 || !args[0]->IsBoolean()) { + return ThrowException(Exception::Error(String::New("Blobs only flag is required and must be a Boolean."))); } - if(args.Length() == 1 || !args[1]->IsNumber()) { - return ThrowException(Exception::Error(String::New("Index is required and must be a Number."))); + if(args.Length() == 1 || !args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Entry callback is required and must be a Function."))); } if(args.Length() == 2 || !args[2]->IsFunction()) { - return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + return ThrowException(Exception::Error(String::New("End callback is required and must be a Function."))); } - callback = Local::Cast(args[2]); + WalkBaton* baton = new WalkBaton; + uv_async_init(uv_default_loop(), &baton->asyncEntry, WalkWorkSendEntry); + uv_async_init(uv_default_loop(), &baton->asyncEnd, WalkWorkSendEnd); - entryindex_request *er = new entryindex_request(); - er->tree = tree; - er->entry = ObjectWrap::Unwrap(args[0]->ToObject()); - er->idx = args[1]->ToInteger()->Value(); - er->callback = Persistent::New(callback); + uv_mutex_init(&baton->mutex); - tree->Ref(); + baton->rawTree = tree->GetValue(); + baton->error = NULL; + baton->blobsOnly = CastFromJS(args[0]->ToBoolean()); + baton->entryCallback = Persistent::New(Local::Cast(args[1])); + baton->endCallback = Persistent::New(Local::Cast(args[2])); - uv_work_t *req = new uv_work_t; - req->data = er; - uv_queue_work(uv_default_loop(), req, EIO_EntryByIndex, (uv_after_work_cb)EIO_AfterEntryByIndex); + uv_thread_create(&baton->threadId, WalkWork, baton); - return scope.Close( Undefined() ); + return Undefined(); } +void GitTree::WalkWork(void* payload) { + WalkBaton *baton = static_cast(payload); -void GitTree::EIO_EntryByIndex(uv_work_t *req) { - entryindex_request *er = static_cast(req->data); + int returnCode = git_tree_walk(baton->rawTree, GIT_TREEWALK_PRE, WalkWorkEntry, payload); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + baton->asyncEnd.data = baton; + uv_async_send(&baton->asyncEnd); + return; + } - er->entry->SetValue(er->tree->EntryByIndex(er->idx)); + baton->asyncEntry.data = baton; + uv_async_send(&baton->asyncEntry); + baton->asyncEnd.data = baton; + uv_async_send(&baton->asyncEnd); } +int GitTree::WalkWorkEntry(const char* root, const git_tree_entry* entry, + void* payload) { + WalkBaton *baton = static_cast(payload); -void GitTree::EIO_AfterEntryByIndex(uv_work_t *req) { - entryindex_request *er = static_cast(req->data); + uv_mutex_lock(&baton->mutex); - Handle argv[0]; + if (!baton->blobsOnly) { - TryCatch try_catch; + GitTree::WalkEntry* walkEntry = new WalkEntry; + walkEntry->rawEntry = git_tree_entry_dup(entry); + walkEntry->root = root; + baton->rawTreeEntries.push_back(walkEntry); + + } else { + git_tree_entry* rawEntry = git_tree_entry_dup(entry); + git_filemode_t fileMode = git_tree_entry_filemode(rawEntry); - er->callback->Call(Context::GetCurrent()->Global(), 0, argv); + if (fileMode == GIT_FILEMODE_BLOB || + fileMode == GIT_FILEMODE_BLOB_EXECUTABLE) { - if(try_catch.HasCaught()) - FatalException(try_catch); + GitTree::WalkEntry* walkEntry = new WalkEntry; + walkEntry->rawEntry = rawEntry; + walkEntry->root = root; + baton->rawTreeEntries.push_back(walkEntry); + } + } - er->callback.Dispose(); + uv_mutex_unlock(&baton->mutex); - delete req; - er->tree->Unref(); - delete er; + if ((unsigned int)baton->rawTreeEntries.size() == (unsigned int)GitTree::WALK_ENTRY_SEND_THRESHOLD) { + baton->asyncEntry.data = baton; + uv_async_send(&baton->asyncEntry); + } + + return GIT_OK; +} +void GitTree::WalkWorkSendEntry(uv_async_t *handle, int status /*UNUSED*/) { + HandleScope scope; + + WalkBaton *baton = static_cast(handle->data); + + uv_mutex_lock(&baton->mutex); + + if (success(baton->error, baton->entryCallback)) { + + std::vector > treeEntries; + + for(std::vector::iterator treeEntriesIterator = baton->rawTreeEntries.begin(); treeEntriesIterator != baton->rawTreeEntries.end(); ++treeEntriesIterator) { + WalkEntry* walkEntry = (*treeEntriesIterator); + + Local entry = GitTreeEntry::constructor_template->NewInstance(); + GitTreeEntry *entryInstance = ObjectWrap::Unwrap(entry); + entryInstance->SetValue(walkEntry->rawEntry); + entryInstance->SetRoot(walkEntry->root); + + treeEntries.push_back(entry); + } + + baton->rawTreeEntries.clear(); + + Handle argv[2] = { + Local::New(Null()), + CastToJS(treeEntries) + }; + + TryCatch try_catch; + baton->entryCallback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + uv_mutex_unlock(&baton->mutex); +} +void GitTree::WalkWorkSendEnd(uv_async_t *handle, int status /*UNUSED*/) { + WalkBaton *baton = static_cast(handle->data); + + uv_mutex_destroy(&baton->mutex); + uv_close((uv_handle_t*) &baton->asyncEnd, NULL); + uv_close((uv_handle_t*) &baton->asyncEntry, NULL); + + Local argv[1]; + if (baton->error) { + argv[0] = GitError::WrapError(baton->error); + } else { + argv[0] = Local::New(Null()); + } + + TryCatch try_catch; + baton->endCallback->Call(Context::GetCurrent()->Global(), 1, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } } Handle GitTree::EntryByPath(const Arguments& args) { HandleScope scope; if(args.Length() == 0 || !args[0]->IsString()) { - return ThrowException(Exception::Error(String::New("Name is required and must be a String."))); + return ThrowException(Exception::Error(String::New("Path is required and must be a String."))); } if(args.Length() == 1 || !args[1]->IsFunction()) { @@ -242,7 +291,6 @@ Handle GitTree::EntryByPath(const Arguments& args) { return Undefined(); } - void GitTree::EntryByPathWork(uv_work_t *req) { EntryByPathBaton *baton = static_cast(req->data); @@ -251,28 +299,15 @@ void GitTree::EntryByPathWork(uv_work_t *req) { baton->error = giterr_last(); } } - void GitTree::EntryByPathAfterWork(uv_work_t *req) { HandleScope scope; EntryByPathBaton *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 { - + if (success(baton->error, baton->callback)) { Local entry = GitTreeEntry::constructor_template->NewInstance(); GitTreeEntry *entryInstance = ObjectWrap::Unwrap(entry); entryInstance->SetValue(baton->rawEntry); + entryInstance->SetRoot(baton->path.substr(0, baton->path.find_last_of("\\/"))); Handle argv[2] = { Local::New(Null()), @@ -288,23 +323,4 @@ void GitTree::EntryByPathAfterWork(uv_work_t *req) { delete req; } -Handle GitTree::SortEntries(const Arguments& args) { - HandleScope scope; - - GitTree *tree = ObjectWrap::Unwrap(args.This()); - - int err = tree->SortEntries(); - - return scope.Close( Integer::New(err) ); -} - -Handle GitTree::ClearEntries(const Arguments& args) { - HandleScope scope; - - //GitTree *tree = ObjectWrap::Unwrap(args.This()); - - //tree->ClearEntries(); - - return scope.Close( Undefined() ); -} Persistent GitTree::constructor_template; diff --git a/src/tree_entry.cc b/src/tree_entry.cc index 134932f7e..78578cb4c 100755 --- a/src/tree_entry.cc +++ b/src/tree_entry.cc @@ -1,21 +1,34 @@ /* -Copyright (c) 2011, Tim Branyen @tbranyen -*/ + * Copyright 2013, Tim Branyen @tbranyen + * @author Michael Robinson @codeofinterest + * + * Dual licensed under the MIT and GPL licenses. + */ #include #include -#include "../vendor/libgit2/include/git2.h" +#include "cvv8/v8-convert.hpp" + +#include "git2.h" #include "../include/repo.h" #include "../include/blob.h" #include "../include/tree.h" -#include "../include/object.h" #include "../include/oid.h" #include "../include/tree_entry.h" +#include "../include/error.h" + +#include "../include/functions/utilities.h" using namespace v8; using namespace node; +using namespace cvv8; + +namespace cvv8 { + template <> + struct NativeToJS : NativeToJS {}; +} void GitTreeEntry::Initialize(Handle target) { Local tpl = FunctionTemplate::New(New); @@ -24,36 +37,37 @@ void GitTreeEntry::Initialize(Handle target) { tpl->SetClassName(String::NewSymbol("TreeEntry")); NODE_SET_PROTOTYPE_METHOD(tpl, "name", Name); - NODE_SET_PROTOTYPE_METHOD(tpl, "attributes", Attributes); - NODE_SET_PROTOTYPE_METHOD(tpl, "id", Id); - NODE_SET_PROTOTYPE_METHOD(tpl, "toObject", ToObject); + NODE_SET_PROTOTYPE_METHOD(tpl, "root", Root); + NODE_SET_PROTOTYPE_METHOD(tpl, "fileMode", FileMode); + NODE_SET_PROTOTYPE_METHOD(tpl, "oid", Oid); + NODE_SET_PROTOTYPE_METHOD(tpl, "toBlob", ToBlob); + + // Add libgit2 file modes to entry object + Local libgit2FileModes = Object::New(); + + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_NEW"), CastToJS(GIT_FILEMODE_NEW), ReadOnly); + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_TREE"), CastToJS(GIT_FILEMODE_TREE), ReadOnly); + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_BLOB"), CastToJS(GIT_FILEMODE_BLOB), ReadOnly); + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_BLOB_EXECUTABLE"), CastToJS(GIT_FILEMODE_BLOB_EXECUTABLE), ReadOnly); + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_LINK"), CastToJS(GIT_FILEMODE_LINK), ReadOnly); + libgit2FileModes->Set(String::NewSymbol("GIT_FILEMODE_COMMIT"), CastToJS(GIT_FILEMODE_COMMIT), ReadOnly); constructor_template = Persistent::New(tpl->GetFunction()); + constructor_template->Set(String::NewSymbol("fileModes"), libgit2FileModes, ReadOnly); target->Set(String::NewSymbol("TreeEntry"), constructor_template); } git_tree_entry* GitTreeEntry::GetValue() { return this->entry; } - void GitTreeEntry::SetValue(git_tree_entry* entry) { this->entry = entry; } - -const char* GitTreeEntry::Name() { - return git_tree_entry_name(this->entry); -} - -int GitTreeEntry::Attributes() { - return git_tree_entry_filemode(this->entry); -} - -const git_oid* GitTreeEntry::Id() { - return git_tree_entry_id(this->entry); +void GitTreeEntry::SetRoot(std::string root) { + this->root = root; } - -int GitTreeEntry::ToObject(git_repository* repo, git_object** obj) { - return git_tree_entry_to_object(obj, repo, this->entry); +std::string GitTreeEntry::GetRoot() { + return this->root; } Handle GitTreeEntry::New(const Arguments& args) { @@ -66,60 +80,202 @@ Handle GitTreeEntry::New(const Arguments& args) { return scope.Close(args.This()); } -Handle GitTreeEntry::Name(const Arguments& args) { +Handle GitTreeEntry::Root(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."))); + } + GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); - return scope.Close( String::New(entry->Name()) ); + Handle argv[2] = { + Local::New(Null()), + String::New(entry->GetRoot().c_str()) + }; + + TryCatch try_catch; + Local::Cast(args[0])->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + + return Undefined(); } -Handle GitTreeEntry::Attributes(const Arguments& args) { +Handle GitTreeEntry::Name(const Arguments& args) { HandleScope scope; - GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); + if(args.Length() == 0 || !args[0]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } + + NameBaton *baton = new NameBaton; + baton->request.data = baton; + + GitTreeEntry *treeEntry = ObjectWrap::Unwrap(args.This()); + baton->rawEntry = treeEntry->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); - return scope.Close( Number::New(entry->Attributes()) ); + uv_queue_work(uv_default_loop(), &baton->request, NameWork, (uv_after_work_cb)NameAfterWork); + + return Undefined(); } +void GitTreeEntry::NameWork(uv_work_t* req) { + NameBaton *baton = static_cast(req->data); -Handle GitTreeEntry::Id(const Arguments& args) { + baton->name = git_tree_entry_name(baton->rawEntry); +} +void GitTreeEntry::NameAfterWork(uv_work_t* req) { HandleScope scope; + NameBaton *baton = static_cast(req->data); - GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); + Handle argv[2] = { + Local::New(Null()), + String::New(baton->name) + }; - if(args.Length() == 0 || !args[0]->IsObject()) { - return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + delete req; +} + +Handle GitTreeEntry::FileMode(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."))); } - GitOid* oid = ObjectWrap::Unwrap(args[0]->ToObject()); + FileModeBaton *baton = new FileModeBaton; + baton->request.data = baton; + baton->rawEntry = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); - oid->SetValue(*const_cast(entry->Id())); + uv_queue_work(uv_default_loop(), &baton->request, FileModeWork, (uv_after_work_cb)FileModeAfterWork); - return scope.Close( Undefined() ); + return Undefined(); } +void GitTreeEntry::FileModeWork(uv_work_t* req) { + FileModeBaton *baton = static_cast(req->data); -Handle GitTreeEntry::ToObject(const Arguments& args) { + baton->fileMode = git_tree_entry_filemode(baton->rawEntry); +} +void GitTreeEntry::FileModeAfterWork(uv_work_t* req) { HandleScope scope; + FileModeBaton *baton = static_cast(req->data); - GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); + Handle argv[2] = { + Local::New(Null()), + Integer::New(baton->fileMode) + }; + + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + delete req; +} + +Handle GitTreeEntry::Oid(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."))); + } + + OidBaton* baton = new OidBaton; + baton->request.data = baton; + baton->rawEntry = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->callback = Persistent::New(Local::Cast(args[0])); + + uv_queue_work(uv_default_loop(), &baton->request, OidWork, (uv_after_work_cb)OidAfterWork); + + return Undefined(); +} +void GitTreeEntry::OidWork(uv_work_t* req) { + OidBaton *baton = static_cast(req->data); + + baton->rawOid = git_tree_entry_id(const_cast(baton->rawEntry)); +} +void GitTreeEntry::OidAfterWork(uv_work_t* req) { + HandleScope scope; + OidBaton *baton = static_cast(req->data); + + Handle oid = GitOid::constructor_template->NewInstance(); + GitOid* oidInstance = ObjectWrap::Unwrap(oid); + oidInstance->SetValue(*const_cast(baton->rawOid)); + + Handle argv[2] = { + Local::New(Null()), + oid + }; + + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + delete req; +} + +Handle GitTreeEntry::ToBlob(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."))); + return ThrowException(Exception::Error(String::New("Repository is required and must be an Object."))); } - if(args.Length() == 1 || !args[1]->IsObject()) { - return ThrowException(Exception::Error(String::New("Object is required and must be an Object."))); + if(args.Length() == 1 || !args[1]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); } - GitRepo* repo = ObjectWrap::Unwrap(args[0]->ToObject()); - GitObject* object = ObjectWrap::Unwrap(args[1]->ToObject()); + ToBlobBaton* baton = new ToBlobBaton; + baton->request.data = baton; + baton->error = NULL; + baton->rawRepo = ObjectWrap::Unwrap(args[0]->ToObject())->GetValue(); + baton->rawEntry = ObjectWrap::Unwrap(args.This())->GetValue(); + baton->rawBlob = NULL; + baton->callback = Persistent::New(Local::Cast(args[1])); - git_object* out; - entry->ToObject(repo->GetValue(), &out); + uv_queue_work(uv_default_loop(), &baton->request, ToBlobWork, (uv_after_work_cb)ToBlobAfterWork); - object->SetValue(out); + return Undefined(); +} +void GitTreeEntry::ToBlobWork(uv_work_t *req) { + ToBlobBaton* baton = static_cast(req->data); - return scope.Close( Undefined() ); + int returnCode = git_tree_entry_to_object((git_object**)&baton->rawBlob, baton->rawRepo, baton->rawEntry); + if (returnCode != GIT_OK) { + baton->error = giterr_last(); + } } +void GitTreeEntry::ToBlobAfterWork(uv_work_t *req) { + HandleScope scope; + ToBlobBaton* baton = static_cast(req->data); + + if (success(baton->error, baton->callback)) { + Handle blob = GitBlob::constructor_template->NewInstance(); + GitBlob *blobInstance = ObjectWrap::Unwrap(blob); + blobInstance->SetValue(baton->rawBlob); + + Handle argv[2] = { + Local::New(Null()), + blob + }; + + TryCatch try_catch; + baton->callback->Call(Context::GetCurrent()->Global(), 2, argv); + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + } + delete req; +} + Persistent GitTreeEntry::constructor_template; diff --git a/test/convenience-commit.js b/test/convenience-commit.js index 9f0b2bbcb..663ffe99d 100644 --- a/test/convenience-commit.js +++ b/test/convenience-commit.js @@ -1,6 +1,8 @@ -var git = require('../'); -var rimraf = require('rimraf'); -var fs = require( 'fs' ); +var git = require('../'), + rimraf = require('rimraf'), + fs = require( 'fs' ); + +var historyCountKnownSHA = 'fce88902e66c72b5b93e75bdb5ae717038b221f6'; // Helper functions var helper = { @@ -32,6 +34,146 @@ exports.method = function(test){ test.done(); }; +exports.message = function(test) { + test.expect(3); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.message(function(error, message) { + test.equals(error, null, 'There should be no error'); + test.notEqual(message, null, 'Message should not be null'); + test.equals(message, 'Update README.md', 'Message should match expected value'); + test.done(); + }); + }); + }); +}; + +exports.sha = function(test) { + test.expect(3); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.sha(function(error, sha) { + test.equals(error, null, 'There should be no error'); + test.notEqual(sha, null, 'SHA should not be null'); + test.equals(sha, historyCountKnownSHA, 'SHA should match expected value'); + test.done(); + }); + }); + }); +}; + +exports.time = function(test) { + test.expect(3); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.time(function(error, time) { + test.equals(error, null, 'There should be no error'); + test.notEqual(time, null, 'Time should not be null'); + test.equals(time, 1362012884000, 'Time should match expected value'); + test.done(); + }); + }); + }); +}; + +exports.date = function(test) { + test.expect(4); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.date(function(error, date) { + test.equals(error, null, 'There should be no error'); + test.notEqual(date, null, 'Date should not be null'); + test.equal(date instanceof Date, true, 'Date should be a date object'); + test.equals(date.getTime(), 1362012884000, 'Date should match expected value'); + test.done(); + }); + }); + }); +}; + +exports.offset = function(test) { + test.expect(3); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.offset(function(error, offset) { + test.equals(error, null, 'There should be no error'); + test.notEqual(offset, null, 'Offset should not be null'); + test.equals(offset, 780, 'Offset should match expected value'); + test.done(); + }); + }); + }); +}; + +exports.author = function(test) { + test.expect(2); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.author(function(error, author) { + test.equals(error, null, 'There should be no error'); + test.notEqual(author, null, 'Author should not be null'); + test.done(); + }); + }); + }); +}; + +exports.authorName = function(test) { + test.expect(1); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.author(function commitAuthor(error, author) { + author.name(function authorName(error, name) { + test.equals(name, 'Michael Robinson', 'The author name should match expected value'); + test.done(); + }); + }); + }); + }); +}; + +exports.authorEmail = function(test) { + test.expect(1); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.author(function commitAuthor(error, author) { + author.email(function authorName(error, email) { + test.equals(email, 'mike@panmedia.co.nz', 'The author email should match expected value'); + test.done(); + }); + }); + }); + }); +}; + +exports.committerName = function(test) { + test.expect(1); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.committer(function commitCommitter(error, committer) { + committer.name(function committerName(error, name) { + test.equals(name, 'Michael Robinson', 'The author name should match expected value'); + test.done(); + }); + }); + }); + }); +}; + +exports.committerEmail = function(test) { + test.expect(1); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.committer(function commitCommitter(error, committer) { + committer.email(function committerName(error, email) { + test.equals(email, 'mike@panmedia.co.nz', 'The committer email should match expected value'); + test.done(); + }); + }); + }); + }); +}; + /** * Test that improper commit ID's result in an error message */ @@ -45,12 +187,8 @@ exports.improperCommitId = function(test) { }); }; -var historyCountKnownSHA = 'fce88902e66c72b5b93e75bdb5ae717038b221f6'; - /** * Test that retreiving walking a given commit's history works as expected. - * - * @param {Object} test */ exports.history = function(test) { test.expect(368); @@ -66,7 +204,6 @@ exports.history = function(test) { test.equals(null, error, 'There should be no errors'); test.equals(historyCount, expectedHistoryCount, 'Manual count does not match expected'); test.equals(commits.length, expectedHistoryCount, '"end" count does not match expected'); - test.done(); }); }); @@ -80,14 +217,12 @@ exports.masterHead = function(test) { test.expect(2); git.repo('../.git', function(error, repository) { repository.branch('master', function(error, branch) { - test.equals(error, null, 'Getting branch should not error'); - - repository.commit(branch.sha, function(error, commit) { - - test.equals(error, null, 'Getting latest branch commit should not error'); - - test.done(); + branch.sha(function(error, sha) { + repository.commit(sha, function(error, commit) { + test.equals(error, null, 'Getting latest branch commit should not error'); + test.done(); + }); }); }); }); @@ -98,34 +233,18 @@ exports.masterHead = function(test) { * * @param {Object} test */ -exports.parentSync = function(test) { - test.expect(2); +exports.parents = function(test) { + test.expect(3); git.repo('../.git', function(error, repository) { - repository.commit('2d71044741412280370cb0326c96d3a5a7b5dca1', function(error, commit) { - test.equals(commit.parentCount, 1, 'Commit has exactly one parent'); - var parent = commit.parentSync(0); - test.equals(parent.sha, 'e8876707938abf94d5cc02b0c4017c4fec2aa44e', 'Parent SHA should match expected value'); - test.done(); - }); - }); -}; - -/** - * Test that retreiving parent works as expected. - * - * @param {Object} test - */ -exports.parent = function(test) { - test.expect(2); - git.repo('../.git', function(error, repository) { - repository.commit('2d71044741412280370cb0326c96d3a5a7b5dca1', function(error, commit) { - test.equals(commit.parentCount, 1, 'Commit has exactly one parent'); - commit.parent(0, function(error, parent) { - if (error) throw error; - test.equals(parent.sha, 'e8876707938abf94d5cc02b0c4017c4fec2aa44e', 'Parent SHA should match expected value'); - test.done(); + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.parents(function(error, parents) { + test.equals(parents.length, 1, 'Commit should have exactly one parent'); + parents[0].sha(function parentSha(error, sha) { + test.equals(error, null, 'Getting parent SHA should not error'); + test.equals(sha, 'ecfd36c80a3e9081f200dfda2391acadb56dac27', 'Parent SHA should match expected value'); + test.done(); + }); }); - }); }); }; @@ -136,21 +255,19 @@ exports.parent = function(test) { exports.tree = function(test) { test.expect(2); git.repo('../.git', function(error, repository) { - repository.commit(historyCountKnownSHA, function(error, commit) { - test.equals(error, null, 'Getting latest branch commit should not error'); var commitTreeEntryCount = 0; - var expectedCommitTreeEntryCount = 200; + var expectedCommitTreeEntryCount = 198; - commit.tree().walk().on('entry', function(commit) { - commitTreeEntryCount++; - }).on('end', function(commits) { - - test.equals(commitTreeEntryCount, expectedCommitTreeEntryCount, 'Commit tree entry count does not match expected'); - - test.done(); + commit.tree(function commitTree(error, tree) { + tree.walk().on('entry', function(error, entry) { + commitTreeEntryCount++; + }).on('end', function(error, entries) { + test.equals(commitTreeEntryCount, expectedCommitTreeEntryCount, 'Commit tree entry count does not match expected'); + test.done(); + }); }); }); }); @@ -170,19 +287,3 @@ exports.parentsDiffTrees = function(test) { }); }); }; - -exports.file = function(test) { - test.expect(5); - git.repo('../.git', function(error, repository) { - repository.commit(historyCountKnownSHA, function(error, commit) { - commit.file('README.md', function(error, file) { - test.equal(error, null, 'Should not error'); - test.notEqual(file, null, 'File should not be null'); - test.equal(file.name, 'README.md', 'File name should match expected'); - test.equal(file.sha, 'b252f396b17661462372f78b7bcfc403b8731aaa', 'SHA shoud match expected'); - test.equal(file.attributes, 33188, 'Attributes should match expected'); - test.done(); - }); - }); - }); -}; diff --git a/test/convenience-difflist.js b/test/convenience-difflist.js new file mode 100644 index 000000000..c010691c1 --- /dev/null +++ b/test/convenience-difflist.js @@ -0,0 +1,124 @@ +var git = require('../'), + rimraf = require('rimraf'), + fs = require( 'fs' ); + +var historyCountKnownSHA = 'fce88902e66c72b5b93e75bdb5ae717038b221f6'; + +// Helper functions +var helper = { + // Test if obj is a true function + testFunction: function(test, obj, label) { + // The object reports itself as a function + test(typeof obj, 'function', label + ' reports as a function.'); + // This ensures the repo is actually a derivative of the Function [[Class]] + test(toString.call(obj), '[object Function]', label + ' [[Class]] is of type function.'); + }, + // Test code and handle exception thrown + testException: function(test, fun, label) { + try { + fun(); + test(false, label); + } + catch (ex) { + test(true, label); + } + } +}; + +/** + * Test that the commit object is present. + */ +exports.method = function(test){ + test.expect(2); + helper.testFunction(test.equals, git.diffList, 'DiffList'); + test.done(); +}; + +/** + * Test that retreiving parent works as expected. + * + * @param {Object} test + */ +exports.walkingDiffs = function(test) { + test.expect(15); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.parents(function(error, parents) { + parents[0].sha(function(error, parentSha) { + (new git.diffList(commit.rawRepo)).treeToTree(parentSha, historyCountKnownSHA, function(error, diffList) { + test.equal(null, error, 'Should not error'); + diffList.walk().on('delta', function(error, delta) { + test.equal(null, error, 'Should not error'); + test.equal(delta.oldFile.path, 'README.md', 'Old file path should match expected'); + test.equal(delta.newFile.path, 'README.md', 'New file path should match expected'); + test.equal(delta.content.length, 5, 'Content array should be of known length'); + test.equal(delta.status, diffList.deltaTypes.GIT_DELTA_MODIFIED, 'Status should be known type'); + test.equal(delta.content[0].lineOrigin, diffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, 'First content item should be context'); + test.equal(delta.content[1].lineOrigin, diffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, 'Second content item should be context'); + test.equal(delta.content[2].lineOrigin, diffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, 'Third content item should be context'); + + var oldContent = '__Before submitting a pull request, please ensure both unit tests and lint checks pass.__\n'; + test.equal(delta.content[3].content, oldContent, 'Old content should match known value'); + test.equal(delta.content[3].lineOrigin, diffList.lineOriginTypes.GIT_DIFF_LINE_DELETION, 'Fourth content item should be deletion'); + test.equal(delta.content[3].contentLength, 90, 'Fourth content length should match known value'); + + var newContent = '__Before submitting a pull request, please ensure both that you\'ve added unit tests to cover your shiny new code, and that all unit tests and lint checks pass.__\n'; + test.equal(delta.content[4].content, newContent, 'New content should match known value'); + test.equal(delta.content[4].lineOrigin, diffList.lineOriginTypes.GIT_DIFF_LINE_ADDITION, 'Fifth content item should be addition'); + test.equal(delta.content[4].contentLength, 162, 'Fifth content length should match known value'); + test.done(); + }); + }); + }); + }); + }); + }); +}; + +exports.walkingEnd = function(test) { + test.expect(2); + git.repo('../.git', function(error, repository) { + repository.commit(historyCountKnownSHA, function(error, commit) { + commit.parents(function(error, parents) { + parents[0].sha(function(error, parentSha) { + (new git.diffList(commit.rawRepo)).treeToTree(parentSha, historyCountKnownSHA, function(error, diffList) { + diffList.walk().on('end', function(error, diffs) { + test.equal(null, error, 'Should not error'); + test.equal(diffs.length, 1, 'Diffs array should be of known length'); + test.done(); + }); + }); + }); + }); + }); + }); +}; + +exports.deltaTypes = function(test) { + test.expect(9); + var diffList = new git.diffList((new git.repo()).rawRepo); + test.equal(diffList.deltaTypes.GIT_DELTA_UNMODIFIED, git.raw.DiffList.deltaTypes.GIT_DELTA_UNMODIFIED, 'GIT_DELTA_UNMODIFIED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_ADDED, git.raw.DiffList.deltaTypes.GIT_DELTA_ADDED, 'GIT_DELTA_ADDED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_DELETED, git.raw.DiffList.deltaTypes.GIT_DELTA_DELETED, 'GIT_DELTA_DELETED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_MODIFIED, git.raw.DiffList.deltaTypes.GIT_DELTA_MODIFIED, 'GIT_DELTA_MODIFIED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_RENAMED, git.raw.DiffList.deltaTypes.GIT_DELTA_RENAMED, 'GIT_DELTA_RENAMED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_COPIED, git.raw.DiffList.deltaTypes.GIT_DELTA_COPIED, 'GIT_DELTA_COPIED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_IGNORED, git.raw.DiffList.deltaTypes.GIT_DELTA_IGNORED, 'GIT_DELTA_IGNORED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_UNTRACKED, git.raw.DiffList.deltaTypes.GIT_DELTA_UNTRACKED, 'GIT_DELTA_UNTRACKED delta type should match expected value'); + test.equal(diffList.deltaTypes.GIT_DELTA_TYPECHANGE, git.raw.DiffList.deltaTypes.GIT_DELTA_TYPECHANGE, 'GIT_DELTA_TYPECHANGE delta type should match expected value'); + test.done(); +}; + +exports.lineOriginTypes = function(test) { + test.expect(8); + var diffList = new git.diffList((new git.repo()).rawRepo); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_CONTEXT, 'GIT_DIFF_LINE_CONTEXT line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_ADDITION, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_ADDITION, 'GIT_DIFF_LINE_ADDITION line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_DELETION, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_DELETION, 'GIT_DIFF_LINE_DELETION line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_ADD_EOFNL, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_ADD_EOFNL, 'GIT_DIFF_LINE_ADD_EOFNL line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_DEL_EOFNL, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_DEL_EOFNL, 'GIT_DIFF_LINE_DEL_EOFNL line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_FILE_HDR, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_FILE_HDR, 'GIT_DIFF_LINE_FILE_HDR line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_HUNK_HDR, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_HUNK_HDR, 'GIT_DIFF_LINE_HUNK_HDR line origin type should match expected value'); + test.equal(diffList.lineOriginTypes.GIT_DIFF_LINE_BINARY, git.raw.DiffList.lineOriginTypes.GIT_DIFF_LINE_BINARY, 'GIT_DIFF_LINE_BINARY line origin type should match expected value'); + test.done(); +}; diff --git a/test/convenience-entry.js b/test/convenience-entry.js new file mode 100644 index 000000000..f24068a7a --- /dev/null +++ b/test/convenience-entry.js @@ -0,0 +1,123 @@ +var git = require('../'); + +var sha = '5716e9757886eaf38d51c86b192258c960d9cfea'; + +var getEntry = function(path, callback) { + git.repo('../.git', function(error, repo) { + repo.commit(sha, function(error, commit) { + commit.file(path, function(error, entry) { + callback(error, entry); + }); + }); + }); +}; + +exports.missingFile = function(test) { + test.expect(1); + + getEntry('test/convenience-entry.js', function(error, entry) { + test.notEqual(error, null, 'Missing file should error'); + test.done(); + }); +}; + +exports.sha = function(test) { + test.expect(1); + getEntry('README.md', function(error, entry) { + entry.sha(function(error, sha) { + test.equal(sha, '6cb45ba5d32532bf0d1310dc31ca4f20f59964bc', 'Entry SHA should match expected value'); + test.done(); + }); + }); +}; + +exports.isFile = function(test) { + test.expect(2); + getEntry('README.md', function(error, entry) { + entry.isFile(function(error, isFile) { + test.equal(isFile, true, 'Entry is a file'); + getEntry('example', function(error, entry) { + entry.isFile(function(error, isFile) { + test.equal(isFile, false, 'Entry is a directory'); + test.done(); + }); + }); + }); + }); +}; + +exports.isDirectory = function(test) { + test.expect(2); + getEntry('example', function(error, entry) { + entry.isFile(function(error, isFile) { + test.equal(isFile, false, 'Entry is a directory'); + getEntry('README.md', function(error, entry) { + entry.isFile(function(error, isFile) { + test.equal(isFile, true, 'Entry is a file'); + test.done(); + }); + }); + }); + }); +}; + +exports.name = function(test) { + test.expect(2); + getEntry('test/raw-commit.js', function(error, entry) { + test.equal(error, null, 'Should not error'); + entry.name(function(error, name) { + test.equal(name, 'raw-commit.js', 'Name should match expected value'); + test.done(); + }); + }); +}; + +exports.root = function(test) { + test.expect(1); + getEntry('test/raw-commit.js', function(error, entry) { + entry.root(function(error, root) { + test.equal(root, 'test', 'Root should match expected value'); + test.done(); + }); + }); +}; + +exports.path = function(test) { + test.expect(1); + getEntry('test/raw-commit.js', function(error, entry) { + entry.path(function(error, path) { + test.equal(path, 'test/raw-commit.js', 'Path should match expected value'); + test.done(); + }); + }); +}; + +exports.content = function(test) { + test.expect(1); + getEntry('test/raw-commit.js', function(error, entry) { + entry.content(function(error, content) { + test.equal(content.length, 2736, 'Content length should match expected value'); + test.done(); + }); + }); +}; + +exports.toBlob = function(test) { + test.expect(1); + getEntry('test/raw-commit.js', function(error, entry) { + entry.toBlob(function(error, blob) { + test.equal(blob instanceof git.blob, true, 'Expected instance of Blob'); + test.done(); + }); + }); +}; + +exports.tree = function(test) { + test.expect(1); + getEntry('test', function(error, entry) { + entry.tree(function(error, tree) { + test.equal(tree instanceof git.tree, true, 'Expected instance of Tree'); + test.done(); + }); + }); +}; diff --git a/test/convenience-error.js b/test/convenience-error.js index cb1b10cca..38393dffa 100644 --- a/test/convenience-error.js +++ b/test/convenience-error.js @@ -34,6 +34,40 @@ exports.method = function(test){ test.done(); }; +exports.codes = function(test) { + test.expect(14); + var error = new git.error(); + test.equal(error.codes.GITERR_NOMEMORY, git.raw.Error.codes.GITERR_NOMEMORY, 'GITERR_NOMEMORY code should match expected value'); + test.equal(error.codes.GITERR_OS, git.raw.Error.codes.GITERR_OS, 'GITERR_OS code should match expected value'); + test.equal(error.codes.GITERR_INVALID, git.raw.Error.codes.GITERR_INVALID, 'GITERR_INVALID code should match expected value'); + test.equal(error.codes.GITERR_REFERENCE, git.raw.Error.codes.GITERR_REFERENCE, 'GITERR_REFERENCE code should match expected value'); + test.equal(error.codes.GITERR_ZLIB, git.raw.Error.codes.GITERR_ZLIB, 'GITERR_ZLIB code should match expected value'); + test.equal(error.codes.GITERR_REPOSITORY, git.raw.Error.codes.GITERR_REPOSITORY, 'GITERR_REPOSITORY code should match expected value'); + test.equal(error.codes.GITERR_CONFIG, git.raw.Error.codes.GITERR_CONFIG, 'GITERR_CONFIG code should match expected value'); + test.equal(error.codes.GITERR_REGEX, git.raw.Error.codes.GITERR_REGEX, 'GITERR_REGEX code should match expected value'); + test.equal(error.codes.GITERR_ODB, git.raw.Error.codes.GITERR_ODB, 'GITERR_ODB code should match expected value'); + test.equal(error.codes.GITERR_INDEX, git.raw.Error.codes.GITERR_INDEX, 'GITERR_INDEX code should match expected value'); + test.equal(error.codes.GITERR_OBJECT, git.raw.Error.codes.GITERR_OBJECT, 'GITERR_OBJECT code should match expected value'); + test.equal(error.codes.GITERR_NET, git.raw.Error.codes.GITERR_NET, 'GITERR_NET code should match expected value'); + test.equal(error.codes.GITERR_TAG, git.raw.Error.codes.GITERR_TAG, 'GITERR_TAG code should match expected value'); + test.equal(error.codes.GITERR_TREE, git.raw.Error.codes.GITERR_TREE, 'GITERR_TREE code should match expected value'); + test.done(); +}; + +exports.returnCodes = function(test) { + test.expect(8); + var error = new git.error(); + test.equal(error.returnCodes.GIT_OK, git.raw.Error.returnCodes.GIT_OK, 'GIT_OK return code should match expected value'); + test.equal(error.returnCodes.GIT_ERROR, git.raw.Error.returnCodes.GIT_ERROR, 'GIT_ERROR return code should match expected value'); + test.equal(error.returnCodes.GIT_ENOTFOUND, git.raw.Error.returnCodes.GIT_ENOTFOUND, 'GIT_ENOTFOUND return code should match expected value'); + test.equal(error.returnCodes.GIT_EEXISTS, git.raw.Error.returnCodes.GIT_EEXISTS, 'GIT_EEXISTS return code should match expected value'); + test.equal(error.returnCodes.GIT_EAMBIGUOUS, git.raw.Error.returnCodes.GIT_EAMBIGUOUS, 'GIT_EAMBIGUOUS return code should match expected value'); + test.equal(error.returnCodes.GIT_EBUFS, git.raw.Error.returnCodes.GIT_EBUFS, 'GIT_EBUFS return code should match expected value'); + test.equal(error.returnCodes.GIT_PASSTHROUGH, git.raw.Error.returnCodes.GIT_PASSTHROUGH, 'GIT_PASSTHROUGH return code should match expected value'); + test.equal(error.returnCodes.GIT_ITEROVER, git.raw.Error.returnCodes.GIT_ITEROVER, 'GIT_ITEROVER return code should match expected value'); + test.done(); +}; + /** * Test that * diff --git a/test/convenience-oid.js b/test/convenience-oid.js new file mode 100644 index 000000000..91f359370 --- /dev/null +++ b/test/convenience-oid.js @@ -0,0 +1,49 @@ +var git = require('../'); + +// Helper functions +var helper = { + // Test if obj is a true function + testFunction: function(test, obj, label) { + // The object reports itself as a function + test(typeof obj, 'function', label + ' reports as a function.'); + // This ensures the repo is actually a derivative of the Function [[Class]] + test(toString.call(obj), '[object Function]', label + ' [[Class]] is of type function.'); + }, + // Test code and handle exception thrown + testException: function(test, fun, label) { + try { + fun(); + test(false, label); + } + catch (ex) { + test(true, label); + } + } +}; + +exports.method = function(test){ + test.expect(2); + helper.testFunction(test.equals, git.commit, 'Oid'); + test.done(); +}; + +var knownSha = 'fce88902e66c72b5b93e75bdb5ae717038b221f6'; + +exports.fromString = function(test) { + test.expect(1); + (new git.oid()).fromString(knownSha, function(error, oid) { + test.equal(error, null, 'Should not error'); + test.done(); + }); +}; + +exports.sha = function(test) { + test.expect(2); + (new git.oid()).fromString(knownSha, function(error, oid) { + oid.sha(function(error, sha) { + test.equal(error, null, 'Should not error'); + test.equal(sha, knownSha, 'SHA should match known value'); + test.done(); + }); + }); +}; diff --git a/test/convenience-repo.js b/test/convenience-repo.js index 94e9eb4a6..d01f7ec72 100644 --- a/test/convenience-repo.js +++ b/test/convenience-repo.js @@ -38,11 +38,11 @@ exports.method = function(test){ }, 'Throw an exception if no callback'); // Test invalid repository - git.repo('/etc/hosts', function(error, path) { - test.equals(error.code, error.GITERR_REPOSITORY, error.message, 'Invalid repository error code'); + git.repo('/etc/hosts', function(error, repository) { + test.equals(error.code, error.codes.GITERR_REPOSITORY, error.message, 'Invalid repository error code'); // Test valid repository - git.repo('../.git', function(error, path) { + git.repo('../.git', function(error, repository) { test.equals(null, error, 'Valid repository error code'); test.done(); }); @@ -62,16 +62,11 @@ exports.nonexistentDirectory = function(test) { }; /** - * Repo::Init - * * Ensure the init method can create repositories at the destination path and - * can create either bare/non-bare. This should work async/sync and provide - * the proper return values. + * can create either bare/non-bare. */ exports.init = function(test) { - test.expect(4); - - helper.testFunction(test.equals, git.repo().init, 'Repo::Init'); + test.expect(2); // Cleanup, remove test repo directory - if it exists rimraf('./test.git', function() { diff --git a/test/convenience-tree.js b/test/convenience-tree.js index 5144a0137..813fa3d8c 100644 --- a/test/convenience-tree.js +++ b/test/convenience-tree.js @@ -3,18 +3,16 @@ var git = require('../'), fs = require('fs'); var sha = '5716e9757886eaf38d51c86b192258c960d9cfea'; -var fileCount = 513; +var fileCount = 512; // Number of blob & blob executabless exports.walk = function(test) { - test.expect(516); + test.expect(515); git.repo('../.git', function(error, repo) { - if(error) { throw error; } - // @todo assert repo is correct - repo.commit(sha, function(error, commit) { - if(error) { throw error; } - var entryCount = 0; - commit.tree().walk().on('entry', function(error, index, entry) { + repo.commit(sha, function(error, commit) { + var entryCount = 0; + commit.tree(function(error, tree) { + tree.walk().on('entry', function(error, index, entry) { test.equals(error, null, 'There should be no error'); entryCount++; }).on('end', function(error, entries) { @@ -24,5 +22,6 @@ exports.walk = function(test) { test.done(); }); }); - }); + }); + }); }; diff --git a/test/raw-blob.js b/test/raw-blob.js index 50629b4a3..583c5bb14 100644 --- a/test/raw-blob.js +++ b/test/raw-blob.js @@ -4,7 +4,6 @@ var git = require('../').raw, var testRepo = new git.Repo(); -// Helper functions var helper = { // Test if obj is a true function testFunction: function(test, obj, label) { @@ -25,16 +24,13 @@ var helper = { } }; -// Blob +/** + * Blob constructor + */ exports.constructor = function(test){ test.expect(3); - - // Test for function helper.testFunction(test.equals, git.Blob, 'Blob'); - - // Ensure we get an instance of Blob test.ok(new git.Blob() instanceof git.Blob, 'Invocation returns an instance of Blob'); - test.done(); }; @@ -65,83 +61,34 @@ exports.lookup = function(test) { }, 'Throw an exception if no callback Object'); testRepo.open(path.resolve('../.git'), function() { - - //testOid.mkstr('59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5'); - - //testCommit.lookup(testRepo, testOid, function(err) { - // var tree = new git.Tree(testRepo) - // , entry = new git.TreeEntry() - // , blob = new git.Blob(testRepo); - - // if(!testCommit.tree(tree) && tree.entryCount() > 1) { - // tree.entryByIndex(entry, 1); - // entry.toObject(testRepo, blob); - - // //console.log(entry.name() + ':'); - // //console.log(blob.rawSize()); - // //console.dir(blob.rawContent()); - // } - //}); + // @todo actually lookup test.done(); - }); }; // Blob::RawContent exports.rawContent = function(test) { - var testOid = new git.Oid() - , testBlob = new git.Blob() - , testCommit = new git.Commit(); - - test.expect(2); - - // Test for function - helper.testFunction(test.equals, testBlob.rawContent, 'Blob::RawContent'); - - testRepo.open(path.resolve('../.git'), function() { - testOid.mkstr('59b20b8d5c6ff8d09518454d4dd8b7b30f095ab5'); - - testCommit.lookup(testRepo, testOid, function(err) { - var tree = new git.Tree(testRepo), - entry = new git.TreeEntry(), - blob = new git.Blob(testRepo); - - //if(!testCommit.tree(tree) && tree.entryCount() > 1) { - // tree.entryByIndex(entry, 1); - // entry.toObject(testRepo, blob); - - // //console.log(entry.name() + ':'); - // //console.log(blob.rawSize()); - // //console.dir(blob.rawContent()); - //} - }); - }); - - test.done(); -}; - -// Blob::RawSize -exports.rawSize = function(test) { var testOid = new git.Oid(), - testBlob = new git.Blob(); + testBlob = new git.Blob(), + testCommit = new git.Commit(); test.expect(2); // Test for function - helper.testFunction(test.equals, testBlob.rawSize, 'Blob::RawSize'); + helper.testFunction(test.equals, testBlob.rawContent, 'Blob::RawContent'); test.done(); }; -// Blob::Close -exports.close = function(test) { +// Blob::Free +exports.free = function(test) { var testOid = new git.Oid(), testBlob = new git.Blob(); test.expect(2); // Test for function - helper.testFunction(test.equals, testBlob.close, 'Blob::Close'); + helper.testFunction(test.equals, testBlob.free, 'Blob::Free'); test.done(); }; @@ -154,7 +101,7 @@ exports.createFromFile = function(test) { test.expect(2); // Test for function - helper.testFunction(test.equals, testBlob.createFromFile, 'Blob::Close'); + helper.testFunction(test.equals, testBlob.createFromFile, 'Blob::CreateFromFile'); test.done(); }; diff --git a/test/raw-commit.js b/test/raw-commit.js index e66443ca5..058ed52eb 100644 --- a/test/raw-commit.js +++ b/test/raw-commit.js @@ -1,6 +1,4 @@ -var git = require('../').raw, - rimraf = require('rimraf'), - path = require('path'); +var git = require('../').raw; var testRepo = new git.Repo(); @@ -33,7 +31,7 @@ exports.constructor = function(test){ // Test for function helper.testFunction(test.equals, git.Commit, 'Commit'); - testRepo.open(path.resolve('../.git'), function(err) { + testRepo.open('../.git', function(err) { // Ensure we get an instance of Commit test.ok(new git.Commit(testRepo) instanceof git.Commit, 'Invocation returns an instance of Commit'); @@ -45,101 +43,43 @@ exports.constructor = function(test){ * Commit::Lookup */ exports.lookup = function(test) { + test.expect(7); + var testOid = new git.Oid(), testCommit = new git.Commit(); - testOid.mkstr('cb09e99e91d41705197e0fb60823fdc7df776691'); - - test.expect(8); - // Test for function helper.testFunction(test.equals, testCommit.lookup, 'Commit::Lookup'); - // Test repo argument existence - helper.testException(test.ok, function() { - testCommit.lookup(); - }, 'Throw an exception if no repo'); + testOid.fromString('cb09e99e91d41705197e0fb60823fdc7df776691', function(error, testOid) { - // Test oid argument existence - helper.testException(test.ok, function() { - testCommit.lookup(testRepo); - }, 'Throw an exception if no oid'); + // Test repo argument existence + helper.testException(test.ok, function() { + testCommit.lookup(); + }, 'Throw an exception if no repo'); - // Test callback argument existence - helper.testException(test.ok, function() { - testCommit.lookup(testOid); - }, 'Throw an exception if no callback'); + // Test oid argument existence + helper.testException(test.ok, function() { + testCommit.lookup(testRepo); + }, 'Throw an exception if no oid'); - // Test that all arguments result correctly - helper.testException(test.ifError, function() { - testCommit.lookup(testRepo, testOid, function() {}); - }, 'No exception is thrown with proper arguments'); + // Test callback argument existence + helper.testException(test.ok, function() { + testCommit.lookup(testOid); + }, 'Throw an exception if no callback'); - testRepo.open(path.resolve('../.git'), function() { - // Test invalid commit - testOid.mkstr('100644'); - testCommit.lookup(testRepo, testOid, function(err) { - test.notEqual(0, err, 'Not a valid commit'); + // Test that all arguments result correctly + helper.testException(test.ifError, function() { + testCommit.lookup(testRepo, testOid, function() {}); + }, 'No exception is thrown with proper arguments'); + testRepo.open('../.git', function() { // Test valid commit - testOid.mkstr('cb76e3c030ab29db332aff3b297dc39451a84762'); - testCommit.lookup(testRepo, testOid, function(err) { - test.equals(null, err, 'Valid commit'); - - //test.equals('Updated gitignore and raw-commit test', testCommit.messageShort(), 'Commit message is valid'); - - test.done(); - }); - }); - }); -}; - -exports.fetchDetails = function(test) { - - test.expect(14); - - var testOid = new git.Oid(); - testOid.mkstr('cb76e3c030ab29db332aff3b297dc39451a84762'); - testRepo.open(path.resolve('../.git'), function() { - var testCommit = new git.Commit(); - testCommit.lookup(testRepo, testOid, function(error, commit) { - commit.fetchDetails(function(error, details) { - - var expected = { - sha: 'cb76e3c030ab29db332aff3b297dc39451a84762', - message: 'bumped package.json up\n', - time: 1300145116, - timeOffset: -240, - committer: - { name: 'Tim Branyen', - email: 'tim.branyen@gmail.com', - when: { time: 1300145116, offset: -240 } }, - author: - { name: 'Tim Branyen', - email: 'tim.branyen@gmail.com', - when: { time: 1300145116, offset: -240 } }, - parentCount: 1, - parentShas: [ 'b1f941c62f508db5f392a6bb0ea1d591753a045b' ] }; - - test.equals(expected.sha, details.sha, 'Expected SHA does not match result'); - test.equals(expected.message, details.message, 'Expected message does not match result'); - test.equals(expected.time, details.time, 'Expected time does not match result'); - test.equals(expected.offset, details.offset, 'Expected offset does not match result'); - - test.equals(expected.committer.name, details.committer.name, 'Expected committer.name does not match result'); - test.equals(expected.committer.email, details.committer.email, 'Expected committer.email does not match result'); - test.equals(expected.committer.when.time, details.committer.when.time, 'Expected committer.when.time does not match result'); - test.equals(expected.committer.when.offset, details.committer.when.offset, 'Expected committer.when.offset does not match result'); - - test.equals(expected.author.name, details.author.name, 'Expected author.name does not match result'); - test.equals(expected.author.email, details.author.email, 'Expected author.email does not match result'); - test.equals(expected.author.when.time, details.author.when.time, 'Expected author.when.time does not match result'); - test.equals(expected.author.when.offset, details.author.when.offset, 'Expected author.when.offset does not match result'); - - test.equals(expected.parentCount, details.parentCount, 'Expected parentCount does not match result'); - test.equals(expected.parentShas[0], details.parentShas[0], 'Expected parentShas[0] does not match result'); - - test.done(); + testOid.fromString('cb76e3c030ab29db332aff3b297dc39451a84762', function(error, testOid) { + testCommit.lookup(testRepo, testOid, function(err) { + test.equal(null, err, 'Valid commit'); + test.done(); + }); }); }); }); diff --git a/test/raw-object.js b/test/raw-object.js deleted file mode 100644 index df9235a8b..000000000 --- a/test/raw-object.js +++ /dev/null @@ -1,38 +0,0 @@ -var git = require('../').raw, - rimraf = require('rimraf'); - -// Helper functions -var helper = { - // Test if obj is a true function - testFunction: function(test, obj, label) { - // The object reports itself as a function - test(typeof obj, 'function', label +' reports as a function.'); - // This ensures the repo is actually a derivative of the Function [[Class]] - test(toString.call(obj), '[object Function]', label +' [[Class]] is of type function.'); - }, - // Test code and handle exception thrown - testException: function(test, fun, label) { - try { - fun(); - test(false, label); - } - catch (ex) { - test(true, label); - } - } -}; - -var repo = new git.Repo(); - -// Obj -exports.constructor = function(test){ - test.expect(3); - - // Test for function - helper.testFunction(test.equals, git.Object, 'GitObject'); - - // Ensure we get an instance of Obj - test.ok(new git.Object() instanceof git.Object, 'Invocation returns an instance of GitObject'); - - test.done(); -}; diff --git a/test/raw-oid.js b/test/raw-oid.js index 20219a6f8..126a6fd31 100644 --- a/test/raw-oid.js +++ b/test/raw-oid.js @@ -35,64 +35,48 @@ exports.constructor = function(test){ test.done(); }; -// Oid::Mkstr -exports.mkstr = function(test) { - var testOid = new git.Oid(); - +// Oid::FromString +exports.fromString = function(test) { test.expect(6); + var testOid = new git.Oid(); + // Test for function - helper.testFunction(test.equals, testOid.mkstr, 'Oid::Mkstr'); + helper.testFunction(test.equals, testOid.fromString, 'Oid::FromString'); // Test path argument existence helper.testException(test.ok, function() { - testOid.mkstr(); + testOid.fromString(); }, 'Throw an exception if no hex String'); // Test that both arguments result correctly helper.testException(test.ifError, function() { - testOid.mkstr("somestr"); + testOid.fromString("somestr", function() {}); }, 'No exception is thrown with proper arguments'); // Test invalid hex id string - test.equals(git.Error.returnCodes.GIT_ERROR, testOid.mkstr('1392DLFJIOS'), 'Invalid hex id String'); - - // Test valid hex id string - test.equals(git.Error.returnCodes.GIT_OK, testOid.mkstr('1810DFF58D8A660512D4832E740F692884338CCD'), 'Valid hex id String'); - - test.done(); + testOid.fromString('1392DLFJIOS', function(error, oid) { + test.notEqual(null, error, 'Invalid hex id String'); + testOid.fromString('1810DFF58D8A660512D4832E740F692884338CCD', function(error, oid) { + // Test valid hex id string + test.equal(null, error, 'Valid hex id String'); + test.done(); + }); + }); }; -// Oid::Fmt -exports.fmt = function(test) { - var testOid = new git.Oid(); - +// Oid::Sha +exports.sha = function(test) { test.expect(3); - - // Test for function - helper.testFunction(test.equals, testOid.fmt, 'Oid::Fmt'); - - // Test valid hex id string - testOid.mkstr('1810DFF58D8A660512D4832E740F692884338CCD'); - - // Slight hackery to get this to work... should investigate oid fmt - test.equals('1810DFF58D8A660512D4832E740F692884338CCD', testOid.fmt().substring(0, 40).toUpperCase(), 'Valid hex id String'); - - test.done(); -}; - -// Oid::Fmt -exports.toString = function(test) { var testOid = new git.Oid(); - test.expect(3); - // Test for function - helper.testFunction(test.equals, testOid.toString, 'Oid::ToString'); + helper.testFunction(test.equals, testOid.sha, 'Oid::Sha'); // Test valid hex id string - testOid.mkstr('1810DFF58D8A660512D4832E740F692884338CCD'); - test.equals('1810DFF58D8A660512D4832E740F692884338CCD', testOid.toString(40).toUpperCase(), 'Valid hex id String'); - - test.done(); + var sha = '1810DFF58D8A660512D4832E740F692884338CCD'; + testOid.fromString(sha, function(error, rawOid) { + test.equals(sha, testOid.sha().toUpperCase(), 'Valid hex id String'); + test.done(); + }); }; diff --git a/test/utilities.js b/test/utilities.js new file mode 100644 index 000000000..06f9714f3 --- /dev/null +++ b/test/utilities.js @@ -0,0 +1,23 @@ +var git = require('../'), + utilities = require('../lib/utilities'); + +exports.successNoError = function(test){ + test.expect(0); + + if (utilities.success(null, function() { })) { + test.done(); + } +}; + +/** + * Test whether success function calls callback with error + */ +exports.successError = function(test){ + test.expect(3); + utilities.success(new git.error('Message', git.raw.Error.codes.GITERR_INVALID), function(error) { + test.notEqual(error, null, 'Error should not be null'); + test.equal(error.code, git.raw.Error.codes.GITERR_INVALID, 'Error code should match input'); + test.equal(error.message, 'Message', 'Error message should match input'); + test.done(); + }); +};