diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 25a01481d..a7f772172 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -258,7 +258,7 @@ "args": { "out": { "isReturn": true, - "cppClassName": "GitBuf", + "cppClassName": "GitBuf", "jsClassName": "Buffer", "cType": "git_buf *", "shouldAlloc": true @@ -1008,19 +1008,76 @@ "hasConstructor": true, "functions": { "git_filter_list_apply_to_blob": { - "ignore": true + "async": true, + "return": { + "isErrorCode": true + }, + "args": { + "out": { + "isReturn": true, + "shouldAlloc": true + }, + "filters": { + "isSelf": true + } + } }, "git_filter_list_apply_to_data": { - "ignore": true + "async": true, + "return": { + "isErrorCode": true + }, + "args": { + "out": { + "isReturn": true, + "shouldAlloc": true + }, + "filters": { + "isSelf": true + } + } }, "git_filter_list_apply_to_file": { - "ignore": true + "async": true, + "return": { + "isErrorCode": true + }, + "args": { + "out": { + "isReturn": true, + "shouldAlloc": true + }, + "filters": { + "isSelf": true + } + } }, "git_filter_list_free": { - "ignore": true + "async": true, + "args": { + "filters": { + "isSelf": true + } + } }, "git_filter_list_load": { - "ignore": true + "async": true, + "return": { + "isErrorCode": true + }, + "args": { + "filters": { + "isReturn": true, + "cType": "git_filter_list **" + }, + "repo": { + "cType": "git_repository *" + }, + "blob": { + "isOptional": true, + "cType": "git_blob *" + } + } }, "git_filter_list_push": { "ignore": true @@ -1769,6 +1826,9 @@ } }, "pathspec": { + "dependencies": [ + "../include/str_array_converter.h" + ], "functions": { "git_pathspec_match_list_free": { "ignore": true @@ -2001,6 +2061,7 @@ }, "remote": { "dependencies": [ + "../include/str_array_converter.h", "../include/remote_head.h" ], "cType": "git_remote", @@ -2282,6 +2343,9 @@ } }, "reset": { + "dependencies": [ + "../include/str_array_converter.h" + ], "functions": { "git_reset": { "args": { @@ -2615,6 +2679,9 @@ } }, "tag": { + "dependencies": [ + "../include/str_array_converter.h" + ], "selfFreeing": true, "functions": { "git_tag_foreach": { diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 78ecb8ad7..0fb18e65d 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -273,6 +273,16 @@ "git_annotated_commit_lookup" ] ], + [ + "filter_list", + [ + "git_filter_list_apply_to_blob", + "git_filter_list_apply_to_data", + "git_filter_list_apply_to_file", + "git_filter_list_free", + "git_filter_list_load" + ] + ], [ "filter_source", [ @@ -772,13 +782,6 @@ }, "filter": { "functions": [ - "git_filter_list_apply_to_blob", - "git_filter_list_apply_to_data", - "git_filter_list_apply_to_file", - "git_filter_list_contains", - "git_filter_list_free", - "git_filter_list_length", - "git_filter_list_load", "git_filter_list_new", "git_filter_list_push", "git_filter_list_stream_blob", diff --git a/generate/templates/manual/include/git_buf_converter.h b/generate/templates/manual/include/git_buf_converter.h index fcbd1eaba..567124629 100644 --- a/generate/templates/manual/include/git_buf_converter.h +++ b/generate/templates/manual/include/git_buf_converter.h @@ -4,13 +4,13 @@ #include #include "nan.h" -#include "git2/strarray.h" +#include "git2/buffer.h" using namespace v8; -class StrArrayConverter { +class GitBufConverter { public: - static git_strarray *Convert (v8::Local val); + static git_buf *Convert(v8::Local val); }; #endif diff --git a/generate/templates/manual/src/git_buf_converter.cc b/generate/templates/manual/src/git_buf_converter.cc index 371f14336..0c1969504 100644 --- a/generate/templates/manual/src/git_buf_converter.cc +++ b/generate/templates/manual/src/git_buf_converter.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include "../include/git_buf_converter.h" #include "git2/buffer.h" @@ -9,18 +8,21 @@ using namespace v8; using namespace node; -git_buf *StrArrayConverter::Convert(Local val) { +git_buf *GitBufConverter::Convert(Local val) { if (val->IsString() || val->IsStringObject()) { - string v8String = ConvertString(val->ToString()); + v8::String::Utf8Value param1(val->ToString()); + std::string v8String = std::string(*param1); + const size_t size = sizeof(git_buf); uint8_t* memory = reinterpret_cast(malloc(size)); git_buf *result = reinterpret_cast(memory); - size_t stringLength = v8String.length() - 1; + size_t stringLength = v8String.length(); memory = reinterpret_cast(malloc(stringLength)); memcpy(memory, v8String.c_str(), stringLength); + result->asize = stringLength; result->size = stringLength; result->ptr = reinterpret_cast(memory); return result; diff --git a/generate/templates/templates/binding.gyp b/generate/templates/templates/binding.gyp index e369a0795..09a5a017b 100644 --- a/generate/templates/templates/binding.gyp +++ b/generate/templates/templates/binding.gyp @@ -22,6 +22,7 @@ "src/convenient_patch.cc", "src/convenient_hunk.cc", "src/filter_registry.cc", + "src/git_buf_converter.cc", "src/str_array_converter.cc", "src/thread_pool.cc", {% each %} diff --git a/lib/repository.js b/lib/repository.js index 33adcc498..56d2f5a6f 100644 --- a/lib/repository.js +++ b/lib/repository.js @@ -8,6 +8,8 @@ var Commit = NodeGit.Commit; var normalizeOptions = NodeGit.Utils.normalizeOptions; var shallowClone = NodeGit.Utils.shallowClone; var path = require("path"); +var Filter = NodeGit.Filter; +var FilterList = NodeGit.FilterList; var Reference = NodeGit.Reference; var Remote = NodeGit.Remote; var Repository = NodeGit.Repository; @@ -37,7 +39,6 @@ function applySelectedLinesToTarget var oldIndex = 0; var linesPromises = []; - // split the original file into lines var oldLines = originalContent.toString().split("\n"); // if no selected lines were sent, return the original content @@ -121,6 +122,7 @@ function applySelectedLinesToTarget } } } + return newContent; }); } @@ -155,7 +157,7 @@ function getPathHunks(repo, index, filePath, isStaged, additionalDiffOptions) { if (!(status & NodeGit.Status.STATUS.WT_MODIFIED) && !(status & NodeGit.Status.STATUS.INDEX_MODIFIED)) { return Promise.reject - ("Selected staging is only available on modified files."); + ("Selected staging is only available on modified files."); } return diff.patches(); }); @@ -729,15 +731,34 @@ Repository.prototype.discardLines = var fullFilePath = path.join(repo.workdir(), filePath); var index; var originalContent; + var filterList; return repo.refreshIndex() .then(function(indexResult) { index = indexResult; + return FilterList.load( + repo, + null, + filePath, + Filter.MODE.CLEAN, + Filter.FLAG.DEFAULT + ); + }) + .then(function(_filterList) { + filterList = _filterList; + + if (filterList) { + return filterList.applyToFile(repo, filePath); + } return fse.readFile(fullFilePath, "utf8"); }) .then(function(content) { originalContent = content; + if (filterList) { + filterList.free(); + filterList = null; + } return getPathHunks(repo, index, filePath, false, additionalDiffOptions); }) @@ -747,7 +768,33 @@ Repository.prototype.discardLines = ); }) .then(function(newContent) { - return fse.writeFile(fullFilePath, newContent); + return FilterList.load( + repo, + null, + filePath, + Filter.MODE.SMUDGE, + Filter.FLAG.DEFAULT + ) + .then(function(_filterList) { + filterList = _filterList; + if (filterList) { + /* jshint ignore:start */ + // We need the constructor for the check in NodeGit's C++ layer + // to accept an object, and this seems to be a nice way to do it + return filterList.applyToData(new String(newContent)); + /* jshint ignore:end */ + } + + return newContent; + }); + }) + .then(function(filteredContent) { + if (filterList) { + filterList.free(); + filterList = null; + } + + return fse.writeFile(fullFilePath, filteredContent); }); }; diff --git a/test/tests/filter.js b/test/tests/filter.js index 021de6426..1003d35e0 100644 --- a/test/tests/filter.js +++ b/test/tests/filter.js @@ -12,6 +12,7 @@ describe("Filter", function() { var Registry = NodeGit.FilterRegistry; var Checkout = NodeGit.Checkout; var Repository = NodeGit.Repository; + var FilterList = NodeGit.FilterList; var reposPath = local("../repos/workdir"); var packageJsonPath = path.join(reposPath, "package.json"); @@ -926,4 +927,175 @@ describe("Filter", function() { }); }); }); + + describe("Manually Apply", function() { + beforeEach(function() { + var test = this; + var opts = { + checkoutStrategy: Checkout.STRATEGY.FORCE, + paths: "README.md" + }; + return Checkout.head(test.repository, opts) + .then(() => fse.readFile(readmePath, "utf8")) + .then((function(content) { + test.originalReadmeContent = content; + })); + }); + + afterEach(function() { + this.timeout(15000); + return fse.writeFile(readmePath, this.originalReadmeContent); + }); + + var message = "This is the filtered content, friends"; + var length = message.length; + var tempBuffer = new Buffer(message, "utf-8"); + + it("applies the filters for a path on demand", function() { + var test = this; + var list; + + return Registry.register(filterName, { + apply: function(to, from, source) { + return to.set(tempBuffer, length) + .then(function() { + return NodeGit.Error.CODE.OK; + }); + }, + check: function(src, attr) { + return NodeGit.Error.CODE.OK; + } + }, 0) + .then(function(result) { + assert.strictEqual(result, 0); + }) + .then(function() { + var readmeContent = fse.readFileSync( + readmePath, + "utf-8" + ); + assert.notStrictEqual(readmeContent, message); + fse.writeFileSync(readmePath, "whoa", "utf8"); + + return FilterList.load( + test.repository, + null, + "README.md", + NodeGit.Filter.MODE.CLEAN, + NodeGit.Filter.FLAG.DEFAULT + ); + }) + .then(function(_list) { + list = _list; + return list.applyToFile(test.repository, "README.md"); + }) + .then(function(content) { + assert.equal(content, message); + list.free(); + }); + }); + + it("applies the filters to a buffer on demand", function() { + var test = this; + var list; + + return Registry.register(filterName, { + apply: function(to, from, source) { + return to.set(tempBuffer, length) + .then(function() { + return NodeGit.Error.CODE.OK; + }); + }, + check: function(src, attr) { + return NodeGit.Error.CODE.OK; + } + }, 0) + .then(function(result) { + assert.strictEqual(result, 0); + }) + .then(function() { + var readmeContent = fse.readFileSync( + readmePath, + "utf-8" + ); + assert.notStrictEqual(readmeContent, message); + fse.writeFileSync(readmePath, "whoa", "utf8"); + + return FilterList.load( + test.repository, + null, + "README.md", + NodeGit.Filter.MODE.CLEAN, + NodeGit.Filter.FLAG.DEFAULT + ); + }) + .then(function(_list) { + list = _list; + /* jshint ignore:start */ + return list.applyToData(new String("garbo garbo garbo garbo")); + /* jshint ignore:end */ + }) + .then(function(content) { + assert.equal(content, message); + list.free(); + }); + }); + + it("applies the filters to a blob on demand", function() { + var test = this; + var list; + + return Registry.register(filterName, { + apply: function(to, from, source) { + return to.set(tempBuffer, length) + .then(function() { + return NodeGit.Error.CODE.OK; + }); + }, + check: function(src, attr) { + return NodeGit.Error.CODE.OK; + } + }, 0) + .then(function(result) { + assert.strictEqual(result, 0); + }) + .then(function() { + var readmeContent = fse.readFileSync( + readmePath, + "utf-8" + ); + assert.notStrictEqual(readmeContent, message); + fse.writeFileSync(readmePath, "whoa", "utf8"); + + return FilterList.load( + test.repository, + null, + "README.md", + NodeGit.Filter.MODE.CLEAN, + NodeGit.Filter.FLAG.DEFAULT + ); + }) + .then(function(_list) { + list = _list; + + return test.repository.getHeadCommit(); + }) + .then(function(commit) { + return commit.getTree(); + }) + .then(function(tree) { + return tree.entryByPath("README.md"); + }) + .then(function(entry) { + return test.repository.getBlob(entry.id()); + }) + .then(function(blob) { + return list.applyToBlob(blob); + }) + .then(function(content) { + assert.equal(content, message); + list.free(); + }); + }); + }); });