diff --git a/.travis.yml b/.travis.yml
index ef6f06fc2..2b317c86e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,19 +13,19 @@ sudo: false
env:
matrix:
- - export NODE_VERSION="4" TARGET_ARCH="x64"
- - export NODE_VERSION="6.5" TARGET_ARCH="x64"
+ - export NODE_VERSION="stable" TARGET_ARCH="x64"
- export NODE_VERSION="7.4" TARGET_ARCH="x64"
+ - export NODE_VERSION="6.5" TARGET_ARCH="x64"
matrix:
fast_finish: true
include:
- os: linux
- env: export NODE_VERSION="4" TARGET_ARCH="ia32"
- - os: linux
- env: export NODE_VERSION="6.5" TARGET_ARCH="ia32"
+ env: export NODE_VERSION="stable" TARGET_ARCH="ia32"
- os: linux
env: export NODE_VERSION="7.4" TARGET_ARCH="ia32"
+ - os: linux
+ env: export NODE_VERSION="6.5" TARGET_ARCH="ia32"
git:
depth: 1
@@ -66,7 +66,7 @@ before_install:
fi
install:
- - npm install;
+ - travis_retry npm install;
# This is a random private key used purely for testing.
before_script:
@@ -81,9 +81,9 @@ before_script:
script:
if [ -z "$TRAVIS_TAG" ] && [ $TRAVIS_OS_NAME == "linux" ] && [ $NODE_VERSION == "6" ]; then
- npm test && npm run cov && npm run coveralls;
+ travis_retry npm test && npm run cov && npm run coveralls;
else
- npm test;
+ travis_retry npm test;
fi
after_success:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 516919bed..0d8fdc98a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,86 @@
# Change Log
+## v0.20.0 [(2017-08-16)](https://github.com/nodegit/nodegit/releases/tag/v0.20.0)
+
+[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.19.0...v0.20.0)
+
+#### Summary of changes outside of libgit2 bump:
+ - [BREAKING: Added blob methods and converted to async #1327](https://github.com/nodegit/nodegit/pull/1327)
+ - [BREAKING: Convert sync methods to async methods #1348](https://github.com/nodegit/nodegit/pull/1348)
+ - [Exposed libgit2 git_branch_remote_name method #1340](https://github.com/nodegit/nodegit/pull/1340)
+ - [Adding git_filter support in nodegit #1331](https://github.com/nodegit/nodegit/pull/1331)
+ - [Add CI build config for node stable version #1337](https://github.com/nodegit/nodegit/pull/1337)
+ - [removed node v4 tests #1330](https://github.com/nodegit/nodegit/pull/1330)
+
+#### Included merged libgti2 PRs:
+ - [Include fixups #4288](https://github.com/libgit2/libgit2/pull/4288)
+ - [Docs: Fix inline comments for git_diff_hunk #4330](https://github.com/libgit2/libgit2/pull/4330)
+ - [oid: use memcmp in git_oid__hashcmp #4328](https://github.com/libgit2/libgit2/pull/4328)
+ - [sha1_lookup: drop sha1_entry_pos function #4327](https://github.com/libgit2/libgit2/pull/4327)
+ - [sha1_position: convert do-while to while #4326](https://github.com/libgit2/libgit2/pull/4326)
+ - [patch_generate: represent buffers as void pointers #4304](https://github.com/libgit2/libgit2/pull/4304)
+ - [Remove unused 'sys/remote.h' header #4323](https://github.com/libgit2/libgit2/pull/4323)
+ - [tests: rebase::submodule: verify initialization method calls #4320](https://github.com/libgit2/libgit2/pull/4320)
+ - [tests: rewrite rebase-submodule .gitmodule file #4275](https://github.com/libgit2/libgit2/pull/4275)
+ - [tsort: remove idempotent conditional assignment #4314](https://github.com/libgit2/libgit2/pull/4314)
+ - [Build with patched libcurl #4317](https://github.com/libgit2/libgit2/pull/4317)
+ - [win32: provide fast-path for retrying filesystem operations #4311](https://github.com/libgit2/libgit2/pull/4311)
+ - [Configuration file fixes with includes #4250](https://github.com/libgit2/libgit2/pull/4250)
+ - [Patch ID calculation #4272](https://github.com/libgit2/libgit2/pull/4272)
+ - [signature: don't leave a dangling pointer to the strings on parse failure #4306](https://github.com/libgit2/libgit2/pull/4306)
+ - [git_reset_*: pass parameters as const pointers #4287](https://github.com/libgit2/libgit2/pull/4287)
+ - [tests: config: fix missing declaration causing error #4291](https://github.com/libgit2/libgit2/pull/4291)
+ - [Convert port with htons() in p_getaddrinfo() #4280](https://github.com/libgit2/libgit2/pull/4280)
+ - [Out of tree builds #4235](https://github.com/libgit2/libgit2/pull/4235)
+ - [cmake: Permit disabling external http-parser #4278](https://github.com/libgit2/libgit2/pull/4278)
+ - [merge: fix potential free of uninitialized memory #4277](https://github.com/libgit2/libgit2/pull/4277)
+ - [merge: perform exact rename detection in linear time #4202](https://github.com/libgit2/libgit2/pull/4202)
+ - [travis: upgrade container to Ubuntu 14.04 #4211](https://github.com/libgit2/libgit2/pull/4211)
+ - [Fix template dir empty string #4273](https://github.com/libgit2/libgit2/pull/4273)
+ - [adding GIT_FILTER_VERSION to GIT_FILTER_INIT as part of convention #4267](https://github.com/libgit2/libgit2/pull/4267)
+ - [travis: replace use of deprecated homebrew/dupes tap #4268](https://github.com/libgit2/libgit2/pull/4268)
+ - [Test improvements #4269](https://github.com/libgit2/libgit2/pull/4269)
+ - [Read prefix tests #4265](https://github.com/libgit2/libgit2/pull/4265)
+ - [Allow creation of a configuration object in an in-memory repository #4263](https://github.com/libgit2/libgit2/pull/4263)
+ - [travis: install openssl explicitly #4266](https://github.com/libgit2/libgit2/pull/4266)
+ - [smart_protocol: fix parsing of server ACK responses #4261](https://github.com/libgit2/libgit2/pull/4261)
+ - [odb_read_prefix: reset error in backends loop #4264](https://github.com/libgit2/libgit2/pull/4264)
+ - [Update version number to 0.26 #4262](https://github.com/libgit2/libgit2/pull/4262)
+ - [CHANGELOG: add various changes introduced since v0.25 #4254](https://github.com/libgit2/libgit2/pull/4254)
+ - [Ensure packfiles with different contents have different names #4088](https://github.com/libgit2/libgit2/pull/4088)
+ - [Update to forced checkout and untracked files #4260](https://github.com/libgit2/libgit2/pull/4260)
+ - [settings: rename `GIT_OPT_ENABLE_SYNCHRONOUS_OBJECT_CREATION` #4259](https://github.com/libgit2/libgit2/pull/4259)
+ - [Buffer growing cleanups #4255](https://github.com/libgit2/libgit2/pull/4255)
+ - [Coverity fixes #4253](https://github.com/libgit2/libgit2/pull/4253)
+ - [SHA1DC update #4258](https://github.com/libgit2/libgit2/pull/4258)
+ - [Fix path computations for compressed index entries #4236](https://github.com/libgit2/libgit2/pull/4236)
+ - [(Temporarily) disable UNC tests #4256](https://github.com/libgit2/libgit2/pull/4256)
+ - [fix build with libressl #4251](https://github.com/libgit2/libgit2/pull/4251)
+ - [Fix issue with directory glob ignore in subdirectories #4239](https://github.com/libgit2/libgit2/pull/4239)
+ - [Submodule working directory #4243](https://github.com/libgit2/libgit2/pull/4243)
+ - [Introduce home directory expansion function for config files, attribute files #4179](https://github.com/libgit2/libgit2/pull/4179)
+ - [Fix proxy auto detect not utilizing callbacks #4097](https://github.com/libgit2/libgit2/pull/4097)
+ - [git_repository_set_head: use tag name in reflog #4174](https://github.com/libgit2/libgit2/pull/4174)
+ - [revparse: support open-ended ranges #4231](https://github.com/libgit2/libgit2/pull/4231)
+ - [Fix GCC warnings #4240](https://github.com/libgit2/libgit2/pull/4240)
+ - [Update README: VS -> VSTS #4238](https://github.com/libgit2/libgit2/pull/4238)
+ - [tests: repo: fix repo discovery tests on overlayfs #4232](https://github.com/libgit2/libgit2/pull/4232)
+ - [libssh2 shutdown #4229](https://github.com/libgit2/libgit2/pull/4229)
+ - [WIP: squash some memleaks #4226](https://github.com/libgit2/libgit2/pull/4226)
+ - [Verify object hashes #4197](https://github.com/libgit2/libgit2/pull/4197)
+ - [transport: provide a getter for the proxy options #4206](https://github.com/libgit2/libgit2/pull/4206)
+ - [Debian HTTPS feature test failure #4216](https://github.com/libgit2/libgit2/pull/4216)
+ - [Do not free config when creating remote #4224](https://github.com/libgit2/libgit2/pull/4224)
+ - [socket_stream: continue to next addrinfo on socket creation failure #4219](https://github.com/libgit2/libgit2/pull/4219)
+ - [Honor read-only flag when writing to config backends #4217](https://github.com/libgit2/libgit2/pull/4217)
+ - [diff_parse: free object instead of its pointer #4215](https://github.com/libgit2/libgit2/pull/4215)
+
+#### Included non-merged libgit2 PRs:
+
+ - [Parallelize checkout_create_the_new for ntfs perf gains #4205](https://github.com/libgit2/libgit2/pull/4205)
+ - [negotiate always fails via libcurl #4126](https://github.com/libgit2/libgit2/pull/4126)
+
+
## v0.19.0 [(2017-04-20)](https://github.com/nodegit/nodegit/releases/tag/v0.19.0)
[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.18.0...v0.19.0)
@@ -81,7 +162,7 @@
- `ontoSha` The sha that we rebased onto
- `originalHeadName` The name of the branch that we rebased
- `originalHeadSha` The sha of the branch that was rebased
- - `rewitten` which is an array of sha pairs that contain which contain what the commit sha was before the rebase and what the commit sha is after the rebase
+ - `rewitten` which is an array of sha pairs that contain which contain what the commit sha was before the rebase and what the commit sha is after the rebase
### Summary of Changes from bumping libgit2 to 43275f5
@@ -140,7 +221,7 @@
In this release we had added support for Node v7 and latest Electron. We have removed support for Node v0.12 and v5.
-We are also deprecating nw.js support since it is currently broken, no one in the current team uses it and we would not be able to currently support nw.js in an effective manner with a good user experience.
+We are also deprecating nw.js support since it is currently broken, no one in the current team uses it and we would not be able to currently support nw.js in an effective manner with a good user experience.
### Now building against shared libcurl lib
diff --git a/README.md b/README.md
index 029378eb9..3a9ee8b80 100644
--- a/README.md
+++ b/README.md
@@ -31,8 +31,8 @@ NodeGit
-**Stable (libgit2#master): 0.19.0**
-**Stable (libgit2@0.24): 0.14.1**
+**Stable (libgit2#master): 0.20.0**
+**Stable (libgit2@v0.26.0): 0.26.0**
## Have a problem? Come chat with us! ##
diff --git a/appveyor.yml b/appveyor.yml
index 5f7172f5c..c25c50ebb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -27,9 +27,9 @@ environment:
GYP_MSVS_VERSION: 2013
matrix:
# Node.js
- - nodejs_version: "4"
- - nodejs_version: "6"
+ - nodejs_version: "stable"
- nodejs_version: "7"
+ - nodejs_version: "6"
matrix:
fast_finish: true
@@ -40,12 +40,12 @@ install:
- ps: Start-Process c:\projects\nodegit\vendor\pageant.exe c:\projects\nodegit\vendor\private.ppk
- npm install -g npm
- cmd: npm install -g node-gyp
- - npm install
+ - appveyor-retry call npm install
test_script:
- node --version
- npm --version
- - cmd: npm test
+ - appveyor-retry call npm test
on_success:
- IF %APPVEYOR_REPO_TAG%==true npm install -g node-pre-gyp
diff --git a/generate/input/callbacks.json b/generate/input/callbacks.json
index 999ccd57f..5fe947aa5 100644
--- a/generate/input/callbacks.json
+++ b/generate/input/callbacks.json
@@ -311,6 +311,102 @@
"error": -1
}
},
+ "git_filter_apply_fn": {
+ "args": [
+ {
+ "name": "self",
+ "cType": "git_filter *"
+ },
+ {
+ "name": "payload",
+ "cType": "void **"
+ },
+ {
+ "name": "to",
+ "cType": "git_buf *"
+ },
+ {
+ "name": "from",
+ "cType": "const git_buf *"
+ },
+ {
+ "name": "src",
+ "cType": "const git_filter_source *"
+ }
+ ],
+ "return": {
+ "type": "int",
+ "noResults": -30,
+ "success": 0,
+ "error": -1
+ }
+ },
+ "git_filter_check_fn": {
+ "args": [
+ {
+ "name": "self",
+ "cType": "git_filter *"
+ },
+ {
+ "name": "payload",
+ "cType": "void **"
+ },
+ {
+ "name": "src",
+ "cType": "const git_filter_source *"
+ },
+ {
+ "name": "attr_values",
+ "cType": "const char **"
+ }
+ ],
+ "return": {
+ "type": "int",
+ "noResults": -30,
+ "success": 0,
+ "error": -1
+ }
+ },
+ "git_filter_cleanup_fn": {
+ "args": [
+ {
+ "name": "self",
+ "cType": "git_filter *"
+ },
+ {
+ "name": "payload",
+ "cType": "void *"
+ }
+ ],
+ "return": {
+ "type": "void"
+ }
+ },
+ "git_filter_init_fn": {
+ "args": [
+ {
+ "name": "self",
+ "cType": "git_filter *"
+ }
+ ],
+ "return": {
+ "type": "int",
+ "noResults": 0,
+ "success": 0,
+ "error": -1
+ }
+ },
+ "git_filter_shutdown_fn": {
+ "args": [
+ {
+ "name": "self",
+ "cType": "git_filter *"
+ }
+ ],
+ "return": {
+ "type": "void"
+ }
+ },
"git_index_matched_path_cb": {
"args": [
{
diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json
index d33298c30..7d1953de8 100644
--- a/generate/input/descriptor.json
+++ b/generate/input/descriptor.json
@@ -130,7 +130,7 @@
"selfFreeing": true,
"functions": {
"git_blob_create_frombuffer": {
- "isAsync": false,
+ "isAsync": true,
"args": {
"id": {
"isReturn": true
@@ -141,11 +141,59 @@
}
}
},
+ "git_blob_create_fromworkdir": {
+ "isAsync": true,
+ "args": {
+ "id": {
+ "isReturn": true
+ }
+ },
+ "return": {
+ "isErrorCode": true
+ }
+ },
+ "git_blob_create_fromdisk": {
+ "isAsync": true,
+ "args": {
+ "id": {
+ "isReturn": true
+ }
+ },
+ "return": {
+ "isErrorCode": true
+ }
+ },
"git_blob_create_fromchunks": {
"ignore": true
},
"git_blob_filtered_content": {
- "ignore": true
+ "isAsync": true,
+ "isPrototypeMethod": false,
+ "args": {
+ "out": {
+ "isReturn": true,
+ "cppClassName": "GitBuf",
+ "jsClassName": "Buffer"
+ },
+ "blob": {
+ "cppClassName": "GitBlob",
+ "jsClassName": "Blob",
+ "isSelf": false
+ },
+ "as_path": {
+ "cppClassName": "String",
+ "jsClassName": "String",
+ "cType": "const char *"
+ },
+ "check_for_binary_data": {
+ "cppClassName": "Number",
+ "jsClassName": "Number",
+ "cType": "int"
+ }
+ },
+ "return": {
+ "isErrorCode": true
+ }
},
"git_blob_id": {
"return": {
@@ -202,6 +250,32 @@
"git_branch_next": {
"ignore": true
},
+ "git_branch_remote_name": {
+ "cppFunctionName": "RemoteName",
+ "jsFunctionName": "remoteName",
+ "isAsync": true,
+ "args": {
+ "out": {
+ "isReturn": true,
+ "cppClassName": "GitBuf",
+ "jsClassName": "Buffer",
+ "cType": "git_buf *"
+ },
+ "repo": {
+ "cppClassName": "GitRepository",
+ "jsClassName": "Repo",
+ "cType": "git_repository *"
+ },
+ "canonical_branch_name": {
+ "cppClassName": "String",
+ "jsClassName": "String",
+ "cType": "const char *"
+ }
+ },
+ "return": {
+ "isErrorCode": true
+ }
+ },
"git_branch_set_upstream": {
"isAsync": true,
"args": {
@@ -221,7 +295,10 @@
"isErrorCode": true
}
}
- }
+ },
+ "dependencies": [
+ "../include/buf.h"
+ ]
},
"buf": {
"functions": {
@@ -373,6 +450,7 @@
"selfFreeing": true,
"functions": {
"git_commit_amend": {
+ "isAsync": true,
"args": {
"author": {
"isOptional": true
@@ -879,6 +957,8 @@
}
},
"filter": {
+ "selfFreeing": false,
+ "hasConstructor": true,
"functions": {
"git_filter_list_apply_to_blob": {
"ignore": true
@@ -920,30 +1000,21 @@
"ignore": true
}
},
+ "cDependencies": [
+ "git2/sys/filter.h"
+ ],
"fields": {
- "initialize": {
- "ignore": true
- },
- "shutdown": {
- "ignore": true
- },
- "check": {
- "ignore": true
- },
- "apply": {
- "ignore": true
- },
- "cleanup": {
+ "stream": {
"ignore": true
}
- },
+ }
+ },
+ "filter_source": {
+ "ignore": false,
"cDependencies": [
"git2/sys/filter.h"
]
},
- "filter_source": {
- "ignore": true
- },
"graph": {
"functions": {
"git_graph_ahead_behind": {
@@ -1246,13 +1317,35 @@
"merge": {
"functions": {
"git_merge": {
+ "isAsync": true,
"args": {
+ "repo": {
+ "cppClassName": "GitRepository",
+ "cType": "git_repository *",
+ "jsClassName": "Repo"
+ },
"their_heads": {
"cType": "const git_annotated_commit **",
"cppClassName": "Array",
"jsClassName": "Array",
"arrayElementCppClassName": "GitAnnotatedCommit"
+ },
+ "their_heads_len": {
+ "cType": "size_t",
+ "cppClassName": "Number",
+ "jsClassName": "Number"
+ },
+ "merge_opts": {
+ "cType": "git_merge_options *",
+ "cppClassName": "GitMergeOptions"
+ },
+ "checkout_opts": {
+ "cType": "git_checkout_options *",
+ "cppClassName": "GitCheckoutOptions"
}
+ },
+ "return": {
+ "isErrorCode": true
}
},
"git_merge_analysis": {
@@ -1637,8 +1730,12 @@
"rebase": {
"functions": {
"git_rebase_commit": {
+ "isAsync": true,
"args": {
"id": {
+ "cType": "git_oid *",
+ "cppClassName": "GitOid",
+ "jsClassName": "Oid",
"isReturn": true,
"shouldAlloc": true
},
@@ -1651,6 +1748,9 @@
"message": {
"isOptional": true
}
+ },
+ "return": {
+ "isErrorCode": true
}
},
"git_rebase_finish": {
@@ -1678,6 +1778,22 @@
"isOptional": true
}
}
+ },
+ "git_rebase_abort": {
+ "isAsync": true,
+ "args": {
+ "rebase": {
+ "cType": "git_rebase *",
+ "cppClassName": "GitRebase",
+ "jsClassName": "Rebase",
+ "isOptional": false,
+ "isSelf": true,
+ "isReturn": false
+ }
+ },
+ "return": {
+ "isErrorCode": true
+ }
}
}
},
@@ -1776,6 +1892,17 @@
"needsForwardDeclaration": false,
"ignore": true
},
+ "reflog": {
+ "functions": {
+ "git_reflog_write": {
+ "isAsync": true,
+ "isSelf": true,
+ "return": {
+ "isErrorCode": true
+ }
+ }
+ }
+ },
"reflog_entry": {
"functions": {
"git_reflog_entry_id_new": {
@@ -2219,9 +2346,11 @@
"isAsync": false
},
"git_status_file": {
+ "isAsync": true,
"args": {
"status_flags": {
- "isReturn": true
+ "isReturn": true,
+ "shouldAlloc": true
},
"return": {
"isErrorCode": true
diff --git a/generate/input/libgit2-docs.json b/generate/input/libgit2-docs.json
index 48099e8b0..fb9f175af 100644
--- a/generate/input/libgit2-docs.json
+++ b/generate/input/libgit2-docs.json
@@ -2632,6 +2632,38 @@
"comments": "
The name of the branch matches the definition of the name for git_branch_lookup. That is, if the returned name is given to git_branch_lookup() then the reference is returned that was given to this function.
\n",
"group": "branch"
},
+ "git_branch_remote_name": {
+ "type": "function",
+ "file": "branch.h",
+ "line": 274,
+ "lineto": 277,
+ "args": [
+ {
+ "name": "out",
+ "type": "git_buf *",
+ "comment": "where the name is stored."
+ },
+ {
+ "name": "repo",
+ "type": "git_respository *",
+ "comment": "the repo to check."
+ },
+ {
+ "name": "canonical_branch_name",
+ "type": "const char *",
+ "comment": "the ref name of the branch"
+ }
+ ],
+ "argline": "git_buf *out, git_repository *repo, const char *canonical_branch_name",
+ "sig": "git_buf *::git_repository *::const char *",
+ "return": {
+ "type": "int",
+ "comment": " 0 on success; otherwise an error code (e.g., if the\n ref is no local or remote branch)."
+ },
+ "description": "Return the name of the given remote branch.
\n",
+ "comments": "
\n",
+ "group": "branch"
+ },
"git_branch_upstream": {
"type": "function",
"file": "branch.h",
@@ -36087,6 +36119,7 @@
"git_branch_name",
"git_branch_next",
"git_branch_set_upstream",
+ "git_branch_remote_name",
"git_branch_upstream"
]
],
@@ -37149,4 +37182,4 @@
"ex/HEAD/tag.html"
]
]
-}
\ No newline at end of file
+}
diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json
index 52bb45651..12cfe7b2a 100644
--- a/generate/input/libgit2-supplement.json
+++ b/generate/input/libgit2-supplement.json
@@ -273,6 +273,17 @@
"git_annotated_commit_lookup"
]
],
+ [
+ "filter_source",
+ [
+ "git_filter_source_repo",
+ "git_filter_source_path",
+ "git_filter_source_filemode",
+ "git_filter_source_id",
+ "git_filter_source_mode",
+ "git_filter_source_flags"
+ ]
+ ],
[
"odb_object",
[
@@ -422,33 +433,23 @@
},
{
"type": "git_filter_init_fn",
- "name": "initialize",
- "ignore": true
+ "name": "initialize"
},
{
"type": "git_filter_shutdown_fn",
- "name": "shutdown",
- "ignore": true
+ "name": "shutdown"
},
{
"type": "git_filter_check_fn",
- "name": "check",
- "ignore": true
+ "name": "check"
},
{
"type": "git_filter_apply_fn",
- "name": "apply",
- "ignore": true
- },
- {
- "type": "git_filter_stream_fn",
- "name": "stream",
- "ignore": true
+ "name": "apply"
},
{
"type": "git_filter_cleanup_fn",
- "name": "cleanup",
- "ignore": true
+ "name": "cleanup"
}
]
}
@@ -765,14 +766,26 @@
"git_annotated_commit_lookup"
]
},
- "odb": {
+ "filter": {
"functions": [
- "git_odb_object_data",
- "git_odb_object_dup",
- "git_odb_object_free",
- "git_odb_object_id",
- "git_odb_object_size",
- "git_odb_object_type"
+ "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",
+ "git_filter_list_stream_data",
+ "git_filter_list_stream_file",
+ "git_filter_source_filemode",
+ "git_filter_source_flags",
+ "git_filter_source_id",
+ "git_filter_source_mode",
+ "git_filter_source_path",
+ "git_filter_source_repo"
]
},
"merge": {
@@ -784,6 +797,16 @@
"git_merge_head_id"
]
},
+ "odb": {
+ "functions": [
+ "git_odb_object_data",
+ "git_odb_object_dup",
+ "git_odb_object_free",
+ "git_odb_object_id",
+ "git_odb_object_size",
+ "git_odb_object_type"
+ ]
+ },
"reflog": {
"functions": [
"git_reflog_entry_committer",
diff --git a/generate/scripts/generateJson.js b/generate/scripts/generateJson.js
index 323247e13..49a2795c2 100644
--- a/generate/scripts/generateJson.js
+++ b/generate/scripts/generateJson.js
@@ -169,9 +169,18 @@ module.exports = function generateJson() {
}
};
+ var addSelfReferentialField = function(prop){
+ if (helpers.isSelfReferential(prop.type)) {
+ prop.isSelfReferential = true;
+ def.isExtendedStruct = true;
+ }
+ };
+
def.fields.forEach(addDependencies);
+ def.fields.forEach(addSelfReferentialField);
def.functions.forEach(addDependencies);
+
Object.keys(dependencies).forEach(function (dependencyFilename) {
def.dependencies.push("../include/" + dependencyFilename + ".h");
});
@@ -183,7 +192,6 @@ module.exports = function generateJson() {
fn.cppClassName = def.cppClassName;
});
});
-
// Process enums
_(enums).forEach(function(enumerable) {
output.some(function(obj) {
diff --git a/generate/scripts/generateNativeCode.js b/generate/scripts/generateNativeCode.js
index a82bef4e5..30fc52707 100644
--- a/generate/scripts/generateNativeCode.js
+++ b/generate/scripts/generateNativeCode.js
@@ -68,9 +68,11 @@ module.exports = function generateNativeCode() {
replace: require("../templates/filters/replace"),
returnsCount: require("../templates/filters/returns_count"),
returnsInfo: require("../templates/filters/returns_info"),
+ subtract: require("../templates/filters/subtract"),
titleCase: require("../templates/filters/title_case"),
toBool: require('../templates/filters/to_bool'),
unPointer: require("../templates/filters/un_pointer"),
+ setUnsigned: require("../templates/filters/unsigned"),
upper: require("../templates/filters/upper")
};
diff --git a/generate/scripts/helpers.js b/generate/scripts/helpers.js
index 48b651355..9b7baa2cb 100644
--- a/generate/scripts/helpers.js
+++ b/generate/scripts/helpers.js
@@ -1,4 +1,5 @@
-var callbackTypePattern = /\s*_cb/;
+var callbackTypePattern = /\s*_(cb|fn)/,
+ selfReferentialTypePattern = /\s*_fn/;
var utils = require("./utils");
var _ = require("lodash");
@@ -93,6 +94,10 @@ var Helpers = {
&& ~cbField.name.indexOf(payloadName.replace("_payload", ""));
},
+ isSelfReferential: function(cType){
+ return selfReferentialTypePattern.test(cType);
+ },
+
getLibgitType: function(normalizedType, types) {
var libgitType;
diff --git a/generate/templates/filters/args_info.js b/generate/templates/filters/args_info.js
index 1382c67d6..0c05c30eb 100644
--- a/generate/templates/filters/args_info.js
+++ b/generate/templates/filters/args_info.js
@@ -13,12 +13,8 @@ module.exports = function(args) {
jsArg++;
}
- if (cArg === args.length -1) {
- arg.lastArg = true;
- }
- else {
- arg.lastArg = false;
- }
+ arg.lastArg = cArg === args.length - 1;
+ arg.firstArg = !arg.lastArg && cArg === 0;
arg.cArg = cArg;
arg.isCppClassStringOrArray = ~["String", "Array"].indexOf(arg.cppClassName);
diff --git a/generate/templates/filters/subtract.js b/generate/templates/filters/subtract.js
new file mode 100644
index 000000000..6329f2e2c
--- /dev/null
+++ b/generate/templates/filters/subtract.js
@@ -0,0 +1,3 @@
+module.exports = function(value, other) {
+ return value - other;
+};
diff --git a/generate/templates/filters/unsigned.js b/generate/templates/filters/unsigned.js
new file mode 100644
index 000000000..a46078331
--- /dev/null
+++ b/generate/templates/filters/unsigned.js
@@ -0,0 +1,3 @@
+module.exports = function(value) {
+ return value < 0 ? 0 : value;
+};
diff --git a/generate/templates/manual/include/async_baton.h b/generate/templates/manual/include/async_baton.h
index 5f6874102..f8373cd0d 100644
--- a/generate/templates/manual/include/async_baton.h
+++ b/generate/templates/manual/include/async_baton.h
@@ -60,4 +60,38 @@ struct AsyncBatonWithResult : public AsyncBaton {
}
};
+struct AsyncBatonWithNoResult : public AsyncBaton {
+ void (*onCompletion)(AsyncBaton *);
+
+ void Done() {
+ if (onCompletion) {
+ onCompletion(this);
+ } else {
+ // signal completion
+ uv_sem_post(&semaphore);
+ }
+ }
+
+ void ExecuteAsync(ThreadPool::Callback asyncCallback, void (*onCompletion)(AsyncBaton *) = NULL) {
+ this->onCompletion = onCompletion;
+ if (!onCompletion) {
+ uv_sem_init(&semaphore, 0);
+ }
+
+ {
+ LockMaster::TemporaryUnlock temporaryUnlock;
+
+ libgit2ThreadPool.ExecuteReverseCallback(asyncCallback, this);
+
+ if (!onCompletion) {
+ // wait for completion
+ uv_sem_wait(&semaphore);
+ uv_sem_destroy(&semaphore);
+ }
+ }
+
+ return;
+ }
+};
+
#endif
diff --git a/generate/templates/manual/include/filter_registry.h b/generate/templates/manual/include/filter_registry.h
new file mode 100644
index 000000000..b75938218
--- /dev/null
+++ b/generate/templates/manual/include/filter_registry.h
@@ -0,0 +1,75 @@
+#ifndef GITFILTERREGISTRY_H
+#define GITFILTERREGISTRY_H
+#include
+#include
+#include
+#include
+
+#include "async_baton.h"
+#include "nodegit_wrapper.h"
+#include "promise_completion.h"
+
+extern "C" {
+#include
+}
+
+#include "../include/typedefs.h"
+
+#include "../include/filter.h"
+
+using namespace node;
+using namespace v8;
+
+
+class GitFilterRegistry : public Nan::ObjectWrap {
+ public:
+ static void InitializeComponent(v8::Local target);
+
+ static Nan::Persistent persistentHandle;
+
+ private:
+
+ static NAN_METHOD(GitFilterRegister);
+
+ static NAN_METHOD(GitFilterUnregister);
+
+ struct FilterRegisterBaton {
+ const git_error *error;
+ git_filter *filter;
+ char *filter_name;
+ int filter_priority;
+ int error_code;
+ };
+
+ struct FilterUnregisterBaton {
+ const git_error *error;
+ char *filter_name;
+ int error_code;
+ };
+
+ class RegisterWorker : public Nan::AsyncWorker {
+ public:
+ RegisterWorker(FilterRegisterBaton *_baton, Nan::Callback *callback)
+ : Nan::AsyncWorker(callback), baton(_baton) {};
+ ~RegisterWorker() {};
+ void Execute();
+ void HandleOKCallback();
+
+ private:
+ FilterRegisterBaton *baton;
+ };
+
+ class UnregisterWorker : public Nan::AsyncWorker {
+ public:
+ UnregisterWorker(FilterUnregisterBaton *_baton, Nan::Callback *callback)
+ : Nan::AsyncWorker(callback), baton(_baton) {};
+ ~UnregisterWorker() {};
+ void Execute();
+ void HandleOKCallback();
+
+ private:
+ FilterUnregisterBaton *baton;
+ };
+};
+
+#endif
diff --git a/generate/templates/manual/src/filter_registry.cc b/generate/templates/manual/src/filter_registry.cc
new file mode 100644
index 000000000..a44e4ae70
--- /dev/null
+++ b/generate/templates/manual/src/filter_registry.cc
@@ -0,0 +1,215 @@
+#include
+#include
+
+extern "C" {
+ #include
+}
+
+#include "../include/nodegit.h"
+#include "../include/lock_master.h"
+#include "../include/functions/copy.h"
+#include "../include/filter_registry.h"
+#include "nodegit_wrapper.cc"
+#include "../include/async_libgit2_queue_worker.h"
+
+#include "../include/filter.h"
+
+using namespace std;
+using namespace v8;
+using namespace node;
+
+Nan::Persistent GitFilterRegistry::persistentHandle;
+
+// #pragma unmanaged
+void GitFilterRegistry::InitializeComponent(v8::Local target) {
+ Nan::HandleScope scope;
+
+ v8::Local object = Nan::New();
+
+ Nan::SetMethod(object, "register", GitFilterRegister);
+ Nan::SetMethod(object, "unregister", GitFilterUnregister);
+
+ Nan::Set(target, Nan::New("FilterRegistry").ToLocalChecked(), object);
+ GitFilterRegistry::persistentHandle.Reset(object);
+}
+
+NAN_METHOD(GitFilterRegistry::GitFilterRegister) {
+ Nan::EscapableHandleScope scope;
+
+ if (info.Length() == 0 || !info[0]->IsString()) {
+ return Nan::ThrowError("String name is required.");
+ }
+
+ if (info.Length() == 1 || !info[1]->IsObject()) {
+ return Nan::ThrowError("Filter filter is required.");
+ }
+
+ if (info.Length() == 2 || !info[2]->IsNumber()) {
+ return Nan::ThrowError("Number priority is required.");
+ }
+
+ if (info.Length() == 3 || !info[3]->IsFunction()) {
+ return Nan::ThrowError("Callback is required and must be a Function.");
+ }
+
+ FilterRegisterBaton *baton = new FilterRegisterBaton;
+
+ baton->filter = Nan::ObjectWrap::Unwrap(info[1]->ToObject())->GetValue();
+ String::Utf8Value name(info[0]->ToString());
+
+ baton->filter_name = (char *)malloc(name.length() + 1);
+ memcpy((void *)baton->filter_name, *name, name.length());
+ memset((void *)(((char *)baton->filter_name) + name.length()), 0, 1);
+
+ baton->error_code = GIT_OK;
+ baton->filter_priority = (int)info[2]->ToNumber()->Value();
+
+ Nan::New(GitFilterRegistry::persistentHandle)->Set(info[0]->ToString(), info[1]->ToObject());
+
+ Nan::Callback *callback = new Nan::Callback(Local::Cast(info[3]));
+ RegisterWorker *worker = new RegisterWorker(baton, callback);
+
+ worker->SaveToPersistent("filter_name", info[0]->ToObject());
+ worker->SaveToPersistent("filter_priority", info[2]->ToObject());
+
+ AsyncLibgit2QueueWorker(worker);
+ return;
+}
+
+void GitFilterRegistry::RegisterWorker::Execute() {
+ giterr_clear();
+
+ {
+ LockMaster lockMaster(/*asyncAction: */true, baton->filter_name, baton->filter);
+ int result = git_filter_register(baton->filter_name, baton->filter, baton->filter_priority);
+ baton->error_code = result;
+
+ if (result != GIT_OK && giterr_last() != NULL) {
+ baton->error = git_error_dup(giterr_last());
+ }
+ }
+}
+
+void GitFilterRegistry::RegisterWorker::HandleOKCallback() {
+ if (baton->error_code == GIT_OK) {
+ v8::Local result = Nan::New(baton->error_code);
+ v8::Local argv[2] = {
+ Nan::Null(),
+ result
+ };
+ callback->Call(2, argv);
+ }
+ else if (baton->error) {
+ v8::Local err;
+ if (baton->error->message) {
+ err = Nan::Error(baton->error->message)->ToObject();
+ } else {
+ err = Nan::Error("Method register has thrown an error.")->ToObject();
+ }
+ err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
+ v8::Local argv[1] = {
+ err
+ };
+ callback->Call(1, argv);
+ if (baton->error->message)
+ free((void *)baton->error->message);
+ free((void *)baton->error);
+ }
+ else if (baton->error_code < 0) {
+ v8::Local err = Nan::Error("Method register has thrown an error.")->ToObject();
+ err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
+ v8::Local argv[1] = {
+ err
+ };
+ callback->Call(1, argv);
+ }
+ else {
+ callback->Call(0, NULL);
+ }
+ delete baton;
+ return;
+}
+
+NAN_METHOD(GitFilterRegistry::GitFilterUnregister) {
+ Nan::EscapableHandleScope scope;
+
+ if (info.Length() == 0 || !info[0]->IsString()) {
+ return Nan::ThrowError("String name is required.");
+ }
+
+ if (info.Length() == 1 || !info[1]->IsFunction()) {
+ return Nan::ThrowError("Callback is required and must be a Function.");
+ }
+
+ FilterUnregisterBaton *baton = new FilterUnregisterBaton;
+ String::Utf8Value name(info[0]->ToString());
+
+ baton->filter_name = (char *)malloc(name.length() + 1);
+ memcpy((void *)baton->filter_name, *name, name.length());
+ memset((void *)(((char *)baton->filter_name) + name.length()), 0, 1);
+
+ baton->error_code = GIT_OK;
+
+ /* Setting up Async Worker */
+ Nan::Callback *callback = new Nan::Callback(Local::Cast(info[1]));
+ UnregisterWorker *worker = new UnregisterWorker(baton, callback);
+
+ worker->SaveToPersistent("filter_name", info[0]);
+
+ AsyncLibgit2QueueWorker(worker);
+ return;
+}
+
+void GitFilterRegistry::UnregisterWorker::Execute() {
+ giterr_clear();
+
+ {
+ LockMaster lockMaster(/*asyncAction: */true, baton->filter_name);
+ int result = git_filter_unregister(baton->filter_name);
+ baton->error_code = result;
+
+ if (result != GIT_OK && giterr_last() != NULL) {
+ baton->error = git_error_dup(giterr_last());
+ }
+ }
+}
+
+void GitFilterRegistry::UnregisterWorker::HandleOKCallback() {
+ if (baton->error_code == GIT_OK) {
+ v8::Local result = Nan::New(baton->error_code);
+ v8::Local argv[2] = {
+ Nan::Null(),
+ result
+ };
+ callback->Call(2, argv);
+ }
+ else if (baton->error) {
+ v8::Local err;
+ if (baton->error->message) {
+ err = Nan::Error(baton->error->message)->ToObject();
+ } else {
+ err = Nan::Error("Method register has thrown an error.")->ToObject();
+ }
+ err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
+ v8::Local argv[1] = {
+ err
+ };
+ callback->Call(1, argv);
+ if (baton->error->message)
+ free((void *)baton->error->message);
+ free((void *)baton->error);
+ }
+ else if (baton->error_code < 0) {
+ v8::Local err = Nan::Error("Method unregister has thrown an error.")->ToObject();
+ err->Set(Nan::New("errno").ToLocalChecked(), Nan::New(baton->error_code));
+ v8::Local argv[1] = {
+ err
+ };
+ callback->Call(1, argv);
+ }
+ else {
+ callback->Call(0, NULL);
+ }
+ delete baton;
+ return;
+}
diff --git a/generate/templates/partials/async_function.cc b/generate/templates/partials/async_function.cc
index b8e19ddb0..20813136d 100644
--- a/generate/templates/partials/async_function.cc
+++ b/generate/templates/partials/async_function.cc
@@ -16,9 +16,13 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
{{ cppFunctionName }}_globalPayload* globalPayload = new {{ cppFunctionName }}_globalPayload;
{%endif%}
{%if arg.cppClassName == "GitBuf" %}
- baton->{{arg.name}} = ({{ arg.cType }})malloc(sizeof({{ arg.cType|replace '*' '' }}));;
- baton->{{arg.name}}->ptr = NULL;
- baton->{{arg.name}}->size = baton->{{arg.name}}->asize = 0;
+ {%if cppFunctionName == "Set"%}
+ baton->{{arg.name}} = Nan::ObjectWrap::Unwrap<{{ arg.cppClassName }}>(info.This())->GetValue();
+ {%else%}
+ baton->{{arg.name}} = ({{ arg.cType }})malloc(sizeof({{ arg.cType|replace '*' '' }}));
+ baton->{{arg.name}}->ptr = NULL;
+ baton->{{arg.name}}->size = baton->{{arg.name}}->asize = 0;
+ {%endif%}
{%endif%}
{%endeach%}
@@ -57,7 +61,10 @@ NAN_METHOD({{ cppClassName }}::{{ cppFunctionName }}) {
{%endif%}
{%endif%}
{%elsif arg.shouldAlloc %}
- baton->{{ arg.name }} = ({{ arg.cType }})malloc(sizeof({{ arg.cType|replace '*' '' }}));
+ {%if arg.cppClassName == "GitBuf" %}
+ {%else%}
+ baton->{{ arg.name }} = ({{ arg.cType }})malloc(sizeof({{ arg.cType|replace '*' '' }}));
+ {%endif%}
{%endif%}
{%endeach%}
@@ -273,8 +280,11 @@ void {{ cppClassName }}::{{ cppFunctionName }}Worker::HandleOKCallback() {
delete ({{ cppFunctionName}}_globalPayload*)baton->{{ arg.name }};
{%endif%}
{%if arg.cppClassName == "GitBuf" %}
- git_buf_free(baton->{{ arg.name }});
- free((void *)baton->{{ arg.name }});
+ {%if cppFunctionName == "Set" %}
+ {%else%}
+ git_buf_free(baton->{{ arg.name }});
+ free((void *)baton->{{ arg.name }});
+ {%endif%}
{%endif%}
{%endeach%}
diff --git a/generate/templates/partials/field_accessors.cc b/generate/templates/partials/field_accessors.cc
index ea5bb5db1..480adc76f 100644
--- a/generate/templates/partials/field_accessors.cc
+++ b/generate/templates/partials/field_accessors.cc
@@ -109,11 +109,20 @@
{% if field.isCallbackFunction %}
{{ cppClassName }}* {{ cppClassName }}::{{ field.name }}_getInstanceFromBaton({{ field.name|titleCase }}Baton* baton) {
- return static_cast<{{ cppClassName }}*>(baton->{% each field.args|argsInfo as arg %}
- {% if arg.payload == true %}{{arg.name}}{% elsif arg.lastArg %}{{arg.name}}{% endif %}
- {% endeach %});
+ {% if isExtendedStruct %}
+ return static_cast<{{ cppClassName }}*>((({{cType}}_extended *)baton->self)->payload);
+ {% else %}
+ return static_cast<{{ cppClassName }}*>(baton->
+ {% each field.args|argsInfo as arg %}
+ {% if arg.payload == true %}
+ {{arg.name}}
+ {% elsif arg.lastArg %}
+ {{arg.name}}
+ {% endif %}
+ {% endeach %});
+ {% endif %}
}
-
+
{{ field.return.type }} {{ cppClassName }}::{{ field.name }}_cppCallback (
{% each field.args|argsInfo as arg %}
{{ arg.cType }} {{ arg.name}}{% if not arg.lastArg %},{% endif %}
@@ -127,21 +136,34 @@
{% endeach %}
{{ cppClassName }}* instance = {{ field.name }}_getInstanceFromBaton(baton);
-
- {{ field.return.type }} result;
-
- if (instance->{{ field.name }}.WillBeThrottled()) {
- result = baton->defaultResult;
- delete baton;
- } else if (instance->{{ field.name }}.ShouldWaitForResult()) {
- result = baton->ExecuteAsync({{ field.name }}_async);
- delete baton;
- } else {
- result = baton->defaultResult;
- baton->ExecuteAsync({{ field.name }}_async, deleteBaton);
- }
- return result;
+
+ {% if field.return.type == "void" %}
+ if (instance->{{ field.name }}.WillBeThrottled()) {
+ delete baton;
+ } else if (instance->{{ field.name }}.ShouldWaitForResult()) {
+ baton->ExecuteAsync({{ field.name }}_async);
+ delete baton;
+ } else {
+ baton->ExecuteAsync({{ field.name }}_async, deleteBaton);
+ }
+ return;
+ {% else %}
+ {{ field.return.type }} result;
+
+ if (instance->{{ field.name }}.WillBeThrottled()) {
+ result = baton->defaultResult;
+ delete baton;
+ } else if (instance->{{ field.name }}.ShouldWaitForResult()) {
+ result = baton->ExecuteAsync({{ field.name }}_async);
+ delete baton;
+ } else {
+ result = baton->defaultResult;
+ baton->ExecuteAsync({{ field.name }}_async, deleteBaton);
+ }
+ return result;
+ {% endif %}
}
+
void {{ cppClassName }}::{{ field.name }}_async(void *untypedBaton) {
Nan::HandleScope scope;
@@ -153,7 +175,6 @@
{% if field.return.type == "int" %}
baton->result = baton->defaultResult; // no results acquired
{% endif %}
-
baton->Done();
return;
}
@@ -163,15 +184,53 @@
{%-- Do nothing --%}
{% elsif arg.isJsArg %}
{% if arg.cType == "const char *" %}
- if (baton->{{ arg.name }} == NULL) {
- baton->{{ arg.name }} = "";
- }
+ if (baton->{{ arg.name }} == NULL) {
+ baton->{{ arg.name }} = "";
+ }
+ {% elsif arg.cppClassName == "String" %}
+ v8::Local src;
+ if (baton->{{ arg.name }} == NULL) {
+ src = Nan::Null();
+ }
+ else {
+ src = Nan::New(*baton->{{ arg.name }}).ToLocalChecked();
+ }
{% endif %}
{% endif %}
{% endeach %}
- v8::Local argv[{{ field.args|jsArgsCount }}] = {
- {% each field.args|argsInfo as arg %}
+ {% if field.isSelfReferential %}
+ {% if field.args|jsArgsCount|subtract 2| setUnsigned == 0 %}
+ v8::Local *argv = NULL;
+ {% else %}
+ v8::Local argv[{{ field.args|jsArgsCount|subtract 2| setUnsigned }}] = {
+ {% endif %}
+ {% else %}
+ v8::Local argv[{{ field.args|jsArgsCount }}] = {
+ {% endif %}
+ {% each field.args|argsInfo as arg %}
+ {% if field.isSelfReferential %}
+ {% if not arg.firstArg %}
+ {% if field.args|jsArgsCount|subtract 1|or 0 %}
+ {% if arg.cppClassName == "String" %}
+ {%-- src is always the last arg --%}
+ src
+ {% elsif arg.isJsArg %}
+ {% if arg.isEnum %}
+ Nan::New((int)baton->{{ arg.name }}),
+ {% elsif arg.isLibgitType %}
+ {{ arg.cppClassName }}::New(baton->{{ arg.name }}, false),
+ {% elsif arg.cType == "size_t" %}
+ Nan::New((unsigned int)baton->{{ arg.name }}),
+ {% elsif arg.name == "payload" %}
+ {%-- skip, filters should not have a payload --%}
+ {% else %}
+ Nan::New(baton->{{ arg.name }}),
+ {% endif %}
+ {% endif %}
+ {% endif %}
+ {% endif %}
+ {% else %}
{% if arg.name == "payload" %}
{%-- payload is always the last arg --%}
Nan::New(instance->{{ fields|payloadFor field.name }})
@@ -183,55 +242,35 @@
{% elsif arg.cType == "size_t" %}
// HACK: NAN should really have an overload for Nan::New to support size_t
Nan::New((unsigned int)baton->{{ arg.name }}),
- {% elsif arg.cppClassName == 'String' %}
+ {% elsif arg.cppClassName == "String" %}
Nan::New(baton->{{ arg.name }}).ToLocalChecked(),
{% else %}
Nan::New(baton->{{ arg.name }}),
{% endif %}
{% endif %}
- {% endeach %}
- };
+ {% endif %}
+ {% endeach %}
+ {% if not field.isSelfReferential %}
+ };
+ {% elsif field.args|jsArgsCount|subtract 2| setUnsigned > 0 %}
+ };
+ {% endif %}
Nan::TryCatch tryCatch;
- v8::Local result = instance->{{ field.name }}.GetCallback()->Call({{ field.args|jsArgsCount }}, argv);
+
+ {% if field.isSelfReferential %}
+ v8::Local result = instance->{{ field.name }}.GetCallback()->Call({{ field.args|jsArgsCount|subtract 2| setUnsigned }}, argv);
+ {% else %}
+ v8::Local result = instance->{{ field.name }}.GetCallback()->Call({{ field.args|jsArgsCount }}, argv);
+ {% endif %}
if(PromiseCompletion::ForwardIfPromise(result, baton, {{ cppClassName }}::{{ field.name }}_promiseCompleted)) {
return;
}
-
- {% each field|returnsInfo false true as _return %}
- if (result.IsEmpty() || result->IsNativeError()) {
- baton->result = {{ field.return.error }};
- }
- else if (!result->IsNull() && !result->IsUndefined()) {
- {% if _return.isOutParam %}
- {{ _return.cppClassName }}* wrapper = Nan::ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
- wrapper->selfFreeing = false;
-
- *baton->{{ _return.name }} = wrapper->GetValue();
- baton->result = {{ field.return.success }};
- {% else %}
- if (result->IsNumber()) {
- baton->result = (int)result->ToNumber()->Value();
- }
- else {
- baton->result = baton->defaultResult;
- }
- {% endif %}
- }
- else {
- baton->result = baton->defaultResult;
- }
- {% endeach %}
- baton->Done();
- }
-
- void {{ cppClassName }}::{{ field.name }}_promiseCompleted(bool isFulfilled, AsyncBaton *_baton, v8::Local result) {
- Nan::HandleScope scope;
-
- {{ field.name|titleCase }}Baton* baton = static_cast<{{ field.name|titleCase }}Baton*>(_baton);
-
- if (isFulfilled) {
+
+ {% if field.return.type == "void" %}
+ baton->Done();
+ {% else %}
{% each field|returnsInfo false true as _return %}
if (result.IsEmpty() || result->IsNativeError()) {
baton->result = {{ field.return.error }};
@@ -247,7 +286,7 @@
if (result->IsNumber()) {
baton->result = (int)result->ToNumber()->Value();
}
- else{
+ else {
baton->result = baton->defaultResult;
}
{% endif %}
@@ -256,18 +295,59 @@
baton->result = baton->defaultResult;
}
{% endeach %}
- }
- else {
- // promise was rejected
- {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->{% each field.args|argsInfo as arg %}
- {% if arg.payload == true %}{{arg.name}}{% elsif arg.lastArg %}{{arg.name}}{% endif %}
- {% endeach %});
- v8::Local parent = instance->handle();
- SetPrivate(parent, Nan::New("NodeGitPromiseError").ToLocalChecked(), result);
+ baton->Done();
+ {% endif %}
+ }
- baton->result = {{ field.return.error }};
- }
- baton->Done();
+ void {{ cppClassName }}::{{ field.name }}_promiseCompleted(bool isFulfilled, AsyncBaton *_baton, v8::Local result) {
+ Nan::HandleScope scope;
+
+ {{ field.name|titleCase }}Baton* baton = static_cast<{{ field.name|titleCase }}Baton*>(_baton);
+ {% if field.return.type == "void" %}
+ baton->Done();
+ {% else %}
+ if (isFulfilled) {
+ {% each field|returnsInfo false true as _return %}
+ if (result.IsEmpty() || result->IsNativeError()) {
+ baton->result = {{ field.return.error }};
+ }
+ else if (!result->IsNull() && !result->IsUndefined()) {
+ {% if _return.isOutParam %}
+ {{ _return.cppClassName }}* wrapper = Nan::ObjectWrap::Unwrap<{{ _return.cppClassName }}>(result->ToObject());
+ wrapper->selfFreeing = false;
+
+ *baton->{{ _return.name }} = wrapper->GetValue();
+ baton->result = {{ field.return.success }};
+ {% else %}
+ if (result->IsNumber()) {
+ baton->result = (int)result->ToNumber()->Value();
+ }
+ else{
+ baton->result = baton->defaultResult;
+ }
+ {% endif %}
+ }
+ else {
+ baton->result = baton->defaultResult;
+ }
+ {% endeach %}
+ }
+ else {
+ // promise was rejected
+ {% if isExtendedStruct %}
+ {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>((({{cType}}_extended *)baton->self)->payload);
+ {% else %}
+ {{ cppClassName }}* instance = static_cast<{{ cppClassName }}*>(baton->{% each field.args|argsInfo as arg %}
+ {% if arg.payload == true %}{{arg.name}}{% elsif arg.lastArg %}{{arg.name}}{% endif %}
+ {% endeach %});
+ {% endif %}
+ v8::Local parent = instance->handle();
+ SetPrivate(parent, Nan::New("NodeGitPromiseError").ToLocalChecked(), result);
+
+ baton->result = {{ field.return.error }};
+ }
+ baton->Done();
+ {% endif %}
}
{% endif %}
{% endif %}
diff --git a/generate/templates/templates/binding.gyp b/generate/templates/templates/binding.gyp
index 69e2042ad..1be9be490 100644
--- a/generate/templates/templates/binding.gyp
+++ b/generate/templates/templates/binding.gyp
@@ -10,7 +10,6 @@
"variables": {
"coverage%": 0
},
-
"sources": [
"src/async_baton.cc",
"src/lock_master.cc",
@@ -22,6 +21,7 @@
"src/functions/free.cc",
"src/convenient_patch.cc",
"src/convenient_hunk.cc",
+ "src/filter_registry.cc",
"src/str_array_converter.cc",
"src/thread_pool.cc",
{% each %}
diff --git a/generate/templates/templates/nodegit.cc b/generate/templates/templates/nodegit.cc
index 9f392cabc..e81bbb85f 100644
--- a/generate/templates/templates/nodegit.cc
+++ b/generate/templates/templates/nodegit.cc
@@ -21,6 +21,7 @@
{% endeach %}
#include "../include/convenient_patch.h"
#include "../include/convenient_hunk.h"
+#include "../include/filter_registry.h"
#if (NODE_MODULE_VERSION > 48)
v8::Local GetPrivate(v8::Local object,
@@ -140,6 +141,7 @@ extern "C" void init(v8::Local target) {
ConvenientHunk::InitializeComponent(target);
ConvenientPatch::InitializeComponent(target);
+ GitFilterRegistry::InitializeComponent(target);
NODE_SET_METHOD(target, "enableThreadSafety", LockMasterEnable);
NODE_SET_METHOD(target, "setThreadSafetyStatus", LockMasterSetStatus);
diff --git a/generate/templates/templates/nodegit.js b/generate/templates/templates/nodegit.js
index 84bc558b8..246f5b26b 100644
--- a/generate/templates/templates/nodegit.js
+++ b/generate/templates/templates/nodegit.js
@@ -58,6 +58,14 @@ _ConvenientPatch.prototype.hunks = promisify(_ConvenientPatch_hunks);
var _ConvenientHunk = rawApi.ConvenientHunk;
var _ConvenientHunk_lines = _ConvenientHunk.prototype.lines;
_ConvenientHunk.prototype.lines = promisify(_ConvenientHunk_lines);
+
+var _FilterRegistry = rawApi.FilterRegistry;
+var _FilterRegistry_register = _FilterRegistry.register;
+_FilterRegistry.register = promisify(_FilterRegistry_register);
+
+var _FilterRegistry_unregister = _FilterRegistry.unregister;
+_FilterRegistry.unregister = promisify(_FilterRegistry_unregister);
+
/* jshint ignore:end */
// Set the exports prototype to the raw API.
@@ -86,6 +94,8 @@ require("./status_file");
require("./enums.js");
// Import extensions
+// [Manual] extensions
+importExtension("filter_registry");
{% each %}
{% if type != "enum" %}
importExtension("{{ filename }}");
diff --git a/generate/templates/templates/struct_content.cc b/generate/templates/templates/struct_content.cc
index c6f9e5b91..9840cfe4f 100644
--- a/generate/templates/templates/struct_content.cc
+++ b/generate/templates/templates/struct_content.cc
@@ -35,9 +35,15 @@ using namespace std;
{% if ignoreInit == true %}
this->raw = new {{ cType }};
{% else %}
- {{ cType }} wrappedValue = {{ cType|upper }}_INIT;
- this->raw = ({{ cType }}*) malloc(sizeof({{ cType }}));
- memcpy(this->raw, &wrappedValue, sizeof({{ cType }}));
+ {% if isExtendedStruct %}
+ {{ cType }}_extended wrappedValue = {{ cType|upper }}_INIT;
+ this->raw = ({{ cType }}*) malloc(sizeof({{ cType }}_extended));
+ memcpy(this->raw, &wrappedValue, sizeof({{ cType }}_extended));
+ {% else %}
+ {{ cType }} wrappedValue = {{ cType|upper }}_INIT;
+ this->raw = ({{ cType }}*) malloc(sizeof({{ cType }}));
+ memcpy(this->raw, &wrappedValue, sizeof({{ cType }}));
+ {% endif %}
{% endif %}
this->ConstructFields();
@@ -54,9 +60,13 @@ using namespace std;
{% if not field.ignore %}
{% if not field.isEnum %}
{% if field.isCallbackFunction %}
- if (this->{{ field.name }}.HasCallback()) {
- this->raw->{{ fields|payloadFor field.name }} = NULL;
- }
+ if (this->{{ field.name }}.HasCallback()) {
+ {% if isExtendedStruct %}
+ (({{ cType }}_extended *)this->raw)->payload = NULL;
+ {% else %}
+ this->raw->{{ fields|payloadFor field.name }} = NULL;
+ {% endif %}
+ }
{% endif %}
{% endif %}
{% endif %}
@@ -79,7 +89,11 @@ void {{ cppClassName }}::ConstructFields() {
// Set the static method call and set the payload for this function to be
// the current instance
this->raw->{{ field.name }} = NULL;
- this->raw->{{ fields|payloadFor field.name }} = (void *)this;
+ {% if isExtendedStruct %}
+ (({{ cType }}_extended *)this->raw)->payload = (void *)this;
+ {% else %}
+ this->raw->{{ fields|payloadFor field.name }} = (void *)this;
+ {% endif %}
{% elsif field.payloadFor %}
v8::Local {{ field.name }} = Nan::Undefined();
diff --git a/generate/templates/templates/struct_header.h b/generate/templates/templates/struct_header.h
index 94fd3bc0f..4d2ec2af5 100644
--- a/generate/templates/templates/struct_header.h
+++ b/generate/templates/templates/struct_header.h
@@ -24,7 +24,12 @@ using namespace node;
using namespace v8;
{%partial traits .%}
-
+{% if isExtendedStruct %}
+struct {{ cType }}_extended {
+ {{ cType }} raw;
+ void* payload;
+};
+{% endif %}
class {{ cppClassName }} : public NodeGitWrapper<{{ cppClassName }}Traits> {
// grant full access to base class
friend class NodeGitWrapper<{{ cppClassName }}Traits>;
@@ -46,15 +51,27 @@ class {{ cppClassName }} : public NodeGitWrapper<{{ cppClassName }}Traits> {
static void {{ field.name }}_async(void *baton);
static void {{ field.name }}_promiseCompleted(bool isFulfilled, AsyncBaton *_baton, v8::Local result);
- struct {{ field.name|titleCase }}Baton : public AsyncBatonWithResult<{{ field.return.type }}> {
- {% each field.args|argsInfo as arg %}
- {{ arg.cType }} {{ arg.name}};
- {% endeach %}
+ {% if field.return.type == 'void' %}
+ struct {{ field.name|titleCase }}Baton : public AsyncBatonWithNoResult {
+ {% each field.args|argsInfo as arg %}
+ {{ arg.cType }} {{ arg.name }};
+ {% endeach %}
+
+ {{ field.name|titleCase }}Baton()
+ : AsyncBatonWithNoResult() {
+ }
+ };
+ {% else %}
+ struct {{ field.name|titleCase }}Baton : public AsyncBatonWithResult<{{ field.return.type }}> {
+ {% each field.args|argsInfo as arg %}
+ {{ arg.cType }} {{ arg.name }};
+ {% endeach %}
- {{ field.name|titleCase }}Baton(const {{ field.return.type }} &defaultResult)
- : AsyncBatonWithResult<{{ field.return.type }}>(defaultResult) {
- }
- };
+ {{ field.name|titleCase }}Baton(const {{ field.return.type }} &defaultResult)
+ : AsyncBatonWithResult<{{ field.return.type }}>(defaultResult) {
+ }
+ };
+ {% endif %}
static {{ cppClassName }} * {{ field.name }}_getInstanceFromBaton (
{{ field.name|titleCase }}Baton *baton);
{% endif %}
diff --git a/lib/branch.js b/lib/branch.js
new file mode 100644
index 000000000..bc4b23443
--- /dev/null
+++ b/lib/branch.js
@@ -0,0 +1,19 @@
+var NodeGit = require("../");
+var Branch = NodeGit.Branch;
+
+var _remoteName = Branch.remoteName;
+
+/**
+ * Retrieve the Branch's Remote Name as a String.
+ *
+ * @async
+ * @param {Repository} repo The repo to get the remote name from
+ * @param {String} the refname of the branch
+ * @return {String} remote name as a string.
+ */
+Branch.remoteName = function(repo, remoteRef) {
+ return _remoteName.call(this, repo, remoteRef)
+ .then(function(remoteNameBuffer) {
+ return remoteNameBuffer.toString();
+ });
+};
diff --git a/lib/filter_registry.js b/lib/filter_registry.js
new file mode 100644
index 000000000..76dfba573
--- /dev/null
+++ b/lib/filter_registry.js
@@ -0,0 +1,42 @@
+var NodeGit = require("../");
+var normalizeOptions = NodeGit.Utils.normalizeOptions;
+
+var FilterRegistry = NodeGit.FilterRegistry;
+
+var _register = FilterRegistry.register;
+var _unregister = FilterRegistry.unregister;
+
+// register should add filter by name to dict and return
+// Override FilterRegistry.register to normalize Filter
+FilterRegistry.register = function(name, filter, priority, callback) {
+ // setting default value of attributes
+ if (filter.attributes === undefined) {
+ filter.attributes = "";
+ }
+
+ filter = normalizeOptions(filter, NodeGit.Filter);
+
+ if (!filter.check || !filter.apply) {
+ return callback(new Error(
+ "ERROR: please provide check and apply callbacks for filter"
+ ));
+ }
+
+ return _register(name, filter, priority)
+ .then(function(result) {
+ if (typeof callback === "function") {
+ callback(null, result);
+ }
+ return result;
+ }, callback);
+};
+
+FilterRegistry.unregister = function(name, callback) {
+ return _unregister(name)
+ .then(function(result) {
+ if (typeof callback === "function") {
+ callback(null, result);
+ }
+ return result;
+ }, callback);
+};
diff --git a/lib/rebase.js b/lib/rebase.js
index 22ca72c7d..28882ea6c 100644
--- a/lib/rebase.js
+++ b/lib/rebase.js
@@ -5,6 +5,8 @@ var shallowClone = NodeGit.Utils.shallowClone;
var _init = Rebase.init;
var _open = Rebase.open;
+var _abort = Rebase.prototype.abort;
+var _commit = Rebase.prototype.commit;
/**
* Initializes a rebase
* @async
@@ -83,3 +85,12 @@ Rebase.open = function(repository, options) {
);
return _open(repository, options);
};
+
+Rebase.prototype.commit = function(author, committer, encoding, message) {
+ return _commit.call(this, author, committer, encoding, message);
+};
+
+Rebase.prototype.abort = function() {
+ return _abort.call(this);
+};
+
diff --git a/lib/repository.js b/lib/repository.js
index 90be1d91c..33adcc498 100644
--- a/lib/repository.js
+++ b/lib/repository.js
@@ -150,15 +150,15 @@ function getPathHunks(repo, index, filePath, isStaged, additionalDiffOptions) {
});
})
.then(function(diff) {
- if (!(NodeGit.Status.file(repo, filePath) &
- NodeGit.Status.STATUS.WT_MODIFIED) &&
- !(NodeGit.Status.file(repo, filePath) &
- NodeGit.Status.STATUS.INDEX_MODIFIED)) {
- return Promise.reject
- ("Selected staging is only available on modified files.");
- }
-
- return diff.patches();
+ return NodeGit.Status.file(repo, filePath)
+ .then(function(status) {
+ if (!(status & NodeGit.Status.STATUS.WT_MODIFIED) &&
+ !(status & NodeGit.Status.STATUS.INDEX_MODIFIED)) {
+ return Promise.reject
+ ("Selected staging is only available on modified files.");
+ }
+ return diff.patches();
+ });
})
.then(function(patches) {
var pathPatch = patches.filter(function(patch) {
@@ -217,23 +217,26 @@ function performRebase(
function getPromise() {
return rebase.next()
.then(function() {
- return repository.refreshIndex()
- .then(function(index) {
- if (index.hasConflicts()) {
- throw index;
- }
+ return repository.refreshIndex();
+ })
+ .then(function(index) {
+ if (index.hasConflicts()) {
+ throw index;
+ }
- rebase.commit(null, signature);
+ return rebase.commit(null, signature);
+ })
+ .then(function() {
- return performRebase(
- repository,
- rebase,
- signature,
- beforeNextFn,
- beforeFinishFn
- );
- });
- }, function(error) {
+ return performRebase(
+ repository,
+ rebase,
+ signature,
+ beforeNextFn,
+ beforeFinishFn
+ );
+ })
+ .catch(function(error) {
if (error && error.errno === NodeGit.Error.CODE.ITEROVER) {
const calcRewritten = fp.flow([
fp.split("\n"),
@@ -435,6 +438,7 @@ Repository.prototype.continueRebase = function(
signature = signature || repo.defaultSignature();
+ var rebase;
return repo.refreshIndex()
.then(function(index) {
if (index.hasConflicts()) {
@@ -443,9 +447,17 @@ Repository.prototype.continueRebase = function(
return NodeGit.Rebase.open(repo);
})
- .then(function(rebase) {
- rebase.commit(null, signature);
-
+ .then(function(_rebase) {
+ rebase = _rebase;
+ return rebase.commit(null, signature)
+ .catch(function() {
+ // Ignore all errors to prevent
+ // this routine from choking now
+ // that we made rebase.commit
+ // asynchronous
+ });
+ })
+ .then(function() {
return performRebase(
repo,
rebase,
@@ -499,8 +511,8 @@ Repository.prototype.createBranch = function(name, commit, force) {
* @param {Buffer} buffer
* @return {Oid}
*/
-Repository.prototype.createBlobFromBuffer = function(buffer, callback) {
- return Blob.createFromBuffer(this, buffer, buffer.length, callback);
+Repository.prototype.createBlobFromBuffer = function(buffer) {
+ return Blob.createFromBuffer(this, buffer, buffer.length);
};
/**
@@ -1636,17 +1648,36 @@ Repository.prototype.stageFilemode =
})
.then(function(diff) {
var origLength = filePaths.length;
- filePaths = filePaths.filter(function(p) {
- return (
- (NodeGit.Status.file(repo, p) & NodeGit.Status.STATUS.WT_MODIFIED) ||
- (NodeGit.Status.file(repo, p) & NodeGit.Status.STATUS.INDEX_MODIFIED)
- );
- });
- if (filePaths.length === 0 && origLength > 0) {
- return Promise.reject
- ("Selected staging is only available on modified files.");
- }
- return diff.patches();
+ var fileFilterPromises = fp.map(function(p) {
+ return NodeGit.Status.file(repo, p)
+ .then(function(status) {
+ return {
+ path: p,
+ filter: (
+ (status & NodeGit.Status.STATUS.WT_MODIFIED) ||
+ (status & NodeGit.Status.STATUS.INDEX_MODIFIED)
+ )
+ };
+ });
+ }, filePaths);
+
+ return Promise.all(fileFilterPromises)
+ .then(function(results) {
+ filePaths = fp.flow([
+ fp.filter(function(filterResult) {
+ return filterResult.filter;
+ }),
+ fp.map(function(filterResult) {
+ return filterResult.path;
+ })
+ ])(results);
+
+ if (filePaths.length === 0 && origLength > 0) {
+ return Promise.reject
+ ("Selected staging is only available on modified files.");
+ }
+ return diff.patches();
+ });
})
.then(function(patches) {
var pathPatches = patches.filter(function(patch) {
@@ -1753,9 +1784,9 @@ Repository.prototype.stageLines =
.then(function(newContent) {
var newContentBuffer = new Buffer(newContent);
- var newOid = repo.createBlobFromBuffer(newContentBuffer);
- return repo.getBlob(newOid);
+ return repo.createBlobFromBuffer(newContentBuffer);
})
+ .then(newOid => repo.getBlob(newOid))
.then(function(newBlob) {
var entry = index.getByPath(filePath, 0);
entry.id = newBlob.id();
diff --git a/package.json b/package.json
index 5becdd1c1..268d00f86 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "nodegit",
"description": "Node.js libgit2 asynchronous native bindings",
- "version": "0.19.0",
+ "version": "0.20.0",
"homepage": "http://nodegit.org",
"keywords": [
"libgit2",
diff --git a/test/tests/blob.js b/test/tests/blob.js
index 342cb2578..dce4ce619 100644
--- a/test/tests/blob.js
+++ b/test/tests/blob.js
@@ -1,16 +1,81 @@
var assert = require("assert");
var path = require("path");
var local = path.join.bind(path, __dirname);
+var promisify = require("promisify-node");
+var fse = promisify("fs-extra");
+var exec = require("../../utils/execPromise");
describe("Blob", function() {
var NodeGit = require("../../");
var Oid = NodeGit.Oid;
var Repository = NodeGit.Repository;
+ var Blob = NodeGit.Blob;
var FileMode = NodeGit.TreeEntry.FILEMODE;
var reposPath = local("../repos/workdir");
var oid = "111dd657329797f6165f52f5085f61ac976dcf04";
+ var previousCommitOid = "";
+
+ function commitFile(repo, fileName, fileContent, commitMessage) {
+ var index;
+ var treeOid;
+ var parent;
+
+ return fse.writeFile(path.join(repo.workdir(), fileName), fileContent)
+ .then(function() {
+ return repo.refreshIndex();
+ })
+ .then(function(indexResult) {
+ index = indexResult;
+ })
+ .then(function() {
+ return index.addByPath(fileName);
+ })
+ .then(function() {
+ return index.write();
+ })
+ .then(function() {
+ return index.writeTree();
+ })
+ .then(function(oidResult) {
+ treeOid = oidResult;
+ return NodeGit.Reference.nameToId(repo, "HEAD");
+ })
+ .then(function(head) {
+ return repo.getCommit(head);
+ })
+ .then(function(parentResult) {
+ parent = parentResult;
+ return Promise.all([
+ NodeGit.Signature.create("Foo Bar", "foo@bar.com", 123456789, 60),
+ NodeGit.Signature.create("Foo A Bar", "foo@bar.com", 987654321, 90)
+ ]);
+ })
+ .then(function(signatures) {
+ var author = signatures[0];
+ var committer = signatures[1];
+
+ return repo.createCommit(
+ "HEAD",
+ author,
+ committer,
+ commitMessage,
+ treeOid,
+ [parent]
+ );
+ });
+ }
+
+ before(function() {
+ return Repository.open(reposPath)
+ .then(function(repository) {
+ return repository.getHeadCommit();
+ })
+ .then(function(commit) {
+ previousCommitOid = commit.id();
+ });
+ });
beforeEach(function() {
var test = this;
@@ -26,6 +91,16 @@ describe("Blob", function() {
});
});
+ after(function() {
+ return exec("git clean -xdf", {cwd: reposPath})
+ .then(function() {
+ return exec("git checkout master", {cwd: reposPath});
+ })
+ .then(function() {
+ return exec("git reset --hard " + previousCommitOid, {cwd: reposPath});
+ });
+ });
+
it("can provide content as a buffer", function() {
var contents = this.blob.content();
@@ -50,4 +125,359 @@ describe("Blob", function() {
assert.equal(blob.id().toString(), oid);
});
});
+
+ describe("createFromBuffer", function() {
+ it("creates a new blob from the buffer", function() {
+ var content = "This is a new buffer";
+ var buf = new Buffer(content, content.length);
+ var test = this;
+
+ return Blob.createFromBuffer(test.repository, buf, content.length)
+ .then(function(oid) {
+ return test.repository.getBlob(oid);
+ })
+ .then(function(newBlob) {
+ assert.equal(newBlob.toString(), content);
+ });
+ });
+
+ it("creates blob with content equal to length", function() {
+ var content = "This is a new buffer";
+ var buf = new Buffer(content, content.length);
+ var test = this;
+
+ return Blob.createFromBuffer(test.repository, buf, 2)
+ .then(function(oid) {
+ return test.repository.getBlob(oid);
+ })
+ .then(function(newBlob) {
+ assert.equal(newBlob.toString(), "Th");
+ });
+ });
+
+ it("throws an error when repository is null", function() {
+ return Blob.createFromBuffer(null, null, 0)
+ .catch(function(error) {
+ assert.strictEqual(error.message, "Repository repo is required.");
+ });
+ });
+
+ it("throws an error when buffer is null", function() {
+ var test = this;
+ return Blob.createFromBuffer(test.repository, null)
+ .catch(function(error) {
+ assert.strictEqual(error.message, "Buffer buffer is required.");
+ });
+ });
+
+ it("throws an error when no length is provided", function() {
+ var test = this;
+ return Blob.createFromBuffer(test.repository, new Buffer("testing"))
+ .catch(function(error) {
+ assert.strictEqual(error.message, "Number len is required.");
+ });
+ });
+ });
+
+ describe("createFromDisk", function() {
+ var fileName = path.join(reposPath, "testFile.zzz");
+ var fileContent = "this is my file content";
+
+ beforeEach(function() {
+ return fse.writeFile(fileName, fileContent);
+ });
+
+ afterEach(function() {
+ return fse.unlink(fileName);
+ });
+
+ it("creates a new blob from the file", function() {
+ var test = this;
+
+ return Blob.createFromDisk(test.repository, fileName)
+ .then(function(oid) {
+ return test.repository.getBlob(oid);
+ })
+ .then(function(newBlob) {
+ assert.equal(newBlob.toString(), fileContent);
+ });
+ });
+
+ it("throws an error when the file cannot be found", function() {
+ var test = this;
+
+ return Blob.createFromDisk(test.repository, "aaaaaaaaaa")
+ .catch(function(error) {
+ assert.equal(error.errno, -3);
+ });
+ });
+
+ it("throws an error when repository is null", function() {
+ return Blob.createFromDisk(null, null, 0)
+ .catch(function(error) {
+ assert.strictEqual(error.message, "Repository repo is required.");
+ });
+ });
+
+ it("throws an error when path is null", function() {
+ var test = this;
+ return Blob.createFromDisk(test.repository, null)
+ .catch(function(error) {
+ assert.strictEqual(error.message, "String path is required.");
+ });
+ });
+ });
+
+ describe("createFromWorkdir", function() {
+ it("creates a blob from the file", function() {
+ var fileName = "package.json";
+ var filePath = path.join(reposPath, "package.json");
+ var test = this;
+
+ return fse.readFile(filePath)
+ .then(function(content) {
+ test.content = content.toString();
+ return Blob.createFromWorkdir(test.repository, fileName);
+ })
+ .then(function(oid) {
+ return test.repository.getBlob(oid);
+ })
+ .then(function(newBlob) {
+ assert.equal(newBlob.toString(), test.content);
+ });
+ });
+
+ it("throws an error when the file cannot be found", function() {
+ var test = this;
+
+ return Blob.createFromWorkdir(test.repository, "thisisabadfile.jpg")
+ .catch(function(error) {
+ assert.equal(error.errno, -3);
+ });
+ });
+
+ it("throws an error when repository is null", function() {
+ return Blob.createFromWorkdir(null, null, 0)
+ .catch(function(error) {
+ assert.strictEqual(error.message, "Repository repo is required.");
+ });
+ });
+
+ it("throws an error when path is null", function() {
+ var test = this;
+ return Blob.createFromWorkdir(test.repository, null)
+ .catch(function(error) {
+ assert
+ .strictEqual(error.message, "String relative_path is required.");
+ });
+ });
+ });
+
+ describe("filteredContent", function() {
+ var attrFileName = ".gitattributes";
+ var filter = "* text eol=crlf";
+ var lineEndingRegex = /\r\n|\r|\n/;
+ var newFileName = "testfile.test";
+
+ it("retrieves the filtered content", function() {
+ var test = this;
+
+ return commitFile(
+ test.repository,
+ attrFileName,
+ filter,
+ "added gitattributes")
+ .then(function() {
+ return commitFile(
+ test.repository,
+ newFileName,
+ "this\nis\nfun\guys",
+ "added LF ending file"
+ );
+ })
+ .then(function(oid) {
+ return test.repository.getCommit(oid);
+ })
+ .then(function(commit) {
+ test.filteredCommit = commit;
+ return commit.getEntry(newFileName);
+ })
+ .then(function(entry) {
+ return entry.getBlob();
+ })
+ .then(function(lfBlob) {
+ test.lfBlob = lfBlob;
+ var ending = test.lfBlob.toString().match(lineEndingRegex);
+ assert.strictEqual(ending[0], "\n");
+
+ return Blob.filteredContent(
+ test.lfBlob,
+ newFileName,
+ 0
+ );
+ })
+ .then(function(content) {
+ var ending = content.match(lineEndingRegex);
+ assert.strictEqual(ending[0], "\r\n");
+ assert.notStrictEqual(content, test.blob.toString());
+ });
+ });
+
+ it("returns non-binary filtered content when checking binary", function() {
+ var test = this;
+
+ return commitFile(
+ test.repository,
+ attrFileName,
+ filter,
+ "added gitattributes")
+ .then(function() {
+ return commitFile(
+ test.repository,
+ newFileName,
+ "this\nis\nfun\guys",
+ "added LF ending file"
+ );
+ })
+ .then(function(oid) {
+ return test.repository.getCommit(oid);
+ })
+ .then(function(commit) {
+ test.filteredCommit = commit;
+ return commit.getEntry(newFileName);
+ })
+ .then(function(entry) {
+ return entry.getBlob();
+ })
+ .then(function(lfBlob) {
+ test.lfBlob = lfBlob;
+ var ending = test.lfBlob.toString().match(lineEndingRegex);
+ assert.strictEqual(ending[0], "\n");
+
+ return Blob.filteredContent(
+ test.lfBlob,
+ newFileName,
+ 1
+ );
+ })
+ .then(function(content) {
+ var ending = content.match(lineEndingRegex);
+ assert.strictEqual(ending[0], "\r\n");
+ assert.notStrictEqual(content, test.blob.toString());
+ });
+ });
+
+ it("returns nothing when checking binary blob", function() {
+ var test = this;
+ var binary = new Buffer(new Uint8Array([1,2,3,4,5,6]));
+
+ return commitFile(
+ test.repository,
+ attrFileName,
+ filter,
+ "added gitattributes")
+ .then(function() {
+ return commitFile(
+ test.repository,
+ newFileName,
+ binary,
+ "binary content"
+ );
+ })
+ .then(function(oid) {
+ return test.repository.getCommit(oid);
+ })
+ .then(function(commit) {
+ test.filteredCommit = commit;
+ return commit.getEntry(newFileName);
+ })
+ .then(function(entry) {
+ return entry.getBlob();
+ })
+ .then(function(binaryBlob) {
+ test.binaryBlob = binaryBlob;
+ assert.equal(true, binaryBlob.isBinary());
+
+ return Blob.filteredContent(
+ test.binaryBlob,
+ newFileName,
+ 1
+ );
+ })
+ .then(function(content) {
+ assert.strictEqual(content, "");
+ });
+ });
+
+ it("returns blob when not checking binary on binary blob", function() {
+ var test = this;
+ var binary = new Buffer(new Uint8Array([1,2,3,4,5,6]));
+
+ return commitFile(
+ test.repository,
+ attrFileName,
+ filter,
+ "added gitattributes")
+ .then(function() {
+ return commitFile(
+ test.repository,
+ newFileName,
+ binary,
+ "binary content"
+ );
+ })
+ .then(function(oid) {
+ return test.repository.getCommit(oid);
+ })
+ .then(function(commit) {
+ test.filteredCommit = commit;
+ return commit.getEntry(newFileName);
+ })
+ .then(function(entry) {
+ return entry.getBlob();
+ })
+ .then(function(binaryBlob) {
+ test.binaryBlob = binaryBlob;
+ assert.equal(true, binaryBlob.isBinary());
+
+ return Blob.filteredContent(
+ test.binaryBlob,
+ newFileName,
+ 0
+ );
+ })
+ .then(function(content) {
+ assert.strictEqual(content, binary.toString());
+ });
+ });
+
+ it("throws an error when the blob is null", function() {
+ return Blob.filteredContent(null, "", 0)
+ .catch(function(err) {
+ assert.strictEqual(
+ err.message,
+ "Blob blob is required."
+ );
+ });
+ });
+
+ it("throws an error when the path is null", function() {
+ var test = this;
+ return Blob.filteredContent(test.blob, null, 0)
+ .catch(function(err) {
+ assert.strictEqual(err.message, "String as_path is required.");
+ });
+ });
+
+ it("throws an error when the flag is undefined", function() {
+ var test = this;
+ return Blob.filteredContent(test.blob, "")
+ .catch(function(err) {
+ assert.strictEqual(
+ err.message,
+ "Number check_for_binary_data is required."
+ );
+ });
+ });
+ });
});
diff --git a/test/tests/branch.js b/test/tests/branch.js
index 8d587c019..3900e4242 100644
--- a/test/tests/branch.js
+++ b/test/tests/branch.js
@@ -11,6 +11,7 @@ describe("Branch", function() {
var branchName2 = "test-branch2";
var fullBranchName = "refs/heads/" + branchName;
var fullBranchName2 = "refs/heads/" + branchName2;
+ var remoteName = "origin";
var upstreamName = "origin/master";
var fullUpstreamName = "refs/remotes/origin/master";
var nonHeadCommit = "c82fb078a192ea221c9f1093c64321c60d64aa0d";
@@ -85,6 +86,27 @@ describe("Branch", function() {
});
});
+ it("can get the remote name of a branch", function() {
+ var repo = this.repository;
+
+ return NodeGit.Branch.remoteName(repo, fullUpstreamName)
+ .then(function(remoteNameToTest) {
+ assert.equal(remoteNameToTest, remoteName);
+ });
+ });
+
+ it("cannot get remote name from a non-remote branch", function() {
+ var repo = this.repository;
+
+ return NodeGit.Branch.remoteName(repo, fullBranchName)
+ .then(function() {
+ assert.fail("The ref should not have been a remote");
+ })
+ .catch(function(err) {
+ assert.strictEqual(err.errno, -1);
+ });
+ });
+
it("can rename a branch", function() {
var branch = this.branch;
diff --git a/test/tests/diff.js b/test/tests/diff.js
index 43d14a3ec..fdad25354 100644
--- a/test/tests/diff.js
+++ b/test/tests/diff.js
@@ -247,10 +247,13 @@ describe("Diff", function() {
function(done) {
var evilString = "Unicode’s fun!\nAnd it’s good for you!\n";
var buffer = new Buffer(evilString);
- var oid = Blob.createFromBuffer(this.repository, buffer, buffer.length);
- Blob.lookup(this.repository, oid)
+ var test = this;
+ Blob.createFromBuffer(test.repository, buffer, buffer.length)
+ .then(function(oid) {
+ return Blob.lookup(test.repository, oid);
+ })
.then(function(blob) {
- blob.repo = this.repository;
+ blob.repo = test.repository;
return Diff.blobToBuffer(
blob,
null,
diff --git a/test/tests/filter.js b/test/tests/filter.js
new file mode 100644
index 000000000..1db1c9ecb
--- /dev/null
+++ b/test/tests/filter.js
@@ -0,0 +1,852 @@
+var assert = require("assert");
+var promisify = require("promisify-node");
+var fse = promisify(require("fs-extra"));
+var path = require("path");
+var local = path.join.bind(path, __dirname);
+
+describe("Filter", function() {
+ var NodeGit = require("../../");
+
+ var emptyRepoPath = local("../repos/empty");
+ var filterName = "psuedo_filter";
+ var Registry = NodeGit.FilterRegistry;
+ var Checkout = NodeGit.Checkout;
+ var Repository = NodeGit.Repository;
+ var reposPath = local("../repos/workdir");
+
+ var packageJsonPath = path.join(reposPath, "package.json");
+ var readmePath = path.join(reposPath, "README.md");
+
+ var mockFilter = {
+ apply: function() {},
+ check: function() {}
+ };
+
+ function commitFile(repo, fileName, fileContent, commitMessage) {
+ let index;
+ let treeOid;
+ let parent;
+
+ return fse.writeFile(path.join(repo.workdir(), fileName), fileContent)
+ .then(function() {
+ return repo.refreshIndex();
+ })
+ .then(function(indexResult) {
+ index = indexResult;
+ })
+ .then(function() {
+ return index.addByPath(fileName);
+ })
+ .then(function() {
+ return index.write();
+ })
+ .then(function() {
+ return index.writeTree();
+ })
+ .then(function(oidResult) {
+ treeOid = oidResult;
+ return NodeGit.Reference.nameToId(repo, "HEAD");
+ })
+ .then(function(head) {
+ return repo.getCommit(head);
+ })
+ .then(function(parentResult) {
+ parent = parentResult;
+ return Promise.all([
+ NodeGit.Signature.create("Foo Bar", "foo@bar.com", 123456789, 60),
+ NodeGit.Signature.create("Foo A Bar", "foo@bar.com", 987654321, 90)
+ ]);
+ })
+ .then(function(signatures) {
+ let author = signatures[0];
+ let committer = signatures[1];
+
+ return repo.createCommit(
+ "HEAD",
+ author,
+ committer,
+ commitMessage,
+ treeOid,
+ [parent]
+ );
+ });
+ }
+
+ beforeEach(function() {
+ var test = this;
+
+ return Repository.open(reposPath)
+ .then(function(repository) {
+ test.repository = repository;
+ return Repository.open(emptyRepoPath);
+ })
+ .then(function(emptyRepo) {
+ test.emptyRepo = emptyRepo;
+ return fse.writeFile(
+ path.join(reposPath, ".gitattributes"),
+ "*.md filter=" + filterName + " -text",
+ { encoding: "utf-8" }
+ );
+ });
+ });
+
+ afterEach(function() {
+ return Registry.unregister(filterName)
+ .catch(function(error) {
+ if (error === NodeGit.Error.CODE.ERROR) {
+ throw new Error("Cannot unregister filter");
+ }
+ });
+ });
+
+ describe("Register", function() {
+ var secondFilter = "hellofilter";
+
+ after(function(done) {
+ return Registry.unregister(secondFilter)
+ .then(function() {
+ done();
+ });
+ });
+
+ it("can register a filter", function() {
+ return Registry.register(filterName, mockFilter, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ });
+ });
+
+ it("can register multiple filters", function() {
+ return Registry.register(filterName, mockFilter, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return Registry.register(secondFilter, mockFilter, 1);
+ })
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ });
+ });
+
+ it("cannot register the same filter twice", function() {
+ return Registry.register(filterName, mockFilter, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return Registry.register(filterName, mockFilter, 0);
+ })
+ .catch(function(error) {
+ assert.strictEqual(error.errno, NodeGit.Error.CODE.EEXISTS);
+ });
+ });
+ });
+
+ describe("Unregister", function() {
+ beforeEach(function() {
+ return Registry.register(filterName, mockFilter, 0);
+ });
+
+ it("can unregister the filter", function() {
+ return Registry.unregister(filterName)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ });
+ });
+
+ it("cannot unregister the filter twice", function() {
+ return Registry.unregister(filterName)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return Registry.unregister(filterName);
+ })
+ .then(function(result) {
+ assert.fail("Should not have unregistered successfully");
+ })
+ .catch(function(error) {
+ assert.strictEqual(error.errno, NodeGit.Error.CODE.ENOTFOUND);
+ });
+ });
+ });
+
+ describe("Initialize", function(){
+ it("initializes successfully", function() {
+ var test = this;
+ var initialized = false;
+ return Registry.register(filterName, {
+ initialize: function() {
+ initialized = true;
+ return NodeGit.Error.CODE.OK;
+ },
+ apply: function() {},
+ check: function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout"
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.strictEqual(initialized, true);
+ });
+ });
+
+ it("initializes successfully even on garbage collect", function() {
+ var test = this;
+ var initialized = false;
+ return Registry.register(filterName, {
+ initialize: function() {
+ initialized = true;
+ return NodeGit.Error.CODE.OK;
+ },
+ apply: function() {},
+ check: function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ global.gc();
+
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout"
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.strictEqual(initialized, true);
+ });
+ });
+
+ it("does not initialize successfully", function() {
+ var test = this;
+ var initialized = false;
+ return Registry.register(filterName, {
+ initialize: function() {
+ initialized = true;
+ return NodeGit.Error.CODE.ERROR;
+ },
+ apply: function() {},
+ check: function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout"
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function(head) {
+ assert.fail(head, undefined, "Should not have actually checked out");
+ })
+ .catch(function(error) {
+ assert.strictEqual(initialized, true);
+ });
+ });
+ });
+
+ describe("Shutdown", function() {
+ it("filter successfully shuts down", function() {
+ var test = this;
+ var shutdown = false;
+ return Registry.register(filterName, {
+ apply: function() {},
+ check: function(){
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ shutdown: function(){
+ shutdown = true;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ return Registry.unregister(filterName);
+ })
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ assert.strictEqual(shutdown, true);
+ });
+ });
+
+ it("filter successfully shuts down on garbage collect", function() {
+ var test = this;
+ var shutdown = false;
+ return Registry.register(filterName, {
+ apply: function() {},
+ check: function(){
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ shutdown: function(){
+ shutdown = true;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ global.gc();
+ return Registry.unregister(filterName);
+ })
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ assert.strictEqual(shutdown, true);
+ });
+ });
+
+ it("shutdown completes even if there is an error", function() {
+ var test = this;
+ var shutdown = false;
+ return Registry.register(filterName, {
+ apply: function() {},
+ check: function(){
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ shutdown: function(){
+ shutdown = true;
+ throw new Error("I failed");
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ return Registry.unregister(filterName);
+ })
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ assert.strictEqual(shutdown, true);
+ })
+ .catch(function(error) {
+ assert.fail(error, null, "The operation should not have failed");
+ });
+ });
+ });
+
+ describe("Apply", function() {
+ var message = "some new fancy filter";
+ var length = message.length;
+ var tempBuffer = new Buffer(message, "utf-8");
+
+ it("should not apply when check returns GIT_PASSTHROUGH", function(){
+ var test = this;
+ var applied = false;
+
+ return Registry.register(filterName, {
+ apply: function() {
+ applied = true;
+ },
+ check: function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.notStrictEqual(applied, true);
+ });
+ });
+
+ it("should apply filter when check succeeds", function() {
+ var test = this;
+ var applied = true;
+
+ return Registry.register(filterName, {
+ apply: function() {
+ applied = true;
+ },
+ check: function() {
+ return NodeGit.Error.CODE.OK;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.strictEqual(applied, true);
+ });
+ });
+
+ it("does not apply when GIT_PASSTHROUGH is returned", function() {
+ var test = this;
+
+ return Registry.register(filterName, {
+ apply: function(to, from, source) {
+ return to.set(tempBuffer, length)
+ .then(function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ });
+ },
+ check: function() {
+ return NodeGit.Error.CODE.OK;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var readmeContent = fse.readFileSync(
+ packageJsonPath,
+ "utf-8"
+ );
+ assert.notStrictEqual(readmeContent, message);
+
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout"
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ var postInitializeReadmeContents = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.notStrictEqual(postInitializeReadmeContents, message);
+ });
+ });
+
+ it("applies the filter data on checkout", function() {
+ var test = this;
+
+ return Registry.register(filterName, {
+ apply: function(to, from, source) {
+ return to.set(tempBuffer, length)
+ .then(function(buf) {
+ 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");
+
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: ["README.md"]
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ var postInitializeReadmeContents = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.strictEqual(postInitializeReadmeContents, message);
+ });
+ });
+
+ it("applies the filter data on checkout with gc", function() {
+ var test = this;
+
+ return Registry.register(filterName, {
+ apply: function(to, from, source) {
+ return to.set(tempBuffer, length)
+ .then(function(buf) {
+ return NodeGit.Error.CODE.OK;
+ });
+ },
+ check: function(src, attr) {
+ return NodeGit.Error.CODE.OK;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var readmeContent = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+ assert.notStrictEqual(readmeContent, message);
+ fse.writeFileSync(readmePath, "whoa", "utf8");
+ global.gc();
+
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: ["README.md"]
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ var postInitializeReadmeContents = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.strictEqual(postInitializeReadmeContents, message);
+ });
+ });
+
+ it("applies the filter data on commit", function() {
+ var test = this;
+
+ return Registry.register(filterName, {
+ apply: function(to, from, source) {
+ return to.set(tempBuffer, length)
+ .then(function(buf) {
+ return NodeGit.Error.CODE.OK;
+ });
+ },
+ check: function(src, attr) {
+ return src.path() === "README.md" ?
+ 0 : NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ cleanup: function() {}
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var readmeContent = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+ assert.notStrictEqual(readmeContent, "testing commit contents");
+ })
+ .then(function() {
+ return commitFile(test.repository, "README.md",
+ "testing commit contents",
+ "test commit"
+ );
+ })
+ .then(function(oid) {
+ return test.repository.getHeadCommit();
+ })
+ .then(function(commit) {
+ var postInitializeReadmeContents = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.strictEqual(
+ postInitializeReadmeContents, "testing commit contents"
+ );
+ assert.strictEqual(commit.message(), "test commit");
+
+ return commit.getEntry("README.md");
+ })
+ .then(function(entry) {
+ assert.strictEqual(entry.isBlob(), true);
+ return entry.getBlob();
+ })
+ .then(function(blob) {
+ assert.strictEqual(blob.toString(), message);
+ });
+ });
+
+ it("applies the filter data on commit with gc", function() {
+ var test = this;
+
+ return Registry.register(filterName, {
+ apply: function(to, from, source) {
+ return to.set(tempBuffer, length)
+ .then(function(buf) {
+ return NodeGit.Error.CODE.OK;
+ });
+ },
+ check: function(src, attr) {
+ return src.path() === "README.md" ?
+ 0 : NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ cleanup: function() {}
+ }, 0)
+ .then(function(result) {
+ global.gc();
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var readmeContent = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+ assert.notStrictEqual(readmeContent, "testing commit contents");
+ })
+ .then(function() {
+ return commitFile(test.repository, "README.md",
+ "testing commit contents",
+ "test commit"
+ );
+ })
+ .then(function(oid) {
+ global.gc();
+ return test.repository.getHeadCommit();
+ })
+ .then(function(commit) {
+ var postInitializeReadmeContents = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.strictEqual(
+ postInitializeReadmeContents, "testing commit contents"
+ );
+ assert.strictEqual(commit.message(), "test commit");
+ global.gc();
+
+ return commit.getEntry("README.md");
+ })
+ .then(function(entry) {
+ assert.strictEqual(entry.isBlob(), true);
+ return entry.getBlob();
+ })
+ .then(function(blob) {
+ assert.strictEqual(blob.toString(), message);
+ });
+ });
+ });
+
+ describe("Cleanup", function() {
+ it("is called successfully", function() {
+ var test = this;
+ var cleaned = false;
+ return Registry.register(filterName, {
+ initialize: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ apply: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ check: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ cleanup: function() {
+ cleaned = true;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var packageContent = fse.readFileSync(
+ packageJsonPath,
+ "utf-8"
+ );
+ assert.notEqual(packageContent, "");
+
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.strictEqual(cleaned, true);
+ });
+ });
+
+ it("is called successfully with gc", function() {
+ var test = this;
+ var cleaned = false;
+ return Registry.register(filterName, {
+ initialize: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ apply: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ check: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ cleanup: function() {
+ cleaned = true;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var packageContent = fse.readFileSync(
+ packageJsonPath,
+ "utf-8"
+ );
+ assert.notEqual(packageContent, "");
+
+ global.gc();
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "package.json"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.strictEqual(cleaned, true);
+ });
+ });
+
+ it("is not called when check returns GIT_PASSTHROUGH", function() {
+ var test = this;
+ var cleaned = false;
+
+ return Registry.register(filterName, {
+ initialize: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ apply: function() {
+ return NodeGit.Error.CODE.OK;
+ },
+ check: function() {
+ return NodeGit.Error.CODE.PASSTHROUGH;
+ },
+ cleanup: function() {
+ cleaned = true;
+ }
+ }, 0)
+ .then(function(result) {
+ assert.strictEqual(result, NodeGit.Error.CODE.OK);
+ })
+ .then(function() {
+ var packageContent = fse.readFileSync(
+ packageJsonPath,
+ "utf-8"
+ );
+ var readmeContent = fse.readFileSync(
+ readmePath,
+ "utf-8"
+ );
+
+ assert.notEqual(packageContent, "");
+ assert.notEqual(readmeContent, "Initialized");
+ })
+ .then(function() {
+ return fse.writeFile(
+ packageJsonPath,
+ "Changing content to trigger checkout",
+ { encoding: "utf-8" }
+ );
+ })
+ .then(function() {
+ var opts = {
+ checkoutStrategy: Checkout.STRATEGY.FORCE,
+ paths: "README.md"
+ };
+ return Checkout.head(test.repository, opts);
+ })
+ .then(function() {
+ assert.notStrictEqual(cleaned, true);
+ });
+ });
+ });
+});
diff --git a/test/tests/merge.js b/test/tests/merge.js
index 66728bdb9..86531aa2c 100644
--- a/test/tests/merge.js
+++ b/test/tests/merge.js
@@ -1539,9 +1539,7 @@ describe("Merge", function() {
.then(function(theirAnnotatedCommit) {
return NodeGit.Merge(repository, theirAnnotatedCommit);
})
- .then(function(result) {
- assert.equal(result, 0);
-
+ .then(function() {
assert.equal(repository.state(),
NodeGit.Repository.STATE.MERGE);
// verify the convenience method
diff --git a/test/tests/rebase.js b/test/tests/rebase.js
index 731884f82..d121a6c0f 100644
--- a/test/tests/rebase.js
+++ b/test/tests/rebase.js
@@ -658,7 +658,7 @@ describe("Rebase", function() {
});
})
.then(function() {
- return rebase.abort(ourSignature);
+ return rebase.abort();
})
.then(function() {
return NodeGit.Rebase.open(repository)
diff --git a/vendor/libgit2 b/vendor/libgit2
index 9e76f2c72..3c1308d8c 160000
--- a/vendor/libgit2
+++ b/vendor/libgit2
@@ -1 +1 @@
-Subproject commit 9e76f2c72bb562aba6f72b95216bc4e885a46551
+Subproject commit 3c1308d8c1afa74d5da76ff1d3b2106d352ed36f