Commit 5da98dc9 authored by Tyler Wanek's avatar Tyler Wanek

Implement file history walk in revwalk

parent e63536fa
......@@ -1857,6 +1857,10 @@
"ignore": true
},
"revwalk": {
"dependencies": [
"../include/commit.h",
"../include/functions/copy.h"
],
"functions": {
"git_revwalk_add_hide_cb": {
"ignore": true
......
......@@ -200,6 +200,36 @@
"isErrorCode": true
}
},
"git_revwalk_file_history_walk": {
"args": [
{
"name": "file_path",
"type": "const char *"
},
{
"name": "max_count",
"type": "int"
},
{
"name": "out",
"type": "std::vector< std::pair<git_commit *, char *> *> *"
},
{
"name": "walk",
"type": "git_revwalk *"
}
],
"type": "function",
"isManual": true,
"cFile": "generate/templates/manual/revwalk/file_history_walk.cc",
"isAsync": true,
"isPrototypeMethod": true,
"group": "revwalk",
"return": {
"type": "int",
"isErrorCode": true
}
},
"git_stash_save": {
"type": "function",
"file": "stash.h",
......@@ -263,7 +293,8 @@
[
"revwalk",
[
"git_revwalk_fast_walk"
"git_revwalk_fast_walk",
"git_revwalk_file_history_walk"
]
],
[
......
NAN_METHOD(GitRevwalk::FileHistoryWalk)
{
if (info.Length() == 0 || !info[0]->IsString()) {
return Nan::ThrowError("File path to get the history is required.");
}
if (info.Length() == 1 || !info[1]->IsNumber()) {
return Nan::ThrowError("Max count is required and must be a number.");
}
if (info.Length() == 2 || !info[2]->IsFunction()) {
return Nan::ThrowError("Callback is required and must be a Function.");
}
FileHistoryWalkBaton* baton = new FileHistoryWalkBaton;
baton->error_code = GIT_OK;
baton->error = NULL;
String::Utf8Value from_js_file_path(info[0]->ToString());
baton->file_path = strdup(*from_js_file_path);
baton->max_count = (unsigned int)info[1]->ToNumber()->Value();
baton->out = new std::vector< std::pair<git_commit*, char *> *>;
baton->out->reserve(baton->max_count);
baton->walk = Nan::ObjectWrap::Unwrap<GitRevwalk>(info.This())->GetValue();
Nan::Callback *callback = new Nan::Callback(Local<Function>::Cast(info[2]));
FileHistoryWalkWorker *worker = new FileHistoryWalkWorker(baton, callback);
worker->SaveToPersistent("fileHistoryWalk", info.This());
Nan::AsyncQueueWorker(worker);
return;
}
void GitRevwalk::FileHistoryWalkWorker::Execute()
{
git_repository *repo = git_revwalk_repository(baton->walk);
git_oid *nextOid = (git_oid *)malloc(sizeof(git_oid));
giterr_clear();
char * trackPath = strdup(baton->file_path);
bool addedFlag = false;
for (
unsigned int i = 0;
i < baton->max_count && (baton->error_code = git_revwalk_next(nextOid, baton->walk)) == GIT_OK;
++i
) {
// check if this commit has the file
git_commit *nextCommit;
if ((baton->error_code = git_commit_lookup(&nextCommit, repo, nextOid)) != GIT_OK) {
break;
}
git_tree *thisTree, *parentTree;
if ((baton->error_code = git_commit_tree(&thisTree, nextCommit)) != GIT_OK) {
git_commit_free(nextCommit);
break;
}
git_diff *diffs;
git_commit *parent;
unsigned int parents = git_commit_parentcount(nextCommit);
if (
parents >= 1) {
if ((baton->error_code = git_commit_parent(&parent, nextCommit, 0)) != GIT_OK) {
git_commit_free(nextCommit);
break;
}
if (
(baton->error_code = git_commit_tree(&parentTree, parent)) != GIT_OK ||
(baton->error_code = git_diff_tree_to_tree(&diffs, repo, parentTree, thisTree, NULL)) != GIT_OK
) {
git_commit_free(nextCommit);
git_commit_free(parent);
break;
}
} else {
if ((baton->error_code = git_diff_tree_to_tree(&diffs, repo, NULL, thisTree, NULL)) != GIT_OK) {
git_commit_free(nextCommit);
break;
}
}
bool flag = false;
unsigned int numDeltas = git_diff_num_deltas(diffs);
for (unsigned int j = 0; j < numDeltas; ++j) {
git_patch *nextPatch;
baton->error_code = git_patch_from_diff(&nextPatch, diffs, j);
if (baton->error_code < GIT_OK) {
break;
}
if (nextPatch == NULL) {
continue;
}
const git_diff_delta *delta = git_patch_get_delta(nextPatch);
bool isEqualOldFile = !strcmp(delta->old_file.path, trackPath);
bool isEqualNewFile = !strcmp(delta->new_file.path, trackPath);
if (isEqualNewFile) {
baton->out->push_back(
new std::pair<git_commit *, char *>(
nextCommit,
strdup(delta->new_file.path)
));
if (delta->status == GIT_DELTA_ADDED) {
addedFlag = true;
} else if (!isEqualOldFile) {
free(trackPath);
trackPath = strdup(delta->old_file.path);
}
flag = true;
}
git_patch_free(nextPatch);
if (flag) {
break;
}
}
if (!flag && nextCommit != NULL) {
git_commit_free(nextCommit);
}
if (baton->error_code != GIT_OK) {
break;
}
if (addedFlag) {
break;
}
}
free(trackPath);
free(nextOid);
if (baton->error_code != GIT_OK) {
if (baton->error_code != GIT_ITEROVER) {
baton->error = git_error_dup(giterr_last());
while(!baton->out->empty())
{
std::pair<git_commit *, char *> *pairToFree = baton->out->back();
baton->out->pop_back();
git_commit_free(pairToFree->first);
free(pairToFree->second);
free(pairToFree);
}
delete baton->out;
baton->out = NULL;
}
} else {
baton->error_code = GIT_OK;
}
}
void GitRevwalk::FileHistoryWalkWorker::HandleOKCallback()
{
if (baton->out != NULL) {
unsigned int size = baton->out->size();
Local<Array> result = Nan::New<Array>(size);
for (unsigned int i = 0; i < size; i++) {
Local<v8::Object> historyEntry = Nan::New<Object>();
std::pair<git_commit *, char *> *batonResult = baton->out->at(i);
Nan::Set(historyEntry, Nan::New("commit").ToLocalChecked(), GitCommit::New(batonResult->first, true));
Nan::Set(historyEntry, Nan::New("name").ToLocalChecked(), Nan::New(batonResult->second).ToLocalChecked());
Nan::Set(result, Nan::New<Number>(i), historyEntry);
free(batonResult->second);
free(batonResult);
}
Local<v8::Value> argv[2] = {
Nan::Null(),
result
};
callback->Call(2, argv);
delete baton->out;
return;
}
if (baton->error) {
Local<v8::Value> 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<v8::Object> err = Nan::Error("Method next has thrown an error.")->ToObject();
err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
Local<v8::Value> argv[1] = {
err
};
callback->Call(1, argv);
return;
}
callback->Call(0, NULL);
}
......@@ -4,6 +4,7 @@
#include <nan.h>
#include <string>
#include <queue>
#include <utility>
#include "async_baton.h"
#include "promise_completion.h"
......
......@@ -4,6 +4,7 @@
#include <nan.h>
#include <string>
#include <queue>
#include <utility>
#include "async_baton.h"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment