The NodeGit is Native Node bindings to Git,based on libgit2
.
It's a great library, but not provides all APIs of libgit2
.
I need to export the 3-Way-Merge algorithm from git_merge_file by myself.
- First of all, clone a depository from my forked project.
git clone git@github.com:myname/nodegit.git
cd nodegit
npm i
- Edit the configuration file of Customize the generated code using.
generate/input/descriptor.json
{
"types": {
"merge": {
"functions": {
"git_merge_file": {
// Remove `"ignore": true`, add config
"args": {
"out": {
"isReturn": true, // As return value
"shouldAlloc": true // Auto create
},
"opts": {
"isOptional": true // Can be null
}
},
"return": {
"isErrorCode": true
}
}
}
},
"merge_file_result": {
"ignore": false // Can not be ignore
}
}
}
- Generate detail configuration JSON
npm run generateJson
output at:
generate/output/idefs.json
search "cFunctionName": "git_merge_file",
, have got args and returns configuration.
- Build
nodegit.node
npm run install
build/Release/nodegit.node
- Define a new JS function
Merge.file
for convert args.
lib/merge.js
var _file = Merge.file;
/**
* Merge two files as they exist in the in-memory data structures, using
* the given common ancestor as the baseline. (git_merge_file)
*
* @param {MergeFileInput} ancestor The contents of the ancestor file
* @param {MergeFileInput} ours The contents of the file in "our" side
* @param {MergeFileInput} theirs The contents of the file in "theirs" side
* @param {MergeFileOptions} [options] The merge file options or `NULL` for defaults
*/
Merge.file = function(ancestor, ours, theirs, options) {
ancestor = normalizeOptions(ancestor, NodeGit.MergeFileInput);
ours = normalizeOptions(ours, NodeGit.MergeFileInput);
theirs = normalizeOptions(theirs, NodeGit.MergeFileInput);
options = normalizeOptions(options || {}, NodeGit.MergeFileOptions);
return _file.call(this, ancestor, ours, theirs, options);
};
- Generate JS in
dist/
with babel.
npm run prepublish
- Create test.
Create three Markdown files.
printf "a" > test/base.md
printf "a\nb" > test/ours.md
printf "a\nc" > test/theirs.md
Create test JS file.
test/mergefile.js
var NodeGit = require('..');
var path = require('path');
var fse = require('fs-extra');
const dir = '/mnt/d/workspace/nodegit/test';
async function newMergeFileInput(filePath) {
const stat = await fse.stat(filePath);
const content = await fse.readFile(filePath, 'utf-8');
// Wrong: It will case Crash while without normalizeOptions in 'lib/merge.js'
// const input = {
// ptr: content,
// size: content.length,
// mode: stat.mode,
// path: filePath,
// }
// Correct: Must create object from NodeGit class.
const input = new NodeGit.MergeFileInput();
input.ptr = content;
input.size = stat.size; // UTF8 size! (not content.length)
input.mode = stat.mode;
input.path = filePath;
return input;
}
function getFilePath(fileName) {
return path.join(dir, fileName);
}
async function test() {
// 3 files.
const base = await newMergeFileInput(getFilePath('base.md'));
const ours = await newMergeFileInput(getFilePath('ours.md'));
const theirs = await newMergeFileInput(getFilePath('theirs.md'));
console.log({ base, ours, theirs });
// Call merge file function.
const res = await NodeGit.Merge.file(base, ours, theirs, {
version: 0,
ancestorLabel: 'BASE',
ourLabel: 'OUR',
theirLabel: 'THEIR',
favor: 0,
flags: 0,
markerSize: 10,
});
// Must cut UTF8 Buff with len, or be wrong.
const ptr = Buffer.from(res.ptr(), 'utf-8').slice(0, res.len()).toString();
// Must call function to get field.
console.log('res:', {
automergeable: res.automergeable(),
path: res.path(),
mode: res.mode(),
ptr,
len: res.len(),
});
}
test();
Run.
node test/mergefile.js
Output.
{
base: MergeFileInput {
mode: 33279,
path: '/mnt/d/workspace/nodegit/test/base.md',
size: 1,
ptr: 'a',
version: 1
},
ours: MergeFileInput {
mode: 33279,
path: '/mnt/d/workspace/nodegit/test/ours.md',
size: 3,
ptr: 'a\nb',
version: 1
},
theirs: MergeFileInput {
mode: 33279,
path: '/mnt/d/workspace/nodegit/test/theirs.md',
size: 3,
ptr: 'a\nc',
version: 1
}
}
res: {
automergeable: 0,
path: null,
mode: 33279,
ptr: 'a\n<<<<<<<<<< OUR\nb\n==========\nc\n>>>>>>>>>> THEIR\n',
len: 49
}
- Publish
Change name and version in package.json for publish new version.
npm login
npm publish
Install it use yarn.
yarn add pj-nodegit@latest
IMPORTANT
If argument object don't create with NodeGit Class and without wrap normalizeOptions
. The nodegit.node
will be crash.
node: ../node_modules/nan/nan_object_wrap.h:32: static T* Nan::ObjectWrap::Unwrap(v8::Local<v8::Object>) [with T = GitMergeFileOptions]: Assertion `object->InternalFieldCount() > 0' failed.
Why?
It lacks embedder fields
, by compare with object of correct function. The v8 object InternalFieldCount()
only counts the embedder fields
actually. What is it? I don't know, may be a special sign of v8 object. No mater how, create object with NodeGit Class will be correct. I found the solution according by function normalizeOptions
in file lib/utils/normalize_options.js
.
var NodeGit = require('../../');
function normalizeOptions(options, Ctor) {
if (!options) {
return null;
}
if (options instanceof Ctor) {
return options;
}
var instance = new Ctor(); // here.
Object.keys(options).forEach(function(key) {
if (typeof options[key] !== 'undefined') {
instance[key] = options[key];
}
});
return instance;
}
NodeGit.Utils.normalizeOptions = normalizeOptions;