Skip to content

Commit b66dd42

Browse files
authored
Merge pull request #1728 from implausible/feature/commit-walk-commit-models
Optionally retrieve more data on commit walk
2 parents ee7b26c + d7c9860 commit b66dd42

File tree

3 files changed

+153
-24
lines changed

3 files changed

+153
-24
lines changed

generate/input/libgit2-supplement.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,11 @@
753753
},
754754
{
755755
"name": "out",
756-
"type": "std::vector<git_commit *> *"
756+
"type": "void *"
757+
},
758+
{
759+
"name": "returnPlainObjects",
760+
"type": "bool"
757761
},
758762
{
759763
"name": "walk",

generate/templates/manual/revwalk/commit_walk.cc

Lines changed: 147 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,149 @@
1+
#define SET_ON_OBJECT(obj, field, data) Nan::Set(obj, Nan::New(field).ToLocalChecked(), data)
2+
3+
v8::Local<v8::Object> signatureToJavascript(const git_signature *signature) {
4+
v8::Local<v8::Object> signatureObject = Nan::New<v8::Object>();
5+
SET_ON_OBJECT(signatureObject, "name", Nan::New(signature->name).ToLocalChecked());
6+
SET_ON_OBJECT(signatureObject, "email", Nan::New(signature->email).ToLocalChecked());
7+
SET_ON_OBJECT(signatureObject, "date", Nan::New<v8::Number>(signature->when.time * 1000));
8+
std::stringstream fullSignature;
9+
fullSignature << signature->name << " <" << signature << ">";
10+
SET_ON_OBJECT(signatureObject, "full", Nan::New(fullSignature.str()).ToLocalChecked());
11+
return signatureObject;
12+
}
13+
14+
#include <iostream>
15+
class CommitModel {
16+
public:
17+
CommitModel(git_commit *commit, bool fetchSignature):
18+
commit(commit),
19+
fetchSignature(fetchSignature),
20+
signature({ 0, 0, 0 }),
21+
signedData({ 0, 0, 0 })
22+
{
23+
if (fetchSignature) {
24+
const int error = git_commit_extract_signature(
25+
&signature,
26+
&signedData,
27+
git_commit_owner(commit),
28+
const_cast<git_oid *>(git_commit_id(commit)),
29+
NULL
30+
);
31+
if (error != GIT_ENOTFOUND) {
32+
assert(error == GIT_OK);
33+
}
34+
}
35+
36+
const size_t parentCount = git_commit_parentcount(commit);
37+
parentIds.reserve(parentCount);
38+
for (size_t parentIndex = 0; parentIndex < parentCount; ++parentIndex) {
39+
parentIds.push_back(git_oid_tostr_s(git_commit_parent_id(commit, parentIndex)));
40+
}
41+
}
42+
43+
v8::Local<v8::Value> toJavascript() {
44+
if (!fetchSignature) {
45+
v8::Local<v8::Value> commitObject = GitCommit::New(
46+
commit,
47+
true,
48+
Nan::To<v8::Object>(GitRepository::New(
49+
git_commit_owner(commit),
50+
true
51+
)).ToLocalChecked()
52+
);
53+
commit = NULL;
54+
return commitObject;
55+
}
56+
57+
v8::Local<v8::Object> commitModel = Nan::New<v8::Object>();
58+
SET_ON_OBJECT(commitModel, "sha", Nan::New(git_oid_tostr_s(git_commit_id(commit))).ToLocalChecked());
59+
SET_ON_OBJECT(commitModel, "message", Nan::New(git_commit_message(commit)).ToLocalChecked());
60+
SET_ON_OBJECT(commitModel, "author", signatureToJavascript(git_commit_author(commit)));
61+
SET_ON_OBJECT(commitModel, "committer", signatureToJavascript(git_commit_committer(commit)));
62+
63+
size_t parentCount = parentIds.size();
64+
v8::Local<v8::Array> parents = Nan::New<v8::Array>(parentCount);
65+
for (size_t parentIndex = 0; parentIndex < parentCount; ++parentIndex) {
66+
Nan::Set(parents, Nan::New<v8::Number>(parentIndex), Nan::New(parentIds[parentIndex]).ToLocalChecked());
67+
}
68+
SET_ON_OBJECT(commitModel, "parents", parents);
69+
70+
if (signature.size != 0 || signedData.size != 0) {
71+
v8::Local<v8::Object> gpgSignature = Nan::New<v8::Object>();
72+
if (signature.size != 0) {
73+
SET_ON_OBJECT(gpgSignature, "signature", Nan::New(signature.ptr).ToLocalChecked());
74+
} else {
75+
SET_ON_OBJECT(gpgSignature, "signature", Nan::Null());
76+
}
77+
78+
if (signedData.size != 0) {
79+
SET_ON_OBJECT(gpgSignature, "signedData", Nan::New(signedData.ptr).ToLocalChecked());
80+
} else {
81+
SET_ON_OBJECT(gpgSignature, "signedData", Nan::Null());
82+
}
83+
84+
SET_ON_OBJECT(commitModel, "gpgSignature", gpgSignature);
85+
}
86+
87+
return commitModel;
88+
}
89+
90+
~CommitModel() {
91+
git_buf_dispose(&signature);
92+
git_buf_dispose(&signedData);
93+
if (commit) {
94+
git_commit_free(commit);
95+
}
96+
}
97+
98+
private:
99+
git_commit *commit;
100+
bool fetchSignature;
101+
git_buf signature, signedData;
102+
std::vector<std::string> parentIds;
103+
};
104+
1105
NAN_METHOD(GitRevwalk::CommitWalk) {
2106
if (info.Length() == 0 || !info[0]->IsNumber()) {
3107
return Nan::ThrowError("Max count is required and must be a number.");
4108
}
5109

6-
if (info.Length() == 1 || !info[1]->IsFunction()) {
110+
if (info.Length() == 1 || (info.Length() == 2 && !info[1]->IsFunction())) {
7111
return Nan::ThrowError("Callback is required and must be a Function.");
8112
}
9113

114+
if (info.Length() >= 3) {
115+
if (!info[1]->IsNull() && !info[1]->IsUndefined() && !info[1]->IsObject()) {
116+
return Nan::ThrowError("Options must be an object, null, or undefined.");
117+
}
118+
119+
if (!info[2]->IsFunction()) {
120+
return Nan::ThrowError("Callback is required and must be a Function.");
121+
}
122+
}
123+
10124
CommitWalkBaton* baton = new CommitWalkBaton;
11125

12126
baton->error_code = GIT_OK;
13127
baton->error = NULL;
14128
baton->max_count = Nan::To<unsigned int>(info[0]).FromJust();
15-
baton->out = new std::vector<git_commit *>;
16-
baton->out->reserve(baton->max_count);
129+
std::vector<CommitModel *> *out = new std::vector<CommitModel *>;
130+
out->reserve(baton->max_count);
131+
baton->out = static_cast<void *>(out);
132+
if (info.Length() == 3 && info[1]->IsObject()) {
133+
v8::Local<v8::Object> options = Nan::To<v8::Object>(info[1]).ToLocalChecked();
134+
v8::Local<v8::String> propName = Nan::New("returnPlainObjects").ToLocalChecked();
135+
if (Nan::Has(options, propName).FromJust()) {
136+
baton->returnPlainObjects = Nan::Get(options, propName).ToLocalChecked()->IsTrue();
137+
} else {
138+
baton->returnPlainObjects = false;
139+
}
140+
} else {
141+
baton->returnPlainObjects = false;
142+
}
17143
baton->walk = Nan::ObjectWrap::Unwrap<GitRevwalk>(info.This())->GetValue();
18-
19-
Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[1]));
144+
Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[1]->IsFunction() ? info[1] : info[2]));
20145
CommitWalkWorker *worker = new CommitWalkWorker(baton, callback);
21-
worker->SaveToPersistent("fastWalk", info.This());
146+
worker->SaveToPersistent("commitWalk", info.This());
22147

