diff --git a/CHANGELOG.md b/CHANGELOG.md index 647bcb660..f954d31e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,40 @@ # Change Log +## [0.10.0](https://github.com/nodegit/nodegit/releases/tag/v0.10.0) (2016-02-01) + +[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.9.0...v0.10.0) + +- Clean mutexes are part of GC. No longer leaves processes running after the script ends [PR #880](https://github.com/nodegit/nodegit/pull/880 +- Increased the performance of `ConvenientPatch` by an order of magnitude [PR #883](https://github.com/nodegit/nodegit/pull/883 + +# API changes +- `ConvenientPatch` + - `ConvenientPatch` does not have a `patch` or a `delta` property associated with it, if you were using the `delta`, please just use prototype methods `oldFIle`, `newFile`, and `Status`, which are stripped directly from the `delta`. + - `ConvenientPatch#hunks` returns a promise with an array of `ConvenientHunks`. +- `ConvenientHunk` + - `ConvenientHunk` does not have an exposed diffHunk associated with it, but does have the same members as diffHunk: + - `size()` : number of lines in the hunk + - `oldStart()` : old starting position + - `oldLines()` : number of lines in old file + - `newStart()` : new starting position + - `newLines()` : number of lines in new file + - `headerLen()` : length of header + - `header()` : returns the header of the hunk + - `lines()` : returns a promise containing `DiffLines`, not `ConvenientLines`. +- `DiffLine` +- `DiffLine` now contains the members `rawContent()` and `content()`. + - `rawContent()` contains the unformatted content of the line. This is no longer a string from the line to the end of the file. + - `content()` contains the utf8 formatted content of the line. + ## [0.9.0](https://github.com/nodegit/nodegit/releases/tag/v0.9.0) (2016-01-21) -[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.8.0...0.9.0) +[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.8.0...v0.9.0) - Thread safe fix to stop crashing on releasing mutexes [PR #876](https://github.com/nodegit/nodegit/pull/876) - `Submodule#setIgnore`, `Submodule#setUpdate`, and `Submodule#setUrl` are now all async. `Submodule#status` and `Submodule#location` are now available [PR #867](https://github.com/nodegit/nodegit/pull/867) and [PR #870](https://github.com/nodegit/nodegit/pull/870) - `Remote#defaultBranch` is now available [PR #872](https://github.com/nodegit/nodegit/pull/872) - `Repository#mergeBranches` now takes in a `MergeOptions` parameter [PR #873](https://github.com/nodegit/nodegit/pull/873) -- Remove a NodeGit specific hack to make `Index#addAll` faster since that is fixed in libgit2 [PR #875](https://github.com/nodegit/nodegit/pull/875) +- Remove a NodeGit specific hack to make `Index#addAll` faster since that is fixed in libgit2 [PR #875](https://github.com/nodegit/nodegit/pull/875)) ## [0.8.0](https://github.com/nodegit/nodegit/releases/tag/v0.8.0) (2016-01-15) diff --git a/README.md b/README.md index 83c6dcb4d..f8206d211 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ NodeGit -**Stable: 0.9.0** +**Stable: 0.10.0** ## Have a problem? Come chat with us! ## diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index a64a9b4e9..ffd28f344 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1344,6 +1344,9 @@ } }, "patch": { + "dependencies": [ + "../include/convenient_patch.h" + ], "functions": { "git_patch_free": { "ignore": true diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 57e32ea91..708b78982 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -108,6 +108,28 @@ }, "new" : { "functions": { + "git_patch_convenient_from_diff": { + "args": [ + { + "name": "diff", + "type": "git_diff *" + }, + { + "name": "out", + "type": "std::vector *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/patches/convenient_patches.cc", + "isAsync": true, + "isPrototypeMethod": false, + "group": "patch", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_rebase_next": { "type": "function", "file": "rebase.h", @@ -232,6 +254,12 @@ "git_odb_object_type" ] ], + [ + "patch", + [ + "git_patch_convenient_from_diff" + ] + ], [ "revwalk", [ diff --git a/generate/templates/manual/include/convenient_hunk.h b/generate/templates/manual/include/convenient_hunk.h new file mode 100644 index 000000000..63d77d900 --- /dev/null +++ b/generate/templates/manual/include/convenient_hunk.h @@ -0,0 +1,75 @@ +#ifndef CONVENIENTHUNK_H +#define CONVENIENTHUNK_H +// generated from class_header.h +#include +#include + +#include "async_baton.h" +#include "promise_completion.h" + +extern "C" { +#include +} + +#include "../include/typedefs.h" + +struct HunkData { + git_diff_hunk hunk; + std::vector *lines; + size_t numLines; +}; + +void HunkDataFree(HunkData *hunk); + +using namespace node; +using namespace v8; + +class ConvenientHunk : public Nan::ObjectWrap { + public: + static Nan::Persistent constructor_template; + static void InitializeComponent (Local target); + + static Local New(void *raw); + + HunkData *GetValue(); + char *GetHeader(); + size_t GetSize(); + + private: + ConvenientHunk(HunkData *hunk); + ~ConvenientHunk(); + + HunkData *hunk; + + static NAN_METHOD(JSNewFunction); + static NAN_METHOD(Size); + + static NAN_METHOD(OldStart); + static NAN_METHOD(OldLines); + static NAN_METHOD(NewStart); + static NAN_METHOD(NewLines); + static NAN_METHOD(HeaderLen); + static NAN_METHOD(Header); + + struct LinesBaton { + HunkData *hunk; + std::vector *lines; + }; + class LinesWorker : public Nan::AsyncWorker { + public: + LinesWorker( + LinesBaton *_baton, + Nan::Callback *callback + ) : Nan::AsyncWorker(callback) + , baton(_baton) {}; + ~LinesWorker() {}; + void Execute(); + void HandleOKCallback(); + + private: + LinesBaton *baton; + }; + static NAN_METHOD(Lines); +}; + +#endif diff --git a/generate/templates/manual/include/convenient_patch.h b/generate/templates/manual/include/convenient_patch.h new file mode 100644 index 000000000..d6f6c69a1 --- /dev/null +++ b/generate/templates/manual/include/convenient_patch.h @@ -0,0 +1,108 @@ +#ifndef CONVENIENTPATCH_H +#define CONVENIENTPATCH_H +// generated from class_header.h +#include +#include + +#include "async_baton.h" +#include "promise_completion.h" + +extern "C" { +#include +} + +#include "../include/typedefs.h" +#include "../include/convenient_hunk.h" + +struct ConvenientLineStats { + size_t context; + size_t additions; + size_t deletions; +}; + +struct PatchData { + ConvenientLineStats lineStats; + git_delta_t status; + git_diff_file new_file; + git_diff_file old_file; + std::vector *hunks; + size_t numHunks; +}; + +PatchData *createFromRaw(git_patch *raw); +void PatchDataFree(PatchData *patch); + +using namespace node; +using namespace v8; + +class ConvenientPatch : public Nan::ObjectWrap { + public: + static Nan::Persistent constructor_template; + static void InitializeComponent (Local target); + + static Local New(void *raw); + + ConvenientLineStats GetLineStats(); + git_delta_t GetStatus(); + git_diff_file GetOldFile(); + git_diff_file GetNewFile(); + size_t GetNumHunks(); + PatchData *GetValue(); + + private: + ConvenientPatch(PatchData *raw); + ~ConvenientPatch(); + + PatchData *patch; + + static NAN_METHOD(JSNewFunction); + + // patch methods + static NAN_METHOD(LineStats); + + // hunk methods + static NAN_METHOD(Size); + + struct HunksBaton { + PatchData *patch; + std::vector *hunks; + }; + class HunksWorker : public Nan::AsyncWorker { + public: + HunksWorker( + HunksBaton *_baton, + Nan::Callback *callback + ) : Nan::AsyncWorker(callback) + , baton(_baton) {}; + ~HunksWorker() {}; + void Execute(); + void HandleOKCallback(); + + private: + HunksBaton *baton; + }; + + static NAN_METHOD(Hunks); + + // delta methods + static NAN_METHOD(OldFile); + static NAN_METHOD(NewFile); + + // convenient status methods + static NAN_METHOD(Status); + static NAN_METHOD(IsUnmodified); + static NAN_METHOD(IsAdded); + static NAN_METHOD(IsDeleted); + static NAN_METHOD(IsModified); + static NAN_METHOD(IsRenamed); + static NAN_METHOD(IsCopied); + static NAN_METHOD(IsIgnored); + static NAN_METHOD(IsUntracked); + static NAN_METHOD(IsTypeChange); + static NAN_METHOD(IsUnreadable); + static NAN_METHOD(IsConflicted); + + // Hunk methods +}; + +#endif diff --git a/generate/templates/manual/patches/convenient_patches.cc b/generate/templates/manual/patches/convenient_patches.cc new file mode 100644 index 000000000..38ccc44a6 --- /dev/null +++ b/generate/templates/manual/patches/convenient_patches.cc @@ -0,0 +1,123 @@ +NAN_METHOD(GitPatch::ConvenientFromDiff) { + if (info.Length() == 0 || !info[0]->IsObject()) { + return Nan::ThrowError("Diff diff is required."); + } + + if (info.Length() == 1 || !info[1]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + ConvenientFromDiffBaton *baton = new ConvenientFromDiffBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + + baton->diff = Nan::ObjectWrap::Unwrap(info[0]->ToObject())->GetValue(); + baton->out = new std::vector; + baton->out->reserve(git_diff_num_deltas(baton->diff)); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[1])); + ConvenientFromDiffWorker *worker = new ConvenientFromDiffWorker(baton, callback); + + worker->SaveToPersistent("diff", info[0]); + + Nan::AsyncQueueWorker(worker); + return; +} + +void GitPatch::ConvenientFromDiffWorker::Execute() { + giterr_clear(); + + { + LockMaster lockMaster(true, baton->diff); + std::vector patchesToBeFreed; + + for (int i = 0; i < git_diff_num_deltas(baton->diff); ++i) { + git_patch *nextPatch; + int result = git_patch_from_diff(&nextPatch, baton->diff, i); + + if (result) { + while (!patchesToBeFreed.empty()) + { + git_patch_free(patchesToBeFreed.back()); + patchesToBeFreed.pop_back(); + } + + while (!baton->out->empty()) { + PatchDataFree(baton->out->back()); + baton->out->pop_back(); + } + + baton->error_code = result; + + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + delete baton->out; + baton->out = NULL; + + return; + } + + if (nextPatch != NULL) { + baton->out->push_back(createFromRaw(nextPatch)); + } + } + + while (!patchesToBeFreed.empty()) + { + git_patch_free(patchesToBeFreed.back()); + patchesToBeFreed.pop_back(); + } + } +} + +void GitPatch::ConvenientFromDiffWorker::HandleOKCallback() { + if (baton->out != NULL) { + unsigned int size = baton->out->size(); + Local result = Nan::New(size); + + for (unsigned int i = 0; i < size; ++i) { + Nan::Set(result, Nan::New(i), ConvenientPatch::New((void *)baton->out->at(i))); + } + + delete baton->out; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv); + + return; + } + + if (baton->error) { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv); + if (baton->error->message) + { + free((void *)baton->error->message); + } + + free((void *)baton->error); + + return; + } + + if (baton->error_code < 0) { + Local err = Nan::Error("method convenientFromDiff has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + Local argv[1] = { + err + }; + callback->Call(1, argv); + + return; + } + + callback->Call(0, NULL); +} diff --git a/generate/templates/manual/src/convenient_hunk.cc b/generate/templates/manual/src/convenient_hunk.cc new file mode 100644 index 000000000..845ba4098 --- /dev/null +++ b/generate/templates/manual/src/convenient_hunk.cc @@ -0,0 +1,185 @@ +#include +#include + +extern "C" { + #include +} + +#include "../include/functions/copy.h" +#include "../include/convenient_hunk.h" +#include "../include/diff_line.h" + +using namespace std; +using namespace v8; +using namespace node; + +void HunkDataFree(HunkData *hunk) { + while (!hunk->lines->empty()) { + git_diff_line *line = hunk->lines->back(); + hunk->lines->pop_back(); + free((void *)line->content); + free((void *)line); + } + delete hunk->lines; + delete hunk; +} + +ConvenientHunk::ConvenientHunk(HunkData *raw) { + this->hunk = raw; +} + +ConvenientHunk::~ConvenientHunk() { + HunkDataFree(this->hunk); +} + +void ConvenientHunk::InitializeComponent(Local target) { + Nan::HandleScope scope; + + Local tpl = Nan::New(JSNewFunction); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->SetClassName(Nan::New("ConvenientHunk").ToLocalChecked()); + + Nan::SetPrototypeMethod(tpl, "size", Size); + Nan::SetPrototypeMethod(tpl, "lines", Lines); + + Nan::SetPrototypeMethod(tpl, "oldStart", OldStart); + Nan::SetPrototypeMethod(tpl, "oldLines", OldLines); + Nan::SetPrototypeMethod(tpl, "newStart", NewStart); + Nan::SetPrototypeMethod(tpl, "newLines", NewLines); + Nan::SetPrototypeMethod(tpl, "headerLen", HeaderLen); + Nan::SetPrototypeMethod(tpl, "header", Header); + + Local _constructor_template = Nan::GetFunction(tpl).ToLocalChecked(); + constructor_template.Reset(_constructor_template); + Nan::Set(target, Nan::New("ConvenientHunk").ToLocalChecked(), _constructor_template); +} + +NAN_METHOD(ConvenientHunk::JSNewFunction) { + + if (info.Length() == 0 || !info[0]->IsExternal()) { + return Nan::ThrowError("A new ConvenientHunk cannot be instantiated."); + } + + ConvenientHunk* object = new ConvenientHunk(static_cast(Local::Cast(info[0])->Value())); + object->Wrap(info.This()); + + info.GetReturnValue().Set(info.This()); +} + +Local ConvenientHunk::New(void *raw) { + Nan::EscapableHandleScope scope; + Local argv[1] = { Nan::New((void *)raw) }; + return scope.Escape(Nan::NewInstance(Nan::New(ConvenientHunk::constructor_template), 1, argv).ToLocalChecked()); +} + +HunkData *ConvenientHunk::GetValue() { + return this->hunk; +} + +size_t ConvenientHunk::GetSize() { + return this->hunk->numLines; +} + +NAN_METHOD(ConvenientHunk::Size) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetSize()); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientHunk::Lines) { + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + LinesBaton *baton = new LinesBaton; + + baton->hunk = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + LinesWorker *worker = new LinesWorker(baton, callback); + + worker->SaveToPersistent("hunk", info.This()); + + Nan::AsyncQueueWorker(worker); + return; +} + +void ConvenientHunk::LinesWorker::Execute() { + baton->lines = new std::vector; + baton->lines->reserve(baton->hunk->numLines); + for (unsigned int i = 0; i < baton->hunk->numLines; ++i) { + git_diff_line *storeLine = (git_diff_line *)malloc(sizeof(git_diff_line)); + storeLine->origin = baton->hunk->lines->at(i)->origin; + storeLine->old_lineno = baton->hunk->lines->at(i)->old_lineno; + storeLine->new_lineno = baton->hunk->lines->at(i)->new_lineno; + storeLine->num_lines = baton->hunk->lines->at(i)->num_lines; + storeLine->content_len = baton->hunk->lines->at(i)->content_len; + storeLine->content_offset = baton->hunk->lines->at(i)->content_offset; + storeLine->content = strdup(baton->hunk->lines->at(i)->content); + baton->lines->push_back(storeLine); + } +} + +void ConvenientHunk::LinesWorker::HandleOKCallback() { + unsigned int size = baton->lines->size(); + Local result = Nan::New(size); + + for(unsigned int i = 0; i < size; ++i) { + Nan::Set(result, Nan::New(i), GitDiffLine::New(baton->lines->at(i), true)); + } + + delete baton->lines; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv); +} + +NAN_METHOD(ConvenientHunk::OldStart) { + Local to; + int old_start = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.old_start; + info.GetReturnValue().Set(Nan::New(old_start)); +} + + +NAN_METHOD(ConvenientHunk::OldLines) { + Local to; + int old_lines = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.old_lines; + info.GetReturnValue().Set(Nan::New(old_lines)); +} + +NAN_METHOD(ConvenientHunk::NewStart) { + Local to; + int new_start = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.new_start; + info.GetReturnValue().Set(Nan::New(new_start)); +} + +NAN_METHOD(ConvenientHunk::NewLines) { + Local to; + int new_lines = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.new_lines; + info.GetReturnValue().Set(Nan::New(new_lines)); +} + +NAN_METHOD(ConvenientHunk::HeaderLen) { + Local to; + size_t header_len = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.header_len; + info.GetReturnValue().Set(Nan::New(header_len)); +} + +NAN_METHOD(ConvenientHunk::Header) { + Local to; + + char *header = Nan::ObjectWrap::Unwrap(info.This())->GetValue()->hunk.header; + if (header) { + to = Nan::New(header).ToLocalChecked(); + } else { + to = Nan::Null(); + } + + info.GetReturnValue().Set(to); +} + +Nan::Persistent ConvenientHunk::constructor_template; diff --git a/generate/templates/manual/src/convenient_patch.cc b/generate/templates/manual/src/convenient_patch.cc new file mode 100644 index 000000000..ff4d54306 --- /dev/null +++ b/generate/templates/manual/src/convenient_patch.cc @@ -0,0 +1,390 @@ +#include +#include + +extern "C" { + #include +} + +#include "../include/convenient_hunk.h" +#include "../include/convenient_patch.h" +#include "../include/functions/copy.h" +#include "../include/diff_file.h" + +using namespace std; +using namespace v8; +using namespace node; + +void PatchDataFree(PatchData *patch) { + free((void *)patch->old_file.path); + free((void *)patch->new_file.path); + while(!patch->hunks->empty()) { + HunkData *hunk = patch->hunks->back(); + patch->hunks->pop_back(); + while (!hunk->lines->empty()) { + git_diff_line *line = hunk->lines->back(); + hunk->lines->pop_back(); + free((void *)line->content); + free((void *)line); + } + delete hunk; + } + delete patch; +} + +PatchData *createFromRaw(git_patch *raw) { + PatchData *patch = new PatchData; + const git_diff_delta *delta = git_patch_get_delta(raw); + + patch->status = delta->status; + + patch->old_file = delta->old_file; + patch->old_file.path = strdup(delta->old_file.path); + + patch->new_file = delta->new_file; + patch->new_file.path = strdup(delta->new_file.path); + + git_patch_line_stats( + &patch->lineStats.context, + &patch->lineStats.additions, + &patch->lineStats.deletions, + raw + ); + + patch->numHunks = git_patch_num_hunks(raw); + patch->hunks = new std::vector; + patch->hunks->reserve(patch->numHunks); + + for (unsigned int i = 0; i < patch->numHunks; ++i) { + HunkData *hunkData = new HunkData; + const git_diff_hunk *hunk; + git_patch_get_hunk(&hunk, &hunkData->numLines, raw, i); + hunkData->hunk.old_start = hunk->old_start; + hunkData->hunk.old_lines = hunk->old_lines; + hunkData->hunk.new_start = hunk->new_start; + hunkData->hunk.new_lines = hunk->new_lines; + hunkData->hunk.header_len = hunk->header_len; + memcpy(&hunkData->hunk.header, &hunk->header, 128); + + hunkData->lines = new std::vector; + hunkData->lines->reserve(hunkData->numLines); + + + bool EOFFlag = false; + for (unsigned int j = 0; j < hunkData->numLines; ++j) { + git_diff_line *storeLine = (git_diff_line *)malloc(sizeof(git_diff_line)); + const git_diff_line *line; + git_patch_get_line_in_hunk(&line, raw, i, j); + + if ( + j == 0 && + !strcmp( + &line->content[strlen(line->content) - 29], + "\n\\ No newline at end of file\n" + )) { + EOFFlag = true; + } + + storeLine->origin = line->origin; + storeLine->old_lineno = line->old_lineno; + storeLine->new_lineno = line->new_lineno; + storeLine->num_lines = line->num_lines; + storeLine->content_len = line->content_len; + storeLine->content_offset = line->content_offset; + char * transferContent; + if (EOFFlag) { + transferContent = (char *)malloc(storeLine->content_len + 30); + memcpy(transferContent, line->content, storeLine->content_len); + memcpy(transferContent + storeLine->content_len, "\n\\ No newline at end of file\n", 29); + transferContent[storeLine->content_len + 29] = '\0'; + } else { + transferContent = (char *)malloc(storeLine->content_len + 1); + memcpy(transferContent, line->content, storeLine->content_len); + transferContent[storeLine->content_len] = '\0'; + } + storeLine->content = strdup(transferContent); + free((void *)transferContent); + hunkData->lines->push_back(storeLine); + } + patch->hunks->push_back(hunkData); + } + + return patch; +} + +ConvenientPatch::ConvenientPatch(PatchData *raw) { + this->patch = raw; +} + +ConvenientPatch::~ConvenientPatch() { + PatchDataFree(this->patch); +} + +void ConvenientPatch::InitializeComponent(Local target) { + Nan::HandleScope scope; + + Local tpl = Nan::New(JSNewFunction); + + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->SetClassName(Nan::New("ConvenientPatch").ToLocalChecked()); + + Nan::SetPrototypeMethod(tpl, "hunks", Hunks); + Nan::SetPrototypeMethod(tpl, "lineStats", LineStats); + Nan::SetPrototypeMethod(tpl, "size", Size); + + Nan::SetPrototypeMethod(tpl, "oldFile", OldFile); + Nan::SetPrototypeMethod(tpl, "newFile", NewFile); + Nan::SetPrototypeMethod(tpl, "status", Status); + Nan::SetPrototypeMethod(tpl, "isUnmodified", IsUnmodified); + Nan::SetPrototypeMethod(tpl, "isAdded", IsAdded); + Nan::SetPrototypeMethod(tpl, "isDeleted", IsDeleted); + Nan::SetPrototypeMethod(tpl, "isModified", IsModified); + Nan::SetPrototypeMethod(tpl, "isRenamed", IsRenamed); + Nan::SetPrototypeMethod(tpl, "isCopied", IsCopied); + Nan::SetPrototypeMethod(tpl, "isIgnored", IsIgnored); + Nan::SetPrototypeMethod(tpl, "isUntracked", IsUntracked); + Nan::SetPrototypeMethod(tpl, "isTypeChange", IsTypeChange); + Nan::SetPrototypeMethod(tpl, "isUnreadable", IsUnreadable); + Nan::SetPrototypeMethod(tpl, "isConflicted", IsConflicted); + + Local _constructor_template = Nan::GetFunction(tpl).ToLocalChecked(); + constructor_template.Reset(_constructor_template); + Nan::Set(target, Nan::New("ConvenientPatch").ToLocalChecked(), _constructor_template); +} + +NAN_METHOD(ConvenientPatch::JSNewFunction) { + + if (info.Length() == 0 || !info[0]->IsExternal()) { + return Nan::ThrowError("A new ConvenientPatch cannot be instantiated."); + } + + ConvenientPatch* object = new ConvenientPatch(static_cast(Local::Cast(info[0])->Value())); + object->Wrap(info.This()); + + info.GetReturnValue().Set(info.This()); +} + +Local ConvenientPatch::New(void *raw) { + Nan::EscapableHandleScope scope; + Local argv[1] = { Nan::New((void *)raw) }; + return scope.Escape(Nan::NewInstance(Nan::New(ConvenientPatch::constructor_template), 1, argv).ToLocalChecked()); +} + +ConvenientLineStats ConvenientPatch::GetLineStats() { + return this->patch->lineStats; +} + +git_delta_t ConvenientPatch::GetStatus() { + return this->patch->status; +} + +git_diff_file ConvenientPatch::GetOldFile() { + return this->patch->old_file; +} + +git_diff_file ConvenientPatch::GetNewFile() { + return this->patch->new_file; +} + +size_t ConvenientPatch::GetNumHunks() { + return this->patch->numHunks; +} + +PatchData *ConvenientPatch::GetValue() { + return this->patch; +} + +NAN_METHOD(ConvenientPatch::Hunks) { + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + HunksBaton *baton = new HunksBaton; + + baton->patch = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + HunksWorker *worker = new HunksWorker(baton, callback); + + worker->SaveToPersistent("patch", info.This()); + + Nan::AsyncQueueWorker(worker); + return; +} + +void ConvenientPatch::HunksWorker::Execute() { + // copy hunks + baton->hunks = new std::vector; + baton->hunks->reserve(baton->patch->numHunks); + + for (unsigned int i = 0; i < baton->patch->numHunks; ++i) { + HunkData *hunkData = new HunkData; + hunkData->numLines = baton->patch->hunks->at(i)->numLines; + hunkData->hunk.old_start = baton->patch->hunks->at(i)->hunk.old_start; + hunkData->hunk.old_lines = baton->patch->hunks->at(i)->hunk.old_lines; + hunkData->hunk.new_start = baton->patch->hunks->at(i)->hunk.new_start; + hunkData->hunk.new_lines = baton->patch->hunks->at(i)->hunk.new_lines; + hunkData->hunk.header_len = baton->patch->hunks->at(i)->hunk.header_len; + memcpy(&hunkData->hunk.header, &baton->patch->hunks->at(i)->hunk.header, 128); + + hunkData->lines = new std::vector; + hunkData->lines->reserve(hunkData->numLines); + + for (unsigned int j = 0; j < hunkData->numLines; ++j) { + git_diff_line *storeLine = (git_diff_line *)malloc(sizeof(git_diff_line)); + storeLine->origin = baton->patch->hunks->at(i)->lines->at(j)->origin; + storeLine->old_lineno = baton->patch->hunks->at(i)->lines->at(j)->old_lineno; + storeLine->new_lineno = baton->patch->hunks->at(i)->lines->at(j)->new_lineno; + storeLine->num_lines = baton->patch->hunks->at(i)->lines->at(j)->num_lines; + storeLine->content_len = baton->patch->hunks->at(i)->lines->at(j)->content_len; + storeLine->content_offset = baton->patch->hunks->at(i)->lines->at(j)->content_offset; + storeLine->content = strdup(baton->patch->hunks->at(i)->lines->at(j)->content); + hunkData->lines->push_back(storeLine); + } + baton->hunks->push_back(hunkData); + } +} + +void ConvenientPatch::HunksWorker::HandleOKCallback() { + unsigned int size = baton->hunks->size(); + Local result = Nan::New(size); + + for(unsigned int i = 0; i < size; ++i) { + Nan::Set(result, Nan::New(i), ConvenientHunk::New(baton->hunks->at(i))); + } + + delete baton->hunks; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv); +} + +NAN_METHOD(ConvenientPatch::LineStats) { + Nan::EscapableHandleScope scope; + + Local to; + Local toReturn = Nan::New(); + ConvenientLineStats stats = Nan::ObjectWrap::Unwrap(info.This())->GetLineStats(); + + to = Nan::New(stats.context); + Nan::Set(toReturn, Nan::New("total_context").ToLocalChecked(), to); + to = Nan::New(stats.additions); + Nan::Set(toReturn, Nan::New("total_additions").ToLocalChecked(), to); + to = Nan::New(stats.deletions); + Nan::Set(toReturn, Nan::New("total_deletions").ToLocalChecked(), to); + + return info.GetReturnValue().Set(scope.Escape(toReturn)); +} + +NAN_METHOD(ConvenientPatch::Size) { + Local to; + + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetNumHunks()); + + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::OldFile) { + Nan::EscapableHandleScope scope; + + Local to; + git_diff_file *old_file = (git_diff_file *)malloc(sizeof(git_diff_file)); + *old_file = Nan::ObjectWrap::Unwrap(info.This())->GetOldFile(); + + to = GitDiffFile::New((void *)old_file, true); + + return info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::NewFile) { + Nan::EscapableHandleScope scope; + + Local to; + git_diff_file *new_file = (git_diff_file *)malloc(sizeof(git_diff_file)); + *new_file = Nan::ObjectWrap::Unwrap(info.This())->GetNewFile(); + if (new_file != NULL) { + to = GitDiffFile::New((void *)new_file, true); + } else { + to = Nan::Null(); + } + + return info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::Status) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus()); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsUnmodified) { + Nan::EscapableHandleScope scope; + + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_UNMODIFIED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsAdded) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_ADDED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsDeleted) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_DELETED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsModified) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_MODIFIED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsRenamed) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_RENAMED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsCopied) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_COPIED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsIgnored) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_IGNORED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsUntracked) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_UNTRACKED); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsTypeChange) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_TYPECHANGE); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsUnreadable) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_UNREADABLE); + info.GetReturnValue().Set(to); +} + +NAN_METHOD(ConvenientPatch::IsConflicted) { + Local to; + to = Nan::New(Nan::ObjectWrap::Unwrap(info.This())->GetStatus() == GIT_DELTA_CONFLICTED); + info.GetReturnValue().Set(to); +} + +Nan::Persistent ConvenientPatch::constructor_template; diff --git a/generate/templates/manual/src/functions/copy.cc b/generate/templates/manual/src/functions/copy.cc index a7b59c199..48d20ce38 100644 --- a/generate/templates/manual/src/functions/copy.cc +++ b/generate/templates/manual/src/functions/copy.cc @@ -2,6 +2,7 @@ #include #include "git2.h" +#include "git2/diff.h" const git_error *git_error_dup(const git_error *arg) { git_error *result = (git_error *)malloc(sizeof(git_error)); diff --git a/generate/templates/manual/src/lock_master.cc b/generate/templates/manual/src/lock_master.cc index d7d8239f5..bc26b9900 100644 --- a/generate/templates/manual/src/lock_master.cc +++ b/generate/templates/manual/src/lock_master.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -31,10 +32,8 @@ class LockMasterImpl { // A libuv key used to store the current thread-specific LockMasterImpl instance static uv_key_t currentLockMasterKey; - // A libuv async handle used to trigger / debounce mutex cleanup - static uv_async_t cleanupMutexesHandle; // Cleans up any mutexes that are not currently used - static void CleanupMutexes(uv_async_t *async); + static NAN_GC_CALLBACK(CleanupMutexes); public: static void Initialize(); @@ -49,7 +48,6 @@ class LockMasterImpl { std::vector GetMutexes(int useCountDelta); void Register(); void Unregister(); - void CleanupMutexes(); public: static LockMasterImpl *CurrentLockMasterImpl() { @@ -64,7 +62,6 @@ class LockMasterImpl { ~LockMasterImpl() { Unregister(); Unlock(true); - CleanupMutexes(); } void ObjectToLock(const void *objectToLock) { @@ -78,16 +75,22 @@ class LockMasterImpl { std::map LockMasterImpl::mutexes; uv_mutex_t LockMasterImpl::mapMutex; uv_key_t LockMasterImpl::currentLockMasterKey; -uv_async_t LockMasterImpl::cleanupMutexesHandle; - void LockMasterImpl::Initialize() { uv_mutex_init(&mapMutex); uv_key_create(¤tLockMasterKey); - uv_async_init(uv_default_loop(), &cleanupMutexesHandle, CleanupMutexes); + Nan::AddGCEpilogueCallback(CleanupMutexes); } -void LockMasterImpl::CleanupMutexes(uv_async_t *async) { +NAN_GC_CALLBACK(LockMasterImpl::CleanupMutexes) { + // skip cleanup if thread safety is disabled + // this means that turning thread safety on and then off + // could result in remaining mutexes - but they would get cleaned up + // if thread safety is turned on again + if (!LockMaster::IsEnabled()) { + return; + } + uv_mutex_lock(&mapMutex); for (auto it = mutexes.begin(); it != mutexes.end(); ) @@ -197,12 +200,6 @@ void LockMasterImpl::Unlock(bool releaseMutexes) { GetMutexes(releaseMutexes * -1); } -void LockMasterImpl::CleanupMutexes() { - // schedule mutex cleanup on the main event loop - // this somewhat delays and debounces cleanup (uv_async_send coalesces calls) - uv_async_send(&cleanupMutexesHandle); -} - LockMaster::Diagnostics LockMasterImpl::GetDiagnostics() { LockMaster::Diagnostics diagnostics; uv_mutex_lock(&LockMasterImpl::mapMutex); diff --git a/generate/templates/templates/binding.gyp b/generate/templates/templates/binding.gyp index 396ce199f..81b7a8708 100644 --- a/generate/templates/templates/binding.gyp +++ b/generate/templates/templates/binding.gyp @@ -20,6 +20,8 @@ "src/wrapper.cc", "src/functions/copy.cc", "src/functions/sleep_for_ms.cc", + "src/convenient_patch.cc", + "src/convenient_hunk.cc", "src/str_array_converter.cc", {% each %} {% if type != "enum" %} diff --git a/generate/templates/templates/nodegit.cc b/generate/templates/templates/nodegit.cc index 255681420..dde6974f2 100644 --- a/generate/templates/templates/nodegit.cc +++ b/generate/templates/templates/nodegit.cc @@ -15,6 +15,8 @@ #include "../include/{{ filename }}.h" {% endif %} {% endeach %} +#include "../include/convenient_patch.h" +#include "../include/convenient_hunk.h" void LockMasterEnable(const FunctionCallbackInfo& info) { LockMaster::Enable(); @@ -51,6 +53,9 @@ extern "C" void init(Local target) { {% endif %} {% endeach %} + ConvenientHunk::InitializeComponent(target); + ConvenientPatch::InitializeComponent(target); + NODE_SET_METHOD(target, "enableThreadSafety", LockMasterEnable); NODE_SET_METHOD(target, "disableThreadSafety", LockMasterDisable); NODE_SET_METHOD(target, "isThreadSafetyEnabled", LockMasterIsEnabled); diff --git a/generate/templates/templates/nodegit.js b/generate/templates/templates/nodegit.js index 608dd1c2d..da27db6cd 100644 --- a/generate/templates/templates/nodegit.js +++ b/generate/templates/templates/nodegit.js @@ -50,6 +50,14 @@ catch (ex) { {% endif %} {% endeach %} + +var _ConvenientPatch = rawApi.ConvenientPatch; +var _ConvenientPatch_hunks = _ConvenientPatch.prototype.hunks; +_ConvenientPatch.prototype.hunks = promisify(_ConvenientPatch_hunks); + +var _ConvenientHunk = rawApi.ConvenientHunk; +var _ConvenientHunk_lines = _ConvenientHunk.prototype.lines; +_ConvenientHunk.prototype.lines = promisify(_ConvenientHunk_lines); /* jshint ignore:end */ // Set the exports prototype to the raw API. @@ -73,9 +81,6 @@ require("./utils/normalize_options"); require("./utils/shallow_clone"); // Load up extra types; -require("./convenient_line"); -require("./convenient_hunk"); -require("./convenient_patch"); require("./status_file"); require("./enums.js"); diff --git a/lib/convenient_hunk.js b/lib/convenient_hunk.js deleted file mode 100644 index 4152e1324..000000000 --- a/lib/convenient_hunk.js +++ /dev/null @@ -1,54 +0,0 @@ -var NodeGit = require("../"); -var ConvenientLine = NodeGit.ConvenientLine; - -function ConvenientHunk(hunk, linesInHunk, patch, i) { - this.hunk = hunk; - this.linesInHunk = linesInHunk; - this.patch = patch; - this.i = i; -} - -/** - * Diff header string that represents the context of this hunk - * of the diff. Something like `@@ -169,14 +167,12 @@ ...` - * @return {String} - */ -ConvenientHunk.prototype.header = function() { - return this.hunk.header(); -}; - -/** - * Number of lines in this hunk - * @return {Number} - */ -ConvenientHunk.prototype.size = function() { - return this.linesInHunk; -}; - -/** - * The lines in this hunk - * @async - * @return {Array} a promise that resolves to an array of - * ConvenientLines - */ -ConvenientHunk.prototype.lines = function() { - var _this = this; - var size = _this.size(); - var linePromises = []; - var i; - - function makeLinePromise(i) { - return _this.patch.getLineInHunk(_this.i, i) - .then(function(line) { - return new ConvenientLine(line, i); - }); - } - - for (i = 0; i < size; i++) { - linePromises.push(makeLinePromise(i)); - } - - return Promise.all(linePromises); -}; - -NodeGit.ConvenientHunk = ConvenientHunk; diff --git a/lib/convenient_line.js b/lib/convenient_line.js deleted file mode 100644 index ae2f6f577..000000000 --- a/lib/convenient_line.js +++ /dev/null @@ -1,79 +0,0 @@ -var NodeGit = require("../"); - -function ConvenientLine(raw, i) { - this.raw = raw; - this.i = i; - this._cache = {}; -} - -/** -* The type of the line (context, addition, deletion, etc...) -* @return {Diff.LINE} -*/ -ConvenientLine.prototype.origin = function() { - return this.raw.origin(); -}; - -/** -* Line number in old file or -1 for added line -* @return {Number} -*/ -ConvenientLine.prototype.oldLineno = function() { - return this.raw.oldLineno(); -}; - -/** -* Line number in new file or -1 for deleted line -* @return {Number} -*/ -ConvenientLine.prototype.newLineno = function() { - return this.raw.newLineno(); -}; - -/** -* Number of newline characters in content -* @return {Number} -*/ -ConvenientLine.prototype.numLines = function() { - return this.raw.numLines(); -}; - -/** -* Number of bytes in the string -* @return {Number} -*/ -ConvenientLine.prototype.contentLen = function() { - return this.raw.contentLen(); -}; - -/** -* Offset in the original file to the content -* @return {Number} -*/ -ConvenientLine.prototype.contentOffset = function() { - return this.raw.contentOffset(); -}; - -/** -* Pointer to diff text, not NUL-byte terminated -* @return {String} -*/ -ConvenientLine.prototype.rawContent = function() { - return this.raw.content(); -}; - -/** -* The relevant line -* @return {String} -*/ -ConvenientLine.prototype.content = function() { - if (!this._cache.content) { - this._cache.content = new Buffer(this.raw.content()) - .slice(0, this.raw.contentLen()) - .toString("utf8"); - } - - return this._cache.content; -}; - -NodeGit.ConvenientLine = ConvenientLine; diff --git a/lib/convenient_patch.js b/lib/convenient_patch.js deleted file mode 100644 index 75dfa2b9e..000000000 --- a/lib/convenient_patch.js +++ /dev/null @@ -1,178 +0,0 @@ -var NodeGit = require("../"); -var Diff = NodeGit.Diff; -var ConvenientHunk = NodeGit.ConvenientHunk; - -function ConvenientPatch(delta, patch, i) { - this.delta = delta; - this.patch = patch; - this.i = i; -} - -/** - * Old name of the file - * @return {String} - */ -ConvenientPatch.prototype.oldFile = function() { - return this.delta.oldFile(); -}; - -/** - * New name of the file - * @return {String} - */ -ConvenientPatch.prototype.newFile = function() { - return this.delta.newFile(); -}; - -/** - * The number of hunks in this patch - * @return {Number} - */ -ConvenientPatch.prototype.size = function() { - return this.patch.numHunks(); -}; - -/** - * The hunks in this patch - * @async - * @return {Array} a promise that resolves to an array of - * ConvenientHunks - */ -ConvenientPatch.prototype.hunks = function() { - var _this = this; - var size = _this.size(); - var hunkPromises = []; - var i; - - function makeHunkPromise(i) { - return _this.patch.getHunk(i) - .then(function(hunkWithLineCount) { - return new ConvenientHunk( - hunkWithLineCount.hunk, - hunkWithLineCount.linesInHunk, - _this.patch, - i - ); - }); - } - - for (i = 0; i < size; i++) { - hunkPromises.push(makeHunkPromise(i)); - } - - return Promise.all(hunkPromises); -}; - -/** - * The status of this patch (unmodified, added, deleted) - * @return {Number} - */ -ConvenientPatch.prototype.status = function() { - return this.delta.status(); -}; - -/** - * @typedef lineStats - * @type {Object} - * @property {number} total_context # of contexts in the patch - * @property {number} total_additions # of lines added in the patch - * @property {number} total_deletions # of lines deleted in the patch - */ - -/** - * The line statistics of this patch (#contexts, #added, #deleted) - * @return {lineStats} - */ -ConvenientPatch.prototype.lineStats = function() { - return this.patch.lineStats(); -}; - -/** - * Is this an unmodified patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isUnmodified = function() { - return this.status() == Diff.DELTA.UNMODIFIED; -}; - -/** - * Is this an added patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isAdded = function() { - return this.status() == Diff.DELTA.ADDED; -}; - -/** - * Is this a deleted patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isDeleted = function() { - return this.status() == Diff.DELTA.DELETED; -}; - -/** - * Is this an modified patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isModified = function() { - return this.status() == Diff.DELTA.MODIFIED; -}; - -/** - * Is this a renamed patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isRenamed = function() { - return this.status() == Diff.DELTA.RENAMED; -}; - -/** - * Is this a copied patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isCopied = function() { - return this.status() == Diff.DELTA.COPIED; -}; - -/** - * Is this an ignored patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isIgnored = function() { - return this.status() == Diff.DELTA.IGNORED; -}; - -/** - * Is this an untracked patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isUntracked = function() { - return this.status() == Diff.DELTA.UNTRACKED; -}; - -/** - * Is this a type change? - * @return {Boolean} - */ -ConvenientPatch.prototype.isTypeChange = function() { - return this.status() == Diff.DELTA.TYPECHANGE; -}; - -/** - * Is this an undreadable patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isUnreadable = function() { - return this.status() == Diff.DELTA.UNREADABLE; -}; - -/** - * Is this a conflicted patch? - * @return {Boolean} - */ -ConvenientPatch.prototype.isConflicted = function() { - return this.status() == Diff.DELTA.CONFLICTED; -}; - -NodeGit.ConvenientPatch = ConvenientPatch; diff --git a/lib/diff.js b/lib/diff.js index efa33148c..bf3eccb82 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -1,6 +1,5 @@ var NodeGit = require("../"); var Diff = NodeGit.Diff; -var ConvenientPatch = NodeGit.ConvenientPatch; var normalizeOptions = NodeGit.Utils.normalizeOptions; var Patch = NodeGit.Patch; @@ -13,23 +12,7 @@ var Patch = NodeGit.Patch; * ConvenientPatches */ Diff.prototype.patches = function() { - var _this = this; - var size = _this.numDeltas(); - var patchPromises = []; - var i; - - function makePatchPromise(i) { - return Patch.fromDiff(_this, i) - .then(function(patch) { - return new ConvenientPatch(_this.getDelta(i), patch, i); - }); - } - - for (i = 0; i < size; i++) { - patchPromises.push(makePatchPromise(i)); - } - - return Promise.all(patchPromises); + return Patch.convenientFromDiff(this); }; // Override Diff.indexToWorkdir to normalize opts diff --git a/lib/diff_line.js b/lib/diff_line.js new file mode 100644 index 000000000..fdd2e9df6 --- /dev/null +++ b/lib/diff_line.js @@ -0,0 +1,31 @@ +var NodeGit = require("../"); +var DiffLine = NodeGit.DiffLine; + +/** +* The non utf8 translated text +* @return {String} +*/ +var rawContent = DiffLine.prototype.content; +DiffLine.prototype.rawContent = function() { + return rawContent.call(this); +}; + +/** +* The relevant line +* @return {String} +*/ +DiffLine.prototype.content = function() { + if (!this._cache) { + this._cache = {}; + } + + if (!this._cache.content) { + this._cache.content = new Buffer(this.rawContent()) + .slice(0, this.contentLen()) + .toString("utf8"); + } + + return this._cache.content; +}; + +NodeGit.DiffLine = DiffLine; diff --git a/lib/repository.js b/lib/repository.js index 31390f0ed..50a165bf5 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -1290,7 +1290,7 @@ Repository.prototype.stageLines = (hunkLine.newLineno() === nLine.newLineno())); }); - if (hunkLine.raw.content() + if (hunkLine.content() .indexOf("\\ No newline at end of file") != -1) { return false; } @@ -1336,8 +1336,8 @@ Repository.prototype.stageLines = return Promise.all(linesPromises).then(function(results) { for (var index = 0; index < results.length && newContent.length < 1; index++) { - var hunkStart = isStaged ? pathHunks[index].hunk.newStart() - : pathHunks[index].hunk.oldStart(); + var hunkStart = isStaged ? pathHunks[index].newStart() + : pathHunks[index].oldStart(); var lines = results[index]; if (lines.filter(lineEqualsFirstNewLine).length > 0) { //add content that is before the hunk @@ -1396,7 +1396,7 @@ Repository.prototype.stageLines = var emptyPatch = false; if (pathPatch.length > 0) { //No hunks, unchanged file mode, and no type changes. - emptyPatch = pathPatch[0].patch.numHunks() === 0 && + emptyPatch = pathPatch[0].size() === 0 && pathPatch[0].oldFile().mode() == pathPatch[0].newFile().mode() && !pathPatch[0].isTypeChange(); } diff --git a/package.json b/package.json index bb94ca07d..76063d750 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nodegit", "description": "Node.js libgit2 asynchronous native bindings", - "version": "0.9.0", + "version": "0.10.0", "homepage": "http://nodegit.org", "keywords": [ "libgit2", diff --git a/test/tests/diff.js b/test/tests/diff.js index e3aad985a..42c0e624c 100644 --- a/test/tests/diff.js +++ b/test/tests/diff.js @@ -133,11 +133,11 @@ describe("Diff", function() { assert.equal(patches.length, 3); assert(patches[2].isUntracked()); - var oldFile = patches[2].delta.oldFile(); + var oldFile = patches[2].oldFile(); assert.equal(oldFile.path(), "wddiff.txt"); assert.equal(oldFile.size(), 0); - var newFile = patches[2].delta.newFile(); + var newFile = patches[2].newFile(); assert.equal(newFile.path(), "wddiff.txt"); assert.equal(newFile.size(), 23); }); diff --git a/test/tests/thread_safety.js b/test/tests/thread_safety.js index 785d20327..ee341a95a 100644 --- a/test/tests/thread_safety.js +++ b/test/tests/thread_safety.js @@ -42,18 +42,21 @@ describe("ThreadSafety", function() { } }); - it("can lock something", function() { + it("can lock something and cleanup mutex", function() { // call a sync method to guarantee that it stores a mutex, - // and that it will not clean up the mutex (since the cleanup is - // scheduled on the main node thread) + // and that it will clean up the mutex in a garbage collection cycle this.repository.headDetached(); var diagnostics = NodeGit.getThreadSafetyDiagnostics(); - if (diagnostics.isEnabled) { + if (NodeGit.isThreadSafetyEnabled()) { // this is a fairly vague test - it just tests that something // had a mutex created for it at some point (i.e., the thread safety // code is not completely dead) assert.ok(diagnostics.storedMutexesCount > 0); + // now test that GC cleans up mutexes + global.gc(); + diagnostics = NodeGit.getThreadSafetyDiagnostics(); + assert.equal(0, diagnostics.storedMutexesCount); } else { assert.equal(0, diagnostics.storedMutexesCount); }