From 99cca732b8e704ab5e768ad42d6eb7b80b141ca5 Mon Sep 17 00:00:00 2001 From: Tyler Wanek Date: Tue, 23 Oct 2018 11:28:20 -0700 Subject: [PATCH 1/8] getRemotes should return the remote objects. getRemotes has been renamed to getRemotes and getRemotes is now a method that returns the remotes for a repository --- generate/input/descriptor.json | 10 +- generate/input/libgit2-supplement.json | 28 ++++ .../manual/repository/get_remotes.cc | 134 ++++++++++++++++++ lib/repository.js | 4 +- test/tests/remote.js | 2 +- test/tests/repository.js | 2 +- 6 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 generate/templates/manual/repository/get_remotes.cc diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 2909115b6..e846c62e6 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1504,6 +1504,7 @@ } }, "dependencies": [ + "../include/git_buf_converter.h", "../include/filter_registry.h" ] }, @@ -3241,7 +3242,9 @@ "selfFreeing": true, "isSingleton": true, "dependencies": [ - "git2/sys/repository.h" + "git2/sys/repository.h", + "../include/submodule.h", + "../include/remote.h" ], "functions": { "git_repository_config": { @@ -4220,7 +4223,10 @@ "git_worktree_prune_init_options": { "ignore": true } - } + }, + "dependencies": [ + "../include/git_buf_converter.h" + ] }, "writestream": { "cType": "git_writestream", diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 7b19afba9..a40107397 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -260,6 +260,28 @@ "isErrorCode": true } }, + "git_repository_get_remotes": { + "args": [ + { + "name": "out", + "type": "std::vector *" + }, + { + "name": "repo", + "type": "git_repository *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/repository/get_remotes.cc", + "isAsync": true, + "isPrototypeMethod": true, + "group": "repository", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_reset": { "type": "function", "file": "reset.h", @@ -530,6 +552,12 @@ "git_remote_reference_list" ] ], + [ + "repository", + [ + "git_repository_get_remotes" + ] + ], [ "revwalk", [ diff --git a/generate/templates/manual/repository/get_remotes.cc b/generate/templates/manual/repository/get_remotes.cc new file mode 100644 index 000000000..e16f1131c --- /dev/null +++ b/generate/templates/manual/repository/get_remotes.cc @@ -0,0 +1,134 @@ +NAN_METHOD(GitRepository::GetRemotes) +{ + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + GetRemotesBaton* baton = new GetRemotesBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + baton->out = new std::vector; + baton->repo = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + GetRemotesWorker *worker = new GetRemotesWorker(baton, callback); + worker->SaveToPersistent("repo", info.This()); + Nan::AsyncQueueWorker(worker); + return; +} + +void GitRepository::GetRemotesWorker::Execute() +{ + giterr_clear(); + + git_repository *repo; + { + LockMaster lockMaster(true, baton->repo); + baton->error_code = git_repository_open(&repo, git_repository_workdir(baton->repo)); + } + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete baton->out; + baton->out = NULL; + return; + } + + git_strarray remote_names; + baton->error_code = git_remote_list(&remote_names, repo); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete baton->out; + baton->out = NULL; + return; + } + + for (size_t remote_index = 0; remote_index < remote_names.count; ++remote_index) { + git_remote *remote; + baton->error_code = git_remote_lookup(&remote, repo, remote_names.strings[remote_index]); + + // stop execution and return if there is an error + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + // unwind and return + while (baton->out->size()) { + git_remote *remoteToFree = baton->out->back(); + baton->out->pop_back(); + git_remote_free(remoteToFree); + } + + git_strarray_free(&remote_names); + git_repository_free(repo); + delete baton->out; + baton->out = NULL; + return; + } + + baton->out->push_back(remote); + } +} + +void GitRepository::GetRemotesWorker::HandleOKCallback() +{ + if (baton->out != NULL) + { + unsigned int size = baton->out->size(); + Local result = Nan::New(size); + for (unsigned int i = 0; i < size; i++) { + git_remote *remote = baton->out->at(i); + Nan::Set( + result, + Nan::New(i), + GitRemote::New( + remote, + true, + GitRepository::New(git_remote_owner(remote), true)->ToObject() + ) + ); + } + + delete baton->out; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv, async_resource); + } + else if (baton->error) + { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv, async_resource); + if (baton->error->message) + { + free((void *)baton->error->message); + } + + free((void *)baton->error); + } + else if (baton->error_code < 0) + { + Local err = Nan::Error("Repository refreshRemotes has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Repository.refreshRemotes").ToLocalChecked()); + Local argv[1] = { + err + }; + callback->Call(1, argv, async_resource); + } + else + { + callback->Call(0, NULL, async_resource); + } +} diff --git a/lib/repository.js b/lib/repository.js index f311900eb..0e8427c95 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -1068,7 +1068,7 @@ Repository.prototype.fetchAll = function( var certificateCheck = remoteCallbacks.certificateCheck; var transferProgress = remoteCallbacks.transferProgress; - return repo.getRemotes() + return repo.getRemoteNames() .then(function(remotes) { return remotes.reduce(function(fetchPromise, remote) { var wrappedFetchOptions = shallowClone(fetchOptions); @@ -1328,7 +1328,7 @@ Repository.prototype.getRemote = function(remote, callback) { * @param {Function} Optional callback * @return {Object} Promise object. */ -Repository.prototype.getRemotes = function(callback) { +Repository.prototype.getRemoteNames = function(callback) { return Remote.list(this).then(function(remotes) { if (typeof callback === "function") { callback(null, remotes); diff --git a/test/tests/remote.js b/test/tests/remote.js index 27611dd15..0aa502619 100644 --- a/test/tests/remote.js +++ b/test/tests/remote.js @@ -19,7 +19,7 @@ describe("Remote", function() { var privateUrl = "git@github.com:nodegit/private"; function removeNonOrigins(repo) { - return repo.getRemotes() + return repo.getRemoteNames() .then(function(remotes) { return remotes.reduce(function(promise, remote) { if (remote !== "origin") { diff --git a/test/tests/repository.js b/test/tests/repository.js index bcdfc2c3f..bce03a6cb 100644 --- a/test/tests/repository.js +++ b/test/tests/repository.js @@ -95,7 +95,7 @@ describe("Repository", function() { }); it("can list remotes", function() { - return this.repository.getRemotes() + return this.repository.getRemoteNames() .then(function(remotes) { assert.equal(remotes.length, 1); assert.equal(remotes[0], "origin"); From adcc5c520aae1a3351d9c463ee0ae7a207e666d1 Mon Sep 17 00:00:00 2001 From: Tyler Wanek Date: Tue, 23 Oct 2018 11:29:50 -0700 Subject: [PATCH 2/8] Move getReferences to C++ --- generate/input/libgit2-supplement.json | 23 +++ .../manual/repository/get_references.cc | 133 ++++++++++++++++++ lib/repository.js | 53 ++----- 3 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 generate/templates/manual/repository/get_references.cc diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index a40107397..f9da58340 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -260,6 +260,28 @@ "isErrorCode": true } }, + "git_repository_get_references": { + "args": [ + { + "name": "out", + "type": "std::vector *" + }, + { + "name": "repo", + "type": "git_repository *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/repository/get_references.cc", + "isAsync": true, + "isPrototypeMethod": true, + "group": "repository", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_repository_get_remotes": { "args": [ { @@ -555,6 +577,7 @@ [ "repository", [ + "git_repository_get_references", "git_repository_get_remotes" ] ], diff --git a/generate/templates/manual/repository/get_references.cc b/generate/templates/manual/repository/get_references.cc new file mode 100644 index 000000000..d0e4fd987 --- /dev/null +++ b/generate/templates/manual/repository/get_references.cc @@ -0,0 +1,133 @@ +NAN_METHOD(GitRepository::GetReferences) +{ + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + GetReferencesBaton* baton = new GetReferencesBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + baton->out = new std::vector; + baton->repo = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + GetReferencesWorker *worker = new GetReferencesWorker(baton, callback); + worker->SaveToPersistent("repo", info.This()); + Nan::AsyncQueueWorker(worker); + return; +} + +void GitRepository::GetReferencesWorker::Execute() +{ + giterr_clear(); + + LockMaster lockMaster(true, baton->repo); + git_repository *repo = baton->repo; + + git_strarray reference_names; + baton->error_code = git_reference_list(&reference_names, repo); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete baton->out; + baton->out = NULL; + return; + } + + for (size_t reference_index = 0; reference_index < reference_names.count; ++reference_index) { + git_reference *reference; + baton->error_code = git_reference_lookup(&reference, repo, reference_names.strings[reference_index]); + + // stop execution and return if there is an error + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + // unwind and return + while (baton->out->size()) { + git_reference *referenceToFree = baton->out->back(); + baton->out->pop_back(); + git_reference_free(referenceToFree); + } + + git_strarray_free(&reference_names); + git_repository_free(repo); + delete baton->out; + baton->out = NULL; + return; + } + + if (git_reference_type(reference) == GIT_REF_SYMBOLIC) { + git_reference *resolved_reference; + int resolve_result = git_reference_resolve(&resolved_reference, reference); + git_reference_free(reference); + + // if we can't resolve the ref, then just ignore it + if (resolve_result == GIT_OK) { + baton->out->push_back(resolved_reference); + } + } else { + baton->out->push_back(reference); + } + } +} + +void GitRepository::GetReferencesWorker::HandleOKCallback() +{ + if (baton->out != NULL) + { + unsigned int size = baton->out->size(); + Local result = Nan::New(size); + for (unsigned int i = 0; i < size; i++) { + git_reference *reference = baton->out->at(i); + Nan::Set( + result, + Nan::New(i), + GitRefs::New( + reference, + true, + GitRepository::New(git_reference_owner(reference), true)->ToObject() + ) + ); + } + + delete baton->out; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv, async_resource); + } + else if (baton->error) + { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv, async_resource); + if (baton->error->message) + { + free((void *)baton->error->message); + } + + free((void *)baton->error); + } + else if (baton->error_code < 0) + { + Local err = Nan::Error("Repository getReferences has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Repository.getReferences").ToLocalChecked()); + Local argv[1] = { + err + }; + callback->Call(1, argv, async_resource); + } + else + { + callback->Call(0, NULL, async_resource); + } +} diff --git a/lib/repository.js b/lib/repository.js index 0e8427c95..cdbad7054 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -24,6 +24,7 @@ var TreeBuilder = NodeGit.Treebuilder; var _discover = Repository.discover; var _initExt = Repository.initExt; var _fetchheadForeach = Repository.prototype.fetchheadForeach; +var _getReferences = Repository.prototype.getReferences; var _mergeheadForeach = Repository.prototype.mergeheadForeach; function applySelectedLinesToTarget @@ -349,44 +350,21 @@ Repository.initExt = function(repo_path, opts) { }; -Repository.getReferences = function(repo, type, refNamesOnly, callback) { - return Reference.list(repo).then(function(refList) { - var refFilterPromises = []; - var filteredRefs = []; - - refList.forEach(function(refName) { - refFilterPromises.push(Reference.lookup(repo, refName) - .then(function(ref) { - if (type == Reference.TYPE.LISTALL || ref.type() == type) { - if (refNamesOnly) { - filteredRefs.push(refName); - return; - } - - if (ref.isSymbolic()) { - return ref.resolve().then(function(resolvedRef) { - resolvedRef.repo = repo; - - filteredRefs.push(resolvedRef); - }) - .catch(function() { - // If we can't resolve the ref then just ignore it. - }); - } - else { - filteredRefs.push(ref); - } - } - }) - ); +Repository.getReferences = function(repo, type, refNamesOnly) { + return repo.getReferences().then(function(refList) { + var filteredRefList = refList; + + filteredRefList.filter(function(reference) { + return type == Reference.TYPE.LISTALL || ref.type === type }); - return Promise.all(refFilterPromises).then(function() { - if (typeof callback === "function") { - callback(null, filteredRefs); - } - return filteredRefs; - }, callback); + if (refNamesOnly) { + filteredRefList.map(function(reference) { + return reference.name(); + }); + } + + return filteredRefList; }); }; @@ -1289,9 +1267,6 @@ Repository.prototype.getReferenceNames = function(type, callback) { * @param {Reference.TYPE} type Type of reference to look up * @return {Array} */ -Repository.prototype.getReferences = function(type, callback) { - return Repository.getReferences(this, type, false, callback); -}; /** * Gets a remote from the repo From ac46386ab0c6e56607ccfbba95e40549169604b2 Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Wed, 17 Apr 2019 14:07:28 -0700 Subject: [PATCH 3/8] add repository getSubmodules in C++ --- generate/input/libgit2-supplement.json | 23 ++++ .../manual/repository/get_submodules.cc | 115 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 generate/templates/manual/repository/get_submodules.cc diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index f9da58340..ca4a3c3f6 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -282,6 +282,28 @@ "isErrorCode": true } }, + "git_repository_get_submodules": { + "args": [ + { + "name": "out", + "type": "std::vector *" + }, + { + "name": "repo", + "type": "git_repository *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/repository/get_submodules.cc", + "isAsync": true, + "isPrototypeMethod": true, + "group": "repository", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_repository_get_remotes": { "args": [ { @@ -578,6 +600,7 @@ "repository", [ "git_repository_get_references", + "git_repository_get_submodules", "git_repository_get_remotes" ] ], diff --git a/generate/templates/manual/repository/get_submodules.cc b/generate/templates/manual/repository/get_submodules.cc new file mode 100644 index 000000000..d51d0a0fd --- /dev/null +++ b/generate/templates/manual/repository/get_submodules.cc @@ -0,0 +1,115 @@ +NAN_METHOD(GitRepository::GetSubmodules) +{ + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + GetSubmodulesBaton* baton = new GetSubmodulesBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + baton->out = new std::vector; + baton->repo = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + GetSubmodulesWorker *worker = new GetSubmodulesWorker(baton, callback); + worker->SaveToPersistent("repo", info.This()); + Nan::AsyncQueueWorker(worker); + return; +} + +struct submodule_foreach_payload { + git_repository *repo; + std::vector *out; +}; + +int foreachSubmoduleCB(git_submodule *submodule, const char *name, void *void_payload) { + submodule_foreach_payload *payload = (submodule_foreach_payload *)void_payload; + git_submodule *out; + + int result = git_submodule_lookup(&out, payload->repo, name); + if (result == GIT_OK) { + payload->out->push_back(out); + } + + return result; +} + +void GitRepository::GetSubmodulesWorker::Execute() +{ + giterr_clear(); + + LockMaster lockMaster(true, baton->repo); + + submodule_foreach_payload payload { baton->repo, baton->out }; + baton->error_code = git_submodule_foreach(baton->repo, foreachSubmoduleCB, (void *)&payload); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + while (baton->out->size()) { + git_submodule_free(baton->out->back()); + baton->out->pop_back(); + } + delete baton->out; + baton->out = NULL; + } +} + +void GitRepository::GetSubmodulesWorker::HandleOKCallback() +{ + if (baton->out != NULL) + { + unsigned int size = baton->out->size(); + Local result = Nan::New(size); + for (unsigned int i = 0; i < size; i++) { + git_submodule *submodule = baton->out->at(i); + Nan::Set( + result, + Nan::New(i), + GitSubmodule::New( + submodule, + true, + GitRepository::New(git_submodule_owner(submodule), true)->ToObject() + ) + ); + } + + delete baton->out; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv, async_resource); + } + else if (baton->error) + { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv, async_resource); + if (baton->error->message) + { + free((void *)baton->error->message); + } + + free((void *)baton->error); + } + else if (baton->error_code < 0) + { + Local err = Nan::Error("Repository getSubmodules has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Repository.getSubmodules").ToLocalChecked()); + Local argv[1] = { + err + }; + callback->Call(1, argv, async_resource); + } + else + { + callback->Call(0, NULL, async_resource); + } +} From 9be9da2dd4707e9a3abed53064a25d0f2daeae73 Mon Sep 17 00:00:00 2001 From: Tyler Wanek Date: Wed, 24 Oct 2018 11:55:43 -0700 Subject: [PATCH 4/8] Add commit walk in C++ --- generate/input/libgit2-supplement.json | 27 ++++ .../templates/manual/revwalk/commit_walk.cc | 123 ++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 generate/templates/manual/revwalk/commit_walk.cc diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index ca4a3c3f6..69a4717e6 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -352,6 +352,32 @@ }, "group": "reset" }, + "git_revwalk_commit_walk": { + "args": [ + { + "name": "max_count", + "type": "int" + }, + { + "name": "out", + "type": "std::vector *" + }, + { + "name": "walk", + "type": "git_revwalk *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/revwalk/commit_walk.cc", + "isAsync": true, + "isPrototypeMethod": true, + "group": "revwalk", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_revwalk_fast_walk": { "args": [ { @@ -607,6 +633,7 @@ [ "revwalk", [ + "git_revwalk_commit_walk", "git_revwalk_fast_walk", "git_revwalk_file_history_walk" ] diff --git a/generate/templates/manual/revwalk/commit_walk.cc b/generate/templates/manual/revwalk/commit_walk.cc new file mode 100644 index 000000000..af0ff112a --- /dev/null +++ b/generate/templates/manual/revwalk/commit_walk.cc @@ -0,0 +1,123 @@ +NAN_METHOD(GitRevwalk::CommitWalk) { + if (info.Length() == 0 || !info[0]->IsNumber()) { + return Nan::ThrowError("Max count is required and must be a number."); + } + + if (info.Length() == 1 || !info[1]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + CommitWalkBaton* baton = new CommitWalkBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + baton->max_count = Nan::To(info[0]).FromJust(); + baton->out = new std::vector; + baton->out->reserve(baton->max_count); + baton->walk = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[1])); + CommitWalkWorker *worker = new CommitWalkWorker(baton, callback); + worker->SaveToPersistent("fastWalk", info.This()); + + Nan::AsyncQueueWorker(worker); + return; +} + +void GitRevwalk::CommitWalkWorker::Execute() { + giterr_clear(); + + for (int i = 0; i < baton->max_count; i++) { + git_oid next_commit_id; + baton->error_code = git_revwalk_next(&next_commit_id, baton->walk); + + if (baton->error_code == GIT_ITEROVER) { + baton->error_code = GIT_OK; + return; + } + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + while (baton->out->size()) { + git_commit_free(baton->out->back()); + baton->out->pop_back(); + } + + delete baton->out; + baton->out = NULL; + + return; + } + + git_commit *commit; + baton->error_code = git_commit_lookup(&commit, git_revwalk_repository(baton->walk), &next_commit_id); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + + while (baton->out->size()) { + git_commit_free(baton->out->back()); + baton->out->pop_back(); + } + + delete baton->out; + baton->out = NULL; + + return; + } + + baton->out->push_back(commit); + } +} + +void GitRevwalk::CommitWalkWorker::HandleOKCallback() { + if (baton->out != NULL) { + unsigned int size = baton->out->size(); + Local result = Nan::New(size); + for (unsigned int i = 0; i < size; i++) { + git_commit *commit = baton->out->at(i); + Nan::Set( + result, + Nan::New(i), + GitCommit::New( + commit, + true, + GitRepository::New(git_commit_owner(commit), true)->ToObject() + ) + ); + } + + delete baton->out; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv, async_resource); + } else if (baton->error) { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv, async_resource); + if (baton->error->message) { + free((void *)baton->error->message); + } + + free((void *)baton->error); + } else if (baton->error_code < 0) { + Local err = Nan::Error("Revwalk commitWalk has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Revwalk.commitWalk").ToLocalChecked()); + Local argv[1] = { + err + }; + callback->Call(1, argv, async_resource); + } else { + callback->Call(0, NULL, async_resource); + } +} From 4e039f589ecc41237207be1a3344fb0badb3a116 Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Thu, 20 Dec 2018 13:56:36 -0700 Subject: [PATCH 5/8] Add refreshReferences for bulk lookup of critical references. --- generate/input/libgit2-supplement.json | 25 +- .../manual/repository/refresh_references.cc | 532 ++++++++++++++++++ 2 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 generate/templates/manual/repository/refresh_references.cc diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 69a4717e6..42d9260b2 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -326,6 +326,28 @@ "isErrorCode": true } }, + "git_repository_refresh_references": { + "args": [ + { + "name": "out", + "type": "void *" + }, + { + "name": "repo", + "type": "git_repository *" + } + ], + "type": "function", + "isManual": true, + "cFile": "generate/templates/manual/repository/refresh_references.cc", + "isAsync": true, + "isPrototypeMethod": true, + "group": "repository", + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_reset": { "type": "function", "file": "reset.h", @@ -627,7 +649,8 @@ [ "git_repository_get_references", "git_repository_get_submodules", - "git_repository_get_remotes" + "git_repository_get_remotes", + "git_repository_refresh_references" ] ], [ diff --git a/generate/templates/manual/repository/refresh_references.cc b/generate/templates/manual/repository/refresh_references.cc new file mode 100644 index 000000000..2ee4c027d --- /dev/null +++ b/generate/templates/manual/repository/refresh_references.cc @@ -0,0 +1,532 @@ +int getOidOfReferenceCommit(git_oid *commitOid, git_reference *ref) { + git_object *commitObject; + int result = git_reference_peel(&commitObject, ref, GIT_OBJ_COMMIT); + + if (result != GIT_OK) { + return result; + } + + git_oid_cpy(commitOid, git_object_id(commitObject)); + git_object_free(commitObject); + return result; +} + +int asDirectReference(git_reference **out, git_reference *ref) { + if (git_reference_type(ref) != GIT_REF_SYMBOLIC) { + return git_reference_dup(out, ref); + } + + return git_reference_resolve(out, ref); +} + +int lookupDirectReferenceByShorthand(git_reference **out, git_repository *repo, const char *shorthand) { + git_reference *ref = NULL; + int result = git_reference_dwim(&ref, repo, shorthand); + + if (result != GIT_OK) { + return result; + } + + result = asDirectReference(out, ref); + git_reference_free(ref); + return result; +} + +int lookupDirectReferenceByFullName(git_reference **out, git_repository *repo, const char *fullName) { + git_reference *ref = NULL; + int result = git_reference_lookup(&ref, repo, fullName); + + if (result != GIT_OK) { + return result; + } + + result = asDirectReference(out, ref); + git_reference_free(ref); + return result; +} + +char *getRemoteNameOfReference(git_reference *remoteReference) { + return strtok(strdup(git_reference_shorthand(remoteReference)), "/"); +} + +bool gitStrArrayContains(git_strarray *strarray, const char *string) { + for (size_t i = 0; i < strarray->count; ++i) { + if (strcmp(strarray->strings[i], string) == 0) { + return true; + } + } + return false; +} + +class RefreshedRefModel { +public: + RefreshedRefModel(git_reference *ref): + fullName(strdup(git_reference_name(ref))), + message(NULL), + sha(new char[GIT_OID_HEXSZ + 1]), + shorthand(strdup(git_reference_shorthand(ref))), + type(NULL) + { + if (git_reference_is_branch(ref)) { + type = "branch"; + } else if (git_reference_is_remote(ref)) { + type = "remote"; + } else { + type = "tag"; + } + } + + static int fromReference(RefreshedRefModel **out, git_reference *ref) { + RefreshedRefModel *refModel = new RefreshedRefModel(ref); + git_oid referencedTargetOid; + + int result = getOidOfReferenceCommit(&referencedTargetOid, ref); + if (result != GIT_OK) { + delete refModel; + return result; + } + + if (git_reference_is_tag(ref)) { + git_repository *repo = git_reference_owner(ref); + + git_tag *referencedTag; + if (git_tag_lookup(&referencedTag, repo, &referencedTargetOid) == GIT_OK) { + refModel->message = strdup(git_tag_message(referencedTag)); + git_tag_free(referencedTag); + } + } + + git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, &referencedTargetOid); + + *out = refModel; + return GIT_OK; + } + + v8::Local toJavascript() { + v8::Local result = Nan::New(); + + v8::Local jsFullName; + if (fullName == NULL) { + jsFullName = Nan::Null(); + } else { + jsFullName = Nan::New(fullName).ToLocalChecked(); + } + Nan::Set(result, Nan::New("fullName").ToLocalChecked(), jsFullName); + + v8::Local jsMessage; + if (message == NULL) { + jsMessage = Nan::Null(); + } else { + jsMessage = Nan::New(message).ToLocalChecked(); + } + Nan::Set(result, Nan::New("message").ToLocalChecked(), jsMessage); + + Nan::Set( + result, + Nan::New("sha").ToLocalChecked(), + Nan::New(sha).ToLocalChecked() + ); + + v8::Local jsShorthand; + if (shorthand == NULL) { + jsShorthand = Nan::Null(); + } else { + jsShorthand = Nan::New(shorthand).ToLocalChecked(); + } + Nan::Set(result, Nan::New("shorthand").ToLocalChecked(), jsShorthand); + + v8::Local jsType; + if (type == NULL) { + jsType = Nan::Null(); + } else { + jsType = Nan::New(type).ToLocalChecked(); + } + Nan::Set(result, Nan::New("type").ToLocalChecked(), jsType); + + return result; + } + + ~RefreshedRefModel() { + if (fullName != NULL) { delete[] fullName; } + if (message != NULL) { delete[] message; } + delete[] sha; + if (shorthand != NULL) { delete[] shorthand; } + } + + char *fullName, *message, *sha, *shorthand; + const char *type; +}; + +class UpstreamModel { +public: + UpstreamModel(const char *inputDownstreamFullName, const char *inputUpstreamFullName): + downstreamFullName((char *)strdup(inputDownstreamFullName)), + upstreamFullName((char *)strdup(inputUpstreamFullName)), + ahead(0), + behind(0) {} + + static bool fromReference(UpstreamModel **out, git_reference *ref) { + if (!git_reference_is_branch(ref)) { + return false; + } + + git_reference *upstream; + int result = git_branch_upstream(&upstream, ref); + if (result != GIT_OK) { + return false; + } + + UpstreamModel *upstreamModel = new UpstreamModel( + git_reference_name(ref), + git_reference_name(upstream) + ); + + git_oid localCommitOid; + result = getOidOfReferenceCommit(&localCommitOid, ref); + if (result != GIT_OK) { + delete upstreamModel; + return false; + } + + git_oid upstreamCommitOid; + result = getOidOfReferenceCommit(&upstreamCommitOid, upstream); + if (result != GIT_OK) { + delete upstreamModel; + return false; + } + + result = git_graph_ahead_behind( + &upstreamModel->ahead, + &upstreamModel->behind, + git_reference_owner(ref), + &localCommitOid, + &upstreamCommitOid + ); + + if (result != GIT_OK) { + delete upstreamModel; + return false; + } + + *out = upstreamModel; + return true; + } + + v8::Local toJavascript() { + v8::Local result = Nan::New(); + + v8::Local jsDownstreamFullName; + if (downstreamFullName == NULL) { + jsDownstreamFullName = Nan::Null(); + } else { + jsDownstreamFullName = Nan::New(downstreamFullName).ToLocalChecked(); + } + Nan::Set(result, Nan::New("downstreamFullName").ToLocalChecked(), jsDownstreamFullName); + + v8::Local jsUpstreamFullName; + if (upstreamFullName == NULL) { + jsUpstreamFullName = Nan::Null(); + } else { + jsUpstreamFullName = Nan::New(upstreamFullName).ToLocalChecked(); + } + Nan::Set(result, Nan::New("upstreamFullName").ToLocalChecked(), jsUpstreamFullName); + + Nan::Set(result, Nan::New("ahead").ToLocalChecked(), Nan::New(ahead)); + Nan::Set(result, Nan::New("behind").ToLocalChecked(), Nan::New(behind)); + return result; + } + + ~UpstreamModel() { + if (downstreamFullName != NULL) { delete[] downstreamFullName; } + if (upstreamFullName != NULL) { delete[] upstreamFullName; } + } + + char *downstreamFullName; + char *upstreamFullName; + size_t ahead; + size_t behind; +}; + +class RefreshReferencesData { +public: + RefreshReferencesData(): + headRefFullName(NULL), + cherrypick(NULL), + merge(NULL) {} + + ~RefreshReferencesData() { + while(refs.size()) { + delete refs.back(); + refs.pop_back(); + } + while(upstreamInfo.size()) { + delete upstreamInfo.back(); + upstreamInfo.pop_back(); + } + if (headRefFullName != NULL) { delete[] headRefFullName; } + if (cherrypick != NULL) { delete cherrypick; } + if (merge != NULL) { delete merge; } + } + + std::vector refs; + std::vector upstreamInfo; + char *headRefFullName; + RefreshedRefModel *cherrypick; + RefreshedRefModel *merge; +}; + +NAN_METHOD(GitRepository::RefreshReferences) +{ + if (info.Length() == 0 || !info[0]->IsFunction()) { + return Nan::ThrowError("Callback is required and must be a Function."); + } + + RefreshReferencesBaton* baton = new RefreshReferencesBaton; + + baton->error_code = GIT_OK; + baton->error = NULL; + baton->out = (void *)new RefreshReferencesData; + baton->repo = Nan::ObjectWrap::Unwrap(info.This())->GetValue(); + + Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); + RefreshReferencesWorker *worker = new RefreshReferencesWorker(baton, callback); + worker->SaveToPersistent("repo", info.This()); + Nan::AsyncQueueWorker(worker); + return; +} + +void GitRepository::RefreshReferencesWorker::Execute() +{ + giterr_clear(); + + LockMaster lockMaster(true, baton->repo); + git_repository *repo = baton->repo; + RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out; + + // START Refresh HEAD + git_reference *headRef = NULL; + baton->error_code = lookupDirectReferenceByShorthand(&headRef, repo, "HEAD"); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete refreshData; + baton->out = NULL; + return; + } + + RefreshedRefModel *headModel; + baton->error_code = RefreshedRefModel::fromReference(&headModel, headRef); + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + git_reference_free(headRef); + delete refreshData; + baton->out = NULL; + return; + } + refreshData->refs.push_back(headModel); + + refreshData->headRefFullName = strdup(git_reference_name(headRef)); + git_reference_free(headRef); + // END Refresh HEAD + + // START Refresh CHERRY_PICK_HEAD + git_reference *cherrypickRef = NULL; + if (lookupDirectReferenceByShorthand(&cherrypickRef, repo, "CHERRY_PICK_HEAD") == GIT_OK) { + baton->error_code = RefreshedRefModel::fromReference(&refreshData->cherrypick, cherrypickRef); + git_reference_free(cherrypickRef); + } else { + cherrypickRef = NULL; + } + // END Refresh CHERRY_PICK_HEAD + + // START Refresh MERGE_HEAD + git_reference *mergeRef = NULL; + // fall through if cherry pick failed + if (baton->error_code == GIT_OK && lookupDirectReferenceByShorthand(&mergeRef, repo, "MERGE_HEAD") == GIT_OK) { + baton->error_code = RefreshedRefModel::fromReference(&refreshData->merge, mergeRef); + git_reference_free(mergeRef); + } else { + mergeRef = NULL; + } + // END Refresh MERGE_HEAD + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete refreshData; + baton->out = NULL; + return; + } + + // Retrieve reference models and upstream info for each reference + git_strarray referenceNames; + baton->error_code = git_reference_list(&referenceNames, repo); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete refreshData; + baton->out = NULL; + return; + } + + git_strarray remoteNames; + baton->error_code = git_remote_list(&remoteNames, repo); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + git_strarray_free(&referenceNames); + delete refreshData; + baton->out = NULL; + return; + } + + for (size_t referenceIndex = 0; referenceIndex < referenceNames.count; ++referenceIndex) { + git_reference *reference; + baton->error_code = lookupDirectReferenceByFullName(&reference, repo, referenceNames.strings[referenceIndex]); + + if (baton->error_code != GIT_OK) { + break; + } + + UpstreamModel *upstreamModel; + if (UpstreamModel::fromReference(&upstreamModel, reference)) { + refreshData->upstreamInfo.push_back(upstreamModel); + } + + bool isBranch = git_reference_is_branch(reference); + bool isRemote = git_reference_is_remote(reference); + bool isTag = git_reference_is_tag(reference); + if ( + strcmp(referenceNames.strings[referenceIndex], headModel->fullName) == 0 + || (!isBranch && !isRemote && !isTag) + ) { + git_reference_free(reference); + continue; + } + + if (isRemote) { + char *remoteNameOfRef = getRemoteNameOfReference(reference); + bool isFromExistingRemote = gitStrArrayContains(&remoteNames, remoteNameOfRef); + delete[] remoteNameOfRef; + if (!isFromExistingRemote) { + git_reference_free(reference); + continue; + } + } + + RefreshedRefModel *refreshedRefModel; + baton->error_code = RefreshedRefModel::fromReference(&refreshedRefModel, reference); + git_reference_free(reference); + + if (baton->error_code == GIT_OK) { + refreshData->refs.push_back(refreshedRefModel); + } + } + + git_strarray_free(&remoteNames); + git_strarray_free(&referenceNames); + + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + delete refreshData; + baton->out = NULL; + return; + } +} + +void GitRepository::RefreshReferencesWorker::HandleOKCallback() +{ + if (baton->out != NULL) + { + RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out; + v8::Local result = Nan::New(); + + Nan::Set( + result, + Nan::New("headRefFullName").ToLocalChecked(), + Nan::New(refreshData->headRefFullName).ToLocalChecked() + ); + + unsigned int numRefs = refreshData->refs.size(); + v8::Local refs = Nan::New(numRefs); + for (unsigned int i = 0; i < numRefs; ++i) { + RefreshedRefModel *refreshedRefModel = refreshData->refs[i]; + Nan::Set(refs, Nan::New(i), refreshedRefModel->toJavascript()); + } + Nan::Set(result, Nan::New("refs").ToLocalChecked(), refs); + + unsigned int numUpstreamInfo = refreshData->upstreamInfo.size(); + v8::Local upstreamInfo = Nan::New(numUpstreamInfo); + for (unsigned int i = 0; i < numUpstreamInfo; ++i) { + UpstreamModel *upstreamModel = refreshData->upstreamInfo[i]; + Nan::Set(upstreamInfo, Nan::New(i), upstreamModel->toJavascript()); + } + Nan::Set(result, Nan::New("upstreamInfo").ToLocalChecked(), upstreamInfo); + + if (refreshData->cherrypick != NULL) { + Nan::Set( + result, + Nan::New("cherrypick").ToLocalChecked(), + refreshData->cherrypick->toJavascript() + ); + } else { + Nan::Set(result, Nan::New("cherrypick").ToLocalChecked(), Nan::Null()); + } + + if (refreshData->merge != NULL) { + Nan::Set( + result, + Nan::New("merge").ToLocalChecked(), + refreshData->merge->toJavascript() + ); + } else { + Nan::Set(result, Nan::New("merge").ToLocalChecked(), Nan::Null()); + } + + delete refreshData; + + Local argv[2] = { + Nan::Null(), + result + }; + callback->Call(2, argv, async_resource); + } + else if (baton->error) + { + Local argv[1] = { + Nan::Error(baton->error->message) + }; + callback->Call(1, argv, async_resource); + if (baton->error->message) + { + free((void *)baton->error->message); + } + + free((void *)baton->error); + } + else if (baton->error_code < 0) + { + Local err = Nan::Error("Repository refreshReferences has thrown an error.")->ToObject(); + err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code)); + err->Set(Nan::New("errorFunction").ToLocalChecked(), Nan::New("Repository.refreshReferences").ToLocalChecked()); + Local argv[1] = { + err + }; + callback->Call(1, argv, async_resource); + } + else + { + callback->Call(0, NULL, async_resource); + } +} From 7139a9e996146c773c311d961745a49d961722c7 Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Wed, 17 Apr 2019 14:29:31 -0700 Subject: [PATCH 6/8] Fix linter issues --- lib/repository.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/repository.js b/lib/repository.js index cdbad7054..29c429fad 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -24,7 +24,6 @@ var TreeBuilder = NodeGit.Treebuilder; var _discover = Repository.discover; var _initExt = Repository.initExt; var _fetchheadForeach = Repository.prototype.fetchheadForeach; -var _getReferences = Repository.prototype.getReferences; var _mergeheadForeach = Repository.prototype.mergeheadForeach; function applySelectedLinesToTarget @@ -355,7 +354,7 @@ Repository.getReferences = function(repo, type, refNamesOnly) { var filteredRefList = refList; filteredRefList.filter(function(reference) { - return type == Reference.TYPE.LISTALL || ref.type === type + return type == Reference.TYPE.LISTALL || reference.type === type; }); if (refNamesOnly) { From ea358d4e6145974beff21cbbc8b9e76740a387ff Mon Sep 17 00:00:00 2001 From: Jordan Wallet Date: Fri, 19 Apr 2019 17:23:19 -0700 Subject: [PATCH 7/8] Add tag gpg signatures to refreshReferences --- .../manual/repository/refresh_references.cc | 183 +++++++++++++++--- 1 file changed, 157 insertions(+), 26 deletions(-) diff --git a/generate/templates/manual/repository/refresh_references.cc b/generate/templates/manual/repository/refresh_references.cc index 2ee4c027d..a525cd691 100644 --- a/generate/templates/manual/repository/refresh_references.cc +++ b/generate/templates/manual/repository/refresh_references.cc @@ -65,6 +65,8 @@ class RefreshedRefModel { message(NULL), sha(new char[GIT_OID_HEXSZ + 1]), shorthand(strdup(git_reference_shorthand(ref))), + tagOdbBuffer(NULL), + tagOdbBufferLength(0), type(NULL) { if (git_reference_is_branch(ref)) { @@ -76,33 +78,89 @@ class RefreshedRefModel { } } - static int fromReference(RefreshedRefModel **out, git_reference *ref) { + static int fromReference(RefreshedRefModel **out, git_reference *ref, git_odb *odb) { RefreshedRefModel *refModel = new RefreshedRefModel(ref); - git_oid referencedTargetOid; + const git_oid *referencedTargetOid = git_reference_target(ref); - int result = getOidOfReferenceCommit(&referencedTargetOid, ref); - if (result != GIT_OK) { - delete refModel; - return result; + if (!git_reference_is_tag(ref)) { + git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, referencedTargetOid); + + *out = refModel; + return GIT_OK; } + git_repository *repo = git_reference_owner(ref); - if (git_reference_is_tag(ref)) { - git_repository *repo = git_reference_owner(ref); + git_tag *referencedTag; + if (git_tag_lookup(&referencedTag, repo, referencedTargetOid) == GIT_OK) { + refModel->message = strdup(git_tag_message(referencedTag)); - git_tag *referencedTag; - if (git_tag_lookup(&referencedTag, repo, &referencedTargetOid) == GIT_OK) { - refModel->message = strdup(git_tag_message(referencedTag)); - git_tag_free(referencedTag); + git_odb_object *tagOdbObject; + if (git_odb_read(&tagOdbObject, odb, git_tag_id(referencedTag)) == GIT_OK) { + refModel->tagOdbBufferLength = git_odb_object_size(tagOdbObject); + refModel->tagOdbBuffer = new char[refModel->tagOdbBufferLength]; + std::memcpy(refModel->tagOdbBuffer, git_odb_object_data(tagOdbObject), refModel->tagOdbBufferLength); + git_odb_object_free(tagOdbObject); } + + git_tag_free(referencedTag); + } + + git_oid peeledReferencedTargetOid; + int error = getOidOfReferenceCommit(&peeledReferencedTargetOid, ref); + if (error != GIT_OK) { + delete refModel; + return error; } - git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, &referencedTargetOid); + git_oid_tostr(refModel->sha, GIT_OID_HEXSZ + 1, &peeledReferencedTargetOid); *out = refModel; return GIT_OK; } - v8::Local toJavascript() { + static void ensureSignatureRegexes() { + if (!signatureRegexesBySignatureType.IsEmpty()) { + return; + } + + v8::Local gpgsigArray = Nan::New(2), + x509Array = Nan::New(1); + + Nan::Set( + gpgsigArray, + Nan::New(0), + Nan::New( + Nan::New("-----BEGIN PGP SIGNATURE-----[\\s\\S]+?-----END PGP SIGNATURE-----").ToLocalChecked(), + static_cast(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline) + ).ToLocalChecked() + ); + + Nan::Set( + gpgsigArray, + Nan::New(1), + Nan::New( + Nan::New("-----BEGIN PGP MESSAGE-----[\\s\\S]+?-----END PGP MESSAGE-----").ToLocalChecked(), + static_cast(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline) + ).ToLocalChecked() + ); + + Nan::Set( + x509Array, + Nan::New(0), + Nan::New( + Nan::New("-----BEGIN SIGNED MESSAGE-----[\s\S]+?-----END SIGNED MESSAGE-----").ToLocalChecked(), + static_cast(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline) + ).ToLocalChecked() + ); + + v8::Local result = Nan::New(); + Nan::Set(result, Nan::New("gpgsig").ToLocalChecked(), gpgsigArray); + Nan::Set(result, Nan::New("x509").ToLocalChecked(), x509Array); + + signatureRegexesBySignatureType.Reset(result); + } + + v8::Local toJavascript(v8::Local signatureType) { v8::Local result = Nan::New(); v8::Local jsFullName; @@ -135,6 +193,34 @@ class RefreshedRefModel { } Nan::Set(result, Nan::New("shorthand").ToLocalChecked(), jsShorthand); + v8::Local jsTagSignature = Nan::Null(); + if (tagOdbBuffer != NULL && tagOdbBufferLength != 0) { + // tagOdbBuffer is already a copy, so we'd like to use NewBuffer instead, + // but we were getting segfaults and couldn't easily figure out why. :( + // We tried passing the tagOdbBuffer directly to NewBuffer and then nullifying tagOdbBuffer so that + // the destructor didn't double free, but that still segfaulted internally in Node. + v8::Local buffer = Nan::CopyBuffer(tagOdbBuffer, tagOdbBufferLength).ToLocalChecked(); + v8::Local toStringProp = Nan::Get(buffer, Nan::New("toString").ToLocalChecked()).ToLocalChecked(); + v8::Local jsTagOdbObjectString = Nan::CallAsFunction(toStringProp->ToObject(), buffer, 0, NULL).ToLocalChecked()->ToObject(); + + v8::Local _signatureRegexesBySignatureType = Nan::New(signatureRegexesBySignatureType); + v8::Local signatureRegexes = v8::Local::Cast(Nan::Get(_signatureRegexesBySignatureType, signatureType).ToLocalChecked()); + + for (uint32_t i = 0; i < signatureRegexes->Length(); ++i) { + v8::Local argv[] = { + Nan::Get(signatureRegexes, Nan::New(i)).ToLocalChecked() + }; + + v8::Local matchProp = Nan::Get(jsTagOdbObjectString, Nan::New("match").ToLocalChecked()).ToLocalChecked(); + v8::Local match = Nan::CallAsFunction(matchProp->ToObject(), jsTagOdbObjectString, 1, argv).ToLocalChecked(); + if (match->IsArray()) { + jsTagSignature = Nan::Get(match->ToObject(), 0).ToLocalChecked(); + break; + } + } + } + Nan::Set(result, Nan::New("tagSignature").ToLocalChecked(), jsTagSignature); + v8::Local jsType; if (type == NULL) { jsType = Nan::Null(); @@ -151,12 +237,17 @@ class RefreshedRefModel { if (message != NULL) { delete[] message; } delete[] sha; if (shorthand != NULL) { delete[] shorthand; } + if (tagOdbBuffer != NULL) { delete[] tagOdbBuffer; } } - char *fullName, *message, *sha, *shorthand; + char *fullName, *message, *sha, *shorthand, *tagOdbBuffer; + size_t tagOdbBufferLength; const char *type; + static Nan::Persistent signatureRegexesBySignatureType; }; +Nan::Persistent RefreshedRefModel::signatureRegexesBySignatureType; + class UpstreamModel { public: UpstreamModel(const char *inputDownstreamFullName, const char *inputUpstreamFullName): @@ -277,7 +368,25 @@ class RefreshReferencesData { NAN_METHOD(GitRepository::RefreshReferences) { - if (info.Length() == 0 || !info[0]->IsFunction()) { + v8::Local signatureType; + if (info.Length() == 2) { + if (!info[0]->IsString()) { + return Nan::ThrowError("Signature type must be \"gpgsig\" or \"x509\"."); + } + + v8::Local signatureTypeParam = info[0]->ToString(); + if ( + Nan::Equals(signatureTypeParam, Nan::New("gpgsig").ToLocalChecked()) != Nan::Just(true) + && Nan::Equals(signatureTypeParam, Nan::New("x509").ToLocalChecked()) != Nan::Just(true) + ) { + return Nan::ThrowError("Signature type must be \"gpgsig\" or \"x509\"."); + } + signatureType = signatureTypeParam; + } else { + signatureType = Nan::New("gpgsig").ToLocalChecked(); + } + + if (info.Length() == 0 || (info.Length() == 1 && !info[0]->IsFunction()) || (info.Length() == 2 && !info[1]->IsFunction())) { return Nan::ThrowError("Callback is required and must be a Function."); } @@ -291,6 +400,7 @@ NAN_METHOD(GitRepository::RefreshReferences) Nan::Callback *callback = new Nan::Callback(Local::Cast(info[0])); RefreshReferencesWorker *worker = new RefreshReferencesWorker(baton, callback); worker->SaveToPersistent("repo", info.This()); + worker->SaveToPersistent("signatureType", signatureType); Nan::AsyncQueueWorker(worker); return; } @@ -302,6 +412,18 @@ void GitRepository::RefreshReferencesWorker::Execute() LockMaster lockMaster(true, baton->repo); git_repository *repo = baton->repo; RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out; + git_odb *odb; + + baton->error_code = git_repository_odb(&odb, repo); + if (baton->error_code != GIT_OK) { + if (giterr_last() != NULL) { + baton->error = git_error_dup(giterr_last()); + } + git_odb_free(odb); + delete refreshData; + baton->out = NULL; + return; + } // START Refresh HEAD git_reference *headRef = NULL; @@ -311,17 +433,19 @@ void GitRepository::RefreshReferencesWorker::Execute() if (giterr_last() != NULL) { baton->error = git_error_dup(giterr_last()); } + git_odb_free(odb); delete refreshData; baton->out = NULL; return; } RefreshedRefModel *headModel; - baton->error_code = RefreshedRefModel::fromReference(&headModel, headRef); + baton->error_code = RefreshedRefModel::fromReference(&headModel, headRef, odb); if (baton->error_code != GIT_OK) { if (giterr_last() != NULL) { baton->error = git_error_dup(giterr_last()); } + git_odb_free(odb); git_reference_free(headRef); delete refreshData; baton->out = NULL; @@ -336,7 +460,7 @@ void GitRepository::RefreshReferencesWorker::Execute() // START Refresh CHERRY_PICK_HEAD git_reference *cherrypickRef = NULL; if (lookupDirectReferenceByShorthand(&cherrypickRef, repo, "CHERRY_PICK_HEAD") == GIT_OK) { - baton->error_code = RefreshedRefModel::fromReference(&refreshData->cherrypick, cherrypickRef); + baton->error_code = RefreshedRefModel::fromReference(&refreshData->cherrypick, cherrypickRef, odb); git_reference_free(cherrypickRef); } else { cherrypickRef = NULL; @@ -347,7 +471,7 @@ void GitRepository::RefreshReferencesWorker::Execute() git_reference *mergeRef = NULL; // fall through if cherry pick failed if (baton->error_code == GIT_OK && lookupDirectReferenceByShorthand(&mergeRef, repo, "MERGE_HEAD") == GIT_OK) { - baton->error_code = RefreshedRefModel::fromReference(&refreshData->merge, mergeRef); + baton->error_code = RefreshedRefModel::fromReference(&refreshData->merge, mergeRef, odb); git_reference_free(mergeRef); } else { mergeRef = NULL; @@ -358,6 +482,7 @@ void GitRepository::RefreshReferencesWorker::Execute() if (giterr_last() != NULL) { baton->error = git_error_dup(giterr_last()); } + git_odb_free(odb); delete refreshData; baton->out = NULL; return; @@ -371,6 +496,7 @@ void GitRepository::RefreshReferencesWorker::Execute() if (giterr_last() != NULL) { baton->error = git_error_dup(giterr_last()); } + git_odb_free(odb); delete refreshData; baton->out = NULL; return; @@ -383,6 +509,7 @@ void GitRepository::RefreshReferencesWorker::Execute() if (giterr_last() != NULL) { baton->error = git_error_dup(giterr_last()); } + git_odb_free(odb); git_strarray_free(&referenceNames); delete refreshData; baton->out = NULL; @@ -424,7 +551,7 @@ void GitRepository::RefreshReferencesWorker::Execute() } RefreshedRefModel *refreshedRefModel; - baton->error_code = RefreshedRefModel::fromReference(&refreshedRefModel, reference); + baton->error_code = RefreshedRefModel::fromReference(&refreshedRefModel, reference, odb); git_reference_free(reference); if (baton->error_code == GIT_OK) { @@ -432,6 +559,7 @@ void GitRepository::RefreshReferencesWorker::Execute() } } + git_odb_free(odb); git_strarray_free(&remoteNames); git_strarray_free(&referenceNames); @@ -449,6 +577,7 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback() { if (baton->out != NULL) { + RefreshedRefModel::ensureSignatureRegexes(); RefreshReferencesData *refreshData = (RefreshReferencesData *)baton->out; v8::Local result = Nan::New(); @@ -458,19 +587,21 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback() Nan::New(refreshData->headRefFullName).ToLocalChecked() ); + v8::Local signatureType = GetFromPersistent("signatureType")->ToString(); + unsigned int numRefs = refreshData->refs.size(); - v8::Local refs = Nan::New(numRefs); + v8::Local refs = Nan::New(numRefs); for (unsigned int i = 0; i < numRefs; ++i) { RefreshedRefModel *refreshedRefModel = refreshData->refs[i]; - Nan::Set(refs, Nan::New(i), refreshedRefModel->toJavascript()); + Nan::Set(refs, Nan::New(i), refreshedRefModel->toJavascript(signatureType)); } Nan::Set(result, Nan::New("refs").ToLocalChecked(), refs); unsigned int numUpstreamInfo = refreshData->upstreamInfo.size(); - v8::Local upstreamInfo = Nan::New(numUpstreamInfo); + v8::Local upstreamInfo = Nan::New(numUpstreamInfo); for (unsigned int i = 0; i < numUpstreamInfo; ++i) { UpstreamModel *upstreamModel = refreshData->upstreamInfo[i]; - Nan::Set(upstreamInfo, Nan::New(i), upstreamModel->toJavascript()); + Nan::Set(upstreamInfo, Nan::New(i), upstreamModel->toJavascript()); } Nan::Set(result, Nan::New("upstreamInfo").ToLocalChecked(), upstreamInfo); @@ -478,7 +609,7 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback() Nan::Set( result, Nan::New("cherrypick").ToLocalChecked(), - refreshData->cherrypick->toJavascript() + refreshData->cherrypick->toJavascript(signatureType) ); } else { Nan::Set(result, Nan::New("cherrypick").ToLocalChecked(), Nan::Null()); @@ -488,7 +619,7 @@ void GitRepository::RefreshReferencesWorker::HandleOKCallback() Nan::Set( result, Nan::New("merge").ToLocalChecked(), - refreshData->merge->toJavascript() + refreshData->merge->toJavascript(signatureType) ); } else { Nan::Set(result, Nan::New("merge").ToLocalChecked(), Nan::Null()); From f4926a0a534edbe3b2ae46606aff3289c7fbc0ea Mon Sep 17 00:00:00 2001 From: Tyler Ang-Wanek Date: Tue, 23 Apr 2019 16:31:20 -0700 Subject: [PATCH 8/8] Fixup refreshReferences Reset baton->error_code to GIT_OK on clean loop exit. Use \\s instead of \s for regex string --- generate/templates/manual/repository/refresh_references.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/generate/templates/manual/repository/refresh_references.cc b/generate/templates/manual/repository/refresh_references.cc index a525cd691..a13f3641c 100644 --- a/generate/templates/manual/repository/refresh_references.cc +++ b/generate/templates/manual/repository/refresh_references.cc @@ -148,7 +148,7 @@ class RefreshedRefModel { x509Array, Nan::New(0), Nan::New( - Nan::New("-----BEGIN SIGNED MESSAGE-----[\s\S]+?-----END SIGNED MESSAGE-----").ToLocalChecked(), + Nan::New("-----BEGIN SIGNED MESSAGE-----[\\s\\S]+?-----END SIGNED MESSAGE-----").ToLocalChecked(), static_cast(v8::RegExp::Flags::kGlobal | v8::RegExp::Flags::kMultiline) ).ToLocalChecked() ); @@ -556,6 +556,8 @@ void GitRepository::RefreshReferencesWorker::Execute() if (baton->error_code == GIT_OK) { refreshData->refs.push_back(refreshedRefModel); + } else { + baton->error_code = GIT_OK; } }