23148
Nan::AsyncQueueWorker(worker);
24149
return;
@@ -27,6 +152,7 @@ NAN_METHOD(GitRevwalk::CommitWalk) {
27152
void GitRevwalk::CommitWalkWorker::Execute() {
28153
giterr_clear();
29154

155+
std::vector<CommitModel *> *out = static_cast<std::vector<CommitModel *> *>(baton->out);
30156
for (int i = 0; i < baton->max_count; i++) {
31157
git_oid next_commit_id;
32158
baton->error_code = git_revwalk_next(&next_commit_id, baton->walk);
@@ -41,12 +167,12 @@ void GitRevwalk::CommitWalkWorker::Execute() {
41167
baton->error = git_error_dup(giterr_last());
42168
}
43169

44-
while (baton->out->size()) {
45-
git_commit_free(baton->out->back());
46-
baton->out->pop_back();
170+
while (out->size()) {
171+
delete out->back();
172+
out->pop_back();
47173
}
48174

49-
delete baton->out;
175+
delete out;
50176
baton->out = NULL;
51177

52178
return;
@@ -60,39 +186,37 @@ void GitRevwalk::CommitWalkWorker::Execute() {
60186
baton->error = git_error_dup(giterr_last());
61187
}
62188

63-
while (baton->out->size()) {
64-
git_commit_free(baton->out->back());
65-
baton->out->pop_back();
189+
while (out->size()) {
190+
delete out->back();
191+
out->pop_back();
66192
}
67193

68-
delete baton->out;
194+
delete out;
69195
baton->out = NULL;
70196

71197
return;
72198
}
73199

74-
baton->out->push_back(commit);
200+
out->push_back(new CommitModel(commit, baton->returnPlainObjects));
75201
}
76202
}
77203

78204
void GitRevwalk::CommitWalkWorker::HandleOKCallback() {
79205
if (baton->out != NULL) {
80-
unsigned int size = baton->out->size();
206+
std::vector<CommitModel *> *out = static_cast<std::vector<CommitModel *> *>(baton->out);
207+
const unsigned int size = out->size();
81208
Local<Array> result = Nan::New<Array>(size);
82209
for (unsigned int i = 0; i < size; i++) {
83-
git_commit *commit = baton->out->at(i);
210+
CommitModel *commitModel = out->at(i);
84211
Nan::Set(
85212
result,
86213
Nan::New<Number>(i),
87-
GitCommit::New(
88-
commit,
89-
true,
90-
Nan::To<v8::Object>(GitRepository::New(git_commit_owner(commit), true)).ToLocalChecked()
91-
)
214+
commitModel->toJavascript()
92215
);
216+
delete commitModel;
93217
}
94218

95-
delete baton->out;
219+
delete out;
96220

97221
Local<v8::Value> argv[2] = {
98222
Nan::Null(),

generate/templates/templates/class_header.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <queue>
66
#include <utility>
77
#include <unordered_map>
8+
#include <sstream>
89

910
#include "async_baton.h"
1011
#include "nodegit_wrapper.h"

0 commit comments

Comments
 (0)