diff --git a/Makefile b/Makefile index e43af0a40..4207da976 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NODE_JS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) NODE_BLD = node-waf -NODE_LIB_PATH = ~/.node_libraries +NODE_LIB_PATH = ~/.node_modules BASE = . INSTALL_PATH = $(NODE_LIB_PATH)/nodegit @@ -22,11 +22,13 @@ debug: install: @@mkdir -p $(INSTALL_PATH) - @@mkdir -p $(INSTALL_PATH)/build/default + @@mkdir -p $(INSTALL_PATH)/build/Release @@mkdir -p $(INSTALL_PATH)/lib + @@mkdir -p $(INSTALL_PATH)/vendor - @@cp -f $(BASE)/build/default/nodegit.node $(INSTALL_PATH)/build/default/nodegit.node + @@cp -f $(BASE)/build/Release/nodegit.node $(INSTALL_PATH)/build/Release/nodegit.node @@cp -f $(BASE)/lib/* $(INSTALL_PATH)/lib/ + @@cp -rf $(BASE)/vendor/* $(INSTALL_PATH)/vendor/ @@cp -f $(BASE)/package.json $(INSTALL_PATH)/ @@echo "Installed to $(INSTALL_PATH)" diff --git a/README.md b/README.md index 5f1ee04a8..f0e0d639a 100644 --- a/README.md +++ b/README.md @@ -14,31 +14,33 @@ To run `nodegit` you need `Node.js` and to run unit tests you will need to have ### Easy install (Recommended) ### This will install and configure everything you need to use `nodegit`. - $ sudo npm install nodegit - -To update an existing installation, run - - $ sudo npm update nodegit +```` bash +$ npm install nodegit +```` ### Mac OS X/Linux/Unix ### #### Install `nodegit` by cloning source from GitHub and running the `configure`, `make`, and `make install` commands: #### \*Note: `nodegit` assumes your library path exists at `~/.node_libraries` you can change this by specifying a new lib path\* - $ git clone git://github.com/tbranyen/nodegit.git - $ cd nodegit +```` bash +$ git clone git://github.com/tbranyen/nodegit.git +$ cd nodegit - $ ./configure - $ make - $ make install - - $ make install NODE_LIB_PATH=/path/to/your/libraries +$ ./configure +$ make +$ make install + +$ make install NODE_LIB_PATH=/path/to/your/libraries +```` \*Updating to a new version\* - $ make update +```` bash +$ make update - $ make update NODE_LIB_PATH=/path/to/your/libraries +$ make update NODE_LIB_PATH=/path/to/your/libraries +```` ### Windows via Cygwin ### @@ -54,121 +56,125 @@ API Example Usage #### Convenience API #### - var git = require( 'nodegit' ); +```` javascript +var git = require("nodegit"); + +// Read a repository +git.repo(".git", function(err, repo) { + // Success is always 0, failure is always an error string + if (err) { throw err; } + + // Use the master branch + repo.branch("master", function(err, branch) { + if (err) { throw err; } + + // Iterate over the revision history + var history = branch.history(); - // Read a repository - git.repo( '.git', function( err, repo ) { - // Success is always 0, failure is always an error string - if( err ) { throw err; } - - // Use the master branch - repo.branch( 'master', function( err, branch ) { - if( err ) { throw err; } - - // Iterate over the revision history - var history = branch.history(); - - // Commit event is emitted with index 0,n... and commit object - history.on( 'commit', function( idx, commit ) { - // Print out `git log` emulation - console.log( 'commit ' + commit.sha ); - console.log( commit.author.name + '<' + commit.author.email + '>' ); - console.log( commit.time ); - console.log( '\n' ); - console.log( commit.message ); - console.log( '\n' ); - }); - }); + // Commit event emits commit object + history.on("commit", function(commit) { + // Print out `git log` emulation + console.log("commit " + commit.sha); + console.log(commit.author.name + "<" + commit.author.email + ">"); + console.log(commit.time); + console.log("\n"); + console.log(commit.message); + console.log("\n"); }); + }); +}); +```` #### Raw API #### - var git = require( 'nodegit' ).raw; - - // Create instance of Repo constructor - var repo = new git.Repo(); +```` javascript +var git = require( 'nodegit' ).raw; + +// Create instance of Repo constructor +var repo = new git.Repo(); - // Read a repository - repo.open( '.git', function( err ) { - // Err is an integer, success is 0, use strError for string representation +// Read a repository +repo.open( '.git', function( err ) { + // Err is an integer, success is 0, use strError for string representation + if( err ) { + var error = new git.Error(); + throw error.strError( err ); + } + + // Create instance of Ref constructor with this repository + var ref = new git.Ref( repo ); + + // Find the master branch + repo.lookupRef( ref, '/refs/heads/master', function( err ) { if( err ) { - var error = new git.Error(); - throw error.strError( err ); + var error = new git.Error(); + throw error.strError( err ); } - // Create instance of Ref constructor with this repository - var ref = new git.Ref( repo ); - - // Find the master branch - repo.lookupRef( ref, '/refs/heads/master', function( err ) { + // Create instance of Commit constructor with this repository + var commit = new git.Commit( repo ), + // Create instance of Oid constructor + oid = new git.Oid(); + + // Set the oid constructor internal reference to this branch reference + ref.oid( oid ); + + // Lookup the commit for this oid + commit.lookup( oid, function() { if( err ) { var error = new git.Error(); throw error.strError( err ); } - // Create instance of Commit constructor with this repository - var commit = new git.Commit( repo ), - // Create instance of Oid constructor - oid = new git.Oid(); - - // Set the oid constructor internal reference to this branch reference - ref.oid( oid ); - - // Lookup the commit for this oid - commit.lookup( oid, function() { - if( err ) { - var error = new git.Error(); - throw error.strError( err ); - } + // Create instance of RevWalk constructor with this repository + var revwalk = new git.RevWalk( repo ); - // Create instance of RevWalk constructor with this repository - var revwalk = new git.RevWalk( repo ); + // Push the commit as the start to walk + revwalk.push( commit ); - // Push the commit as the start to walk - revwalk.push( commit ); + // Recursive walk + function walk() { + // Each revision walk iteration yields a commit + var revisionCommit = new git.Commit( repo ); - // Recursive walk - function walk() { - // Each revision walk iteration yields a commit - var revisionCommit = new git.Commit( repo ); + revwalk.next( revisionCommit, function( err ) { + // Finish recursion once no more revision commits are left + if( err ) { return; } - revwalk.next( revisionCommit, function( err ) { - // Finish recursion once no more revision commits are left - if( err ) { return; } + // Create instance of Oid for sha + var oid = new git.Oid(); - // Create instance of Oid for sha - var oid = new git.Oid(); + // Set oid to the revision commit + revisionCommit.id( oid ); - // Set oid to the revision commit - revisionCommit.id( oid ); + // Create instance of Sig for author + var author = new git.Sig(); - // Create instance of Sig for author - var author = new git.Sig(); + // Set the author to the revision commit author + revisionCommit.author( author ); - // Set the author to the revision commit author - revisionCommit.author( author ); + // Convert timestamp to milliseconds and set new Date object + var time = new Date( revisionCommit.time() * 1000 ); - // Convert timestamp to milliseconds and set new Date object - var time = new Date( revisionCommit.time() * 1000 ); + // Print out `git log` emulation + console.log( oid.toString( 40 ) ); + console.log( author.name() + '<' + author.email() + '>' ); + console.log( time ); + console.log( '\n' ); + console.log( revisionCommit.message() ); + console.log( '\n' ); - // Print out `git log` emulation - console.log( oid.toString( 40 ) ); - console.log( author.name() + '<' + author.email() + '>' ); - console.log( time ); - console.log( '\n' ); - console.log( revisionCommit.message() ); - console.log( '\n' ); - - // Recurse! - walk(); - }); - } + // Recurse! + walk(); + }); + } - // Initiate recursion - walk(): - }); + // Initiate recursion + walk(): }); }); +}); +```` Running tests ------------- @@ -199,6 +205,11 @@ Release information __Can keep track of current method coverage at: [http://bit.ly/tb_methods](http://bit.ly/tb_methods)__ +### v0.0.5: ### + * Added in fast Buffer support. + * Blob raw write supported added, no convenience methods yet... + * Updated libgit2 to version 0.12.0 + ### v0.0.4: ### * Many fixes! * Blob raw write supported added, no convenience methods yet... diff --git a/example/convenience-tree.js b/example/convenience-tree.js index bcdf7ef5d..dbec82cc3 100644 --- a/example/convenience-tree.js +++ b/example/convenience-tree.js @@ -6,9 +6,19 @@ git.repo( '../.git', function( err, repo ) { repo.branch( 'master', function( err, branch ) { if( err ) { throw err; } - branch.tree().walk( function( idx, entry ) { - console.log( entry.name ); - console.log( entry.content ); + branch.tree().walk().on('entry', function( idx, entry ) { + //console.log(entry.entry); + console.log( entry.name, entry.attributes ); + //console.log( entry.content ); }); + + //branch.tree().entry('example/raw-blob.js', function( entry ) { + // if( entry ) { + // console.log(entry.name); + // } + // else { + // console.log('not found'); + // } + //}); }); }); diff --git a/example/raw-blob.js b/example/raw-blob.js index 2eec95049..cee4b70a2 100644 --- a/example/raw-blob.js +++ b/example/raw-blob.js @@ -18,7 +18,7 @@ repo.open( path.resolve( '../.git' ), function() { console.log( entry.name() + ':' ); console.log( blob.rawSize() ); - console.log( blob.rawContent() ); + console.log( typeof blob.rawContent() ); } }); }); diff --git a/example/stress/revwalk.js b/example/stress/revwalk.js index 081f59832..c4e733fe0 100644 --- a/example/stress/revwalk.js +++ b/example/stress/revwalk.js @@ -4,7 +4,7 @@ var git = require( 'nodegit' ); //* Stress test revision walking //setInterval(function() { - for(var i=0; i<10000; i++) { +// for(var i=0; i<10000; i++) { (function() { @@ -13,7 +13,7 @@ var git = require( 'nodegit' ); this.branch( 'master', function() { this.history().on( 'commit', function( i, commit ) { - //console.log( commit.id.toString(40) ); + console.log( commit.id.toString(40) ); }); }); }); @@ -53,6 +53,6 @@ var git = require( 'nodegit' ); })(); - } +// } //}, 0); //*/ diff --git a/include/blob.h b/include/blob.h index 5e7243c7b..34ed78b08 100755 --- a/include/blob.h +++ b/include/blob.h @@ -137,7 +137,7 @@ class GitBlob : public ObjectWrap { * Returns: * completion code integer */ - static int EIO_Lookup(eio_req* req); + static void EIO_Lookup(eio_req* req); /** * Function: EIO_AfterLookup * diff --git a/include/commit.h b/include/commit.h index e11c2cf4d..d3a4229a8 100755 --- a/include/commit.h +++ b/include/commit.h @@ -8,7 +8,6 @@ #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -23,7 +22,7 @@ using namespace v8; /** * Class wrapper for libgit2 git_commit */ -class GitCommit : public EventEmitter { +class GitCommit : public ObjectWrap { public: /** * v8::FunctionTemplate used to create Node.js constructor @@ -59,7 +58,7 @@ class GitCommit : public EventEmitter { static Handle New(const Arguments& args); static Handle Lookup(const Arguments& args); - static int EIO_Lookup(eio_req *req); + static void EIO_Lookup(eio_req *req); static int EIO_AfterLookup(eio_req *req); static Handle Close(const Arguments& args); diff --git a/include/error.h b/include/error.h index 9f730ed41..0ca2bddd0 100755 --- a/include/error.h +++ b/include/error.h @@ -7,7 +7,6 @@ #define ERROR_H #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/include/index.h b/include/index.h index ada24ca5a..03d479da9 100755 --- a/include/index.h +++ b/include/index.h @@ -7,7 +7,6 @@ #define INDEX_H #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/include/object.h b/include/object.h index 92a3914da..d84de5c05 100755 --- a/include/object.h +++ b/include/object.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -19,7 +18,7 @@ using namespace node; /** * Class wrapper for libgit2 git_object */ -class GitObject : public EventEmitter { +class GitObject : public ObjectWrap { public: /** * v8::FunctionTemplate used to create Node.js constructor diff --git a/include/odb.h b/include/odb.h index 1bdbb14d9..a0d9692cc 100755 --- a/include/odb.h +++ b/include/odb.h @@ -7,7 +7,6 @@ #define ODB_H #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/include/odb_backend.h b/include/odb_backend.h index 20a1275cc..0615cda43 100755 --- a/include/odb_backend.h +++ b/include/odb_backend.h @@ -7,7 +7,6 @@ #define ODB_BACKEND_H #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/include/oid.h b/include/oid.h index 6fd23c937..da782eddb 100755 --- a/include/oid.h +++ b/include/oid.h @@ -7,14 +7,13 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" using namespace node; using namespace v8; -class GitOid : public EventEmitter { +class GitOid : public ObjectWrap { public: static Persistent constructor_template; static void Initialize (Handle target); diff --git a/include/reference.h b/include/reference.h index ea47c1315..cbe3caf85 100755 --- a/include/reference.h +++ b/include/reference.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include #include "../vendor/libgit2/include/git2.h" @@ -18,7 +17,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace node; using namespace v8; -class GitReference : public EventEmitter { +class GitReference : public ObjectWrap { public: static Persistent constructor_template; static void Initialize(Handle target); @@ -33,7 +32,7 @@ class GitReference : public EventEmitter { static Handle New(const Arguments& args); static Handle Lookup(const Arguments& args); - static int EIO_Lookup(eio_req* req); + static void EIO_Lookup(eio_req* req); static int EIO_AfterLookup(eio_req* req); static Handle Oid(const Arguments& args); diff --git a/include/repo.h b/include/repo.h index 620877a2a..c840036f4 100755 --- a/include/repo.h +++ b/include/repo.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include #include "../vendor/libgit2/include/git2.h" @@ -17,7 +16,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace node; using namespace v8; -class GitRepo : public EventEmitter { +class GitRepo : public ObjectWrap { public: static Persistent constructor_template; static void Initialize(Handle target); @@ -43,7 +42,7 @@ class GitRepo : public EventEmitter { static Handle New(const Arguments& args); static Handle Open(const Arguments& args); - static int EIO_Open(eio_req* req); + static void EIO_Open(eio_req* req); static int EIO_AfterOpen(eio_req* req); static Handle Lookup(const Arguments& args); @@ -53,7 +52,7 @@ class GitRepo : public EventEmitter { static Handle Free(const Arguments& args); static Handle Init(const Arguments& args); - static int EIO_Init(eio_req* req); + static void EIO_Init(eio_req* req); static int EIO_AfterInit(eio_req* req); private: diff --git a/include/revwalk.h b/include/revwalk.h index 0a86ae222..2e1db881e 100755 --- a/include/revwalk.h +++ b/include/revwalk.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -17,7 +16,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace node; using namespace v8; -class GitRevWalk : public EventEmitter { +class GitRevWalk : public ObjectWrap { public: static Persistent constructor_template; static void Initialize(Handle target); @@ -42,7 +41,7 @@ class GitRevWalk : public EventEmitter { static Handle Hide(const Arguments& args); static Handle Next(const Arguments& args); - static int EIO_Next(eio_req* req); + static void EIO_Next(eio_req* req); static int EIO_AfterNext(eio_req* req); static Handle Sorting(const Arguments& args); diff --git a/include/sig.h b/include/sig.h index 2f275b47f..c01778e20 100755 --- a/include/sig.h +++ b/include/sig.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -16,7 +15,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen using namespace v8; using namespace node; -class GitSig : public EventEmitter { +class GitSig : public ObjectWrap { public: static Persistent constructor_template; static void Initialize(Handle target); diff --git a/include/tag.h b/include/tag.h index 6a941eedb..db8560d44 100755 --- a/include/tag.h +++ b/include/tag.h @@ -7,7 +7,6 @@ #define TAG_H #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/include/tree.h b/include/tree.h index 2d6da237f..74c748aab 100755 --- a/include/tree.h +++ b/include/tree.h @@ -7,7 +7,7 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include +#include #include "../vendor/libgit2/include/git2.h" @@ -20,7 +20,7 @@ using namespace node; /** * Class wrapper for libgit2 git_tree */ -class GitTree : public EventEmitter { +class GitTree : public ObjectWrap { public: /** * v8::FunctionTemplate used to create Node.js constructor @@ -48,13 +48,12 @@ class GitTree : public EventEmitter { /** * Lookup a tree object from a repository. * - * @param tree pointer to the looked up tree * @param repo the repo to use when locating the tree. * @param id identity of the tree to locate. * * @return 0 on success; error code otherwise */ - int Lookup(git_tree** tree, git_repository* repo, const git_oid* id); + int Lookup(git_repository* repo, const git_oid* id); /** * Get number of entries in the looked up tree. * @@ -93,9 +92,16 @@ class GitTree : public EventEmitter { */ static Handle New(const Arguments& args); + static Handle Lookup(const Arguments& args); + static int EIO_Lookup(eio_req *req); + static int EIO_AfterLookup(eio_req *req); static Handle EntryCount(const Arguments& args); static Handle EntryByIndex(const Arguments& args); + static void EIO_EntryByIndex(eio_req *req); + static int EIO_AfterEntryByIndex(eio_req *req); static Handle EntryByName(const Arguments& args); + static void EIO_EntryByName(eio_req *req); + static int EIO_AfterEntryByName(eio_req *req); static Handle SortEntries(const Arguments& args); static Handle ClearEntries(const Arguments& args); @@ -104,6 +110,34 @@ class GitTree : public EventEmitter { * Internal reference to git_tree object */ git_tree* tree; + /** + * Structure to handle async lookups + */ + struct lookup_request { + GitTree* tree; + GitRepo* repo; + GitOid* oid; + int err; + Persistent callback; + }; + /** + * Structure to handle async entryByIndex + */ + struct entryindex_request { + GitTree* tree; + GitTreeEntry* entry; + int idx; + Persistent callback; + }; + /** + * Structure to handle async entryByName + */ + struct entryname_request { + GitTree* tree; + GitTreeEntry* entry; + std::string name; + Persistent callback; + }; }; #endif diff --git a/include/tree_entry.h b/include/tree_entry.h index cc871ad7b..2d3634695 100755 --- a/include/tree_entry.h +++ b/include/tree_entry.h @@ -7,7 +7,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -22,7 +21,7 @@ using namespace node; /** * Class wrapper for libgit2 git_tree_entry */ -class GitTreeEntry : EventEmitter { +class GitTreeEntry : ObjectWrap { public: /** * v8::FunctionTemplate used to create Node.js constructor diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 000000000..f8a4faf55 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,19 @@ +#ifndef UTILS_H +#define UTILS_H + +// Credit: @samcday +// http://sambro.is-super-awesome.com/2011/03/03/creating-a-proper-buffer-in-a-node-c-addon/ +#define MAKE_FAST_BUFFER(NG_SLOW_BUFFER, NG_FAST_BUFFER) \ + Local NG_JS_BUFFER = Local::Cast( \ + Context::GetCurrent()->Global()->Get( \ + String::New("Buffer"))); \ + \ + Handle NG_JS_ARGS[3] = { \ + NG_SLOW_BUFFER->handle_, \ + Integer::New(Buffer::Length(NG_SLOW_BUFFER)), \ + Integer::New(0) \ + }; \ + \ + NG_FAST_BUFFER = NG_JS_BUFFER->NewInstance(3, NG_JS_ARGS); + +#endif diff --git a/lib/commit.js b/lib/commit.js index 2708b40d1..43afc7bae 100644 --- a/lib/commit.js +++ b/lib/commit.js @@ -114,7 +114,7 @@ var _Commit = function( obj ) { event.emit( 'end', commits ); } else { - event.emit( 'commit', index, commit ); + event.emit( 'commit', commit ); commits.push( commit ); } }); diff --git a/lib/index.js b/lib/index.js index 90028ae8e..613b17b17 100755 --- a/lib/index.js +++ b/lib/index.js @@ -1,39 +1,29 @@ -// System -var os = require( 'os' ); - -// Library -var util = require( './util.js' ).util, - blob = require( './blob.js' ).blob, - repo = require( './repo.js' ).repo, - error = require( './error.js' ).error, - sig = require( './sig.js' ).sig, - oid = require( './oid.js' ).oid, - object = require( './object.js' ).object, - ref = require( './ref.js' ).ref, - revwalk = require( './revwalk.js' ).revwalk, - commit = require( './commit.js' ).commit, - tree = require( './tree.js' ).tree, - entry = require( './tree_entry.js' ).entry; +// Used to detect for Cygwin +var os = require("os"); // Required for Windows/Cygwin support -var root = [ __dirname, '/../vendor/libgit2/build/shared' ].join( '' ), path = process.env.PATH; -if( ~os.type().indexOf( 'CYGWIN' ) && !~path.indexOf( root ) ) { - process.env.PATH = root + ':' + path; +var root = [ __dirname, "/../vendor/libgit2/build/shared" ].join(""), + path = process.env.PATH; + +if (~os.type().indexOf("CYGWIN") && !~path.indexOf(root)) { + process.env.PATH = root + ":" + path; } -// Assign raw api to module -exports.raw = require( '../build/default/nodegit' ); +// Import libraries +exports.util = require("./util.js").util; +exports.blob = require("./blob.js").blob; +exports.repo = require("./repo.js").repo; +exports.error = require("./error.js").error; +exports.sig = require("./sig.js").sig; +exports.oid = require("./oid.js").oid; +exports.object = require("./object.js").object; +exports.ref = require("./ref.js").ref; +exports.revwalk = require("./revwalk.js").revwalk; +exports.commit = require("./commit.js").commit; +exports.tree = require("./tree.js").tree; +exports.entry = require("./tree_entry.js").entry; -// Assign to module -exports.blob = blob; -exports.util = util; -exports.repo = repo; -exports.ref = ref; -exports.oid = oid; -exports.object = object; -exports.sig = sig; -exports.error = error; -exports.revwalk = revwalk; -exports.commit = commit; -exports.tree = tree; -exports.entry = entry; +// Assign raw api to module +exports.raw = require("../build/Release/nodegit"); +// Set version +exports.version = "0.0.6"; diff --git a/lib/repo.js b/lib/repo.js index 785bef8b8..89de31f60 100644 --- a/lib/repo.js +++ b/lib/repo.js @@ -1,66 +1,62 @@ -var git = require( '../' ), - path = require( 'path' ); - -var _Repo = function( dir, callback ) { - // Public namespace - var self = {}; - - self.repo = new git.raw.Repo(); - - if( dir && callback ) { - if( !callback ) { return; } - - self.repo.open( path.resolve && path.resolve( dir ) || dir, function() { - var args = Array.prototype.slice.call( arguments ); - - args[0] = git.util().error( args[0] ); +var git = require("../"); + +/* Module: Repo + * Work with a repository. + */ +exports.repo = function(dir, async) { + var self = { + // Assign a new repo object + repo: new git.raw.Repo() + }; - callback.apply( self, args.concat( self ) ); + if (dir && async) { + self.repo.open(dir, function() { + git.util().asyncComplete.call(this, arguments, async); }); } - else if( dir ) { - self.repo.open( path.resolve && path.resolve( dir ) || dir ); + else if (dir) { + // TODO: Make this eventually + // this.repo.openSync(path.resolve(dir) + //this.repo.open(path.resolve(dir) + self.repo.open(dir); } // Look up a branch and find its tree - self.branch = function( name, callback ) { - if( !callback ) { return; } - - git.ref( self.repo ).lookup( 'refs/heads/' + name, function( err, ref ) { - if( err ) { - var args = Array.prototype.slice.call( arguments ); - args[0] = git.util().error( args[0] ); - - callback.apply( this, args.concat( this ) ); + self.branch = function(name, async) { + if (!async) { + // TODO: Implement Sync API + return; + } + + git.ref(self.repo).lookup("refs/heads/" + name, function(err, ref) { + if (err) { + return git.util().asyncComplete.call(this, arguments, async); } - git.commit( self.repo ).lookup( self.repo, ref.oid().oid, function() { - var args = Array.prototype.slice.call( arguments ); - args[0] = git.util().error( args[0] ); - - callback.apply( this, args.concat( this ) ); + git.commit(self.repo).lookup(self.repo, ref.oid().oid, function() { + git.util().asyncComplete.call(this, arguments, async); }); }); }; // Find a single commit - self.commit = function( sha, callback ) { - if( !callback ) { return; } + self.commit = function(sha, async) { + if (!async) { + // TODO: Implement Sync API + return; + } - var oid = git.oid( sha ); - - git.commit().lookup( self.repo, oid.oid, callback ); + git.commit().lookup(self.repo, git.oid(sha).oid, async); }; - self.init = function( dir, is_bare, callback ) { - if( !callback ) { return; } - - self.repo.init( path.resolve && path.resolve( dir ) || dir, is_bare, function() { - var args = Array.prototype.slice.call( arguments ); + self.init = function(dir, isBare, async) { + if (!async) { + // TODO: Implement Sync API + return; + } - args[0] = git.util().error( args[0] ); - - callback.apply( self, args.concat( self ) ); + self.repo.init(dir, isBare, function() { + git.util().asyncComplete.call(this, arguments, async); }); return self; @@ -70,8 +66,6 @@ var _Repo = function( dir, callback ) { self.repo.free(); delete self.repo; }; - + return self; }; - -exports.repo = _Repo; diff --git a/lib/tree.js b/lib/tree.js index 243624e85..57ddb2630 100644 --- a/lib/tree.js +++ b/lib/tree.js @@ -25,52 +25,80 @@ var _Tree = function( obj, tree ) { enumerable: true }); - // Synchronous walk - self.walk = function( callback ) { - if( !callback ) { return; } - + self.walk = function( repo ) { var entry - , i; - - for( i=0, len=self.length; i (http://twitter.com/tbranyen)", "main": "./lib/index.js", @@ -13,11 +13,8 @@ "build": "./build", "lib": "./lib" }, - "modules": { - "index": "./lib/index" - }, "engines": { - "node": "*" + "node": "~0.6" }, "scripts": { "preinstall": "./configure", diff --git a/src/base.cc b/src/base.cc index 26b5d2d64..a74f08273 100755 --- a/src/base.cc +++ b/src/base.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/src/blob.cc b/src/blob.cc index 7b32c44a0..3ef5668a8 100755 --- a/src/blob.cc +++ b/src/blob.cc @@ -10,6 +10,7 @@ #include "../vendor/libgit2/include/git2.h" +#include "../include/utils.h" #include "../include/repo.h" #include "../include/blob.h" @@ -110,13 +111,12 @@ Handle GitBlob::Lookup(const Arguments& args) { return scope.Close( Undefined() ); } -int GitBlob::EIO_Lookup(eio_req* req) { +void GitBlob::EIO_Lookup(eio_req* req) { lookup_request* ar = static_cast(req->data); git_oid oid = ar->oid->GetValue(); ar->err = ar->blob->Lookup(ar->repo->GetValue(), &oid); - return 0; } int GitBlob::EIO_AfterLookup(eio_req* req) { @@ -147,11 +147,15 @@ Handle GitBlob::RawContent(const Arguments& args) { GitBlob* blob = ObjectWrap::Unwrap(args.This()); int rawSize = blob->RawSize(); - const char* contents = (const char *)const_cast(blob->RawContent()); + std::string contents = (const char *)const_cast(blob->RawContent()); - Buffer* buffer = Buffer::New(const_cast(contents), strlen(contents)); + int bufferLength = rawSize; + Buffer* buffer = Buffer::New(const_cast(contents.c_str()), bufferLength); + + Local fastBuffer; + MAKE_FAST_BUFFER(buffer, fastBuffer); - return scope.Close( buffer->handle_ ); + return scope.Close( fastBuffer ); } Handle GitBlob::RawSize(const Arguments& args) { diff --git a/src/commit.cc b/src/commit.cc index 39009a214..d85a9b727 100755 --- a/src/commit.cc +++ b/src/commit.cc @@ -6,7 +6,6 @@ #include #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -67,7 +66,8 @@ const git_oid* GitCommit::Id() { } const char* GitCommit::MessageShort() { - return git_commit_message_short(this->commit); + return ""; + //return git_commit_message_short(this->commit); } const char* GitCommit::Message() { @@ -146,13 +146,12 @@ Handle GitCommit::Lookup(const Arguments& args) { return scope.Close( Undefined() ); } -int GitCommit::EIO_Lookup(eio_req *req) { +void GitCommit::EIO_Lookup(eio_req *req) { lookup_request *ar = static_cast(req->data); git_oid oid = ar->oid->GetValue(); ar->err = ar->commit->Lookup(ar->repo->GetValue(), &oid); - return 0; } int GitCommit::EIO_AfterLookup(eio_req *req) { @@ -169,8 +168,9 @@ int GitCommit::EIO_AfterLookup(eio_req *req) { ar->callback->Call(Context::GetCurrent()->Global(), 1, argv); - if(try_catch.HasCaught()) + if(try_catch.HasCaught()) { FatalException(try_catch); + } ar->callback.Dispose(); diff --git a/src/error.cc b/src/error.cc index 011beb22d..9f180f667 100755 --- a/src/error.cc +++ b/src/error.cc @@ -5,7 +5,6 @@ #include #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/src/object.cc b/src/object.cc index a0b43d54f..9a15102ec 100755 --- a/src/object.cc +++ b/src/object.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" diff --git a/src/oid.cc b/src/oid.cc index 572cd5f44..a7bf5481b 100755 --- a/src/oid.cc +++ b/src/oid.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -43,11 +42,11 @@ void GitOid::SetValue(git_oid oid) { } int GitOid::Mkstr(const char* id) { - return git_oid_mkstr(&this->oid, id); + return git_oid_fromstr(&this->oid, id); } void GitOid::Mkraw(const unsigned char* raw) { - git_oid_mkraw(&this->oid, raw); + git_oid_fromraw(&this->oid, raw); } void GitOid::Fmt(char* buffer) { diff --git a/src/reference.cc b/src/reference.cc index ab37f871f..5a9f8e29b 100755 --- a/src/reference.cc +++ b/src/reference.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include #include "../vendor/libgit2/include/git2.h" @@ -94,14 +93,13 @@ Handle GitReference::Lookup(const Arguments& args) { return scope.Close( Undefined() ); } -int GitReference::EIO_Lookup(eio_req *req) { +void GitReference::EIO_Lookup(eio_req *req) { lookup_request *ar = static_cast(req->data); git_repository* repo = ar->repo->GetValue(); ar->err = ar->ref->Lookup(repo, ar->name.c_str()); - return 0; } int GitReference::EIO_AfterLookup(eio_req *req) { diff --git a/src/repo.cc b/src/repo.cc index d288ef612..f44d41236 100755 --- a/src/repo.cc +++ b/src/repo.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include #include "../vendor/libgit2/include/git2.h" @@ -101,12 +100,11 @@ Handle GitRepo::Open(const Arguments& args) { return scope.Close( Undefined() ); } -int GitRepo::EIO_Open(eio_req *req) { +void GitRepo::EIO_Open(eio_req *req) { open_request *ar = static_cast(req->data); ar->err = ar->repo->Open(ar->path.c_str()); - return 0; } int GitRepo::EIO_AfterOpen(eio_req *req) { @@ -258,12 +256,11 @@ Handle GitRepo::Init(const Arguments& args) { return scope.Close( Undefined() ); } -int GitRepo::EIO_Init(eio_req *req) { +void GitRepo::EIO_Init(eio_req *req) { init_request *ar = static_cast(req->data); ar->err = ar->repo->Init(ar->path.c_str(), ar->is_bare); - return 0; } int GitRepo::EIO_AfterInit(eio_req *req) { diff --git a/src/revwalk.cc b/src/revwalk.cc index a010f2e19..3715a601d 100755 --- a/src/revwalk.cc +++ b/src/revwalk.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -61,6 +60,9 @@ void GitRevWalk::Reset() { } int GitRevWalk::Push(git_oid* oid) { + // Test + git_revwalk_sorting(this->revwalk, GIT_SORT_TIME | GIT_SORT_REVERSE); + return git_revwalk_push(this->revwalk, oid); } @@ -153,14 +155,13 @@ Handle GitRevWalk::Next(const Arguments& args) { return scope.Close( Undefined() ); } -int GitRevWalk::EIO_Next(eio_req *req) { +void GitRevWalk::EIO_Next(eio_req *req) { next_request *ar = static_cast(req->data); git_oid oid = ar->oid->GetValue(); ar->err = ar->revwalk->Next(&oid); ar->oid->SetValue(oid); - return 0; } int GitRevWalk::EIO_AfterNext(eio_req *req) { diff --git a/src/sig.cc b/src/sig.cc index 251b879a9..0b33e1089 100755 --- a/src/sig.cc +++ b/src/sig.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -44,7 +43,8 @@ void GitSig::SetValue(git_signature* sig) { } void GitSig::New(const char *name, const char *email, time_t time, int offset) { - this->sig = git_signature_new(name, email, time, offset); + git_signature_new(&this->sig, name, email, time, offset); + //this->sig = git_signature_new(name, email, time, offset); } git_signature* GitSig::Dup() { diff --git a/src/tree.cc b/src/tree.cc index be2306391..fe3b1bdbf 100755 --- a/src/tree.cc +++ b/src/tree.cc @@ -4,11 +4,11 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" #include "../include/repo.h" +#include "../include/oid.h" #include "../include/tree.h" #include "../include/tree_entry.h" @@ -24,6 +24,7 @@ void GitTree::Initialize (Handle target) { constructor_template->InstanceTemplate()->SetInternalFieldCount(1); constructor_template->SetClassName(String::NewSymbol("Tree")); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "lookup", Lookup); NODE_SET_PROTOTYPE_METHOD(constructor_template, "entryCount", EntryCount); NODE_SET_PROTOTYPE_METHOD(constructor_template, "entryByIndex", EntryByIndex); NODE_SET_PROTOTYPE_METHOD(constructor_template, "entryByName", EntryByName); @@ -40,6 +41,10 @@ void GitTree::SetValue(git_tree* tree) { this->tree = tree; } +int GitTree::Lookup(git_repository* repo, const git_oid* id) { + return git_tree_lookup(&this->tree, repo, id); +} + size_t GitTree::EntryCount() { return git_tree_entrycount(this->tree); } @@ -67,6 +72,79 @@ Handle GitTree::New(const Arguments& args) { return scope.Close( args.This() ); } +Handle GitTree::Lookup(const Arguments& args) { + HandleScope scope; + + GitTree *tree = ObjectWrap::Unwrap(args.This()); + + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); + } + + if(args.Length() == 1 || !args[1]->IsObject()) { + return ThrowException(Exception::Error(String::New("Oid is required and must be an Object."))); + } + + GitRepo* repo = ObjectWrap::Unwrap(args[0]->ToObject()); + GitOid* oid = ObjectWrap::Unwrap(args[1]->ToObject()); + + git_oid ref_oid = oid->GetValue(); + int err = tree->Lookup(repo->GetValue(), &ref_oid); + + return scope.Close( Integer::New(err) ); + +// if(args.Length() == 2 || !args[2]->IsFunction()) { +// return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); +// } +// +// callback = Local::Cast(args[2]); +// +// lookup_request *lr = new lookup_request(); +// lr->tree = tree; +// lr->repo = ObjectWrap::Unwrap(args[0]->ToObject()); +// lr->oid = ObjectWrap::Unwrap(args[1]->ToObject()); +// lr->callback = Persistent::New(callback); +// +// tree->Ref(); +// +// eio_custom(EIO_Lookup, EIO_PRI_DEFAULT, EIO_AfterLookup, lr); +// ev_ref(EV_DEFAULT_UC); +// +// return scope.Close( Undefined() ); +} + +//int GitTree::EIO_Lookup(eio_req *req) { +// lookup_request *lr = static_cast(req->data); +// +// git_oid oid = lr->oid->GetValue(); +// lr->err = lr->tree->Lookup(lr->repo->GetValue(), &oid); +// +// return 0; +//} + +//int GitTree::EIO_AfterLookup(eio_req *req) { +// lookup_request *lr = static_cast(req->data); +// +// ev_unref(EV_DEFAULT_UC); +// lr->tree->Unref(); +// +// Handle argv[1]; +// argv[0] = Integer::New(lr->err); +// +// TryCatch try_catch; +// +// lr->callback->Call(Context::GetCurrent()->Global(), 1, argv); +// +// if(try_catch.HasCaught()) +// FatalException(try_catch); +// +// lr->callback.Dispose(); +// +// delete lr; +// +// return 0; +//} + Handle GitTree::EntryCount(const Arguments& args) { HandleScope scope; @@ -81,6 +159,7 @@ Handle GitTree::EntryByIndex(const Arguments& args) { HandleScope scope; GitTree *tree = ObjectWrap::Unwrap(args.This()); + Local callback; if(args.Length() == 0 || !args[0]->IsObject()) { return ThrowException(Exception::Error(String::New("TreeEntry is required and must be a Object."))); @@ -90,20 +169,62 @@ Handle GitTree::EntryByIndex(const Arguments& args) { return ThrowException(Exception::Error(String::New("Index is required and must be a Number."))); } - GitTreeEntry* entry = ObjectWrap::Unwrap(args[0]->ToObject()); + if(args.Length() == 2 || !args[2]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } + + callback = Local::Cast(args[2]); - int index = args[1]->ToInteger()->Value(); + entryindex_request *er = new entryindex_request(); + er->tree = tree; + er->entry = ObjectWrap::Unwrap(args[0]->ToObject()); + er->idx = args[1]->ToInteger()->Value(); + er->callback = Persistent::New(callback); + + tree->Ref(); + + eio_custom(EIO_EntryByIndex, EIO_PRI_DEFAULT, EIO_AfterEntryByIndex, er); + ev_ref(EV_DEFAULT_UC); - entry->SetValue(tree->EntryByIndex(index)); - return scope.Close( Undefined() ); } +void GitTree::EIO_EntryByIndex(eio_req *req) { + entryindex_request *er = static_cast(req->data); + + er->entry->SetValue(er->tree->EntryByIndex(er->idx)); + +} + +int GitTree::EIO_AfterEntryByIndex(eio_req *req) { + entryindex_request *er = static_cast(req->data); + + ev_unref(EV_DEFAULT_UC); + er->tree->Unref(); + + Handle argv[0]; + + TryCatch try_catch; + + er->callback->Call(Context::GetCurrent()->Global(), 0, argv); + + if(try_catch.HasCaught()) + FatalException(try_catch); + + er->callback.Dispose(); + + delete er; + + return 0; +} + Handle GitTree::EntryByName(const Arguments& args) { HandleScope scope; GitTree *tree = ObjectWrap::Unwrap(args.This()); + Local callback; + if(args.Length() == 0 || !args[0]->IsObject()) { return ThrowException(Exception::Error(String::New("TreeEntry is required and must be a Object."))); } @@ -112,17 +233,57 @@ Handle GitTree::EntryByName(const Arguments& args) { return ThrowException(Exception::Error(String::New("Name is required and must be a String."))); } - GitTreeEntry* entry = ObjectWrap::Unwrap(args[0]->ToObject()); - - int index = args[1]->ToInteger()->Value(); + if(args.Length() == 2 || !args[2]->IsFunction()) { + return ThrowException(Exception::Error(String::New("Callback is required and must be a Function."))); + } + callback = Local::Cast(args[2]); String::Utf8Value name(args[1]->ToString()); - entry->SetValue(tree->EntryByName(*name)); - + entryname_request *er = new entryname_request(); + er->tree = tree; + er->entry = ObjectWrap::Unwrap(args[0]->ToObject()); + er->name = *name; + er->callback = Persistent::New(callback); + + tree->Ref(); + + eio_custom(EIO_EntryByName, EIO_PRI_DEFAULT, EIO_AfterEntryByName, er); + ev_ref(EV_DEFAULT_UC); + return scope.Close( Undefined() ); } +void GitTree::EIO_EntryByName(eio_req *req) { + entryname_request *er = static_cast(req->data); + + er->entry->SetValue(er->tree->EntryByName(er->name.c_str())); + +} + +int GitTree::EIO_AfterEntryByName(eio_req *req) { + entryname_request *er = static_cast(req->data); + + ev_unref(EV_DEFAULT_UC); + er->tree->Unref(); + + Handle argv[1]; + argv[0] = Boolean::New(er->entry->GetValue() != NULL); + + TryCatch try_catch; + + er->callback->Call(Context::GetCurrent()->Global(), 1, argv); + + if(try_catch.HasCaught()) + FatalException(try_catch); + + er->callback.Dispose(); + + delete er; + + return 0; +} + Handle GitTree::SortEntries(const Arguments& args) { HandleScope scope; diff --git a/src/tree_entry.cc b/src/tree_entry.cc index 2226c9910..088ae8b82 100755 --- a/src/tree_entry.cc +++ b/src/tree_entry.cc @@ -4,7 +4,6 @@ Copyright (c) 2011, Tim Branyen @tbranyen #include #include -#include #include "../vendor/libgit2/include/git2.h" @@ -27,11 +26,16 @@ void GitTreeEntry::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(constructor_template, "name", Name); NODE_SET_PROTOTYPE_METHOD(constructor_template, "attributes", Attributes); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "id", Id); NODE_SET_PROTOTYPE_METHOD(constructor_template, "toObject", ToObject); target->Set(String::NewSymbol("TreeEntry"), constructor_template->GetFunction()); } +git_tree_entry* GitTreeEntry::GetValue() { + return this->entry; +} + void GitTreeEntry::SetValue(git_tree_entry* entry) { this->entry = entry; } @@ -97,24 +101,24 @@ Handle GitTreeEntry::Id(const Arguments& args) { Handle GitTreeEntry::ToObject(const Arguments& args) { HandleScope scope; - //GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); + GitTreeEntry *entry = ObjectWrap::Unwrap(args.This()); - //if(args.Length() == 0 || !args[0]->IsObject()) { - // return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); - //} + if(args.Length() == 0 || !args[0]->IsObject()) { + return ThrowException(Exception::Error(String::New("Repo is required and must be an Object."))); + } - //if(args.Length() == 1 || !args[1]->IsObject()) { - // return ThrowException(Exception::Error(String::New("Object is required and must be an Object."))); - //} + if(args.Length() == 1 || !args[1]->IsObject()) { + return ThrowException(Exception::Error(String::New("Object is required and must be an Object."))); + } - //GitRepo* repo = ObjectWrap::Unwrap(args[0]->ToObject()); - //GitObject* object = ObjectWrap::UnwrapToObject()); + GitRepo* repo = ObjectWrap::Unwrap(args[0]->ToObject()); + GitObject* object = ObjectWrap::Unwrap(args[1]->ToObject()); - //git_object* out; - //entry->ToObject(repo->GetValue(), &out); + git_object* out; + entry->ToObject(repo->GetValue(), &out); - //GitObject->SetValue(out); - // + object->SetValue(out); + return scope.Close( Undefined() ); } Persistent GitTreeEntry::constructor_template; diff --git a/test/convenience-repo.js b/test/convenience-repo.js index 2a96583a3..782085adc 100644 --- a/test/convenience-repo.js +++ b/test/convenience-repo.js @@ -1,47 +1,48 @@ -var git = require( '../' ), - rimraf = require( '../vendor/rimraf') || require( 'rimraf' ), - fs = require( 'fs' ); +var git = require( "../" ); +var rimraf = require( "../vendor/rimraf"); +var fs = require( "fs" ); // Helper functions var helper = { // Test if obj is a true function - testFunction: function( test, obj, label ) { + testFunction: function(test, obj, label) { // The object reports itself as a function - test( typeof obj, 'function', label +' reports as a function.' ); + test(typeof obj, "function", label + " reports as a function."); // This ensures the repo is actually a derivative of the Function [[Class]] - test( toString.call( obj ), '[object Function]', label +' [[Class]] is of type function.' ); + test(toString.call(obj), "[object Function]", label + " [[Class]] is of type function."); }, // Test code and handle exception thrown - testException: function( test, fun, label ) { + testException: function(test, fun, label) { try { fun(); - test( false, label ); + test(false, label); } catch (ex) { - test( true, label ); + test(true, label); } } }; // Repo -exports.constructor = function( test ){ - test.expect( 5 ); +// Ensure the repo method can handle opening repositories with async/sync +// signatures properly. +exports.method = function(test){ + test.expect(5); - // Test for function - helper.testFunction( test.equals, git.repo, 'Repo' ); + helper.testFunction(test.equals, git.repo, "Repo"); // Test callback argument existence - helper.testException( test.ok, function() { - git.repo( 'some/path' ); - }, 'Throw an exception if no callback' ); + helper.testException(test.ok, function() { + git.repo("some/path"); + }, "Throw an exception if no callback"); // Test invalid repository - git.repo( '/etc/hosts', function( err, path ) { - test.equals( 'The specified repository is invalid', err, 'Invalid repository error code' ); + git.repo("/etc/hosts", function(err, path) { + test.equals("The specified repository is invalid", err, "Invalid repository error code"); // Test valid repository - git.repo( '../.git', function( err, path ) { - test.equals( 0, err, 'Valid repository error code' ); + git.repo("../.git", function(err, path) { + test.equals(0, err, "Valid repository error code"); test.done(); }); @@ -49,25 +50,25 @@ exports.constructor = function( test ){ }; // Repo::Init -exports.init = function( test ) { - test.expect( 4 ); +// Ensure the init method can create repositories at the destination path and +// can create either bare/non-bare. This should work async/sync and provide +// the proper return values. +exports.init = function(test) { + test.expect(4); - // Test for function - helper.testFunction( test.equals, git.repo().init, 'Repo::Init' ); + helper.testFunction(test.equals, git.repo().init, "Repo::Init"); // Cleanup, remove test repo directory - if it exists - rimraf( './test.git', function() { + rimraf("./test.git", function() { // Create bare repo and test for creation - git.repo().init( './test.git', true, function( err, path, is_bare ) { - test.equals( 0, err, 'Successfully created bare repository' ); + git.repo().init("./test.git", true, function(err, path, isBare) { + test.equals(0, err, "Successfully created bare repository"); // Verify repo exists - git.repo('./test.git', function(err, path, repo) { - test.equals( 0, err, 'Valid repository created' ); + git.repo("./test.git", function(err, path, repo) { + test.equals(0, err, "Valid repository created"); // Cleanup, remove test repo directory - rimraf( './test.git', function() { - test.done(); - }); + rimraf("./test.git", test.done); }); }); }); diff --git a/test/index.js b/test/index.js index 84b37099e..7712e11f6 100644 --- a/test/index.js +++ b/test/index.js @@ -1,10 +1,9 @@ -require.paths.unshift( '../vendor' ); try { var reporter = require( '../vendor/nodeunit' ).reporters['default']; } catch( e ) { - var sys = require( 'sys' ); + var sys = require( 'util' ); sys.puts( 'Cannot find nodeunit module.' ); sys.puts( 'You can download submodules for this project by doing:' ); sys.puts( '' ); @@ -17,7 +16,7 @@ try { var rimraf = require( '../vendor/rimraf' ); } catch(e) { - var sys = require( 'sys' ); + var sys = require( 'util' ); sys.puts( 'Cannot find rimraf module.' ); sys.puts( 'You can download submodules for this project by doing:' ); sys.puts( '' ); diff --git a/test/raw-blob.js b/test/raw-blob.js index d63eb70af..dd927bf9d 100644 --- a/test/raw-blob.js +++ b/test/raw-blob.js @@ -106,14 +106,14 @@ exports.rawContent = function( test ) { entry = new git.TreeEntry(), blob = new git.Blob( testRepo ); - if( !testCommit.tree( tree ) && tree.entryCount() > 1 ) { - tree.entryByIndex( entry, 1 ); - entry.toObject( testRepo, blob ); - - //console.log( entry.name() + ':' ); - //console.log( blob.rawSize() ); - //console.dir( blob.rawContent() ); - } + //if( !testCommit.tree( tree ) && tree.entryCount() > 1 ) { + // tree.entryByIndex( entry, 1 ); + // entry.toObject( testRepo, blob ); + + // //console.log( entry.name() + ':' ); + // //console.log( blob.rawSize() ); + // //console.dir( blob.rawContent() ); + //} }); }); diff --git a/test/raw-repo.js b/test/raw-repo.js index 5d50d16a1..9e6460f44 100644 --- a/test/raw-repo.js +++ b/test/raw-repo.js @@ -58,7 +58,7 @@ exports.open = function( test ) { // Test invalid repository testRepo.open( '/etc/hosts', function( err ) { - test.equals( -8, err, 'Invalid repository error code' ); + test.equals( -7, err, 'Invalid repository error code' ); // Test valid repository testRepo.open( path.resolve( '../.git' ), function( err ) { diff --git a/util/hint-check.js b/util/hint-check.js index bacd1666b..54091ff8b 100644 --- a/util/hint-check.js +++ b/util/hint-check.js @@ -2,42 +2,44 @@ var nodejshint = require( './nodejshint.js' ).test, files = [ // Test convenience api - 'lib/blob.js', - 'lib/commit.js', - 'lib/error.js', - 'lib/index.js', - 'lib/object.js', - 'lib/oid.js', - 'lib/ref.js', - 'lib/repo.js', - 'lib/revwalk.js', - 'lib/sig.js', - 'lib/tree.js', - 'lib/tree_entry.js', - 'lib/util.js', + 'lib/blob.js' +, 'lib/commit.js' +, 'lib/error.js' +, 'lib/index.js' +, 'lib/object.js' +, 'lib/oid.js' +, 'lib/ref.js' +, 'lib/repo.js' +, 'lib/revwalk.js' +, 'lib/sig.js' +, 'lib/tree.js' +, 'lib/tree_entry.js' +, 'lib/util.js' // Test unit test - 'test/convenience-repo.js', - 'test/index.js', - 'test/raw-blob.js', - 'test/raw-commit.js', - 'test/raw-error.js', - 'test/raw-obj.js', - 'test/raw-oid.js', - 'test/raw-ref.js', - 'test/raw-repo.js', - 'test/raw-revwalk.js', +, 'test/convenience-repo.js' +, 'test/index.js' +, 'test/raw-blob.js' +, 'test/raw-commit.js' +, 'test/raw-error.js' +, 'test/raw-object.js' +, 'test/raw-oid.js' +, 'test/raw-reference.js' +, 'test/raw-repo.js' +, 'test/raw-revwalk.js' // Test examples - 'example/convenience-repo.js', - 'example/convenience-tree.js', - 'example/raw-error.js', - 'example/raw-oid.js', - 'example/raw-repo.js', - 'example/raw-revwalk.js' +, 'example/convenience-repo.js' +, 'example/convenience-tree.js' +, 'example/raw-error.js' +, 'example/raw-oid.js' +, 'example/raw-repo.js' +, 'example/raw-revwalk.js' ]; nodejshint( files, function( failures ) { + console.log( failures, 'failures' ); + if( !files.length ) { process.exit( 0 ); } diff --git a/util/jshint.js b/util/jshint.js index a9254757d..f622efd9a 100755 --- a/util/jshint.js +++ b/util/jshint.js @@ -40,7 +40,7 @@ The first parameter is either a string or an array of strings. If it is a string, it will be split on '\n' or '\r'. If it is an array of strings, it is assumed that each string represents one line. The source can be a - JavaScript text, or HTML text, or a JSON text, or a CSS text. + JavaScript text or a JSON text. The second parameter is an optional object of options which control the operation of JSHINT. Most of the options are booleans: They are all @@ -158,111 +158,57 @@ "(begin)", "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(last)", "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "*", "+", "++", "-", - "--", "\/", "<", "<=", "==", "===", ">", ">=", $, ADSAFE, __filename, __dirname, - ActiveXObject, Array, Boolean, Buffer, COM, CScript, Canvas, CustomAnimation, - Date, Debug, E, Enumerator, Error, EvalError, FadeAnimation, Flash, - FormField, Frame, Function, HotKey, Image, JSON, LN10, LN2, LOG10E, - LOG2E, MAX_VALUE, MIN_VALUE, Math, MenuItem, MoveAnimation, - NEGATIVE_INFINITY, Number, Object, Option, PI, POSITIVE_INFINITY, Point, - RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, - RotateAnimation, SQRT1_2, SQRT2, ScrollBar, String, Style, SyntaxError, - System, Text, TextArea, Timer, TypeError, URIError, URL, VBArray, - WScript, Web, Window, XMLDOM, XMLHttpRequest, "\\", a, abbr, acronym, - activeborder, activecaption, addEventListener, address, adsafe, alert, - aliceblue, all, animator, antiquewhite, appleScript, applet, apply, - approved, appworkspace, applicationCache, aqua, aquamarine, area, arguments, - arity, article, aside, audio, autocomplete, azure, b, background, - "background-attachment", "background-color", "background-image", - "background-position", "background-repeat", base, bdo, beep, beige, big, - bisque, bitwise, black, blanchedalmond, block, blockquote, blue, - blueviolet, blur, body, border, "border-bottom", "border-bottom-color", - "border-bottom-style", "border-bottom-width", "border-collapse", - "border-color", "border-left", "border-left-color", "border-left-style", - "border-left-width", "border-right", "border-right-color", - "border-right-style", "border-right-width", "border-spacing", - "border-style", "border-top", "border-top-color", "border-top-style", - "border-top-width", "border-width", bottom, boss, br, braille, brown, browser, - burlywood, button, buttonface, buttonhighlight, buttonshadow, - buttontext, bytesToUIString, c, cadetblue, call, callee, caller, canvas, - cap, caption, "caption-side", captiontext, cases, center, charAt, - charCodeAt, character, chartreuse, chocolate, chooseColor, chooseFile, - chooseFolder, cite, clear, clearInterval, clearTimeout, clip, close, - closeWidget, closed, closure, cm, code, col, colgroup, color, command, - comment, condition, confirm, console, constructor, content, - convertPathToHFS, convertPathToPlatform, coral, cornflowerblue, - cornsilk, couch, "counter-increment", "counter-reset", create, crimson, - css, curly, cursor, cyan, d, darkblue, darkcyan, darkgoldenrod, darkgray, - darkgreen, darkkhaki, darkmagenta, darkolivegreen, darkorange, darkorchid, - darkred, darksalmon, darkseagreen, darkslateblue, darkslategray, darkturquoise, - darkviolet, data, datalist, dd, debug, decodeURI, decodeURIComponent, - deeppink, deepskyblue, defaultStatus, defineClass, del, deserialize, - details, devel, dfn, dialog, dimgray, dir, direction, display, div, dl, - document, dodgerblue, dt, edition, else, em, embed, embossed, emit, empty, - "empty-cells", encodeURI, encodeURIComponent, entityify, eqeqeq, errors, - es5, escape, eval, event, evidence, evil, ex, exception, exec, exps, exports, - fieldset, figure, filesystem, FileReader, firebrick, first, float, floor, - floralwhite, focus, focusWidget, font, "font-family", "font-size", - "font-size-adjust", "font-stretch", "font-style", "font-variant", - "font-weight", footer, forestgreen, forin, form, fragment, frame, - frames, frameset, from, fromCharCode, fuchsia, fud, funct, function, - functions, g, gainsboro, gc, getComputedStyle, getRow, ghostwhite, GLOBAL, global, - globals, gold, goldenrod, gray, graytext, green, greenyellow, h1, h2, - h3, h4, h5, h6, handheld, hasOwnProperty, head, header, height, help, - hgroup, highlight, highlighttext, history, honeydew, hotpink, hr, - "hta:application", html, i, iTunes, id, identifier, iframe, img, immed, - implieds, in, inactiveborder, inactivecaption, inactivecaptiontext, - include, indent, indexOf, indianred, indigo, infobackground, infotext, - init, input, ins, isAlpha, isApplicationRunning, isArray, isDigit, - isFinite, isNaN, ivory, join, jshint, JSHINT, json, jquery, jQuery, kbd, - keygen, keys, khaki, konfabulatorVersion, label, labelled, lang, last, - lavender, lavenderblush, lawngreen, laxbreak, lbp, led, left, legend, - lemonchiffon, length, "letter-spacing", li, lib, lightblue, lightcoral, - lightcyan, lightgoldenrodyellow, lightgreen, lightpink, lightsalmon, - lightseagreen, lightskyblue, lightslategray, lightsteelblue, - lightyellow, lime, limegreen, line, "line-height", linen, link, - "list-style", "list-style-image", "list-style-position", - "list-style-type", load, loadClass, localStorage, location, log, m, magenta, - map, margin, "margin-bottom", "margin-left", "margin-right", "margin-top", - mark, "marker-offset", maroon, match, "max-height", "max-width", maxerr, - maxlen, md5, mediumaquamarine, mediumblue, mediumorchid, mediumpurple, - mediumseagreen, mediumslateblue, mediumspringgreen, mediumturquoise, - mediumvioletred, member, menu, menutext, message, meta, meter, - midnightblue, "min-height", "min-width", mintcream, mistyrose, mm, - moccasin, module, moveBy, moveTo, name, nav, navajowhite, navigator, navy, new, - newcap, noarg, node, noempty, noframes, nomen, nonew, noscript, nud, object, ol, - oldlace, olive, olivedrab, on, onbeforeunload, onblur, onerror, onevar, - onfocus, onload, onresize, onunload, opacity, open, openDatabase, openURL, opener, - opera, optgroup, option, orange, orangered, orchid, outer, outline, "outline-color", - "outline-style", "outline-width", output, overflow, "overflow-x", - "overflow-y", p, padding, "padding-bottom", "padding-left", - "padding-right", "padding-top", "page-break-after", "page-break-before", - palegoldenrod, palegreen, paleturquoise, palevioletred, papayawhip, - param, parent, parseFloat, parseInt, passfail, pc, peachpuff, peru, - pink, play, plum, plusplus, pop, popupMenu, position, powderblue, pre, - predef, preferenceGroups, preferences, print, process, progress, projection, - prompt, prototype, pt, purple, push, px, q, quit, quotes, random, range, - raw, reach, readFile, readUrl, reason, red, regexp, reloadWidget, - removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, - resolvePath, resumeUpdates, respond, rhino, right, rosybrown, royalblue, - rp, rt, ruby, runCommand, runCommandInBg, saddlebrown, safe, salmon, samp, - sandybrown, saveAs, savePreferences, screen, script, scroll, scrollBy, - scrollTo, scrollbar, seagreen, seal, search, seashell, section, send, select, - serialize, setInterval, setTimeout, shift, showWidgetPreferences, - sienna, silver, skyblue, slateblue, slategray, sleep, slice, small, - snow, sort, source, span, spawn, speak, speech, split, springgreen, src, - stack, status, start, steelblue, strict, strong, style, styleproperty, sub, - substr, sum, sup, supplant, suppressUpdates, sync, system, table, - "table-layout", tan, tbody, td, teal, tellWidget, test, "text-align", - "text-decoration", "text-indent", "text-shadow", "text-transform", - textarea, tfoot, th, thead, thistle, threeddarkshadow, threedface, - threedhighlight, threedlightshadow, threedshadow, time, title, - toLowerCase, toString, toUpperCase, toint32, token, tomato, top, tr, tt, - tty, turquoise, tv, type, u, ul, undef, unescape, "unicode-bidi", - unused, unwatch, updateNow, urls, value, valueOf, var, version, - "vertical-align", video, violet, visibility, watch, WebSocket, wheat, white, - "white-space", whitesmoke, widget, width, window, windowframe, windows, - windowtext, Worker, "word-spacing", "word-wrap", yahooCheckLogin, - yahooLogin, yahooLogout, yellow, yellowgreen, "z-index" + "--", "\/", "<", "<=", "==", "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, + $continue, $w, Abstract, Ajax, __filename, __dirname, ActiveXObject, Array, + ArrayBuffer, ArrayBufferView, Autocompleter, Assets, Boolean, Builder, + Buffer, Browser, COM, CScript, Canvas, CustomAnimation, Class, Control, + Chain, Color, Cookie, Core, DataView, Date, Debug, Draggable, Draggables, + Droppables, Document, DomReady, DOMReady, Drag, E, Enumerator, Enumerable, + Element, Elements, Error, Effect, EvalError, Event, Events, FadeAnimation, + Field, Flash, Float32Array, Float64Array, Form, FormField, Frame, Function, + Fx, Group, Hash, HotKey, HTMLElement, HtmlTable, Iframe, IframeShim, Image, + Int16Array, Int32Array, Int8Array, Insertion, InputValidator, JSON, Keyboard, + Locale, LN10, LN2, LOG10E, LOG2E, MAX_VALUE, MIN_VALUE, Mask, Math, MenuItem, + MoveAnimation, MooTools, Native, NEGATIVE_INFINITY, Number, Object, + ObjectRange, Option, Options, OverText, PI, POSITIVE_INFINITY, + PeriodicalExecuter, Point, Position, Prototype, RangeError, Rectangle, + ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, SQRT1_2, + SQRT2, ScrollBar, Scriptaculous, Scroller, Slick, Slider, Selector, String, + Style, SyntaxError, Sortable, Sortables, SortableObserver, Sound, Spinner, + System, Swiff, Text, TextArea, Template, Timer, Tips, Type, TypeError, + Toggle, Try, URI, URIError, URL, VBArray, WScript, Web, Window, XMLDOM, + XMLHttpRequest, XPathEvaluator, XPathException, XPathExpression, + XPathNamespace, XPathNSResolver, XPathResult, "\\", a, addEventListener, + address, alert, apply, applicationCache, arguments, arity, asi, b, bitwise, + block, blur, boolOptions, boss, browser, c, call, callee, caller, cases, + charAt, charCodeAt, character, clearInterval, clearTimeout, close, closed, + closure, comment, condition, confirm, console, constructor, content, couch, + create, css, curly, d, data, datalist, dd, debug, decodeURI, + decodeURIComponent, defaultStatus, defineClass, deserialize, devel, + document, edition, else, emit, encodeURI, encodeURIComponent, entityify, + eqeqeq, eqnull, errors, es5, escape, eval, event, evidence, evil, ex, + exception, exec, exps, expr, exports, FileReader, first, floor, focus, + forin, fragment, frames, from, fromCharCode, fud, funct, function, functions, + g, gc, getComputedStyle, getRow, GLOBAL, global, globals, globalstrict, + hasOwnProperty, help, history, i, id, identifier, immed, implieds, + include, indent, indexOf, init, ins, instanceOf, isAlpha, + isApplicationRunning, isArray, isDigit, isFinite, isNaN, join, jshint, + JSHINT, json, jquery, jQuery, keys, label, labelled, last, laxbreak, + latedef, lbp, led, left, length, line, load, loadClass, localStorage, + location, log, loopfunc, m, match, maxerr, maxlen, member,message, meta, + module, moveBy, moveTo, mootools, name, navigator, new, newcap, noarg, + node, noempty, nomen, nonew, nud, onbeforeunload, onblur, onerror, onevar, + onfocus, onload, onresize, onunload, open, openDatabase, openURL, opener, + opera, outer, param, parent, parseFloat, parseInt, passfail, plusplus, + predef, print, process, prompt, prototype, prototypejs, push, quit, range, + raw, reach, reason, regexp, readFile, readUrl, removeEventListener, replace, + report, require, reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, + respond, rhino, right, runCommand, scroll, screen, scrollBy, scrollTo, + scrollbar, search, seal, send, serialize, setInterval, setTimeout, shift, + slice, sort,spawn, split, stack, status, start, strict, sub, substr, supernew, + shadow, supplant, sum, sync, test, toLowerCase, toString, toUpperCase, toint32, + token, top, type, typeOf, Uint16Array, Uint32Array, Uint8Array, undef, + unused, urls, value, valueOf, var, version, WebSocket, white, window, Worker */ /*global exports: false */ @@ -274,11 +220,7 @@ var JSHINT = (function () { "use strict"; - var adsafe_id, // The widget's ADsafe id. - adsafe_may, // The widget may load approved scripts. - adsafe_went, // ADSAFE.go has been called. - anonname, // The guessed name for anonymous functions. - approved, // ADsafe approved urls. + var anonname, // The guessed name for anonymous functions. // These are operators that should not be used with the ! operator. @@ -298,68 +240,56 @@ var JSHINT = (function () { '%' : true }, -// These are property names that should not be permitted in the safe subset. - - banned = { // the member names that ADsafe prohibits. - 'arguments' : true, - callee : true, - caller : true, - constructor : true, - 'eval' : true, - prototype : true, - stack : true, - unwatch : true, - valueOf : true, - watch : true - }, - - // These are the JSHint boolean options. boolOptions = { - adsafe : true, // if ADsafe should be enforced - bitwise : true, // if bitwise operators should not be allowed - boss : true, // if advanced usage of assignments and == should be allowed - browser : true, // if the standard browser globals should be predefined - cap : true, // if upper case HTML should be allowed - couch : true, // if CouchDB globals should be predefined - css : true, // if CSS workarounds should be tolerated - curly : true, // if curly braces around blocks should be required (even in if/for/while) - debug : true, // if debugger statements should be allowed - devel : true, // if logging should be allowed (console, alert, etc.) - eqeqeq : true, // if === should be required - es5 : true, // if ES5 syntax should be allowed - evil : true, // if eval should be allowed - forin : true, // if for in statements must filter - fragment : true, // if HTML fragments should be allowed - immed : true, // if immediate invocations must be wrapped in parens - jquery : true, // if jQuery globals should be predefined - laxbreak : true, // if line breaks should not be checked - newcap : true, // if constructor names must be capitalized - noarg : true, // if arguments.caller and arguments.callee should be disallowed - node : true, // if the Node.js environment globals should be predefined - noempty : true, // if empty blocks should be disallowed - nonew : true, // if using `new` for side-effects should be disallowed - nomen : true, // if names should be checked - on : true, // if HTML event handlers should be allowed - onevar : true, // if only one var statement per function should be allowed - passfail : true, // if the scan should stop on first error - plusplus : true, // if increment/decrement should not be allowed - regexp : true, // if the . should not be allowed in regexp literals - rhino : true, // if the Rhino environment globals should be predefined - undef : true, // if variables should be declared before used - safe : true, // if use of some browser features should be restricted - windows : true, // if MS Windows-specigic globals should be predefined - strict : true, // require the "use strict"; pragma - sub : true, // if all forms of subscript notation are tolerated - white : true, // if strict whitespace rules apply - widget : true // if the Yahoo Widgets globals should be predefined + asi : true, // if automatic semicolon insertion should be tolerated + bitwise : true, // if bitwise operators should not be allowed + boss : true, // if advanced usage of assignments should be allowed + browser : true, // if the standard browser globals should be predefined + couch : true, // if CouchDB globals should be predefined + curly : true, // if curly braces around blocks should be required (even in if/for/while) + debug : true, // if debugger statements should be allowed + devel : true, // if logging globals should be predefined (console, alert, etc.) + eqeqeq : true, // if === should be required + eqnull : true, // if == null comparisons should be tolerated + es5 : true, // if ES5 syntax should be allowed + evil : true, // if eval should be allowed + expr : true, // if ExpressionStatement should be allowed as Programs + forin : true, // if for in statements must filter + globalstrict: true, // if global "use strict"; should be allowed (also enables 'strict') + immed : true, // if immediate invocations must be wrapped in parens + jquery : true, // if jQuery globals should be predefined + latedef : true, // if the use before definition should not be tolerated + laxbreak : true, // if line breaks should not be checked + loopfunc : true, // if functions should be allowed to be defined within loops + mootools : true, // if MooTools globals should be predefined + newcap : true, // if constructor names must be capitalized + noarg : true, // if arguments.caller and arguments.callee should be disallowed + node : true, // if the Node.js environment globals should be predefined + noempty : true, // if empty blocks should be disallowed + nonew : true, // if using `new` for side-effects should be disallowed + nomen : true, // if names should be checked + onevar : true, // if only one var statement per function should be allowed + passfail : true, // if the scan should stop on first error + plusplus : true, // if increment/decrement should not be allowed + prototypejs : true, // if Prototype and Scriptaculous globals shoudl be predefined + regexp : true, // if the . should not be allowed in regexp literals + rhino : true, // if the Rhino environment globals should be predefined + undef : true, // if variables should be declared before used + shadow : true, // if variable shadowing should be tolerated + strict : true, // require the "use strict"; pragma + sub : true, // if all forms of subscript notation are tolerated + supernew : true, // if `new function () { ... };` and `new Object;` should be tolerated + white : true // if strict whitespace rules apply }, // browser contains a set of global names which are commonly provided by a // web browser environment. browser = { + ArrayBuffer : false, + ArrayBufferView : false, addEventListener: false, applicationCache: false, blur : false, @@ -367,14 +297,21 @@ var JSHINT = (function () { clearTimeout : false, close : false, closed : false, + DataView : false, defaultStatus : false, document : false, event : false, FileReader : false, + Float32Array : false, + Float64Array : false, focus : false, frames : false, getComputedStyle: false, + HTMLElement : false, history : false, + Int16Array : false, + Int32Array : false, + Int8Array : false, Image : false, length : false, localStorage : false, @@ -407,10 +344,19 @@ var JSHINT = (function () { setTimeout : false, status : false, top : false, + Uint16Array : false, + Uint32Array : false, + Uint8Array : false, WebSocket : false, window : false, Worker : false, - XMLHttpRequest : false + XMLHttpRequest : false, + XPathEvaluator : false, + XPathException : false, + XPathExpression : false, + XPathNamespace : false, + XPathNSResolver : false, + XPathResult : false }, couch = { @@ -426,198 +372,6 @@ var JSHINT = (function () { module : false }, - cssAttributeData, - cssAny, - - cssColorData = { - "aliceblue" : true, - "antiquewhite" : true, - "aqua" : true, - "aquamarine" : true, - "azure" : true, - "beige" : true, - "bisque" : true, - "black" : true, - "blanchedalmond" : true, - "blue" : true, - "blueviolet" : true, - "brown" : true, - "burlywood" : true, - "cadetblue" : true, - "chartreuse" : true, - "chocolate" : true, - "coral" : true, - "cornflowerblue" : true, - "cornsilk" : true, - "crimson" : true, - "cyan" : true, - "darkblue" : true, - "darkcyan" : true, - "darkgoldenrod" : true, - "darkgray" : true, - "darkgreen" : true, - "darkkhaki" : true, - "darkmagenta" : true, - "darkolivegreen" : true, - "darkorange" : true, - "darkorchid" : true, - "darkred" : true, - "darksalmon" : true, - "darkseagreen" : true, - "darkslateblue" : true, - "darkslategray" : true, - "darkturquoise" : true, - "darkviolet" : true, - "deeppink" : true, - "deepskyblue" : true, - "dimgray" : true, - "dodgerblue" : true, - "firebrick" : true, - "floralwhite" : true, - "forestgreen" : true, - "fuchsia" : true, - "gainsboro" : true, - "ghostwhite" : true, - "gold" : true, - "goldenrod" : true, - "gray" : true, - "green" : true, - "greenyellow" : true, - "honeydew" : true, - "hotpink" : true, - "indianred" : true, - "indigo" : true, - "ivory" : true, - "khaki" : true, - "lavender" : true, - "lavenderblush" : true, - "lawngreen" : true, - "lemonchiffon" : true, - "lightblue" : true, - "lightcoral" : true, - "lightcyan" : true, - "lightgoldenrodyellow" : true, - "lightgreen" : true, - "lightpink" : true, - "lightsalmon" : true, - "lightseagreen" : true, - "lightskyblue" : true, - "lightslategray" : true, - "lightsteelblue" : true, - "lightyellow" : true, - "lime" : true, - "limegreen" : true, - "linen" : true, - "magenta" : true, - "maroon" : true, - "mediumaquamarine" : true, - "mediumblue" : true, - "mediumorchid" : true, - "mediumpurple" : true, - "mediumseagreen" : true, - "mediumslateblue" : true, - "mediumspringgreen" : true, - "mediumturquoise" : true, - "mediumvioletred" : true, - "midnightblue" : true, - "mintcream" : true, - "mistyrose" : true, - "moccasin" : true, - "navajowhite" : true, - "navy" : true, - "oldlace" : true, - "olive" : true, - "olivedrab" : true, - "orange" : true, - "orangered" : true, - "orchid" : true, - "palegoldenrod" : true, - "palegreen" : true, - "paleturquoise" : true, - "palevioletred" : true, - "papayawhip" : true, - "peachpuff" : true, - "peru" : true, - "pink" : true, - "plum" : true, - "powderblue" : true, - "purple" : true, - "red" : true, - "rosybrown" : true, - "royalblue" : true, - "saddlebrown" : true, - "salmon" : true, - "sandybrown" : true, - "seagreen" : true, - "seashell" : true, - "sienna" : true, - "silver" : true, - "skyblue" : true, - "slateblue" : true, - "slategray" : true, - "snow" : true, - "springgreen" : true, - "steelblue" : true, - "tan" : true, - "teal" : true, - "thistle" : true, - "tomato" : true, - "turquoise" : true, - "violet" : true, - "wheat" : true, - "white" : true, - "whitesmoke" : true, - "yellow" : true, - "yellowgreen" : true, - - "activeborder" : true, - "activecaption" : true, - "appworkspace" : true, - "background" : true, - "buttonface" : true, - "buttonhighlight" : true, - "buttonshadow" : true, - "buttontext" : true, - "captiontext" : true, - "graytext" : true, - "highlight" : true, - "highlighttext" : true, - "inactiveborder" : true, - "inactivecaption" : true, - "inactivecaptiontext" : true, - "infobackground" : true, - "infotext" : true, - "menu" : true, - "menutext" : true, - "scrollbar" : true, - "threeddarkshadow" : true, - "threedface" : true, - "threedhighlight" : true, - "threedlightshadow" : true, - "threedshadow" : true, - "window" : true, - "windowframe" : true, - "windowtext" : true - }, - - cssBorderStyle, - cssBreak, - - cssLengthData = { - '%': true, - 'cm': true, - 'em': true, - 'ex': true, - 'in': true, - 'mm': true, - 'pc': true, - 'pt': true, - 'px': true - }, - - cssMedia, - cssOverflow, - devel = { alert : false, confirm : false, @@ -648,125 +402,6 @@ var JSHINT = (function () { functions, // All of the functions global, // The global scope - htmltag = { - a: {}, - abbr: {}, - acronym: {}, - address: {}, - applet: {}, - area: {empty: true, parent: ' map '}, - article: {}, - aside: {}, - audio: {}, - b: {}, - base: {empty: true, parent: ' head '}, - bdo: {}, - big: {}, - blockquote: {}, - body: {parent: ' html noframes '}, - br: {empty: true}, - button: {}, - canvas: {parent: ' body p div th td '}, - caption: {parent: ' table '}, - center: {}, - cite: {}, - code: {}, - col: {empty: true, parent: ' table colgroup '}, - colgroup: {parent: ' table '}, - command: {parent: ' menu '}, - datalist: {}, - dd: {parent: ' dl '}, - del: {}, - details: {}, - dialog: {}, - dfn: {}, - dir: {}, - div: {}, - dl: {}, - dt: {parent: ' dl '}, - em: {}, - embed: {}, - fieldset: {}, - figure: {}, - font: {}, - footer: {}, - form: {}, - frame: {empty: true, parent: ' frameset '}, - frameset: {parent: ' html frameset '}, - h1: {}, - h2: {}, - h3: {}, - h4: {}, - h5: {}, - h6: {}, - head: {parent: ' html '}, - header: {}, - hgroup: {}, - hr: {empty: true}, - 'hta:application': - {empty: true, parent: ' head '}, - html: {parent: '*'}, - i: {}, - iframe: {}, - img: {empty: true}, - input: {empty: true}, - ins: {}, - kbd: {}, - keygen: {}, - label: {}, - legend: {parent: ' details fieldset figure '}, - li: {parent: ' dir menu ol ul '}, - link: {empty: true, parent: ' head '}, - map: {}, - mark: {}, - menu: {}, - meta: {empty: true, parent: ' head noframes noscript '}, - meter: {}, - nav: {}, - noframes: {parent: ' html body '}, - noscript: {parent: ' body head noframes '}, - object: {}, - ol: {}, - optgroup: {parent: ' select '}, - option: {parent: ' optgroup select '}, - output: {}, - p: {}, - param: {empty: true, parent: ' applet object '}, - pre: {}, - progress: {}, - q: {}, - rp: {}, - rt: {}, - ruby: {}, - samp: {}, - script: {empty: true, parent: ' body div frame head iframe p pre span '}, - section: {}, - select: {}, - small: {}, - span: {}, - source: {}, - strong: {}, - style: {parent: ' head ', empty: true}, - sub: {}, - sup: {}, - table: {}, - tbody: {parent: ' table '}, - td: {parent: ' tr '}, - textarea: {}, - tfoot: {parent: ' table '}, - th: {parent: ' tr '}, - thead: {parent: ' table '}, - time: {}, - title: {parent: ' head '}, - tr: {parent: ' table tbody thead tfoot '}, - tt: {}, - u: {}, - ul: {}, - 'var': {}, - video: {} - }, - - ids, // HTML ids implied, // Implied globals inblock, indent, @@ -781,11 +416,60 @@ var JSHINT = (function () { lookahead, member, membersOnly, + + mootools = { + '$' : false, + '$$' : false, + Assets : false, + Browser : false, + Chain : false, + Class : false, + Color : false, + Cookie : false, + Core : false, + Document : false, + DomReady : false, + DOMReady : false, + Drag : false, + Element : false, + Elements : false, + Event : false, + Events : false, + Fx : false, + Group : false, + Hash : false, + HtmlTable : false, + Iframe : false, + IframeShim : false, + InputValidator : false, + instanceOf : false, + Keyboard : false, + Locale : false, + Mask : false, + MooTools : false, + Native : false, + Options : false, + OverText : false, + Request : false, + Scroller : false, + Slick : false, + Slider : false, + Sortables : false, + Spinner : false, + Swiff : false, + Tips : false, + Type : false, + typeOf : false, + URI : false, + Window : false + }, + nexttoken, node = { __filename : false, __dirname : false, + exports : false, Buffer : false, GLOBAL : false, global : false, @@ -800,6 +484,47 @@ var JSHINT = (function () { prereg, prevtoken, + prototypejs = { + '$' : false, + '$$' : false, + '$A' : false, + '$F' : false, + '$H' : false, + '$R' : false, + '$break' : false, + '$continue' : false, + '$w' : false, + Abstract : false, + Ajax : false, + Class : false, + Enumerable : false, + Element : false, + Event : false, + Field : false, + Form : false, + Hash : false, + Insertion : false, + ObjectRange : false, + PeriodicalExecuter: false, + Position : false, + Prototype : false, + Selector : false, + Template : false, + Toggle : false, + Try : false, + Autocompleter : false, + Builder : false, + Control : false, + Draggable : false, + Draggables : false, + Droppables : false, + Effect : false, + Sortable : false, + SortableObserver : false, + Sound : false, + Scriptaculous : false + }, + rhino = { defineClass : false, deserialize : false, @@ -879,109 +604,6 @@ var JSHINT = (function () { urls, warnings, -// widget contains the global names which are provided to a Yahoo -// (fna Konfabulator) widget. - - widget = { - alert : true, - animator : true, - appleScript : true, - beep : true, - bytesToUIString : true, - Canvas : true, - chooseColor : true, - chooseFile : true, - chooseFolder : true, - closeWidget : true, - COM : true, - convertPathToHFS : true, - convertPathToPlatform : true, - CustomAnimation : true, - escape : true, - FadeAnimation : true, - filesystem : true, - Flash : true, - focusWidget : true, - form : true, - FormField : true, - Frame : true, - HotKey : true, - Image : true, - include : true, - isApplicationRunning : true, - iTunes : true, - konfabulatorVersion : true, - log : true, - md5 : true, - MenuItem : true, - MoveAnimation : true, - openURL : true, - play : true, - Point : true, - popupMenu : true, - preferenceGroups : true, - preferences : true, - print : true, - prompt : true, - random : true, - Rectangle : true, - reloadWidget : true, - ResizeAnimation : true, - resolvePath : true, - resumeUpdates : true, - RotateAnimation : true, - runCommand : true, - runCommandInBg : true, - saveAs : true, - savePreferences : true, - screen : true, - ScrollBar : true, - showWidgetPreferences : true, - sleep : true, - speak : true, - Style : true, - suppressUpdates : true, - system : true, - tellWidget : true, - Text : true, - TextArea : true, - Timer : true, - unescape : true, - updateNow : true, - URL : true, - Web : true, - widget : true, - Window : true, - XMLDOM : true, - XMLHttpRequest : true, - yahooCheckLogin : true, - yahooLogin : true, - yahooLogout : true - }, - - windows = { - ActiveXObject: false, - CScript : false, - Debug : false, - Enumerator : false, - System : false, - VBArray : false, - WScript : false - }, - -// xmode is used to adapt to the exceptions in html parsing. -// It can have these states: -// false .js script file -// html -// outer -// script -// style -// scriptstring -// styleproperty - - xmode, - xquote, - // Regular expressions. Some of these are stupidly long. // unsafe comment or string @@ -989,37 +611,18 @@ var JSHINT = (function () { // unsafe characters that are silently deleted by one or more browsers cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, // token - tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, -// html token - hx = /^\s*(['"=>\/&#]|<(?:\/|\!(?:--)?)?|[a-zA-Z][a-zA-Z0-9_\-:]*|[0-9]+|--)/, + tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint|jslint|members?|global)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/, // characters in strings that need escapement nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/, nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, -// outer html token - ox = /[>&]|<[\/!]?|--/, // star slash lx = /\*\/|\/\*/, // identifier ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/, // javascript url jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i, -// url badness - ux = /&|\+|\u00AD|\.\.|\/\*|%[^;]|base64|url|expression|data|mailto/i, -// style - sx = /^\s*([{:#%.=,>+\[\]@()"';]|\*=?|\$=|\|=|\^=|~=|[a-zA-Z_][a-zA-Z0-9_\-]*|[0-9]+|<\/|\/\*)/, - ssx = /^\s*([@#!"'};:\-%.=,+\[\]()*_]|[a-zA-Z][a-zA-Z0-9._\-]*|\/\*?|\d+(?:\.\d+)?|<\/)/, -// attributes characters - qx = /[^a-zA-Z0-9+\-_\/ ]/, -// query characters for ids - dx = /[\[\]\/\\"'*<>.&:(){}+=#]/, - - rx = { - outer: hx, - html: hx, - style: sx, - styleproperty: ssx - }; - +// catches /* falls through */ comments + ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; function F() {} // Used by Object.create @@ -1127,15 +730,15 @@ var JSHINT = (function () { } function assume() { - if (option.safe) - return; - if (option.couch) combine(predefined, couch); if (option.rhino) combine(predefined, rhino); + if (option.prototypejs) + combine(predefined, prototypejs); + if (option.node) combine(predefined, node); @@ -1148,11 +751,11 @@ var JSHINT = (function () { if (option.jquery) combine(predefined, jquery); - if (option.windows) - combine(predefined, windows); + if (option.mootools) + combine(predefined, mootools); - if (option.widget) - combine(predefined, widget); + if (option.globalstrict) + option.strict = true; } @@ -1228,25 +831,34 @@ var JSHINT = (function () { // Private lex methods function nextLine() { - var at; - if (line >= lines.length) { + var at, + tw; // trailing whitespace check + + if (line >= lines.length) return false; - } + character = 1; s = lines[line]; line += 1; at = s.search(/ \t/); - if (at >= 0) { + + if (at >= 0) warningAt("Mixed spaces and tabs.", line, at + 1); - } + s = s.replace(/\t/g, tab); at = s.search(cx); - if (at >= 0) { + + if (at >= 0) warningAt("Unsafe character.", line, at); - } - if (option.maxlen && option.maxlen < s.length) { + + if (option.maxlen && option.maxlen < s.length) warningAt("Line too long.", line, s.length); - } + + // Check for trailing whitespaces + tw = s.search(/\s+$/); + if (option.white && ~tw) + warningAt("Trailing whitespace.", line, tw); + return true; } @@ -1305,6 +917,12 @@ var JSHINT = (function () { } else { lines = source; } + + // If the first line is a shebang (#!), make it a blank and move on. + // Shebangs are used by Node scripts. + if (lines[0] && lines[0].substr(0, 2) == '#!') + lines[0] = ''; + line = 0; nextLine(); from = 1; @@ -1329,7 +947,6 @@ var JSHINT = (function () { s = s.slice(1); character += 1; return it('(range)', value); - case xquote: case '\\': warningAt("Unexpected '{a}'.", line, character, c); } @@ -1364,10 +981,6 @@ var JSHINT = (function () { line, character); } - if (xquote === x || (xmode === 'scriptstring' && !xquote)) { - return it('(punctuator)', x); - } - function esc(n) { var i = parseInt(s.substr(j + 1, n), 16); j += n; @@ -1382,7 +995,7 @@ var JSHINT = (function () { for (;;) { while (j >= s.length) { j = 0; - if (xmode !== 'html' || !nextLine()) { + if (!nextLine()) { errorAt("Unclosed string.", line, from); } } @@ -1398,82 +1011,52 @@ var JSHINT = (function () { } warningAt("Control character in string: {a}.", line, character + j, s.slice(0, j)); - } else if (c === xquote) { - warningAt("Bad HTML string", line, character + j); - } else if (c === '<') { - if (option.safe && xmode === 'html') { - warningAt("ADsafe string violation.", - line, character + j); - } else if (s.charAt(j + 1) === '/' && (xmode || option.safe)) { - warningAt("Expected '<\\/' and instead saw ' 0) { - character += 1; - s = s.slice(i); - break; - } else { - if (!nextLine()) { - return it('(end)', ''); - } - } - } - t = match(rx[xmode] || tx); + t = match(tx); if (!t) { t = ''; c = ''; @@ -1508,12 +1077,7 @@ var JSHINT = (function () { s = s.substr(1); } if (s) { - if (xmode === 'html') { - return it('(error)', s.charAt(0)); - } else { - errorAt("Unexpected '{a}'.", - line, character, s.substr(0, 1)); - } + errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); } } else { @@ -1526,20 +1090,18 @@ var JSHINT = (function () { // number if (c.isDigit()) { - if (xmode !== 'style' && !isFinite(Number(t))) { + if (!isFinite(Number(t))) { warningAt("Bad number '{a}'.", line, character, t); } - if (xmode !== 'style' && - xmode !== 'styleproperty' && - s.substr(0, 1).isAlpha()) { + if (s.substr(0, 1).isAlpha()) { warningAt("Missing space after '{a}'.", line, character, t); } if (c === '0') { d = t.substr(1, 1); if (d.isDigit()) { - if (token.id !== '.' && xmode !== 'styleproperty') { + if (token.id !== '.') { warningAt("Don't use extra leading zeros '{a}'.", line, character, t); } @@ -1565,12 +1127,8 @@ var JSHINT = (function () { // // comment case '//': - if (src || (xmode && xmode !== 'script')) { + if (src) { warningAt("Unexpected comment.", line, character); - } else if (xmode === 'script' && /<\s*\//i.test(s)) { - warningAt("Unexpected <\/ in comment.", line, character); - } else if ((option.safe || xmode === 'script') && ax.test(s)) { - warningAt("Dangerous comment.", line, character); } s = ''; token.comment = true; @@ -1579,12 +1137,9 @@ var JSHINT = (function () { // /* comment case '/*': - if (src || (xmode && xmode !== 'script' && xmode !== 'style' && xmode !== 'styleproperty')) { + if (src) { warningAt("Unexpected comment.", line, character); } - if (option.safe && ax.test(s)) { - warningAt("ADsafe comment violation.", line, character); - } for (;;) { i = s.search(lx); if (i >= 0) { @@ -1592,11 +1147,6 @@ var JSHINT = (function () { } if (!nextLine()) { errorAt("Unclosed comment.", line, character); - } else { - if (option.safe && ax.test(s)) { - warningAt("ADsafe comment violation.", - line, character); - } } } character += i + 2; @@ -1612,6 +1162,7 @@ var JSHINT = (function () { case '/*members': case '/*member': case '/*jshint': + case '/*jslint': case '/*global': case '*/': return { @@ -1779,13 +1330,6 @@ klass: do { q = true; break; case '<': - if (xmode === 'script') { - c = s.charAt(l); - if (c === '!' || c === '/') { - warningAt( -"HTML confusion in regular expression '<{a}'.", line, from + l, c); - } - } q = true; break; default: @@ -1807,15 +1351,6 @@ klass: do { case '*': warningAt("Unescaped '{a}'.", line, from + l, c); - break; - case '<': - if (xmode === 'script') { - c = s.charAt(l); - if (c === '!' || c === '/') { - warningAt( -"HTML confusion in regular expression '<{a}'.", line, from + l, c); - } - } } if (b) { switch (s.charAt(l)) { @@ -1887,75 +1422,9 @@ klass: do { // punctuator - case '.", line, character); - } - character += 3; - s = s.slice(i + 3); - break; case '#': - if (xmode === 'html' || xmode === 'styleproperty') { - for (;;) { - c = s.charAt(0); - if ((c < '0' || c > '9') && - (c < 'a' || c > 'f') && - (c < 'A' || c > 'F')) { - break; - } - character += 1; - s = s.substr(1); - t += c; - } - if (t.length !== 4 && t.length !== 7) { - warningAt("Bad hex color '{a}'.", line, - from + l, t); - } - return it('(color)', t); - } return it('(punctuator)', t); default: - if (xmode === 'outer' && c === '&') { - character += 1; - s = s.substr(1); - for (;;) { - c = s.charAt(0); - character += 1; - s = s.substr(1); - if (c === ';') { - break; - } - if (!((c >= '0' && c <= '9') || - (c >= 'a' && c <= 'z') || - c === '#')) { - errorAt("Bad entity", line, from + l, - character); - } - } - break; - } return it('(punctuator)', t); } } @@ -1967,26 +1436,28 @@ klass: do { function addlabel(t, type) { - if (option.safe && funct['(global)'] && - typeof predefined[t] !== 'boolean') { - warning('ADsafe global: ' + t + '.', token); - } else if (t === 'hasOwnProperty') { + if (t === 'hasOwnProperty') { warning("'hasOwnProperty' is a really bad name."); } // Define t in the current function in the current scope. if (is_own(funct, t) && !funct['(global)']) { - warning(funct[t] === true ? - "'{a}' was used before it was defined." : - "'{a}' is already defined.", - nexttoken, t); + if (funct[t] === true) { + if (option.latedef) + warning("'{a}' was used before it was defined.", nexttoken, t); + } else { + if (!option.shadow) + warning("'{a}' is already defined.", nexttoken, t); + } } + funct[t] = type; if (funct['(global)']) { global[t] = funct; if (is_own(implied, t)) { - warning("'{a}' was used before it was defined.", nexttoken, t); + if (option.latedef) + warning("'{a}' was used before it was defined.", nexttoken, t); delete implied[t]; } } else { @@ -2010,16 +1481,11 @@ klass: do { obj = membersOnly; break; case '/*jshint': - if (option.safe) { - warning("ADsafe restriction."); - } + case '/*jslint': obj = option; filter = boolOptions; break; case '/*global': - if (option.safe) { - warning("ADsafe restriction."); - } obj = predefined; break; default: @@ -2047,7 +1513,7 @@ loop: for (;;) { error("Expected '{a}' and instead saw '{b}'.", t, '*/', ':'); } - if (t.value === 'indent' && o === '/*jshint') { + if (t.value === 'indent' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { @@ -2056,7 +1522,7 @@ loop: for (;;) { } obj.white = true; obj.indent = b; - } else if (t.value === 'maxerr' && o === '/*jshint') { + } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { @@ -2064,7 +1530,7 @@ loop: for (;;) { v, v.value); } obj.maxerr = b; - } else if (t.value === 'maxlen' && o === '/*jshint') { + } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { @@ -2081,7 +1547,7 @@ loop: for (;;) { } t = lex.token(); } else { - if (o === '/*jshint') { + if (o === '/*jshint' || o === '/*jslint') { error("Missing option value.", t); } obj[t.value] = false; @@ -2121,8 +1587,7 @@ loop: for (;;) { switch (token.id) { case '(number)': if (nexttoken.id === '.') { - warning( -"A dot following a number can be confused with a decimal point.", token); + warning("A dot following a number can be confused with a decimal point.", token); } break; case '-': @@ -2145,8 +1610,7 @@ loop: for (;;) { if (nexttoken.id === '(end)') { warning("Unmatched '{a}'.", t, t.id); } else { - warning( -"Expected '{a}' to matchd '{b}' from line {c} and instead saw '{d}'.", + warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", nexttoken, id, t.id, t.line, nexttoken.value); } } else if (nexttoken.type !== '(identifier)' || @@ -2188,15 +1652,12 @@ loop: for (;;) { // They are elements of the parsing method called Top Down Operator Precedence. function expression(rbp, initial) { - var left; - if (nexttoken.id === '(end)') { + var left, isArray = false; + + if (nexttoken.id === '(end)') error("Unexpected early end of program.", token); - } + advance(); - if (option.safe && typeof predefined[token.value] === 'boolean' && - (nexttoken.id !== '(' && nexttoken.id !== '.')) { - warning('ADsafe violation.', token); - } if (initial) { anonname = 'anonymous'; funct['(verb)'] = token.value; @@ -2208,8 +1669,7 @@ loop: for (;;) { left = token.nud(); } else { if (nexttoken.type === '(number)' && token.id === '.') { - warning( -"A leading decimal point can be confused with a dot: '.{a}'.", + warning("A leading decimal point can be confused with a dot: '.{a}'.", token, nexttoken.value); advance(); return token; @@ -2219,7 +1679,10 @@ loop: for (;;) { } } while (rbp < nexttoken.lbp) { + isArray = token.value == 'Array'; advance(); + if (isArray && token.id == '(' && nexttoken.id == ')') + warning("Use the array literal notation [].", token); if (token.led) { left = token.led(left); } else { @@ -2237,7 +1700,7 @@ loop: for (;;) { function adjacent(left, right) { left = left || token; right = right || nexttoken; - if (option.white || xmode === 'styleproperty' || xmode === 'style') { + if (option.white) { if (left.character !== right.from && left.line === right.line) { warning("Unexpected space after '{a}'.", right, left.value); } @@ -2460,7 +1923,7 @@ loop: for (;;) { return node && ((node.type === '(number)' && +node.value === 0) || (node.type === '(string)' && node.value === '') || - (node.type === 'null' && !option.boss) || + (node.type === 'null' && !option.eqnull) || node.type === 'true' || node.type === 'false' || node.type === 'undefined'); @@ -2478,15 +1941,6 @@ loop: for (;;) { } else if (left['function']) { warning("'{a}' is a function.", left, left.value); } - if (option.safe) { - l = left; - do { - if (typeof predefined[l.value] === 'boolean') { - warning('ADsafe violation.', l); - } - l = l.left; - } while (l); - } if (left) { if (left.id === '.' || left.id === '[') { if (!left.left || left.left.value === 'arguments') { @@ -2574,9 +2028,7 @@ loop: for (;;) { function optionalidentifier(fnparam) { if (nexttoken.identifier) { advance(); - if (option.safe && banned[token.value]) { - warning("ADsafe violation: '{a}'.", token, token.value); - } else if (token.reserved && !option.es5) { + if (token.reserved && !option.es5) { // `undefined` as a function param is a common pattern to protect // against the case when somebody does `undefined = true` and // help with minification. More info: https://gist.github.com/315916 @@ -2597,7 +2049,7 @@ loop: for (;;) { return i; } if (token.id === 'function' && nexttoken.id === '(') { - warning("Missing name in function statement."); + warning("Missing name in function declaration."); } else { error("Expected an identifier and instead saw '{a}'.", nexttoken, nexttoken.value); @@ -2669,20 +2121,15 @@ loop: for (;;) { // Look for the final semicolon. if (!t.block) { - if (!r || !r.exps) { - if (r.value === '&&') { - adjacent(token, nexttoken); - advance(')'); - nonadjacent(token, nexttoken); - } - else { + if (!option.expr && (!r || !r.exps)) { warning("Expected an assignment or function call and instead saw an expression.", token); - } } else if (option.nonew && r.id === '(' && r.left.id === 'new') { warning("Do not use 'new' for side effects."); } if (nexttoken.id !== ';') { - warningAt("Missing semicolon.", token.line, token.from + token.value.length); + if (!option.asi) { + warningAt("Missing semicolon.", token.line, token.from + token.value.length); + } } else { adjacent(token, nexttoken); advance(';'); @@ -2717,66 +2164,7 @@ loop: for (;;) { function statements(begin) { var a = [], f, p; - if (option.adsafe) { - switch (begin) { - case 'script': - -// JSHint is also the static analizer for ADsafe. See www.ADsafe.org. - - if (!adsafe_may) { - if (nexttoken.value !== 'ADSAFE' || - peek(0).id !== '.' || - (peek(1).value !== 'id' && - peek(1).value !== 'go')) { - error('ADsafe violation: Missing ADSAFE.id or ADSAFE.go.', - nexttoken); - } - } - if (nexttoken.value === 'ADSAFE' && - peek(0).id === '.' && - peek(1).value === 'id') { - if (adsafe_may) { - error('ADsafe violation.', nexttoken); - } - advance('ADSAFE'); - advance('.'); - advance('id'); - advance('('); - if (nexttoken.value !== adsafe_id) { - error('ADsafe violation: id does not match.', nexttoken); - } - advance('(string)'); - advance(')'); - advance(';'); - adsafe_may = true; - } - break; - case 'lib': - if (nexttoken.value === 'ADSAFE') { - advance('ADSAFE'); - advance('.'); - advance('lib'); - advance('('); - advance('(string)'); - comma(); - f = expression(0); - if (f.id !== 'function') { - error('The second argument to lib must be a function.', f); - } - p = f.funct['(params)']; - p = p && p.join(', '); - if (p && p !== 'lib') { - error("Expected '{a}' and instead saw '{b}'.", - f, '(lib)', '(' + p + ')'); - } - advance(')'); - advance(';'); - return a; - } else { - error("ADsafe lib violation."); - } - } - } + while (!nexttoken.reach && nexttoken.id !== '(end)') { if (nexttoken.id === ';') { warning("Unnecessary semicolon."); @@ -2874,1368 +2262,74 @@ loop: for (;;) { } } +// Build the syntax table by declaring the syntactic elements of the language. -// CSS parsing. + type('(number)', function () { + return this; + }); + type('(string)', function () { + return this; + }); - function cssName() { - if (nexttoken.identifier) { - advance(); - return true; - } - } + syntax['(identifier)'] = { + type: '(identifier)', + lbp: 0, + identifier: true, + nud: function () { + var v = this.value, + s = scope[v], + f; + if (typeof s === 'function') { +// Protection against accidental inheritance. - function cssNumber() { - if (nexttoken.id === '-') { - advance('-'); - adjacent(); - nolinebreak(); - } - if (nexttoken.type === '(number)') { - advance('(number)'); - return true; - } - } + s = undefined; + } else if (typeof s === 'boolean') { + f = funct; + funct = functions[0]; + addlabel(v, 'var'); + s = funct; + funct = f; + } +// The name is in scope and defined in the current function. - function cssString() { - if (nexttoken.type === '(string)') { - advance(); - return true; - } - } + if (funct === s) { +// Change 'unused' to 'var', and reject labels. - function cssColor() { - var i, number, value; - if (nexttoken.identifier) { - value = nexttoken.value; - if (value === 'rgb' || value === 'rgba') { - advance(); - advance('('); - for (i = 0; i < 3; i += 1) { - if (i) { - advance(','); - } - number = nexttoken.value; - if (nexttoken.type !== '(number)' || number < 0) { - warning("Expected a positive number and instead saw '{a}'", - nexttoken, number); - advance(); - } else { - advance(); - if (nexttoken.id === '%') { - advance('%'); - if (number > 100) { - warning("Expected a percentage and instead saw '{a}'", - token, number); - } - } else { - if (number > 255) { - warning("Expected a small number and instead saw '{a}'", - token, number); - } - } - } - } - if (value === 'rgba') { - advance(','); - number = +nexttoken.value; - if (nexttoken.type !== '(number)' || number < 0 || number > 1) { - warning("Expected a number between 0 and 1 and instead saw '{a}'", - nexttoken, number); - } - advance(); - if (nexttoken.id === '%') { - warning("Unexpected '%'."); - advance('%'); - } + switch (funct[v]) { + case 'unused': + funct[v] = 'var'; + break; + case 'unction': + funct[v] = 'function'; + this['function'] = true; + break; + case 'function': + this['function'] = true; + break; + case 'label': + warning("'{a}' is a statement label.", token, v); + break; } - advance(')'); - return true; - } else if (cssColorData[nexttoken.value] === true) { - advance(); - return true; - } - } else if (nexttoken.type === '(color)') { - advance(); - return true; - } - return false; - } - - - function cssLength() { - if (nexttoken.id === '-') { - advance('-'); - adjacent(); - nolinebreak(); - } - if (nexttoken.type === '(number)') { - advance(); - if (nexttoken.type !== '(string)' && - cssLengthData[nexttoken.value] === true) { - adjacent(); - advance(); - } else if (+token.value !== 0) { - warning("Expected a linear unit and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - return true; - } - return false; - } +// The name is not defined in the function. If we are in the global scope, +// then we have an undefined variable. +// +// Operators typeof and delete do not raise runtime errors even if the base +// object of a reference is null so no need to display warning if we're +// inside of typeof or delete. - function cssLineHeight() { - if (nexttoken.id === '-') { - advance('-'); - adjacent(); - } - if (nexttoken.type === '(number)') { - advance(); - if (nexttoken.type !== '(string)' && - cssLengthData[nexttoken.value] === true) { - adjacent(); - advance(); - } - return true; - } - return false; - } + } else if (funct['(global)']) { + if (anonname != 'typeof' && anonname != 'delete' && + option.undef && typeof predefined[v] !== 'boolean') { + warning("'{a}' is not defined.", token, v); + } + note_implied(token); - - function cssWidth() { - if (nexttoken.identifier) { - switch (nexttoken.value) { - case 'thin': - case 'medium': - case 'thick': - advance(); - return true; - } - } else { - return cssLength(); - } - } - - - function cssMargin() { - if (nexttoken.identifier) { - if (nexttoken.value === 'auto') { - advance(); - return true; - } - } else { - return cssLength(); - } - } - - function cssAttr() { - if (nexttoken.identifier && nexttoken.value === 'attr') { - advance(); - advance('('); - if (!nexttoken.identifier) { - warning("Expected a name and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - advance(')'); - return true; - } - return false; - } - - - function cssCommaList() { - while (nexttoken.id !== ';') { - if (!cssName() && !cssString()) { - warning("Expected a name and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - if (nexttoken.id !== ',') { - return true; - } - comma(); - } - } - - - function cssCounter() { - if (nexttoken.identifier && nexttoken.value === 'counter') { - advance(); - advance('('); - advance(); - if (nexttoken.id === ',') { - comma(); - if (nexttoken.type !== '(string)') { - warning("Expected a string and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - } - advance(')'); - return true; - } - if (nexttoken.identifier && nexttoken.value === 'counters') { - advance(); - advance('('); - if (!nexttoken.identifier) { - warning("Expected a name and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - if (nexttoken.id === ',') { - comma(); - if (nexttoken.type !== '(string)') { - warning("Expected a string and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - } - if (nexttoken.id === ',') { - comma(); - if (nexttoken.type !== '(string)') { - warning("Expected a string and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - } - advance(')'); - return true; - } - return false; - } - - - function cssShape() { - var i; - if (nexttoken.identifier && nexttoken.value === 'rect') { - advance(); - advance('('); - for (i = 0; i < 4; i += 1) { - if (!cssLength()) { - warning("Expected a number and instead saw '{a}'.", - nexttoken, nexttoken.value); - break; - } - } - advance(')'); - return true; - } - return false; - } - - - function cssUrl() { - var c, url; - if (nexttoken.identifier && nexttoken.value === 'url') { - nexttoken = lex.range('(', ')'); - url = nexttoken.value; - c = url.charAt(0); - if (c === '"' || c === '\'') { - if (url.slice(-1) !== c) { - warning("Bad url string."); - } else { - url = url.slice(1, -1); - if (url.indexOf(c) >= 0) { - warning("Bad url string."); - } - } - } - if (!url) { - warning("Missing url."); - } - advance(); - if (option.safe && ux.test(url)) { - error("ADsafe URL violation."); - } - urls.push(url); - return true; - } - return false; - } - - - cssAny = [cssUrl, function () { - for (;;) { - if (nexttoken.identifier) { - switch (nexttoken.value.toLowerCase()) { - case 'url': - cssUrl(); - break; - case 'expression': - warning("Unexpected expression '{a}'.", - nexttoken, nexttoken.value); - advance(); - break; - default: - advance(); - } - } else { - if (nexttoken.id === ';' || nexttoken.id === '!' || - nexttoken.id === '(end)' || nexttoken.id === '}') { - return true; - } - advance(); - } - } - }]; - - - cssBorderStyle = [ - 'none', 'dashed', 'dotted', 'double', 'groove', - 'hidden', 'inset', 'outset', 'ridge', 'solid' - ]; - - cssBreak = [ - 'auto', 'always', 'avoid', 'left', 'right' - ]; - - cssMedia = { - 'all': true, - 'braille': true, - 'embossed': true, - 'handheld': true, - 'print': true, - 'projection': true, - 'screen': true, - 'speech': true, - 'tty': true, - 'tv': true - }; - - cssOverflow = [ - 'auto', 'hidden', 'scroll', 'visible' - ]; - - cssAttributeData = { - background: [ - true, 'background-attachment', 'background-color', - 'background-image', 'background-position', 'background-repeat' - ], - 'background-attachment': ['scroll', 'fixed'], - 'background-color': ['transparent', cssColor], - 'background-image': ['none', cssUrl], - 'background-position': [ - 2, [cssLength, 'top', 'bottom', 'left', 'right', 'center'] - ], - 'background-repeat': [ - 'repeat', 'repeat-x', 'repeat-y', 'no-repeat' - ], - 'border': [true, 'border-color', 'border-style', 'border-width'], - 'border-bottom': [ - true, 'border-bottom-color', 'border-bottom-style', - 'border-bottom-width' - ], - 'border-bottom-color': cssColor, - 'border-bottom-style': cssBorderStyle, - 'border-bottom-width': cssWidth, - 'border-collapse': ['collapse', 'separate'], - 'border-color': ['transparent', 4, cssColor], - 'border-left': [ - true, 'border-left-color', 'border-left-style', 'border-left-width' - ], - 'border-left-color': cssColor, - 'border-left-style': cssBorderStyle, - 'border-left-width': cssWidth, - 'border-right': [ - true, 'border-right-color', 'border-right-style', - 'border-right-width' - ], - 'border-right-color': cssColor, - 'border-right-style': cssBorderStyle, - 'border-right-width': cssWidth, - 'border-spacing': [2, cssLength], - 'border-style': [4, cssBorderStyle], - 'border-top': [ - true, 'border-top-color', 'border-top-style', 'border-top-width' - ], - 'border-top-color': cssColor, - 'border-top-style': cssBorderStyle, - 'border-top-width': cssWidth, - 'border-width': [4, cssWidth], - bottom: [cssLength, 'auto'], - 'caption-side' : ['bottom', 'left', 'right', 'top'], - clear: ['both', 'left', 'none', 'right'], - clip: [cssShape, 'auto'], - color: cssColor, - content: [ - 'open-quote', 'close-quote', 'no-open-quote', 'no-close-quote', - cssString, cssUrl, cssCounter, cssAttr - ], - 'counter-increment': [ - cssName, 'none' - ], - 'counter-reset': [ - cssName, 'none' - ], - cursor: [ - cssUrl, 'auto', 'crosshair', 'default', 'e-resize', 'help', 'move', - 'n-resize', 'ne-resize', 'nw-resize', 'pointer', 's-resize', - 'se-resize', 'sw-resize', 'w-resize', 'text', 'wait' - ], - direction: ['ltr', 'rtl'], - display: [ - 'block', 'compact', 'inline', 'inline-block', 'inline-table', - 'list-item', 'marker', 'none', 'run-in', 'table', 'table-caption', - 'table-cell', 'table-column', 'table-column-group', - 'table-footer-group', 'table-header-group', 'table-row', - 'table-row-group' - ], - 'empty-cells': ['show', 'hide'], - 'float': ['left', 'none', 'right'], - font: [ - 'caption', 'icon', 'menu', 'message-box', 'small-caption', - 'status-bar', true, 'font-size', 'font-style', 'font-weight', - 'font-family' - ], - 'font-family': cssCommaList, - 'font-size': [ - 'xx-small', 'x-small', 'small', 'medium', 'large', 'x-large', - 'xx-large', 'larger', 'smaller', cssLength - ], - 'font-size-adjust': ['none', cssNumber], - 'font-stretch': [ - 'normal', 'wider', 'narrower', 'ultra-condensed', - 'extra-condensed', 'condensed', 'semi-condensed', - 'semi-expanded', 'expanded', 'extra-expanded' - ], - 'font-style': [ - 'normal', 'italic', 'oblique' - ], - 'font-variant': [ - 'normal', 'small-caps' - ], - 'font-weight': [ - 'normal', 'bold', 'bolder', 'lighter', cssNumber - ], - height: [cssLength, 'auto'], - left: [cssLength, 'auto'], - 'letter-spacing': ['normal', cssLength], - 'line-height': ['normal', cssLineHeight], - 'list-style': [ - true, 'list-style-image', 'list-style-position', 'list-style-type' - ], - 'list-style-image': ['none', cssUrl], - 'list-style-position': ['inside', 'outside'], - 'list-style-type': [ - 'circle', 'disc', 'square', 'decimal', 'decimal-leading-zero', - 'lower-roman', 'upper-roman', 'lower-greek', 'lower-alpha', - 'lower-latin', 'upper-alpha', 'upper-latin', 'hebrew', 'katakana', - 'hiragana-iroha', 'katakana-oroha', 'none' - ], - margin: [4, cssMargin], - 'margin-bottom': cssMargin, - 'margin-left': cssMargin, - 'margin-right': cssMargin, - 'margin-top': cssMargin, - 'marker-offset': [cssLength, 'auto'], - 'max-height': [cssLength, 'none'], - 'max-width': [cssLength, 'none'], - 'min-height': cssLength, - 'min-width': cssLength, - opacity: cssNumber, - outline: [true, 'outline-color', 'outline-style', 'outline-width'], - 'outline-color': ['invert', cssColor], - 'outline-style': [ - 'dashed', 'dotted', 'double', 'groove', 'inset', 'none', - 'outset', 'ridge', 'solid' - ], - 'outline-width': cssWidth, - overflow: cssOverflow, - 'overflow-x': cssOverflow, - 'overflow-y': cssOverflow, - padding: [4, cssLength], - 'padding-bottom': cssLength, - 'padding-left': cssLength, - 'padding-right': cssLength, - 'padding-top': cssLength, - 'page-break-after': cssBreak, - 'page-break-before': cssBreak, - position: ['absolute', 'fixed', 'relative', 'static'], - quotes: [8, cssString], - right: [cssLength, 'auto'], - 'table-layout': ['auto', 'fixed'], - 'text-align': ['center', 'justify', 'left', 'right'], - 'text-decoration': [ - 'none', 'underline', 'overline', 'line-through', 'blink' - ], - 'text-indent': cssLength, - 'text-shadow': ['none', 4, [cssColor, cssLength]], - 'text-transform': ['capitalize', 'uppercase', 'lowercase', 'none'], - top: [cssLength, 'auto'], - 'unicode-bidi': ['normal', 'embed', 'bidi-override'], - 'vertical-align': [ - 'baseline', 'bottom', 'sub', 'super', 'top', 'text-top', 'middle', - 'text-bottom', cssLength - ], - visibility: ['visible', 'hidden', 'collapse'], - 'white-space': [ - 'normal', 'nowrap', 'pre', 'pre-line', 'pre-wrap', 'inherit' - ], - width: [cssLength, 'auto'], - 'word-spacing': ['normal', cssLength], - 'word-wrap': ['break-word', 'normal'], - 'z-index': ['auto', cssNumber] - }; - - function styleAttribute() { - var v; - while (nexttoken.id === '*' || nexttoken.id === '#' || - nexttoken.value === '_') { - if (!option.css) { - warning("Unexpected '{a}'.", nexttoken, nexttoken.value); - } - advance(); - } - if (nexttoken.id === '-') { - if (!option.css) { - warning("Unexpected '{a}'.", nexttoken, nexttoken.value); - } - advance('-'); - if (!nexttoken.identifier) { - warning( -"Expected a non-standard style attribute and instead saw '{a}'.", - nexttoken, nexttoken.value); - } - advance(); - return cssAny; - } else { - if (!nexttoken.identifier) { - warning("Excepted a style attribute, and instead saw '{a}'.", - nexttoken, nexttoken.value); - } else { - if (is_own(cssAttributeData, nexttoken.value)) { - v = cssAttributeData[nexttoken.value]; - } else { - v = cssAny; - if (!option.css) { - warning("Unrecognized style attribute '{a}'.", - nexttoken, nexttoken.value); - } - } - } - advance(); - return v; - } - } - - - function styleValue(v) { - var i = 0, - n, - once, - match, - round, - start = 0, - vi; - switch (typeof v) { - case 'function': - return v(); - case 'string': - if (nexttoken.identifier && nexttoken.value === v) { - advance(); - return true; - } - return false; - } - for (;;) { - if (i >= v.length) { - return false; - } - vi = v[i]; - i += 1; - if (vi === true) { - break; - } else if (typeof vi === 'number') { - n = vi; - vi = v[i]; - i += 1; - } else { - n = 1; - } - match = false; - while (n > 0) { - if (styleValue(vi)) { - match = true; - n -= 1; - } else { - break; - } - } - if (match) { - return true; - } - } - start = i; - once = []; - for (;;) { - round = false; - for (i = start; i < v.length; i += 1) { - if (!once[i]) { - if (styleValue(cssAttributeData[v[i]])) { - match = true; - round = true; - once[i] = true; - break; - } - } - } - if (!round) { - return match; - } - } - } - - function styleChild() { - if (nexttoken.id === '(number)') { - advance(); - if (nexttoken.value === 'n' && nexttoken.identifier) { - adjacent(); - advance(); - if (nexttoken.id === '+') { - adjacent(); - advance('+'); - adjacent(); - advance('(number)'); - } - } - return; - } else { - switch (nexttoken.value) { - case 'odd': - case 'even': - if (nexttoken.identifier) { - advance(); - return; - } - } - } - warning("Unexpected token '{a}'.", nexttoken, nexttoken.value); - } - - function substyle() { - var v; - for (;;) { - if (nexttoken.id === '}' || nexttoken.id === '(end)' || - xquote && nexttoken.id === xquote) { - return; - } - while (nexttoken.id === ';') { - warning("Misplaced ';'."); - advance(';'); - } - v = styleAttribute(); - advance(':'); - if (nexttoken.identifier && nexttoken.value === 'inherit') { - advance(); - } else { - if (!styleValue(v)) { - warning("Unexpected token '{a}'.", nexttoken, - nexttoken.value); - advance(); - } - } - if (nexttoken.id === '!') { - advance('!'); - adjacent(); - if (nexttoken.identifier && nexttoken.value === 'important') { - advance(); - } else { - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, 'important', nexttoken.value); - } - } - if (nexttoken.id === '}' || nexttoken.id === xquote) { - warning("Missing '{a}'.", nexttoken, ';'); - } else { - advance(';'); - } - } - } - - function styleSelector() { - if (nexttoken.identifier) { - if (!is_own(htmltag, option.cap ? - nexttoken.value.toLowerCase() : nexttoken.value)) { - warning("Expected a tagName, and instead saw {a}.", - nexttoken, nexttoken.value); - } - advance(); - } else { - switch (nexttoken.id) { - case '>': - case '+': - advance(); - styleSelector(); - break; - case ':': - advance(':'); - switch (nexttoken.value) { - case 'active': - case 'after': - case 'before': - case 'checked': - case 'disabled': - case 'empty': - case 'enabled': - case 'first-child': - case 'first-letter': - case 'first-line': - case 'first-of-type': - case 'focus': - case 'hover': - case 'last-child': - case 'last-of-type': - case 'link': - case 'only-of-type': - case 'root': - case 'target': - case 'visited': - advance(); - break; - case 'lang': - advance(); - advance('('); - if (!nexttoken.identifier) { - warning("Expected a lang code, and instead saw :{a}.", - nexttoken, nexttoken.value); - } - advance(')'); - break; - case 'nth-child': - case 'nth-last-child': - case 'nth-last-of-type': - case 'nth-of-type': - advance(); - advance('('); - styleChild(); - advance(')'); - break; - case 'not': - advance(); - advance('('); - if (nexttoken.id === ':' && peek(0).value === 'not') { - warning("Nested not."); - } - styleSelector(); - advance(')'); - break; - default: - warning("Expected a pseudo, and instead saw :{a}.", - nexttoken, nexttoken.value); - } - break; - case '#': - advance('#'); - if (!nexttoken.identifier) { - warning("Expected an id, and instead saw #{a}.", - nexttoken, nexttoken.value); - } - advance(); - break; - case '*': - advance('*'); - break; - case '.': - advance('.'); - if (!nexttoken.identifier) { - warning("Expected a class, and instead saw #.{a}.", - nexttoken, nexttoken.value); - } - advance(); - break; - case '[': - advance('['); - if (!nexttoken.identifier) { - warning("Expected an attribute, and instead saw [{a}].", - nexttoken, nexttoken.value); - } - advance(); - if (nexttoken.id === '=' || nexttoken.value === '~=' || - nexttoken.value === '$=' || - nexttoken.value === '|=' || - nexttoken.id === '*=' || - nexttoken.id === '^=') { - advance(); - if (nexttoken.type !== '(string)') { - warning("Expected a string, and instead saw {a}.", - nexttoken, nexttoken.value); - } - advance(); - } - advance(']'); - break; - default: - error("Expected a CSS selector, and instead saw {a}.", - nexttoken, nexttoken.value); - } - } - } - - function stylePattern() { - if (nexttoken.id === '{') { - warning("Expected a style pattern, and instead saw '{a}'.", nexttoken, - nexttoken.id); - } - for (;;) { - styleSelector(); - if (nexttoken.id === ' fragments and .js files.", token); - } - if (option.fragment) { - if (n !== 'div') { - error("ADsafe violation: Wrap the widget in a div.", token); - } - } else { - error("Use the fragment option.", token); - } - } - option.browser = true; - assume(); - } - - function doAttribute(n, a, v) { - var u, x; - if (a === 'id') { - u = typeof v === 'string' ? v.toUpperCase() : ''; - if (ids[u] === true) { - warning("Duplicate id='{a}'.", nexttoken, v); - } - if (!/^[A-Za-z][A-Za-z0-9._:\-]*$/.test(v)) { - warning("Bad id: '{a}'.", nexttoken, v); - } else if (option.adsafe) { - if (adsafe_id) { - if (v.slice(0, adsafe_id.length) !== adsafe_id) { - warning("ADsafe violation: An id must have a '{a}' prefix", - nexttoken, adsafe_id); - } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { - warning("ADSAFE violation: bad id."); - } - } else { - adsafe_id = v; - if (!/^[A-Z]+_$/.test(v)) { - warning("ADSAFE violation: bad id."); - } - } - } - x = v.search(dx); - if (x >= 0) { - warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); - } - ids[u] = true; - } else if (a === 'class' || a === 'type' || a === 'name') { - x = v.search(qx); - if (x >= 0) { - warning("Unexpected character '{a}' in {b}.", token, v.charAt(x), a); - } - ids[u] = true; - } else if (a === 'href' || a === 'background' || - a === 'content' || a === 'data' || - a.indexOf('src') >= 0 || a.indexOf('url') >= 0) { - if (option.safe && ux.test(v)) { - error("ADsafe URL violation."); - } - urls.push(v); - } else if (a === 'for') { - if (option.adsafe) { - if (adsafe_id) { - if (v.slice(0, adsafe_id.length) !== adsafe_id) { - warning("ADsafe violation: An id must have a '{a}' prefix", - nexttoken, adsafe_id); - } else if (!/^[A-Z]+_[A-Z]+$/.test(v)) { - warning("ADSAFE violation: bad id."); - } - } else { - warning("ADSAFE violation: bad id."); - } - } - } else if (a === 'name') { - if (option.adsafe && v.indexOf('_') >= 0) { - warning("ADsafe name violation."); - } - } - } - - function doTag(n, a) { - var i, t = htmltag[n], x; - src = false; - if (!t) { - error("Unrecognized tag '<{a}>'.", - nexttoken, - n === n.toLowerCase() ? n : - n + ' (capitalization error)'); - } - if (stack.length > 0) { - if (n === 'html') { - error("Too many tags.", token); - } - x = t.parent; - if (x) { - if (x.indexOf(' ' + stack[stack.length - 1].name + ' ') < 0) { - error("A '<{a}>' must be within '<{b}>'.", - token, n, x); - } - } else if (!option.adsafe && !option.fragment) { - i = stack.length; - do { - if (i <= 0) { - error("A '<{a}>' must be within '<{b}>'.", - token, n, 'body'); - } - i -= 1; - } while (stack[i].name !== 'body'); - } - } - switch (n) { - case 'div': - if (option.adsafe && stack.length === 1 && !adsafe_id) { - warning("ADSAFE violation: missing ID_."); - } - break; - case 'script': - xmode = 'script'; - advance('>'); - indent = nexttoken.from; - if (a.lang) { - warning("lang is deprecated.", token); - } - if (option.adsafe && stack.length !== 1) { - warning("ADsafe script placement violation.", token); - } - if (a.src) { - if (option.adsafe && (!adsafe_may || !approved[a.src])) { - warning("ADsafe unapproved script source.", token); - } - if (a.type) { - warning("type is unnecessary.", token); - } - } else { - if (adsafe_went) { - error("ADsafe script violation.", token); - } - use_strict(); - statements('script'); - } - xmode = 'html'; - advance(''); - styles(); - xmode = 'html'; - advance(''; - } - - function html() { - var a, attributes, e, n, q, t, v, w = option.white, wmode; - xmode = 'html'; - xquote = ''; - stack = null; - for (;;) { - switch (nexttoken.value) { - case '<': - xmode = 'html'; - advance('<'); - attributes = {}; - t = nexttoken; - if (!t.identifier) { - warning("Bad identifier {a}.", t, t.value); - } - n = t.value; - if (option.cap) { - n = n.toLowerCase(); - } - t.name = n; - advance(); - if (!stack) { - stack = []; - doBegin(n); - } - v = htmltag[n]; - if (typeof v !== 'object') { - error("Unrecognized tag '<{a}>'.", t, n); - } - e = v.empty; - t.type = n; - for (;;) { - if (nexttoken.id === '/') { - advance('/'); - if (nexttoken.id !== '>') { - warning("Expected '{a}' and instead saw '{b}'.", - nexttoken, '>', nexttoken.value); - } - break; - } - if (nexttoken.id && nexttoken.id.substr(0, 1) === '>') { - break; - } - if (!nexttoken.identifier) { - if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { - error("Missing '>'.", nexttoken); - } - warning("Bad identifier."); - } - option.white = true; - nonadjacent(token, nexttoken); - a = nexttoken.value; - option.white = w; - advance(); - if (!option.cap && a !== a.toLowerCase()) { - warning("Attribute '{a}' not all lower case.", nexttoken, a); - } - a = a.toLowerCase(); - xquote = ''; - if (is_own(attributes, a)) { - warning("Attribute '{a}' repeated.", nexttoken, a); - } - if (a.slice(0, 2) === 'on') { - if (!option.on) { - warning("Avoid HTML event handlers."); - } - xmode = 'scriptstring'; - advance('='); - q = nexttoken.id; - if (q !== '"' && q !== "'") { - error("Missing quote."); - } - xquote = q; - wmode = option.white; - option.white = false; - advance(q); - use_strict(); - statements('on'); - option.white = wmode; - if (nexttoken.id !== q) { - error("Missing close quote on script attribute."); - } - xmode = 'html'; - xquote = ''; - advance(q); - v = false; - } else if (a === 'style') { - xmode = 'scriptstring'; - advance('='); - q = nexttoken.id; - if (q !== '"' && q !== "'") { - error("Missing quote."); - } - xmode = 'styleproperty'; - xquote = q; - advance(q); - substyle(); - xmode = 'html'; - xquote = ''; - advance(q); - v = false; - } else { - if (nexttoken.id === '=') { - advance('='); - v = nexttoken.value; - if (!nexttoken.identifier && - nexttoken.id !== '"' && - nexttoken.id !== '\'' && - nexttoken.type !== '(string)' && - nexttoken.type !== '(number)' && - nexttoken.type !== '(color)') { - warning("Expected an attribute value and instead saw '{a}'.", token, a); - } - advance(); - } else { - v = true; - } - } - attributes[a] = v; - doAttribute(n, a, v); - } - doTag(n, attributes); - if (!e) { - stack.push(t); - } - xmode = 'outer'; - advance('>'); - break; - case '') { - error("Missing '{a}'.", nexttoken, '>'); - } - xmode = 'outer'; - advance('>'); - break; - case '' || nexttoken.id === '(end)') { - break; - } - if (nexttoken.value.indexOf('--') >= 0) { - error("Unexpected --."); - } - if (nexttoken.value.indexOf('<') >= 0) { - error("Unexpected <."); - } - if (nexttoken.value.indexOf('>') >= 0) { - error("Unexpected >."); - } - } - xmode = 'outer'; - advance('>'); - break; - case '(end)': - return; - default: - if (nexttoken.id === '(end)') { - error("Missing '{a}'.", nexttoken, - ''); - } else { - advance(); - } - } - if (stack && stack.length === 0 && (option.adsafe || - !option.fragment || nexttoken.id === '(end)')) { - break; - } - } - if (nexttoken.id !== '(end)') { - error("Unexpected material after the end."); - } - } - - -// Build the syntax table by declaring the syntactic elements of the language. - - type('(number)', function () { - return this; - }); - type('(string)', function () { - return this; - }); - - syntax['(identifier)'] = { - type: '(identifier)', - lbp: 0, - identifier: true, - nud: function () { - var v = this.value, - s = scope[v], - f; - if (typeof s === 'function') { - -// Protection against accidental inheritance. - - s = undefined; - } else if (typeof s === 'boolean') { - f = funct; - funct = functions[0]; - addlabel(v, 'var'); - s = funct; - funct = f; - } - -// The name is in scope and defined in the current function. - - if (funct === s) { - -// Change 'unused' to 'var', and reject labels. - - switch (funct[v]) { - case 'unused': - funct[v] = 'var'; - break; - case 'unction': - funct[v] = 'function'; - this['function'] = true; - break; - case 'function': - this['function'] = true; - break; - case 'label': - warning("'{a}' is a statement label.", token, v); - break; - } - -// The name is not defined in the function. If we are in the global scope, -// then we have an undefined variable. -// -// Operators typeof and delete do not raise runtime errors even if the base -// object of a reference is null so no need to display warning if we're -// inside of typeof or delete. - - } else if (funct['(global)']) { - if (anonname != 'typeof' && anonname != 'delete' && - option.undef && typeof predefined[v] !== 'boolean') { - warning("'{a}' is not defined.", token, v); - } - note_implied(token); - -// If the name is already defined in the current -// function, but not as outer, then there is a scope error. +// If the name is already defined in the current +// function, but not as outer, then there is a scope error. } else { switch (funct[v]) { @@ -4336,15 +2430,9 @@ loop: for (;;) { reservevar('arguments', function (x) { if (strict_mode && funct['(global)']) { warning("Strict violation.", x); - } else if (option.safe) { - warning("ADsafe violation.", x); - } - }); - reservevar('eval', function (x) { - if (option.safe) { - warning("ADsafe violation.", x); } }); + reservevar('eval'); reservevar('false'); reservevar('Infinity'); reservevar('NaN'); @@ -4353,8 +2441,6 @@ loop: for (;;) { if (strict_mode && ((funct['(statement)'] && funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) { warning("Strict violation.", x); - } else if (option.safe) { - warning("ADsafe violation.", x); } }); reservevar('true'); @@ -4387,7 +2473,10 @@ loop: for (;;) { bitwise('^', 'bitxor', 80); bitwise('&', 'bitand', 90); relation('==', function (left, right) { - if (option.eqeqeq) { + var eqnull = option.eqnull && + (left.value == 'null' || right.value == 'null'); + + if (!eqnull && option.eqeqeq) { warning("Expected '{a}' and instead saw '{b}'.", this, '===', '=='); } else if (isPoorRelation(left)) { @@ -4484,7 +2573,6 @@ loop: for (;;) { return this; }).exps = true; - prefix('~', function () { if (option.bitwise) { warning("Unexpected '{a}'.", this, '~'); @@ -4492,6 +2580,7 @@ loop: for (;;) { expression(150); return this; }); + prefix('!', function () { this.right = expression(150); this.arity = 'unary'; @@ -4510,18 +2599,6 @@ loop: for (;;) { case 'Object': warning("Use the object literal notation {}.", token); break; - case 'Array': - if (nexttoken.id !== '(') { - warning("Use the array literal notation [].", token); - } else { - advance('('); - if (nexttoken.id === ')') { - warning("Use the array literal notation [].", token); - } - advance(')'); - } - this.first = c; - return this; case 'Number': case 'String': case 'Boolean': @@ -4541,9 +2618,7 @@ loop: for (;;) { if (c.id !== 'function') { i = c.value.substr(0, 1); if (option.newcap && (i < 'A' || i > 'Z')) { - warning( - "A constructor name should start with an uppercase letter.", - token); + warning("A constructor name should start with an uppercase letter.", token); } } } @@ -4553,10 +2628,11 @@ loop: for (;;) { } } } else { - warning("Weird construction. Delete 'new'.", this); + if (!option.supernew) + warning("Weird construction. Delete 'new'.", this); } adjacent(token, nexttoken); - if (nexttoken.id !== '(') { + if (nexttoken.id !== '(' && !option.supernew) { warning("Missing '()' invoking a constructor."); } this.first = c; @@ -4564,6 +2640,8 @@ loop: for (;;) { }); syntax['new'].exps = true; + prefix('void').exps = true; + infix('.', function (left, that) { adjacent(prevtoken, token); nobreak(); @@ -4579,54 +2657,9 @@ loop: for (;;) { } else if (!option.evil && left && left.value === 'document' && (m === 'write' || m === 'writeln')) { warning("document.write can be a form of eval.", left); - } else if (option.adsafe) { - if (left && left.value === 'ADSAFE') { - if (m === 'id' || m === 'lib') { - warning("ADsafe violation.", that); - } else if (m === 'go') { - if (xmode !== 'script') { - warning("ADsafe violation.", that); - } else if (adsafe_went || nexttoken.id !== '(' || - peek(0).id !== '(string)' || - peek(0).value !== adsafe_id || - peek(1).id !== ',') { - error("ADsafe violation: go.", that); - } - adsafe_went = true; - adsafe_may = false; - } - } } if (!option.evil && (m === 'eval' || m === 'execScript')) { warning('eval is evil.'); - } else if (option.safe) { - for (;;) { - if (banned[m] === true) { - warning("ADsafe restricted word '{a}'.", token, m); - } - if (typeof predefined[left.value] !== 'boolean' || - nexttoken.id === '(') { - break; - } - if (standard_member[m] === true) { - if (nexttoken.id === '.') { - warning("ADsafe violation.", that); - } - break; - } - if (nexttoken.id !== '.') { - warning("ADsafe violation.", that); - break; - } - advance('.'); - token.left = that; - token.right = m; - that = token; - m = identifier(); - if (typeof m === 'string') { - countMember(m); - } - } } return that; }, 160, true); @@ -4657,11 +2690,6 @@ loop: for (;;) { } } } - } else if (left.id === '.') { - if (option.safe && left.left.value === 'Math' && - left.right === 'random') { - warning("ADsafe violation.", left); - } } } if (nexttoken.id !== ')') { @@ -4707,12 +2735,7 @@ loop: for (;;) { nexttoken.immed = true; } var v = expression(0); - if (nexttoken.id === ',') { - nospace(nexttoken.id, token); - } - else { - advance(')'); - } + advance(')', this); nospace(prevtoken, token); if (option.immed && v.id === 'function') { if (nexttoken.id === '(') { @@ -4732,14 +2755,8 @@ loop: for (;;) { nospace(); var e = expression(0), s; if (e && e.type === '(string)') { - if (option.safe && banned[e.value] === true) { - warning("ADsafe restricted word '{a}'.", that, e.value); - } else if (!option.evil && - (e.value === 'eval' || e.value === 'execScript')) { + if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) { warning("eval is evil.", that); - } else if (option.safe && - (e.value.charAt(0) === '_' || e.value.charAt(0) === '-')) { - warning("ADsafe restricted subscript '{a}'.", that, e.value); } countMember(e.value); if (!option.sub && ix.test(e.value)) { @@ -4749,10 +2766,6 @@ loop: for (;;) { e, e.value); } } - } else if (!e || e.type !== '(number)' || e.value < 0) { - if (option.safe) { - warning('ADsafe subscripting.'); - } } advance(']', that); nospace(prevtoken, token); @@ -4806,12 +2819,6 @@ loop: for (;;) { if (!id) { if (nexttoken.id === '(string)') { id = nexttoken.value; - if (option.adsafe && - (id.charAt(0) === '_' || - id.charAt(id.length - 1) === '_')) { - warning("Unexpected {a} in '{b}'.", token, - "dangling '_'", id); - } advance(); } else if (nexttoken.id === '(number)') { id = nexttoken.value.toString(); @@ -4847,8 +2854,13 @@ loop: for (;;) { function doFunction(i, statement) { - var f, s = scope; - scope = Object.create(s); + var f, + oldOption = option, + oldScope = scope; + + option = Object.create(option); + scope = Object.create(scope); + funct = { '(name)' : i || '"' + anonname + '"', '(line)' : nexttoken.line, @@ -4867,7 +2879,8 @@ loop: for (;;) { funct['(params)'] = functionparams(); block(false); - scope = s; + scope = oldScope; + option = oldOption; funct['(last)'] = token.line; funct = funct['(context)']; return f; @@ -4877,6 +2890,7 @@ loop: for (;;) { (function (x) { x.nud = function () { var b, f, i, j, p, seen = {}, t; + b = token.line !== nexttoken.line; if (b) { indent += option.indent; @@ -4902,8 +2916,8 @@ loop: for (;;) { } t = nexttoken; adjacent(token, nexttoken); - f = doFunction(i); - if (funct['(loopage)']) { + f = doFunction(); + if (!option.loopfunc && funct['(loopage)']) { warning("Don't make functions within a loop.", t); } p = f['(params)']; @@ -4920,7 +2934,7 @@ loop: for (;;) { } t = nexttoken; adjacent(token, nexttoken); - f = doFunction(i); + f = doFunction(); p = f['(params)']; if (!p || p.length !== 1 || p[0] !== 'value') { warning("Expected (value) in set {a} function.", t, i); @@ -4962,12 +2976,9 @@ loop: for (;;) { }; }(delim('{'))); - - var varstatement = function varstatement(prefix) { - -// JavaScript does not have block scope. It only has function scope. So, -// declaring a variable in a block can have unexpected consequences. - + var varstatement = stmt('var', function (prefix) { + // JavaScript does not have block scope. It only has function scope. So, + // declaring a variable in a block can have unexpected consequences. var id, name, value; if (funct['(onevar)'] && option.onevar) { @@ -5008,16 +3019,13 @@ loop: for (;;) { comma(); } return this; - }; - - - stmt('var', varstatement).exps = true; - + }); + varstatement.exps = true; blockstmt('function', function () { if (inblock) { warning( -"Function statements should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token); +"Function declarations should not be placed in blocks. Use a function expression or move the statement to the top of the outer function.", token); } var i = identifier(); @@ -5026,7 +3034,7 @@ loop: for (;;) { doFunction(i, true); if (nexttoken.id === '(' && nexttoken.line === token.line) { error( -"Function statements are not invocable. Wrap the whole function invocation in parens."); +"Function declarations are not invocable. Wrap the whole function invocation in parens."); } return this; }); @@ -5039,7 +3047,7 @@ loop: for (;;) { nonadjacent(token, nexttoken); } doFunction(i); - if (funct['(loopage)']) { + if (!option.loopfunc && funct['(loopage)']) { warning("Don't make functions within a loop."); } return this; @@ -5074,9 +3082,7 @@ loop: for (;;) { blockstmt('try', function () { var b, e, s; - if (option.adsafe) { - warning("ADsafe try violation.", this); - } + block(false); if (nexttoken.id === 'catch') { advance('catch'); @@ -5160,9 +3166,14 @@ loop: for (;;) { case 'throw': break; default: - warning( - "Expected a 'break' statement before 'case'.", - token); + // You can tell JSHint that you don't use break intentionally by + // adding a comment /* falls through */ on a line just before + // the next `case`. + if (!ft.test(lines[nexttoken.line - 2])) { + warning( + "Expected a 'break' statement before 'case'.", + token); + } } indentation(-option.indent); advance('case'); @@ -5179,9 +3190,11 @@ loop: for (;;) { case 'throw': break; default: - warning( - "Expected a 'break' statement before 'default'.", - token); + if (!ft.test(lines[nexttoken.line - 2])) { + warning( + "Expected a 'break' statement before 'default'.", + token); + } } indentation(-option.indent); advance('default'); @@ -5257,7 +3270,7 @@ loop: for (;;) { }()); blockstmt('for', function () { - var f = option.forin, s, t = nexttoken; + var s, t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); @@ -5266,7 +3279,7 @@ loop: for (;;) { if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') { if (nexttoken.id === 'var') { advance('var'); - varstatement(true); + varstatement.fud.call(varstatement, true); } else { switch (funct[nexttoken.value]) { case 'unused': @@ -5284,7 +3297,7 @@ loop: for (;;) { expression(20); advance(')', t); s = block(true, true); - if (!f && (s.length > 1 || typeof s[0] !== 'object' || + if (option.forin && (s.length > 1 || typeof s[0] !== 'object' || s[0].value !== 'if')) { warning("The body of a for in should be wrapped in an if statement to filter unwanted properties from the prototype.", this); } @@ -5295,7 +3308,7 @@ loop: for (;;) { if (nexttoken.id !== ';') { if (nexttoken.id === 'var') { advance('var'); - varstatement(); + varstatement.fud.call(varstatement); } else { for (;;) { expression(0, 'for'); @@ -5410,8 +3423,6 @@ loop: for (;;) { return this; }).exps = true; - reserve('void'); - // Superfluous reserved words reserve('class'); @@ -5529,10 +3540,11 @@ loop: for (;;) { // The actual JSHINT function itself. - var itself = function (s, o) { + var itself = function (s, o, g) { var a, i, k; JSHINT.errors = []; predefined = Object.create(standard); + combine(predefined, g || {}); if (o) { a = o.predef; if (a) { @@ -5547,52 +3559,13 @@ loop: for (;;) { } } } - if (o.adsafe) { - o.safe = true; - } - if (o.safe) { - o.browser = - o.css = - o.debug = - o.devel = - o.evil = - o.forin = - o.on = - o.rhino = - o.windows = - o.sub = - o.widget = false; - - o.eqeqeq = - o.nomen = - o.safe = - o.undef = true; - - predefined.Date = - predefined['eval'] = - predefined.Function = - predefined.Object = null; - - predefined.ADSAFE = - predefined.lib = false; - } option = o; } else { option = {}; } option.indent = option.indent || 4; option.maxerr = option.maxerr || 50; - adsafe_id = ''; - adsafe_may = false; - adsafe_went = false; - approved = {}; - if (option.approved) { - for (i = 0; i < option.approved.length; i += 1) { - approved[option.approved[i]] = option.approved[i]; - } - } else { - approved.test = 'test'; - } + tab = ''; for (i = 0; i < option.indent; i += 1) { tab += ' '; @@ -5608,10 +3581,8 @@ loop: for (;;) { '(loopage)': 0 }; functions = [funct]; - ids = {}; urls = []; src = false; - xmode = false; stack = null; member = {}; membersOnly = null; @@ -5629,52 +3600,20 @@ loop: for (;;) { try { advance(); - if (nexttoken.value.charAt(0) === '<') { - html(); - if (option.adsafe && !adsafe_went) { - warning("ADsafe violation: Missing ADSAFE.go.", this); - } - } else { - switch (nexttoken.id) { - case '{': - case '[': - option.laxbreak = true; - jsonmode = true; - jsonValue(); - break; - case '@': - case '*': - case '#': - case '.': - case ':': - xmode = 'style'; - advance(); - if (token.id !== '@' || !nexttoken.identifier || - nexttoken.value !== 'charset' || token.line !== 1 || - token.from !== 1) { - error("A css file should begin with @charset 'UTF-8';"); - } - advance(); - if (nexttoken.type !== '(string)' && - nexttoken.value !== 'UTF-8') { - error("A css file should begin with @charset 'UTF-8';"); - } - advance(); - advance(';'); - styles(); - break; - - default: - if (option.adsafe && option.fragment) { - error("Expected '{a}' and instead saw '{b}'.", - nexttoken, '
', nexttoken.value); - } - if (nexttoken.value === 'use strict') { + switch (nexttoken.id) { + case '{': + case '[': + option.laxbreak = true; + jsonmode = true; + jsonValue(); + break; + default: + if (nexttoken.value === 'use strict') { + if (!option.globalstrict) warning("Use the function form of \"use strict\"."); - use_strict(); - } - statements('lib'); + use_strict(); } + statements('lib'); } advance('(end)'); } catch (e) { @@ -5848,9 +3787,7 @@ loop: for (;;) { detail("URLs
", data.urls, '
'); } - if (xmode === 'style') { - o.push('

CSS.

'); - } else if (data.json && !err) { + if (data.json && !err) { o.push('

JSON: good.

'); } else if (data.globals) { o.push('
Global ' + @@ -5906,7 +3843,7 @@ loop: for (;;) { }; itself.jshint = itself; - itself.edition = '2011-02-19'; + itself.edition = '2011-04-16'; return itself; @@ -5915,3 +3852,4 @@ loop: for (;;) { // Make JSHINT a Node module, if possible. if (typeof exports == 'object' && exports) exports.JSHINT = JSHINT; + diff --git a/util/nodejshint.js b/util/nodejshint.js index 13a2ebcc5..e4b3c0943 100644 --- a/util/nodejshint.js +++ b/util/nodejshint.js @@ -1,25 +1,23 @@ -var JSHINT = require( './jshint.js' ).JSHINT, - fs = require( 'fs' ); +var JSHINT = require( './jshint.js' ).JSHINT + , fs = require( 'fs' ); var nodejshint = function() { var counter = 0; return function( files, callback ) { if( files.length ) { - var file = files.pop(), - pass = false; + var file = files.pop(); fs.readFile( file, function( err, data ) { if (err) { throw err; } - if( pass = JSHINT( data.toString() ), pass, { laxbreak: true } ) { + if( JSHINT(data.toString(), { laxbreak: true }) ) { counter++; console.log( '✔ Passed '+ file ); } else { console.log( 'x Failed '+ file ); JSHINT.errors.forEach( function( err ) { - if( err ) { console.log( 'line '+ err.line +'\t', err.reason +'' ); } diff --git a/vendor/libgit2/.gitattributes b/vendor/libgit2/.gitattributes new file mode 100644 index 000000000..f90540b55 --- /dev/null +++ b/vendor/libgit2/.gitattributes @@ -0,0 +1,2 @@ +*.c eol=lf +*.h eol=lf diff --git a/vendor/libgit2/.gitignore b/vendor/libgit2/.gitignore deleted file mode 100644 index ddff317f6..000000000 --- a/vendor/libgit2/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ - -/apidocs -/trash-*.exe -/libgit2.pc -/config.mak -*.o -*.a -*.exe -*.gcda -*.gcno -*.gcov -.lock-wafbuild -.waf* -build/ -tests/tmp/ -msvc/Debug/ -msvc/Release/ -*.suo -*.user -*.sdf -*.opensdf -.DS_Store diff --git a/vendor/libgit2/CMakeLists.txt b/vendor/libgit2/CMakeLists.txt index acac2a6de..e149cd27f 100644 --- a/vendor/libgit2/CMakeLists.txt +++ b/vendor/libgit2/CMakeLists.txt @@ -4,7 +4,7 @@ # > mkdir build && cd build # > cmake .. [-DSETTINGS=VALUE] # > cmake --build . -# +# # Testing: # > ctest -V # @@ -22,37 +22,19 @@ STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}") # Find required dependencies -INCLUDE_DIRECTORIES(deps/zlib src include) - -# Try finding openssl -FIND_PACKAGE(OpenSSL) -IF (OPENSSL_CRYPTO_LIBRARIES) - SET(SHA1_TYPE "openssl" CACHE STRING "Which SHA1 implementation to use: builtin, ppc, openssl") -ELSEIF () - SET(SHA1_TYPE "builtin" CACHE STRING "Which SHA1 implementation to use: builtin, ppc") -ENDIF () - -INCLUDE(FindPkgConfig) - -# Show SQLite3 settings in GUI (if they won't be found out) -SET(SQLITE3_INCLUDE_DIRS "" CACHE PATH "SQLite include directory") -SET(SQLITE3_LIBRARIES "" CACHE FILEPATH "SQLite library") - -# Are SQLite3 variables already set up? (poor Windows/no pkg-config/no sqlite3.pc) -IF (SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) - SET(SQLITE3_FOUND 1) -ENDIF () - -# Try to find SQLite3 via pkg-config -IF (PKG_CONFIG_FOUND AND NOT SQLITE3_FOUND) - pkg_check_modules(SQLITE3 sqlite3) -ENDIF () +INCLUDE_DIRECTORIES(src include) +IF (NOT WIN32) + FIND_PACKAGE(ZLIB) +ENDIF() -# Compile SQLite backend if SQLite3 is available -IF (SQLITE3_FOUND) - ADD_DEFINITIONS(-DGIT2_SQLITE_BACKEND) - INCLUDE_DIRECTORIES(${SQLITE3_INCLUDE_DIRS}) -ENDIF () +IF (ZLIB_FOUND) + INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) + LINK_LIBRARIES(${ZLIB_LIBRARIES}) +ELSE (ZLIB_FOUND) + INCLUDE_DIRECTORIES(deps/zlib) + ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP) + FILE(GLOB SRC_ZLIB deps/zlib/*.c) +ENDIF() # Installation paths SET(INSTALL_BIN bin CACHE PATH "Where to install binaries to.") @@ -63,10 +45,27 @@ SET(INSTALL_INC include CACHE PATH "Where to install headers to.") OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) OPTION (BUILD_TESTS "Build Tests" ON) OPTION (THREADSAFE "Build libgit2 as threadsafe" OFF) +OPTION (STDCALL "Buildl libgit2 with the __stdcall convention (Windows)" ON) + +# Platform specific compilation flags +IF (MSVC) + SET(CMAKE_C_FLAGS "/W4 /WX /nologo /Zi") + IF (STDCALL) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gz") + ENDIF () + # TODO: bring back /RTC1 /RTCc + SET(CMAKE_C_FLAGS_DEBUG "/Od /DEBUG /MTd") + SET(CMAKE_C_FLAGS_RELEASE "/MT /O2") +ELSE () + SET(CMAKE_C_FLAGS "-O2 -g -Wall -Wextra") + IF (NOT MINGW) # MinGW always does PIC and complains if we tell it to + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + ENDIF () +ENDIF() -# Build Release by default +# Build Debug by default IF (NOT CMAKE_BUILD_TYPE) - SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) + SET(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) ENDIF () IF (THREADSAFE) @@ -77,57 +76,59 @@ IF (THREADSAFE) ADD_DEFINITIONS(-DGIT_THREADS) ENDIF() +ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) + # Collect sourcefiles -FILE(GLOB SRC src/*.c src/backends/*.c) -FILE(GLOB SRC_ZLIB deps/zlib/*.c) -FILE(GLOB SRC_SHA1 src/block-sha1/*.c) -FILE(GLOB SRC_PLAT src/unix/*.c) FILE(GLOB SRC_H include/git2/*.h) # On Windows use specific platform sources IF (WIN32 AND NOT CYGWIN) ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_LIB) - FILE(GLOB SRC_PLAT src/win32/*.c) + FILE(GLOB SRC src/*.c src/win32/*.c) +ELSE() + FILE(GLOB SRC src/*.c src/unix/*.c) ENDIF () -# Specify sha1 implementation -IF (SHA1_TYPE STREQUAL "ppc") - ADD_DEFINITIONS(-DPPC_SHA1) - FILE(GLOB SRC_SHA1 src/ppc/*.c) -ELSEIF (SHA1_TYPE STREQUAL "openssl") - ADD_DEFINITIONS(-DOPENSSL_SHA1) - SET (SRC_SHA1) - INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR}) - SET (LIB_SHA1 ${OPENSSL_CRYPTO_LIBRARIES}) +# Compile and link libgit2 +ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB}) + +IF (WIN32) + TARGET_LINK_LIBRARIES(git2 ws2_32) +ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + TARGET_LINK_LIBRARIES(git2 socket nsl) ENDIF () -# Compile and link libgit2 -ADD_LIBRARY(git2 ${SRC} ${SRC_PLAT} ${SRC_SHA1} ${SRC_ZLIB}) -TARGET_LINK_LIBRARIES(git2 ${LIB_SHA1} ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIBRARIES}) +TARGET_LINK_LIBRARIES(git2 ${CMAKE_THREAD_LIBS_INIT}) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR}) +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY) # Install -INSTALL(TARGETS git2 +INSTALL(TARGETS git2 RUNTIME DESTINATION ${INSTALL_BIN} LIBRARY DESTINATION ${INSTALL_LIB} ARCHIVE DESTINATION ${INSTALL_LIB} ) +INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc DESTINATION ${INSTALL_LIB}/pkgconfig ) INSTALL(DIRECTORY include/git2 DESTINATION ${INSTALL_INC} ) INSTALL(FILES include/git2.h DESTINATION ${INSTALL_INC} ) # Tests IF (BUILD_TESTS) - SET(TEST_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.") + SET(TEST_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.") ADD_DEFINITIONS(-DTEST_RESOURCES=\"${TEST_RESOURCES}\") - - ENABLE_TESTING() - INCLUDE_DIRECTORIES(tests) + INCLUDE_DIRECTORIES(tests) FILE(GLOB SRC_TEST tests/t??-*.c) - ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_PLAT} ${SRC_SHA1} ${SRC_TEST} ${SRC_ZLIB}) - TARGET_LINK_LIBRARIES(libgit2_test ${LIB_SHA1} ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIBRARIES}) + ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_TEST} ${SRC_ZLIB}) + TARGET_LINK_LIBRARIES(libgit2_test ${CMAKE_THREAD_LIBS_INIT}) + IF (WIN32) + TARGET_LINK_LIBRARIES(libgit2_test ws2_32) + ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") + TARGET_LINK_LIBRARIES(libgit2_test socket nsl) + ENDIF () + ENABLE_TESTING() ADD_TEST(libgit2_test libgit2_test) ENDIF () diff --git a/vendor/libgit2/COPYING b/vendor/libgit2/COPYING index c36f4cf1e..75bc6a1fe 100644 --- a/vendor/libgit2/COPYING +++ b/vendor/libgit2/COPYING @@ -71,7 +71,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -126,7 +126,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -184,7 +184,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -241,7 +241,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -294,7 +294,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest diff --git a/vendor/libgit2/Makefile.embed b/vendor/libgit2/Makefile.embed new file mode 100644 index 000000000..fec090fa7 --- /dev/null +++ b/vendor/libgit2/Makefile.embed @@ -0,0 +1,26 @@ +rm=rm -f +CC=cc +AR=ar cq +RANLIB=ranlib +LIBNAME=libgit2.a + +INCLUDES= -I. -Isrc -Iinclude -Ideps/zlib + +DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 +CFLAGS= -g $(DEFINES) -Wall -Wextra -fPIC -O2 + +SRCS = $(wildcard src/*.c) $(wildcard src/unix/*.c) $(wildcard deps/zlib/*.c) +OBJS = $(patsubst %.c,%.o,$(SRCS)) + +%.c.o: + $(CC) $(CFLAGS) -c $*.c + +all: $(LIBNAME) + +$(LIBNAME): $(OBJS) + $(rm) $@ + $(AR) $@ $(OBJS) + $(RANLIB) $@ + +clean: + $(rm) $(OBJS) $(LIBNAME) diff --git a/vendor/libgit2/README.md b/vendor/libgit2/README.md index dae6a76bf..b5c76a6be 100644 --- a/vendor/libgit2/README.md +++ b/vendor/libgit2/README.md @@ -11,7 +11,7 @@ release its source code. * Mailing list: * Website: -* API documentation: +* API documentation: * Usage guide: What It Can Do @@ -20,82 +20,27 @@ What It Can Do libgit2 is already very usable. * SHA conversions, formatting and shortening -* object reading (loose and packed) -* object writing (loose) -* commit, tag, tree and blob parsing and write-back +* abstracked ODB backend system +* commit, tag, tree and blob parsing, editing, and write-back * tree traversal * revision walking * index file (staging area) manipulation -* custom ODB backends * reference management (including packed references) -* ...and more +* config file management +* high level repository management +* thread safety and reentrancy +* descriptive and detailed error messages +* ...and more (over 175 different API calls) - -Building libgit2 - External dependencies -======================================== +Building libgit2 - Using CMake +============================== libgit2 builds cleanly on most platforms without any external dependencies. -Under Unix-like systems, like Linux, *BSD and Mac OS X, libgit2 expects `pthreads` to be available; +Under Unix-like systems, like Linux, * BSD and Mac OS X, libgit2 expects `pthreads` to be available; they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API for threading. -Additionally, he following libraries may be used as replacement for built-in functionality: - -* LibSSL **(optional)** - -libgit2 can be built using the SHA1 implementation of LibSSL-Crypto, instead of the built-in custom implementations. Performance wise, they are quite similar. - -Building libgit2 - Using waf -====================== - -Waf is a minimalist build system which only requires a Python 2.5+ interpreter to run. This is the default build system for libgit2. - -To build libgit2 using waf, first configure the build system by running: - - $ ./waf configure - -Then build the library, either in its shared (libgit2.so) or static form (libgit2.a): - - $ ./waf build-static - $ ./waf build-shared - -You can then run the full test suite with: - - $ ./waf test - -And finally you can install the library with (you may need to sudo): - - $ sudo ./waf install - -The waf build system for libgit2 accepts the following flags: - - --debug - build the library with debug symbols. - Defaults to off. - - --sha1=[builtin|ppc|openssl] - use the builtin SHA1 functions, the optimized PPC versions - or the SHA1 functions from LibCrypto (OpenSSL). - Defaults to 'builtin'. - - --msvc=[7.1|8.0|9.0|10.0] - Force a specific version of the MSVC compiler, if more than - one version is installed. - - --arch=[ia64|x64|x86|x86_amd64|x86_ia64] - Force a specific architecture for compilers that support it. - - --with-sqlite - Enable sqlite support. - -You can run `./waf --help` to see a full list of install options and -targets. - - -Building libgit2 - Using CMake -============================== - -The libgit2 library can also be built using CMake 2.6+ () on all platforms. +The libgit2 library is built using CMake 2.6+ () on all platforms. On most systems you can build the library using the following commands @@ -112,6 +57,14 @@ To install the library you can specify the install prefix by setting: For more advanced use or questions about CMake please read . +The following CMake variables are declared: + +- `INSTALL_BIN`: Where to install binaries to. +- `INSTALL_LIB`: Where to install libraries to. +- `INSTALL_INC`: Where to install headers to. +- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON) +- `BUILD_TESTS`: Build the libgit2 test suite (defaults to ON) +- `THREADSAFE`: Build libgit2 with threading support (defaults to OFF) Language Bindings ================================== @@ -131,6 +84,9 @@ Here are the bindings to libgit2 that are currently available: * libqgit2 (C++ QT bindings) * libgit2-ocaml (ocaml bindings) * Geef (Erlang bindings) +* libgit2net (.NET bindings, low level) +* parrot-libgit2 (Parrot Virtual Machine bindings) +* hgit2 (Haskell bindings) If you start another language binding to libgit2, please let us know so we can add it to the list. @@ -147,7 +103,7 @@ GitHub, or join us on the mailing list by sending an email to: libgit2@librelist.com -License +License ================================== libgit2 is under GPL2 **with linking exemption**. This means you can link to the library with any program, commercial, open source or diff --git a/vendor/libgit2/api.docurium b/vendor/libgit2/api.docurium new file mode 100644 index 000000000..9e17817db --- /dev/null +++ b/vendor/libgit2/api.docurium @@ -0,0 +1,13 @@ +{ + "name": "libgit2", + "github": "libgit2/libgit2", + "input": "include/git2", + "prefix": "git_", + "output": "docs", + "branch": "gh-pages", + "examples": "examples", + "legacy": { + "input": {"src/git": ["v0.1.0"], + "src/git2": ["v0.2.0", "v0.3.0"]} + } +} diff --git a/vendor/libgit2/deps/zlib/crc32.c b/vendor/libgit2/deps/zlib/crc32.c new file mode 100644 index 000000000..91be372d2 --- /dev/null +++ b/vendor/libgit2/deps/zlib/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/vendor/libgit2/deps/zlib/crc32.h b/vendor/libgit2/deps/zlib/crc32.h new file mode 100644 index 000000000..8053b6117 --- /dev/null +++ b/vendor/libgit2/deps/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/vendor/libgit2/deps/zlib/zconf.h b/vendor/libgit2/deps/zlib/zconf.h index 494992aba..150814361 100644 --- a/vendor/libgit2/deps/zlib/zconf.h +++ b/vendor/libgit2/deps/zlib/zconf.h @@ -10,9 +10,6 @@ #include "../../src/common.h" -#define NO_GZIP -#define STDC - /* Jeez, don't complain about non-prototype * forms, we didn't write zlib */ #if defined(_MSC_VER) diff --git a/vendor/libgit2/examples/.gitignore b/vendor/libgit2/examples/.gitignore new file mode 100644 index 000000000..4c34e4ab5 --- /dev/null +++ b/vendor/libgit2/examples/.gitignore @@ -0,0 +1,2 @@ +general +showindex diff --git a/vendor/libgit2/examples/Makefile b/vendor/libgit2/examples/Makefile new file mode 100644 index 000000000..f7bf469a5 --- /dev/null +++ b/vendor/libgit2/examples/Makefile @@ -0,0 +1,10 @@ +all: general showindex + +general : general.c + gcc -lgit2 -o general general.c + +showindex : showindex.c + gcc -lgit2 -o showindex showindex.c + +clean: + rm general showindex diff --git a/vendor/libgit2/examples/general.c b/vendor/libgit2/examples/general.c new file mode 100644 index 000000000..91b6ee859 --- /dev/null +++ b/vendor/libgit2/examples/general.c @@ -0,0 +1,447 @@ +// [**libgit2**][lg] is a portable, pure C implementation of the Git core methods +// provided as a re-entrant linkable library with a solid API, allowing you +// to write native speed custom Git applications in any language which +// supports C bindings. +// +// This file is an example of using that API in a real, compilable C file. +// As the API is updated, this file will be updated to demonstrate the +// new functionality. +// +// If you're trying to write something in C using [libgit2][lg], you will also want +// to check out the generated [API documentation][ap] and the [Usage Guide][ug]. We've +// tried to link to the relevant sections of the API docs in each section in this file. +// +// **libgit2** only implements the core plumbing functions, not really the higher +// level porcelain stuff. For a primer on Git Internals that you will need to know +// to work with Git at this level, check out [Chapter 9][pg] of the Pro Git book. +// +// [lg]: http://libgit2.github.com +// [ap]: http://libgit2.github.com/libgit2 +// [ug]: http://libgit2.github.com/api.html +// [pg]: http://progit.org/book/ch9-0.html + +// ### Includes + +// Including the `git2.h` header will include all the other libgit2 headers that you need. +// It should be the only thing you need to include in order to compile properly and get +// all the libgit2 API. +#include +#include + +int main (int argc, char** argv) +{ + // ### Opening the Repository + + // There are a couple of methods for opening a repository, this being the simplest. + // There are also [methods][me] for specifying the index file and work tree locations, here + // we are assuming they are in the normal places. + // + // [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository + git_repository *repo; + git_repository_open(&repo, "/opt/libgit2-test/.git"); + + // ### SHA-1 Value Conversions + + // For our first example, we will convert a 40 character hex value to the 20 byte raw SHA1 value. + printf("*Hex to Raw*\n"); + char hex[] = "fd6e612585290339ea8bf39c692a7ff6a29cb7c3"; + + // The `git_oid` is the structure that keeps the SHA value. We will use this throughout the example + // for storing the value of the current SHA key we're working with. + git_oid oid; + git_oid_fromstr(&oid, hex); + + // Once we've converted the string into the oid value, we can get the raw value of the SHA. + printf("Raw 20 bytes: [%s]\n", (&oid)->id); + + // Next we will convert the 20 byte raw SHA1 value to a human readable 40 char hex value. + printf("\n*Raw to Hex*\n"); + char out[41]; + out[40] = '\0'; + + // If you have a oid, you can easily get the hex value of the SHA as well. + git_oid_fmt(out, &oid); + printf("SHA hex string: %s\n", out); + + // ### Working with the Object Database + // **libgit2** provides [direct access][odb] to the object database. + // The object database is where the actual objects are stored in Git. For + // working with raw objects, we'll need to get this structure from the + // repository. + // [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb + git_odb *odb; + odb = git_repository_database(repo); + + // #### Raw Object Reading + + printf("\n*Raw Object Read*\n"); + git_odb_object *obj; + git_otype otype; + const unsigned char *data; + const char *str_type; + int error; + + // We can read raw objects directly from the object database if we have the oid (SHA) + // of the object. This allows us to access objects without knowing thier type and inspect + // the raw bytes unparsed. + error = git_odb_read(&obj, odb, &oid); + + // A raw object only has three properties - the type (commit, blob, tree or tag), the size + // of the raw data and the raw, unparsed data itself. For a commit or tag, that raw data + // is human readable plain ASCII text. For a blob it is just file contents, so it could be + // text or binary data. For a tree it is a special binary format, so it's unlikely to be + // hugely helpful as a raw object. + data = (const unsigned char *)git_odb_object_data(obj); + otype = git_odb_object_type(obj); + + // We provide methods to convert from the object type which is an enum, to a string + // representation of that value (and vice-versa). + str_type = git_object_type2string(otype); + printf("object length and type: %d, %s\n", + (int)git_odb_object_size(obj), + str_type); + + // For proper memory management, close the object when you are done with it or it will leak + // memory. + git_odb_object_close(obj); + + // #### Raw Object Writing + + printf("\n*Raw Object Write*\n"); + + // You can also write raw object data to Git. This is pretty cool because it gives you + // direct access to the key/value properties of Git. Here we'll write a new blob object + // that just contains a simple string. Notice that we have to specify the object type as + // the `git_otype` enum. + git_odb_write(&oid, odb, "test data", sizeof("test data") - 1, GIT_OBJ_BLOB); + + // Now that we've written the object, we can check out what SHA1 was generated when the + // object was written to our database. + git_oid_fmt(out, &oid); + printf("Written Object: %s\n", out); + + // ### Object Parsing + // libgit2 has methods to parse every object type in Git so you don't have to work directly + // with the raw data. This is much faster and simpler than trying to deal with the raw data + // yourself. + + // #### Commit Parsing + // [Parsing commit objects][pco] is simple and gives you access to all the data in the commit + // - the // author (name, email, datetime), committer (same), tree, message, encoding and parent(s). + // [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit + + printf("\n*Commit Parsing*\n"); + + git_commit *commit; + git_oid_fromstr(&oid, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + + error = git_commit_lookup(&commit, repo, &oid); + + const git_signature *author, *cmtter; + const char *message, *message_short; + time_t ctime; + unsigned int parents, p; + + // Each of the properties of the commit object are accessible via methods, including commonly + // needed variations, such as `git_commit_time` which returns the author time and `_message_short` + // which gives you just the first line of the commit message. + message = git_commit_message(commit); + message_short = git_commit_message_short(commit); + author = git_commit_author(commit); + cmtter = git_commit_committer(commit); + ctime = git_commit_time(commit); + + // The author and committer methods return [git_signature] structures, which give you name, email + // and `when`, which is a `git_time` structure, giving you a timestamp and timezone offset. + printf("Author: %s (%s)\n", author->name, author->email); + + // Commits can have zero or more parents. The first (root) commit will have no parents, most commits + // will have one, which is the commit it was based on, and merge commits will have two or more. + // Commits can technically have any number, though it's pretty rare to have more than two. + parents = git_commit_parentcount(commit); + for (p = 0;p < parents;p++) { + git_commit *parent; + git_commit_parent(&parent, commit, p); + git_oid_fmt(out, git_commit_id(parent)); + printf("Parent: %s\n", out); + git_commit_close(parent); + } + + // Don't forget to close the object to prevent memory leaks. You will have to do this for + // all the objects you open and parse. + git_commit_close(commit); + + // #### Writing Commits + // + // libgit2 provides a couple of methods to create commit objects easily as well. There are four + // different create signatures, we'll just show one of them here. You can read about the other + // ones in the [commit API docs][cd]. + // [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit + + printf("\n*Commit Writing*\n"); + git_oid tree_id, parent_id, commit_id; + git_tree *tree; + git_commit *parent; + + // Creating signatures for an authoring identity and time is pretty simple - you will need to have + // this to create a commit in order to specify who created it and when. Default values for the name + // and email should be found in the `user.name` and `user.email` configuration options. See the `config` + // section of this example file to see how to access config values. + author = git_signature_new("Scott Chacon", "schacon@gmail.com", + 123456789, 60); + cmtter = git_signature_new("Scott A Chacon", "scott@github.com", + 987654321, 90); + + // Commit objects need a tree to point to and optionally one or more parents. Here we're creating oid + // objects to create the commit with, but you can also use + git_oid_fromstr(&tree_id, "28873d96b4e8f4e33ea30f4c682fd325f7ba56ac"); + git_tree_lookup(&tree, repo, &tree_id); + git_oid_fromstr(&parent_id, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + git_commit_lookup(&parent, repo, &parent_id); + + // Here we actually create the commit object with a single call with all the values we need to create + // the commit. The SHA key is written to the `commit_id` variable here. + git_commit_create_v( + &commit_id, /* out id */ + repo, + NULL, /* do not update the HEAD */ + author, + cmtter, + "example commit", + tree, + 1, parent); + + // Now we can take a look at the commit SHA we've generated. + git_oid_fmt(out, &commit_id); + printf("New Commit: %s\n", out); + + // #### Tag Parsing + // You can parse and create tags with the [tag management API][tm], which functions very similarly + // to the commit lookup, parsing and creation methods, since the objects themselves are very similar. + // [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag + printf("\n*Tag Parsing*\n"); + git_tag *tag; + const char *tmessage, *tname; + git_otype ttype; + + // We create an oid for the tag object if we know the SHA and look it up in the repository the same + // way that we would a commit (or any other) object. + git_oid_fromstr(&oid, "bc422d45275aca289c51d79830b45cecebff7c3a"); + + error = git_tag_lookup(&tag, repo, &oid); + + // Now that we have the tag object, we can extract the information it generally contains: the target + // (usually a commit object), the type of the target object (usually 'commit'), the name ('v1.0'), + // the tagger (a git_signature - name, email, timestamp), and the tag message. + git_tag_target((git_object **)&commit, tag); + tname = git_tag_name(tag); // "test" + ttype = git_tag_type(tag); // GIT_OBJ_COMMIT (otype enum) + tmessage = git_tag_message(tag); // "tag message\n" + printf("Tag Message: %s\n", tmessage); + + git_commit_close(commit); + + // #### Tree Parsing + // [Tree parsing][tp] is a bit different than the other objects, in that we have a subtype which is the + // tree entry. This is not an actual object type in Git, but a useful structure for parsing and + // traversing tree entries. + // + // [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree + printf("\n*Tree Parsing*\n"); + + const git_tree_entry *entry; + git_object *objt; + + // Create the oid and lookup the tree object just like the other objects. + git_oid_fromstr(&oid, "2a741c18ac5ff082a7caaec6e74db3075a1906b5"); + git_tree_lookup(&tree, repo, &oid); + + // Getting the count of entries in the tree so you can iterate over them if you want to. + int cnt = git_tree_entrycount(tree); // 3 + printf("tree entries: %d\n", cnt); + + entry = git_tree_entry_byindex(tree, 0); + printf("Entry name: %s\n", git_tree_entry_name(entry)); // "hello.c" + + // You can also access tree entries by name if you know the name of the entry you're looking for. + entry = git_tree_entry_byname(tree, "hello.c"); + git_tree_entry_name(entry); // "hello.c" + + // Once you have the entry object, you can access the content or subtree (or commit, in the case + // of submodules) that it points to. You can also get the mode if you want. + git_tree_entry_2object(&objt, repo, entry); // blob + + // Remember to close the looked-up object once you are done using it + git_object_close(objt); + + // #### Blob Parsing + // + // The last object type is the simplest and requires the least parsing help. Blobs are just file + // contents and can contain anything, there is no structure to it. The main advantage to using the + // [simple blob api][ba] is that when you're creating blobs you don't have to calculate the size + // of the content. There is also a helper for reading a file from disk and writing it to the db and + // getting the oid back so you don't have to do all those steps yourself. + // + // [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob + + printf("\n*Blob Parsing*\n"); + git_blob *blob; + + git_oid_fromstr(&oid, "af7574ea73f7b166f869ef1a39be126d9a186ae0"); + git_blob_lookup(&blob, repo, &oid); + + // You can access a buffer with the raw contents of the blob directly. + // Note that this buffer may not be contain ASCII data for certain blobs (e.g. binary files): + // do not consider the buffer a NULL-terminated string, and use the `git_blob_rawsize` attribute to + // find out its exact size in bytes + printf("Blob Size: %d\n", git_blob_rawsize(blob)); // 8 + git_blob_rawcontent(blob); // "content" + + // ### Revwalking + // + // The libgit2 [revision walking api][rw] provides methods to traverse the directed graph created + // by the parent pointers of the commit objects. Since all commits point back to the commit that + // came directly before them, you can walk this parentage as a graph and find all the commits that + // were ancestors of (reachable from) a given starting point. This can allow you to create `git log` + // type functionality. + // + // [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk + + printf("\n*Revwalking*\n"); + git_revwalk *walk; + git_commit *wcommit; + + git_oid_fromstr(&oid, "f0877d0b841d75172ec404fc9370173dfffc20d1"); + + // To use the revwalker, create a new walker, tell it how you want to sort the output and then push + // one or more starting points onto the walker. If you want to emulate the output of `git log` you + // would push the SHA of the commit that HEAD points to into the walker and then start traversing them. + // You can also 'hide' commits that you want to stop at or not see any of their ancestors. So if you + // want to emulate `git log branch1..branch2`, you would push the oid of `branch2` and hide the oid + // of `branch1`. + git_revwalk_new(&walk, repo); + git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE); + git_revwalk_push(walk, &oid); + + const git_signature *cauth; + const char *cmsg; + + // Now that we have the starting point pushed onto the walker, we can start asking for ancestors. It + // will return them in the sorting order we asked for as commit oids. + // We can then lookup and parse the commited pointed at by the returned OID; + // note that this operation is specially fast since the raw contents of the commit object will + // be cached in memory + while ((git_revwalk_next(&oid, walk)) == GIT_SUCCESS) { + error = git_commit_lookup(&wcommit, repo, &oid); + cmsg = git_commit_message_short(wcommit); + cauth = git_commit_author(wcommit); + printf("%s (%s)\n", cmsg, cauth->email); + git_commit_close(wcommit); + } + + // Like the other objects, be sure to free the revwalker when you're done to prevent memory leaks. + // Also, make sure that the repository being walked it not deallocated while the walk is in + // progress, or it will result in undefined behavior + git_revwalk_free(walk); + + // ### Index File Manipulation + // + // The [index file API][gi] allows you to read, traverse, update and write the Git index file + // (sometimes thought of as the staging area). + // + // [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index + + printf("\n*Index Walking*\n"); + + git_index *index; + unsigned int i, ecount; + + // You can either open the index from the standard location in an open repository, as we're doing + // here, or you can open and manipulate any index file with `git_index_open_bare()`. The index + // for the repository will be located and loaded from disk. + git_repository_index(&index, repo); + + // For each entry in the index, you can get a bunch of information including the SHA (oid), path + // and mode which map to the tree objects that are written out. It also has filesystem properties + // to help determine what to inspect for changes (ctime, mtime, dev, ino, uid, gid, file_size and flags) + // All these properties are exported publicly in the `git_index_entry` struct + ecount = git_index_entrycount(index); + for (i = 0; i < ecount; ++i) { + git_index_entry *e = git_index_get(index, i); + + printf("path: %s\n", e->path); + printf("mtime: %d\n", (int)e->mtime.seconds); + printf("fs: %d\n", (int)e->file_size); + } + + git_index_free(index); + + // ### References + // + // The [reference API][ref] allows you to list, resolve, create and update references such as + // branches, tags and remote references (everything in the .git/refs directory). + // + // [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference + + printf("\n*Reference Listing*\n"); + + // Here we will implement something like `git for-each-ref` simply listing out all available + // references and the object SHA they resolve to. + git_strarray ref_list; + git_reference_listall(&ref_list, repo, GIT_REF_LISTALL); + + const char *refname; + git_reference *ref; + + // Now that we have the list of reference names, we can lookup each ref one at a time and + // resolve them to the SHA, then print both values out. + for (i = 0; i < ref_list.count; ++i) { + refname = ref_list.strings[i]; + git_reference_lookup(&ref, repo, refname); + + switch (git_reference_type(ref)) { + case GIT_REF_OID: + git_oid_fmt(out, git_reference_oid(ref)); + printf("%s [%s]\n", refname, out); + break; + + case GIT_REF_SYMBOLIC: + printf("%s => %s\n", refname, git_reference_target(ref)); + break; + default: + fprintf(stderr, "Unexpected reference type\n"); + exit(1); + } + } + + git_strarray_free(&ref_list); + + // ### Config Files + // + // The [config API][config] allows you to list and updatee config values in + // any of the accessible config file locations (system, global, local). + // + // [config]: http://libgit2.github.com/libgit2/#HEAD/group/config + + printf("\n*Config Listing*\n"); + + const char *email; + int j; + + git_config *cfg; + + // Open a config object so we can read global values from it. + git_config_open_ondisk(&cfg, "~/.gitconfig"); + + git_config_get_int(cfg, "help.autocorrect", &j); + printf("Autocorrect: %d\n", j); + + git_config_get_string(cfg, "user.email", &email); + printf("Email: %s\n", email); + + // Finally, when you're done with the repository, you can free it as well. + git_repository_free(repo); + + return 0; +} + diff --git a/vendor/libgit2/examples/showindex.c b/vendor/libgit2/examples/showindex.c new file mode 100644 index 000000000..7f2130b90 --- /dev/null +++ b/vendor/libgit2/examples/showindex.c @@ -0,0 +1,43 @@ +#include +#include + +int main (int argc, char** argv) +{ + git_repository *repo; + git_index *index; + unsigned int i, e, ecount; + git_index_entry **entries; + git_oid oid; + + char out[41]; + out[40] = '\0'; + + git_repository_open(&repo, "/opt/libgit2-test/.git"); + + git_repository_index(&index, repo); + git_index_read(index); + + ecount = git_index_entrycount(index); + for (i = 0; i < ecount; ++i) { + git_index_entry *e = git_index_get(index, i); + + oid = e->oid; + git_oid_fmt(out, &oid); + + printf("File Path: %s\n", e->path); + printf(" Blob SHA: %s\n", out); + printf("File Size: %d\n", (int)e->file_size); + printf(" Device: %d\n", (int)e->dev); + printf(" Inode: %d\n", (int)e->ino); + printf(" UID: %d\n", (int)e->uid); + printf(" GID: %d\n", (int)e->gid); + printf(" ctime: %d\n", (int)e->ctime.seconds); + printf(" mtime: %d\n", (int)e->mtime.seconds); + printf("\n"); + } + + git_index_free(index); + + git_repository_free(repo); +} + diff --git a/vendor/libgit2/include/git2.h b/vendor/libgit2/include/git2.h index d44c3f8df..96de524e7 100644 --- a/vendor/libgit2/include/git2.h +++ b/vendor/libgit2/include/git2.h @@ -26,9 +26,9 @@ #ifndef INCLUDE_git_git_h__ #define INCLUDE_git_git_h__ -#define LIBGIT2_VERSION "0.12.0" +#define LIBGIT2_VERSION "0.14.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 12 +#define LIBGIT2_VER_MINOR 14 #define LIBGIT2_VER_REVISION 0 #include "git2/common.h" @@ -44,6 +44,7 @@ #include "git2/repository.h" #include "git2/revwalk.h" #include "git2/refs.h" +#include "git2/reflog.h" #include "git2/object.h" #include "git2/blob.h" @@ -52,5 +53,13 @@ #include "git2/tree.h" #include "git2/index.h" +#include "git2/config.h" +#include "git2/remote.h" + +#include "git2/refspec.h" +#include "git2/net.h" +#include "git2/transport.h" +#include "git2/status.h" +#include "git2/indexer.h" #endif diff --git a/vendor/libgit2/include/git2/blob.h b/vendor/libgit2/include/git2/blob.h index 0e05d6f89..e366ce880 100644 --- a/vendor/libgit2/include/git2/blob.h +++ b/vendor/libgit2/include/git2/blob.h @@ -52,6 +52,23 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB); } +/** + * Lookup a blob object from a repository, + * given a prefix of its identifier (short id). + * + * @see git_object_lookup_prefix + * + * @param blob pointer to the looked up blob + * @param repo the repo to use when locating the blob. + * @param id identity of the blob to locate. + * @param len the length of the short identifier + * @return 0 on success; error code otherwise + */ +GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len) +{ + return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB); +} + /** * Close an open blob * diff --git a/vendor/libgit2/include/git2/branch.h b/vendor/libgit2/include/git2/branch.h new file mode 100644 index 000000000..456b7d1ac --- /dev/null +++ b/vendor/libgit2/include/git2/branch.h @@ -0,0 +1,9 @@ +#ifndef INCLUDE_branch_h__ +#define INCLUDE_branch_h__ + +struct git_branch { + char *remote; /* TODO: Make this a git_remote */ + char *merge; +}; + +#endif diff --git a/vendor/libgit2/include/git2/commit.h b/vendor/libgit2/include/git2/commit.h index 3687d9460..12646cf58 100644 --- a/vendor/libgit2/include/git2/commit.h +++ b/vendor/libgit2/include/git2/commit.h @@ -53,6 +53,24 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT); } +/** + * Lookup a commit object from a repository, + * given a prefix of its identifier (short id). + * + * @see git_object_lookup_prefix + * + * @param commit pointer to the looked up commit + * @param repo the repo to use when locating the commit. + * @param id identity of the commit to locate. If the object is + * an annotated tag it will be peeled back to the commit. + * @param len the length of the short identifier + * @return 0 on success; error code otherwise + */ +GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len) +{ + return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT); +} + /** * Close an open commit * @@ -79,12 +97,16 @@ GIT_INLINE(void) git_commit_close(git_commit *commit) GIT_EXTERN(const git_oid *) git_commit_id(git_commit *commit); /** - * Get the short (one line) message of a commit. + * Get the encoding for the message of a commit, + * as a string representing a standard encoding name. + * + * The encoding may be NULL if the `encoding` header + * in the commit is missing; in that case UTF-8 is assumed. * * @param commit a previously loaded commit. - * @return the short message of a commit + * @return NULL, or the encoding */ -GIT_EXTERN(const char *) git_commit_message_short(git_commit *commit); +GIT_EXTERN(const char *) git_commit_message_encoding(git_commit *commit); /** * Get the full message of a commit. @@ -175,8 +197,8 @@ GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsig GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned int n); /** - * Create a new commit in the repository - * + * Create a new commit in the repository using `git_object` + * instances as parameters. * * @param oid Pointer where to store the OID of the * newly created commit @@ -195,18 +217,23 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i * @param committer Signature representing the committer and the * commit time of this commit * + * @param message_encoding The encoding for the message in the + * commit, represented with a standard encoding name. + * E.g. "UTF-8". If NULL, no encoding header is written and + * UTF-8 is assumed. + * * @param message Full message for this commit * - * @param tree_oid Object ID of the tree for this commit. Note that - * no validation is performed on this OID. Use the _o variants of - * this method to assure a proper tree is passed to the commit. + * @param tree An instance of a `git_tree` object that will + * be used as the tree for the commit. This tree object must + * also be owned by the given `repo`. * * @param parent_count Number of parents for this commit * - * @param parents Array of pointers to parent OIDs for this commit. - * Note that no validation is performed on these OIDs. Use the _o - * variants of this method to assure that are parents for the commit - * are proper objects. + * @param parents[] Array of `parent_count` pointers to `git_commit` + * objects that will be used as the parents for this commit. This + * array may be NULL if `parent_count` is 0 (root commit). All the + * given commits must be owned by the `repo`. * * @return 0 on success; error code otherwise * The created commit will be written to the Object Database and @@ -218,39 +245,15 @@ GIT_EXTERN(int) git_commit_create( const char *update_ref, const git_signature *author, const git_signature *committer, - const char *message, - const git_oid *tree_oid, - int parent_count, - const git_oid *parent_oids[]); - -/** - * Create a new commit in the repository using `git_object` - * instances as parameters. - * - * The `tree_oid` and `parent_oids` paremeters now take a instance - * of `git_tree` and `git_commit`, respectively. - * - * All other parameters remain the same - * - * @see git_commit_create - */ -GIT_EXTERN(int) git_commit_create_o( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, + const char *message_encoding, const char *message, const git_tree *tree, int parent_count, const git_commit *parents[]); /** - * Create a new commit in the repository using `git_object` - * instances and a variable argument list. - * - * The `tree_oid` paremeter now takes a instance - * of `const git_tree *`. + * Create a new commit in the repository using a variable + * argument list. * * The parents for the commit are specified as a variable * list of pointers to `const git_commit *`. Note that this @@ -261,39 +264,15 @@ GIT_EXTERN(int) git_commit_create_o( * * @see git_commit_create */ -GIT_EXTERN(int) git_commit_create_ov( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_tree *tree, - int parent_count, - ...); - - -/** - * Create a new commit in the repository using - * a variable argument list. - * - * The parents for the commit are specified as a variable - * list of pointers to `const git_oid *`. Note that this - * is a convenience method which may not be safe to export - * for certain languages or compilers - * - * All other parameters remain the same - * - * @see git_commit_create - */ GIT_EXTERN(int) git_commit_create_v( git_oid *oid, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, + const char *message_encoding, const char *message, - const git_oid *tree_oid, + const git_tree *tree, int parent_count, ...); diff --git a/vendor/libgit2/include/git2/common.h b/vendor/libgit2/include/git2/common.h index 9a27ac2e5..58cb1f200 100644 --- a/vendor/libgit2/include/git2/common.h +++ b/vendor/libgit2/include/git2/common.h @@ -76,6 +76,10 @@ # define GIT_FORMAT_PRINTF(a,b) /* empty */ #endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#define GIT_WIN32 1 +#endif + /** * @file git2/common.h * @brief Git common platform definitions @@ -86,6 +90,22 @@ GIT_BEGIN_DECL +/** + * The separator used in path list strings (ie like in the PATH + * environment variable). A semi-colon ";" is used on Windows, and + * a colon ":" for all other systems. + */ +#ifdef GIT_WIN32 +#define GIT_PATH_LIST_SEPARATOR ';' +#else +#define GIT_PATH_LIST_SEPARATOR ':' +#endif + +/** + * The maximum length of a git valid git path. + */ +#define GIT_PATH_MAX 4096 + typedef struct { char **strings; size_t count; @@ -93,6 +113,16 @@ typedef struct { GIT_EXTERN(void) git_strarray_free(git_strarray *array); +/** + * Return the version of the libgit2 library + * being currently used. + * + * @param major Store the major version number + * @param minor Store the minor version number + * @param rev Store the revision (patch) number + */ +GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/config.h b/vendor/libgit2/include/git2/config.h new file mode 100644 index 000000000..e05d23694 --- /dev/null +++ b/vendor/libgit2/include/git2/config.h @@ -0,0 +1,284 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_config_h__ +#define INCLUDE_git_config_h__ + +#include "common.h" +#include "types.h" + +/** + * @file git2/config.h + * @brief Git config management routines + * @defgroup git_config Git config management routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Generic backend that implements the interface to + * access a configuration file + */ +struct git_config_file { + struct git_config *cfg; + + /* Open means open the file/database and parse if necessary */ + int (*open)(struct git_config_file *); + int (*get)(struct git_config_file *, const char *key, const char **value); + int (*set)(struct git_config_file *, const char *key, const char *value); + int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data); + void (*free)(struct git_config_file *); +}; + +/** + * Locate the path to the global configuration file + * + * The user or global configuration file is usually + * located in `$HOME/.gitconfig`. + * + * This method will try to guess the full path to that + * file, if the file exists. The returned path + * may be used on any `git_config` call to load the + * global configuration file. + * + * @param global_config_path Buffer of GIT_PATH_MAX length to store the path + * @return GIT_SUCCESS if a global configuration file has been + * found. Its path will be stored in `buffer`. + */ +GIT_EXTERN(int) git_config_find_global(char *global_config_path); + +/** + * Open the global configuration file + * + * Utility wrapper that calls `git_config_find_global` + * and opens the located file, if it exists. + * + * @param out Pointer to store the config instance + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_open_global(git_config **out); + +/** + * Create a configuration file backend for ondisk files + * + * These are the normal `.gitconfig` files that Core Git + * processes. Note that you first have to add this file to a + * configuration object before you can query it for configuration + * variables. + * + * @param out the new backend + * @param path where the config file is located + */ +GIT_EXTERN(int) git_config_file__ondisk(struct git_config_file **out, const char *path); + +/** + * Allocate a new configuration object + * + * This object is empty, so you have to add a file to it before you + * can do anything with it. + * + * @param out pointer to the new configuration + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_new(git_config **out); + +/** + * Add a generic config file instance to an existing config + * + * Note that the configuration object will free the file + * automatically. + * + * Further queries on this config object will access each + * of the config file instances in order (instances with + * a higher priority will be accessed first). + * + * @param cfg the configuration to add the file to + * @param file the configuration file (backend) to add + * @param priority the priority the backend should have + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority); + +/** + * Add an on-disk config file instance to an existing config + * + * The on-disk file pointed at by `path` will be opened and + * parsed; it's expected to be a native Git config file following + * the default Git config syntax (see man git-config). + * + * Note that the configuration object will free the file + * automatically. + * + * Further queries on this config object will access each + * of the config file instances in order (instances with + * a higher priority will be accessed first). + * + * @param cfg the configuration to add the file to + * @param path path to the configuration file (backend) to add + * @param priority the priority the backend should have + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, int priority); + + +/** + * Create a new config instance containing a single on-disk file + * + * This method is a simple utility wrapper for the following sequence + * of calls: + * - git_config_new + * - git_config_add_file_ondisk + * + * @param cfg The configuration instance to create + * @param path Path to the on-disk file to open + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path); + +/** + * Free the configuration and its associated memory and files + * + * @param cfg the configuration to free + */ +GIT_EXTERN(void) git_config_free(git_config *cfg); + +/** + * Get the value of an integer config variable. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param out pointer to the variable where the value should be stored + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_get_int(git_config *cfg, const char *name, int *out); + +/** + * Get the value of a long integer config variable. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param out pointer to the variable where the value should be stored + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_get_long(git_config *cfg, const char *name, long int *out); + +/** + * Get the value of a boolean config variable. + * + * This function uses the usual C convention of 0 being false and + * anything else true. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param out pointer to the variable where the value should be stored + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out); + +/** + * Get the value of a string config variable. + * + * The string is owned by the variable and should not be freed by the + * user. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param out pointer to the variable's value + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out); + +/** + * Set the value of an integer config variable. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param value Integer value for the variable + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_set_int(git_config *cfg, const char *name, int value); + +/** + * Set the value of a long integer config variable. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param value Long integer value for the variable + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_set_long(git_config *cfg, const char *name, long int value); + +/** + * Set the value of a boolean config variable. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param value the value to store + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value); + +/** + * Set the value of a string config variable. + * + * A copy of the string is made and the user is free to use it + * afterwards. + * + * @param cfg where to look for the variable + * @param name the variable's name + * @param value the string to store. + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value); + +/** + * Delete a config variable + * + * @param cfg the configuration + * @param name the variable to delete + */ +GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name); + +/** + * Perform an operation on each config variable. + * + * The callback receives the normalized name and value of each variable + * in the config backend, and the data pointer passed to this function. + * As soon as one of the callback functions returns something other than 0, + * this function returns that value. + * + * @param cfg where to get the variables from + * @param callback the function to call on each variable + * @param payload the data to pass to the callback + * @return GIT_SUCCESS or the return value of the callback which didn't return 0 + */ +GIT_EXTERN(int) git_config_foreach( + git_config *cfg, + int (*callback)(const char *var_name, const char *value, void *payload), + void *payload); + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/errors.h b/vendor/libgit2/include/git2/errors.h index dbe565aab..710ac244b 100644 --- a/vendor/libgit2/include/git2/errors.h +++ b/vendor/libgit2/include/git2/errors.h @@ -25,6 +25,8 @@ #ifndef INCLUDE_git_errors_h__ #define INCLUDE_git_errors_h__ +#include "common.h" + /** * @file git2/errors.h * @brief Git error handling routines and variables @@ -33,97 +35,103 @@ */ GIT_BEGIN_DECL -/** Operation completed successfully. */ -#define GIT_SUCCESS 0 +typedef enum { + GIT_SUCCESS = 0, + GIT_ERROR = -1, -/** - * Operation failed, with unspecified reason. - * This value also serves as the base error code; all other - * error codes are subtracted from it such that all errors - * are < 0, in typical POSIX C tradition. - */ -#define GIT_ERROR -1 + /** Input was not a properly formatted Git object id. */ + GIT_ENOTOID = -2, + + /** Input does not exist in the scope searched. */ + GIT_ENOTFOUND = -3, + + /** Not enough space available. */ + GIT_ENOMEM = -4, -/** Input was not a properly formatted Git object id. */ -#define GIT_ENOTOID (GIT_ERROR - 1) + /** Consult the OS error information. */ + GIT_EOSERR = -5, -/** Input does not exist in the scope searched. */ -#define GIT_ENOTFOUND (GIT_ERROR - 2) + /** The specified object is of invalid type */ + GIT_EOBJTYPE = -6, -/** Not enough space available. */ -#define GIT_ENOMEM (GIT_ERROR - 3) + /** The specified repository is invalid */ + GIT_ENOTAREPO = -7, -/** Consult the OS error information. */ -#define GIT_EOSERR (GIT_ERROR - 4) + /** The object type is invalid or doesn't match */ + GIT_EINVALIDTYPE = -8, -/** The specified object is of invalid type */ -#define GIT_EOBJTYPE (GIT_ERROR - 5) + /** The object cannot be written because it's missing internal data */ + GIT_EMISSINGOBJDATA = -9, -/** The specified object has its data corrupted */ -#define GIT_EOBJCORRUPTED (GIT_ERROR - 6) + /** The packfile for the ODB is corrupted */ + GIT_EPACKCORRUPTED = -10, -/** The specified repository is invalid */ -#define GIT_ENOTAREPO (GIT_ERROR - 7) + /** Failed to acquire or release a file lock */ + GIT_EFLOCKFAIL = -11, -/** The object type is invalid or doesn't match */ -#define GIT_EINVALIDTYPE (GIT_ERROR - 8) + /** The Z library failed to inflate/deflate an object's data */ + GIT_EZLIB = -12, -/** The object cannot be written because it's missing internal data */ -#define GIT_EMISSINGOBJDATA (GIT_ERROR - 9) + /** The queried object is currently busy */ + GIT_EBUSY = -13, -/** The packfile for the ODB is corrupted */ -#define GIT_EPACKCORRUPTED (GIT_ERROR - 10) + /** The index file is not backed up by an existing repository */ + GIT_EBAREINDEX = -14, -/** Failed to acquire or release a file lock */ -#define GIT_EFLOCKFAIL (GIT_ERROR - 11) + /** The name of the reference is not valid */ + GIT_EINVALIDREFNAME = -15, -/** The Z library failed to inflate/deflate an object's data */ -#define GIT_EZLIB (GIT_ERROR - 12) + /** The specified reference has its data corrupted */ + GIT_EREFCORRUPTED = -16, -/** The queried object is currently busy */ -#define GIT_EBUSY (GIT_ERROR - 13) + /** The specified symbolic reference is too deeply nested */ + GIT_ETOONESTEDSYMREF = -17, -/** The index file is not backed up by an existing repository */ -#define GIT_EBAREINDEX (GIT_ERROR - 14) + /** The pack-refs file is either corrupted or its format is not currently supported */ + GIT_EPACKEDREFSCORRUPTED = -18, -/** The name of the reference is not valid */ -#define GIT_EINVALIDREFNAME (GIT_ERROR - 15) + /** The path is invalid */ + GIT_EINVALIDPATH = -19, -/** The specified reference has its data corrupted */ -#define GIT_EREFCORRUPTED (GIT_ERROR - 16) + /** The revision walker is empty; there are no more commits left to iterate */ + GIT_EREVWALKOVER = -20, -/** The specified symbolic reference is too deeply nested */ -#define GIT_ETOONESTEDSYMREF (GIT_ERROR - 17) + /** The state of the reference is not valid */ + GIT_EINVALIDREFSTATE = -21, -/** The pack-refs file is either corrupted or its format is not currently supported */ -#define GIT_EPACKEDREFSCORRUPTED (GIT_ERROR - 18) + /** This feature has not been implemented yet */ + GIT_ENOTIMPLEMENTED = -22, -/** The path is invalid */ -#define GIT_EINVALIDPATH (GIT_ERROR - 19) + /** A reference with this name already exists */ + GIT_EEXISTS = -23, -/** The revision walker is empty; there are no more commits left to iterate */ -#define GIT_EREVWALKOVER (GIT_ERROR - 20) + /** The given integer literal is too large to be parsed */ + GIT_EOVERFLOW = -24, -/** The state of the reference is not valid */ -#define GIT_EINVALIDREFSTATE (GIT_ERROR - 21) + /** The given literal is not a valid number */ + GIT_ENOTNUM = -25, -/** This feature has not been implemented yet */ -#define GIT_ENOTIMPLEMENTED (GIT_ERROR - 22) + /** Streaming error */ + GIT_ESTREAM = -26, -/** A reference with this name already exists */ -#define GIT_EEXISTS (GIT_ERROR - 23) + /** invalid arguments to function */ + GIT_EINVALIDARGS = -27, -/** The given integer literal is too large to be parsed */ -#define GIT_EOVERFLOW (GIT_ERROR - 24) + /** The specified object has its data corrupted */ + GIT_EOBJCORRUPTED = -28, -/** The given literal is not a valid number */ -#define GIT_ENOTNUM (GIT_ERROR - 25) + /** The given short oid is ambiguous */ + GIT_EAMBIGUOUSOIDPREFIX = -29, -/** Streaming error */ -#define GIT_ESTREAM (GIT_ERROR - 26) + /** Skip and passthrough the given ODB backend */ + GIT_EPASSTHROUGH = -30, -/** invalid arguments to function */ -#define GIT_EINVALIDARGS (GIT_ERROR - 27) + /** The path pattern and string did not match */ + GIT_ENOMATCH = -31, + + /** The buffer is too short to satisfy the request */ + GIT_ESHORTBUFFER = -32, +} git_error; /** * Return a detailed error string with the latest error @@ -144,6 +152,11 @@ GIT_EXTERN(const char *) git_lasterror(void); */ GIT_EXTERN(const char *) git_strerror(int num); +/** + * Clear the latest library error + */ +GIT_EXTERN(void) git_clearerror(void); + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/index.h b/vendor/libgit2/include/git2/index.h index 2d8975ca1..18ab9b858 100644 --- a/vendor/libgit2/include/git2/index.h +++ b/vendor/libgit2/include/git2/index.h @@ -101,31 +101,32 @@ typedef struct git_index_entry { char *path; } git_index_entry; +/** Representation of an unmerged file entry in the index. */ +typedef struct git_index_entry_unmerged { + unsigned int mode[3]; + git_oid oid[3]; + char *path; +} git_index_entry_unmerged; /** - * Create a new Git index object as a memory representation + * Create a new bare Git index object as a memory representation * of the Git index file in 'index_path', without a repository * to back it. * - * Since there is no ODB behind this index, any Index methods - * which rely on the ODB (e.g. index_add) will fail with the - * GIT_EBAREINDEX error code. + * Since there is no ODB or working directory behind this index, + * any Index methods which rely on these (e.g. index_add) will + * fail with the GIT_EBAREINDEX error code. * - * @param index the pointer for the new index - * @param index_path the path to the index file in disk - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_index_open_bare(git_index **index, const char *index_path); - -/** - * Open the Index inside the git repository pointed - * by 'repo'. + * If you need to access the index of an actual repository, + * use the `git_repository_index` wrapper. + * + * The index must be freed once it's no longer in use. * * @param index the pointer for the new index - * @param repo the git repo which owns the index + * @param index_path the path to the index file in disk * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_index_open_inrepo(git_index **index, git_repository *repo); +GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path); /** * Clear the contents (all the entries) of an index object. @@ -171,6 +172,13 @@ GIT_EXTERN(int) git_index_write(git_index *index); */ GIT_EXTERN(int) git_index_find(git_index *index, const char *path); +/** + * Remove all entries with equal path except last added + * + * @param index an existing index object + */ +GIT_EXTERN(void) git_index_uniq(git_index *index); + /** * Add or update an index entry from a file in disk * @@ -250,11 +258,13 @@ GIT_EXTERN(int) git_index_remove(git_index *index, int position); * This entry can be modified, and the changes will be written * back to disk on the next write() call. * + * The entry should not be freed by the caller. + * * @param index an existing index object * @param n the position of the entry * @return a pointer to the entry; NULL if out of bounds */ -GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, int n); +GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, unsigned int n); /** * Get the count of entries currently in the index @@ -264,6 +274,50 @@ GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, int n); */ GIT_EXTERN(unsigned int) git_index_entrycount(git_index *index); +/** + * Get the count of unmerged entries currently in the index + * + * @param index an existing index object + * @return integer of count of current unmerged entries + */ +GIT_EXTERN(unsigned int) git_index_entrycount_unmerged(git_index *index); + +/** + * Get an unmerged entry from the index. + * + * The returned entry is read-only and should not be modified + * of freed by the caller. + * + * @param index an existing index object + * @param path path to search + * @return the unmerged entry; NULL if not found + */ +GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_bypath(git_index *index, const char *path); + +/** + * Get an unmerged entry from the index. + * + * The returned entry is read-only and should not be modified + * of freed by the caller. + * + * @param index an existing index object + * @param n the position of the entry + * @return a pointer to the unmerged entry; NULL if out of bounds + */ +GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, unsigned int n); + +/** + * Return the stage number from a git index entry + * + * This entry is calculated from the entrie's flag + * attribute like this: + * + * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT + * + * @param entry The entry + * @returns the stage number + */ +GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/indexer.h b/vendor/libgit2/include/git2/indexer.h new file mode 100644 index 000000000..2852d7e6f --- /dev/null +++ b/vendor/libgit2/include/git2/indexer.h @@ -0,0 +1,70 @@ +#ifndef _INCLUDE_git_indexer_h__ +#define _INCLUDE_git_indexer_h__ + +#include "git2/common.h" +#include "git2/oid.h" + +GIT_BEGIN_DECL + +/** + * This is passed as the first argument to the callback to allow the + * user to see the progress. + */ +typedef struct git_indexer_stats { + unsigned int total; + unsigned int processed; +} git_indexer_stats; + + +typedef struct git_indexer git_indexer; + +/** + * Create a new indexer instance + * + * @param out where to store the indexer instance + * @param packname the absolute filename of the packfile to index + */ +GIT_EXTERN(int) git_indexer_new(git_indexer **out, const char *packname); + +/** + * Iterate over the objects in the packfile and extract the information + * + * Indexing a packfile can be very expensive so this function is + * expected to be run in a worker thread and the stats used to provide + * feedback the user. + * + * @param idx the indexer instance + * @param stats storage for the running state + */ +GIT_EXTERN(int) git_indexer_run(git_indexer *idx, git_indexer_stats *stats); + +/** + * Write the index file to disk. + * + * The file will be stored as pack-$hash.idx in the same directory as + * the packfile. + * + * @param idx the indexer instance + */ +GIT_EXTERN(int) git_indexer_write(git_indexer *idx); + +/** + * Get the packfile's hash + * + * A packfile's name is derived from the sorted hashing of all object + * names. This is only correct after the index has been written to disk. + * + * @param idx the indexer instance + */ +GIT_EXTERN(const git_oid *) git_indexer_hash(git_indexer *idx); + +/** + * Free the indexer and its resources + * + * @param idx the indexer to free + */ +GIT_EXTERN(void) git_indexer_free(git_indexer *idx); + +GIT_END_DECL + +#endif diff --git a/vendor/libgit2/include/git2/net.h b/vendor/libgit2/include/git2/net.h new file mode 100644 index 000000000..d4f475527 --- /dev/null +++ b/vendor/libgit2/include/git2/net.h @@ -0,0 +1,71 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_net_h__ +#define INCLUDE_net_h__ + +#include "common.h" +#include "oid.h" +#include "types.h" + +/** + * @file git2/net.h + * @brief Git networking declarations + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +#define GIT_DEFAULT_PORT "9418" + +/* + * We need this because we need to know whether we should call + * git-upload-pack or git-receive-pack on the remote end when get_refs + * gets called. + */ + +#define GIT_DIR_FETCH 0 +#define GIT_DIR_PUSH 1 + +/** + * Remote head description, given out on `ls` calls. + */ +struct git_remote_head { + int local:1; /* available locally */ + git_oid oid; + git_oid loid; + char *name; +}; + +/** + * Array of remote heads + */ +struct git_headarray { + unsigned int len; + struct git_remote_head **heads; +}; + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/object.h b/vendor/libgit2/include/git2/object.h index 16dde8e56..07ba1a1c7 100644 --- a/vendor/libgit2/include/git2/object.h +++ b/vendor/libgit2/include/git2/object.h @@ -56,7 +56,45 @@ GIT_BEGIN_DECL * @param type the type of the object * @return a reference to the object */ -GIT_EXTERN(int) git_object_lookup(git_object **object, git_repository *repo, const git_oid *id, git_otype type); +GIT_EXTERN(int) git_object_lookup( + git_object **object, + git_repository *repo, + const git_oid *id, + git_otype type); + +/** + * Lookup a reference to one of the objects in a repostory, + * given a prefix of its identifier (short id). + * + * The object obtained will be so that its identifier + * matches the first 'len' hexadecimal characters + * (packets of 4 bits) of the given 'id'. + * 'len' must be at least GIT_OID_MINPREFIXLEN, and + * long enough to identify a unique object matching + * the prefix; otherwise the method will fail. + * + * The generated reference is owned by the repository and + * should be closed with the `git_object_close` method + * instead of free'd manually. + * + * The 'type' parameter must match the type of the object + * in the odb; the method will fail otherwise. + * The special value 'GIT_OBJ_ANY' may be passed to let + * the method guess the object's type. + * + * @param object_out pointer where to store the looked-up object + * @param repo the repository to look up the object + * @param id a short identifier for the object + * @param len the length of the short identifier + * @param type the type of the object + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_object_lookup_prefix( + git_object **object_out, + git_repository *repo, + const git_oid *id, + unsigned int len, + git_otype type); /** * Get the id (SHA1) of a repository object @@ -77,6 +115,12 @@ GIT_EXTERN(git_otype) git_object_type(const git_object *obj); /** * Get the repository that owns this object * + * Freeing or calling `git_repository_close` on the + * returned pointer will invalidate the actual object. + * + * Any other operation may be run on the repository without + * affecting the object. + * * @param obj the object * @return the repository who owns this object */ diff --git a/vendor/libgit2/include/git2/odb.h b/vendor/libgit2/include/git2/odb.h index 1d351beea..d0c369055 100644 --- a/vendor/libgit2/include/git2/odb.h +++ b/vendor/libgit2/include/git2/odb.h @@ -74,10 +74,14 @@ GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir); /** * Add a custom backend to an existing Object DB * + * The backends are checked in relative ordering, based on the + * value of the `priority` parameter. + * * Read for more information. * * @param odb database to add the backend to - * @paramm backend pointer to a git_odb_backend instance + * @param backend pointer to a git_odb_backend instance + * @param priority Value for ordering the backends queue * @return 0 on sucess; error code otherwise */ GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority); @@ -89,12 +93,16 @@ GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int * Alternate backends are always checked for objects *after* * all the main backends have been exhausted. * + * The backends are checked in relative ordering, based on the + * value of the `priority` parameter. + * * Writing is disabled on alternate backends. * * Read for more information. * * @param odb database to add the backend to - * @paramm backend pointer to a git_odb_backend instance + * @param backend pointer to a git_odb_backend instance + * @param priority Value for ordering the backends queue * @return 0 on sucess; error code otherwise */ GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority); @@ -109,7 +117,7 @@ GIT_EXTERN(void) git_odb_close(git_odb *db); /** * Read an object from the database. * - * This method queries all avaiable ODB backends + * This method queries all available ODB backends * trying to read the given OID. * * The returned object is reference counted and @@ -125,6 +133,34 @@ GIT_EXTERN(void) git_odb_close(git_odb *db); */ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id); +/** + * Read an object from the database, given a prefix + * of its identifier. + * + * This method queries all available ODB backends + * trying to match the 'len' first hexadecimal + * characters of the 'short_id'. + * The remaining (GIT_OID_HEXSZ-len)*4 bits of + * 'short_id' must be 0s. + * 'len' must be at least GIT_OID_MINPREFIXLEN, + * and the prefix must be long enough to identify + * a unique object in all the backends; the + * method will fail otherwise. + * + * The returned object is reference counted and + * internally cached, so it should be closed + * by the user once it's no longer in use. + * + * @param out pointer where to store the read object + * @param db database to search for the object in. + * @param short_id a prefix of the id of the object to read. + * @param len the length of the prefix + * @return GIT_SUCCESS if the object was read; + * GIT_ENOTFOUND if the object is not in the database. + * GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix) + */ +GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len); + /** * Read the header of an object from the database, without * reading its full contents. @@ -184,12 +220,12 @@ GIT_EXTERN(int) git_odb_write(git_oid *oid, git_odb *odb, const void *data, size * * The returned stream will be of type `GIT_STREAM_WRONLY` and * will have the following methods: - * + * * - stream->write: write `n` bytes into the stream * - stream->finalize_write: close the stream and store the object in * the odb * - stream->free: free the stream - * + * * The streaming write won't be effective until `stream->finalize_write` * is called and returns without an error * @@ -216,7 +252,7 @@ GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_ * * The returned stream will be of type `GIT_STREAM_RDONLY` and * will have the following methods: - * + * * - stream->read: read `n` bytes from the stream * - stream->free: free the stream * @@ -245,6 +281,19 @@ GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const */ GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type); +/** + * Read a file from disk and fill a git_oid with the object id + * that the file would have if it were written to the Object + * Database as an object of the given type. Similar functionality + * to git.git's `git hash-object` without the `-w` flag. + * + * @param out oid structure the result is written into. + * @param path file to read and determine object id for + * @param type the type of the object that will be hashed + * @return GIT_SUCCESS if valid; error code otherwise + */ +GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type); + /** * Close an ODB object * diff --git a/vendor/libgit2/include/git2/odb_backend.h b/vendor/libgit2/include/git2/odb_backend.h index ba41f726c..43a1c2d21 100644 --- a/vendor/libgit2/include/git2/odb_backend.h +++ b/vendor/libgit2/include/git2/odb_backend.h @@ -49,6 +49,19 @@ struct git_odb_backend { struct git_odb_backend *, const git_oid *); + /* To find a unique object given a prefix + * of its oid. + * The oid given must be so that the + * remaining (GIT_OID_HEXSZ - len)*4 bits + * are 0s. + */ + int (* read_prefix)( + git_oid *, + void **, size_t *, git_otype *, + struct git_odb_backend *, + const git_oid *, + unsigned int); + int (* read_header)( size_t *, git_otype *, struct git_odb_backend *, @@ -97,10 +110,8 @@ typedef enum { GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY), } git_odb_streammode; - GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir); GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir); -GIT_EXTERN(int) git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db); GIT_END_DECL diff --git a/vendor/libgit2/include/git2/oid.h b/vendor/libgit2/include/git2/oid.h index 4538c6147..8a0f134b9 100644 --- a/vendor/libgit2/include/git2/oid.h +++ b/vendor/libgit2/include/git2/oid.h @@ -43,31 +43,52 @@ GIT_BEGIN_DECL /** Size (in bytes) of a hex formatted oid */ #define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2) +/** Minimum length (in number of hex characters, + * i.e. packets of 4 bits) of an oid prefix */ +#define GIT_OID_MINPREFIXLEN 4 + /** Unique identity of any object (commit, tree, blob, tag). */ -typedef struct { +typedef struct _git_oid git_oid; +struct _git_oid { /** raw binary formatted id */ unsigned char id[GIT_OID_RAWSZ]; -} git_oid; +}; /** * Parse a hex formatted object id into a git_oid. + * * @param out oid structure the result is written into. * @param str input hex string; must be pointing at the start of * the hex sequence and have at least the number of bytes * needed for an oid encoded in hex (40 bytes). * @return GIT_SUCCESS if valid; GIT_ENOTOID on failure. */ -GIT_EXTERN(int) git_oid_mkstr(git_oid *out, const char *str); +GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str); + +/** + * Parse N characters of a hex formatted object id into a git_oid + * + * If N is odd, N-1 characters will be parsed instead. + * The remaining space in the git_oid will be set to zero. + * + * @param out oid structure the result is written into. + * @param str input hex string of at least size `length` + * @param length length of the input string + * @return GIT_SUCCESS if valid; GIT_ENOTOID on failure. + */ +GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length); /** * Copy an already raw oid into a git_oid structure. + * * @param out oid structure the result is written into. * @param raw the raw input bytes to be copied. */ -GIT_EXTERN(void) git_oid_mkraw(git_oid *out, const unsigned char *raw); +GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw); /** * Format a git_oid into a hex string. + * * @param str output hex string; must be pointing at the start of * the hex sequence and have at least the number of bytes * needed for an oid encoded in hex (40 bytes). Only the @@ -79,7 +100,7 @@ GIT_EXTERN(void) git_oid_fmt(char *str, const git_oid *oid); /** * Format a git_oid into a loose-object path string. - *

+ * * The resulting string is "aa/...", where "aa" is the first two * hex digitis of the oid and "..." is the remaining 38 digits. * @@ -93,7 +114,8 @@ GIT_EXTERN(void) git_oid_fmt(char *str, const git_oid *oid); GIT_EXTERN(void) git_oid_pathfmt(char *str, const git_oid *oid); /** - * Format a gid_oid into a newly allocated c-string. + * Format a git_oid into a newly allocated c-string. + * * @param oid the oid structure to format * @return the c-string; NULL if memory is exhausted. Caller must * deallocate the string with free(). @@ -102,7 +124,7 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *oid); /** * Format a git_oid into a buffer as a hex format c-string. - *

+ * * If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting * oid c-string will be truncated to n-1 characters. If there are * any input parameter errors (out == NULL, n == 0, oid == NULL), @@ -119,6 +141,7 @@ GIT_EXTERN(char *) git_oid_to_string(char *out, size_t n, const git_oid *oid); /** * Copy an oid from one structure to another. + * * @param out oid structure the result is written into. * @param src oid structure to copy from. */ @@ -126,12 +149,24 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src); /** * Compare two oid structures. + * * @param a first oid structure. * @param b second oid structure. * @return <0, 0, >0 if a < b, a == b, a > b. */ GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b); +/** + * Compare the first 'len' hexadecimal characters (packets of 4 bits) + * of two oid structures. + * + * @param a first oid structure. + * @param b second oid structure. + * @param len the number of hex chars to compare + * @return 0 in case of a match + */ +GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int len); + /** * OID Shortener object */ @@ -181,7 +216,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid); /** * Free an OID shortener instance - * + * * @param os a `git_oid_shorten` instance */ void git_oid_shorten_free(git_oid_shorten *os); diff --git a/vendor/libgit2/include/git2/reflog.h b/vendor/libgit2/include/git2/reflog.h new file mode 100644 index 000000000..53b344733 --- /dev/null +++ b/vendor/libgit2/include/git2/reflog.h @@ -0,0 +1,129 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_reflog_h__ +#define INCLUDE_git_reflog_h__ + +#include "common.h" +#include "types.h" +#include "oid.h" + +/** + * @file git2/reflog.h + * @brief Git reflog management routines + * @defgroup git_reflog Git reflog management routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Read the reflog for the given reference + * + * The reflog must be freed manually by using + * git_reflog_free(). + * + * @param reflog pointer to reflog + * @param ref reference to read the reflog for + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref); + +/** + * Write a new reflog for the given reference + * + * If there is no reflog file for the given + * reference yet, it will be created. + * + * `oid_old` may be NULL in case it's a new reference. + * + * `msg` is optional and can be NULL. + * + * @param ref the changed reference + * @param oid_old the OID the reference was pointing to + * @param committer the signature of the committer + * @param msg the reflog message + * @return GIT_SUCCESS on success; error code otherwise + */ +GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_signature *committer, const char *msg); + +/** + * Get the number of log entries in a reflog + * + * @param reflog the previously loaded reflog + * @return the number of log entries + */ +GIT_EXTERN(unsigned int) git_reflog_entrycount(git_reflog *reflog); + +/** + * Lookup an entry by its index + * + * @param reflog a previously loaded reflog + * @param idx the position to lookup + * @return the entry; NULL if not found + */ +GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx); + +/** + * Get the old oid + * + * @param entry a reflog entry + * @return the old oid + */ +GIT_EXTERN(const git_oid *) git_reflog_entry_oidold(const git_reflog_entry *entry); + +/** + * Get the new oid + * + * @param entry a reflog entry + * @return the new oid at this time + */ +GIT_EXTERN(const git_oid *) git_reflog_entry_oidnew(const git_reflog_entry *entry); + +/** + * Get the committer of this entry + * + * @param entry a reflog entry + * @return the committer + */ +GIT_EXTERN(git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry); + +/** + * Get the log msg + * + * @param entry a reflog entry + * @return the log msg + */ +GIT_EXTERN(char *) git_reflog_entry_msg(const git_reflog_entry *entry); + +/** + * Free the reflog + * + * @param reflog reflog to free + */ +GIT_EXTERN(void) git_reflog_free(git_reflog *reflog); + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/refs.h b/vendor/libgit2/include/git2/refs.h index 298c66d51..ff2bc9d87 100644 --- a/vendor/libgit2/include/git2/refs.h +++ b/vendor/libgit2/include/git2/refs.h @@ -60,34 +60,17 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito * This reference is owned by the repository and shall not * be free'd by the user. * - * @param ref_out Pointer to the newly created reference - * @param repo Repository where that reference will live - * @param name The name of the reference - * @param target The target of the reference - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target); - -/** - * Create a new symbolic reference, overwriting an existing one with - * the same name, if it exists. - * - * If the new reference isn't a symbolic one, any pointers to the old - * reference become invalid. - * - * The reference will be created in the repository and written - * to the disk. - * - * This reference is owned by the repository and shall not - * be free'd by the user. + * If `force` is true and there already exists a reference + * with the same name, it will be overwritten. * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param target The target of the reference + * @param force Overwrite existing references * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_reference_create_symbolic_f(git_reference **ref_out, git_repository *repo, const char *name, const char *target); +GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force); /** * Create a new object id reference. @@ -98,38 +81,21 @@ GIT_EXTERN(int) git_reference_create_symbolic_f(git_reference **ref_out, git_rep * This reference is owned by the repository and shall not * be free'd by the user. * - * @param ref_out Pointer to the newly created reference - * @param repo Repository where that reference will live - * @param name The name of the reference - * @param id The object id pointed to by the reference. - * @return 0 on success; error code otherwise - */ -GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id); - -/** - * Create a new object id reference, overwriting an existing one with - * the same name, if it exists. - * - * If the new reference isn't an object id one, any pointers to the - * old reference become invalid. - * - * The reference will be created in the repository and written - * to the disk. - * - * This reference is owned by the repository and shall not - * be free'd by the user. + * If `force` is true and there already exists a reference + * with the same name, it will be overwritten. * * @param ref_out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param id The object id pointed to by the reference. + * @param force Overwrite existing references * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_reference_create_oid_f(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id); +GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force); /** * Get the OID pointed to by a reference. - * + * * Only available if the reference is direct (i.e. not symbolic) * * @param ref The reference @@ -139,7 +105,7 @@ GIT_EXTERN(const git_oid *) git_reference_oid(git_reference *ref); /** * Get full name to the reference pointed by this reference - * + * * Only available if the reference is symbolic * * @param ref The reference @@ -166,7 +132,7 @@ GIT_EXTERN(git_rtype) git_reference_type(git_reference *ref); GIT_EXTERN(const char *) git_reference_name(git_reference *ref); /** - * Resolve a symbolic reference + * Resolve a symbolic reference * * Thie method iteratively peels a symbolic reference * until it resolves to a direct reference to an OID. @@ -213,7 +179,7 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target) * memory and on disk. * * @param ref The reference - * @param target The new target OID for the reference + * @param id The new target OID for the reference * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id); @@ -229,21 +195,7 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id); * and on disk. * */ -GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name); - -/** - * Rename an existing reference, overwriting an existing one with the - * same name, if it exists. - * - * This method works for both direct and symbolic references. - * The new name will be checked for validity and may be - * modified into a normalized form. - * - * The refernece will be immediately renamed in-memory - * and on disk. - * - */ -GIT_EXTERN(int) git_reference_rename_f(git_reference *ref, const char *new_name); +GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force); /** * Delete an existing reference @@ -260,7 +212,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref); * Pack all the loose references in the repository * * This method will load into the cache all the loose - * references on the repository and update the + * references on the repository and update the * `packed-refs` file with them. * * Once the `packed-refs` file has been written properly, @@ -299,10 +251,9 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, /** - * List all the references in the repository, calling a custom - * callback for each one. + * Perform an operation on each reference in the repository * - * The listed references may be filtered by type, or using + * The processed references may be filtered by type, or using * a bitwise OR of several types. Use the magic value * `GIT_REF_LISTALL` to obtain all references, including * packed ones. @@ -318,7 +269,7 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, * @param payload Additional data to pass to the callback * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload); +GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/refspec.h b/vendor/libgit2/include/git2/refspec.h new file mode 100644 index 000000000..dd0dc5873 --- /dev/null +++ b/vendor/libgit2/include/git2/refspec.h @@ -0,0 +1,78 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_refspec_h__ +#define INCLUDE_git_refspec_h__ + +#include "git2/types.h" + +/** + * @file git2/refspec.h + * @brief Git refspec attributes + * @defgroup git_refspec Git refspec attributes + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Get the source specifier + * + * @param refspec the refspec + * @return the refspec's source specifier + */ +const char *git_refspec_src(const git_refspec *refspec); + +/** + * Get the destination specifier + * + * @param refspec the refspec + * @return the refspec's destination specifier + */ +const char *git_refspec_dst(const git_refspec *refspec); + +/** + * Match a refspec's source descriptor with a reference name + * + * @param refspec the refspec + * @param refname the name of the reference to check + * @return GIT_SUCCESS on successful match; GIT_ENOMACH on match + * failure or an error code on other failure + */ +int git_refspec_src_match(const git_refspec *refspec, const char *refname); + +/** + * Transform a reference to its target following the refspec's rules + * + * @param out where to store the target name + * @param outlen the size ouf the `out` buffer + * @param spec the refspec + * @param name the name of the reference to transform + * @preturn GIT_SUCCESS, GIT_ESHORTBUFFER or another error + */ +int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name); + +GIT_END_DECL + +#endif diff --git a/vendor/libgit2/include/git2/remote.h b/vendor/libgit2/include/git2/remote.h new file mode 100644 index 000000000..5ce876e07 --- /dev/null +++ b/vendor/libgit2/include/git2/remote.h @@ -0,0 +1,168 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_remote_h__ +#define INCLUDE_git_remote_h__ + +#include "git2/common.h" +#include "git2/repository.h" +#include "refspec.h" +/** + * @file git2/remote.h + * @brief Git remote management functions + * @defgroup git_remote remote management functions + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/* + * TODO: This functions still need to be implemented: + * - _listcb/_foreach + * - _add + * - _rename + * - _del (needs support from config) + */ + +/** + * Create a new unnamed remote + * + * Useful when you don't want to store the remote + * + * @param out pointer to the new remote object + * @param repo the associtated repository + * @param url the remote repository's URL + * @return GIT_SUCCESS or an error message + */ +int git_remote_new(git_remote **out, git_repository *repo, const char *url); + +/** + * Get the information for a particular remote + * + * @param out pointer to the new remote object + * @param cfg the repository's configuration + * @param name the remote's name + * @return 0 on success; error value otherwise + */ +GIT_EXTERN(int) git_remote_get(struct git_remote **out, struct git_config *cfg, const char *name); + +/** + * Get the remote's name + * + * @param remote the remote + * @return a pointer to the name + */ +GIT_EXTERN(const char *) git_remote_name(struct git_remote *remote); + +/** + * Get the remote's url + * + * @param remote the remote + * @return a pointer to the url + */ +GIT_EXTERN(const char *) git_remote_url(struct git_remote *remote); + +/** + * Get the fetch refspec + * + * @param remote the remote + * @return a pointer to the fetch refspec or NULL if it doesn't exist + */ +GIT_EXTERN(const git_refspec *) git_remote_fetchspec(struct git_remote *remote); + +/** + * Get the push refspec + * + * @param remote the remote + * @return a pointer to the push refspec or NULL if it doesn't exist + */ + +GIT_EXTERN(const git_refspec *) git_remote_pushspec(struct git_remote *remote); + +/** + * Open a connection to a remote + * + * The transport is selected based on the URL. The direction argument + * is due to a limitation of the git protocol (over TCP or SSH) which + * starts up a specific binary which can only do the one or the other. + * + * @param remote the remote to connect to + * @param direction whether you want to receive or send data + * @return GIT_SUCCESS or an error code + */ +GIT_EXTERN(int) git_remote_connect(struct git_remote *remote, int direction); + +/** + * Get a list of refs at the remote + * + * The remote (or more exactly its transport) must be connected. + * + * @param refs where to store the refs + * @param remote the remote + * @return GIT_SUCCESS or an error code + */ +GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs); + +/** + * Negotiate what data needs to be exchanged to synchroize the remtoe + * and local references + * + * @param remote the remote you want to negotiate with + */ +GIT_EXTERN(int) git_remote_negotiate(git_remote *remote); + +/** + * Download the packfile + * + * The packfile is downloaded with a temporary filename, as it's final + * name is not known yet. If there was no packfile needed (all the + * objects were available locally), filename will be NULL and the + * function will return success. + * + * @param remote the remote to download from + * @param filename where to store the temproray filename + * @return GIT_SUCCESS or an error code + */ +GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote); + +/** + * Free the memory associated with a remote + * + * @param remote the remote to free + */ +GIT_EXTERN(void) git_remote_free(struct git_remote *remote); + +/** + * Update the tips to the new state + * + * Make sure that you only call this once you've successfully indexed + * or expanded the packfile. + * + * @param remote the remote to update + */ +GIT_EXTERN(int) git_remote_update_tips(struct git_remote *remote); + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/repository.h b/vendor/libgit2/include/git2/repository.h index c47fcfc9a..4088ff7f9 100644 --- a/vendor/libgit2/include/git2/repository.h +++ b/vendor/libgit2/include/git2/repository.h @@ -71,7 +71,7 @@ GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *pat * * @param git_dir The full path to the repository folder * e.g. a '.git' folder for live repos, any folder for bare - * Equivalent to $GIT_DIR. + * Equivalent to $GIT_DIR. * Cannot be NULL. * * @param git_object_directory The full path to the ODB folder. @@ -105,7 +105,7 @@ GIT_EXTERN(int) git_repository_open2(git_repository **repository, * * @param git_dir The full path to the repository folder * e.g. a '.git' folder for live repos, any folder for bare - * Equivalent to $GIT_DIR. + * Equivalent to $GIT_DIR. * Cannot be NULL. * * @param object_database A pointer to a git_odb created & initialized @@ -132,6 +132,34 @@ GIT_EXTERN(int) git_repository_open3(git_repository **repository, const char *git_index_file, const char *git_work_tree); +/** + * Look for a git repository and copy its path in the given buffer. The lookup start + * from base_path and walk across parent directories if nothing has been found. The + * lookup ends when the first repository is found, or when reaching a directory + * referenced in ceiling_dirs or when the filesystem changes (in case across_fs + * is true). + * + * The method will automatically detect if the repository is bare (if there is + * a repository). + * + * @param repository_path The user allocated buffer which will contain the found path. + * + * @param size repository_path size + * + * @param start_path The base path where the lookup starts. + * + * @param across_fs If true, then the lookup will not stop when a filesystem device change + * is detected while exploring parent directories. + * + * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR separated list of absolute symbolic link + * free paths. The lookup will stop when any of this paths is reached. Note that the + * lookup always performs on start_path no matter start_path appears in ceiling_dirs + * ceiling_dirs might be NULL (which is equivalent to an empty string) + * + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs); + /** * Get the object database behind a Git repository * @@ -141,10 +169,18 @@ GIT_EXTERN(int) git_repository_open3(git_repository **repository, GIT_EXTERN(git_odb *) git_repository_database(git_repository *repo); /** - * Get the Index file of a Git repository + * Open the Index file of a Git repository * - * This is a cheap operation; the index is only opened on the first call, - * and subsequent calls only retrieve the previous pointer. + * This returns a new and unique `git_index` object representing the + * active index for the repository. + * + * This method may be called more than once (e.g. on different threads). + * + * Each returned `git_index` object is independent and suffers no race + * conditions: synchronization is done at the FS level. + * + * Each returned `git_index` object must be manually freed by the user, + * using `git_index_free`. * * @param index Pointer where to store the index * @param repo a repository object @@ -174,14 +210,38 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo); * * @param repo_out pointer to the repo which will be created or reinitialized * @param path the path to the repository - * @param is_bare if true, a Git repository without a working directory is created - * at the pointed path. If false, provided path will be considered as the working + * @param is_bare if true, a Git repository without a working directory is created + * at the pointed path. If false, provided path will be considered as the working * directory into which the .git directory will be created. * * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare); +/** + * Check if a repository's HEAD is detached + * + * A repository's HEAD is detached when it points directly to a commit + * instead of a branch. + * + * @param repo Repo to test + * @return 1 if HEAD is detached, 0 if i'ts not; error code if there + * was an error. + */ +GIT_EXTERN(int) git_repository_head_detached(git_repository *repo); + +/** + * Check if the current branch is an orphan + * + * An orphan branch is one named from HEAD but which doesn't exist in + * the refs namespace, because it doesn't have any commit to point to. + * + * @param repo Repo to test + * @return 1 if the current branch is an orphan, 0 if it's not; error + * code if therewas an error + */ +GIT_EXTERN(int) git_repository_head_orphan(git_repository *repo); + /** * Check if a repository is empty * @@ -195,22 +255,77 @@ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, GIT_EXTERN(int) git_repository_is_empty(git_repository *repo); /** - * Get the normalized path to the git repository. + * Internal path identifiers for a repository + */ +typedef enum { + GIT_REPO_PATH, + GIT_REPO_PATH_INDEX, + GIT_REPO_PATH_ODB, + GIT_REPO_PATH_WORKDIR +} git_repository_pathid; + +/** + * Get one of the paths to the repository + * + * Possible values for `id`: + * + * GIT_REPO_PATH: return the path to the repository + * GIT_REPO_PATH_INDEX: return the path to the index + * GIT_REPO_PATH_ODB: return the path to the ODB + * GIT_REPO_PATH_WORKDIR: return the path to the working + * directory * * @param repo a repository object - * @return absolute path to the git directory + * @param id The ID of the path to return + * @return absolute path of the requested id */ -GIT_EXTERN(const char *) git_repository_path(git_repository *repo); +GIT_EXTERN(const char *) git_repository_path(git_repository *repo, git_repository_pathid id); /** - * Get the normalized path to the working directory of the repository. + * Check if a repository is bare * - * If the repository is bare, there is no working directory and NULL we be returned. + * @param repo Repo to test + * @return 1 if the repository is empty, 0 otherwise. + */ +GIT_EXTERN(int) git_repository_is_bare(git_repository *repo); + +/** + * Retrieve the relevant configuration for a repository * - * @param repo a repository object - * @return NULL if the repository is bare; absolute path to the working directory otherwise. + * By default he returned `git_config` instance contains a single + * configuration file, the `.gitconfig' file that may be found + * inside the repository. + * + * If the `user_config_path` variable is not NULL, the given config + * file will be also included in the configuration set. On most UNIX + * systems, this file may be found on `$HOME/.gitconfig`. + * + * If the `system_config_path` variable is not NULL, the given config + * file will be also included in the configuration set. On most UNIX + * systems, this file may be found on `$PREFIX/etc/gitconfig`. + * + * The resulting `git_config` instance will query the files in the following + * order: + * + * - Repository configuration file + * - User configuration file + * - System configuration file + * + * The method will fail if any of the passed config files cannot be + * found or accessed. + * + * The returned `git_config` instance is owned by the caller and must + * be manually free'd once it's no longer on use. + * + * @param out the repository's configuration + * @param repo the repository for which to get the config + * @param user_config_path Path to the user config file + * @param system_config_path Path to the system-wide config file */ -GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo); +GIT_EXTERN(int) git_repository_config(git_config **out, + git_repository *repo, + const char *user_config_path, + const char *system_config_path); /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/include/git2/revwalk.h b/vendor/libgit2/include/git2/revwalk.h index f3e0152d4..b37a16c83 100644 --- a/vendor/libgit2/include/git2/revwalk.h +++ b/vendor/libgit2/include/git2/revwalk.h @@ -113,7 +113,7 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker); * must be pushed the repository before a walk can * be started. * - * @param walker the walker being used for the traversal. + * @param walk the walker being used for the traversal. * @param oid the oid of the commit to start from. * @return 0 on success; error code otherwise */ @@ -129,8 +129,8 @@ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid); * The resolved commit and all its parents will be hidden from the * output on the revision walk. * - * @param walker the walker being used for the traversal. - * @param commit the commit that will be ignored during the traversal + * @param walk the walker being used for the traversal. + * @param oid the oid of commit that will be ignored during the traversal * @return 0 on success; error code otherwise */ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid); diff --git a/vendor/libgit2/include/git2/signature.h b/vendor/libgit2/include/git2/signature.h index 44d1f285e..f5d03ac77 100644 --- a/vendor/libgit2/include/git2/signature.h +++ b/vendor/libgit2/include/git2/signature.h @@ -41,23 +41,25 @@ GIT_BEGIN_DECL * Create a new action signature. The signature must be freed * manually or using git_signature_free * + * @param sig_out new signature, in case of error NULL * @param name name of the person - * @param mail email of the person + * @param email email of the person * @param time time when the action happened * @param offset timezone offset in minutes for the time - * @return the new sig, NULL on out of memory + * @return 0 on success; error code otherwise */ -GIT_EXTERN(git_signature *) git_signature_new(const char *name, const char *email, git_time_t time, int offset); +GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset); /** * Create a new action signature with a timestamp of 'now'. The * signature must be freed manually or using git_signature_free * + * @param sig_out new signature, in case of error NULL * @param name name of the person * @param email email of the person - * @return the new sig, NULL on out of memory + * @return 0 on success; error code otherwise */ -GIT_EXTERN(git_signature *) git_signature_now(const char *name, const char *email); +GIT_EXTERN(int) git_signature_now(git_signature **sig_out, const char *name, const char *email); /** diff --git a/vendor/libgit2/include/git2/status.h b/vendor/libgit2/include/git2/status.h new file mode 100644 index 000000000..7946cc1f3 --- /dev/null +++ b/vendor/libgit2/include/git2/status.h @@ -0,0 +1,79 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_status_h__ +#define INCLUDE_git_status_h__ + +#include "common.h" +#include "types.h" + +/** + * @file git2/status.h + * @brief Git file status routines + * @defgroup git_status Git file status routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +#define GIT_STATUS_CURRENT 0 +/** Flags for index status */ +#define GIT_STATUS_INDEX_NEW (1 << 0) +#define GIT_STATUS_INDEX_MODIFIED (1 << 1) +#define GIT_STATUS_INDEX_DELETED (1 << 2) + +/** Flags for worktree status */ +#define GIT_STATUS_WT_NEW (1 << 3) +#define GIT_STATUS_WT_MODIFIED (1 << 4) +#define GIT_STATUS_WT_DELETED (1 << 5) + +// TODO Ignored files not handled yet +#define GIT_STATUS_IGNORED (1 << 6) + +/** + * Gather file statuses and run a callback for each one. + * + * The callback is passed the path of the file, the status and the data pointer + * passed to this function. If the callback returns something other than + * GIT_SUCCESS, this function will return that value. + * + * @param repo a repository object + * @param callback the function to call on each file + * @return GIT_SUCCESS or the return value of the callback which did not return 0; + */ +GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload); + +/** + * Get file status for a single file + * + * @param status_flags the status value + * @param repo a repository object + * @param path the file to retrieve status for, rooted at the repo's workdir + * @return GIT_SUCCESS + */ +GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo, const char *path); + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/tag.h b/vendor/libgit2/include/git2/tag.h index 3fc6b4499..2b10d4525 100644 --- a/vendor/libgit2/include/git2/tag.h +++ b/vendor/libgit2/include/git2/tag.h @@ -52,6 +52,23 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG); } +/** + * Lookup a tag object from the repository, + * given a prefix of its identifier (short id). + * + * @see git_object_lookup_prefix + * + * @param tag pointer to the looked up tag + * @param repo the repo to use when locating the tag. + * @param id identity of the tag to locate. + * @param len the length of the short identifier + * @return 0 on success; error code otherwise + */ +GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len) +{ + return git_object_lookup_prefix((git_object **)tag, repo, id, len, (git_otype)GIT_OBJ_TAG); +} + /** * Close an open tag * @@ -88,7 +105,7 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag); * @param tag a previously loaded tag. * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *t); +GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *tag); /** * Get the OID of the tagged object of a tag @@ -96,7 +113,7 @@ GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *t); * @param tag a previously loaded tag. * @return pointer to the OID */ -GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *t); +GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *tag); /** * Get the type of a tag's tagged object @@ -104,7 +121,7 @@ GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *t); * @param tag a previously loaded tag. * @return type of the tagged object */ -GIT_EXTERN(git_otype) git_tag_type(git_tag *t); +GIT_EXTERN(git_otype) git_tag_type(git_tag *tag); /** * Get the name of a tag @@ -112,7 +129,7 @@ GIT_EXTERN(git_otype) git_tag_type(git_tag *t); * @param tag a previously loaded tag. * @return name of the tag */ -GIT_EXTERN(const char *) git_tag_name(git_tag *t); +GIT_EXTERN(const char *) git_tag_name(git_tag *tag); /** * Get the tagger (author) of a tag @@ -120,7 +137,7 @@ GIT_EXTERN(const char *) git_tag_name(git_tag *t); * @param tag a previously loaded tag. * @return reference to the tag's author */ -GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *t); +GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *tag); /** * Get the message of a tag @@ -128,138 +145,98 @@ GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *t); * @param tag a previously loaded tag. * @return message of the tag */ -GIT_EXTERN(const char *) git_tag_message(git_tag *t); +GIT_EXTERN(const char *) git_tag_message(git_tag *tag); /** - * Create a new tag in the repository from an OID + * Create a new tag in the repository from an object + * + * A new reference will also be created pointing to + * this tag object. If `force` is true and a reference + * already exists with the given name, it'll be replaced. * * @param oid Pointer where to store the OID of the - * newly created tag + * newly created tag. If the tag already exists, this parameter + * will be the oid of the existing tag, and the function will + * return a GIT_EEXISTS error code. * * @param repo Repository where to store the tag * * @param tag_name Name for the tag; this name is validated - * for consistency. It should also not conflict with an + * for consistency. It should also not conflict with an * already existing tag name * - * @param target OID to which this tag points; note that no - * validation is done on this OID. Use the _o version of this - * method to assure a proper object is being tagged - * - * @param target_type Type of the tagged OID; note that no - * validation is performed here either + * @param target Object to which this tag points. This object + * must belong to the given `repo`. * * @param tagger Signature of the tagger for this tag, and * of the tagging time * * @param message Full message for this tag * + * @param force Overwrite existing references + * * @return 0 on success; error code otherwise. * A tag object is written to the ODB, and a proper reference * is written in the /refs/tags folder, pointing to it */ GIT_EXTERN(int) git_tag_create( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_oid *target, - git_otype target_type, - const git_signature *tagger, - const char *message); - - -/** - * Create a new tag in the repository from an existing - * `git_object` instance - * - * This method replaces the `target` and `target_type` - * paremeters of `git_tag_create` by a single instance - * of a `const git_object *`, which is assured to be - * a proper object in the ODB and hence will create - * a valid tag - * - * @see git_tag_create - */ -GIT_EXTERN(int) git_tag_create_o( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, const git_signature *tagger, - const char *message); + const char *message, + int force); /** * Create a new tag in the repository from a buffer * * @param oid Pointer where to store the OID of the newly created tag - * * @param repo Repository where to store the tag - * * @param buffer Raw tag data + * @param force Overwrite existing tags + * @return 0 on sucess; error code otherwise */ GIT_EXTERN(int) git_tag_create_frombuffer( git_oid *oid, git_repository *repo, - const char *buffer); + const char *buffer, + int force); /** - * Create a new tag in the repository from an OID - * and overwrite an already existing tag reference, if any. + * Create a new lightweight tag pointing at a target object * - * @param oid Pointer where to store the OID of the - * newly created tag + * A new direct reference will be created pointing to + * this target object. If `force` is true and a reference + * already exists with the given name, it'll be replaced. * - * @param repo Repository where to store the tag + * @param oid Pointer where to store the OID of the provided + * target object. If the tag already exists, this parameter + * will be filled with the oid of the existing pointed object + * and the function will return a GIT_EEXISTS error code. + * + * @param repo Repository where to store the lightweight tag * * @param tag_name Name for the tag; this name is validated - * for consistency. + * for consistency. It should also not conflict with an + * already existing tag name * - * @param target OID to which this tag points; note that no - * validation is done on this OID. Use the _fo version of this - * method to assure a proper object is being tagged + * @param target Object to which this tag points. This object + * must belong to the given `repo`. * - * @param target_type Type of the tagged OID; note that no - * validation is performed here either - * - * @param tagger Signature of the tagger for this tag, and - * of the tagging time - * - * @param message Full message for this tag + * @param force Overwrite existing references * * @return 0 on success; error code otherwise. - * A tag object is written to the ODB, and a proper reference - * is written in the /refs/tags folder, pointing to it - */ -GIT_EXTERN(int) git_tag_create_f( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_oid *target, - git_otype target_type, - const git_signature *tagger, - const char *message); - -/** - * Create a new tag in the repository from an existing - * `git_object` instance and overwrite an already existing - * tag reference, if any. - * - * This method replaces the `target` and `target_type` - * paremeters of `git_tag_create_f` by a single instance - * of a `const git_object *`, which is assured to be - * a proper object in the ODB and hence will create - * a valid tag - * - * @see git_tag_create_f + * A proper reference is written in the /refs/tags folder, + * pointing to the provided target object */ -GIT_EXTERN(int) git_tag_create_fo( +GIT_EXTERN(int) git_tag_create_lightweight( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, - const git_signature *tagger, - const char *message); + int force); /** * Delete an existing tag reference. @@ -283,7 +260,7 @@ GIT_EXTERN(int) git_tag_delete( * should be free'd manually when no longer needed, using * `git_strarray_free`. * - * @param array Pointer to a git_strarray structure where + * @param tag_names Pointer to a git_strarray structure where * the tag names will be stored * @param repo Repository where to find the tags * @return 0 on success; error code otherwise @@ -292,6 +269,29 @@ GIT_EXTERN(int) git_tag_list( git_strarray *tag_names, git_repository *repo); +/** + * Fill a list with all the tags in the Repository + * which name match a defined pattern + * + * If an empty pattern is provided, all the tags + * will be returned. + * + * The string array will be filled with the names of the + * matching tags; these values are owned by the user and + * should be free'd manually when no longer needed, using + * `git_strarray_free`. + * + * @param tag_names Pointer to a git_strarray structure where + * the tag names will be stored + * @param pattern Standard fnmatch pattern + * @param repo Repository where to find the tags + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_tag_list_match( + git_strarray *tag_names, + const char *pattern, + git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/vendor/libgit2/include/git2/thread-utils.h b/vendor/libgit2/include/git2/thread-utils.h index e26876bea..62e6199a4 100644 --- a/vendor/libgit2/include/git2/thread-utils.h +++ b/vendor/libgit2/include/git2/thread-utils.h @@ -31,41 +31,48 @@ * http://predef.sourceforge.net/precomp.html */ -#define GIT_HAS_TLS 1 +#ifdef GIT_THREADS +# define GIT_HAS_TLS 1 -#if defined(__APPLE__) && defined(__MACH__) -# undef GIT_TLS -# define GIT_TLS +/* No TLS in Cygwin */ +# if defined(__CHECKER__) || defined(__CYGWIN__) +# undef GIT_HAS_TLS +# define GIT_TLS -#elif defined(__GNUC__) || \ - defined(__SUNPRO_C) || \ - defined(__SUNPRO_CC) || \ - defined(__xlc__) || \ - defined(__xlC__) -# define GIT_TLS __thread +/* No TLS in Mach binaries for Mac OS X */ +# elif defined(__APPLE__) && defined(__MACH__) +# undef GIT_TLS +# define GIT_TLS -#elif defined(__INTEL_COMPILER) -# if defined(_WIN32) || defined(_WIN32_CE) -# define GIT_TLS __declspec(thread) -# else -# define GIT_TLS __thread -# endif +/* Normal TLS for GCC */ +# elif defined(__GNUC__) || \ + defined(__SUNPRO_C) || \ + defined(__SUNPRO_CC) || \ + defined(__xlc__) || \ + defined(__xlC__) +# define GIT_TLS __thread -#elif defined(_WIN32) || \ - defined(_WIN32_CE) || \ - defined(__BORLANDC__) -# define GIT_TLS __declspec(thread) +/* ICC may run on Windows or Linux */ +# elif defined(__INTEL_COMPILER) +# if defined(_WIN32) || defined(_WIN32_CE) +# define GIT_TLS __declspec(thread) +# else +# define GIT_TLS __thread +# endif -#else -# undef GIT_HAS_TLS -# define GIT_TLS /* nothing: tls vars are thread-global */ -#endif +/* Declspec for MSVC in Win32 */ +# elif defined(_WIN32) || \ + defined(_WIN32_CE) || \ + defined(__BORLANDC__) +# define GIT_TLS __declspec(thread) -/* sparse and cygwin don't grok thread-local variables */ -#if defined(__CHECKER__) || defined(__CYGWIN__) -# undef GIT_HAS_TLS -# undef GIT_TLS -# define GIT_TLS -#endif +/* Other platform; no TLS */ +# else +# undef GIT_HAS_TLS +# define GIT_TLS /* nothing: tls vars are thread-global */ +# endif +#else /* Disable TLS if libgit2 is not threadsafe */ +# define GIT_TLS +#endif /* GIT_THREADS */ #endif /* INCLUDE_git_thread_utils_h__ */ diff --git a/vendor/libgit2/include/git2/transport.h b/vendor/libgit2/include/git2/transport.h new file mode 100644 index 000000000..d19eb8a88 --- /dev/null +++ b/vendor/libgit2/include/git2/transport.h @@ -0,0 +1,50 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#ifndef INCLUDE_git_transport_h__ +#define INCLUDE_git_transport_h__ + +#include "common.h" +#include "types.h" +#include "net.h" + +/** + * @file git2/transport.h + * @brief Git protocol transport abstraction + * @defgroup git_transport Git protocol transport abstraction + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Get the appropriate transport for an URL. + * @param tranport the transport for the url + * @param url the url of the repo + */ +GIT_EXTERN(int) git_transport_new(git_transport **transport, const char *url); + +/** @} */ +GIT_END_DECL +#endif diff --git a/vendor/libgit2/include/git2/tree.h b/vendor/libgit2/include/git2/tree.h index 0caf60a48..5656f48f3 100644 --- a/vendor/libgit2/include/git2/tree.h +++ b/vendor/libgit2/include/git2/tree.h @@ -52,6 +52,23 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE); } +/** + * Lookup a tree object from the repository, + * given a prefix of its identifier (short id). + * + * @see git_object_lookup_prefix + * + * @param tree pointer to the looked up tree + * @param repo the repo to use when locating the tree. + * @param id identity of the tree to locate. + * @param len the length of the short identifier + * @return 0 on success; error code otherwise + */ +GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len) +{ + return git_object_lookup_prefix((git_object **)tree, repo, id, len, GIT_OBJ_TREE); +} + /** * Close an open tree * @@ -84,7 +101,7 @@ GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree); * @param tree a previously loaded tree. * @return the number of entries in the tree */ -GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree); +GIT_EXTERN(unsigned int) git_tree_entrycount(git_tree *tree); /** * Lookup a tree entry by its filename @@ -102,7 +119,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const c * @param idx the position in the entry list * @return the tree entry; NULL if not found */ -GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx); +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, unsigned int idx); /** * Get the UNIX file attributes of a tree entry @@ -128,6 +145,14 @@ GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry); */ GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry); +/** + * Get the type of the object pointed by the entry + * + * @param entry a tree entry + * @return the type of the pointed object + */ +GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry); + /** * Convert a tree entry to the git_object it points too. * @@ -165,7 +190,7 @@ GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index); * * If the `source` parameter is not NULL, the tree builder * will be initialized with the entries of the given tree. - * + * * If the `source` parameter is NULL, the tree builder will * have no entries and will have to be filled manually. * diff --git a/vendor/libgit2/include/git2/types.h b/vendor/libgit2/include/git2/types.h index 6123abc82..b9db4e529 100644 --- a/vendor/libgit2/include/git2/types.h +++ b/vendor/libgit2/include/git2/types.h @@ -25,6 +25,8 @@ #ifndef INCLUDE_git_types_h__ #define INCLUDE_git_types_h__ +#include "common.h" + /** * @file git2/types.h * @brief libgit2 base & compatibility types @@ -59,9 +61,14 @@ typedef __time64_t git_time_t; typedef off64_t git_off_t; typedef __time64_t git_time_t; +#elif defined(__HAIKU__) + +typedef __haiku_std_int64 git_off_t; +typedef __haiku_std_int64 git_time_t; + #else /* POSIX */ -/* +/* * Note: Can't use off_t since if a client program includes * before us (directly or indirectly), they'll get 32 bit off_t in their client * app, even though /we/ define _FILE_OFFSET_BITS=64. @@ -130,6 +137,18 @@ typedef struct git_treebuilder git_treebuilder; /** Memory representation of an index file. */ typedef struct git_index git_index; +/** Memory representation of a set of config files */ +typedef struct git_config git_config; + +/** Interface to access a configuration file */ +typedef struct git_config_file git_config_file; + +/** Representation of a reference log entry */ +typedef struct git_reflog_entry git_reflog_entry; + +/** Representation of a reference log */ +typedef struct git_reflog git_reflog; + /** Time in a signature */ typedef struct git_time { git_time_t time; /** time in seconds from epoch */ @@ -156,6 +175,18 @@ typedef enum { GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED, } git_rtype; + +typedef struct git_refspec git_refspec; +typedef struct git_remote git_remote; + +/** A transport to use */ +typedef struct git_transport git_transport; + +typedef int (*git_transport_cb)(git_transport **transport); + +typedef struct git_remote_head git_remote_head; +typedef struct git_headarray git_headarray; + /** @} */ GIT_END_DECL diff --git a/vendor/libgit2/libgit2.pc.in b/vendor/libgit2/libgit2.pc.in index ece5f2b8e..6165ad678 100644 --- a/vendor/libgit2/libgit2.pc.in +++ b/vendor/libgit2/libgit2.pc.in @@ -1,11 +1,9 @@ -prefix=@prefix@ -exec_prefix=${prefix} -libdir=@libdir@ -includedir=${prefix}/include +libdir=@CMAKE_INSTALL_PREFIX@/@INSTALL_LIB@ +includedir=@CMAKE_INSTALL_PREFIX@/@INSTALL_INC@ Name: libgit2 Description: The git library, take 2 -Version: @version@ +Version: @LIBGIT2_VERSION_STRING@ Requires: libcrypto Libs: -L${libdir} -lgit2 -lz -lcrypto Cflags: -I${includedir} diff --git a/vendor/libgit2/src/blob.c b/vendor/libgit2/src/blob.c index 5e3c22fbf..b8282e505 100644 --- a/vendor/libgit2/src/blob.c +++ b/vendor/libgit2/src/blob.c @@ -62,51 +62,68 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b git_odb_stream *stream; if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to create blob"); - stream->write(stream, buffer, len); + if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) { + stream->free(stream); + return error; + } error = stream->finalize_write(oid, stream); stream->free(stream); - return error; + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create blob"); + + return GIT_SUCCESS; } int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path) { - int error, fd; + int error, islnk; + int fd = 0; char full_path[GIT_PATH_MAX]; char buffer[2048]; git_off_t size; git_odb_stream *stream; + struct stat st; if (repo->path_workdir == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)"); - git__joinpath(full_path, repo->path_workdir, path); + git_path_join(full_path, repo->path_workdir, path); - if ((fd = gitfo_open(full_path, O_RDONLY)) < 0) - return GIT_ENOTFOUND; - - if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) { - gitfo_close(fd); - return GIT_EOSERR; + error = p_lstat(full_path, &st); + if (error < 0) { + return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno)); } + islnk = S_ISLNK(st.st_mode); + size = st.st_size; + + if (!islnk) + if ((fd = p_open(full_path, O_RDONLY)) < 0) + return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path); + if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) { - gitfo_close(fd); - return error; + if (!islnk) + p_close(fd); + return git__rethrow(error, "Failed to create blob"); } while (size > 0) { ssize_t read_len; - read_len = read(fd, buffer, sizeof(buffer)); + if (!islnk) + read_len = p_read(fd, buffer, sizeof(buffer)); + else + read_len = p_readlink(full_path, buffer, sizeof(buffer)); if (read_len < 0) { - gitfo_close(fd); + if (!islnk) + p_close(fd); stream->free(stream); - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file"); } stream->write(stream, buffer, read_len); @@ -115,8 +132,9 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat error = stream->finalize_write(oid, stream); stream->free(stream); - gitfo_close(fd); + if (!islnk) + p_close(fd); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create blob"); } diff --git a/vendor/libgit2/src/buffer.c b/vendor/libgit2/src/buffer.c new file mode 100644 index 000000000..6af4c9195 --- /dev/null +++ b/vendor/libgit2/src/buffer.c @@ -0,0 +1,95 @@ +#include "buffer.h" +#include "posix.h" +#include + +#define ENSURE_SIZE(b, d) \ + if ((ssize_t)(d) >= buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\ + return; + +int git_buf_grow(git_buf *buf, size_t target_size) +{ + char *new_ptr; + + if (buf->asize < 0) + return GIT_ENOMEM; + + if (buf->asize == 0) + buf->asize = target_size; + + /* grow the buffer size by 1.5, until it's big enough + * to fit our target size */ + while (buf->asize < (int)target_size) + buf->asize = (buf->asize << 1) - (buf->asize >> 1); + + new_ptr = git__realloc(buf->ptr, buf->asize); + if (!new_ptr) { + buf->asize = -1; + return GIT_ENOMEM; + } + + buf->ptr = new_ptr; + return GIT_SUCCESS; +} + +int git_buf_oom(const git_buf *buf) +{ + return (buf->asize < 0); +} + +void git_buf_putc(git_buf *buf, char c) +{ + ENSURE_SIZE(buf, buf->size + 1); + buf->ptr[buf->size++] = c; +} + +void git_buf_put(git_buf *buf, const char *data, size_t len) +{ + ENSURE_SIZE(buf, buf->size + len); + memcpy(buf->ptr + buf->size, data, len); + buf->size += len; +} + +void git_buf_puts(git_buf *buf, const char *string) +{ + git_buf_put(buf, string, strlen(string)); +} + +void git_buf_printf(git_buf *buf, const char *format, ...) +{ + int len; + va_list arglist; + + ENSURE_SIZE(buf, buf->size + 1); + + while (1) { + va_start(arglist, format); + len = p_vsnprintf(buf->ptr + buf->size, buf->asize - buf->size, format, arglist); + va_end(arglist); + + if (len < 0) { + buf->asize = -1; + return; + } + + if (len + 1 <= buf->asize - buf->size) { + buf->size += len; + return; + } + + ENSURE_SIZE(buf, buf->size + len + 1); + } +} + +const char *git_buf_cstr(git_buf *buf) +{ + if (buf->size + 1 >= buf->asize && git_buf_grow(buf, buf->size + 1) < GIT_SUCCESS) + return NULL; + + buf->ptr[buf->size] = '\0'; + return buf->ptr; +} + +void git_buf_free(git_buf *buf) +{ + free(buf->ptr); +} diff --git a/vendor/libgit2/src/buffer.h b/vendor/libgit2/src/buffer.h new file mode 100644 index 000000000..1209340a1 --- /dev/null +++ b/vendor/libgit2/src/buffer.h @@ -0,0 +1,24 @@ +#ifndef INCLUDE_buffer_h__ +#define INCLUDE_buffer_h__ + +#include "common.h" + +typedef struct { + char *ptr; + ssize_t asize, size; +} git_buf; + +#define GIT_BUF_INIT {NULL, 0, 0} + +int git_buf_grow(git_buf *buf, size_t target_size); +int git_buf_oom(const git_buf *buf); +void git_buf_putc(git_buf *buf, char c); +void git_buf_put(git_buf *buf, const char *data, size_t len); +void git_buf_puts(git_buf *buf, const char *string); +void git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); +const char *git_buf_cstr(git_buf *buf); +void git_buf_free(git_buf *buf); + +#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1) + +#endif diff --git a/vendor/libgit2/src/cache.c b/vendor/libgit2/src/cache.c index fd42e2c5b..433fc3d9c 100644 --- a/vendor/libgit2/src/cache.c +++ b/vendor/libgit2/src/cache.c @@ -29,10 +29,7 @@ #include "thread-utils.h" #include "cache.h" -#define GIT_CACHE_OPENADR 3 - - -void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) +int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) { size_t i; @@ -52,12 +49,15 @@ void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_p cache->free_obj = free_ptr; cache->nodes = git__malloc((size + 1) * sizeof(cache_node)); + if (cache->nodes == NULL) + return GIT_ENOMEM; for (i = 0; i < (size + 1); ++i) { git_mutex_init(&cache->nodes[i].lock); cache->nodes[i].ptr = NULL; - cache->nodes[i].lru = 0; } + + return GIT_SUCCESS; } void git_cache_free(git_cache *cache) @@ -77,85 +77,53 @@ void git_cache_free(git_cache *cache) void *git_cache_get(git_cache *cache, const git_oid *oid) { const uint32_t *hash; - size_t i, pos, found = 0; cache_node *node = NULL; + void *result = NULL; hash = (const uint32_t *)oid->id; + node = &cache->nodes[hash[0] & cache->size_mask]; - for (i = 0; !found && i < GIT_CACHE_OPENADR; ++i) { - pos = hash[i] & cache->size_mask; - node = &cache->nodes[pos]; - - git_mutex_lock(&node->lock); - { - if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { - git_cached_obj_incref(node->ptr); - node->lru = ++cache->lru_count; - found = 1; - } + git_mutex_lock(&node->lock); + { + if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_incref(node->ptr); + result = node->ptr; } - git_mutex_unlock(&node->lock); } + git_mutex_unlock(&node->lock); - - return found ? node->ptr : NULL; + return result; } void *git_cache_try_store(git_cache *cache, void *entry) { - cache_node *nodes[GIT_CACHE_OPENADR], *lru_node; const uint32_t *hash; const git_oid *oid; - size_t i; + cache_node *node = NULL; oid = &((git_cached_obj*)entry)->oid; hash = (const uint32_t *)oid->id; + node = &cache->nodes[hash[0] & cache->size_mask]; /* increase the refcount on this object, because * the cache now owns it */ git_cached_obj_incref(entry); - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) { - size_t pos = hash[i] & cache->size_mask; - - nodes[i] = &cache->nodes[pos]; - git_mutex_lock(&nodes[i]->lock); - } - - lru_node = nodes[0]; - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) { - - if (nodes[i]->ptr == NULL) { - nodes[i]->ptr = entry; - nodes[i]->lru = ++cache->lru_count; - break; - } else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) { - git_cached_obj_decref(entry, cache->free_obj); - entry = nodes[i]->ptr; - nodes[i]->lru = ++cache->lru_count; - break; - } - - if (nodes[i]->lru < lru_node->lru) - lru_node = nodes[i]; - } - - if (i == GIT_CACHE_OPENADR) { - void *old_entry = lru_node->ptr; - assert(old_entry); - - git_cached_obj_decref(old_entry, cache->free_obj); - lru_node->ptr = entry; - lru_node->lru = ++cache->lru_count; + git_mutex_lock(&node->lock); + + if (node->ptr == NULL) { + node->ptr = entry; + } else if (git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_decref(entry, cache->free_obj); + entry = node->ptr; + } else { + git_cached_obj_decref(node->ptr, cache->free_obj); + node->ptr = entry; } /* increase the refcount again, because we are * returning it to the user */ git_cached_obj_incref(entry); - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) - git_mutex_unlock(&nodes[i]->lock); + git_mutex_unlock(&node->lock); return entry; } diff --git a/vendor/libgit2/src/cache.h b/vendor/libgit2/src/cache.h index 975aaff7e..4794dea3a 100644 --- a/vendor/libgit2/src/cache.h +++ b/vendor/libgit2/src/cache.h @@ -19,7 +19,6 @@ typedef struct { typedef struct { git_cached_obj *ptr; git_mutex lock; - unsigned int lru; } cache_node; typedef struct { @@ -31,7 +30,7 @@ typedef struct { } git_cache; -void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); +int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); void git_cache_free(git_cache *cache); void *git_cache_try_store(git_cache *cache, void *entry); diff --git a/vendor/libgit2/src/commit.c b/vendor/libgit2/src/commit.c index 54d7a47fe..dc9e5362a 100644 --- a/vendor/libgit2/src/commit.c +++ b/vendor/libgit2/src/commit.c @@ -65,7 +65,7 @@ void git_commit__free(git_commit *commit) git_signature_free(commit->committer); free(commit->message); - free(commit->message_short); + free(commit->message_encoding); free(commit); } @@ -74,43 +74,13 @@ const git_oid *git_commit_id(git_commit *c) return git_object_id((git_object *)c); } - int git_commit_create_v( git_oid *oid, git_repository *repo, const char *update_ref, const git_signature *author, const git_signature *committer, - const char *message, - const git_oid *tree_oid, - int parent_count, - ...) -{ - va_list ap; - int i, error; - const git_oid **oids; - - oids = git__malloc(parent_count * sizeof(git_oid *)); - - va_start(ap, parent_count); - for (i = 0; i < parent_count; ++i) - oids[i] = va_arg(ap, const git_oid *); - va_end(ap); - - error = git_commit_create( - oid, repo, update_ref, author, committer, message, - tree_oid, parent_count, oids); - - free((void *)oids); - return error; -} - -int git_commit_create_ov( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, + const char *message_encoding, const char *message, const git_tree *tree, int parent_count, @@ -118,49 +88,22 @@ int git_commit_create_ov( { va_list ap; int i, error; - const git_oid **oids; + const git_commit **parents; - oids = git__malloc(parent_count * sizeof(git_oid *)); + parents = git__malloc(parent_count * sizeof(git_commit *)); va_start(ap, parent_count); for (i = 0; i < parent_count; ++i) - oids[i] = git_object_id(va_arg(ap, const git_object *)); + parents[i] = va_arg(ap, const git_commit *); va_end(ap); error = git_commit_create( - oid, repo, update_ref, author, committer, message, - git_object_id((git_object *)tree), - parent_count, oids); + oid, repo, update_ref, author, committer, + message_encoding, message, + tree, parent_count, parents); - free((void *)oids); - return error; -} + free((void *)parents); -int git_commit_create_o( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message, - const git_tree *tree, - int parent_count, - const git_commit *parents[]) -{ - int i, error; - const git_oid **oids; - - oids = git__malloc(parent_count * sizeof(git_oid *)); - - for (i = 0; i < parent_count; ++i) - oids[i] = git_object_id((git_object *)parents[i]); - - error = git_commit_create( - oid, repo, update_ref, author, committer, message, - git_object_id((git_object *)tree), - parent_count, oids); - - free((void *)oids); return error; } @@ -170,64 +113,57 @@ int git_commit_create( const char *update_ref, const git_signature *author, const git_signature *committer, + const char *message_encoding, const char *message, - const git_oid *tree_oid, + const git_tree *tree, int parent_count, - const git_oid *parents[]) + const git_commit *parents[]) { - size_t final_size = 0; - int message_length, author_length, committer_length; - - char *author_str, *committer_str; - + git_buf commit = GIT_BUF_INIT; int error, i; - git_odb_stream *stream; - - message_length = strlen(message); - author_length = git_signature__write(&author_str, "author", author); - committer_length = git_signature__write(&committer_str, "committer", committer); - if (author_length < 0 || committer_length < 0) - return GIT_ENOMEM; + if (git_object_owner((const git_object *)tree) != repo) + return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository"); - final_size += GIT_OID_LINE_LENGTH("tree"); - final_size += GIT_OID_LINE_LENGTH("parent") * parent_count; - final_size += author_length; - final_size += committer_length; - final_size += 1 + message_length; + git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree)); - if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_COMMIT)) < GIT_SUCCESS) - return error; - - git__write_oid(stream, "tree", tree_oid); + for (i = 0; i < parent_count; ++i) { + if (git_object_owner((const git_object *)parents[i]) != repo) { + error = git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository"); + goto cleanup; + } - for (i = 0; i < parent_count; ++i) - git__write_oid(stream, "parent", parents[i]); + git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i])); + } - stream->write(stream, author_str, author_length); - free(author_str); + git_signature__writebuf(&commit, "author ", author); + git_signature__writebuf(&commit, "committer ", committer); - stream->write(stream, committer_str, committer_length); - free(committer_str); + if (message_encoding != NULL) + git_buf_printf(&commit, "encoding %s\n", message_encoding); + git_buf_putc(&commit, '\n'); + git_buf_puts(&commit, message); - stream->write(stream, "\n", 1); - stream->write(stream, message, message_length); + if (git_buf_oom(&commit)) { + error = git__throw(GIT_ENOMEM, "Not enough memory to build the commit data"); + goto cleanup; + } - error = stream->finalize_write(oid, stream); - stream->free(stream); + error = git_odb_write(oid, git_repository_database(repo), commit.ptr, commit.size, GIT_OBJ_COMMIT); + git_buf_free(&commit); if (error == GIT_SUCCESS && update_ref != NULL) { git_reference *head; error = git_reference_lookup(&head, repo, update_ref); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to create commit"); error = git_reference_resolve(&head, head); if (error < GIT_SUCCESS) { if (error != GIT_ENOTFOUND) - return error; + return git__rethrow(error, "Failed to create commit"); /* * The target of the reference was not found. This can happen * just after a repository has been initialized (the master @@ -235,33 +171,40 @@ int git_commit_create( * point to) or after an orphan checkout, so if the target * branch doesn't exist yet, create it and return. */ - return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid); + return git_reference_create_oid(&head, repo, git_reference_target(head), oid, 1); } error = git_reference_set_oid(head, oid); } + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create commit"); + + return GIT_SUCCESS; + +cleanup: + git_buf_free(&commit); return error; } int commit_parse_buffer(git_commit *commit, const void *data, size_t len) { - const char *buffer = (char *)data; - const char *buffer_end = (char *)data + len; + const char *buffer = data; + const char *buffer_end = (const char *)data + len; git_oid parent_oid; int error; git_vector_init(&commit->parent_oids, 4, NULL); - if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) - return error; + if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) + return git__rethrow(error, "Failed to parse buffer"); /* * TODO: commit grafts! */ - while (git__parse_oid(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) { + while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) { git_oid *new_oid; new_oid = git__malloc(sizeof(git_oid)); @@ -272,36 +215,37 @@ int commit_parse_buffer(git_commit *commit, const void *data, size_t len) } commit->author = git__malloc(sizeof(git_signature)); - if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS) - return error; + if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS) + return git__rethrow(error, "Failed to parse commit"); /* Always parse the committer; we need the commit time */ commit->committer = git__malloc(sizeof(git_signature)); - if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ")) < GIT_SUCCESS) - return error; + if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS) + return git__rethrow(error, "Failed to parse commit"); + + if (git__prefixcmp(buffer, "encoding ") == 0) { + const char *encoding_end; + buffer += strlen("encoding "); + + encoding_end = buffer; + while (encoding_end < buffer_end && *encoding_end != '\n') + encoding_end++; + + commit->message_encoding = git__strndup(buffer, encoding_end - buffer); + if (!commit->message_encoding) + return GIT_ENOMEM; + + buffer = encoding_end; + } /* parse commit message */ - while (buffer <= buffer_end && *buffer == '\n') + while (buffer < buffer_end && *buffer == '\n') buffer++; if (buffer < buffer_end) { - const char *line_end; - size_t message_len; - - /* Long message */ - message_len = buffer_end - buffer; - commit->message = git__malloc(message_len + 1); - memcpy(commit->message, buffer, message_len); - commit->message[message_len] = 0; - - /* Short message */ - if((line_end = memchr(buffer, '\n', buffer_end - buffer)) == NULL) - line_end = buffer_end; - message_len = line_end - buffer; - - commit->message_short = git__malloc(message_len + 1); - memcpy(commit->message_short, buffer, message_len); - commit->message_short[message_len] = 0; + commit->message = git__strndup(buffer, buffer_end - buffer); + if (!commit->message) + return GIT_ENOMEM; } return GIT_SUCCESS; @@ -323,7 +267,7 @@ int git_commit__parse(git_commit *commit, git_odb_object *obj) GIT_COMMIT_GETTER(const git_signature *, author, commit->author) GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer) GIT_COMMIT_GETTER(const char *, message, commit->message) -GIT_COMMIT_GETTER(const char *, message_short, commit->message_short) +GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding) GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time) GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset) GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length) @@ -343,7 +287,7 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n) parent_oid = git_vector_get(&commit->parent_oids, n); if (parent_oid == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n); return git_commit_lookup(parent, commit->object.repo, parent_oid); } diff --git a/vendor/libgit2/src/commit.h b/vendor/libgit2/src/commit.h index 3d15c5044..ff2f28248 100644 --- a/vendor/libgit2/src/commit.h +++ b/vendor/libgit2/src/commit.h @@ -17,8 +17,8 @@ struct git_commit { git_signature *author; git_signature *committer; + char *message_encoding; char *message; - char *message_short; }; void git_commit__free(git_commit *c); diff --git a/vendor/libgit2/src/common.h b/vendor/libgit2/src/common.h index f4f11fd2f..5986a6568 100644 --- a/vendor/libgit2/src/common.h +++ b/vendor/libgit2/src/common.h @@ -1,13 +1,7 @@ #ifndef INCLUDE_common_h__ #define INCLUDE_common_h__ -/** Force 64 bit off_t size on POSIX. */ -#define _FILE_OFFSET_BITS 64 - -#if defined(_WIN32) && !defined(__CYGWIN__) -#define GIT_WIN32 1 -#endif - +#include "git2/common.h" #include "git2/thread-utils.h" #include "cc-compat.h" @@ -29,8 +23,8 @@ # include # include # include -# include "msvc-compat.h" -# include "mingw-compat.h" +# include "win32/msvc-compat.h" +# include "win32/mingw-compat.h" # ifdef GIT_THREADS # include "win32/pthread.h" #endif @@ -41,22 +35,24 @@ typedef SSIZE_T ssize_t; #else # include -# include # ifdef GIT_THREADS # include # endif #endif -#include "git2/common.h" #include "git2/types.h" #include "git2/errors.h" #include "thread-utils.h" #include "bswap.h" -#define GIT_PATH_MAX 4096 -extern int git__throw(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3); -extern int git__rethrow(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3); +extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2); +#define git__throw(error, ...) \ + (git___throw(__VA_ARGS__), error) + +extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2); +#define git__rethrow(error, ...) \ + (git___rethrow(__VA_ARGS__), error) #include "util.h" diff --git a/vendor/libgit2/src/config.c b/vendor/libgit2/src/config.c new file mode 100644 index 000000000..771250731 --- /dev/null +++ b/vendor/libgit2/src/config.c @@ -0,0 +1,354 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "fileops.h" +#include "hashtable.h" +#include "config.h" +#include "git2/config.h" +#include "vector.h" + +#include + +typedef struct { + git_config_file *file; + int priority; +} file_internal; + +void git_config_free(git_config *cfg) +{ + unsigned int i; + git_config_file *file; + file_internal *internal; + + for(i = 0; i < cfg->files.length; ++i){ + internal = git_vector_get(&cfg->files, i); + file = internal->file; + file->free(file); + free(internal); + } + + git_vector_free(&cfg->files); + free(cfg); +} + +static int config_backend_cmp(const void *a, const void *b) +{ + const file_internal *bk_a = (const file_internal *)(a); + const file_internal *bk_b = (const file_internal *)(b); + + return bk_b->priority - bk_a->priority; +} + +int git_config_new(git_config **out) +{ + git_config *cfg; + + cfg = git__malloc(sizeof(git_config)); + if (cfg == NULL) + return GIT_ENOMEM; + + memset(cfg, 0x0, sizeof(git_config)); + + if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) { + free(cfg); + return GIT_ENOMEM; + } + + *out = cfg; + + return GIT_SUCCESS; +} + +int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority) +{ + git_config_file *file = NULL; + int error; + + error = git_config_file__ondisk(&file, path); + if (error < GIT_SUCCESS) + return error; + + error = git_config_add_file(cfg, file, priority); + if (error < GIT_SUCCESS) { + /* + * free manually; the file is not owned by the config + * instance yet and will not be freed on cleanup + */ + file->free(file); + return error; + } + + return GIT_SUCCESS; +} + +int git_config_open_ondisk(git_config **cfg, const char *path) +{ + int error; + + error = git_config_new(cfg); + if (error < GIT_SUCCESS) + return error; + + error = git_config_add_file_ondisk(*cfg, path, 1); + if (error < GIT_SUCCESS) + git_config_free(*cfg); + + return error; +} + +int git_config_add_file(git_config *cfg, git_config_file *file, int priority) +{ + file_internal *internal; + int error; + + assert(cfg && file); + + if ((error = file->open(file)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to open config file"); + + internal = git__malloc(sizeof(file_internal)); + if (internal == NULL) + return GIT_ENOMEM; + + internal->file = file; + internal->priority = priority; + + if (git_vector_insert(&cfg->files, internal) < 0) { + free(internal); + return GIT_ENOMEM; + } + + git_vector_sort(&cfg->files); + internal->file->cfg = cfg; + + return GIT_SUCCESS; +} + +/* + * Loop over all the variables + */ + +int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data) +{ + int ret = GIT_SUCCESS; + unsigned int i; + file_internal *internal; + git_config_file *file; + + for(i = 0; i < cfg->files.length && ret == 0; ++i) { + internal = git_vector_get(&cfg->files, i); + file = internal->file; + ret = file->foreach(file, fn, data); + } + + return ret; +} + +int git_config_delete(git_config *cfg, const char *name) +{ + return git_config_set_string(cfg, name, NULL); +} + +/************** + * Setters + **************/ + +int git_config_set_long(git_config *cfg, const char *name, long int value) +{ + char str_value[32]; /* All numbers should fit in here */ + p_snprintf(str_value, sizeof(str_value), "%ld", value); + return git_config_set_string(cfg, name, str_value); +} + +int git_config_set_int(git_config *cfg, const char *name, int value) +{ + return git_config_set_long(cfg, name, value); +} + +int git_config_set_bool(git_config *cfg, const char *name, int value) +{ + return git_config_set_string(cfg, name, value ? "true" : "false"); +} + +int git_config_set_string(git_config *cfg, const char *name, const char *value) +{ + file_internal *internal; + git_config_file *file; + + if (cfg->files.length == 0) + return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance"); + + internal = git_vector_get(&cfg->files, 0); + file = internal->file; + + return file->set(file, name, value); +} + +/*********** + * Getters + ***********/ + +int git_config_get_long(git_config *cfg, const char *name, long int *out) +{ + const char *value, *num_end; + int ret; + long int num; + + ret = git_config_get_string(cfg, name, &value); + if (ret < GIT_SUCCESS) + return git__rethrow(ret, "Failed to get value for %s", name); + + ret = git__strtol32(&num, value, &num_end, 0); + if (ret < GIT_SUCCESS) + return git__rethrow(ret, "Failed to get value for %s", name); + + switch (*num_end) { + case '\0': + break; + case 'k': + case 'K': + num *= 1024; + break; + case 'm': + case 'M': + num *= 1024 * 1024; + break; + case 'g': + case 'G': + num *= 1024 * 1024 * 1024; + break; + default: + return git__throw(GIT_EINVALIDTYPE, "Failed to get value for %s. Value is of invalid type", name); + } + + *out = num; + + return GIT_SUCCESS; +} + +int git_config_get_int(git_config *cfg, const char *name, int *out) +{ + long int tmp; + int ret; + + ret = git_config_get_long(cfg, name, &tmp); + + *out = (int) tmp; + + return ret; +} + +int git_config_get_bool(git_config *cfg, const char *name, int *out) +{ + const char *value; + int error = GIT_SUCCESS; + + error = git_config_get_string(cfg, name, &value); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to get value for %s", name); + + /* A missing value means true */ + if (value == NULL) { + *out = 1; + return GIT_SUCCESS; + } + + if (!strcasecmp(value, "true") || + !strcasecmp(value, "yes") || + !strcasecmp(value, "on")) { + *out = 1; + return GIT_SUCCESS; + } + if (!strcasecmp(value, "false") || + !strcasecmp(value, "no") || + !strcasecmp(value, "off")) { + *out = 0; + return GIT_SUCCESS; + } + + /* Try to parse it as an integer */ + error = git_config_get_int(cfg, name, out); + if (error == GIT_SUCCESS) + *out = !!(*out); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to get value for %s", name); + return error; +} + +int git_config_get_string(git_config *cfg, const char *name, const char **out) +{ + file_internal *internal; + git_config_file *file; + int error = GIT_ENOTFOUND; + unsigned int i; + + if (cfg->files.length == 0) + return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance"); + + for (i = 0; i < cfg->files.length; ++i) { + internal = git_vector_get(&cfg->files, i); + file = internal->file; + if ((error = file->get(file, name, out)) == GIT_SUCCESS) + return GIT_SUCCESS; + } + + return git__throw(error, "Config value '%s' not found", name); +} + +int git_config_find_global(char *global_config_path) +{ + const char *home; + + home = getenv("HOME"); + +#ifdef GIT_WIN32 + if (home == NULL) + home = getenv("USERPROFILE"); +#endif + + if (home == NULL) + return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory"); + + git_path_join(global_config_path, home, GIT_CONFIG_FILENAME); + + if (git_futils_exists(global_config_path) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist"); + + return GIT_SUCCESS; +} + +int git_config_open_global(git_config **out) +{ + int error; + char global_path[GIT_PATH_MAX]; + + if ((error = git_config_find_global(global_path)) < GIT_SUCCESS) + return error; + + return git_config_open_ondisk(out, global_path); +} + diff --git a/vendor/libgit2/src/config.h b/vendor/libgit2/src/config.h new file mode 100644 index 000000000..e2f301bf1 --- /dev/null +++ b/vendor/libgit2/src/config.h @@ -0,0 +1,17 @@ +#ifndef INCLUDE_config_h__ +#define INCLUDE_config_h__ + +#include "git2.h" +#include "git2/config.h" +#include "vector.h" +#include "repository.h" + +#define GIT_CONFIG_FILENAME ".gitconfig" +#define GIT_CONFIG_FILENAME_INREPO "config" + +struct git_config { + git_vector files; + git_repository *repo; +}; + +#endif diff --git a/vendor/libgit2/src/config_file.c b/vendor/libgit2/src/config_file.c new file mode 100644 index 000000000..520a806b5 --- /dev/null +++ b/vendor/libgit2/src/config_file.c @@ -0,0 +1,1233 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "config.h" +#include "fileops.h" +#include "filebuf.h" +#include "git2/config.h" +#include "git2/types.h" + + +#include + +typedef struct cvar_t { + struct cvar_t *next; + char *section; + char *name; + char *value; +} cvar_t; + +typedef struct { + struct cvar_t *head; + struct cvar_t *tail; +} cvar_t_list; + +#define CVAR_LIST_HEAD(list) ((list)->head) + +#define CVAR_LIST_TAIL(list) ((list)->tail) + +#define CVAR_LIST_NEXT(var) ((var)->next) + +#define CVAR_LIST_EMPTY(list) ((list)->head == NULL) + +#define CVAR_LIST_APPEND(list, var) do {\ + if (CVAR_LIST_EMPTY(list)) {\ + CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\ + } else {\ + CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\ + CVAR_LIST_TAIL(list) = var;\ + }\ +} while(0) + +#define CVAR_LIST_REMOVE_HEAD(list) do {\ + CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\ +} while(0) + +#define CVAR_LIST_REMOVE_AFTER(var) do {\ + CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\ +} while(0) + +#define CVAR_LIST_FOREACH(list, iter)\ + for ((iter) = CVAR_LIST_HEAD(list);\ + (iter) != NULL;\ + (iter) = CVAR_LIST_NEXT(iter)) + +/* + * Inspired by the FreeBSD functions + */ +#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\ + for ((iter) = CVAR_LIST_HEAD(vars);\ + (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\ + (iter) = (tmp)) + +typedef struct { + git_config_file parent; + + cvar_t_list var_list; + + struct { + git_fbuffer buffer; + char *read_ptr; + int line_number; + int eof; + } reader; + + char *file_path; +} diskfile_backend; + +static int config_parse(diskfile_backend *cfg_file); +static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value); +static int config_write(diskfile_backend *cfg, cvar_t *var); + +static void cvar_free(cvar_t *var) +{ + if (var == NULL) + return; + + free(var->section); + free(var->name); + free(var->value); + free(var); +} + +static void cvar_list_free(cvar_t_list *list) +{ + cvar_t *cur; + + while (!CVAR_LIST_EMPTY(list)) { + cur = CVAR_LIST_HEAD(list); + CVAR_LIST_REMOVE_HEAD(list); + cvar_free(cur); + } +} + +/* + * Compare two strings according to the git section-subsection + * rules. The order of the strings is important because local is + * assumed to have the internal format (only the section name and with + * case information) and input the normalized one (only dots, no case + * information). + */ +static int cvar_match_section(const char *local, const char *input) +{ + char *first_dot; + char *local_sp = strchr(local, ' '); + int comparison_len; + + /* + * If the local section name doesn't contain a space, then we can + * just do a case-insensitive compare. + */ + if (local_sp == NULL) + return !strncasecmp(local, input, strlen(local)); + + /* + * From here onwards, there is a space diving the section and the + * subsection. Anything before the space in local is + * case-insensitive. + */ + if (strncasecmp(local, input, local_sp - local)) + return 0; + + /* + * We compare starting from the first character after the + * quotation marks, which is two characters beyond the space. For + * the input, we start one character beyond the dot. If the names + * have different lengths, then we can fail early, as we know they + * can't be the same. + * The length is given by the length between the quotation marks. + */ + + first_dot = strchr(input, '.'); + comparison_len = strlen(local_sp + 2) - 1; + + return !strncmp(local_sp + 2, first_dot + 1, comparison_len); +} + +static int cvar_match_name(const cvar_t *var, const char *str) +{ + const char *name_start; + + if (!cvar_match_section(var->section, str)) { + return 0; + } + /* Early exit if the lengths are different */ + name_start = strrchr(str, '.') + 1; + if (strlen(var->name) != strlen(name_start)) + return 0; + + return !strcasecmp(var->name, name_start); +} + +static cvar_t *cvar_list_find(cvar_t_list *list, const char *name) +{ + cvar_t *iter; + + CVAR_LIST_FOREACH (list, iter) { + if (cvar_match_name(iter, name)) + return iter; + } + + return NULL; +} + +static int cvar_normalize_name(cvar_t *var, char **output) +{ + char *section_sp = strchr(var->section, ' '); + char *quote, *name; + int len, ret; + + /* + * The final string is going to be at most one char longer than + * the input + */ + len = strlen(var->section) + strlen(var->name) + 1; + name = git__malloc(len + 1); + if (name == NULL) + return GIT_ENOMEM; + + /* If there aren't any spaces in the section, it's easy */ + if (section_sp == NULL) { + ret = p_snprintf(name, len + 1, "%s.%s", var->section, var->name); + if (ret < 0) { + free(name); + return git__throw(GIT_EOSERR, "Failed to normalize name. OS err: %s", strerror(errno)); + } + + *output = name; + return GIT_SUCCESS; + } + + /* + * If there are spaces, we replace the space by a dot, move + * section name so it overwrites the first quotation mark and + * replace the last quotation mark by a dot. We then append the + * variable name. + */ + strcpy(name, var->section); + section_sp = strchr(name, ' '); + *section_sp = '.'; + /* Remove first quote */ + quote = strchr(name, '"'); + memmove(quote, quote+1, strlen(quote+1)); + /* Remove second quote */ + quote = strchr(name, '"'); + *quote = '.'; + strcpy(quote+1, var->name); + + *output = name; + return GIT_SUCCESS; +} + +static char *interiorize_section(const char *orig) +{ + char *dot, *last_dot, *section, *ret; + int len; + + dot = strchr(orig, '.'); + last_dot = strrchr(orig, '.'); + len = last_dot - orig; + + /* No subsection, this is easy */ + if (last_dot == dot) + return git__strndup(orig, dot - orig); + + section = git__malloc(len + 4); + if (section == NULL) + return NULL; + + memset(section, 0x0, len + 4); + ret = section; + len = dot - orig; + memcpy(section, orig, len); + section += len; + len = strlen(" \""); + memcpy(section, " \"", len); + section += len; + len = last_dot - dot - 1; + memcpy(section, dot + 1, len); + section += len; + *section = '"'; + + return ret; +} + +static int config_open(git_config_file *cfg) +{ + int error; + diskfile_backend *b = (diskfile_backend *)cfg; + + error = git_futils_readbuffer(&b->reader.buffer, b->file_path); + if(error < GIT_SUCCESS) + goto cleanup; + + error = config_parse(b); + if (error < GIT_SUCCESS) + goto cleanup; + + git_futils_freebuffer(&b->reader.buffer); + + return error; + + cleanup: + cvar_list_free(&b->var_list); + git_futils_freebuffer(&b->reader.buffer); + + return git__rethrow(error, "Failed to open config"); +} + +static void backend_free(git_config_file *_backend) +{ + diskfile_backend *backend = (diskfile_backend *)_backend; + + if (backend == NULL) + return; + + free(backend->file_path); + cvar_list_free(&backend->var_list); + + free(backend); +} + +static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data) +{ + int ret = GIT_SUCCESS; + cvar_t *var; + diskfile_backend *b = (diskfile_backend *)backend; + + CVAR_LIST_FOREACH(&b->var_list, var) { + char *normalized = NULL; + + ret = cvar_normalize_name(var, &normalized); + if (ret < GIT_SUCCESS) + return ret; + + ret = fn(normalized, var->value, data); + free(normalized); + if (ret) + break; + } + + return ret; +} + +static int config_set(git_config_file *cfg, const char *name, const char *value) +{ + cvar_t *var = NULL; + cvar_t *existing = NULL; + int error = GIT_SUCCESS; + const char *last_dot; + diskfile_backend *b = (diskfile_backend *)cfg; + + /* + * If it already exists, we just need to update its value. + */ + existing = cvar_list_find(&b->var_list, name); + if (existing != NULL) { + char *tmp = value ? git__strdup(value) : NULL; + if (tmp == NULL && value != NULL) + return GIT_ENOMEM; + + free(existing->value); + existing->value = tmp; + + return config_write(b, existing); + } + + /* + * Otherwise, create it and stick it at the end of the queue. If + * value is NULL, we return an error, because you can't delete a + * variable that doesn't exist. + */ + + if (value == NULL) + return git__throw(GIT_ENOTFOUND, "Can't delete non-exitent variable"); + + last_dot = strrchr(name, '.'); + if (last_dot == NULL) { + return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed"); + } + + var = git__malloc(sizeof(cvar_t)); + if (var == NULL) + return GIT_ENOMEM; + + memset(var, 0x0, sizeof(cvar_t)); + + var->section = interiorize_section(name); + if (var->section == NULL) { + error = GIT_ENOMEM; + goto out; + } + + var->name = git__strdup(last_dot + 1); + if (var->name == NULL) { + error = GIT_ENOMEM; + goto out; + } + + var->value = value ? git__strdup(value) : NULL; + if (var->value == NULL && value != NULL) { + error = GIT_ENOMEM; + goto out; + } + + CVAR_LIST_APPEND(&b->var_list, var); + error = config_write(b, var); + + out: + if (error < GIT_SUCCESS) + cvar_free(var); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set config value"); +} + +/* + * Internal function that actually gets the value in string form + */ +static int config_get(git_config_file *cfg, const char *name, const char **out) +{ + cvar_t *var; + int error = GIT_SUCCESS; + diskfile_backend *b = (diskfile_backend *)cfg; + + var = cvar_list_find(&b->var_list, name); + + if (var == NULL) + return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name); + + *out = var->value; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name); +} + +int git_config_file__ondisk(git_config_file **out, const char *path) +{ + diskfile_backend *backend; + + backend = git__malloc(sizeof(diskfile_backend)); + if (backend == NULL) + return GIT_ENOMEM; + + memset(backend, 0x0, sizeof(diskfile_backend)); + + backend->file_path = git__strdup(path); + if (backend->file_path == NULL) { + free(backend); + return GIT_ENOMEM; + } + + backend->parent.open = config_open; + backend->parent.get = config_get; + backend->parent.set = config_set; + backend->parent.foreach = file_foreach; + backend->parent.free = backend_free; + + *out = (git_config_file *)backend; + + return GIT_SUCCESS; +} + +static int cfg_getchar_raw(diskfile_backend *cfg) +{ + int c; + + c = *cfg->reader.read_ptr++; + + /* + Win 32 line breaks: if we find a \r\n sequence, + return only the \n as a newline + */ + if (c == '\r' && *cfg->reader.read_ptr == '\n') { + cfg->reader.read_ptr++; + c = '\n'; + } + + if (c == '\n') + cfg->reader.line_number++; + + if (c == 0) { + cfg->reader.eof = 1; + c = '\n'; + } + + return c; +} + +#define SKIP_WHITESPACE (1 << 1) +#define SKIP_COMMENTS (1 << 2) + +static int cfg_getchar(diskfile_backend *cfg_file, int flags) +{ + const int skip_whitespace = (flags & SKIP_WHITESPACE); + const int skip_comments = (flags & SKIP_COMMENTS); + int c; + + assert(cfg_file->reader.read_ptr); + + do c = cfg_getchar_raw(cfg_file); + while (skip_whitespace && isspace(c)); + + if (skip_comments && (c == '#' || c == ';')) { + do c = cfg_getchar_raw(cfg_file); + while (c != '\n'); + } + + return c; +} + +/* + * Read the next char, but don't move the reading pointer. + */ +static int cfg_peek(diskfile_backend *cfg, int flags) +{ + void *old_read_ptr; + int old_lineno, old_eof; + int ret; + + assert(cfg->reader.read_ptr); + + old_read_ptr = cfg->reader.read_ptr; + old_lineno = cfg->reader.line_number; + old_eof = cfg->reader.eof; + + ret = cfg_getchar(cfg, flags); + + cfg->reader.read_ptr = old_read_ptr; + cfg->reader.line_number = old_lineno; + cfg->reader.eof = old_eof; + + return ret; +} + +/* + * Read and consume a line, returning it in newly-allocated memory. + */ +static char *cfg_readline(diskfile_backend *cfg) +{ + char *line = NULL; + char *line_src, *line_end; + int line_len; + + line_src = cfg->reader.read_ptr; + + /* Skip empty empty lines */ + while (isspace(*line_src)) + ++line_src; + + line_end = strchr(line_src, '\n'); + + /* no newline at EOF */ + if (line_end == NULL) + line_end = strchr(line_src, 0); + + line_len = line_end - line_src; + + line = git__malloc(line_len + 1); + if (line == NULL) + return NULL; + + memcpy(line, line_src, line_len); + + line[line_len] = '\0'; + + while (--line_len >= 0 && isspace(line[line_len])) + line[line_len] = '\0'; + + if (*line_end == '\n') + line_end++; + + if (*line_end == '\0') + cfg->reader.eof = 1; + + cfg->reader.line_number++; + cfg->reader.read_ptr = line_end; + + return line; +} + +/* + * Consume a line, without storing it anywhere + */ +void cfg_consume_line(diskfile_backend *cfg) +{ + char *line_start, *line_end; + + line_start = cfg->reader.read_ptr; + line_end = strchr(line_start, '\n'); + /* No newline at EOF */ + if(line_end == NULL){ + line_end = strchr(line_start, '\0'); + } + + if (*line_end == '\n') + line_end++; + + if (*line_end == '\0') + cfg->reader.eof = 1; + + cfg->reader.line_number++; + cfg->reader.read_ptr = line_end; +} + +GIT_INLINE(int) config_keychar(int c) +{ + return isalnum(c) || c == '-'; +} + +static int parse_section_header_ext(const char *line, const char *base_name, char **section_name) +{ + int buf_len, total_len, pos, rpos; + int c, ret; + char *subsection, *first_quote, *last_quote; + int error = GIT_SUCCESS; + int quote_marks; + /* + * base_name is what came before the space. We should be at the + * first quotation mark, except for now, line isn't being kept in + * sync so we only really use it to calculate the length. + */ + + first_quote = strchr(line, '"'); + last_quote = strrchr(line, '"'); + + if (last_quote - first_quote == 0) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark"); + + buf_len = last_quote - first_quote + 2; + + subsection = git__malloc(buf_len + 2); + if (subsection == NULL) + return GIT_ENOMEM; + + pos = 0; + rpos = 0; + quote_marks = 0; + + line = first_quote; + c = line[rpos++]; + + /* + * At the end of each iteration, whatever is stored in c will be + * added to the string. In case of error, jump to out + */ + do { + if (quote_marks == 2) { + error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote"); + goto out; + + } + + switch (c) { + case '"': + ++quote_marks; + break; + case '\\': + c = line[rpos++]; + switch (c) { + case '"': + case '\\': + break; + default: + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c); + goto out; + } + break; + default: + break; + } + + subsection[pos++] = (char) c; + } while ((c = line[rpos++]) != ']'); + + subsection[pos] = '\0'; + + total_len = strlen(base_name) + strlen(subsection) + 2; + *section_name = git__malloc(total_len); + if (*section_name == NULL) { + error = GIT_ENOMEM; + goto out; + } + + ret = p_snprintf(*section_name, total_len, "%s %s", base_name, subsection); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to parse ext header. OS error: %s", strerror(errno)); + goto out; + } + + git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name); + + out: + free(subsection); + + return error; +} + +static int parse_section_header(diskfile_backend *cfg, char **section_out) +{ + char *name, *name_end; + int name_length, c, pos; + int error = GIT_SUCCESS; + char *line; + + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + /* find the end of the variable's name */ + name_end = strchr(line, ']'); + if (name_end == NULL) { + free(line); + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end"); + } + + name = (char *)git__malloc((size_t)(name_end - line) + 1); + if (name == NULL) { + free(line); + return GIT_ENOMEM; + } + + name_length = 0; + pos = 0; + + /* Make sure we were given a section header */ + c = line[pos++]; + if (c != '[') { + error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug"); + goto error; + } + + c = line[pos++]; + + do { + if (isspace(c)){ + name[name_length] = '\0'; + error = parse_section_header_ext(line, name, section_out); + free(line); + free(name); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header"); + } + + if (!config_keychar(c) && c != '.') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header"); + goto error; + } + + name[name_length++] = (char) tolower(c); + + } while ((c = line[pos++]) != ']'); + + if (line[pos - 1] != ']') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly"); + goto error; + } + + name[name_length] = 0; + free(line); + git__strtolower(name); + *section_out = name; + return GIT_SUCCESS; + +error: + free(line); + free(name); + return error; +} + +static int skip_bom(diskfile_backend *cfg) +{ + static const char *utf8_bom = "\xef\xbb\xbf"; + + if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0) + cfg->reader.read_ptr += sizeof(utf8_bom); + + /* TODO: the reference implementation does pretty stupid + shit with the BoM + */ + + return GIT_SUCCESS; +} + +/* + (* basic types *) + digit = "0".."9" + integer = digit { digit } + alphabet = "a".."z" + "A" .. "Z" + + section_char = alphabet | "." | "-" + extension_char = (* any character except newline *) + any_char = (* any character *) + variable_char = "alphabet" | "-" + + + (* actual grammar *) + config = { section } + + section = header { definition } + + header = "[" section [subsection | subsection_ext] "]" + + subsection = "." section + subsection_ext = "\"" extension "\"" + + section = section_char { section_char } + extension = extension_char { extension_char } + + definition = variable_name ["=" variable_value] "\n" + + variable_name = variable_char { variable_char } + variable_value = string | boolean | integer + + string = quoted_string | plain_string + quoted_string = "\"" plain_string "\"" + plain_string = { any_char } + + boolean = boolean_true | boolean_false + boolean_true = "yes" | "1" | "true" | "on" + boolean_false = "no" | "0" | "false" | "off" +*/ + +static void strip_comments(char *line) +{ + int quote_count = 0; + char *ptr; + + for (ptr = line; *ptr; ++ptr) { + if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\') + quote_count++; + + if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) { + ptr[0] = '\0'; + break; + } + } + + if (isspace(ptr[-1])) { + /* TODO skip whitespace */ + } +} + +static int config_parse(diskfile_backend *cfg_file) +{ + int error = GIT_SUCCESS, c; + char *current_section = NULL; + char *var_name; + char *var_value; + cvar_t *var; + + /* Initialize the reading position */ + cfg_file->reader.read_ptr = cfg_file->reader.buffer.data; + cfg_file->reader.eof = 0; + + /* If the file is empty, there's nothing for us to do */ + if (*cfg_file->reader.read_ptr == '\0') + return GIT_SUCCESS; + + skip_bom(cfg_file); + + while (error == GIT_SUCCESS && !cfg_file->reader.eof) { + + c = cfg_peek(cfg_file, SKIP_WHITESPACE); + + switch (c) { + case '\0': /* We've arrived at the end of the file */ + break; + + case '[': /* section header, new section begins */ + free(current_section); + current_section = NULL; + error = parse_section_header(cfg_file, ¤t_section); + break; + + case ';': + case '#': + cfg_consume_line(cfg_file); + break; + + default: /* assume variable declaration */ + error = parse_variable(cfg_file, &var_name, &var_value); + + if (error < GIT_SUCCESS) + break; + + var = malloc(sizeof(cvar_t)); + if (var == NULL) { + error = GIT_ENOMEM; + break; + } + + memset(var, 0x0, sizeof(cvar_t)); + + var->section = git__strdup(current_section); + if (var->section == NULL) { + error = GIT_ENOMEM; + free(var); + break; + } + + var->name = var_name; + var->value = var_value; + git__strtolower(var->name); + + CVAR_LIST_APPEND(&cfg_file->var_list, var); + + break; + } + } + + free(current_section); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config"); +} + +static int write_section(git_filebuf *file, cvar_t *var) +{ + int error; + + error = git_filebuf_printf(file, "[%s]\n", var->section); + if (error < GIT_SUCCESS) + return error; + + error = git_filebuf_printf(file, " %s = %s\n", var->name, var->value); + return error; +} + +/* + * This is pretty much the parsing, except we write out anything we don't have + */ +static int config_write(diskfile_backend *cfg, cvar_t *var) +{ + int error = GIT_SUCCESS, c; + int section_matches = 0, last_section_matched = 0; + char *current_section = NULL; + char *var_name, *var_value, *data_start; + git_filebuf file; + const char *pre_end = NULL, *post_start = NULL; + + /* We need to read in our own config file */ + error = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path); + if (error < GIT_SUCCESS) { + return git__rethrow(error, "Failed to read existing config file %s", cfg->file_path); + } + + /* Initialise the reading position */ + cfg->reader.read_ptr = cfg->reader.buffer.data; + cfg->reader.eof = 0; + data_start = cfg->reader.read_ptr; + + /* Lock the file */ + error = git_filebuf_open(&file, cfg->file_path, 0); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to lock config file"); + + skip_bom(cfg); + + while (error == GIT_SUCCESS && !cfg->reader.eof) { + c = cfg_peek(cfg, SKIP_WHITESPACE); + + switch (c) { + case '\0': /* We've arrived at the end of the file */ + break; + + case '[': /* section header, new section begins */ + /* + * We set both positions to the current one in case we + * need to add a variable to the end of a section. In that + * case, we want both variables to point just before the + * new section. If we actually want to replace it, the + * default case will take care of updating them. + */ + pre_end = post_start = cfg->reader.read_ptr; + if (current_section) + free(current_section); + error = parse_section_header(cfg, ¤t_section); + if (error < GIT_SUCCESS) + break; + + /* Keep track of when it stops matching */ + last_section_matched = section_matches; + section_matches = !strcmp(current_section, var->section); + break; + + case ';': + case '#': + cfg_consume_line(cfg); + break; + + default: + /* + * If the section doesn't match, but the last section did, + * it means we need to add a variable (so skip the line + * otherwise). If both the section and name match, we need + * to overwrite the variable (so skip the line + * otherwise). pre_end needs to be updated each time so we + * don't loose that information, but we only need to + * update post_start if we're going to use it in this + * iteration. + */ + if (!section_matches) { + if (!last_section_matched) { + cfg_consume_line(cfg); + break; + } + } else { + int cmp = -1; + + pre_end = cfg->reader.read_ptr; + if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS) + cmp = strcasecmp(var->name, var_name); + + free(var_name); + free(var_value); + + if (cmp != 0) + break; + + post_start = cfg->reader.read_ptr; + } + + /* + * We've found the variable we wanted to change, so + * write anything up to it + */ + error = git_filebuf_write(&file, data_start, pre_end - data_start); + if (error < GIT_SUCCESS) { + git__rethrow(error, "Failed to write the first part of the file"); + break; + } + + /* + * Then replace the variable. If the value is NULL, it + * means we want to delete it, so pretend everything went + * fine + */ + if (var->value == NULL) + error = GIT_SUCCESS; + else + error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value); + if (error < GIT_SUCCESS) { + git__rethrow(error, "Failed to overwrite the variable"); + break; + } + + /* And then the write out rest of the file */ + error = git_filebuf_write(&file, post_start, + cfg->reader.buffer.len - (post_start - data_start)); + + if (error < GIT_SUCCESS) { + git__rethrow(error, "Failed to write the rest of the file"); + break; + } + + goto cleanup; + } + } + + /* + * Being here can mean that + * + * 1) our section is the last one in the file and we're + * adding a variable + * + * 2) we didn't find a section for us so we need to create it + * ourselves. + * + * Either way we need to write out the whole file. + */ + + error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len); + if (error < GIT_SUCCESS) { + git__rethrow(error, "Failed to write original config content"); + goto cleanup; + } + + /* And now if we just need to add a variable */ + if (section_matches) { + error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value); + goto cleanup; + } + + /* Or maybe we need to write out a whole section */ + error = write_section(&file, var); + if (error < GIT_SUCCESS) + git__rethrow(error, "Failed to write new section"); + + cleanup: + free(current_section); + + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&file); + else + error = git_filebuf_commit(&file); + + git_futils_freebuffer(&cfg->reader.buffer); + return error; +} + +static int is_multiline_var(const char *str) +{ + char *end = strrchr(str, '\0') - 1; + + while (isspace(*end)) + --end; + + return *end == '\\'; +} + +static int parse_multiline_variable(diskfile_backend *cfg, const char *first, char **out) +{ + char *line = NULL, *end; + int error = GIT_SUCCESS, len, ret; + char *buf; + + /* Check that the next line exists */ + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + /* We've reached the end of the file, there is input missing */ + if (line[0] == '\0') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly"); + goto out; + } + + strip_comments(line); + + /* If it was just a comment, pretend it didn't exist */ + if (line[0] == '\0') { + error = parse_multiline_variable(cfg, first, out); + goto out; + } + + /* Find the continuation character '\' and strip the whitespace */ + end = strrchr(first, '\\'); + while (isspace(end[-1])) + --end; + + *end = '\0'; /* Terminate the string here */ + + len = strlen(first) + strlen(line) + 2; + buf = git__malloc(len); + if (buf == NULL) { + error = GIT_ENOMEM; + goto out; + } + + ret = p_snprintf(buf, len, "%s %s", first, line); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno)); + free(buf); + goto out; + } + + /* + * If we need to continue reading the next line, pretend + * everything we've read up to now was in one line and call + * ourselves. + */ + if (is_multiline_var(buf)) { + char *final_val; + error = parse_multiline_variable(cfg, buf, &final_val); + free(buf); + buf = final_val; + } + + *out = buf; + + out: + free(line); + return error; +} + +static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value) +{ + char *tmp; + int error = GIT_SUCCESS; + const char *var_end = NULL; + const char *value_start = NULL; + char *line; + + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + strip_comments(line); + + var_end = strchr(line, '='); + + if (var_end == NULL) + var_end = strchr(line, '\0'); + else + value_start = var_end + 1; + + if (isspace(var_end[-1])) { + do var_end--; + while (isspace(var_end[0])); + } + + tmp = git__strndup(line, var_end - line + 1); + if (tmp == NULL) { + error = GIT_ENOMEM; + goto out; + } + + *var_name = tmp; + + /* + * Now, let's try to parse the value + */ + if (value_start != NULL) { + + while (isspace(value_start[0])) + value_start++; + + if (value_start[0] == '\0') + goto out; + + if (is_multiline_var(value_start)) { + error = parse_multiline_variable(cfg, value_start, var_value); + if (error < GIT_SUCCESS) + free(*var_name); + goto out; + } + + tmp = strdup(value_start); + if (tmp == NULL) { + free(*var_name); + error = GIT_ENOMEM; + goto out; + } + + *var_value = tmp; + } else { + /* If there is no value, boolean true is assumed */ + *var_value = NULL; + } + + out: + free(line); + return error; +} diff --git a/vendor/libgit2/src/delta-apply.c b/vendor/libgit2/src/delta-apply.c index 16f26be44..a6b711436 100644 --- a/vendor/libgit2/src/delta-apply.c +++ b/vendor/libgit2/src/delta-apply.c @@ -46,13 +46,13 @@ int git__delta_apply( * base object, resulting in data corruption or segfault. */ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data"); if (hdr_sz(&res_sz, &delta, delta_end) < 0) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data"); if ((res_dp = git__malloc(res_sz + 1)) == NULL) - return GIT_ERROR; + return GIT_ENOMEM; res_dp[res_sz] = '\0'; out->data = res_dp; out->len = res_sz; @@ -105,5 +105,5 @@ int git__delta_apply( fail: free(out->data); out->data = NULL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to apply delta"); } diff --git a/vendor/libgit2/src/errors.c b/vendor/libgit2/src/errors.c index bf3810174..5031245de 100644 --- a/vendor/libgit2/src/errors.c +++ b/vendor/libgit2/src/errors.c @@ -42,7 +42,7 @@ static struct { {GIT_EOBJTYPE, "The specified object is of invalid type"}, {GIT_EOBJCORRUPTED, "The specified object has its data corrupted"}, {GIT_ENOTAREPO, "The specified repository is invalid"}, - {GIT_EINVALIDTYPE, "The object type is invalid or doesn't match"}, + {GIT_EINVALIDTYPE, "The object or config variable type is invalid or doesn't match"}, {GIT_EMISSINGOBJDATA, "The object cannot be written that because it's missing internal data"}, {GIT_EPACKCORRUPTED, "The packfile for the ODB is corrupted"}, {GIT_EFLOCKFAIL, "Failed to adquire or release a file lock"}, @@ -61,6 +61,7 @@ static struct { {GIT_EEXISTS, "A reference with this name already exists"}, {GIT_EOVERFLOW, "The given integer literal is too large to be parsed"}, {GIT_ENOTNUM, "The given literal is not a valid number"}, + {GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"}, }; const char *git_strerror(int num) @@ -76,7 +77,7 @@ const char *git_strerror(int num) return "Unknown error"; } -int git__rethrow(int error, const char *msg, ...) +void git___rethrow(const char *msg, ...) { char new_error[1024]; char *old_error = NULL; @@ -90,23 +91,26 @@ int git__rethrow(int error, const char *msg, ...) old_error = strdup(g_last_error); snprintf(g_last_error, sizeof(g_last_error), "%s \n - %s", new_error, old_error); free(old_error); - - return error; } -int git__throw(int error, const char *msg, ...) +void git___throw(const char *msg, ...) { va_list va; va_start(va, msg); vsnprintf(g_last_error, sizeof(g_last_error), msg, va); va_end(va); - - return error; } const char *git_lasterror(void) { + if (!g_last_error[0]) + return NULL; + return g_last_error; } +void git_clearerror(void) +{ + g_last_error[0] = '\0'; +} diff --git a/vendor/libgit2/src/fetch.c b/vendor/libgit2/src/fetch.c new file mode 100644 index 000000000..74c93da8d --- /dev/null +++ b/vendor/libgit2/src/fetch.c @@ -0,0 +1,136 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "git2/remote.h" +#include "git2/oid.h" +#include "git2/refs.h" +#include "git2/revwalk.h" + +#include "common.h" +#include "transport.h" +#include "remote.h" +#include "refspec.h" +#include "fetch.h" + +static int filter_wants(git_remote *remote) +{ + git_vector list; + git_headarray refs; + git_transport *t = remote->transport; + git_repository *repo = remote->repo; + const git_refspec *spec; + int error; + unsigned int i; + + error = git_vector_init(&list, 16, NULL); + if (error < GIT_SUCCESS) + return error; + + error = t->ls(t, &refs); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to get remote ref list"); + goto cleanup; + } + + spec = git_remote_fetchspec(remote); + if (spec == NULL) { + error = git__throw(GIT_ERROR, "The remote has no fetchspec"); + goto cleanup; + } + + for (i = 0; i < refs.len; ++i) { + git_remote_head *head = refs.heads[i]; + + /* If it doesn't match the refpec, we don't want it */ + error = git_refspec_src_match(spec, head->name); + if (error == GIT_ENOMATCH) + continue; + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Error matching remote ref name"); + goto cleanup; + } + + /* If we have the object, mark it so we don't ask for it */ + if (git_odb_exists(repo->db, &head->oid)) + head->local = 1; + else + remote->need_pack = 1; + + error = git_vector_insert(&list, head); + if (error < GIT_SUCCESS) + goto cleanup; + } + + remote->refs.len = list.length; + remote->refs.heads = (git_remote_head **) list.contents; + + return GIT_SUCCESS; + +cleanup: + git_vector_free(&list); + return error; +} + +/* + * In this first version, we push all our refs in and start sending + * them out. When we get an ACK we hide that commit and continue + * traversing until we're done + */ +int git_fetch_negotiate(git_remote *remote) +{ + int error; + git_headarray *list = &remote->refs; + git_transport *t = remote->transport; + + error = filter_wants(remote); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to filter the reference list for wants"); + + /* Don't try to negotiate when we don't want anything */ + if (list->len == 0) + return GIT_SUCCESS; + if (!remote->need_pack) + return GIT_SUCCESS; + + /* + * Now we have everything set up so we can start tell the server + * what we want and what we have. + */ + error = t->send_wants(t, list); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to send want list"); + + return t->negotiate_fetch(t, remote->repo, &remote->refs); +} + +int git_fetch_download_pack(char **out, git_remote *remote) +{ + if(!remote->need_pack) { + *out = NULL; + return GIT_SUCCESS; + } + + return remote->transport->download_pack(out, remote->transport, remote->repo); +} diff --git a/vendor/libgit2/src/fetch.h b/vendor/libgit2/src/fetch.h new file mode 100644 index 000000000..ad4451ffe --- /dev/null +++ b/vendor/libgit2/src/fetch.h @@ -0,0 +1,7 @@ +#ifndef INCLUDE_fetch_h__ +#define INCLUDE_fetch_h__ + +int git_fetch_negotiate(git_remote *remote); +int git_fetch_download_pack(char **out, git_remote *remote); + +#endif diff --git a/vendor/libgit2/src/filebuf.c b/vendor/libgit2/src/filebuf.c index eb93424ef..6d398a7db 100644 --- a/vendor/libgit2/src/filebuf.c +++ b/vendor/libgit2/src/filebuf.c @@ -32,41 +32,39 @@ static const size_t WRITE_BUFFER_SIZE = (4096 * 2); static int lock_file(git_filebuf *file, int flags) { - if (gitfo_exists(file->path_lock) == 0) { + if (git_futils_exists(file->path_lock) == 0) { if (flags & GIT_FILEBUF_FORCE) - gitfo_unlink(file->path_lock); + p_unlink(file->path_lock); else - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to lock file"); } /* create path to the file buffer is required */ if (flags & GIT_FILEBUF_FORCE) { - file->fd = gitfo_creat_force(file->path_lock, 0644); + file->fd = git_futils_creat_locked_withpath(file->path_lock, 0644); } else { - file->fd = gitfo_creat(file->path_lock, 0644); + file->fd = git_futils_creat_locked(file->path_lock, 0644); } if (file->fd < 0) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to create lock"); - /* TODO: do a flock() in the descriptor file_lock */ - - if ((flags & GIT_FILEBUF_APPEND) && gitfo_exists(file->path_original) == 0) { + if ((flags & GIT_FILEBUF_APPEND) && git_futils_exists(file->path_original) == 0) { git_file source; char buffer[2048]; size_t read_bytes; - source = gitfo_open(file->path_original, O_RDONLY); + source = p_open(file->path_original, O_RDONLY); if (source < 0) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to lock file. Could not open %s", file->path_original); - while ((read_bytes = gitfo_read(source, buffer, 2048)) > 0) { - gitfo_write(file->fd, buffer, read_bytes); + while ((read_bytes = p_read(source, buffer, 2048)) > 0) { + p_write(file->fd, buffer, read_bytes); if (file->digest) git_hash_update(file->digest, buffer, read_bytes); } - gitfo_close(source); + p_close(source); } return GIT_SUCCESS; @@ -75,10 +73,10 @@ static int lock_file(git_filebuf *file, int flags) void git_filebuf_cleanup(git_filebuf *file) { if (file->fd >= 0) - gitfo_close(file->fd); + p_close(file->fd); - if (file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS) - gitfo_unlink(file->path_lock); + if (file->fd >= 0 && file->path_lock && git_futils_exists(file->path_lock) == GIT_SUCCESS) + p_unlink(file->path_lock); if (file->digest) git_hash_free_ctx(file->digest); @@ -99,12 +97,12 @@ GIT_INLINE(int) flush_buffer(git_filebuf *file) return result; } -static int write_normal(git_filebuf *file, const void *source, size_t len) +static int write_normal(git_filebuf *file, void *source, size_t len) { int result = 0; if (len > 0) { - result = gitfo_write(file->fd, (void *)source, len); + result = p_write(file->fd, (void *)source, len); if (file->digest) git_hash_update(file->digest, source, len); } @@ -112,7 +110,7 @@ static int write_normal(git_filebuf *file, const void *source, size_t len) return result; } -static int write_deflate(git_filebuf *file, const void *source, size_t len) +static int write_deflate(git_filebuf *file, void *source, size_t len) { int result = Z_OK; z_stream *zs = &file->zs; @@ -132,8 +130,8 @@ static int write_deflate(git_filebuf *file, const void *source, size_t len) have = file->buf_size - zs->avail_out; - if (gitfo_write(file->fd, file->z_buf, have) < GIT_SUCCESS) - return GIT_EOSERR; + if (p_write(file->fd, file->z_buf, have) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to write to file"); } while (zs->avail_out == 0); @@ -179,7 +177,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) /* Initialize the ZLib stream */ if (deflateInit(&file->zs, Z_BEST_SPEED) != Z_OK) { - error = GIT_EZLIB; + error = git__throw(GIT_EZLIB, "Failed to initialize zlib"); goto cleanup; } @@ -202,7 +200,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) char tmp_path[GIT_PATH_MAX]; /* Open the file as temporary for locking */ - file->fd = gitfo_mktemp(tmp_path, path); + file->fd = git_futils_mktmp(tmp_path, path); if (file->fd < 0) { error = GIT_EOSERR; goto cleanup; @@ -245,18 +243,17 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) cleanup: git_filebuf_cleanup(file); - return error; + return git__rethrow(error, "Failed to open file buffer for '%s'", path); } int git_filebuf_hash(git_oid *oid, git_filebuf *file) { int error; - if (file->digest == NULL) - return GIT_ERROR; + assert(oid && file && file->digest); if ((error = flush_buffer(file)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to get hash for file"); git_hash_final(oid, file->digest); git_hash_free_ctx(file->digest); @@ -279,22 +276,23 @@ int git_filebuf_commit(git_filebuf *file) { int error; - /* tmp file cannot be commited */ - if (file->path_original == NULL) - return GIT_EOSERR; + /* temporary files cannot be committed */ + assert(file && file->path_original); file->flush_mode = Z_FINISH; if ((error = flush_buffer(file)) < GIT_SUCCESS) goto cleanup; - gitfo_close(file->fd); + p_close(file->fd); file->fd = -1; - error = gitfo_mv(file->path_lock, file->path_original); + error = git_futils_mv_atomic(file->path_lock, file->path_original); cleanup: git_filebuf_cleanup(file); - return error; + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to commit locked file from buffer"); + return GIT_SUCCESS; } GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len) @@ -317,23 +315,13 @@ int git_filebuf_write(git_filebuf *file, const void *buff, size_t len) return GIT_SUCCESS; } - /* flush the cache if it doesn't fit */ - if (file->buf_pos > 0) { - add_to_cache(file, buf, space_left); - - if ((error = flush_buffer(file)) < GIT_SUCCESS) - return error; + add_to_cache(file, buf, space_left); - len -= space_left; - buf += space_left; - } + if ((error = flush_buffer(file)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write to buffer"); - /* write too-large chunks immediately */ - if (len > file->buf_size) { - error = file->write(file, buf, len); - if (error < GIT_SUCCESS) - return error; - } + len -= space_left; + buf += space_left; } } @@ -349,7 +337,7 @@ int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len) if (space_left <= len) { if ((error = flush_buffer(file)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to reserve buffer"); } *buffer = (file->buffer + file->buf_pos); @@ -361,24 +349,48 @@ int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len) int git_filebuf_printf(git_filebuf *file, const char *format, ...) { va_list arglist; - size_t space_left = file->buf_size - file->buf_pos; + size_t space_left; int len, error; + char *tmp_buffer; - va_start(arglist, format); + space_left = file->buf_size - file->buf_pos; + + do { + va_start(arglist, format); + len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist); + va_end(arglist); - len = vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist); + if (len < 0) + return git__throw(GIT_EOSERR, "Failed to format string"); + + if ((size_t)len + 1 <= space_left) { + file->buf_pos += len; + return GIT_SUCCESS; + } - if (len < 0 || (size_t)len >= space_left) { if ((error = flush_buffer(file)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to output to buffer"); + + space_left = file->buf_size - file->buf_pos; + + } while ((size_t)len + 1 <= space_left); + + tmp_buffer = git__malloc(len + 1); + if (!tmp_buffer) + return GIT_ENOMEM; + + va_start(arglist, format); + len = p_vsnprintf(tmp_buffer, len + 1, format, arglist); + va_end(arglist); - len = vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist); - if (len < 0 || (size_t)len > file->buf_size) - return GIT_ENOMEM; + if (len < 0) { + free(tmp_buffer); + return git__throw(GIT_EOSERR, "Failed to format string"); } - file->buf_pos += len; - return GIT_SUCCESS; + error = git_filebuf_write(file, tmp_buffer, len); + free(tmp_buffer); + return error; } diff --git a/vendor/libgit2/src/filebuf.h b/vendor/libgit2/src/filebuf.h index 37cb36784..9154cabcd 100644 --- a/vendor/libgit2/src/filebuf.h +++ b/vendor/libgit2/src/filebuf.h @@ -22,8 +22,7 @@ struct git_filebuf { char *path_original; char *path_lock; - int (*write)(struct git_filebuf *file, - const void *source, size_t len); + int (*write)(struct git_filebuf *file, void *source, size_t len); git_hash_ctx *digest; @@ -41,7 +40,7 @@ typedef struct git_filebuf git_filebuf; int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len); int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); -int git_filebuf_printf(git_filebuf *file, const char *format, ...); +int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); int git_filebuf_open(git_filebuf *lock, const char *path, int flags); int git_filebuf_commit(git_filebuf *lock); diff --git a/vendor/libgit2/src/fileops.c b/vendor/libgit2/src/fileops.c index 5dd4a3806..d7413a138 100644 --- a/vendor/libgit2/src/fileops.c +++ b/vendor/libgit2/src/fileops.c @@ -2,185 +2,190 @@ #include "fileops.h" #include -int gitfo_mkdir_2file(const char *file_path) +int git_futils_mv_atomic(const char *from, const char *to) +{ +#ifdef GIT_WIN32 + /* + * Win32 POSIX compilance my ass. If the destination + * file exists, the `rename` call fails. This is as + * close as it gets with the Win32 API. + */ + return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; +#else + /* Don't even try this on Win32 */ + if (!link(from, to)) { + p_unlink(from); + return GIT_SUCCESS; + } + + if (!rename(from, to)) + return GIT_SUCCESS; + + return GIT_ERROR; +#endif +} + +int git_futils_mkpath2file(const char *file_path) { const int mode = 0755; /* or 0777 ? */ int error = GIT_SUCCESS; char target_folder_path[GIT_PATH_MAX]; - error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path); + error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) - return error; + return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path); /* Does the containing folder exist? */ - if (gitfo_isdir(target_folder_path)) { - git__joinpath(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ + if (git_futils_isdir(target_folder_path)) { + git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */ /* Let's create the tree structure */ - error = gitfo_mkdir_recurs(target_folder_path, mode); + error = git_futils_mkdir_r(target_folder_path, mode); if (error < GIT_SUCCESS) - return error; + return error; /* The callee already takes care of setting the correct error message. */ } return GIT_SUCCESS; } -int gitfo_mktemp(char *path_out, const char *filename) +int git_futils_mktmp(char *path_out, const char *filename) { int fd; strcpy(path_out, filename); strcat(path_out, "_git2_XXXXXX"); -#if defined(_MSC_VER) - /* FIXME: there may be race conditions when multi-threading - * with the library */ - if (_mktemp_s(path_out, GIT_PATH_MAX) != 0) - return GIT_EOSERR; - - fd = gitfo_creat(path_out, 0744); -#else - fd = mkstemp(path_out); -#endif + if ((fd = p_mkstemp(path_out)) < 0) + return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out); - return fd >= 0 ? fd : GIT_EOSERR; + return fd; } -int gitfo_open(const char *path, int flags) +int git_futils_creat_withpath(const char *path, int mode) { - int fd = open(path, flags | O_BINARY); - return fd >= 0 ? fd : GIT_EOSERR; + if (git_futils_mkpath2file(path) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to create file %s", path); + + return p_creat(path, mode); } -int gitfo_creat(const char *path, int mode) +int git_futils_creat_locked(const char *path, int mode) { - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); - return fd >= 0 ? fd : GIT_EOSERR; + int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode); + return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path); } -int gitfo_creat_force(const char *path, int mode) +int git_futils_creat_locked_withpath(const char *path, int mode) { - if (gitfo_mkdir_2file(path) < GIT_SUCCESS) - return GIT_EOSERR; + if (git_futils_mkpath2file(path) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to create locked file %s", path); - return gitfo_creat(path, mode); + return git_futils_creat_locked(path, mode); } -int gitfo_read(git_file fd, void *buf, size_t cnt) +int git_futils_isdir(const char *path) { - char *b = buf; - while (cnt) { - ssize_t r = read(fd, b, cnt); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return GIT_EOSERR; - } - if (!r) { - errno = EPIPE; - return GIT_EOSERR; - } - cnt -= r; - b += r; - } - return GIT_SUCCESS; -} +#ifdef GIT_WIN32 + DWORD attr = GetFileAttributes(path); + if (attr == INVALID_FILE_ATTRIBUTES) + return GIT_ERROR; -int gitfo_write(git_file fd, void *buf, size_t cnt) -{ - char *b = buf; - while (cnt) { - ssize_t r = write(fd, b, cnt); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return GIT_EOSERR; - } - if (!r) { - errno = EPIPE; - return GIT_EOSERR; - } - cnt -= r; - b += r; - } - return GIT_SUCCESS; + return (attr & FILE_ATTRIBUTE_DIRECTORY) ? GIT_SUCCESS : GIT_ERROR; + +#else + struct stat st; + if (p_stat(path, &st) < GIT_SUCCESS) + return GIT_ERROR; + + return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR; +#endif } -int gitfo_isdir(const char *path) +int git_futils_isfile(const char *path) { struct stat st; - int len, stat_error; - - if (!path) - return GIT_ENOTFOUND; - - len = strlen(path); - - /* win32: stat path for folders cannot end in a slash */ - if (path[len - 1] == '/') { - char *path_fixed = NULL; - path_fixed = git__strdup(path); - path_fixed[len - 1] = 0; - stat_error = gitfo_stat(path_fixed, &st); - free(path_fixed); - } else { - stat_error = gitfo_stat(path, &st); - } + int stat_error; + + assert(path); + stat_error = p_stat(path, &st); if (stat_error < GIT_SUCCESS) - return GIT_ENOTFOUND; + return -1; - if (!S_ISDIR(st.st_mode)) - return GIT_ENOTFOUND; + if (!S_ISREG(st.st_mode)) + return -1; - return GIT_SUCCESS; + return 0; } -int gitfo_exists(const char *path) +int git_futils_exists(const char *path) { assert(path); return access(path, F_OK); } -git_off_t gitfo_size(git_file fd) +git_off_t git_futils_filesize(git_file fd) { struct stat sb; - if (gitfo_fstat(fd, &sb)) - return GIT_EOSERR; + if (p_fstat(fd, &sb)) + return GIT_ERROR; + return sb.st_size; } -int gitfo_read_file(gitfo_buf *obj, const char *path) +int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated) { git_file fd; size_t len; - git_off_t size; + struct stat st; unsigned char *buff; assert(obj && path && *path); - if ((fd = gitfo_open(path, O_RDONLY)) < 0) - return GIT_ERROR; + if (updated != NULL) + *updated = 0; - if (((size = gitfo_size(fd)) < 0) || !git__is_sizet(size+1)) { - gitfo_close(fd); - return GIT_ERROR; - } - len = (size_t) size; + if (p_stat(path, &st) < 0) + return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path); + + if (S_ISDIR(st.st_mode)) + return git__throw(GIT_ERROR, "Can't read a dir into a buffer"); + + /* + * If we were given a time, we only want to read the file if it + * has been modified. + */ + if (mtime != NULL && *mtime >= st.st_mtime) + return GIT_SUCCESS; + + if (mtime != NULL) + *mtime = st.st_mtime; + if (!git__is_sizet(st.st_size+1)) + return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path); + + len = (size_t) st.st_size; + + if ((fd = p_open(path, O_RDONLY)) < 0) + return git__throw(GIT_EOSERR, "Failed to open %s for reading", path); if ((buff = git__malloc(len + 1)) == NULL) { - gitfo_close(fd); - return GIT_ERROR; + p_close(fd); + return GIT_ENOMEM; } - if (gitfo_read(fd, buff, len) < 0) { - gitfo_close(fd); + if (p_read(fd, buff, len) < 0) { + p_close(fd); free(buff); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to read file `%s`", path); } buff[len] = '\0'; - gitfo_close(fd); + p_close(fd); + + if (mtime != NULL) + *mtime = st.st_mtime; + if (updated != NULL) + *updated = 1; obj->data = buff; obj->len = len; @@ -188,146 +193,46 @@ int gitfo_read_file(gitfo_buf *obj, const char *path) return GIT_SUCCESS; } -void gitfo_free_buf(gitfo_buf *obj) +int git_futils_readbuffer(git_fbuffer *obj, const char *path) { - assert(obj); - free(obj->data); - obj->data = NULL; + return git_futils_readbuffer_updated(obj, path, NULL, NULL); } -int gitfo_mv(const char *from, const char *to) +void git_futils_freebuffer(git_fbuffer *obj) { -#ifdef GIT_WIN32 - /* - * Win32 POSIX compilance my ass. If the destination - * file exists, the `rename` call fails. This is as - * close as it gets with the Win32 API. - */ - return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; -#else - /* Don't even try this on Win32 */ - if (!link(from, to)) { - gitfo_unlink(from); - return GIT_SUCCESS; - } - - if (!rename(from, to)) - return GIT_SUCCESS; - - return GIT_EOSERR; -#endif -} - -int gitfo_mv_force(const char *from, const char *to) -{ - if (gitfo_mkdir_2file(to) < GIT_SUCCESS) - return GIT_EOSERR; - - return gitfo_mv(from, to); -} - -int gitfo_map_ro(git_map *out, git_file fd, git_off_t begin, size_t len) -{ - if (git__mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin) < GIT_SUCCESS) - return GIT_EOSERR; - return GIT_SUCCESS; -} - -void gitfo_free_map(git_map *out) -{ - git__munmap(out); + assert(obj); + free(obj->data); + obj->data = NULL; } -/* cached diskio */ -struct gitfo_cache { - git_file fd; - size_t cache_size, pos; - unsigned char *cache; -}; -gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size) +int git_futils_mv_withpath(const char *from, const char *to) { - gitfo_cache *ioc; - - ioc = git__malloc(sizeof(*ioc)); - if (!ioc) - return NULL; - - ioc->fd = fd; - ioc->pos = 0; - ioc->cache_size = cache_size; - ioc->cache = git__malloc(cache_size); - if (!ioc->cache) { - free(ioc); - return NULL; - } + if (git_futils_mkpath2file(to) < GIT_SUCCESS) + return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */ - return ioc; + return git_futils_mv_atomic(from, to); /* The callee already takes care of setting the correct error message. */ } -GIT_INLINE(void) gitfo_add_to_cache(gitfo_cache *ioc, void *buf, size_t len) +int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len) { - memcpy(ioc->cache + ioc->pos, buf, len); - ioc->pos += len; + return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin); } -int gitfo_flush_cached(gitfo_cache *ioc) +void git_futils_mmap_free(git_map *out) { - int result = GIT_SUCCESS; - - if (ioc->pos) { - result = gitfo_write(ioc->fd, ioc->cache, ioc->pos); - ioc->pos = 0; - } - - return result; + p_munmap(out); } -int gitfo_write_cached(gitfo_cache *ioc, void *buff, size_t len) +/* Taken from git.git */ +GIT_INLINE(int) is_dot_or_dotdot(const char *name) { - unsigned char *buf = buff; - - for (;;) { - size_t space_left = ioc->cache_size - ioc->pos; - /* cache if it's small */ - if (space_left > len) { - gitfo_add_to_cache(ioc, buf, len); - return GIT_SUCCESS; - } - - /* flush the cache if it doesn't fit */ - if (ioc->pos) { - int rc; - gitfo_add_to_cache(ioc, buf, space_left); - rc = gitfo_flush_cached(ioc); - if (rc < 0) - return rc; - - len -= space_left; - buf += space_left; - } - - /* write too-large chunks immediately */ - if (len > ioc->cache_size) - return gitfo_write(ioc->fd, buf, len); - } + return (name[0] == '.' && + (name[1] == '\0' || + (name[1] == '.' && name[2] == '\0'))); } -int gitfo_close_cached(gitfo_cache *ioc) -{ - git_file fd; - - if (gitfo_flush_cached(ioc) < GIT_SUCCESS) - return GIT_ERROR; - - fd = ioc->fd; - free(ioc->cache); - free(ioc); - - return gitfo_close(fd); -} - -int gitfo_dirent( +int git_futils_direach( char *path, size_t path_sz, int (*fn)(void *, char *), @@ -338,7 +243,7 @@ int gitfo_dirent( struct dirent *de; if (!wd_len || path_sz < wd_len + 2) - return GIT_ERROR; + return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path); while (path[wd_len - 1] == '/') wd_len--; @@ -347,31 +252,26 @@ int gitfo_dirent( dir = opendir(path); if (!dir) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path); while ((de = readdir(dir)) != NULL) { size_t de_len; int result; - /* always skip '.' and '..' */ - if (de->d_name[0] == '.') { - if (de->d_name[1] == '\0') - continue; - if (de->d_name[1] == '.' && de->d_name[2] == '\0') - continue; - } + if (is_dot_or_dotdot(de->d_name)) + continue; de_len = strlen(de->d_name); if (path_sz < wd_len + de_len + 1) { closedir(dir); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path); } strcpy(path + wd_len, de->d_name); result = fn(arg, path); if (result < GIT_SUCCESS) { closedir(dir); - return result; + return result; /* The callee is reponsible for setting the correct error message */ } if (result > 0) { closedir(dir); @@ -383,27 +283,7 @@ int gitfo_dirent( return GIT_SUCCESS; } - -int retrieve_path_root_offset(const char *path) -{ - int offset = 0; - -#ifdef GIT_WIN32 - - /* Does the root of the path look like a windows drive ? */ - if (isalpha(path[0]) && (path[1] == ':')) - offset += 2; - -#endif - - if (*(path + offset) == '/') - return offset; - - return GIT_ERROR; -} - - -int gitfo_mkdir_recurs(const char *path, int mode) +int git_futils_mkdir_r(const char *path, int mode) { int error, root_path_offset; char *pp, *sp; @@ -415,14 +295,14 @@ int gitfo_mkdir_recurs(const char *path, int mode) error = GIT_SUCCESS; pp = path_copy; - root_path_offset = retrieve_path_root_offset(pp); + root_path_offset = git_path_root(pp); if (root_path_offset > 0) pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */ - while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != 0) { - if (sp != pp && gitfo_isdir(path_copy) < GIT_SUCCESS) { + while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) { + if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) { *sp = 0; - error = gitfo_mkdir(path_copy, mode); + error = p_mkdir(path_copy, mode); /* Do not choke while trying to recreate an existing directory */ if (errno == EEXIST) @@ -434,152 +314,49 @@ int gitfo_mkdir_recurs(const char *path, int mode) pp = sp + 1; } - if (*(pp - 1) != '/' && error == GIT_SUCCESS) - error = gitfo_mkdir(path, mode); + if (*pp != '\0' && error == GIT_SUCCESS) { + error = p_mkdir(path, mode); + if (errno == EEXIST) + error = GIT_SUCCESS; + } free(path_copy); - return error; -} - -static int retrieve_previous_path_component_start(const char *path) -{ - int offset, len, root_offset, start = 0; - - root_offset = retrieve_path_root_offset(path); - if (root_offset > -1) - start += root_offset; - len = strlen(path); - offset = len - 1; - - /* Skip leading slash */ - if (path[start] == '/') - start++; - - /* Skip trailing slash */ - if (path[offset] == '/') - offset--; - - if (offset < root_offset) - return GIT_ERROR; - - while (offset > start && path[offset-1] != '/') { - offset--; - } + if (error < GIT_SUCCESS) + return git__throw(error, "Failed to recursively create `%s` tree structure", path); - return offset; + return GIT_SUCCESS; } -int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path) +static int _rmdir_recurs_foreach(void *opaque, char *path) { - int len = 0, segment_len, only_dots, root_path_offset, error = GIT_SUCCESS; - char *current; - const char *buffer_out_start, *buffer_end; - - current = (char *)path; - buffer_end = path + strlen(path); - buffer_out_start = buffer_out; - - root_path_offset = retrieve_path_root_offset(path); - if (root_path_offset < 0) { - error = gitfo_getcwd(buffer_out, size); - if (error < GIT_SUCCESS) - return error; - - len = strlen(buffer_out); - buffer_out += len; - } - - while (current < buffer_end) { - /* Prevent multiple slashes from being added to the output */ - if (*current == '/' && len > 0 && buffer_out_start[len - 1] == '/') { - current++; - continue; - } - - only_dots = 1; - segment_len = 0; - - /* Copy path segment to the output */ - while (current < buffer_end && *current != '/') - { - only_dots &= (*current == '.'); - *buffer_out++ = *current++; - segment_len++; - len++; - } - - /* Skip current directory */ - if (only_dots && segment_len == 1) - { - current++; - buffer_out -= segment_len; - len -= segment_len; - continue; - } - - /* Handle the double-dot upward directory navigation */ - if (only_dots && segment_len == 2) - { - current++; - buffer_out -= segment_len; - - *buffer_out ='\0'; - len = retrieve_previous_path_component_start(buffer_out_start); + int error = GIT_SUCCESS; + int force = *(int *)opaque; - /* Are we escaping out of the root dir? */ - if (len < 0) - return GIT_EINVALIDPATH; + if (git_futils_isdir(path) == GIT_SUCCESS) { + size_t root_size = strlen(path); - buffer_out = (char *)buffer_out_start + len; - continue; - } + if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_foreach, opaque)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to remove directory `%s`", path); - /* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */ - if (only_dots && segment_len > 0) - return GIT_EINVALIDPATH; + path[root_size] = '\0'; + return p_rmdir(path); - *buffer_out++ = '/'; - len++; + } else if (force) { + return p_unlink(path); } - *buffer_out = '\0'; - - return GIT_SUCCESS; + return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path); } -int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path) +int git_futils_rmdir_r(const char *path, int force) { - int error, path_len, i; - const char* pattern = "/.."; - - path_len = strlen(path); - - /* Let's make sure the filename isn't empty nor a dot */ - if (path_len == 0 || (path_len == 1 && *path == '.')) - return GIT_EINVALIDPATH; - - /* Let's make sure the filename doesn't end with "/", "/." or "/.." */ - for (i = 1; path_len > i && i < 4; i++) { - if (!strncmp(path + path_len - i, pattern, i)) - return GIT_EINVALIDPATH; - } - - error = gitfo_prettify_dir_path(buffer_out, size, path); - if (error < GIT_SUCCESS) - return error; - - path_len = strlen(buffer_out); - if (path_len < 2) - return GIT_EINVALIDPATH; - - /* Remove the trailing slash */ - buffer_out[path_len - 1] = '\0'; - - return GIT_SUCCESS; + char p[GIT_PATH_MAX]; + strncpy(p, path, GIT_PATH_MAX); + return _rmdir_recurs_foreach(&force, p); } -int gitfo_cmp_path(const char *name1, int len1, int isdir1, +int git_futils_cmp_path(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2) { int len = len1 < len2 ? len1 : len2; @@ -597,34 +374,3 @@ int gitfo_cmp_path(const char *name1, int len1, int isdir1, return 0; } -static void posixify_path(char *path) -{ - while (*path) { - if (*path == '\\') - *path = '/'; - - path++; - } -} - -int gitfo_getcwd(char *buffer_out, size_t size) -{ - char *cwd_buffer; - - assert(buffer_out && size > 0); - -#ifdef GIT_WIN32 - cwd_buffer = _getcwd(buffer_out, size); -#else - cwd_buffer = getcwd(buffer_out, size); //TODO: Fixme. Ensure the required headers are correctly included -#endif - - if (cwd_buffer == NULL) - return GIT_EOSERR; - - posixify_path(buffer_out); - - git__joinpath(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash - - return GIT_SUCCESS; -} diff --git a/vendor/libgit2/src/fileops.h b/vendor/libgit2/src/fileops.h index 6e0fd9d14..84c35e41b 100644 --- a/vendor/libgit2/src/fileops.h +++ b/vendor/libgit2/src/fileops.h @@ -9,87 +9,101 @@ #include "common.h" #include "map.h" #include "dir.h" -#include -#include - -#ifdef GIT_WIN32 -GIT_INLINE(int) link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new)) -{ - GIT_UNUSED_ARG(old) - GIT_UNUSED_ARG(new) - errno = ENOSYS; - return -1; -} - -GIT_INLINE(int) git__mkdir(const char *path, int GIT_UNUSED(mode)) -{ - GIT_UNUSED_ARG(mode) - return mkdir(path); -} - -extern int git__unlink(const char *path); -extern int git__mkstemp(char *template); -extern int git__fsync(int fd); - -# ifndef GIT__WIN32_NO_HIDE_FILEOPS -# define unlink(p) git__unlink(p) -# define mkstemp(t) git__mkstemp(t) -# define mkdir(p,m) git__mkdir(p, m) -# define fsync(fd) git__fsync(fd) -# endif -#endif /* GIT_WIN32 */ - - -#if !defined(O_BINARY) -#define O_BINARY 0 -#endif - -#define GITFO_BUF_INIT {NULL, 0} - -typedef int git_file; -typedef struct gitfo_cache gitfo_cache; +#include "posix.h" +#include "path.h" + +/** + * Filebuffer methods + * + * Read whole files into an in-memory buffer for processing + */ +#define GIT_FBUFFER_INIT {NULL, 0} typedef struct { /* file io buffer */ void *data; /* data bytes */ size_t len; /* data length */ -} gitfo_buf; +} git_fbuffer; + +extern int git_futils_readbuffer(git_fbuffer *obj, const char *path); +extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated); +extern void git_futils_freebuffer(git_fbuffer *obj); + +/** + * File utils + * + * These are custom filesystem-related helper methods. They are + * rather high level, and wrap the underlying POSIX methods + * + * All these methods return GIT_SUCCESS on success, + * or an error code on failure and an error message is set. + */ + +/** + * Check if a file exists and can be accessed. + */ +extern int git_futils_exists(const char *path); + +/** + * Create and open a file, while also + * creating all the folders in its path + */ +extern int git_futils_creat_withpath(const char *path, int mode); + +/** + * Create an open a process-locked file + */ +extern int git_futils_creat_locked(const char *path, int mode); -extern int gitfo_exists(const char *path); -extern int gitfo_open(const char *path, int flags); -extern int gitfo_creat(const char *path, int mode); -extern int gitfo_creat_force(const char *path, int mode); -extern int gitfo_mktemp(char *path_out, const char *filename); -extern int gitfo_isdir(const char *path); -extern int gitfo_mkdir_recurs(const char *path, int mode); -extern int gitfo_mkdir_2file(const char *path); -#define gitfo_close(fd) close(fd) +/** + * Create an open a process-locked file, while + * also creating all the folders in its path + */ +extern int git_futils_creat_locked_withpath(const char *path, int mode); + +/** + * Check if the given path points to a directory + */ +extern int git_futils_isdir(const char *path); + +/** + * Check if the given path points to a regular file + */ +extern int git_futils_isfile(const char *path); + +/** + * Create a path recursively + */ +extern int git_futils_mkdir_r(const char *path, int mode); -extern int gitfo_read(git_file fd, void *buf, size_t cnt); -extern int gitfo_write(git_file fd, void *buf, size_t cnt); -#define gitfo_lseek(f,n,w) lseek(f, n, w) -extern git_off_t gitfo_size(git_file fd); +/** + * Create all the folders required to contain + * the full path of a file + */ +extern int git_futils_mkpath2file(const char *path); -extern int gitfo_read_file(gitfo_buf *obj, const char *path); -extern void gitfo_free_buf(gitfo_buf *obj); +extern int git_futils_rmdir_r(const char *path, int force); -/* Move (rename) a file; this operation is atomic */ -extern int gitfo_mv(const char *from, const char *to); +/** + * Create and open a temporary file with a `_git2_` suffix + */ +extern int git_futils_mktmp(char *path_out, const char *filename); -/* Move a file (forced); this will create the destination - * path if it doesn't exist */ -extern int gitfo_mv_force(const char *from, const char *to); +/** + * Atomically rename a file on the filesystem + */ +extern int git_futils_mv_atomic(const char *from, const char *to); -#define gitfo_stat(p,b) stat(p, b) -#define gitfo_fstat(f,b) fstat(f, b) +/** + * Move a file on the filesystem, create the + * destination path if it doesn't exist + */ +extern int git_futils_mv_withpath(const char *from, const char *to); -#define gitfo_unlink(p) unlink(p) -#define gitfo_rmdir(p) rmdir(p) -#define gitfo_chdir(p) chdir(p) -#define gitfo_mkdir(p,m) mkdir(p, m) -#define gitfo_mkstemp(t) mkstemp(t) -#define gitfo_fsync(fd) fsync(fd) -#define gitfo_chmod(p,m) chmod(p, m) +/** + * Get the filesize in bytes of a file + */ +extern git_off_t git_futils_filesize(git_file fd); /** * Read-only map all or part of a file into memory. @@ -106,7 +120,7 @@ extern int gitfo_mv_force(const char *from, const char *to); * - GIT_SUCCESS on success; * - GIT_EOSERR on an unspecified OS related error. */ -extern int gitfo_map_ro( +extern int git_futils_mmap_ro( git_map *out, git_file fd, git_off_t begin, @@ -116,7 +130,7 @@ extern int gitfo_map_ro( * Release the memory associated with a previous memory mapping. * @param map the mapping description previously configured. */ -extern void gitfo_free_map(git_map *map); +extern void git_futils_mmap_free(git_map *map); /** * Walk each directory entry, except '.' and '..', calling fn(state). @@ -129,70 +143,13 @@ extern void gitfo_free_map(git_map *map); * may modify the pathbuf, but only by appending new text. * @param state to pass to fn as the first arg. */ -extern int gitfo_dirent( +extern int git_futils_direach( char *pathbuf, size_t pathmax, int (*fn)(void *, char *), void *state); -extern gitfo_cache *gitfo_enable_caching(git_file fd, size_t cache_size); -extern int gitfo_write_cached(gitfo_cache *ioc, void *buf, size_t len); -extern int gitfo_flush_cached(gitfo_cache *ioc); -extern int gitfo_close_cached(gitfo_cache *ioc); - - -extern int gitfo_cmp_path(const char *name1, int len1, int isdir1, +extern int git_futils_cmp_path(const char *name1, int len1, int isdir1, const char *name2, int len2, int isdir2); -extern int gitfo_getcwd(char *buffer_out, size_t size); - -/** - * Clean up a provided absolute or relative directory path. - * - * This prettification relies on basic operations such as coalescing - * multiple forward slashes into a single slash, removing '.' and - * './' current directory segments, and removing parent directory - * whenever '..' is encountered. - * - * If not empty, the returned path ends with a forward slash. - * - * For instance, this will turn "d1/s1///s2/..//../s3" into "d1/s3/". - * - * This only performs a string based analysis of the path. - * No checks are done to make sure the path actually makes sense from - * the file system perspective. - * - * @param buffer_out buffer to populate with the normalized path. - * @param size buffer size. - * @param path directory path to clean up. - * @return - * - GIT_SUCCESS on success; - * - GIT_ERROR when the input path is invalid or escapes the current directory. - */ -int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path); - -/** - * Clean up a provided absolute or relative file path. - * - * This prettification relies on basic operations such as coalescing - * multiple forward slashes into a single slash, removing '.' and - * './' current directory segments, and removing parent directory - * whenever '..' is encountered. - * - * For instance, this will turn "d1/s1///s2/..//../s3" into "d1/s3". - * - * This only performs a string based analysis of the path. - * No checks are done to make sure the path actually makes sense from - * the file system perspective. - * - * @param buffer_out buffer to populate with the normalized path. - * @param size buffer size. - * @param path file path to clean up. - * @return - * - GIT_SUCCESS on success; - * - GIT_ERROR when the input path is invalid or escapes the current directory. - */ -int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path); - -int retrieve_path_root_offset(const char *path); #endif /* INCLUDE_fileops_h__ */ diff --git a/vendor/libgit2/src/hash.c b/vendor/libgit2/src/hash.c index 775e4b4c1..b8b311bcb 100644 --- a/vendor/libgit2/src/hash.c +++ b/vendor/libgit2/src/hash.c @@ -28,10 +28,8 @@ #if defined(PPC_SHA1) # include "ppc/sha1.h" -#elif defined(OPENSSL_SHA1) -# include #else -# include "block-sha1/sha1.h" +# include "sha1.h" #endif struct git_hash_ctx { diff --git a/vendor/libgit2/src/hashtable.c b/vendor/libgit2/src/hashtable.c index ee6d3a461..86e5a113a 100644 --- a/vendor/libgit2/src/hashtable.c +++ b/vendor/libgit2/src/hashtable.c @@ -94,23 +94,23 @@ static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other) } static int node_insert(git_hashtable *self, git_hashtable_node *new_node) -{ +{ int iteration, hash_id; - for (iteration = 0; iteration < MAX_LOOPS; iteration++) { + for (iteration = 0; iteration < MAX_LOOPS; iteration++) { for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) { git_hashtable_node *node; node = node_with_hash(self, new_node->key, hash_id); node_swap_with(new_node, node); if(new_node->key == 0x0){ self->key_count++; - return GIT_SUCCESS; + return GIT_SUCCESS; } } } - if (self->is_resizing) - return GIT_EBUSY; + if (self->is_resizing) + return git__throw(GIT_EBUSY, "Failed to insert node. Hashtable is currently resizing"); resize_to(self, self->size * 2); git_hashtable_insert(self, new_node->key, new_node->value); @@ -130,7 +130,7 @@ static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size return GIT_SUCCESS; } -git_hashtable *git_hashtable_alloc(size_t min_size, +git_hashtable *git_hashtable_alloc(size_t min_size, git_hash_ptr hash, git_hash_keyeq_ptr key_eq) { @@ -248,7 +248,7 @@ int git_hashtable_remove(git_hashtable *self, const void *key) } } - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Entry not found in hash table"); } int git_hashtable_merge(git_hashtable *self, git_hashtable *other) diff --git a/vendor/libgit2/src/hashtable.h b/vendor/libgit2/src/hashtable.h index c3475b6ed..be21be2b1 100644 --- a/vendor/libgit2/src/hashtable.h +++ b/vendor/libgit2/src/hashtable.h @@ -4,6 +4,7 @@ #include "git2/common.h" #include "git2/oid.h" #include "git2/odb.h" +#include "common.h" #define GIT_HASHTABLE_HASHES 3 @@ -31,7 +32,7 @@ struct git_hashtable { typedef struct git_hashtable_node git_hashtable_node; typedef struct git_hashtable git_hashtable; -git_hashtable *git_hashtable_alloc(size_t min_size, +git_hashtable *git_hashtable_alloc(size_t min_size, git_hash_ptr hash, git_hash_keyeq_ptr key_eq); void *git_hashtable_lookup(git_hashtable *h, const void *key); diff --git a/vendor/libgit2/src/index.c b/vendor/libgit2/src/index.c index f643e73fb..bbe9efa49 100644 --- a/vendor/libgit2/src/index.c +++ b/vendor/libgit2/src/index.c @@ -46,6 +46,7 @@ static const unsigned int INDEX_VERSION_NUMBER_EXT = 3; static const unsigned int INDEX_HEADER_SIG = 0x44495243; static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'}; +static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'}; struct index_header { uint32_t signature; @@ -98,29 +99,50 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe static int read_header(struct index_header *dest, const void *buffer); static int read_tree(git_index *index, const char *buffer, size_t buffer_size); -static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *); +static int read_tree_internal(git_index_tree **, const char **, const char *, git_index_tree *); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); static int is_index_extended(git_index *index); -static void sort_index(git_index *index); static int write_index(git_index *index, git_filebuf *file); -int index_srch(const void *key, const void *array_member) +static int index_srch(const void *key, const void *array_member) { - const char *filename = (const char *)key; - const git_index_entry *entry = *(const git_index_entry **)(array_member); + const git_index_entry *entry = array_member; - return strcmp(filename, entry->path); + return strcmp(key, entry->path); } -int index_cmp(const void *a, const void *b) +static int index_cmp(const void *a, const void *b) { - const git_index_entry *entry_a = *(const git_index_entry **)(a); - const git_index_entry *entry_b = *(const git_index_entry **)(b); + const git_index_entry *entry_a = a; + const git_index_entry *entry_b = b; return strcmp(entry_a->path, entry_b->path); } +static int unmerged_srch(const void *key, const void *array_member) +{ + const git_index_entry_unmerged *entry = array_member; + + return strcmp(key, entry->path); +} + +static int unmerged_cmp(const void *a, const void *b) +{ + const git_index_entry_unmerged *info_a = a; + const git_index_entry_unmerged *info_b = b; + + return strcmp(info_a->path, info_b->path); +} + +static unsigned int index_create_mode(unsigned int mode) +{ + if (S_ISLNK(mode)) + return S_IFLNK; + if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR)) + return (S_IFLNK | S_IFDIR); + return S_IFREG | ((mode & 0100) ? 0755 : 0644); +} static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path) { @@ -145,33 +167,37 @@ static int index_initialize(git_index **index_out, git_repository *owner, const git_vector_init(&index->entries, 32, index_cmp); /* Check if index file is stored on disk already */ - if (gitfo_exists(index->index_file_path) == 0) + if (git_futils_exists(index->index_file_path) == 0) index->on_disk = 1; *index_out = index; return git_index_read(index); } -int git_index_open_bare(git_index **index_out, const char *index_path) +int git_index_open(git_index **index_out, const char *index_path) { return index_initialize(index_out, NULL, index_path); } -int git_index_open_inrepo(git_index **index_out, git_repository *repo) +/* + * Moved from `repository.c` + */ +int git_repository_index(git_index **index_out, git_repository *repo) { if (repo->is_bare) - return GIT_EBAREINDEX; + return git__throw(GIT_EBAREINDEX, "Failed to open index. Repository is bare"); return index_initialize(index_out, repo, repo->path_index); } void git_index_free(git_index *index) { - if (index == NULL || index->repository != NULL) + if (index == NULL) return; git_index_clear(index); git_vector_free(&index->entries); + git_vector_free(&index->unmerged); free(index->index_file_path); free(index); @@ -205,7 +231,15 @@ void git_index_clear(git_index *index) free(e); } + for (i = 0; i < index->unmerged.length; ++i) { + git_index_entry_unmerged *e; + e = git_vector_get(&index->unmerged, i); + free(e->path); + free(e); + } + git_vector_clear(&index->entries); + git_vector_clear(&index->unmerged); index->last_modified = 0; free_tree(index->tree); @@ -214,39 +248,36 @@ void git_index_clear(git_index *index) int git_index_read(git_index *index) { - struct stat indexst; - int error = GIT_SUCCESS; + int error = GIT_SUCCESS, updated; + git_fbuffer buffer = GIT_FBUFFER_INIT; + time_t mtime; assert(index->index_file_path); - if (!index->on_disk || gitfo_exists(index->index_file_path) < 0) { + if (!index->on_disk || git_futils_exists(index->index_file_path) < 0) { git_index_clear(index); index->on_disk = 0; return GIT_SUCCESS; } - if (gitfo_stat(index->index_file_path, &indexst) < 0) - return GIT_EOSERR; - - if (!S_ISREG(indexst.st_mode)) - return GIT_ENOTFOUND; - - if (indexst.st_mtime != index->last_modified) { - - gitfo_buf buffer; - - if (gitfo_read_file(&buffer, index->index_file_path) < GIT_SUCCESS) - return GIT_EOSERR; + /* We don't want to update the mtime if we fail to parse the index */ + mtime = index->last_modified; + error = git_futils_readbuffer_updated(&buffer, index->index_file_path, &mtime, &updated); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to read index"); + if (updated) { git_index_clear(index); error = parse_index(index, buffer.data, buffer.len); if (error == GIT_SUCCESS) - index->last_modified = indexst.st_mtime; + index->last_modified = mtime; - gitfo_free_buf(&buffer); + git_futils_freebuffer(&buffer); } + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to parse index"); return error; } @@ -256,20 +287,20 @@ int git_index_write(git_index *index) struct stat indexst; int error; - sort_index(index); + git_vector_sort(&index->entries); if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write index"); if ((error = write_index(index, &file)) < GIT_SUCCESS) { git_filebuf_cleanup(&file); - return error; + return git__rethrow(error, "Failed to write index"); } if ((error = git_filebuf_commit(&file)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write index"); - if (gitfo_stat(index->index_file_path, &indexst) == 0) { + if (p_stat(index->index_file_path, &indexst) == 0) { index->last_modified = indexst.st_mtime; index->on_disk = 1; } @@ -283,39 +314,105 @@ unsigned int git_index_entrycount(git_index *index) return index->entries.length; } -git_index_entry *git_index_get(git_index *index, int n) +unsigned int git_index_entrycount_unmerged(git_index *index) { assert(index); - sort_index(index); - return git_vector_get(&index->entries, (unsigned int)n); + return index->unmerged.length; } -static void sort_index(git_index *index) +git_index_entry *git_index_get(git_index *index, unsigned int n) { git_vector_sort(&index->entries); + return git_vector_get(&index->entries, n); } -static int index_insert(git_index *index, const git_index_entry *source_entry, int replace) +static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage) { git_index_entry *entry; - size_t path_length; - int position; + char full_path[GIT_PATH_MAX]; + struct stat st; + git_oid oid; + int error; - assert(index && source_entry); + if (index->repository == NULL) + return git__throw(GIT_EBAREINDEX, "Failed to initialize entry. Repository is bare"); + + git_path_join(full_path, index->repository->path_workdir, rel_path); + + if (p_lstat(full_path, &st) < 0) + return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened", full_path); - if (source_entry->path == NULL) - return GIT_EMISSINGOBJDATA; + if (stage < 0 || stage > 3) + return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage); + + /* write the blob to disk and get the oid */ + if ((error = git_blob_create_fromfile(&oid, index->repository, rel_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to initialize index entry"); entry = git__malloc(sizeof(git_index_entry)); - if (entry == NULL) + if (!entry) return GIT_ENOMEM; + memset(entry, 0x0, sizeof(git_index_entry)); + + entry->ctime.seconds = (git_time_t)st.st_ctime; + entry->mtime.seconds = (git_time_t)st.st_mtime; + /* entry.mtime.nanoseconds = st.st_mtimensec; */ + /* entry.ctime.nanoseconds = st.st_ctimensec; */ + entry->dev= st.st_rdev; + entry->ino = st.st_ino; + entry->mode = index_create_mode(st.st_mode); + entry->uid = st.st_uid; + entry->gid = st.st_gid; + entry->file_size = st.st_size; + entry->oid = oid; + + entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); + entry->path = git__strdup(rel_path); + if (entry->path == NULL) { + free(entry); + return GIT_ENOMEM; + } + + *entry_out = entry; + return GIT_SUCCESS; +} + +static git_index_entry *index_entry_dup(const git_index_entry *source_entry) +{ + git_index_entry *entry; + + entry = git__malloc(sizeof(git_index_entry)); + if (!entry) + return NULL; memcpy(entry, source_entry, sizeof(git_index_entry)); /* duplicate the path string so we own it */ entry->path = git__strdup(entry->path); + if (!entry->path) + return NULL; + + return entry; +} + +static void index_entry_free(git_index_entry *entry) +{ + if (!entry) + return; + free(entry->path); + free(entry); +} + +static int index_insert(git_index *index, git_index_entry *entry, int replace) +{ + size_t path_length; + int position; + git_index_entry **entry_array; + + assert(index && entry); + if (entry->path == NULL) - return GIT_ENOMEM; + return git__throw(GIT_EMISSINGOBJDATA, "Failed to insert into index. Entry has no path"); /* make sure that the path length flag is correct */ path_length = strlen(entry->path); @@ -327,199 +424,313 @@ static int index_insert(git_index *index, const git_index_entry *source_entry, i else entry->flags |= GIT_IDXENTRY_NAMEMASK;; + /* + * replacing is not requested: just insert entry at the end; + * the index is no longer sorted + */ + if (!replace) { + if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) + return GIT_ENOMEM; + + return GIT_SUCCESS; + } /* look if an entry with this path already exists */ - position = git_index_find(index, source_entry->path); + position = git_index_find(index, entry->path); - /* if no entry exists and replace is not set, - * add the entry at the end; - * the index is no longer sorted */ - if (!replace || position == GIT_ENOTFOUND) { + /* + * if no entry exists add the entry at the end; + * the index is no longer sorted + */ + if (position == GIT_ENOTFOUND) { if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - /* if a previous entry exists and replace is set, - * replace it */ - } else { - git_index_entry **entry_array = (git_index_entry **)index->entries.contents; - - free(entry_array[position]->path); - free(entry_array[position]); - - entry_array[position] = entry; + return GIT_SUCCESS; } + /* exists, replace it */ + entry_array = (git_index_entry **) index->entries.contents; + free(entry_array[position]->path); + free(entry_array[position]); + entry_array[position] = entry; + return GIT_SUCCESS; } -static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage) +static int index_add(git_index *index, const char *path, int stage, int replace) { - char full_path[GIT_PATH_MAX]; - struct stat st; - int error; - - if (index->repository == NULL) - return GIT_EBAREINDEX; - - git__joinpath(full_path, index->repository->path_workdir, rel_path); - - if (gitfo_exists(full_path) < 0) - return GIT_ENOTFOUND; + git_index_entry *entry = NULL; + int ret; - if (gitfo_stat(full_path, &st) < 0) - return GIT_EOSERR; + ret = index_entry_init(&entry, index, path, stage); + if (ret) + goto err; - if (stage < 0 || stage > 3) - return GIT_ERROR; - - memset(entry, 0x0, sizeof(git_index_entry)); + ret = index_insert(index, entry, replace); + if (ret) + goto err; - entry->ctime.seconds = (git_time_t)st.st_ctime; - entry->mtime.seconds = (git_time_t)st.st_mtime; - /* entry.mtime.nanoseconds = st.st_mtimensec; */ - /* entry.ctime.nanoseconds = st.st_ctimensec; */ - entry->dev= st.st_rdev; - entry->ino = st.st_ino; - entry->mode = st.st_mode; - entry->uid = st.st_uid; - entry->gid = st.st_gid; - entry->file_size = st.st_size; - - /* write the blob to disk and get the oid */ - if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS) - return error; - - entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); - entry->path = (char *)rel_path; /* do not duplicate; index_insert already does this */ - return GIT_SUCCESS; + return ret; +err: + index_entry_free(entry); + return git__rethrow(ret, "Failed to append to index"); } int git_index_add(git_index *index, const char *path, int stage) { - int error; - git_index_entry entry; - - if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) - return error; - - return index_insert(index, &entry, 1); + return index_add(index, path, stage, 1); } int git_index_append(git_index *index, const char *path, int stage) { - int error; - git_index_entry entry; + return index_add(index, path, stage, 0); +} + +static int index_add2(git_index *index, const git_index_entry *source_entry, + int replace) +{ + git_index_entry *entry = NULL; + int ret; + + entry = index_entry_dup(source_entry); + if (entry == NULL) { + ret = GIT_ENOMEM; + goto err; + } - if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) - return error; + ret = index_insert(index, entry, replace); + if (ret) + goto err; - return index_insert(index, &entry, 0); + return ret; +err: + index_entry_free(entry); + return git__rethrow(ret, "Failed to append to index"); } int git_index_add2(git_index *index, const git_index_entry *source_entry) { - return index_insert(index, source_entry, 1); + return index_add2(index, source_entry, 1); } int git_index_append2(git_index *index, const git_index_entry *source_entry) { - return index_insert(index, source_entry, 0); + return index_add2(index, source_entry, 1); } - int git_index_remove(git_index *index, int position) { - assert(index); - sort_index(index); + git_vector_sort(&index->entries); return git_vector_remove(&index->entries, (unsigned int)position); } int git_index_find(git_index *index, const char *path) { - sort_index(index); return git_vector_bsearch2(&index->entries, index_srch, path); } -static git_index_tree *read_tree_internal( +void git_index_uniq(git_index *index) +{ + git_vector_uniq(&index->entries); +} + +const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index, const char *path) +{ + int pos; + assert(index && path); + + if (!index->unmerged.length) + return NULL; + + if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS) + return NULL; + + return git_vector_get(&index->unmerged, pos); +} + +const git_index_entry_unmerged *git_index_get_unmerged_byindex(git_index *index, unsigned int n) +{ + assert(index); + return git_vector_get(&index->unmerged, n); +} + + +static int read_tree_internal(git_index_tree **out, const char **buffer_in, const char *buffer_end, git_index_tree *parent) { git_index_tree *tree; const char *name_start, *buffer; long count; + int error = GIT_SUCCESS; if ((tree = git__malloc(sizeof(git_index_tree))) == NULL) - return NULL; + return GIT_ENOMEM; memset(tree, 0x0, sizeof(git_index_tree)); tree->parent = parent; buffer = name_start = *buffer_in; - if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) - goto error_cleanup; + if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } /* NUL-terminated tree name */ tree->name = git__strdup(name_start); - if (++buffer >= buffer_end) - goto error_cleanup; + if (tree->name == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + if (++buffer >= buffer_end) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } /* Blank-terminated ASCII decimal number of entries in this tree */ - if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || - count < 0) - goto error_cleanup; + if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || count < -1) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } + + /* Invalidated TREE. Free the tree but report success */ + if (count == -1) { + /* FIXME: return buffer_end or the end position for + * this single tree entry */ + *buffer_in = buffer_end; + *out = NULL; + free_tree(tree); /* Needs to be done manually */ + return GIT_SUCCESS; + } - tree->entries = (size_t)count; + tree->entries = count; - if (*buffer != ' ' || ++buffer >= buffer_end) - goto error_cleanup; + if (*buffer != ' ' || ++buffer >= buffer_end) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } /* Number of children of the tree, newline-terminated */ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || - count < 0) - goto error_cleanup; + count < 0) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } - tree->children_count = (size_t)count; + tree->children_count = count; - if (*buffer != '\n' || ++buffer >= buffer_end) - goto error_cleanup; + if (*buffer != '\n' || ++buffer >= buffer_end) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } /* 160-bit SHA-1 for this tree and it's children */ - if (buffer + GIT_OID_RAWSZ > buffer_end) - goto error_cleanup; + if (buffer + GIT_OID_RAWSZ > buffer_end) { + error = GIT_EOBJCORRUPTED; + goto cleanup; + } - git_oid_mkraw(&tree->oid, (const unsigned char *)buffer); + git_oid_fromraw(&tree->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; /* Parse children: */ if (tree->children_count > 0) { unsigned int i; + int err; tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *)); if (tree->children == NULL) - goto error_cleanup; + goto cleanup; for (i = 0; i < tree->children_count; ++i) { - tree->children[i] = read_tree_internal(&buffer, buffer_end, tree); + err = read_tree_internal(&tree->children[i], &buffer, buffer_end, tree); - if (tree->children[i] == NULL) - goto error_cleanup; + if (err < GIT_SUCCESS) + goto cleanup; } } *buffer_in = buffer; - return tree; + *out = tree; + return GIT_SUCCESS; -error_cleanup: + cleanup: free_tree(tree); - return NULL; + return error; } static int read_tree(git_index *index, const char *buffer, size_t buffer_size) { const char *buffer_end = buffer + buffer_size; + int error; + + error = read_tree_internal(&index->tree, &buffer, buffer_end, NULL); - index->tree = read_tree_internal(&buffer, buffer_end, NULL); - return (index->tree != NULL && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED; + if (buffer < buffer_end) + return GIT_EOBJCORRUPTED; + + return error; +} + +static int read_unmerged(git_index *index, const char *buffer, size_t size) +{ + const char *endptr; + size_t len; + int i; + + git_vector_init(&index->unmerged, 16, unmerged_cmp); + + while (size) { + git_index_entry_unmerged *lost; + + len = strlen(buffer) + 1; + if (size <= len) + return git__throw(GIT_ERROR, "Failed to read unmerged entries"); + + if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL) + return GIT_ENOMEM; + + if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS) + return git__throw(GIT_ERROR, "Failed to read unmerged entries"); + + lost->path = git__strdup(buffer); + if (!lost->path) + return GIT_ENOMEM; + + size -= len; + buffer += len; + + for (i = 0; i < 3; i++) { + long tmp; + + if (git__strtol32(&tmp, buffer, &endptr, 8) < GIT_SUCCESS || + !endptr || endptr == buffer || *endptr || (unsigned)tmp > UINT_MAX) + return GIT_ERROR; + + lost->mode[i] = tmp; + + len = (endptr + 1) - buffer; + if (size <= len) + return git__throw(GIT_ERROR, "Failed to read unmerged entries"); + + size -= len; + buffer += len; + } + + for (i = 0; i < 3; i++) { + if (!lost->mode[i]) + continue; + if (size < 20) + return git__throw(GIT_ERROR, "Failed to read unmerged entries"); + git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer); + size -= 20; + buffer += 20; + } + } + + return GIT_SUCCESS; } static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) @@ -527,15 +738,13 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe size_t path_length, entry_size; uint16_t flags_raw; const char *path_ptr; - const struct entry_short *source; + const struct entry_short *source = buffer; if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) return 0; memset(dest, 0x0, sizeof(git_index_entry)); - source = (const struct entry_short *)(buffer); - dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds); dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); @@ -550,7 +759,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe dest->flags = ntohs(source->flags); if (dest->flags & GIT_IDXENTRY_EXTENDED) { - struct entry_long *source_l = (struct entry_long *)source; + const struct entry_long *source_l = (const struct entry_long *)source; path_ptr = source_l->path; flags_raw = ntohs(source_l->flags_extended); @@ -567,7 +776,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe path_end = memchr(path_ptr, '\0', buffer_size); if (path_end == NULL) - return 0; + return 0; path_length = path_end - path_ptr; } @@ -588,8 +797,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe static int read_header(struct index_header *dest, const void *buffer) { - const struct index_header *source; - source = (const struct index_header *)(buffer); + const struct index_header *source = buffer; dest->signature = ntohl(source->signature); if (dest->signature != INDEX_HEADER_SIG) @@ -624,10 +832,14 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') { /* tree cache */ if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) { - if (read_tree(index, buffer + 8, dest.extension_size) < GIT_SUCCESS) return 0; + } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { + if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS) + return 0; } + /* else, unsupported extension. We cannot parse this, but we can skip + * it by returning `total_size */ } else { /* we cannot handle non-ignorable extensions; * in fact they aren't even defined in the standard */ @@ -645,21 +857,21 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) #define seek_forward(_increase) { \ if (_increase >= buffer_size) \ - return GIT_EOBJCORRUPTED; \ + return git__throw(GIT_EOBJCORRUPTED, "Failed to seek forward. Buffer size exceeded"); \ buffer += _increase; \ buffer_size -= _increase;\ } if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer too small"); /* Precalculate the SHA1 of the files's contents -- we'll match it to * the provided SHA1 in the footer */ - git_hash_buf(&checksum_calculated, (const void *)buffer, buffer_size - INDEX_FOOTER_SIZE); + git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); /* Parse header */ if (read_header(&header, buffer) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header is corrupted"); seek_forward(INDEX_HEADER_SIZE); @@ -678,7 +890,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) /* 0 bytes read means an object corruption */ if (entry_size == 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Entry size is zero"); if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; @@ -687,7 +899,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) } if (i != header.entry_count) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header entries changed while parsing"); /* There's still space for some extensions! */ while (buffer_size > INDEX_FOOTER_SIZE) { @@ -697,19 +909,19 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) /* see if we have read any bytes from the extension */ if (extension_size == 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Extension size is zero"); seek_forward(extension_size); } if (buffer_size != INDEX_FOOTER_SIZE) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer size does not match index footer size"); /* 160-bit SHA-1 over the content of the index file before this checksum. */ - git_oid_mkraw(&checksum_expected, (const unsigned char *)buffer); + git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Calculated checksum does not match expected checksum"); #undef seek_forward @@ -755,8 +967,18 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) memset(ondisk, 0x0, disk_size); - ondisk->ctime.seconds = htonl((unsigned long)entry->ctime.seconds); - ondisk->mtime.seconds = htonl((unsigned long)entry->mtime.seconds); + /** + * Yes, we have to truncate. + * + * The on-disk format for Index entries clearly defines + * the time and size fields to be 4 bytes each -- so even if + * we store these values with 8 bytes on-memory, they must + * be truncated to 4 bytes before writing to disk. + * + * In 2038 I will be either too dead or too rich to care about this + */ + ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds); + ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds); ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds); ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds); ondisk->dev = htonl(entry->dev); @@ -764,7 +986,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry) ondisk->mode = htonl(entry->mode); ondisk->uid = htonl(entry->uid); ondisk->gid = htonl(entry->gid); - ondisk->file_size = htonl((unsigned long)entry->file_size); + ondisk->file_size = htonl((uint32_t)entry->file_size); git_oid_cpy(&ondisk->oid, &entry->oid); @@ -819,7 +1041,7 @@ static int write_index(git_index *index, git_filebuf *file) error = write_entries(index, file); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write index"); /* TODO: write extensions (tree cache) */ @@ -829,5 +1051,10 @@ static int write_index(git_index *index, git_filebuf *file) /* write it at the end of the file */ git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write index"); +} + +int git_index_entry_stage(const git_index_entry *entry) +{ + return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT; } diff --git a/vendor/libgit2/src/index.h b/vendor/libgit2/src/index.h index e1e752f7c..f2402fd71 100644 --- a/vendor/libgit2/src/index.h +++ b/vendor/libgit2/src/index.h @@ -29,6 +29,8 @@ struct git_index { unsigned int on_disk:1; git_index_tree *tree; + + git_vector unmerged; }; #endif diff --git a/vendor/libgit2/src/indexer.c b/vendor/libgit2/src/indexer.c new file mode 100644 index 000000000..3934250e2 --- /dev/null +++ b/vendor/libgit2/src/indexer.c @@ -0,0 +1,415 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "git2/indexer.h" +#include "git2/object.h" +#include "git2/zlib.h" +#include "git2/oid.h" + +#include "common.h" +#include "pack.h" +#include "mwindow.h" +#include "posix.h" +#include "pack.h" +#include "filebuf.h" +#include "sha1.h" + +#define UINT31_MAX (0x7FFFFFFF) + +struct entry { + git_oid oid; + uint32_t crc; + uint32_t offset; + uint64_t offset_long; +}; + +struct git_indexer { + struct git_pack_file *pack; + struct stat st; + struct git_pack_header hdr; + size_t nr_objects; + git_vector objects; + git_filebuf file; + unsigned int fanout[256]; + git_oid hash; +}; + +const git_oid *git_indexer_hash(git_indexer *idx) +{ + return &idx->hash; +} + +static int parse_header(git_indexer *idx) +{ + int error; + + /* Verify we recognize this pack file format. */ + if ((error = p_read(idx->pack->mwf.fd, &idx->hdr, sizeof(idx->hdr))) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read in pack header"); + + if (idx->hdr.hdr_signature != ntohl(PACK_SIGNATURE)) + return git__throw(GIT_EOBJCORRUPTED, "Wrong pack signature"); + + if (!pack_version_ok(idx->hdr.hdr_version)) + return git__throw(GIT_EOBJCORRUPTED, "Wrong pack version"); + + + return GIT_SUCCESS; +} + +static int objects_cmp(const void *a, const void *b) +{ + const struct entry *entrya = a; + const struct entry *entryb = b; + + return git_oid_cmp(&entrya->oid, &entryb->oid); +} + +static int cache_cmp(const void *a, const void *b) +{ + const struct git_pack_entry *ea = a; + const struct git_pack_entry *eb = b; + + return git_oid_cmp(&ea->sha1, &eb->sha1); +} + + +int git_indexer_new(git_indexer **out, const char *packname) +{ + git_indexer *idx; + unsigned int namelen; + int ret, error; + + assert(out && packname); + + if (git_path_root(packname) < 0) + return git__throw(GIT_EINVALIDPATH, "Path is not absolute"); + + idx = git__malloc(sizeof(git_indexer)); + if (idx == NULL) + return GIT_ENOMEM; + + memset(idx, 0x0, sizeof(*idx)); + + namelen = strlen(packname); + idx->pack = git__malloc(sizeof(struct git_pack_file) + namelen + 1); + if (idx->pack == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + memset(idx->pack, 0x0, sizeof(struct git_pack_file)); + memcpy(idx->pack->pack_name, packname, namelen + 1); + + ret = p_stat(packname, &idx->st); + if (ret < 0) { + if (errno == ENOENT) + error = git__throw(GIT_ENOTFOUND, "Failed to stat packfile. File not found"); + else + error = git__throw(GIT_EOSERR, "Failed to stat packfile."); + + goto cleanup; + } + + ret = p_open(idx->pack->pack_name, O_RDONLY); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to open packfile"); + goto cleanup; + } + + idx->pack->mwf.fd = ret; + idx->pack->mwf.size = (git_off_t)idx->st.st_size; + + error = parse_header(idx); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to parse packfile header"); + goto cleanup; + } + + idx->nr_objects = ntohl(idx->hdr.hdr_entries); + + error = git_vector_init(&idx->pack->cache, idx->nr_objects, cache_cmp); + if (error < GIT_SUCCESS) + goto cleanup; + + idx->pack->has_cache = 1; + error = git_vector_init(&idx->objects, idx->nr_objects, objects_cmp); + if (error < GIT_SUCCESS) + goto cleanup; + + *out = idx; + + return GIT_SUCCESS; + +cleanup: + git_indexer_free(idx); + + return error; +} + +static void index_path(char *path, git_indexer *idx) +{ + char *ptr; + const char prefix[] = "pack-", suffix[] = ".idx\0"; + + ptr = strrchr(path, '/') + 1; + + memcpy(ptr, prefix, strlen(prefix)); + ptr += strlen(prefix); + git_oid_fmt(ptr, &idx->hash); + ptr += GIT_OID_HEXSZ; + memcpy(ptr, suffix, strlen(suffix)); +} + +int git_indexer_write(git_indexer *idx) +{ + git_mwindow *w = NULL; + int error, namelen; + unsigned int i, long_offsets = 0, left; + struct git_pack_idx_header hdr; + char filename[GIT_PATH_MAX]; + struct entry *entry; + void *packfile_hash; + git_oid file_hash; + SHA_CTX ctx; + + git_vector_sort(&idx->objects); + + namelen = strlen(idx->pack->pack_name); + memcpy(filename, idx->pack->pack_name, namelen); + memcpy(filename + namelen - strlen("pack"), "idx", strlen("idx") + 1); + + error = git_filebuf_open(&idx->file, filename, GIT_FILEBUF_HASH_CONTENTS); + + /* Write out the header */ + hdr.idx_signature = htonl(PACK_IDX_SIGNATURE); + hdr.idx_version = htonl(2); + error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr)); + + /* Write out the fanout table */ + for (i = 0; i < 256; ++i) { + uint32_t n = htonl(idx->fanout[i]); + error = git_filebuf_write(&idx->file, &n, sizeof(n)); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* Write out the object names (SHA-1 hashes) */ + SHA1_Init(&ctx); + git_vector_foreach(&idx->objects, i, entry) { + error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid)); + SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ); + if (error < GIT_SUCCESS) + goto cleanup; + } + SHA1_Final(idx->hash.id, &ctx); + + /* Write out the CRC32 values */ + git_vector_foreach(&idx->objects, i, entry) { + error = git_filebuf_write(&idx->file, &entry->crc, sizeof(uint32_t)); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* Write out the offsets */ + git_vector_foreach(&idx->objects, i, entry) { + uint32_t n; + + if (entry->offset == UINT32_MAX) + n = htonl(0x80000000 | long_offsets++); + else + n = htonl(entry->offset); + + error = git_filebuf_write(&idx->file, &n, sizeof(uint32_t)); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* Write out the long offsets */ + git_vector_foreach(&idx->objects, i, entry) { + uint32_t split[2]; + + if (entry->offset != UINT32_MAX) + continue; + + split[0] = htonl(entry->offset_long >> 32); + split[1] = htonl(entry->offset_long & 0xffffffff); + + error = git_filebuf_write(&idx->file, &split, sizeof(uint32_t) * 2); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* Write out the packfile trailer */ + + packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->st.st_size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left); + git_mwindow_close(&w); + if (packfile_hash == NULL) { + error = git__rethrow(GIT_ENOMEM, "Failed to open window to packfile hash"); + goto cleanup; + } + + memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ); + + git_mwindow_close(&w); + + error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid)); + + /* Write out the index sha */ + error = git_filebuf_hash(&file_hash, &idx->file); + if (error < GIT_SUCCESS) + goto cleanup; + + error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid)); + if (error < GIT_SUCCESS) + goto cleanup; + + /* Figure out what the final name should be */ + index_path(filename, idx); + /* Commit file */ + error = git_filebuf_commit_at(&idx->file, filename); + +cleanup: + git_mwindow_free_all(&idx->pack->mwf); + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&idx->file); + + return error; +} + +int git_indexer_run(git_indexer *idx, git_indexer_stats *stats) +{ + git_mwindow_file *mwf; + off_t off = sizeof(struct git_pack_header); + int error; + struct entry *entry; + unsigned int left, processed; + + assert(idx && stats); + + mwf = &idx->pack->mwf; + error = git_mwindow_file_register(mwf); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to register mwindow file"); + + stats->total = idx->nr_objects; + stats->processed = processed = 0; + + while (processed < idx->nr_objects) { + git_rawobj obj; + git_oid oid; + struct git_pack_entry *pentry; + git_mwindow *w = NULL; + int i; + off_t entry_start = off; + void *packed; + size_t entry_size; + + entry = git__malloc(sizeof(struct entry)); + memset(entry, 0x0, sizeof(struct entry)); + + if (off > UINT31_MAX) { + entry->offset = UINT32_MAX; + entry->offset_long = off; + } else { + entry->offset = off; + } + + error = git_packfile_unpack(&obj, idx->pack, &off); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to unpack object"); + goto cleanup; + } + + /* FIXME: Parse the object instead of hashing it */ + error = git_odb__hash_obj(&oid, &obj); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to hash object"); + goto cleanup; + } + + pentry = git__malloc(sizeof(struct git_pack_entry)); + if (pentry == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + git_oid_cpy(&pentry->sha1, &oid); + pentry->offset = entry_start; + error = git_vector_insert(&idx->pack->cache, pentry); + if (error < GIT_SUCCESS) + goto cleanup; + + git_oid_cpy(&entry->oid, &oid); + entry->crc = crc32(0L, Z_NULL, 0); + + entry_size = off - entry_start; + packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left); + if (packed == NULL) { + error = git__rethrow(error, "Failed to open window to read packed data"); + goto cleanup; + } + entry->crc = htonl(crc32(entry->crc, packed, entry_size)); + git_mwindow_close(&w); + + /* Add the object to the list */ + error = git_vector_insert(&idx->objects, entry); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to add entry to list"); + goto cleanup; + } + + for (i = oid.id[0]; i < 256; ++i) { + idx->fanout[i]++; + } + + free(obj.data); + + stats->processed = ++processed; + } + +cleanup: + git_mwindow_free_all(mwf); + + return error; + +} + +void git_indexer_free(git_indexer *idx) +{ + unsigned int i; + struct entry *e; + struct git_pack_entry *pe; + + p_close(idx->pack->mwf.fd); + git_vector_foreach(&idx->objects, i, e) + free(e); + git_vector_free(&idx->objects); + git_vector_foreach(&idx->pack->cache, i, pe) + free(pe); + git_vector_free(&idx->pack->cache); + free(idx->pack); + free(idx); +} + diff --git a/vendor/libgit2/src/map.h b/vendor/libgit2/src/map.h index be569abc8..1dfbeeb4b 100644 --- a/vendor/libgit2/src/map.h +++ b/vendor/libgit2/src/map.h @@ -4,7 +4,7 @@ #include "common.h" -/* git__mmap() prot values */ +/* p_mmap() prot values */ #define GIT_PROT_NONE 0x0 #define GIT_PROT_READ 0x1 #define GIT_PROT_WRITE 0x2 @@ -25,7 +25,7 @@ typedef struct { /* memory mapped buffer */ #endif } git_map; -extern int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset); -extern int git__munmap(git_map *map); +extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset); +extern int p_munmap(git_map *map); #endif /* INCLUDE_map_h__ */ diff --git a/vendor/libgit2/src/mwindow.c b/vendor/libgit2/src/mwindow.c new file mode 100644 index 000000000..585d75c12 --- /dev/null +++ b/vendor/libgit2/src/mwindow.c @@ -0,0 +1,275 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "mwindow.h" +#include "vector.h" +#include "fileops.h" +#include "map.h" + +#define DEFAULT_WINDOW_SIZE \ + (sizeof(void*) >= 8 \ + ? 1 * 1024 * 1024 * 1024 \ + : 32 * 1024 * 1024) + +#define DEFAULT_MAPPED_LIMIT \ + ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) + +/* + * We need this because each process is only allowed a specific amount + * of memory. Making it writable should generate one instance per + * process, but we still need to set a couple of variables. + */ + +static git_mwindow_ctl ctl = { + 0, + 0, + DEFAULT_WINDOW_SIZE, + DEFAULT_MAPPED_LIMIT, + 0, + 0, + 0, + 0, + {0, 0, 0, 0, 0} +}; + +/* + * Free all the windows in a sequence, typically because we're done + * with the file + */ +void git_mwindow_free_all(git_mwindow_file *mwf) +{ + unsigned int i; + /* + * Remove these windows from the global list + */ + for (i = 0; i < ctl.windowfiles.length; ++i){ + if (git_vector_get(&ctl.windowfiles, i) == mwf) { + git_vector_remove(&ctl.windowfiles, i); + break; + } + } + + if (ctl.windowfiles.length == 0) { + git_vector_free(&ctl.windowfiles); + ctl.windowfiles.contents = NULL; + } + + while (mwf->windows) { + git_mwindow *w = mwf->windows; + assert(w->inuse_cnt == 0); + + ctl.mapped -= w->window_map.len; + ctl.open_windows--; + + git_futils_mmap_free(&w->window_map); + + mwf->windows = w->next; + free(w); + } +} + +/* + * Check if a window 'win' contains the address 'offset' + */ +int git_mwindow_contains(git_mwindow *win, git_off_t offset) +{ + git_off_t win_off = win->offset; + return win_off <= offset + && offset <= (git_off_t)(win_off + win->window_map.len); +} + +/* + * Find the least-recently-used window in a file + */ +void git_mwindow_scan_lru( + git_mwindow_file *mwf, + git_mwindow **lru_w, + git_mwindow **lru_l) +{ + git_mwindow *w, *w_l; + + for (w_l = NULL, w = mwf->windows; w; w = w->next) { + if (!w->inuse_cnt) { + /* + * If the current one is more recent than the last one, + * store it in the output parameter. If lru_w is NULL, + * it's the first loop, so store it as well. + */ + if (!*lru_w || w->last_used < (*lru_w)->last_used) { + *lru_w = w; + *lru_l = w_l; + } + } + w_l = w; + } +} + +/* + * Close the least recently used window. You should check to see if + * the file descriptors need closing from time to time. + */ +int git_mwindow_close_lru(git_mwindow_file *mwf) +{ + unsigned int i; + git_mwindow *lru_w = NULL, *lru_l = NULL; + + /* FIMXE: Does this give us any advantage? */ + if(mwf->windows) + git_mwindow_scan_lru(mwf, &lru_w, &lru_l); + + for (i = 0; i < ctl.windowfiles.length; ++i) { + git_mwindow_scan_lru(git_vector_get(&ctl.windowfiles, i), &lru_w, &lru_l); + } + + if (lru_w) { + git_mwindow_close(&lru_w); + ctl.mapped -= lru_w->window_map.len; + git_futils_mmap_free(&lru_w->window_map); + + if (lru_l) + lru_l->next = lru_w->next; + else + mwf->windows = lru_w->next; + + free(lru_w); + ctl.open_windows--; + + return GIT_SUCCESS; + } + + return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU"); +} + +static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, git_off_t size, git_off_t offset) +{ + size_t walign = ctl.window_size / 2; + git_off_t len; + git_mwindow *w; + + w = git__malloc(sizeof(*w)); + if (w == NULL) + return w; + + memset(w, 0x0, sizeof(*w)); + w->offset = (offset / walign) * walign; + + len = size - w->offset; + if (len > (git_off_t)ctl.window_size) + len = (git_off_t)ctl.window_size; + + ctl.mapped += (size_t)len; + + while(ctl.mapped_limit < ctl.mapped && + git_mwindow_close_lru(mwf) == GIT_SUCCESS) {} + + /* FIXME: Shouldn't we error out if there's an error in closing lru? */ + + if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS) + goto cleanup; + + ctl.mmap_calls++; + ctl.open_windows++; + + if (ctl.mapped > ctl.peak_mapped) + ctl.peak_mapped = ctl.mapped; + + if (ctl.open_windows > ctl.peak_open_windows) + ctl.peak_open_windows = ctl.open_windows; + + return w; + +cleanup: + free(w); + return NULL; +} + +/* + * Open a new window, closing the least recenty used until we have + * enough space. Don't forget to add it to your list + */ +unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, + git_off_t offset, int extra, unsigned int *left) +{ + git_mwindow *w = *cursor; + + if (!w || !git_mwindow_contains(w, offset + extra)) { + if (w) { + w->inuse_cnt--; + } + + for (w = mwf->windows; w; w = w->next) { + if (git_mwindow_contains(w, offset + extra)) + break; + } + + /* + * If there isn't a suitable window, we need to create a new + * one. + */ + if (!w) { + w = new_window(mwf, mwf->fd, mwf->size, offset); + if (w == NULL) + return NULL; + w->next = mwf->windows; + mwf->windows = w; + } + } + + /* If we changed w, store it in the cursor */ + if (w != *cursor) { + w->last_used = ctl.used_ctr++; + w->inuse_cnt++; + *cursor = w; + } + + offset -= w->offset; + assert(git__is_sizet(offset)); + + if (left) + *left = (unsigned int)(w->window_map.len - offset); + + return (unsigned char *) w->window_map.data + offset; +} + +int git_mwindow_file_register(git_mwindow_file *mwf) +{ + int error; + + if (ctl.windowfiles.length == 0 && + (error = git_vector_init(&ctl.windowfiles, 8, NULL)) < GIT_SUCCESS) + return error; + + return git_vector_insert(&ctl.windowfiles, mwf); +} + +void git_mwindow_close(git_mwindow **window) +{ + git_mwindow *w = *window; + if (w) { + w->inuse_cnt--; + *window = NULL; + } +} diff --git a/vendor/libgit2/src/mwindow.h b/vendor/libgit2/src/mwindow.h new file mode 100644 index 000000000..1d4a58453 --- /dev/null +++ b/vendor/libgit2/src/mwindow.h @@ -0,0 +1,66 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDE_mwindow__ +#define INCLUDE_mwindow__ + +#include "map.h" +#include "vector.h" +#include "fileops.h" + +typedef struct git_mwindow { + struct git_mwindow *next; + git_map window_map; + git_off_t offset; + unsigned int last_used; + unsigned int inuse_cnt; +} git_mwindow; + +typedef struct git_mwindow_file { + git_mwindow *windows; + int fd; + git_off_t size; +} git_mwindow_file; + +typedef struct git_mwindow_ctl { + size_t mapped; + unsigned int open_windows; + size_t window_size; /* needs default value */ + size_t mapped_limit; /* needs default value */ + unsigned int mmap_calls; + unsigned int peak_open_windows; + size_t peak_mapped; + size_t used_ctr; + git_vector windowfiles; +} git_mwindow_ctl; + +int git_mwindow_contains(git_mwindow *win, git_off_t offset); +void git_mwindow_free_all(git_mwindow_file *mwf); +unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, int extra, unsigned int *left); +void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l); +int git_mwindow_file_register(git_mwindow_file *mwf); +void git_mwindow_close(git_mwindow **w_cursor); + +#endif diff --git a/vendor/libgit2/src/netops.c b/vendor/libgit2/src/netops.c new file mode 100644 index 000000000..b5251925e --- /dev/null +++ b/vendor/libgit2/src/netops.c @@ -0,0 +1,163 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _WIN32 +# include +# include +# include +# include +#else +# define _WIN32_WINNT 0x0501 +# include +# include +# pragma comment(lib, "Ws2_32.lib") +#endif + +#include "git2/errors.h" + +#include "common.h" +#include "netops.h" + +void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd) +{ + memset(buf, 0x0, sizeof(gitno_buffer)); + memset(data, 0x0, len); + buf->data = data; + buf->len = len - 1; + buf->offset = 0; + buf->fd = fd; +} + +int gitno_recv(gitno_buffer *buf) +{ + int ret; + + ret = recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0); + if (ret < 0) + return git__throw(GIT_EOSERR, "Failed to receive data: %s", strerror(errno)); + if (ret == 0) /* Orderly shutdown, so exit */ + return GIT_SUCCESS; + + buf->offset += ret; + + return ret; +} + +/* Consume up to ptr and move the rest of the buffer to the beginning */ +void gitno_consume(gitno_buffer *buf, const char *ptr) +{ + size_t consumed; + + assert(ptr - buf->data >= 0); + assert(ptr - buf->data <= (int) buf->len); + + consumed = ptr - buf->data; + + memmove(buf->data, ptr, buf->offset - consumed); + memset(buf->data + buf->offset, 0x0, buf->len - buf->offset); + buf->offset -= consumed; +} + +/* Consume const bytes and move the rest of the buffer to the beginning */ +void gitno_consume_n(gitno_buffer *buf, size_t cons) +{ + memmove(buf->data, buf->data + cons, buf->len - buf->offset); + memset(buf->data + cons, 0x0, buf->len - buf->offset); + buf->offset -= cons; +} + +int gitno_connect(const char *host, const char *port) +{ + struct addrinfo *info, *p; + struct addrinfo hints; + int ret, error = GIT_SUCCESS; + int s; + + memset(&hints, 0x0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + ret = getaddrinfo(host, port, &hints, &info); + if (ret != 0) { + error = GIT_EOSERR; + goto cleanup; + } + + for (p = info; p != NULL; p = p->ai_next) { + s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (s < 0) { + error = GIT_EOSERR; + goto cleanup; + } + + ret = connect(s, p->ai_addr, p->ai_addrlen); + /* If we can't connect, try the next one */ + if (ret < 0) { + continue; + } + + /* Return the socket */ + error = s; + goto cleanup; + } + + /* Oops, we couldn't connect to any address */ + error = GIT_EOSERR; + +cleanup: + freeaddrinfo(info); + return error; +} + +int gitno_send(int s, const char *msg, size_t len, int flags) +{ + int ret; + size_t off = 0; + + while (off < len) { + ret = send(s, msg + off, len - off, flags); + if (ret < 0) + return GIT_EOSERR; + + off += ret; + } + + return off; +} + +int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) +{ + fd_set fds; + struct timeval tv; + + tv.tv_sec = sec; + tv.tv_usec = usec; + + FD_ZERO(&fds); + FD_SET(buf->fd, &fds); + + /* The select(2) interface is silly */ + return select(buf->fd + 1, &fds, NULL, NULL, &tv); +} diff --git a/vendor/libgit2/src/netops.h b/vendor/libgit2/src/netops.h new file mode 100644 index 000000000..c259ed2d6 --- /dev/null +++ b/vendor/libgit2/src/netops.h @@ -0,0 +1,29 @@ +/* + * netops.h - convencience functions for networking + */ +#ifndef INCLUDE_netops_h__ +#define INCLUDE_netops_h__ + +#ifndef GIT_WIN32 +typedef int GIT_SOCKET; +#else +typedef unsigned int GIT_SOCKET; +#endif + +typedef struct gitno_buffer { + char *data; + size_t len; + size_t offset; + GIT_SOCKET fd; +} gitno_buffer; + +void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd); +int gitno_recv(gitno_buffer *buf); +void gitno_consume(gitno_buffer *buf, const char *ptr); +void gitno_consume_n(gitno_buffer *buf, size_t cons); + +int gitno_connect(const char *host, const char *port); +int gitno_send(int s, const char *msg, size_t len, int flags); +int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); + +#endif diff --git a/vendor/libgit2/src/object.c b/vendor/libgit2/src/object.c index 0572663eb..8b07197f1 100644 --- a/vendor/libgit2/src/object.c +++ b/vendor/libgit2/src/object.c @@ -86,7 +86,7 @@ static int create_object(git_object **object_out, git_otype type) break; default: - return GIT_EINVALIDTYPE; + return git__throw(GIT_EINVALIDTYPE, "The given type is invalid"); } object->type = type; @@ -95,7 +95,7 @@ static int create_object(git_object **object_out, git_otype type) return GIT_SUCCESS; } -int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) +int git_object_lookup_prefix(git_object **object_out, git_repository *repo, const git_oid *id, unsigned int len, git_otype type) { git_object *object = NULL; git_odb_object *odb_obj; @@ -103,31 +103,72 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o assert(repo && object_out && id); - object = git_cache_get(&repo->objects, id); - if (object != NULL) { - if (type != GIT_OBJ_ANY && type != object->type) - return GIT_EINVALIDTYPE; - - *object_out = object; - return GIT_SUCCESS; + if (len < GIT_OID_MINPREFIXLEN) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, + "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + /* We want to match the full id : we can first look up in the cache, + * since there is no need to check for non ambiguousity + */ + object = git_cache_get(&repo->objects, id); + if (object != NULL) { + if (type != GIT_OBJ_ANY && type != object->type) + { + git_object_close(object); + return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB"); + } + + *object_out = object; + return GIT_SUCCESS; + } + + /* Object was not found in the cache, let's explore the backends. + * We could just use git_odb_read_unique_short_oid, + * it is the same cost for packed and loose object backends, + * but it may be much more costly for sqlite and hiredis. + */ + error = git_odb_read(&odb_obj, repo->db, id); + } else { + git_oid short_oid; + + /* We copy the first len*4 bits from id and fill the remaining with 0s */ + memcpy(short_oid.id, id->id, (len + 1) / 2); + if (len % 2) + short_oid.id[len / 2] &= 0xF0; + memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2); + + /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have + * 2 options : + * - We always search in the cache first. If we find that short oid is + * ambiguous, we can stop. But in all the other cases, we must then + * explore all the backends (to find an object if there was match, + * or to check that oid is not ambiguous if we have found 1 match in + * the cache) + * - We never explore the cache, go right to exploring the backends + * We chose the latter : we explore directly the backends. + */ + error = git_odb_read_prefix(&odb_obj, repo->db, &short_oid, len); } - error = git_odb_read(&odb_obj, repo->db, id); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to lookup object"); if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) { git_odb_object_close(odb_obj); - return GIT_EINVALIDTYPE; + return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB"); } type = odb_obj->raw.type; if ((error = create_object(&object, type)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to lookup object"); /* Initialize parent object */ - git_oid_cpy(&object->cached.oid, id); + git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); object->repo = repo; switch (type) { @@ -155,13 +196,17 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o if (error < GIT_SUCCESS) { git_object__free(object); - return error; + return git__rethrow(error, "Failed to lookup object"); } *object_out = git_cache_try_store(&repo->objects, object); return GIT_SUCCESS; } +int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) { + return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type); +} + void git_object__free(void *_obj) { git_object *object = (git_object *)_obj; diff --git a/vendor/libgit2/src/odb.c b/vendor/libgit2/src/odb.c index e9e495eb1..ec81cdacb 100644 --- a/vendor/libgit2/src/odb.c +++ b/vendor/libgit2/src/odb.c @@ -46,38 +46,35 @@ typedef struct int is_alternate; } backend_internal; -static int format_object_header(char *hdr, size_t n, git_rawobj *obj) +static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type) { - const char *type_str = git_object_type2string(obj->type); - int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj->len); - - assert(len > 0); /* otherwise snprintf() is broken */ - assert(((size_t) len) < n); /* otherwise the caller is broken! */ + const char *type_str = git_object_type2string(obj_type); + int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len); if (len < 0 || ((size_t) len) >= n) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Cannot format object header. Length is out of bounds"); + return len+1; } -int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj) +int git_odb__hash_obj(git_oid *id, git_rawobj *obj) { git_buf_vec vec[2]; - int hdrlen; + char header[64]; + int hdrlen; - assert(id && hdr && len && obj); + assert(id && obj); if (!git_object_typeisloose(obj->type)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to hash object. Wrong object type"); if (!obj->data && obj->len != 0) - return GIT_ERROR; - - if ((hdrlen = format_object_header(hdr, n, obj)) < 0) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to hash object. No data given"); - *len = hdrlen; + if ((hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type)) < 0) + return git__rethrow(hdrlen, "Failed to hash object"); - vec[0].data = hdr; + vec[0].data = header; vec[0].len = hdrlen; vec[1].data = obj->data; vec[1].len = obj->len; @@ -134,10 +131,54 @@ void git_odb_object_close(git_odb_object *object) git_cached_obj_decref((git_cached_obj *)object, &free_odb_object); } +int git_odb_hashfile(git_oid *out, const char *path, git_otype type) +{ + int fd, hdr_len; + char hdr[64], buffer[2048]; + git_off_t size; + git_hash_ctx *ctx; + + if ((fd = p_open(path, O_RDONLY)) < 0) + return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path); + + if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { + p_close(fd); + return git__throw(GIT_EOSERR, "'%s' appears to be corrupted", path); + } + + hdr_len = format_object_header(hdr, sizeof(hdr), (size_t)size, type); + if (hdr_len < 0) + return git__throw(GIT_ERROR, "Failed to format blob header. Length is out of bounds"); + + ctx = git_hash_new_ctx(); + + git_hash_update(ctx, hdr, hdr_len); + + while (size > 0) { + ssize_t read_len; + + read_len = read(fd, buffer, sizeof(buffer)); + + if (read_len < 0) { + p_close(fd); + git_hash_free_ctx(ctx); + return git__throw(GIT_EOSERR, "Can't read full file '%s'", path); + } + + git_hash_update(ctx, buffer, read_len); + size -= read_len; + } + + p_close(fd); + + git_hash_final(out, ctx); + git_hash_free_ctx(ctx); + + return GIT_SUCCESS; +} + int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) { - char hdr[64]; - int hdrlen; git_rawobj raw; assert(id); @@ -146,7 +187,7 @@ int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) raw.len = len; raw.type = type; - return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, &raw); + return git_odb__hash_obj(id, &raw); } /** @@ -170,7 +211,7 @@ static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t { fake_wstream *stream = (fake_wstream *)_stream; - if (stream->written + len >= stream->size) + if (stream->written + len > stream->size) return GIT_ENOMEM; memcpy(stream->buffer + stream->written, data, len); @@ -223,8 +264,8 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend static int backend_sort_cmp(const void *a, const void *b) { - const backend_internal *backend_a = *(const backend_internal **)(a); - const backend_internal *backend_b = *(const backend_internal **)(b); + const backend_internal *backend_a = (const backend_internal *)(a); + const backend_internal *backend_b = (const backend_internal *)(b); if (backend_a->is_alternate == backend_b->is_alternate) return (backend_b->priority - backend_a->priority); @@ -234,15 +275,21 @@ static int backend_sort_cmp(const void *a, const void *b) int git_odb_new(git_odb **out) { + int error; + git_odb *db = git__calloc(1, sizeof(*db)); if (!db) return GIT_ENOMEM; - git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + if (error < GIT_SUCCESS) { + free(db); + return git__rethrow(error, "Failed to create object database"); + } - if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { + if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) { free(db); - return GIT_ENOMEM; + return git__rethrow(error, "Failed to create object database"); } *out = db; @@ -256,7 +303,7 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio assert(odb && backend); if (backend->odb != NULL && backend->odb != odb) - return GIT_EBUSY; + return git__throw(GIT_EBUSY, "The backend is already owned by another ODB"); internal = git__malloc(sizeof(backend_internal)); if (internal == NULL) @@ -298,7 +345,7 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt error = add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to add backend"); /* add the packed file backend */ error = git_odb_backend_pack(&packed, objects_dir); @@ -307,7 +354,7 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt error = add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to add backend"); return GIT_SUCCESS; } @@ -315,28 +362,42 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt static int load_alternates(git_odb *odb, const char *objects_dir) { char alternates_path[GIT_PATH_MAX]; - char alternate[GIT_PATH_MAX]; - char *buffer; + char *buffer, *alternate; - gitfo_buf alternates_buf = GITFO_BUF_INIT; + git_fbuffer alternates_buf = GIT_FBUFFER_INIT; int error; - git__joinpath(alternates_path, objects_dir, GIT_ALTERNATES_FILE); + git_path_join(alternates_path, objects_dir, GIT_ALTERNATES_FILE); - if (gitfo_exists(alternates_path) < GIT_SUCCESS) + if (git_futils_exists(alternates_path) < GIT_SUCCESS) return GIT_SUCCESS; - if (gitfo_read_file(&alternates_buf, alternates_path) < GIT_SUCCESS) - return GIT_EOSERR; + if (git_futils_readbuffer(&alternates_buf, alternates_path) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates"); buffer = (char *)alternates_buf.data; error = GIT_SUCCESS; /* add each alternate as a new backend; one alternate per line */ - while ((error == GIT_SUCCESS) && (buffer = git__strtok(alternate, buffer, "\r\n")) != NULL) - error = add_default_backends(odb, alternate, 1); + while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) { + char full_path[GIT_PATH_MAX]; + + if (*alternate == '\0' || *alternate == '#') + continue; + + /* relative path: build based on the current `objects` folder */ + if (*alternate == '.') { + git_path_join(full_path, objects_dir, alternate); + alternate = full_path; + } - gitfo_free_buf(&alternates_buf); + if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS) + break; + } + + git_futils_freebuffer(&alternates_buf); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to load alternates"); return error; } @@ -350,7 +411,7 @@ int git_odb_open(git_odb **out, const char *objects_dir) *out = NULL; if ((error = git_odb_new(&db)) < 0) - return error; + return git__rethrow(error, "Failed to open ODB"); if ((error = add_default_backends(db, objects_dir, 0)) < GIT_SUCCESS) goto cleanup; @@ -363,7 +424,7 @@ int git_odb_open(git_odb **out, const char *objects_dir) cleanup: git_odb_close(db); - return error; + return error; /* error already set - pass through */ } void git_odb_close(git_odb *db) @@ -435,16 +496,19 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git error = b->read_header(len_p, type_p, b, id); } + if (error == GIT_EPASSTHROUGH) + return GIT_SUCCESS; + /* * no backend could read only the header. * try reading the whole object and freeing the contents */ if (error < 0) { if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS) - return error; + return error; /* error already set - pass through */ *len_p = object->raw.len; - *type_p = object->raw.len; + *type_p = object->raw.type; git_odb_object_close(object); } @@ -471,17 +535,73 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) error = b->read(&raw.data, &raw.len, &raw.type, b, id); } - if (error == GIT_SUCCESS) { + if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) { *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw)); + return GIT_SUCCESS; } - return error; + return git__rethrow(error, "Failed to read object"); +} + +int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len) +{ + unsigned int i; + int error = GIT_ENOTFOUND; + git_oid full_oid; + git_rawobj raw; + int found = 0; + + assert(out && db); + + if (len < GIT_OID_MINPREFIXLEN) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + *out = git_cache_get(&db->cache, short_id); + if (*out != NULL) + return GIT_SUCCESS; + } + + for (i = 0; i < db->backends.length && found < 2; ++i) { + backend_internal *internal = git_vector_get(&db->backends, i); + git_odb_backend *b = internal->backend; + + if (b->read != NULL) { + error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len); + switch (error) { + case GIT_SUCCESS: + found++; + break; + case GIT_ENOTFOUND: + case GIT_EPASSTHROUGH: + break; + case GIT_EAMBIGUOUSOIDPREFIX: + return git__rethrow(error, "Failed to read object. Ambiguous sha1 prefix"); + default: + return git__rethrow(error, "Failed to read object"); + } + } + } + + if (found == 1) { + *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw)); + } else if (found > 1) { + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read object. Ambiguous sha1 prefix"); + } else { + return git__throw(GIT_ENOTFOUND, "Failed to read object. Object not found"); + } + + return GIT_SUCCESS; } int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type) { unsigned int i; int error = GIT_ERROR; + git_odb_stream *stream; assert(oid && db); @@ -497,20 +617,21 @@ int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_o error = b->write(oid, b, data, len, type); } + if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) + return GIT_SUCCESS; + /* if no backends were able to write the object directly, we try a streaming * write to the backends; just write the whole object into the stream in one * push */ - if (error < GIT_SUCCESS) { - git_odb_stream *stream; - if ((error = git_odb_open_wstream(&stream, db, len, type)) == GIT_SUCCESS) { - stream->write(stream, data, len); - error = stream->finalize_write(oid, stream); - stream->free(stream); - } + if ((error = git_odb_open_wstream(&stream, db, len, type)) == GIT_SUCCESS) { + stream->write(stream, data, len); + error = stream->finalize_write(oid, stream); + stream->free(stream); + return GIT_SUCCESS; } - return error; + return git__rethrow(error, "Failed to write object"); } int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type) @@ -534,10 +655,13 @@ int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_ error = init_fake_wstream(stream, b, size, type); } - return error; + if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) + return GIT_SUCCESS; + + return git__rethrow(error, "Failed to open write stream"); } -int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) +int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) { unsigned int i; int error = GIT_ERROR; @@ -552,6 +676,9 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi error = b->readstream(stream, b, oid); } - return error; + if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) + return GIT_SUCCESS; + + return git__rethrow(error, "Failed to open read stream"); } diff --git a/vendor/libgit2/src/odb.h b/vendor/libgit2/src/odb.h index f3685834e..1d4f07dcc 100644 --- a/vendor/libgit2/src/odb.h +++ b/vendor/libgit2/src/odb.h @@ -28,6 +28,6 @@ struct git_odb { git_cache cache; }; -int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj); +int git_odb__hash_obj(git_oid *id, git_rawobj *obj); #endif diff --git a/vendor/libgit2/src/odb_loose.c b/vendor/libgit2/src/odb_loose.c index 873dbfa0a..a3a4e5b56 100644 --- a/vendor/libgit2/src/odb_loose.c +++ b/vendor/libgit2/src/odb_loose.c @@ -26,6 +26,7 @@ #include "common.h" #include "git2/zlib.h" #include "git2/object.h" +#include "git2/oid.h" #include "fileops.h" #include "hash.h" #include "odb.h" @@ -54,6 +55,20 @@ typedef struct loose_backend { char *objects_dir; } loose_backend; +/* State structure for exploring directories, + * in order to locate objects matching a short oid. + */ +typedef struct { + size_t dir_len; + unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */ + unsigned int short_oid_len; + int found; /* number of matching + * objects already found */ + unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of + * the object found */ +} loose_locate_object_state; + + /*********************************************************** * @@ -82,7 +97,7 @@ static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *i } -static size_t get_binary_object_header(obj_hdr *hdr, gitfo_buf *obj) +static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj) { unsigned char c; unsigned char *data = obj->data; @@ -184,7 +199,7 @@ static void set_stream_output(z_stream *s, void *out, size_t len) } -static int start_inflate(z_stream *s, gitfo_buf *obj, void *out, size_t len) +static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len) { int status; @@ -207,7 +222,7 @@ static int finish_inflate(z_stream *s) inflateEnd(s); if ((status != Z_STREAM_END) || (s->avail_in != 0)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to finish inflation. Stream aborted prematurely"); return GIT_SUCCESS; } @@ -234,7 +249,7 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) zs.avail_in = inlen; if (inflateInit(&zs) < Z_OK) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate buffer"); while (status == Z_OK) status = inflate(&zs, Z_FINISH); @@ -242,10 +257,10 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen) inflateEnd(&zs); if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely"); if (zs.total_out != outlen) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely"); return GIT_SUCCESS; } @@ -294,7 +309,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr) * of loose object data into packs. This format is no longer used, but * we must still read it. */ -static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj) +static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj) { unsigned char *in, *buf; obj_hdr hdr; @@ -305,23 +320,23 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj) * binary encoding of the object type and size. */ if ((used = get_binary_object_header(&hdr, obj)) == 0) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate loose object. Object has no header"); if (!git_object_typeisloose(hdr.type)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate loose object. Wrong object type"); /* * allocate a buffer and inflate the data into it */ buf = git__malloc(hdr.size + 1); if (!buf) - return GIT_ERROR; + return GIT_ENOMEM; in = ((unsigned char *)obj->data) + used; len = obj->len - used; if (inflate_buffer(in, len, buf, hdr.size)) { free(buf); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer"); } buf[hdr.size] = '\0'; @@ -332,7 +347,7 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, gitfo_buf *obj) return GIT_SUCCESS; } -static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj) +static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj) { unsigned char head[64], *buf; z_stream zs; @@ -350,20 +365,20 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj) * to parse the object header (type and size). */ if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate disk object. Could not inflate buffer"); if ((used = get_object_header(&hdr, head)) == 0) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate disk object. Object has no header"); if (!git_object_typeisloose(hdr.type)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to inflate disk object. Wrong object type"); /* * allocate a buffer and inflate the object data into it * (including the initial sequence in the head buffer). */ if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL) - return GIT_ERROR; + return GIT_ENOMEM; buf[hdr.size] = '\0'; out->data = buf; @@ -390,7 +405,7 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj) static int read_loose(git_rawobj *out, const char *loc) { int error; - gitfo_buf obj = GITFO_BUF_INIT; + git_fbuffer obj = GIT_FBUFFER_INIT; assert(out && loc); @@ -398,13 +413,13 @@ static int read_loose(git_rawobj *out, const char *loc) out->len = 0; out->type = GIT_OBJ_BAD; - if (gitfo_read_file(&obj, loc) < 0) - return GIT_ENOTFOUND; + if (git_futils_readbuffer(&obj, loc) < 0) + return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found"); error = inflate_disk_obj(out, &obj); - gitfo_free_buf(&obj); + git_futils_freebuffer(&obj); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object"); } static int read_header_loose(git_rawobj *out, const char *loc) @@ -419,8 +434,8 @@ static int read_header_loose(git_rawobj *out, const char *loc) out->data = NULL; - if ((fd = gitfo_open(loc, O_RDONLY)) < 0) - return GIT_ENOTFOUND; + if ((fd = p_open(loc, O_RDONLY)) < 0) + return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found"); init_stream(&zs, inflated_buffer, sizeof(inflated_buffer)); @@ -451,14 +466,99 @@ static int read_header_loose(git_rawobj *out, const char *loc) cleanup: finish_inflate(&zs); - gitfo_close(fd); - return error; + p_close(fd); + + if (error < GIT_SUCCESS) + return git__throw(error, "Failed to read loose object header. Header is corrupted"); + + return GIT_SUCCESS; } static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid) { object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid); - return gitfo_exists(object_location); + return git_futils_exists(object_location); +} + +/* Explore an entry of a directory and see if it matches a short oid */ +int fn_locate_object_short_oid(void *state, char *pathbuf) { + loose_locate_object_state *sstate = (loose_locate_object_state *)state; + + size_t pathbuf_len = strlen(pathbuf); + if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) { + /* Entry cannot be an object. Continue to next entry */ + return GIT_SUCCESS; + } + + if (!git_futils_exists(pathbuf) && git_futils_isdir(pathbuf)) { + /* We are already in the directory matching the 2 first hex characters, + * compare the first ncmp characters of the oids */ + if (!memcmp(sstate->short_oid + 2, + (unsigned char *)pathbuf + sstate->dir_len, + sstate->short_oid_len - 2)) { + + if (!sstate->found) { + sstate->res_oid[0] = sstate->short_oid[0]; + sstate->res_oid[1] = sstate->short_oid[1]; + memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2); + } + sstate->found++; + } + } + if (sstate->found > 1) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects"); + + return GIT_SUCCESS; +} + +/* Locate an object matching a given short oid */ +static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len) +{ + char *objects_dir = backend->objects_dir; + size_t dir_len = strlen(objects_dir); + loose_locate_object_state state; + int error; + + if (dir_len+43 > GIT_PATH_MAX) + return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long"); + + strcpy(object_location, objects_dir); + + /* Add a separator if not already there */ + if (object_location[dir_len-1] != '/') + object_location[dir_len++] = '/'; + + /* Convert raw oid to hex formatted oid */ + git_oid_fmt((char *)state.short_oid, short_oid); + /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ + sprintf(object_location+dir_len, "%.2s/", state.short_oid); + + /* Check that directory exists */ + if (git_futils_exists(object_location) || git_futils_isdir(object_location)) + return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); + + state.dir_len = dir_len+3; + state.short_oid_len = len; + state.found = 0; + /* Explore directory to find a unique object matching short_oid */ + error = git_futils_direach(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state); + if (error) { + return git__rethrow(error, "Failed to locate object from short oid"); + } + if (!state.found) { + return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found"); + } + + /* Convert obtained hex formatted oid to raw */ + error = git_oid_fromstr(res_oid, (char *)state.res_oid); + if (error) { + return git__rethrow(error, "Failed to locate object from short oid"); + } + + /* Update the location according to the oid obtained */ + git_oid_pathfmt(object_location+dir_len, res_oid); + + return GIT_SUCCESS; } @@ -485,8 +585,11 @@ int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend assert(backend && oid); + raw.len = 0; + raw.type = GIT_OBJ_BAD; + if (locate_object(object_path, (loose_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found"); if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS) return error; @@ -505,10 +608,10 @@ int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_o assert(backend && oid); if (locate_object(object_path, (loose_backend *)backend, oid) < 0) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found"); if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to read loose backend"); *buffer_p = raw.data; *len_p = raw.len; @@ -517,6 +620,47 @@ int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_o return GIT_SUCCESS; } +int loose_backend__read_prefix( + git_oid *out_oid, + void **buffer_p, + size_t *len_p, + git_otype *type_p, + git_odb_backend *backend, + const git_oid *short_oid, + unsigned int len) +{ + if (len < GIT_OID_MINPREFIXLEN) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + + if (len >= GIT_OID_HEXSZ) { + /* We can fall back to regular read method */ + int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); + if (error == GIT_SUCCESS) + git_oid_cpy(out_oid, short_oid); + + return error; + } else { + char object_path[GIT_PATH_MAX]; + git_rawobj raw; + int error; + + assert(backend && short_oid); + + if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) { + return git__rethrow(error, "Failed to read loose backend"); + } + + if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read loose backend"); + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + } + + return GIT_SUCCESS; +} + int loose_backend__exists(git_odb_backend *backend, const git_oid *oid) { char object_path[GIT_PATH_MAX]; @@ -535,13 +679,13 @@ int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream) char final_path[GIT_PATH_MAX]; if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write loose backend"); if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) return GIT_ENOMEM; - if ((error = gitfo_mkdir_2file(final_path)) < GIT_SUCCESS) - return error; + if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write loose backend"); stream->finished = 1; return git_filebuf_commit_at(&stream->fbuf, final_path); @@ -572,7 +716,7 @@ static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype o assert(((size_t) len) < n); /* otherwise the caller is broken! */ if (len < 0 || ((size_t) len) >= n) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to format object header. Length is out of bounds"); return len+1; } @@ -592,7 +736,7 @@ int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend hdrlen = format_object_header(hdr, sizeof(hdr), length, type); if (hdrlen < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to create loose backend stream. Object is corrupted"); stream = git__calloc(1, sizeof(loose_writestream)); if (stream == NULL) @@ -605,7 +749,7 @@ int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend stream->stream.free = &loose_backend__stream_free; stream->stream.mode = GIT_STREAM_WRONLY; - git__joinpath(tmp_path, backend->objects_dir, "tmp_object"); + git_path_join(tmp_path, backend->objects_dir, "tmp_object"); error = git_filebuf_open(&stream->fbuf, tmp_path, GIT_FILEBUF_HASH_CONTENTS | @@ -614,20 +758,63 @@ int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend if (error < GIT_SUCCESS) { free(stream); - return error; + return git__rethrow(error, "Failed to create loose backend stream"); } error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen); if (error < GIT_SUCCESS) { git_filebuf_cleanup(&stream->fbuf); free(stream); - return error; + return git__rethrow(error, "Failed to create loose backend stream"); } *stream_out = (git_odb_stream *)stream; return GIT_SUCCESS; } +int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type) +{ + int error, header_len; + char final_path[GIT_PATH_MAX], header[64]; + git_filebuf fbuf; + loose_backend *backend; + + backend = (loose_backend *)_backend; + + /* prepare the header for the file */ + { + header_len = format_object_header(header, sizeof(header), len, type); + if (header_len < GIT_SUCCESS) + return GIT_EOBJCORRUPTED; + } + + git_path_join(final_path, backend->objects_dir, "tmp_object"); + + error = git_filebuf_open(&fbuf, final_path, + GIT_FILEBUF_HASH_CONTENTS | + GIT_FILEBUF_DEFLATE_CONTENTS | + GIT_FILEBUF_TEMPORARY); + + if (error < GIT_SUCCESS) + return error; + + git_filebuf_write(&fbuf, header, header_len); + git_filebuf_write(&fbuf, data, len); + git_filebuf_hash(oid, &fbuf); + + if ((error = object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) < GIT_SUCCESS) + goto cleanup; + + if ((error = git_futils_mkpath2file(final_path)) < GIT_SUCCESS) + goto cleanup; + + return git_filebuf_commit_at(&fbuf, final_path); + +cleanup: + git_filebuf_cleanup(&fbuf); + return error; +} + void loose_backend__free(git_odb_backend *_backend) { loose_backend *backend; @@ -656,6 +843,8 @@ int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir backend->fsync_object_files = 0; backend->parent.read = &loose_backend__read; + backend->parent.write = &loose_backend__write; + backend->parent.read_prefix = &loose_backend__read_prefix; backend->parent.read_header = &loose_backend__read_header; backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; diff --git a/vendor/libgit2/src/odb_pack.c b/vendor/libgit2/src/odb_pack.c index 57ad5e34b..5b2be58e1 100644 --- a/vendor/libgit2/src/odb_pack.c +++ b/vendor/libgit2/src/odb_pack.c @@ -26,107 +26,23 @@ #include "common.h" #include "git2/zlib.h" #include "git2/repository.h" +#include "git2/oid.h" #include "fileops.h" #include "hash.h" #include "odb.h" #include "delta-apply.h" +#include "sha1_lookup.h" +#include "mwindow.h" +#include "pack.h" #include "git2/odb_backend.h" -#define DEFAULT_WINDOW_SIZE \ - (sizeof(void*) >= 8 \ - ? 1 * 1024 * 1024 * 1024 \ - : 32 * 1024 * 1024) - -#define DEFAULT_MAPPED_LIMIT \ - ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256)) - -#define PACK_SIGNATURE 0x5041434b /* "PACK" */ -#define PACK_VERSION 2 -#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) -struct pack_header { - uint32_t hdr_signature; - uint32_t hdr_version; - uint32_t hdr_entries; -}; - -/* - * The first four bytes of index formats later than version 1 should - * start with this signature, as all older git binaries would find this - * value illegal and abort reading the file. - * - * This is the case because the number of objects in a packfile - * cannot exceed 1,431,660,000 as every object would need at least - * 3 bytes of data and the overall packfile cannot exceed 4 GiB with - * version 1 of the index file due to the offsets limited to 32 bits. - * Clearly the signature exceeds this maximum. - * - * Very old git binaries will also compare the first 4 bytes to the - * next 4 bytes in the index and abort with a "non-monotonic index" - * error if the second 4 byte word is smaller than the first 4 - * byte word. This would be true in the proposed future index - * format as idx_signature would be greater than idx_version. - */ -#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ - -struct pack_idx_header { - uint32_t idx_signature; - uint32_t idx_version; -}; - -struct pack_window { - struct pack_window *next; - git_map window_map; - off_t offset; - unsigned int last_used; - unsigned int inuse_cnt; -}; - -struct pack_file { - struct pack_window *windows; - off_t pack_size; - - git_map index_map; - - uint32_t num_objects; - uint32_t num_bad_objects; - git_oid *bad_object_sha1; /* array of git_oid */ - - int index_version; - git_time_t mtime; - int pack_fd; - unsigned pack_local:1, pack_keep:1; - git_oid sha1; - - /* something like ".git/objects/pack/xxxxx.pack" */ - char pack_name[GIT_FLEX_ARRAY]; /* more */ -}; - -struct pack_entry { - off_t offset; - git_oid sha1; - struct pack_file *p; -}; - struct pack_backend { git_odb_backend parent; git_vector packs; - struct pack_file *last_found; + struct git_pack_file *last_found; char *pack_folder; time_t pack_folder_mtime; - - size_t window_size; /* needs default value */ - - size_t mapped_limit; /* needs default value */ - size_t peak_mapped; - size_t mapped; - - size_t used_ctr; - - unsigned int peak_open_windows; - unsigned int open_windows; - - unsigned int mmap_calls; }; /** @@ -224,95 +140,34 @@ struct pack_backend { */ - - /*********************************************************** * * FORWARD DECLARATIONS * ***********************************************************/ -static void pack_window_free_all(struct pack_backend *backend, struct pack_file *p); -static int pack_window_contains(struct pack_window *win, off_t offset); - -static void pack_window_scan_lru(struct pack_file *p, struct pack_file **lru_p, - struct pack_window **lru_w, struct pack_window **lru_l); - -static int pack_window_close_lru( struct pack_backend *backend, - struct pack_file *current, git_file keep_fd); - -static void pack_window_close(struct pack_window **w_cursor); - -static unsigned char *pack_window_open( struct pack_backend *backend, - struct pack_file *p, struct pack_window **w_cursor, off_t offset, - unsigned int *left); +static void pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p); +static int pack_window_contains(git_mwindow *win, off_t offset); static int packfile_sort__cb(const void *a_, const void *b_); -static void pack_index_free(struct pack_file *p); - -static int pack_index_check(const char *path, struct pack_file *p); -static int pack_index_open(struct pack_file *p); - -static struct pack_file *packfile_alloc(int extra); -static int packfile_open(struct pack_file *p); -static int packfile_check(struct pack_file **pack_out, const char *path); static int packfile_load__cb(void *_data, char *path); static int packfile_refresh_all(struct pack_backend *backend); -static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n); - -static int pack_entry_find_offset(off_t *offset_out, - struct pack_file *p, const git_oid *oid); - -static int pack_entry_find1(struct pack_entry *e, - struct pack_file *p, const git_oid *oid); - -static int pack_entry_find(struct pack_entry *e, +static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid); -static off_t get_delta_base(struct pack_backend *backend, - struct pack_file *p, struct pack_window **w_curs, - off_t *curpos, git_otype type, - off_t delta_obj_offset); - -static unsigned long packfile_unpack_header1( - size_t *sizep, - git_otype *type, - const unsigned char *buf, - unsigned long len); - -static int packfile_unpack_header( - size_t *size_p, - git_otype *type_p, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t *curpos); - -static int packfile_unpack_compressed( - git_rawobj *obj, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t curpos, - size_t size, - git_otype type); - -static int packfile_unpack_delta( - git_rawobj *obj, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t curpos, - size_t delta_size, - git_otype delta_type, - off_t obj_offset); - -static int packfile_unpack(git_rawobj *obj, struct pack_backend *backend, - struct pack_file *p, off_t obj_offset); - - +/* Can find the offset of an object given + * a prefix of an identifier. + * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid + * is ambiguous. + * This method assumes that len is between + * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ. + */ +static int pack_entry_find_prefix(struct git_pack_entry *e, + struct pack_backend *backend, + const git_oid *short_oid, + unsigned int len); @@ -322,23 +177,13 @@ static int packfile_unpack(git_rawobj *obj, struct pack_backend *backend, * ***********************************************************/ -void pack_window_free_all(struct pack_backend *backend, struct pack_file *p) +GIT_INLINE(void) pack_window_free_all(struct pack_backend *GIT_UNUSED(backend), struct git_pack_file *p) { - while (p->windows) { - struct pack_window *w = p->windows; - assert(w->inuse_cnt == 0); - - backend->mapped -= w->window_map.len; - backend->open_windows--; - - gitfo_free_map(&w->window_map); - - p->windows = w->next; - free(w); - } + GIT_UNUSED_ARG(backend); + git_mwindow_free_all(&p->mwf); } -GIT_INLINE(int) pack_window_contains(struct pack_window *win, off_t offset) +GIT_INLINE(int) pack_window_contains(git_mwindow *win, off_t offset) { /* We must promise at least 20 bytes (one hash) after the * offset is available from this window, otherwise the offset @@ -346,328 +191,13 @@ GIT_INLINE(int) pack_window_contains(struct pack_window *win, off_t offset) * has that one hash excess) must be used. This is to support * the object header and delta base parsing routines below. */ - off_t win_off = win->offset; - return win_off <= offset - && (offset + 20) <= (off_t)(win_off + win->window_map.len); -} - -static void pack_window_scan_lru( - struct pack_file *p, - struct pack_file **lru_p, - struct pack_window **lru_w, - struct pack_window **lru_l) -{ - struct pack_window *w, *w_l; - - for (w_l = NULL, w = p->windows; w; w = w->next) { - if (!w->inuse_cnt) { - if (!*lru_w || w->last_used < (*lru_w)->last_used) { - *lru_p = p; - *lru_w = w; - *lru_l = w_l; - } - } - w_l = w; - } -} - -static int pack_window_close_lru( - struct pack_backend *backend, - struct pack_file *current, - git_file keep_fd) -{ - struct pack_file *lru_p = NULL; - struct pack_window *lru_w = NULL, *lru_l = NULL; - size_t i; - - if (current) - pack_window_scan_lru(current, &lru_p, &lru_w, &lru_l); - - for (i = 0; i < backend->packs.length; ++i) - pack_window_scan_lru(git_vector_get(&backend->packs, i), &lru_p, &lru_w, &lru_l); - - if (lru_p) { - backend->mapped -= lru_w->window_map.len; - gitfo_free_map(&lru_w->window_map); - - if (lru_l) - lru_l->next = lru_w->next; - else { - lru_p->windows = lru_w->next; - if (!lru_p->windows && lru_p->pack_fd != keep_fd) { - gitfo_close(lru_p->pack_fd); - lru_p->pack_fd = -1; - } - } - - free(lru_w); - backend->open_windows--; - return GIT_SUCCESS; - } - - return GIT_ERROR; -} - -static void pack_window_close(struct pack_window **w_cursor) -{ - struct pack_window *w = *w_cursor; - if (w) { - w->inuse_cnt--; - *w_cursor = NULL; - } -} - -static unsigned char *pack_window_open( - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_cursor, - off_t offset, - unsigned int *left) -{ - struct pack_window *win = *w_cursor; - - if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS) - return NULL; - - /* Since packfiles end in a hash of their content and it's - * pointless to ask for an offset into the middle of that - * hash, and the pack_window_contains function above wouldn't match - * don't allow an offset too close to the end of the file. - */ - if (offset > (p->pack_size - 20)) - return NULL; - - if (!win || !pack_window_contains(win, offset)) { - - if (win) - win->inuse_cnt--; - - for (win = p->windows; win; win = win->next) { - if (pack_window_contains(win, offset)) - break; - } - - if (!win) { - size_t window_align = backend->window_size / 2; - size_t len; - - win = git__calloc(1, sizeof(*win)); - win->offset = (offset / window_align) * window_align; - - len = (size_t)(p->pack_size - win->offset); - if (len > backend->window_size) - len = backend->window_size; - - backend->mapped += len; - - while (backend->mapped_limit < backend->mapped && - pack_window_close_lru(backend, p, p->pack_fd) == GIT_SUCCESS) {} - - if (gitfo_map_ro(&win->window_map, p->pack_fd, - win->offset, len) < GIT_SUCCESS) - return NULL; - - backend->mmap_calls++; - backend->open_windows++; - - if (backend->mapped > backend->peak_mapped) - backend->peak_mapped = backend->mapped; - - if (backend->open_windows > backend->peak_open_windows) - backend->peak_open_windows = backend->open_windows; - - win->next = p->windows; - p->windows = win; - } - } - - if (win != *w_cursor) { - win->last_used = backend->used_ctr++; - win->inuse_cnt++; - *w_cursor = win; - } - - offset -= win->offset; - assert(git__is_sizet(offset)); - - if (left) - *left = win->window_map.len - (size_t)offset; - - return (unsigned char *)win->window_map.data + offset; + return git_mwindow_contains(win, offset + 20); } - - - - - - -/*********************************************************** - * - * PACK INDEX METHODS - * - ***********************************************************/ - -static void pack_index_free(struct pack_file *p) -{ - if (p->index_map.data) { - gitfo_free_map(&p->index_map); - p->index_map.data = NULL; - } -} - -static int pack_index_check(const char *path, struct pack_file *p) -{ - struct pack_idx_header *hdr; - uint32_t version, nr, i, *index; - - void *idx_map; - size_t idx_size; - - struct stat st; - - /* TODO: properly open the file without access time */ - git_file fd = gitfo_open(path, O_RDONLY /*| O_NOATIME */); - - int error; - - if (fd < 0) - return GIT_EOSERR; - - if (gitfo_fstat(fd, &st) < GIT_SUCCESS) { - gitfo_close(fd); - return GIT_EOSERR; - } - - if (!git__is_sizet(st.st_size)) - return GIT_ENOMEM; - - idx_size = (size_t)st.st_size; - - if (idx_size < 4 * 256 + 20 + 20) { - gitfo_close(fd); - return GIT_EOBJCORRUPTED; - } - - error = gitfo_map_ro(&p->index_map, fd, 0, idx_size); - gitfo_close(fd); - - if (error < GIT_SUCCESS) - return error; - - hdr = idx_map = p->index_map.data; - - if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { - version = ntohl(hdr->idx_version); - - if (version < 2 || version > 2) { - gitfo_free_map(&p->index_map); - return GIT_EOBJCORRUPTED; /* unsupported index version */ - } - - } else - version = 1; - - nr = 0; - index = idx_map; - - if (version > 1) - index += 2; /* skip index header */ - - for (i = 0; i < 256; i++) { - uint32_t n = ntohl(index[i]); - if (n < nr) { - gitfo_free_map(&p->index_map); - return GIT_EOBJCORRUPTED; /* non-monotonic index */ - } - nr = n; - } - - if (version == 1) { - /* - * Total size: - * - 256 index entries 4 bytes each - * - 24-byte entries * nr (20-byte sha1 + 4-byte offset) - * - 20-byte SHA1 of the packfile - * - 20-byte SHA1 file checksum - */ - if (idx_size != 4*256 + nr * 24 + 20 + 20) { - gitfo_free_map(&p->index_map); - return GIT_EOBJCORRUPTED; - } - } else if (version == 2) { - /* - * Minimum size: - * - 8 bytes of header - * - 256 index entries 4 bytes each - * - 20-byte sha1 entry * nr - * - 4-byte crc entry * nr - * - 4-byte offset entry * nr - * - 20-byte SHA1 of the packfile - * - 20-byte SHA1 file checksum - * And after the 4-byte offset table might be a - * variable sized table containing 8-byte entries - * for offsets larger than 2^31. - */ - unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20; - unsigned long max_size = min_size; - - if (nr) - max_size += (nr - 1)*8; - - if (idx_size < min_size || idx_size > max_size) { - gitfo_free_map(&p->index_map); - return GIT_EOBJCORRUPTED; - } - - /* Make sure that off_t is big enough to access the whole pack... - * Is this an issue in libgit2? It shouldn't. */ - if (idx_size != min_size && (sizeof(off_t) <= 4)) { - gitfo_free_map(&p->index_map); - return GIT_EOSERR; - } - } - - p->index_version = version; - p->num_objects = nr; - return GIT_SUCCESS; -} - -static int pack_index_open(struct pack_file *p) -{ - char *idx_name; - int error; - - if (p->index_map.data) - return GIT_SUCCESS; - - idx_name = git__strdup(p->pack_name); - strcpy(idx_name + strlen(idx_name) - STRLEN(".pack"), ".idx"); - - error = pack_index_check(idx_name, p); - free(idx_name); - - return error; -} - - - - - - - - - -/*********************************************************** - * - * PACKFILE METHODS - * - ***********************************************************/ - static int packfile_sort__cb(const void *a_, const void *b_) { - struct pack_file *a = *((struct pack_file **)a_); - struct pack_file *b = *((struct pack_file **)b_); + const struct git_pack_file *a = a_; + const struct git_pack_file *b = b_; int st; /* @@ -693,152 +223,12 @@ static int packfile_sort__cb(const void *a_, const void *b_) return -1; } -static struct pack_file *packfile_alloc(int extra) -{ - struct pack_file *p = git__malloc(sizeof(*p) + extra); - memset(p, 0, sizeof(*p)); - p->pack_fd = -1; - return p; -} - - -static void packfile_free(struct pack_backend *backend, struct pack_file *p) -{ - assert(p); - - /* clear_delta_base_cache(); */ - pack_window_free_all(backend, p); - - if (p->pack_fd != -1) - gitfo_close(p->pack_fd); - - pack_index_free(p); - - free(p->bad_object_sha1); - free(p); -} - -static int packfile_open(struct pack_file *p) -{ - struct stat st; - struct pack_header hdr; - git_oid sha1; - unsigned char *idx_sha1; - - if (!p->index_map.data && pack_index_open(p) < GIT_SUCCESS) - return GIT_ENOTFOUND; - - /* TODO: open with noatime */ - p->pack_fd = gitfo_open(p->pack_name, O_RDONLY); - if (p->pack_fd < 0 || gitfo_fstat(p->pack_fd, &st) < GIT_SUCCESS) - return GIT_EOSERR; - - /* If we created the struct before we had the pack we lack size. */ - if (!p->pack_size) { - if (!S_ISREG(st.st_mode)) - goto cleanup; - p->pack_size = (off_t)st.st_size; - } else if (p->pack_size != st.st_size) - goto cleanup; - -#if 0 - /* We leave these file descriptors open with sliding mmap; - * there is no point keeping them open across exec(), though. - */ - fd_flag = fcntl(p->pack_fd, F_GETFD, 0); - if (fd_flag < 0) - return error("cannot determine file descriptor flags"); - - fd_flag |= FD_CLOEXEC; - if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1) - return GIT_EOSERR; -#endif - - /* Verify we recognize this pack file format. */ - if (gitfo_read(p->pack_fd, &hdr, sizeof(hdr)) < GIT_SUCCESS) - goto cleanup; - - if (hdr.hdr_signature != htonl(PACK_SIGNATURE)) - goto cleanup; - - if (!pack_version_ok(hdr.hdr_version)) - goto cleanup; - - /* Verify the pack matches its index. */ - if (p->num_objects != ntohl(hdr.hdr_entries)) - goto cleanup; - - if (gitfo_lseek(p->pack_fd, p->pack_size - GIT_OID_RAWSZ, SEEK_SET) == -1) - goto cleanup; - - if (gitfo_read(p->pack_fd, sha1.id, GIT_OID_RAWSZ) < GIT_SUCCESS) - goto cleanup; - - idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; - - if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) != 0) - goto cleanup; - - return GIT_SUCCESS; - -cleanup: - gitfo_close(p->pack_fd); - p->pack_fd = -1; - return GIT_EPACKCORRUPTED; -} - -static int packfile_check(struct pack_file **pack_out, const char *path) -{ - struct stat st; - struct pack_file *p; - size_t path_len; - - *pack_out = NULL; - path_len = strlen(path); - p = packfile_alloc(path_len + 2); - - /* - * Make sure a corresponding .pack file exists and that - * the index looks sane. - */ - path_len -= STRLEN(".idx"); - if (path_len < 1) { - free(p); - return GIT_ENOTFOUND; - } - - memcpy(p->pack_name, path, path_len); - - strcpy(p->pack_name + path_len, ".keep"); - if (gitfo_exists(p->pack_name) == GIT_SUCCESS) - p->pack_keep = 1; - - strcpy(p->pack_name + path_len, ".pack"); - if (gitfo_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) { - free(p); - return GIT_ENOTFOUND; - } - - /* ok, it looks sane as far as we can check without - * actually mapping the pack file. - */ - p->pack_size = (off_t)st.st_size; - p->pack_local = 1; - p->mtime = (git_time_t)st.st_mtime; - - /* see if we can parse the sha1 oid in the packfile name */ - if (path_len < 40 || - git_oid_mkstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < GIT_SUCCESS) - memset(&p->sha1, 0x0, GIT_OID_RAWSZ); - *pack_out = p; - return GIT_SUCCESS; -} static int packfile_load__cb(void *_data, char *path) { struct pack_backend *backend = (struct pack_backend *)_data; - struct pack_file *pack; + struct git_pack_file *pack; int error; size_t i; @@ -846,14 +236,14 @@ static int packfile_load__cb(void *_data, char *path) return GIT_SUCCESS; /* not an index */ for (i = 0; i < backend->packs.length; ++i) { - struct pack_file *p = git_vector_get(&backend->packs, i); - if (memcmp(p->pack_name, path, strlen(path) - STRLEN(".idx")) == 0) + struct git_pack_file *p = git_vector_get(&backend->packs, i); + if (memcmp(p->pack_name, path, strlen(path) - strlen(".idx")) == 0) return GIT_SUCCESS; } - error = packfile_check(&pack, path); + error = git_packfile_check(&pack, path); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to load packfile"); if (git_vector_insert(&backend->packs, pack) < GIT_SUCCESS) { free(pack); @@ -871,17 +261,17 @@ static int packfile_refresh_all(struct pack_backend *backend) if (backend->pack_folder == NULL) return GIT_SUCCESS; - if (gitfo_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) - return GIT_ENOTFOUND; + if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) + return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found"); if (st.st_mtime != backend->pack_folder_mtime) { char path[GIT_PATH_MAX]; strcpy(path, backend->pack_folder); /* reload all packs */ - error = gitfo_dirent(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend); + error = git_futils_direach(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to refresh packfiles"); git_vector_sort(&backend->packs); backend->pack_folder_mtime = st.st_mtime; @@ -890,439 +280,85 @@ static int packfile_refresh_all(struct pack_backend *backend) return GIT_SUCCESS; } - - - - - - - -/*********************************************************** - * - * PACKFILE ENTRY SEARCH INTERNALS - * - ***********************************************************/ - -static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n) -{ - const unsigned char *index = p->index_map.data; - index += 4 * 256; - if (p->index_version == 1) { - return ntohl(*((uint32_t *)(index + 24 * n))); - } else { - uint32_t off; - index += 8 + p->num_objects * (20 + 4); - off = ntohl(*((uint32_t *)(index + 4 * n))); - if (!(off & 0x80000000)) - return off; - index += p->num_objects * 4 + (off & 0x7fffffff) * 8; - return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | - ntohl(*((uint32_t *)(index + 4))); - } -} - -static int pack_entry_find_offset( - off_t *offset_out, - struct pack_file *p, - const git_oid *oid) -{ - const uint32_t *level1_ofs = p->index_map.data; - const unsigned char *index = p->index_map.data; - unsigned hi, lo, stride; - - *offset_out = 0; - - if (index == NULL) { - int error; - - if ((error = pack_index_open(p)) < GIT_SUCCESS) - return error; - - assert(p->index_map.data); - - index = p->index_map.data; - level1_ofs = p->index_map.data; - } - - if (p->index_version > 1) { - level1_ofs += 2; - index += 8; - } - - index += 4 * 256; - hi = ntohl(level1_ofs[(int)oid->id[0]]); - lo = ((oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)oid->id[0] - 1])); - - if (p->index_version > 1) { - stride = 20; - } else { - stride = 24; - index += 4; - } - -#ifdef INDEX_DEBUG_LOOKUP - printf("%02x%02x%02x... lo %u hi %u nr %d\n", - oid->id[0], oid->id[1], oid->id[2], lo, hi, p->num_objects); -#endif - -#ifdef GIT2_INDEX_LOOKUP /* TODO: use the advanced lookup method from git.git */ - - int pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, oid); - if (pos < 0) - return GIT_ENOTFOUND; - - *offset_out = nth_packed_object_offset(p, pos); - return GIT_SUCCESS; - -#else /* use an old and boring binary search */ - - do { - unsigned mi = (lo + hi) / 2; - int cmp = memcmp(index + mi * stride, oid->id, GIT_OID_RAWSZ); - - if (!cmp) { - *offset_out = nth_packed_object_offset(p, mi); - return GIT_SUCCESS; - } - - if (cmp > 0) - hi = mi; - else - lo = mi+1; - - } while (lo < hi); - - return GIT_ENOTFOUND; -#endif -} - -static int pack_entry_find1( - struct pack_entry *e, - struct pack_file *p, - const git_oid *oid) -{ - off_t offset; - - assert(p); - - if (p->num_bad_objects) { - unsigned i; - for (i = 0; i < p->num_bad_objects; i++) - if (git_oid_cmp(oid, &p->bad_object_sha1[i]) == 0) - return GIT_ERROR; - } - - if (pack_entry_find_offset(&offset, p, oid) < GIT_SUCCESS) - return GIT_ENOTFOUND; - - /* we found an entry in the index; - * make sure the packfile backing the index - * still exists on disk */ - if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS) - return GIT_EOSERR; - - e->offset = offset; - e->p = p; - - git_oid_cpy(&e->sha1, oid); - return GIT_SUCCESS; -} - -static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, const git_oid *oid) +static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid) { int error; size_t i; if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to find pack entry"); if (backend->last_found && - pack_entry_find1(e, backend->last_found, oid) == GIT_SUCCESS) + git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == GIT_SUCCESS) return GIT_SUCCESS; for (i = 0; i < backend->packs.length; ++i) { - struct pack_file *p; + struct git_pack_file *p; p = git_vector_get(&backend->packs, i); if (p == backend->last_found) continue; - if (pack_entry_find1(e, p, oid) == GIT_SUCCESS) { + if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == GIT_SUCCESS) { backend->last_found = p; return GIT_SUCCESS; } } - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to find pack entry"); } - - - - - - - - - - - -/*********************************************************** - * - * PACKFILE ENTRY UNPACK INTERNALS - * - ***********************************************************/ - -static unsigned long packfile_unpack_header1( - size_t *sizep, - git_otype *type, - const unsigned char *buf, - unsigned long len) -{ - unsigned shift; - unsigned long size, c; - unsigned long used = 0; - - c = buf[used++]; - *type = (c >> 4) & 7; - size = c & 15; - shift = 4; - while (c & 0x80) { - if (len <= used || bitsizeof(long) <= shift) - return 0; - - c = buf[used++]; - size += (c & 0x7f) << shift; - shift += 7; - } - - *sizep = (size_t)size; - return used; -} - -static int packfile_unpack_header( - size_t *size_p, - git_otype *type_p, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t *curpos) -{ - unsigned char *base; - unsigned int left; - unsigned long used; - - /* pack_window_open() assures us we have [base, base + 20) available - * as a range that we can look at at. (Its actually the hash - * size that is assured.) With our object header encoding - * the maximum deflated object size is 2^137, which is just - * insane, so we know won't exceed what we have been given. - */ - base = pack_window_open(backend, p, w_curs, *curpos, &left); - if (base == NULL) - return GIT_ENOMEM; - - used = packfile_unpack_header1(size_p, type_p, base, left); - - if (used == 0) - return GIT_EOBJCORRUPTED; - - *curpos += used; - return GIT_SUCCESS; -} - -static int packfile_unpack_compressed( - git_rawobj *obj, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t curpos, - size_t size, - git_otype type) +static int pack_entry_find_prefix( + struct git_pack_entry *e, + struct pack_backend *backend, + const git_oid *short_oid, + unsigned int len) { - int st; - z_stream stream; - unsigned char *buffer, *in; - - buffer = git__malloc(size); - - memset(&stream, 0, sizeof(stream)); - stream.next_out = buffer; - stream.avail_out = size + 1; + int error; + size_t i; + unsigned found = 0; - st = inflateInit(&stream); - if (st != Z_OK) { - free(buffer); - return GIT_EZLIB; + if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to find pack entry"); + + if (backend->last_found) { + error = git_pack_entry_find(e, backend->last_found, short_oid, len); + if (error == GIT_EAMBIGUOUSOIDPREFIX) { + return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix"); + } else if (error == GIT_SUCCESS) { + found = 1; + } } - do { - in = pack_window_open(backend, p, w_curs, curpos, &stream.avail_in); - stream.next_in = in; - st = inflate(&stream, Z_FINISH); - - if (!stream.avail_out) - break; /* the payload is larger than it should be */ - - curpos += stream.next_in - in; - } while (st == Z_OK || st == Z_BUF_ERROR); - - inflateEnd(&stream); - - if ((st != Z_STREAM_END) || stream.total_out != size) { - free(buffer); - return GIT_EZLIB; - } + for (i = 0; i < backend->packs.length; ++i) { + struct git_pack_file *p; - obj->type = type; - obj->len = size; - obj->data = buffer; - return GIT_SUCCESS; -} + p = git_vector_get(&backend->packs, i); + if (p == backend->last_found) + continue; -static off_t get_delta_base( - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t *curpos, - git_otype type, - off_t delta_obj_offset) -{ - unsigned char *base_info = pack_window_open(backend, p, w_curs, *curpos, NULL); - off_t base_offset; - - /* pack_window_open() assured us we have [base_info, base_info + 20) - * as a range that we can look at without walking off the - * end of the mapped window. Its actually the hash size - * that is assured. An OFS_DELTA longer than the hash size - * is stupid, as then a REF_DELTA would be smaller to store. - */ - if (type == GIT_OBJ_OFS_DELTA) { - unsigned used = 0; - unsigned char c = base_info[used++]; - base_offset = c & 127; - while (c & 128) { - base_offset += 1; - if (!base_offset || MSB(base_offset, 7)) - return 0; /* overflow */ - c = base_info[used++]; - base_offset = (base_offset << 7) + (c & 127); + error = git_pack_entry_find(e, p, short_oid, len); + if (error == GIT_EAMBIGUOUSOIDPREFIX) { + return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix"); + } else if (error == GIT_SUCCESS) { + found++; + if (found > 1) + break; + backend->last_found = p; } - base_offset = delta_obj_offset - base_offset; - if (base_offset <= 0 || base_offset >= delta_obj_offset) - return 0; /* out of bound */ - *curpos += used; - } else if (type == GIT_OBJ_REF_DELTA) { - /* The base entry _must_ be in the same pack */ - if (pack_entry_find_offset(&base_offset, p, (git_oid *)base_info) < GIT_SUCCESS) - return GIT_EPACKCORRUPTED; - *curpos += 20; - } else - return 0; - - return base_offset; -} - -static int packfile_unpack_delta( - git_rawobj *obj, - struct pack_backend *backend, - struct pack_file *p, - struct pack_window **w_curs, - off_t curpos, - size_t delta_size, - git_otype delta_type, - off_t obj_offset) -{ - off_t base_offset; - git_rawobj base, delta; - int error; - - base_offset = get_delta_base(backend, p, w_curs, &curpos, delta_type, obj_offset); - if (base_offset == 0) - return GIT_EOBJCORRUPTED; - - pack_window_close(w_curs); - error = packfile_unpack(&base, backend, p, base_offset); - - /* TODO: git.git tries to load the base from other packfiles - * or loose objects */ - if (error < GIT_SUCCESS) - return error; - - error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type); - if (error < GIT_SUCCESS) { - free(base.data); - return error; } - obj->type = base.type; - error = git__delta_apply(obj, - base.data, base.len, - delta.data, delta.len); - - free(base.data); - free(delta.data); - - /* TODO: we might want to cache this shit. eventually */ - //add_delta_base_cache(p, base_offset, base, base_size, *type); - return error; -} - -static int packfile_unpack( - git_rawobj *obj, - struct pack_backend *backend, - struct pack_file *p, - off_t obj_offset) -{ - struct pack_window *w_curs = NULL; - off_t curpos = obj_offset; - int error; - - size_t size; - git_otype type; - - /* - * TODO: optionally check the CRC on the packfile - */ - - obj->data = NULL; - obj->len = 0; - obj->type = GIT_OBJ_BAD; - - error = packfile_unpack_header(&size, &type, backend, p, &w_curs, &curpos); - if (error < GIT_SUCCESS) - return error; - - switch (type) { - case GIT_OBJ_OFS_DELTA: - case GIT_OBJ_REF_DELTA: - error = packfile_unpack_delta( - obj, backend, p, &w_curs, curpos, - size, type, obj_offset); - break; - - case GIT_OBJ_COMMIT: - case GIT_OBJ_TREE: - case GIT_OBJ_BLOB: - case GIT_OBJ_TAG: - error = packfile_unpack_compressed( - obj, backend, p, &w_curs, curpos, - size, type); - break; - - default: - error = GIT_EOBJCORRUPTED; - break; + if (!found) { + return git__rethrow(GIT_ENOTFOUND, "Failed to find pack entry"); + } else if (found > 1) { + return git__rethrow(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find pack entry. Ambiguous sha1 prefix"); + } else { + return GIT_SUCCESS; } - pack_window_close(&w_curs); - return error; } - - - /*********************************************************** * * PACKED BACKEND PUBLIC API @@ -1347,15 +383,15 @@ int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const g int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) { - struct pack_entry e; + struct git_pack_entry e; git_rawobj raw; int error; if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to read pack backend"); - if ((error = packfile_unpack(&raw, (struct pack_backend *)backend, e.p, e.offset)) < GIT_SUCCESS) - return error; + if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read pack backend"); *buffer_p = raw.data; *len_p = raw.len; @@ -1364,9 +400,48 @@ int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_od return GIT_SUCCESS; } +int pack_backend__read_prefix( + git_oid *out_oid, + void **buffer_p, + size_t *len_p, + git_otype *type_p, + git_odb_backend *backend, + const git_oid *short_oid, + unsigned int len) +{ + if (len < GIT_OID_MINPREFIXLEN) + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read pack backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN); + + if (len >= GIT_OID_HEXSZ) { + /* We can fall back to regular read method */ + int error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid); + if (error == GIT_SUCCESS) + git_oid_cpy(out_oid, short_oid); + + return error; + } else { + struct git_pack_entry e; + git_rawobj raw; + int error; + + if ((error = pack_entry_find_prefix(&e, (struct pack_backend *)backend, short_oid, len)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read pack backend"); + + if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read pack backend"); + + *buffer_p = raw.data; + *len_p = raw.len; + *type_p = raw.type; + git_oid_cpy(out_oid, &e.sha1); + } + + return GIT_SUCCESS; +} + int pack_backend__exists(git_odb_backend *backend, const git_oid *oid) { - struct pack_entry e; + struct git_pack_entry e; return pack_entry_find(&e, (struct pack_backend *)backend, oid) == GIT_SUCCESS; } @@ -1380,8 +455,8 @@ void pack_backend__free(git_odb_backend *_backend) backend = (struct pack_backend *)_backend; for (i = 0; i < backend->packs.length; ++i) { - struct pack_file *p = git_vector_get(&backend->packs, i); - packfile_free(backend, p); + struct git_pack_file *p = git_vector_get(&backend->packs, i); + packfile_free(p); } git_vector_free(&backend->packs); @@ -1403,11 +478,8 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) return GIT_ENOMEM; } - backend->window_size = DEFAULT_WINDOW_SIZE; - backend->mapped_limit = DEFAULT_MAPPED_LIMIT; - - git__joinpath(path, objects_dir, "pack"); - if (gitfo_isdir(path) == GIT_SUCCESS) { + git_path_join(path, objects_dir, "pack"); + if (git_futils_isdir(path) == GIT_SUCCESS) { backend->pack_folder = git__strdup(path); backend->pack_folder_mtime = 0; @@ -1418,6 +490,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) } backend->parent.read = &pack_backend__read; + backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_header = NULL; backend->parent.exists = &pack_backend__exists; backend->parent.free = &pack_backend__free; diff --git a/vendor/libgit2/src/oid.c b/vendor/libgit2/src/oid.c index 86c1e0039..f12ba30b9 100644 --- a/vendor/libgit2/src/oid.c +++ b/vendor/libgit2/src/oid.c @@ -49,19 +49,37 @@ static signed char from_hex[] = { }; static char to_hex[] = "0123456789abcdef"; -int git_oid_mkstr(git_oid *out, const char *str) +int git_oid_fromstrn(git_oid *out, const char *str, size_t length) { size_t p; - for (p = 0; p < sizeof(out->id); p++, str += 2) { - int v = (from_hex[(unsigned char)str[0]] << 4) - | from_hex[(unsigned char)str[1]]; + + if (length > GIT_OID_HEXSZ) + length = GIT_OID_HEXSZ; + + if (length % 2) + length--; + + for (p = 0; p < length; p += 2) { + int v = (from_hex[(unsigned char)str[p + 0]] << 4) + | from_hex[(unsigned char)str[p + 1]]; + if (v < 0) - return GIT_ENOTOID; - out->id[p] = (unsigned char)v; + return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash"); + + out->id[p / 2] = (unsigned char)v; } + + for (; p < GIT_OID_HEXSZ; p += 2) + out->id[p / 2] = 0x0; + return GIT_SUCCESS; } +int git_oid_fromstr(git_oid *out, const char *str) +{ + return git_oid_fromstrn(out, str, GIT_OID_HEXSZ); +} + GIT_INLINE(char) *fmt_one(char *str, unsigned int val) { *str++ = to_hex[val >> 4]; @@ -118,7 +136,7 @@ char *git_oid_to_string(char *out, size_t n, const git_oid *oid) return out; } -int git__parse_oid(git_oid *oid, const char **buffer_out, +int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header) { const size_t sha_len = GIT_OID_HEXSZ; @@ -127,37 +145,33 @@ int git__parse_oid(git_oid *oid, const char **buffer_out, const char *buffer = *buffer_out; if (buffer + (header_len + sha_len + 1) > buffer_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer too small"); if (memcmp(buffer, header, header_len) != 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer and header do not match"); if (buffer[header_len + sha_len] != '\n') - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer not terminated correctly"); - if (git_oid_mkstr(oid, buffer + header_len) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + if (git_oid_fromstr(oid, buffer + header_len) < GIT_SUCCESS) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Failed to generate sha1"); *buffer_out = buffer + (header_len + sha_len + 1); return GIT_SUCCESS; } -int git__write_oid(git_odb_stream *stream, const char *header, const git_oid *oid) +void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid) { - char hex_oid[42]; - - git_oid_fmt(hex_oid + 1, oid); - - hex_oid[0] = ' '; - hex_oid[41] = '\n'; + char hex_oid[GIT_OID_HEXSZ]; - stream->write(stream, header, strlen(header)); - stream->write(stream, hex_oid, 42); - return GIT_SUCCESS; + git_oid_fmt(hex_oid, oid); + git_buf_puts(buf, header); + git_buf_put(buf, hex_oid, GIT_OID_HEXSZ); + git_buf_putc(buf, '\n'); } -void git_oid_mkraw(git_oid *out, const unsigned char *raw) +void git_oid_fromraw(git_oid *out, const unsigned char *raw) { memcpy(out->id, raw, sizeof(out->id)); } @@ -172,6 +186,25 @@ int git_oid_cmp(const git_oid *a, const git_oid *b) return memcmp(a->id, b->id, sizeof(a->id)); } +int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len) +{ + const unsigned char *a = oid_a->id; + const unsigned char *b = oid_b->id; + + do { + if (*a != *b) + return 1; + a++; + b++; + len -= 2; + } while (len > 1); + + if (len) + if ((*a ^ *b) & 0xf0) + return 1; + + return 0; +} typedef short node_index; @@ -268,7 +301,7 @@ void git_oid_shorten_free(git_oid_shorten *os) * * - Each normal node points to 16 children (one for each possible * character in the oid). This is *not* stored in an array of - * pointers, because in a 64-bit arch this would be sucking + * pointers, because in a 64-bit arch this would be sucking * 16*sizeof(void*) = 128 bytes of memory per node, which is fucking * insane. What we do is store Node Indexes, and use these indexes * to look up each node in the om->index array. These indexes are @@ -304,6 +337,9 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) if (os->full) return GIT_ENOMEM; + if (text_oid == NULL) + return os->min_length; + idx = 0; is_leaf = 0; @@ -312,7 +348,7 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid) trie_node *node; if (c == -1) - return GIT_ENOTOID; + return git__throw(GIT_ENOTOID, "Failed to shorten OID. Invalid hex value"); node = &os->nodes[idx]; diff --git a/vendor/libgit2/src/pack.c b/vendor/libgit2/src/pack.c new file mode 100644 index 000000000..d882516be --- /dev/null +++ b/vendor/libgit2/src/pack.c @@ -0,0 +1,804 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "mwindow.h" +#include "odb.h" +#include "pack.h" +#include "delta-apply.h" +#include "sha1_lookup.h" + +#include "git2/oid.h" +#include "git2/zlib.h" + +static int packfile_open(struct git_pack_file *p); +static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n); +int packfile_unpack_compressed( + git_rawobj *obj, + struct git_pack_file *p, + git_mwindow **w_curs, + off_t *curpos, + size_t size, + git_otype type); + +/* Can find the offset of an object given + * a prefix of an identifier. + * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid + * is ambiguous within the pack. + * This method assumes that len is between + * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ. + */ +static int pack_entry_find_offset( + off_t *offset_out, + git_oid *found_oid, + struct git_pack_file *p, + const git_oid *short_oid, + unsigned int len); + +/*********************************************************** + * + * PACK INDEX METHODS + * + ***********************************************************/ + +static void pack_index_free(struct git_pack_file *p) +{ + if (p->index_map.data) { + git_futils_mmap_free(&p->index_map); + p->index_map.data = NULL; + } +} + +static int pack_index_check(const char *path, struct git_pack_file *p) +{ + struct git_pack_idx_header *hdr; + uint32_t version, nr, i, *index; + + void *idx_map; + size_t idx_size; + + struct stat st; + + /* TODO: properly open the file without access time */ + git_file fd = p_open(path, O_RDONLY /*| O_NOATIME */); + + int error; + + if (fd < 0) + return git__throw(GIT_EOSERR, "Failed to check index. File missing or corrupted"); + + if (p_fstat(fd, &st) < GIT_SUCCESS) { + p_close(fd); + return git__throw(GIT_EOSERR, "Failed to check index. File appears to be corrupted"); + } + + if (!git__is_sizet(st.st_size)) + return GIT_ENOMEM; + + idx_size = (size_t)st.st_size; + + if (idx_size < 4 * 256 + 20 + 20) { + p_close(fd); + return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted"); + } + + error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size); + p_close(fd); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to check index"); + + hdr = idx_map = p->index_map.data; + + if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) { + version = ntohl(hdr->idx_version); + + if (version < 2 || version > 2) { + git_futils_mmap_free(&p->index_map); + return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Unsupported index version"); + } + + } else + version = 1; + + nr = 0; + index = idx_map; + + if (version > 1) + index += 2; /* skip index header */ + + for (i = 0; i < 256; i++) { + uint32_t n = ntohl(index[i]); + if (n < nr) { + git_futils_mmap_free(&p->index_map); + return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Index is non-monotonic"); + } + nr = n; + } + + if (version == 1) { + /* + * Total size: + * - 256 index entries 4 bytes each + * - 24-byte entries * nr (20-byte sha1 + 4-byte offset) + * - 20-byte SHA1 of the packfile + * - 20-byte SHA1 file checksum + */ + if (idx_size != 4*256 + nr * 24 + 20 + 20) { + git_futils_mmap_free(&p->index_map); + return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted"); + } + } else if (version == 2) { + /* + * Minimum size: + * - 8 bytes of header + * - 256 index entries 4 bytes each + * - 20-byte sha1 entry * nr + * - 4-byte crc entry * nr + * - 4-byte offset entry * nr + * - 20-byte SHA1 of the packfile + * - 20-byte SHA1 file checksum + * And after the 4-byte offset table might be a + * variable sized table containing 8-byte entries + * for offsets larger than 2^31. + */ + unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20; + unsigned long max_size = min_size; + + if (nr) + max_size += (nr - 1)*8; + + if (idx_size < min_size || idx_size > max_size) { + git_futils_mmap_free(&p->index_map); + return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Wrong index size"); + } + + /* Make sure that off_t is big enough to access the whole pack... + * Is this an issue in libgit2? It shouldn't. */ + if (idx_size != min_size && (sizeof(off_t) <= 4)) { + git_futils_mmap_free(&p->index_map); + return git__throw(GIT_EOSERR, "Failed to check index. off_t not big enough to access the whole pack"); + } + } + + p->index_version = version; + p->num_objects = nr; + return GIT_SUCCESS; +} + +static int pack_index_open(struct git_pack_file *p) +{ + char *idx_name; + int error; + + if (p->index_map.data) + return GIT_SUCCESS; + + idx_name = git__strdup(p->pack_name); + strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx"); + + error = pack_index_check(idx_name, p); + free(idx_name); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to open index"); +} + +static unsigned char *pack_window_open( + struct git_pack_file *p, + git_mwindow **w_cursor, + off_t offset, + unsigned int *left) +{ + if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS) + return NULL; + + /* Since packfiles end in a hash of their content and it's + * pointless to ask for an offset into the middle of that + * hash, and the pack_window_contains function above wouldn't match + * don't allow an offset too close to the end of the file. + */ + if (offset > (p->mwf.size - 20)) + return NULL; + + return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left); + } + +static unsigned long packfile_unpack_header1( + size_t *sizep, + git_otype *type, + const unsigned char *buf, + unsigned long len) +{ + unsigned shift; + unsigned long size, c; + unsigned long used = 0; + + c = buf[used++]; + *type = (c >> 4) & 7; + size = c & 15; + shift = 4; + while (c & 0x80) { + if (len <= used || bitsizeof(long) <= shift) + return 0; + + c = buf[used++]; + size += (c & 0x7f) << shift; + shift += 7; + } + + *sizep = (size_t)size; + return used; +} + +int git_packfile_unpack_header( + size_t *size_p, + git_otype *type_p, + git_mwindow_file *mwf, + git_mwindow **w_curs, + off_t *curpos) +{ + unsigned char *base; + unsigned int left; + unsigned long used; + + /* pack_window_open() assures us we have [base, base + 20) available + * as a range that we can look at at. (Its actually the hash + * size that is assured.) With our object header encoding + * the maximum deflated object size is 2^137, which is just + * insane, so we know won't exceed what we have been given. + */ +// base = pack_window_open(p, w_curs, *curpos, &left); + base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left); + if (base == NULL) + return GIT_ENOMEM; + + used = packfile_unpack_header1(size_p, type_p, base, left); + + if (used == 0) + return git__throw(GIT_EOBJCORRUPTED, "Header length is zero"); + + *curpos += used; + return GIT_SUCCESS; +} + +static int packfile_unpack_delta( + git_rawobj *obj, + struct git_pack_file *p, + git_mwindow **w_curs, + off_t *curpos, + size_t delta_size, + git_otype delta_type, + off_t obj_offset) +{ + off_t base_offset; + git_rawobj base, delta; + int error; + + base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset); + if (base_offset == 0) + return git__throw(GIT_EOBJCORRUPTED, "Delta offset is zero"); + if (base_offset < 0) + return git__rethrow(base_offset, "Failed to get delta base"); + + git_mwindow_close(w_curs); + error = git_packfile_unpack(&base, p, &base_offset); + + /* + * TODO: git.git tries to load the base from other packfiles + * or loose objects. + * + * We'll need to do this in order to support thin packs. + */ + if (error < GIT_SUCCESS) + return git__rethrow(error, "Corrupted delta"); + + error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type); + if (error < GIT_SUCCESS) { + free(base.data); + return git__rethrow(error, "Corrupted delta"); + } + + obj->type = base.type; + error = git__delta_apply(obj, + base.data, base.len, + delta.data, delta.len); + + free(base.data); + free(delta.data); + + /* TODO: we might want to cache this shit. eventually */ + //add_delta_base_cache(p, base_offset, base, base_size, *type); + return error; /* error set by git__delta_apply */ +} + +int git_packfile_unpack( + git_rawobj *obj, + struct git_pack_file *p, + off_t *obj_offset) +{ + git_mwindow *w_curs = NULL; + off_t curpos = *obj_offset; + int error; + + size_t size = 0; + git_otype type; + + /* + * TODO: optionally check the CRC on the packfile + */ + + obj->data = NULL; + obj->len = 0; + obj->type = GIT_OBJ_BAD; + + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to unpack packfile"); + + switch (type) { + case GIT_OBJ_OFS_DELTA: + case GIT_OBJ_REF_DELTA: + error = packfile_unpack_delta( + obj, p, &w_curs, &curpos, + size, type, *obj_offset); + break; + + case GIT_OBJ_COMMIT: + case GIT_OBJ_TREE: + case GIT_OBJ_BLOB: + case GIT_OBJ_TAG: + error = packfile_unpack_compressed( + obj, p, &w_curs, &curpos, + size, type); + break; + + default: + error = GIT_EOBJCORRUPTED; + break; + } + + git_mwindow_close(&w_curs); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to unpack object"); + + *obj_offset = curpos; + return GIT_SUCCESS; +} + +int packfile_unpack_compressed( + git_rawobj *obj, + struct git_pack_file *p, + git_mwindow **w_curs, + off_t *curpos, + size_t size, + git_otype type) +{ + int st; + z_stream stream; + unsigned char *buffer, *in; + + buffer = git__malloc(size + 1); + memset(buffer, 0x0, size + 1); + + memset(&stream, 0, sizeof(stream)); + stream.next_out = buffer; + stream.avail_out = size + 1; + + st = inflateInit(&stream); + if (st != Z_OK) { + free(buffer); + return git__throw(GIT_EZLIB, "Error in zlib"); + } + + do { + in = pack_window_open(p, w_curs, *curpos, &stream.avail_in); + stream.next_in = in; + st = inflate(&stream, Z_FINISH); + + if (!stream.avail_out) + break; /* the payload is larger than it should be */ + + *curpos += stream.next_in - in; + } while (st == Z_OK || st == Z_BUF_ERROR); + + inflateEnd(&stream); + + if ((st != Z_STREAM_END) || stream.total_out != size) { + free(buffer); + return git__throw(GIT_EZLIB, "Error in zlib"); + } + + obj->type = type; + obj->len = size; + obj->data = buffer; + return GIT_SUCCESS; +} + +/* + * curpos is where the data starts, delta_obj_offset is the where the + * header starts + */ +off_t get_delta_base( + struct git_pack_file *p, + git_mwindow **w_curs, + off_t *curpos, + git_otype type, + off_t delta_obj_offset) +{ + unsigned char *base_info = pack_window_open(p, w_curs, *curpos, NULL); + off_t base_offset; + git_oid unused; + + /* pack_window_open() assured us we have [base_info, base_info + 20) + * as a range that we can look at without walking off the + * end of the mapped window. Its actually the hash size + * that is assured. An OFS_DELTA longer than the hash size + * is stupid, as then a REF_DELTA would be smaller to store. + */ + if (type == GIT_OBJ_OFS_DELTA) { + unsigned used = 0; + unsigned char c = base_info[used++]; + base_offset = c & 127; + while (c & 128) { + base_offset += 1; + if (!base_offset || MSB(base_offset, 7)) + return 0; /* overflow */ + c = base_info[used++]; + base_offset = (base_offset << 7) + (c & 127); + } + base_offset = delta_obj_offset - base_offset; + if (base_offset <= 0 || base_offset >= delta_obj_offset) + return 0; /* out of bound */ + *curpos += used; + } else if (type == GIT_OBJ_REF_DELTA) { + /* If we have the cooperative cache, search in it first */ + if (p->has_cache) { + int pos; + struct git_pack_entry key; + + git_oid_fromraw(&key.sha1, base_info); + pos = git_vector_bsearch(&p->cache, &key); + if (pos >= 0) { + *curpos += 20; + return ((struct git_pack_entry *)git_vector_get(&p->cache, pos))->offset; + } + } + /* The base entry _must_ be in the same pack */ + if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < GIT_SUCCESS) + return git__rethrow(GIT_EPACKCORRUPTED, "Base entry delta is not in the same pack"); + *curpos += 20; + } else + return 0; + + return base_offset; +} + +/*********************************************************** + * + * PACKFILE METHODS + * + ***********************************************************/ + +static struct git_pack_file *packfile_alloc(int extra) +{ + struct git_pack_file *p = git__malloc(sizeof(*p) + extra); + memset(p, 0, sizeof(*p)); + p->mwf.fd = -1; + return p; +} + + +void packfile_free(struct git_pack_file *p) +{ + assert(p); + + /* clear_delta_base_cache(); */ + git_mwindow_free_all(&p->mwf); + + if (p->mwf.fd != -1) + p_close(p->mwf.fd); + + pack_index_free(p); + + free(p->bad_object_sha1); + free(p); +} + +static int packfile_open(struct git_pack_file *p) +{ + struct stat st; + struct git_pack_header hdr; + git_oid sha1; + unsigned char *idx_sha1; + + if (!p->index_map.data && pack_index_open(p) < GIT_SUCCESS) + return git__throw(GIT_ENOTFOUND, "Failed to open packfile. File not found"); + + /* TODO: open with noatime */ + p->mwf.fd = p_open(p->pack_name, O_RDONLY); + if (p->mwf.fd < 0 || p_fstat(p->mwf.fd, &st) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to open packfile. File appears to be corrupted"); + + if (git_mwindow_file_register(&p->mwf) < GIT_SUCCESS) { + p_close(p->mwf.fd); + return git__throw(GIT_ERROR, "Failed to register packfile windows"); + } + + /* If we created the struct before we had the pack we lack size. */ + if (!p->mwf.size) { + if (!S_ISREG(st.st_mode)) + goto cleanup; + p->mwf.size = (off_t)st.st_size; + } else if (p->mwf.size != st.st_size) + goto cleanup; + +#if 0 + /* We leave these file descriptors open with sliding mmap; + * there is no point keeping them open across exec(), though. + */ + fd_flag = fcntl(p->mwf.fd, F_GETFD, 0); + if (fd_flag < 0) + return error("cannot determine file descriptor flags"); + + fd_flag |= FD_CLOEXEC; + if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1) + return GIT_EOSERR; +#endif + + /* Verify we recognize this pack file format. */ + if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < GIT_SUCCESS) + goto cleanup; + + if (hdr.hdr_signature != htonl(PACK_SIGNATURE)) + goto cleanup; + + if (!pack_version_ok(hdr.hdr_version)) + goto cleanup; + + /* Verify the pack matches its index. */ + if (p->num_objects != ntohl(hdr.hdr_entries)) + goto cleanup; + + if (p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1) + goto cleanup; + + if (p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < GIT_SUCCESS) + goto cleanup; + + idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40; + + if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) != 0) + goto cleanup; + + return GIT_SUCCESS; + +cleanup: + p_close(p->mwf.fd); + p->mwf.fd = -1; + return git__throw(GIT_EPACKCORRUPTED, "Failed to open packfile. Pack is corrupted"); +} + +int git_packfile_check(struct git_pack_file **pack_out, const char *path) +{ + struct stat st; + struct git_pack_file *p; + size_t path_len; + + *pack_out = NULL; + path_len = strlen(path); + p = packfile_alloc(path_len + 2); + + /* + * Make sure a corresponding .pack file exists and that + * the index looks sane. + */ + path_len -= strlen(".idx"); + if (path_len < 1) { + free(p); + return git__throw(GIT_ENOTFOUND, "Failed to check packfile. Wrong path name"); + } + + memcpy(p->pack_name, path, path_len); + + strcpy(p->pack_name + path_len, ".keep"); + if (git_futils_exists(p->pack_name) == GIT_SUCCESS) + p->pack_keep = 1; + + strcpy(p->pack_name + path_len, ".pack"); + if (p_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) { + free(p); + return git__throw(GIT_ENOTFOUND, "Failed to check packfile. File not found"); + } + + /* ok, it looks sane as far as we can check without + * actually mapping the pack file. + */ + p->mwf.size = (off_t)st.st_size; + p->pack_local = 1; + p->mtime = (git_time_t)st.st_mtime; + + /* see if we can parse the sha1 oid in the packfile name */ + if (path_len < 40 || + git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < GIT_SUCCESS) + memset(&p->sha1, 0x0, GIT_OID_RAWSZ); + + *pack_out = p; + return GIT_SUCCESS; +} + +/*********************************************************** + * + * PACKFILE ENTRY SEARCH INTERNALS + * + ***********************************************************/ + +static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n) +{ + const unsigned char *index = p->index_map.data; + index += 4 * 256; + if (p->index_version == 1) { + return ntohl(*((uint32_t *)(index + 24 * n))); + } else { + uint32_t off; + index += 8 + p->num_objects * (20 + 4); + off = ntohl(*((uint32_t *)(index + 4 * n))); + if (!(off & 0x80000000)) + return off; + index += p->num_objects * 4 + (off & 0x7fffffff) * 8; + return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) | + ntohl(*((uint32_t *)(index + 4))); + } +} + +static int pack_entry_find_offset( + off_t *offset_out, + git_oid *found_oid, + struct git_pack_file *p, + const git_oid *short_oid, + unsigned int len) +{ + const uint32_t *level1_ofs = p->index_map.data; + const unsigned char *index = p->index_map.data; + unsigned hi, lo, stride; + int pos, found = 0; + const unsigned char *current = 0; + + *offset_out = 0; + + if (index == NULL) { + int error; + + if ((error = pack_index_open(p)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to find offset for pack entry"); + + assert(p->index_map.data); + + index = p->index_map.data; + level1_ofs = p->index_map.data; + } + + if (p->index_version > 1) { + level1_ofs += 2; + index += 8; + } + + index += 4 * 256; + hi = ntohl(level1_ofs[(int)short_oid->id[0]]); + lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1])); + + if (p->index_version > 1) { + stride = 20; + } else { + stride = 24; + index += 4; + } + +#ifdef INDEX_DEBUG_LOOKUP + printf("%02x%02x%02x... lo %u hi %u nr %d\n", + short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects); +#endif + + /* Use git.git lookup code */ + pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id); + + if (pos >= 0) { + /* An object matching exactly the oid was found */ + found = 1; + current = index + pos * stride; + } else { + /* No object was found */ + /* pos refers to the object with the "closest" oid to short_oid */ + pos = - 1 - pos; + if (pos < (int)p->num_objects) { + current = index + pos * stride; + + if (!git_oid_ncmp(short_oid, (const git_oid *)current, len)) { + found = 1; + } + } + } + + if (found && pos + 1 < (int)p->num_objects) { + /* Check for ambiguousity */ + const unsigned char *next = current + stride; + + if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) { + found = 2; + } + } + + if (!found) { + return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found"); + } else if (found > 1) { + return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find offset for pack entry. Ambiguous sha1 prefix within pack"); + } else { + *offset_out = nth_packed_object_offset(p, pos); + git_oid_fromraw(found_oid, current); + +#ifdef INDEX_DEBUG_LOOKUP + unsigned char hex_sha1[GIT_OID_HEXSZ + 1]; + git_oid_fmt(hex_sha1, found_oid); + hex_sha1[GIT_OID_HEXSZ] = '\0'; + printf("found lo=%d %s\n", lo, hex_sha1); +#endif + return GIT_SUCCESS; + } +} + +int git_pack_entry_find( + struct git_pack_entry *e, + struct git_pack_file *p, + const git_oid *short_oid, + unsigned int len) +{ + off_t offset; + git_oid found_oid; + int error; + + assert(p); + + if (len == GIT_OID_HEXSZ && p->num_bad_objects) { + unsigned i; + for (i = 0; i < p->num_bad_objects; i++) + if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0) + return git__throw(GIT_ERROR, "Failed to find pack entry. Bad object found"); + } + + error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to find pack entry. Couldn't find offset"); + + /* we found a unique entry in the index; + * make sure the packfile backing the index + * still exists on disk */ + if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to find pack entry. Packfile doesn't exist on disk"); + + e->offset = offset; + e->p = p; + + git_oid_cpy(&e->sha1, &found_oid); + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/pack.h b/vendor/libgit2/src/pack.h new file mode 100644 index 000000000..164086fdf --- /dev/null +++ b/vendor/libgit2/src/pack.h @@ -0,0 +1,115 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDE_pack_h__ +#define INCLUDE_pack_h__ + +#include "git2/oid.h" + +#include "common.h" +#include "map.h" +#include "mwindow.h" +#include "odb.h" + +#define PACK_SIGNATURE 0x5041434b /* "PACK" */ +#define PACK_VERSION 2 +#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3)) +struct git_pack_header { + uint32_t hdr_signature; + uint32_t hdr_version; + uint32_t hdr_entries; +}; + +/* + * The first four bytes of index formats later than version 1 should + * start with this signature, as all older git binaries would find this + * value illegal and abort reading the file. + * + * This is the case because the number of objects in a packfile + * cannot exceed 1,431,660,000 as every object would need at least + * 3 bytes of data and the overall packfile cannot exceed 4 GiB with + * version 1 of the index file due to the offsets limited to 32 bits. + * Clearly the signature exceeds this maximum. + * + * Very old git binaries will also compare the first 4 bytes to the + * next 4 bytes in the index and abort with a "non-monotonic index" + * error if the second 4 byte word is smaller than the first 4 + * byte word. This would be true in the proposed future index + * format as idx_signature would be greater than idx_version. + */ + +#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */ + +struct git_pack_idx_header { + uint32_t idx_signature; + uint32_t idx_version; +}; + +struct git_pack_file { + git_mwindow_file mwf; + git_map index_map; + + uint32_t num_objects; + uint32_t num_bad_objects; + git_oid *bad_object_sha1; /* array of git_oid */ + + int index_version; + git_time_t mtime; + unsigned pack_local:1, pack_keep:1, has_cache:1; + git_oid sha1; + git_vector cache; + + /* something like ".git/objects/pack/xxxxx.pack" */ + char pack_name[GIT_FLEX_ARRAY]; /* more */ +}; + +struct git_pack_entry { + off_t offset; + git_oid sha1; + struct git_pack_file *p; +}; + +int git_packfile_unpack_header( + size_t *size_p, + git_otype *type_p, + git_mwindow_file *mwf, + git_mwindow **w_curs, + off_t *curpos); + +int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, off_t *obj_offset); + +off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs, + off_t *curpos, git_otype type, + off_t delta_obj_offset); + +void packfile_free(struct git_pack_file *p); +int git_packfile_check(struct git_pack_file **pack_out, const char *path); +int git_pack_entry_find( + struct git_pack_entry *e, + struct git_pack_file *p, + const git_oid *short_oid, + unsigned int len); + +#endif diff --git a/vendor/libgit2/src/path.c b/vendor/libgit2/src/path.c new file mode 100644 index 000000000..374694432 --- /dev/null +++ b/vendor/libgit2/src/path.c @@ -0,0 +1,264 @@ +#include "common.h" +#include "path.h" +#include "posix.h" + +#include +#include +#include + +/* + * Based on the Android implementation, BSD licensed. + * Check http://android.git.kernel.org/ + */ +int git_path_basename_r(char *buffer, size_t bufflen, const char *path) +{ + const char *endp, *startp; + int len, result; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + startp = "."; + len = 1; + goto Exit; + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* All slashes becomes "/" */ + if (endp == path && *endp == '/') { + startp = "/"; + len = 1; + goto Exit; + } + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + len = endp - startp +1; + +Exit: + result = len; + if (buffer == NULL) { + return result; + } + if (len > (int)bufflen-1) { + len = (int)bufflen-1; + result = GIT_ENOMEM; + } + + if (len >= 0) { + memmove(buffer, startp, len); + buffer[len] = 0; + } + return result; +} + +/* + * Based on the Android implementation, BSD licensed. + * Check http://android.git.kernel.org/ + */ +int git_path_dirname_r(char *buffer, size_t bufflen, const char *path) +{ + const char *endp; + int result, len; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + path = "."; + len = 1; + goto Exit; + } + + /* Strip trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + path = (*endp == '/') ? "/" : "."; + len = 1; + goto Exit; + } + + do { + endp--; + } while (endp > path && *endp == '/'); + + len = endp - path +1; + +#ifdef GIT_WIN32 + /* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return + 'C:/' here */ + + if (len == 2 && isalpha(path[0]) && path[1] == ':') { + len = 3; + goto Exit; + } +#endif + +Exit: + result = len; + if (len+1 > GIT_PATH_MAX) { + return GIT_ENOMEM; + } + if (buffer == NULL) + return result; + + if (len > (int)bufflen-1) { + len = (int)bufflen-1; + result = GIT_ENOMEM; + } + + if (len >= 0) { + memmove(buffer, path, len); + buffer[len] = 0; + } + return result; +} + + +char *git_path_dirname(const char *path) +{ + char *dname = NULL; + int len; + + len = (path ? strlen(path) : 0) + 2; + dname = (char *)git__malloc(len); + if (dname == NULL) + return NULL; + + if (git_path_dirname_r(dname, len, path) < GIT_SUCCESS) { + free(dname); + return NULL; + } + + return dname; +} + +char *git_path_basename(const char *path) +{ + char *bname = NULL; + int len; + + len = (path ? strlen(path) : 0) + 2; + bname = (char *)git__malloc(len); + if (bname == NULL) + return NULL; + + if (git_path_basename_r(bname, len, path) < GIT_SUCCESS) { + free(bname); + return NULL; + } + + return bname; +} + + +const char *git_path_topdir(const char *path) +{ + size_t len; + int i; + + assert(path); + len = strlen(path); + + if (!len || path[len - 1] != '/') + return NULL; + + for (i = len - 2; i >= 0; --i) + if (path[i] == '/') + break; + + return &path[i + 1]; +} + +void git_path_join_n(char *buffer_out, int count, ...) +{ + va_list ap; + int i; + char *buffer_start = buffer_out; + + va_start(ap, count); + for (i = 0; i < count; ++i) { + const char *path; + int len; + + path = va_arg(ap, const char *); + + assert((i == 0) || path != buffer_start); + + if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/') + path++; + + if (!*path) + continue; + + len = strlen(path); + memmove(buffer_out, path, len); + buffer_out = buffer_out + len; + + if (i < count - 1 && buffer_out[-1] != '/') + *buffer_out++ = '/'; + } + va_end(ap); + + *buffer_out = '\0'; +} + +int git_path_root(const char *path) +{ + int offset = 0; + +#ifdef GIT_WIN32 + /* Does the root of the path look like a windows drive ? */ + if (isalpha(path[0]) && (path[1] == ':')) + offset += 2; +#endif + + if (*(path + offset) == '/') + return offset; + + return -1; /* Not a real error. Rather a signal than the path is not rooted */ +} + +int git_path_prettify(char *path_out, const char *path, const char *base) +{ + char *result; + + if (base == NULL || git_path_root(path) >= 0) { + result = p_realpath(path, path_out); + } else { + char aux_path[GIT_PATH_MAX]; + git_path_join(aux_path, base, path); + result = p_realpath(aux_path, path_out); + } + + return result ? GIT_SUCCESS : GIT_EOSERR; +} + +int git_path_prettify_dir(char *path_out, const char *path, const char *base) +{ + size_t end; + + if (git_path_prettify(path_out, path, base) < GIT_SUCCESS) + return GIT_EOSERR; + + end = strlen(path_out); + + if (end && path_out[end - 1] != '/') { + path_out[end] = '/'; + path_out[end + 1] = '\0'; + } + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/path.h b/vendor/libgit2/src/path.h new file mode 100644 index 000000000..36e22a768 --- /dev/null +++ b/vendor/libgit2/src/path.h @@ -0,0 +1,81 @@ +/* + * posix.h - Path management methods + */ +#ifndef INCLUDE_path_h__ +#define INCLUDE_path_h__ + +#include "common.h" + +/* + * The dirname() function shall take a pointer to a character string + * that contains a pathname, and return a pointer to a string that is a + * pathname of the parent directory of that file. Trailing '/' characters + * in the path are not counted as part of the path. + * + * If path does not contain a '/', then dirname() shall return a pointer to + * the string ".". If path is a null pointer or points to an empty string, + * dirname() shall return a pointer to the string "." . + * + * The `git_path_dirname` implementation is thread safe. The returned + * string must be manually free'd. + * + * The `git_path_dirname_r` implementation expects a string allocated + * by the user with big enough size. + */ +extern char *git_path_dirname(const char *path); +extern int git_path_dirname_r(char *buffer, size_t bufflen, const char *path); + +/* + * This function returns the basename of the file, which is the last + * part of its full name given by fname, with the drive letter and + * leading directories stripped off. For example, the basename of + * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo. + * + * Trailing slashes and backslashes are significant: the basename of + * c:/foo/bar/ is an empty string after the rightmost slash. + * + * The `git_path_basename` implementation is thread safe. The returned + * string must be manually free'd. + * + * The `git_path_basename_r` implementation expects a string allocated + * by the user with big enough size. + */ +extern char *git_path_basename(const char *path); +extern int git_path_basename_r(char *buffer, size_t bufflen, const char *path); + +extern const char *git_path_topdir(const char *path); + +/** + * Join two paths together. Takes care of properly fixing the + * middle slashes and everything + * + * The paths are joined together into buffer_out; this is expected + * to be an user allocated buffer of `GIT_PATH_MAX` size + */ +extern void git_path_join_n(char *buffer_out, int npath, ...); + +GIT_INLINE(void) git_path_join(char *buffer_out, const char *path_a, const char *path_b) +{ + git_path_join_n(buffer_out, 2, path_a, path_b); +} + +int git_path_root(const char *path); + +int git_path_prettify(char *path_out, const char *path, const char *base); +int git_path_prettify_dir(char *path_out, const char *path, const char *base); + +#ifdef GIT_WIN32 +GIT_INLINE(void) git_path_mkposix(char *path) +{ + while (*path) { + if (*path == '\\') + *path = '/'; + + path++; + } +} +#else +# define git_path_mkposix(p) /* blank */ +#endif + +#endif diff --git a/vendor/libgit2/src/pkt.c b/vendor/libgit2/src/pkt.c new file mode 100644 index 000000000..4eac0411f --- /dev/null +++ b/vendor/libgit2/src/pkt.c @@ -0,0 +1,362 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" + +#include "git2/types.h" +#include "git2/errors.h" +#include "git2/refs.h" +#include "git2/revwalk.h" + +#include "pkt.h" +#include "util.h" +#include "netops.h" +#include "posix.h" + +#include + +#define PKT_LEN_SIZE 4 + +static int flush_pkt(git_pkt **out) +{ + git_pkt *pkt; + + pkt = git__malloc(sizeof(git_pkt)); + if (pkt == NULL) + return GIT_ENOMEM; + + pkt->type = GIT_PKT_FLUSH; + *out = pkt; + + return GIT_SUCCESS; +} + +/* the rest of the line will be useful for multi_ack */ +static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len)) +{ + git_pkt *pkt; + GIT_UNUSED_ARG(line); + GIT_UNUSED_ARG(len); + + pkt = git__malloc(sizeof(git_pkt)); + if (pkt == NULL) + return GIT_ENOMEM; + + pkt->type = GIT_PKT_ACK; + *out = pkt; + + return GIT_SUCCESS; +} + +static int nak_pkt(git_pkt **out) +{ + git_pkt *pkt; + + pkt = git__malloc(sizeof(git_pkt)); + if (pkt == NULL) + return GIT_ENOMEM; + + pkt->type = GIT_PKT_NAK; + *out = pkt; + + return GIT_SUCCESS; +} + +static int pack_pkt(git_pkt **out) +{ + git_pkt *pkt; + + pkt = git__malloc(sizeof(git_pkt)); + if (pkt == NULL) + return GIT_ENOMEM; + + pkt->type = GIT_PKT_PACK; + *out = pkt; + + return GIT_SUCCESS; +} + +/* + * Parse an other-ref line. + */ +static int ref_pkt(git_pkt **out, const char *line, size_t len) +{ + git_pkt_ref *pkt; + int error, has_caps = 0; + + pkt = git__malloc(sizeof(git_pkt_ref)); + if (pkt == NULL) + return GIT_ENOMEM; + + memset(pkt, 0x0, sizeof(git_pkt_ref)); + pkt->type = GIT_PKT_REF; + error = git_oid_fromstr(&pkt->head.oid, line); + if (error < GIT_SUCCESS) { + error = git__throw(error, "Failed to parse reference ID"); + goto out; + } + + /* Check for a bit of consistency */ + if (line[GIT_OID_HEXSZ] != ' ') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP"); + goto out; + } + + /* Jump from the name */ + line += GIT_OID_HEXSZ + 1; + len -= (GIT_OID_HEXSZ + 1); + + if (strlen(line) < len) + has_caps = 1; + + if (line[len - 1] == '\n') + --len; + + pkt->head.name = git__malloc(len + 1); + if (pkt->head.name == NULL) { + error = GIT_ENOMEM; + goto out; + } + memcpy(pkt->head.name, line, len); + pkt->head.name[len] = '\0'; + + if (has_caps) { + pkt->capabilities = strchr(pkt->head.name, '\0') + 1; + } + +out: + if (error < GIT_SUCCESS) + free(pkt); + else + *out = (git_pkt *)pkt; + + return error; +} + +static ssize_t parse_len(const char *line) +{ + char num[PKT_LEN_SIZE + 1]; + int i, error; + long len; + const char *num_end; + + memcpy(num, line, PKT_LEN_SIZE); + num[PKT_LEN_SIZE] = '\0'; + + for (i = 0; i < PKT_LEN_SIZE; ++i) { + if (!isxdigit(num[i])) + return GIT_ENOTNUM; + } + + error = git__strtol32(&len, num, &num_end, 16); + if (error < GIT_SUCCESS) { + return error; + } + + return (unsigned int) len; +} + +/* + * As per the documentation, the syntax is: + * + * pkt-line = data-pkt / flush-pkt + * data-pkt = pkt-len pkt-payload + * pkt-len = 4*(HEXDIG) + * pkt-payload = (pkt-len -4)*(OCTET) + * flush-pkt = "0000" + * + * Which means that the first four bytes are the length of the line, + * in ASCII hexadecimal (including itself) + */ + +int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen) +{ + int error = GIT_SUCCESS; + size_t len; + + /* Not even enough for the length */ + if (bufflen > 0 && bufflen < PKT_LEN_SIZE) + return GIT_ESHORTBUFFER; + + error = parse_len(line); + if (error < GIT_SUCCESS) { + /* + * If we fail to parse the length, it might be because the + * server is trying to send us the packfile already. + */ + if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { + *out = line; + return pack_pkt(head); + } + + return git__throw(error, "Failed to parse pkt length"); + } + + len = error; + + /* + * If we were given a buffer length, then make sure there is + * enough in the buffer to satisfy this line + */ + if (bufflen > 0 && bufflen < len) + return GIT_ESHORTBUFFER; + + line += PKT_LEN_SIZE; + /* + * TODO: How do we deal with empty lines? Try again? with the next + * line? + */ + if (len == PKT_LEN_SIZE) { + *out = line; + return GIT_SUCCESS; + } + + if (len == 0) { /* Flush pkt */ + *out = line; + return flush_pkt(head); + } + + len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ + + /* Assming the minimal size is actually 4 */ + if (!git__prefixcmp(line, "ACK")) + error = ack_pkt(head, line, len); + else if (!git__prefixcmp(line, "NAK")) + error = nak_pkt(head); + else + error = ref_pkt(head, line, len); + + *out = line + len; + + return error; +} + +void git_pkt_free(git_pkt *pkt) +{ + if(pkt->type == GIT_PKT_REF) { + git_pkt_ref *p = (git_pkt_ref *) pkt; + free(p->head.name); + } + + free(pkt); +} + +int git_pkt_send_flush(int s) +{ + char flush[] = "0000"; + + return gitno_send(s, flush, strlen(flush), 0); +} + +static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, int fd) +{ + char capstr[20]; /* Longer than we need */ + char oid[GIT_OID_HEXSZ +1] = {0}, *cmd; + int error, len; + + if (caps->ofs_delta) + strcpy(capstr, GIT_CAP_OFS_DELTA); + + len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + strlen(capstr) + 1 /* LF */; + cmd = git__malloc(len + 1); + if (cmd == NULL) + return GIT_ENOMEM; + + git_oid_fmt(oid, &head->oid); + memset(cmd, 0x0, len + 1); + p_snprintf(cmd, len + 1, "%04xwant %s%c%s\n", len, oid, 0, capstr); + error = gitno_send(fd, cmd, len, 0); + free(cmd); + return error; +} + +/* + * All "want" packets have the same length and format, so what we do + * is overwrite the OID each time. + */ +#define WANT_PREFIX "0032want " + +int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd) +{ + unsigned int i = 0; + int error = GIT_SUCCESS; + char buf[sizeof(WANT_PREFIX) + GIT_OID_HEXSZ + 1]; + git_remote_head *head; + + memcpy(buf, WANT_PREFIX, strlen(WANT_PREFIX)); + buf[sizeof(buf) - 2] = '\n'; + buf[sizeof(buf) - 1] = '\0'; + + /* If there are common caps, find the first one */ + if (caps->common) { + for (; i < refs->len; ++i) { + head = refs->heads[i]; + if (head->local) + continue; + else + break; + } + + error = send_want_with_caps(refs->heads[i], caps, fd); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to send want pkt with caps"); + /* Increase it here so it's correct whether we run this or not */ + i++; + } + + /* Continue from where we left off */ + for (; i < refs->len; ++i) { + head = refs->heads[i]; + if (head->local) + continue; + + git_oid_fmt(buf + strlen(WANT_PREFIX), &head->oid); + error = gitno_send(fd, buf, strlen(buf), 0); + return git__rethrow(error, "Failed to send want pkt"); + } + + return git_pkt_send_flush(fd); +} + +/* + * TODO: this should be a more generic function, maybe to be used by + * git_pkt_send_wants, as it's not performance-critical + */ +#define HAVE_PREFIX "0032have " + +int git_pkt_send_have(git_oid *oid, int fd) +{ + char buf[] = "0032have 0000000000000000000000000000000000000000\n"; + + git_oid_fmt(buf + strlen(HAVE_PREFIX), oid); + return gitno_send(fd, buf, strlen(buf), 0); +} + +int git_pkt_send_done(int fd) +{ + char buf[] = "0009done\n"; + + return gitno_send(fd, buf, strlen(buf), 0); +} diff --git a/vendor/libgit2/src/pkt.h b/vendor/libgit2/src/pkt.h new file mode 100644 index 000000000..1c6a20659 --- /dev/null +++ b/vendor/libgit2/src/pkt.h @@ -0,0 +1,84 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDE_pkt_h__ +#define INCLUDE_pkt_h__ + +#include "common.h" +#include "transport.h" +#include "git2/net.h" + +enum git_pkt_type { + GIT_PKT_CMD, + GIT_PKT_FLUSH, + GIT_PKT_REF, + GIT_PKT_HAVE, + GIT_PKT_ACK, + GIT_PKT_NAK, + GIT_PKT_PACK, +}; + +/* Used for multi-ack */ +enum git_ack_status { + GIT_ACK_NONE, + GIT_ACK_CONTINUE, + GIT_ACK_COMMON, + GIT_ACK_READY +}; + +/* This would be a flush pkt */ +typedef struct { + enum git_pkt_type type; +} git_pkt; + +struct git_pkt_cmd { + enum git_pkt_type type; + char *cmd; + char *path; + char *host; +}; + +/* This is a pkt-line with some info in it */ +typedef struct { + enum git_pkt_type type; + git_remote_head head; + char *capabilities; +} git_pkt_ref; + +/* Useful later */ +typedef struct { + enum git_pkt_type type; + git_oid oid; + enum git_ack_status status; +} git_pkt_ack; + +int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); +int git_pkt_send_flush(int s); +int git_pkt_send_done(int s); +int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd); +int git_pkt_send_have(git_oid *oid, int fd); +void git_pkt_free(git_pkt *pkt); + +#endif diff --git a/vendor/libgit2/src/posix.c b/vendor/libgit2/src/posix.c new file mode 100644 index 000000000..4bb8c3246 --- /dev/null +++ b/vendor/libgit2/src/posix.c @@ -0,0 +1,74 @@ +#include "common.h" +#include "posix.h" +#include "path.h" +#include +#include + +int p_open(const char *path, int flags) +{ + return open(path, flags | O_BINARY); +} + +int p_creat(const char *path, int mode) +{ + return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); +} + +int p_read(git_file fd, void *buf, size_t cnt) +{ + char *b = buf; + while (cnt) { + ssize_t r = read(fd, b, cnt); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return GIT_EOSERR; + } + if (!r) + break; + cnt -= r; + b += r; + } + return (int)(b - (char *)buf); +} + +int p_write(git_file fd, const void *buf, size_t cnt) +{ + const char *b = buf; + while (cnt) { + ssize_t r = write(fd, b, cnt); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return GIT_EOSERR; + } + if (!r) { + errno = EPIPE; + return GIT_EOSERR; + } + cnt -= r; + b += r; + } + return GIT_SUCCESS; +} + +int p_getcwd(char *buffer_out, size_t size) +{ + char *cwd_buffer; + + assert(buffer_out && size > 0); + +#ifdef GIT_WIN32 + cwd_buffer = _getcwd(buffer_out, size); +#else + cwd_buffer = getcwd(buffer_out, size); +#endif + + if (cwd_buffer == NULL) + return git__throw(GIT_EOSERR, "Failed to retrieve current working directory"); + + git_path_mkposix(buffer_out); + + git_path_join(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/posix.h b/vendor/libgit2/src/posix.h new file mode 100644 index 000000000..f1424f8d3 --- /dev/null +++ b/vendor/libgit2/src/posix.h @@ -0,0 +1,56 @@ +/* + * posix.h - OS agnostic POSIX calls + */ +#ifndef INCLUDE_posix_h__ +#define INCLUDE_posix_h__ + +#include "common.h" +#include +#include + +#define S_IFGITLINK 0160000 +#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) + +#if !defined(O_BINARY) +#define O_BINARY 0 +#endif + +typedef int git_file; + +/** + * Standard POSIX Methods + * + * All the methods starting with the `p_` prefix are + * direct ports of the standard POSIX methods. + * + * Some of the methods are slightly wrapped to provide + * saner defaults. Some of these methods are emulated + * in Windows platforns. + * + * Use your manpages to check the docs on these. + * Straightforward + */ +extern int p_open(const char *path, int flags); +extern int p_creat(const char *path, int mode); +extern int p_read(git_file fd, void *buf, size_t cnt); +extern int p_write(git_file fd, const void *buf, size_t cnt); +extern int p_getcwd(char *buffer_out, size_t size); + +#define p_lseek(f,n,w) lseek(f, n, w) +#define p_stat(p,b) stat(p, b) +#define p_fstat(f,b) fstat(f, b) +#define p_chdir(p) chdir(p) +#define p_rmdir(p) rmdir(p) +#define p_chmod(p,m) chmod(p, m) +#define p_close(fd) close(fd) + +/** + * Platform-dependent methods + */ +#ifdef GIT_WIN32 +# include "win32/posix.h" +#else +# include "unix/posix.h" +#endif + +#endif diff --git a/vendor/libgit2/src/pqueue.c b/vendor/libgit2/src/pqueue.c index 6307175e3..9883a35d8 100644 --- a/vendor/libgit2/src/pqueue.c +++ b/vendor/libgit2/src/pqueue.c @@ -11,7 +11,7 @@ * Copyright 2010 Volkan Yazıcı * Copyright 2006-2010 The Apache Software Foundation * - * This file is licensed under the Apache 2.0 license, which + * This file is licensed under the Apache 2.0 license, which * supposedly makes it compatible with the GPLv2 that libgit2 uses. * * Check the Apache license at: diff --git a/vendor/libgit2/src/pqueue.h b/vendor/libgit2/src/pqueue.h index 7a1394803..ef8362c33 100644 --- a/vendor/libgit2/src/pqueue.h +++ b/vendor/libgit2/src/pqueue.h @@ -11,7 +11,7 @@ * Copyright 2010 Volkan Yazıcı * Copyright 2006-2010 The Apache Software Foundation * - * This file is licensed under the Apache 2.0 license, which + * This file is licensed under the Apache 2.0 license, which * supposedly makes it compatible with the GPLv2 that libgit2 uses. * * Check the Apache license at: diff --git a/vendor/libgit2/src/reflog.c b/vendor/libgit2/src/reflog.c new file mode 100644 index 000000000..d28e5cba1 --- /dev/null +++ b/vendor/libgit2/src/reflog.c @@ -0,0 +1,296 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "reflog.h" +#include "repository.h" +#include "filebuf.h" +#include "signature.h" + +static int reflog_init(git_reflog **reflog, git_reference *ref) +{ + git_reflog *log; + + *reflog = NULL; + + log = git__malloc(sizeof(git_reflog)); + if (log == NULL) + return GIT_ENOMEM; + + memset(log, 0x0, sizeof(git_reflog)); + + log->ref_name = git__strdup(ref->name); + + if (git_vector_init(&log->entries, 0, NULL) < 0) { + free(log->ref_name); + free(log); + return GIT_ENOMEM; + } + + *reflog = log; + + return GIT_SUCCESS; +} + +static int reflog_write(const char *log_path, const char *oid_old, + const char *oid_new, const git_signature *committer, + const char *msg) +{ + int error; + git_buf log = GIT_BUF_INIT; + git_filebuf fbuf; + + assert(log_path && oid_old && oid_new && committer); + + git_buf_puts(&log, oid_old); + git_buf_putc(&log, ' '); + + git_buf_puts(&log, oid_new); + + git_signature__writebuf(&log, " ", committer); + log.size--; /* drop LF */ + + if (msg) { + if (strchr(msg, '\n')) { + git_buf_free(&log); + return git__throw(GIT_ERROR, "Reflog message cannot contain newline"); + } + + git_buf_putc(&log, '\t'); + git_buf_puts(&log, msg); + } + + git_buf_putc(&log, '\n'); + + if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) { + git_buf_free(&log); + return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path); + } + + git_filebuf_write(&fbuf, log.ptr, log.size); + error = git_filebuf_commit(&fbuf); + + git_buf_free(&log); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog"); +} + +static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size) +{ + int error = GIT_SUCCESS; + const char *ptr; + git_reflog_entry *entry; + +#define seek_forward(_increase) { \ + if (_increase >= buf_size) { \ + if (entry->committer) \ + free(entry->committer); \ + free(entry); \ + return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \ + } \ + buf += _increase; \ + buf_size -= _increase; \ +} + + while (buf_size > GIT_REFLOG_SIZE_MIN) { + entry = git__malloc(sizeof(git_reflog_entry)); + if (entry == NULL) + return GIT_ENOMEM; + entry->committer = NULL; + + if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) { + free(entry); + return GIT_ERROR; + } + seek_forward(GIT_OID_HEXSZ + 1); + + if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) { + free(entry); + return GIT_ERROR; + } + seek_forward(GIT_OID_HEXSZ + 1); + + ptr = buf; + + /* Seek forward to the end of the signature. */ + while (*buf && *buf != '\t' && *buf != '\n') + seek_forward(1); + + entry->committer = git__malloc(sizeof(git_signature)); + if (entry->committer == NULL) { + free(entry); + return GIT_ENOMEM; + } + + if ((error = git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf)) < GIT_SUCCESS) { + free(entry->committer); + free(entry); + return git__rethrow(error, "Failed to parse reflog. Could not parse signature"); + } + + if (*buf == '\t') { + /* We got a message. Read everything till we reach LF. */ + seek_forward(1); + ptr = buf; + + while (*buf && *buf != '\n') + seek_forward(1); + + entry->msg = git__strndup(ptr, buf - ptr); + } else + entry->msg = NULL; + + while (*buf && *buf == '\n' && buf_size > 1) + seek_forward(1); + + if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to parse reflog. Could not add new entry"); + } + +#undef seek_forward + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog"); +} + +void git_reflog_free(git_reflog *reflog) +{ + unsigned int i; + git_reflog_entry *entry; + + for (i=0; i < reflog->entries.length; i++) { + entry = git_vector_get(&reflog->entries, i); + + git_signature_free(entry->committer); + + free(entry->msg); + free(entry); + } + + git_vector_free(&reflog->entries); + free(reflog->ref_name); + free(reflog); +} + +int git_reflog_read(git_reflog **reflog, git_reference *ref) +{ + int error; + char log_path[GIT_PATH_MAX]; + git_fbuffer log_file = GIT_FBUFFER_INIT; + git_reflog *log = NULL; + + *reflog = NULL; + + if ((error = reflog_init(&log, ref)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to read reflog. Cannot init reflog"); + + git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + + if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) { + git_reflog_free(log); + return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path); + } + + error = reflog_parse(log, log_file.data, log_file.len); + + git_futils_freebuffer(&log_file); + + if (error == GIT_SUCCESS) + *reflog = log; + else + git_reflog_free(log); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog"); +} + +int git_reflog_write(git_reference *ref, const git_oid *oid_old, + const git_signature *committer, const char *msg) +{ + int error; + char old[GIT_OID_HEXSZ+1]; + char new[GIT_OID_HEXSZ+1]; + char log_path[GIT_PATH_MAX]; + git_reference *r; + const git_oid *oid; + + if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write reflog. Cannot resolve reference `%s`", ref->name); + + oid = git_reference_oid(r); + if (oid == NULL) + return git__throw(GIT_ERROR, "Failed to write reflog. Cannot resolve reference `%s`", r->name); + + git_oid_to_string(new, GIT_OID_HEXSZ+1, oid); + + git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name); + + if (git_futils_exists(log_path)) { + if ((error = git_futils_mkpath2file(log_path)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to write reflog. Cannot create reflog directory"); + } else if (git_futils_isfile(log_path)) { + return git__throw(GIT_ERROR, "Failed to write reflog. `%s` is directory", log_path); + } else if (oid_old == NULL) + return git__throw(GIT_ERROR, "Failed to write reflog. Old OID cannot be NULL for existing reference"); + + if (oid_old) + git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old); + else + p_snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0); + + return reflog_write(log_path, old, new, committer, msg); +} + +unsigned int git_reflog_entrycount(git_reflog *reflog) +{ + assert(reflog); + return reflog->entries.length; +} + +const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx) +{ + assert(reflog); + return git_vector_get(&reflog->entries, idx); +} + +const git_oid * git_reflog_entry_oidold(const git_reflog_entry *entry) +{ + assert(entry); + return &entry->oid_old; +} + +const git_oid * git_reflog_entry_oidnew(const git_reflog_entry *entry) +{ + assert(entry); + return &entry->oid_cur; +} + +git_signature * git_reflog_entry_committer(const git_reflog_entry *entry) +{ + assert(entry); + return entry->committer; +} + +char * git_reflog_entry_msg(const git_reflog_entry *entry) +{ + assert(entry); + return entry->msg; +} diff --git a/vendor/libgit2/src/reflog.h b/vendor/libgit2/src/reflog.h new file mode 100644 index 000000000..b6daf2a76 --- /dev/null +++ b/vendor/libgit2/src/reflog.h @@ -0,0 +1,26 @@ +#ifndef INCLUDE_reflog_h__ +#define INCLUDE_reflog_h__ + +#include "common.h" +#include "git2/reflog.h" +#include "vector.h" + +#define GIT_REFLOG_DIR "logs/" + +#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17) + +struct git_reflog_entry { + git_oid oid_old; + git_oid oid_cur; + + git_signature *committer; + + char *msg; +}; + +struct git_reflog { + char *ref_name; + git_vector entries; +}; + +#endif /* INCLUDE_reflog_h__ */ diff --git a/vendor/libgit2/src/refs.c b/vendor/libgit2/src/refs.c index c4d3d6ae6..77521bc63 100644 --- a/vendor/libgit2/src/refs.c +++ b/vendor/libgit2/src/refs.c @@ -51,7 +51,7 @@ static uint32_t reftable_hash(const void *key, int hash_id) static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = { 2147483647, 0x5d20bb23, - 0x7daaab3c + 0x7daaab3c }; return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]); @@ -59,11 +59,11 @@ static uint32_t reftable_hash(const void *key, int hash_id) static void reference_free(git_reference *reference); static int reference_create(git_reference **ref_out, git_repository *repo, const char *name, git_rtype type); -static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name); +static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated); /* loose refs */ -static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content); -static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content); +static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content); +static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content); static int loose_lookup(git_reference **ref_out, git_repository *repo, const char *name, int skip_symbolic); static int loose_write(git_reference *ref); static int loose_update(git_reference *ref); @@ -80,13 +80,11 @@ static int packed_sort(const void *a, const void *b); static int packed_write(git_repository *repo); /* internal helpers */ -static int reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force); -static int reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force); -static int reference_rename(git_reference *ref, const char *new_name, int force); +static int reference_available(git_repository *repo, const char *ref, const char *old_ref); /* name normalization */ static int check_valid_ref_char(char ch); -static int normalize_name(char *buffer_out, const char *name, int is_oid_ref); +static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref); /***************************************** * Internal methods - Constructor/destructor @@ -111,7 +109,7 @@ static int reference_create( const char *name, git_rtype type) { - char normalized[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char normalized[GIT_REFNAME_MAX]; int error = GIT_SUCCESS, size; git_reference *reference = NULL; @@ -133,7 +131,7 @@ static int reference_create( reference->owner = repo; reference->type = type; - error = normalize_name(normalized, name, (type & GIT_REF_OID)); + error = normalize_name(normalized, sizeof(normalized), name, (type & GIT_REF_OID)); if (error < GIT_SUCCESS) goto cleanup; @@ -145,32 +143,23 @@ static int reference_create( *ref_out = reference; - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference"); cleanup: reference_free(reference); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference"); } -static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *repo_path, const char *ref_name) +static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated) { - struct stat st; char path[GIT_PATH_MAX]; - /* Determine the full path of the file */ - git__joinpath(path, repo_path, ref_name); - - if (gitfo_stat(path, &st) < 0 || S_ISDIR(st.st_mode)) - return git__throw(GIT_ENOTFOUND, - "Cannot read reference file '%s'", ref_name); - - if (mtime) - *mtime = st.st_mtime; + assert(file_content && repo_path && ref_name); - if (file_content) - return gitfo_read_file(file_content, path); + /* Determine the full path of the file */ + git_path_join(path, repo_path, ref_name); - return GIT_SUCCESS; + return git_futils_readbuffer_updated(file_content, path, mtime, updated); } @@ -181,24 +170,26 @@ static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *re *****************************************/ static int loose_update(git_reference *ref) { - int error; - time_t ref_time; - gitfo_buf ref_file = GITFO_BUF_INIT; + int error, updated; + git_fbuffer ref_file = GIT_FBUFFER_INIT; if (ref->type & GIT_REF_PACKED) return packed_load(ref->owner); - error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name); +/* error = reference_read(NULL, &ref_time, ref->owner->path_repository, ref->name); if (error < GIT_SUCCESS) goto cleanup; if (ref_time == ref->mtime) return GIT_SUCCESS; - - error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name); +*/ + error = reference_read(&ref_file, &ref->mtime, ref->owner->path_repository, ref->name, &updated); if (error < GIT_SUCCESS) goto cleanup; + if (!updated) + goto cleanup; + if (ref->type == GIT_REF_SYMBOLIC) error = loose_parse_symbolic(ref, &ref_file); else if (ref->type == GIT_REF_OID) @@ -207,18 +198,18 @@ static int loose_update(git_reference *ref) error = git__throw(GIT_EOBJCORRUPTED, "Invalid reference type (%d) for loose reference", ref->type); - gitfo_free_buf(&ref_file); cleanup: + git_futils_freebuffer(&ref_file); if (error != GIT_SUCCESS) { reference_free(ref); git_hashtable_remove(ref->owner->references.loose_cache, ref->name); } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to update loose reference"); } -static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) +static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content) { const unsigned int header_len = strlen(GIT_SYMREF); const char *refname_start; @@ -232,9 +223,9 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse loose reference. Object too short"); - /* + /* * Assume we have already checked for the header - * before calling this function + * before calling this function */ refname_start += header_len; @@ -257,7 +248,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content) return GIT_SUCCESS; } -static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content) +static int loose_parse_oid(git_reference *ref, git_fbuffer *file_content) { int error; reference_oid *ref_oid; @@ -271,7 +262,7 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse loose reference. Reference too short"); - if ((error = git_oid_mkstr(&ref_oid->oid, buffer)) < GIT_SUCCESS) + if ((error = git_oid_fromstr(&ref_oid->oid, buffer)) < GIT_SUCCESS) return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference."); buffer = buffer + GIT_OID_HEXSZ; @@ -288,36 +279,36 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content) static git_rtype loose_guess_rtype(const char *full_path) { - gitfo_buf ref_file = GITFO_BUF_INIT; + git_fbuffer ref_file = GIT_FBUFFER_INIT; git_rtype type; type = GIT_REF_INVALID; - if (gitfo_read_file(&ref_file, full_path) == GIT_SUCCESS) { + if (git_futils_readbuffer(&ref_file, full_path) == GIT_SUCCESS) { if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) type = GIT_REF_SYMBOLIC; else type = GIT_REF_OID; } - gitfo_free_buf(&ref_file); + git_futils_freebuffer(&ref_file); return type; } static int loose_lookup( - git_reference **ref_out, - git_repository *repo, + git_reference **ref_out, + git_repository *repo, const char *name, int skip_symbolic) { int error = GIT_SUCCESS; - gitfo_buf ref_file = GITFO_BUF_INIT; + git_fbuffer ref_file = GIT_FBUFFER_INIT; git_reference *ref = NULL; - time_t ref_time; + time_t ref_time = 0; *ref_out = NULL; - error = reference_read(&ref_file, &ref_time, repo->path_repository, name); + error = reference_read(&ref_file, &ref_time, repo->path_repository, name, NULL); if (error < GIT_SUCCESS) goto cleanup; @@ -343,77 +334,57 @@ static int loose_lookup( ref->mtime = ref_time; *ref_out = ref; - gitfo_free_buf(&ref_file); + git_futils_freebuffer(&ref_file); return GIT_SUCCESS; cleanup: - gitfo_free_buf(&ref_file); + git_futils_freebuffer(&ref_file); reference_free(ref); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to lookup loose reference"); } static int loose_write(git_reference *ref) { git_filebuf file; char ref_path[GIT_PATH_MAX]; - int error, contents_size; - char *ref_contents = NULL; + int error; struct stat st; - assert((ref->type & GIT_REF_PACKED) == 0); - - git__joinpath(ref_path, ref->owner->path_repository, ref->name); + git_path_join(ref_path, ref->owner->path_repository, ref->name); if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write loose reference"); if (ref->type & GIT_REF_OID) { reference_oid *ref_oid = (reference_oid *)ref; + char oid[GIT_OID_HEXSZ + 1]; - contents_size = GIT_OID_HEXSZ + 1; - ref_contents = git__malloc(contents_size); - if (ref_contents == NULL) { - error = GIT_ENOMEM; - goto unlock; - } + memset(oid, 0x0, sizeof(oid)); - git_oid_fmt(ref_contents, &ref_oid->oid); + git_oid_fmt(oid, &ref_oid->oid); + error = git_filebuf_printf(&file, "%s\n", oid); + if (error < GIT_SUCCESS) + goto unlock; } else if (ref->type & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */ reference_symbolic *ref_sym = (reference_symbolic *)ref; - contents_size = strlen(GIT_SYMREF) + strlen(ref_sym->target) + 1; - ref_contents = git__malloc(contents_size); - if (ref_contents == NULL) { - error = GIT_ENOMEM; - goto unlock; - } - - strcpy(ref_contents, GIT_SYMREF); - strcat(ref_contents, ref_sym->target); + error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref_sym->target); } else { error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type"); goto unlock; } - /* TODO: win32 carriage return when writing references in Windows? */ - ref_contents[contents_size - 1] = '\n'; - - if ((error = git_filebuf_write(&file, ref_contents, contents_size)) < GIT_SUCCESS) - goto unlock; - error = git_filebuf_commit(&file); - if (gitfo_stat(ref_path, &st) == GIT_SUCCESS) + if (p_stat(ref_path, &st) == GIT_SUCCESS) ref->mtime = st.st_mtime; - free(ref_contents); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference"); unlock: git_filebuf_cleanup(&file); - free(ref_contents); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write loose reference"); } @@ -427,7 +398,7 @@ static int loose_write(git_reference *ref) static int packed_parse_peel( reference_oid *tag_ref, - const char **buffer_out, + const char **buffer_out, const char *buffer_end) { const char *buffer = *buffer_out + 1; @@ -436,25 +407,25 @@ static int packed_parse_peel( /* Ensure it's not the first entry of the file */ if (tag_ref == NULL) - return GIT_EPACKEDREFSCORRUPTED; + return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is the first entry of the file"); /* Ensure reference is a tag */ if (git__prefixcmp(tag_ref->ref.name, GIT_REFS_TAGS_DIR) != 0) - return GIT_EPACKEDREFSCORRUPTED; + return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Reference is not a tag"); if (buffer + GIT_OID_HEXSZ >= buffer_end) - return GIT_EPACKEDREFSCORRUPTED; + return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer too small"); /* Is this a valid object id? */ - if (git_oid_mkstr(&tag_ref->peel_target, buffer) < GIT_SUCCESS) - return GIT_EPACKEDREFSCORRUPTED; + if (git_oid_fromstr(&tag_ref->peel_target, buffer) < GIT_SUCCESS) + return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Not a valid object ID"); buffer = buffer + GIT_OID_HEXSZ; if (*buffer == '\r') buffer++; if (*buffer != '\n') - return GIT_EPACKEDREFSCORRUPTED; + return git__throw(GIT_EPACKEDREFSCORRUPTED, "Failed to parse packed reference. Buffer not terminated correctly"); *buffer_out = buffer + 1; tag_ref->ref.type |= GIT_REF_HAS_PEEL; @@ -475,7 +446,7 @@ static int packed_parse_oid( int error = GIT_SUCCESS; int refname_len; - char refname[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char refname[GIT_REFNAME_MAX]; git_oid id; refname_begin = (buffer + GIT_OID_HEXSZ + 1); @@ -486,7 +457,7 @@ static int packed_parse_oid( } /* Is this a valid object id? */ - if ((error = git_oid_mkstr(&id, buffer)) < GIT_SUCCESS) + if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS) goto cleanup; refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin); @@ -517,58 +488,54 @@ static int packed_parse_oid( cleanup: reference_free((git_reference *)ref); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse OID of packed reference"); } static int packed_load(git_repository *repo) { - int error = GIT_SUCCESS; - gitfo_buf packfile = GITFO_BUF_INIT; + int error = GIT_SUCCESS, updated; + git_fbuffer packfile = GIT_FBUFFER_INIT; const char *buffer_start, *buffer_end; git_refcache *ref_cache = &repo->references; - /* already loaded */ - if (repo->references.packfile != NULL) { - time_t packed_time; - - /* check if we can read the time of the index; - * if we can read it and it matches the time of the - * index we had previously loaded, we don't need to do - * anything else. - * - * if we cannot load the time (e.g. the packfile - * has disappeared) or the time is different, we - * have to reload the packfile */ - - if (!reference_read(NULL, &packed_time, repo->path_repository, GIT_PACKEDREFS_FILE) && - packed_time == ref_cache->packfile_time) - return GIT_SUCCESS; - - git_hashtable_clear(repo->references.packfile); - } else { + /* First we make sure we have allocated the hash table */ + if (ref_cache->packfile == NULL) { ref_cache->packfile = git_hashtable_alloc( - default_table_size, + default_table_size, reftable_hash, - (git_hash_keyeq_ptr)strcmp); + (git_hash_keyeq_ptr)(&git__strcmp_cb)); - if (ref_cache->packfile == NULL) - return GIT_ENOMEM; + if (ref_cache->packfile == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } } - /* read the packfile from disk; - * store its modification time to check for future reloads */ - error = reference_read( - &packfile, - &ref_cache->packfile_time, - repo->path_repository, - GIT_PACKEDREFS_FILE); + error = reference_read(&packfile, &ref_cache->packfile_time, + repo->path_repository, GIT_PACKEDREFS_FILE, &updated); - /* there is no packfile on disk; that's ok */ - if (error == GIT_ENOTFOUND) + /* + * If we couldn't find the file, we need to clear the table and + * return. On any other error, we return that error. If everything + * went fine and the file wasn't updated, then there's nothing new + * for us here, so just return. Anything else means we need to + * refresh the packed refs. + */ + if (error == GIT_ENOTFOUND) { + git_hashtable_clear(ref_cache->packfile); + return GIT_SUCCESS; + } else if (error < GIT_SUCCESS) { + return git__rethrow(error, "Failed to read packed refs"); + } else if (!updated) { return GIT_SUCCESS; + } - if (error < GIT_SUCCESS) - goto cleanup; + /* + * At this point, we want to refresh the packed refs. We already + * have the contents in our buffer. + */ + + git_hashtable_clear(ref_cache->packfile); buffer_start = (const char *)packfile.data; buffer_end = (const char *)(buffer_start) + packfile.len; @@ -602,14 +569,14 @@ static int packed_load(git_repository *repo) } } - gitfo_free_buf(&packfile); + git_futils_freebuffer(&packfile); return GIT_SUCCESS; cleanup: git_hashtable_free(ref_cache->packfile); ref_cache->packfile = NULL; - gitfo_free_buf(&packfile); - return error; + git_futils_freebuffer(&packfile); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load packed references"); } @@ -629,8 +596,8 @@ static int _dirent_loose_listall(void *_data, char *full_path) struct dirent_list_data *data = (struct dirent_list_data *)_data; char *file_path = full_path + data->repo_path_len; - if (gitfo_isdir(full_path) == GIT_SUCCESS) - return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data); + if (git_futils_isdir(full_path) == GIT_SUCCESS) + return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_listall, _data); /* do not add twice a reference that exists already in the packfile */ if ((data->list_flags & GIT_REF_PACKED) != 0 && @@ -652,8 +619,8 @@ static int _dirent_loose_load(void *data, char *full_path) char *file_path; int error; - if (gitfo_isdir(full_path) == GIT_SUCCESS) - return gitfo_dirent(full_path, GIT_PATH_MAX, _dirent_loose_load, repository); + if (git_futils_isdir(full_path) == GIT_SUCCESS) + return git_futils_direach(full_path, GIT_PATH_MAX, _dirent_loose_load, repository); file_path = full_path + strlen(repository->path_repository); error = loose_lookup(&reference, repository, file_path, 1); @@ -669,7 +636,7 @@ static int _dirent_loose_load(void *data, char *full_path) reference_free(old_ref); } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to load loose dirent"); } /* @@ -685,7 +652,7 @@ static int packed_loadloose(git_repository *repository) /* the packfile must have been previously loaded! */ assert(repository->references.packfile); - git__joinpath(refs_path, repository->path_repository, GIT_REFS_DIR); + git_path_join(refs_path, repository->path_repository, GIT_REFS_DIR); /* Remove any loose references from the cache */ { @@ -702,9 +669,9 @@ static int packed_loadloose(git_repository *repository) /* * Load all the loose files from disk into the Packfile table. * This will overwrite any old packed entries with their - * updated loose versions + * updated loose versions */ - return gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_load, repository); + return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_load, repository); } /* @@ -718,7 +685,7 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file) git_oid_fmt(oid, &ref->oid); oid[GIT_OID_HEXSZ] = 0; - /* + /* * For references that peel to an object in the repo, we must * write the resulting peel on a separate line, e.g. * @@ -738,13 +705,13 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file) error = git_filebuf_printf(file, "%s %s\n", oid, ref->ref.name); } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference"); } /* * Find out what object this reference resolves to. * - * For references that point to a 'big' tag (e.g. an + * For references that point to a 'big' tag (e.g. an * actual tag object on the repository), we need to * cache on the packfile the OID of the object to * which that 'big tag' is pointing to. @@ -769,7 +736,7 @@ static int packed_find_peel(reference_oid *ref) */ error = git_object_lookup(&object, ref->ref.owner, &ref->oid, GIT_OBJ_ANY); if (error < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference"); /* * If the tagged object is a Tag object, we need to resolve it; @@ -825,10 +792,10 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) if (reference != NULL) continue; - git__joinpath(full_path, repo->path_repository, ref->name); + git_path_join(full_path, repo->path_repository, ref->name); - if (gitfo_exists(full_path) == GIT_SUCCESS && - gitfo_unlink(full_path) < GIT_SUCCESS) + if (git_futils_exists(full_path) == GIT_SUCCESS && + p_unlink(full_path) < GIT_SUCCESS) error = GIT_EOSERR; /* @@ -842,13 +809,13 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list) */ } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to remove loose packed reference"); } static int packed_sort(const void *a, const void *b) { - const git_reference *ref_a = *(const git_reference **)a; - const git_reference *ref_b = *(const git_reference **)b; + const git_reference *ref_a = (const git_reference *)a; + const git_reference *ref_b = (const git_reference *)b; return strcmp(ref_a->name, ref_b->name); } @@ -870,7 +837,7 @@ static int packed_write(git_repository *repo) total_refs = repo->references.packfile->key_count; if ((error = git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write packed reference"); /* Load all the packfile into a vector */ { @@ -886,15 +853,15 @@ static int packed_write(git_repository *repo) git_vector_sort(&packing_list); /* Now we can open the file! */ - git__joinpath(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE); + git_path_join(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE); if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write packed reference"); /* Packfiles have a header... apparently * This is in fact not required, but we might as well print it * just for kicks */ if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write packed reference"); for (i = 0; i < packing_list.length; ++i) { reference_oid *ref = (reference_oid *)git_vector_get(&packing_list, i); @@ -903,8 +870,11 @@ static int packed_write(git_repository *repo) * this is a disaster */ assert(ref->ref.type & GIT_REF_OID); - if ((error = packed_find_peel(ref)) < GIT_SUCCESS) + if ((error = packed_find_peel(ref)) < GIT_SUCCESS) { + error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled"); goto cleanup; + } + if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS) goto cleanup; @@ -923,7 +893,7 @@ static int packed_write(git_repository *repo) error = packed_remove_loose(repo, &packing_list); - if (gitfo_stat(pack_file_path, &st) == GIT_SUCCESS) + if (p_stat(pack_file_path, &st) == GIT_SUCCESS) repo->references.packfile_time = st.st_mtime; } } @@ -931,242 +901,51 @@ static int packed_write(git_repository *repo) git_vector_free(&packing_list); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write packed reference"); } -/***************************************** - * Internal methods - reference creation - *****************************************/ - -static int reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) +static int _reference_available_cb(const char *ref, void *data) { - char normalized[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; - int error = GIT_SUCCESS, updated = 0; - git_reference *ref = NULL, *old_ref = NULL; - - if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return GIT_EEXISTS; + const char *new, *old; + git_vector *refs; - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref->type & GIT_REF_SYMBOLIC){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); - if (error < GIT_SUCCESS) - goto cleanup; - } + assert(ref && data); - /* The target can aither be the name of an object id reference or the name of another symbolic reference */ - error = normalize_name(normalized, target, 0); - if (error < GIT_SUCCESS) - goto cleanup; + refs = (git_vector *)data; - /* set the target; this will write the reference on disk */ - error = git_reference_set_target(ref, normalized); - if (error < GIT_SUCCESS) - goto cleanup; + new = (const char *)git_vector_get(refs, 0); + old = (const char *)git_vector_get(refs, 1); - /* - * If we didn't update the ref, then we need to insert or replace - * it in the loose cache. If we replaced a ref, free it. - */ - if (!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; + if (!old || strcmp(old, ref)) { + int reflen = strlen(ref); + int newlen = strlen(new); + int cmplen = reflen < newlen ? reflen : newlen; + const char *lead = reflen < newlen ? new : ref; - if(old_ref) - reference_free(old_ref); + if (!strncmp(new, ref, cmplen) && + lead[cmplen] == '/') + return GIT_EEXISTS; } - *ref_out = ref; - - return error; - -cleanup: - reference_free(ref); - return error; -} - -static int reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) -{ - int error = GIT_SUCCESS, updated = 0; - git_reference *ref = NULL, *old_ref = NULL; - - if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) - return GIT_EEXISTS; - - /* - * If they old ref was of the same type, then we can just update - * it (once we've checked that the target is valid). Otherwise we - * need a new reference because we can't make a symbolic ref out - * of an oid one. - * If if didn't exist, then we need to create a new one anyway. - */ - if (ref && ref-> type & GIT_REF_OID){ - updated = 1; - } else { - ref = NULL; - error = reference_create(&ref, repo, name, GIT_REF_OID); - if (error < GIT_SUCCESS) - goto cleanup; - } - - /* set the oid; this will write the reference on disk */ - error = git_reference_set_oid(ref, id); - if (error < GIT_SUCCESS) - goto cleanup; - - if(!updated){ - error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); - if (error < GIT_SUCCESS) - goto cleanup; - - if(old_ref) - reference_free(old_ref); - } - - *ref_out = ref; - - return error; - -cleanup: - reference_free(ref); - return error; + return GIT_SUCCESS; } -/* - * Rename a reference - * - * If the reference is packed, we need to rewrite the - * packfile to remove the reference from it and create - * the reference back as a loose one. - * - * If the reference is loose, we just rename it on - * the filesystem. - * - * We also need to re-insert the reference on its corresponding - * in-memory cache, since the caches are indexed by refname. - */ -static int reference_rename(git_reference *ref, const char *new_name, int force) +static int reference_available(git_repository *repo, const char *ref, const char* old_ref) { int error; - char *old_name; - char old_path[GIT_PATH_MAX], new_path[GIT_PATH_MAX], normalized_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; - git_reference *looked_up_ref, *old_ref = NULL; - - assert(ref); - - /* Ensure the name is valid */ - error = normalize_name(normalized_name, new_name, ref->type & GIT_REF_OID); - if (error < GIT_SUCCESS) - return error; - - /* Ensure we're not going to overwrite an existing reference - unless the user has allowed us */ - error = git_reference_lookup(&looked_up_ref, ref->owner, new_name); - if (error == GIT_SUCCESS && !force) - return GIT_EEXISTS; - - if (error < GIT_SUCCESS && - error != GIT_ENOTFOUND) - return error; + git_vector refs; - - old_name = ref->name; - ref->name = git__strdup(new_name); - - if (ref->name == NULL) { - ref->name = old_name; + if (git_vector_init(&refs, 2, NULL) < GIT_SUCCESS) return GIT_ENOMEM; - } - - if (ref->type & GIT_REF_PACKED) { - /* write the packfile to disk; note - * that the state of the in-memory cache is not - * consistent, because the reference is indexed - * by its old name but it already has the new one. - * This doesn't affect writing, though, and allows - * us to rollback if writing fails - */ - - ref->type &= ~GIT_REF_PACKED; - - /* Create the loose ref under its new name */ - error = loose_write(ref); - if (error < GIT_SUCCESS) { - ref->type |= GIT_REF_PACKED; - goto cleanup; - } - - /* Remove from the packfile cache in order to avoid packing it back - * Note : we do not rely on git_reference_delete() because this would - * invalidate the reference. - */ - git_hashtable_remove(ref->owner->references.packfile, old_name); - - /* Recreate the packed-refs file without the reference */ - error = packed_write(ref->owner); - if (error < GIT_SUCCESS) - goto rename_loose_to_old_name; - - } else { - git__joinpath(old_path, ref->owner->path_repository, old_name); - git__joinpath(new_path, ref->owner->path_repository, ref->name); - - error = gitfo_mv_force(old_path, new_path); - if (error < GIT_SUCCESS) - goto cleanup; - /* Once succesfully renamed, remove from the cache the reference known by its old name*/ - git_hashtable_remove(ref->owner->references.loose_cache, old_name); - } + git_vector_insert(&refs, (void *)ref); + git_vector_insert(&refs, (void *)old_ref); - /* Store the renamed reference into the loose ref cache */ - error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **) &old_ref); + error = git_reference_foreach(repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&refs); - /* If we force-replaced, we need to free the old reference */ - if(old_ref) - reference_free(old_ref); + git_vector_free(&refs); - free(old_name); - return error; - -cleanup: - /* restore the old name if this failed */ - free(ref->name); - ref->name = old_name; - return error; - -rename_loose_to_old_name: - /* If we hit this point. Something *bad* happened! Think "Ghostbusters - * crossing the streams" definition of bad. - * Either the packed-refs has been correctly generated and something else - * has gone wrong, or the writing of the new packed-refs has failed, and - * we're stuck with the old one. As a loose ref always takes priority over - * a packed ref, we'll eventually try and rename the generated loose ref to - * its former name. It even that fails, well... we might have lost the reference - * for good. :-/ - */ - - git__joinpath(old_path, ref->owner->path_repository, ref->name); - git__joinpath(new_path, ref->owner->path_repository, old_name); - - /* No error checking. We'll return the initial error */ - gitfo_mv_force(old_path, new_path); - - /* restore the old name */ - free(ref->name); - ref->name = old_name; - - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref); } /***************************************** @@ -1179,15 +958,15 @@ static int reference_rename(git_reference *ref, const char *new_name, int force) int git_reference_lookup(git_reference **ref_out, git_repository *repo, const char *name) { int error; - char normalized_name[GIT_PATH_MAX]; + char normalized_name[GIT_REFNAME_MAX]; assert(ref_out && repo && name); *ref_out = NULL; - error = normalize_name(normalized_name, name, 0); + error = normalize_name(normalized_name, sizeof(normalized_name), name, 0); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to lookup reference"); /* First, check has been previously loaded and cached */ *ref_out = git_hashtable_lookup(repo->references.loose_cache, normalized_name); @@ -1204,16 +983,16 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch /* The loose lookup has failed, but not because the reference wasn't found; * probably the loose reference is corrupted. this is bad. */ if (error != GIT_ENOTFOUND) - return error; + return git__rethrow(error, "Failed to lookup reference"); /* * If we cannot find a loose reference, we look into the packfile - * Load the packfile first if it hasn't been loaded + * Load the packfile first if it hasn't been loaded */ /* load all the packed references */ error = packed_load(repo); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to lookup reference"); /* Look up on the packfile */ *ref_out = git_hashtable_lookup(repo->references.packfile, normalized_name); @@ -1221,27 +1000,7 @@ int git_reference_lookup(git_reference **ref_out, git_repository *repo, const ch return GIT_SUCCESS; /* The reference doesn't exist anywhere */ - return GIT_ENOTFOUND; -} - -int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target) -{ - return reference_create_symbolic(ref_out, repo, name, target, 0); -} - -int git_reference_create_symbolic_f(git_reference **ref_out, git_repository *repo, const char *name, const char *target) -{ - return reference_create_symbolic(ref_out, repo, name, target, 1); -} - -int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id) -{ - return reference_create_oid(ref_out, repo, name, id, 0); -} - -int git_reference_create_oid_f(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id) -{ - return reference_create_oid(ref_out, repo, name, id, 1); + return git__throw(GIT_ENOTFOUND, "Failed to lookup reference. Reference doesn't exist"); } /** @@ -1298,6 +1057,113 @@ const char *git_reference_target(git_reference *ref) return ((reference_symbolic *)ref)->target; } +int git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force) +{ + char normalized[GIT_REFNAME_MAX]; + int error = GIT_SUCCESS, updated = 0; + git_reference *ref = NULL, *old_ref = NULL; + + if (git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + return git__throw(GIT_EEXISTS, "Failed to create symbolic reference. Reference already exists"); + + /* + * If they old ref was of the same type, then we can just update + * it (once we've checked that the target is valid). Otherwise we + * need a new reference because we can't make a symbolic ref out + * of an oid one. + * If if didn't exist, then we need to create a new one anyway. + */ + if (ref && ref->type & GIT_REF_SYMBOLIC){ + updated = 1; + } else { + ref = NULL; + error = reference_create(&ref, repo, name, GIT_REF_SYMBOLIC); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* The target can aither be the name of an object id reference or the name of another symbolic reference */ + error = normalize_name(normalized, sizeof(normalized), target, 0); + if (error < GIT_SUCCESS) + goto cleanup; + + /* set the target; this will write the reference on disk */ + error = git_reference_set_target(ref, normalized); + if (error < GIT_SUCCESS) + goto cleanup; + + /* + * If we didn't update the ref, then we need to insert or replace + * it in the loose cache. If we replaced a ref, free it. + */ + if (!updated){ + error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); + if (error < GIT_SUCCESS) + goto cleanup; + + if(old_ref) + reference_free(old_ref); + } + + *ref_out = ref; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); + +cleanup: + reference_free(ref); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create symbolic reference"); +} + +int git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force) +{ + int error = GIT_SUCCESS, updated = 0; + git_reference *ref = NULL, *old_ref = NULL; + + if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force) + return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists"); + + if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to create reference"); + + /* + * If they old ref was of the same type, then we can just update + * it (once we've checked that the target is valid). Otherwise we + * need a new reference because we can't make a symbolic ref out + * of an oid one. + * If if didn't exist, then we need to create a new one anyway. + */ + if (ref && ref-> type & GIT_REF_OID){ + updated = 1; + } else { + ref = NULL; + error = reference_create(&ref, repo, name, GIT_REF_OID); + if (error < GIT_SUCCESS) + goto cleanup; + } + + /* set the oid; this will write the reference on disk */ + error = git_reference_set_oid(ref, id); + if (error < GIT_SUCCESS) + goto cleanup; + + if(!updated){ + error = git_hashtable_insert2(repo->references.loose_cache, ref->name, ref, (void **) &old_ref); + if (error < GIT_SUCCESS) + goto cleanup; + + if(old_ref) + reference_free(old_ref); + } + + *ref_out = ref; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); + +cleanup: + reference_free(ref); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID"); +} + /** * Setters */ @@ -1329,7 +1195,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) int error = GIT_SUCCESS; if ((ref->type & GIT_REF_OID) == 0) - return GIT_EINVALIDREFSTATE; + return git__throw(GIT_EINVALIDREFSTATE, "Failed to set OID target of reference. Not an OID reference"); ref_oid = (reference_oid *)ref; @@ -1338,7 +1204,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) /* Don't let the user create references to OIDs that * don't exist in the ODB */ if (!git_odb_exists(git_repository_database(ref->owner), id)) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to set OID target of reference. OID doesn't exist in ODB"); /* duplicate the reference; * this copy will stay on the packfile cache */ @@ -1357,7 +1223,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) git_oid_cpy(&ref_oid->oid, id); ref->type &= ~GIT_REF_HAS_PEEL; - error = loose_write(ref); + error = loose_write(ref); if (error < GIT_SUCCESS) goto cleanup; @@ -1379,7 +1245,7 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id) cleanup: reference_free((git_reference *)ref_old); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set OID target of reference"); } /* @@ -1394,7 +1260,7 @@ int git_reference_set_target(git_reference *ref, const char *target) reference_symbolic *ref_sym; if ((ref->type & GIT_REF_SYMBOLIC) == 0) - return GIT_EINVALIDREFSTATE; + return git__throw(GIT_EINVALIDREFSTATE, "Failed to set reference target. Not a symbolic reference"); ref_sym = (reference_symbolic *)ref; @@ -1410,6 +1276,164 @@ int git_reference_set_target(git_reference *ref, const char *target) * Other */ +int git_reference_rename(git_reference *ref, const char *new_name, int force) +{ + int error; + char *old_name = NULL; + + char aux_path[GIT_PATH_MAX]; + char normalized[GIT_REFNAME_MAX]; + + const char *target_ref = NULL; + const char *head_target = NULL; + const git_oid *target_oid = NULL; + git_reference *new_ref = NULL, *old_ref = NULL, *head = NULL; + + assert(ref); + + error = normalize_name(normalized, sizeof(normalized), new_name, ref->type & GIT_REF_OID); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to rename reference. Invalid name"); + + new_name = normalized; + + error = git_reference_lookup(&new_ref, ref->owner, new_name); + if (error == GIT_SUCCESS) { + if (!force) + return git__throw(GIT_EEXISTS, "Failed to rename reference. Reference already exists"); + + error = git_reference_delete(new_ref); + } + + if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) + goto cleanup; + + if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to rename reference. Reference already exists"); + + /* + * First, we backup the reference targets. Just keeping the old + * reference won't work, since we may have to remove it to create + * the new reference, e.g. when renaming foo/bar -> foo. + */ + + old_name = git__strdup(ref->name); + + if (ref->type & GIT_REF_SYMBOLIC) { + if ((target_ref = git_reference_target(ref)) == NULL) + goto cleanup; + } else { + if ((target_oid = git_reference_oid(ref)) == NULL) + goto cleanup; + } + + /* + * Now delete the old ref and remove an possibly existing directory + * named `new_name`. + */ + + if (ref->type & GIT_REF_PACKED) { + ref->type &= ~GIT_REF_PACKED; + + git_hashtable_remove(ref->owner->references.packfile, old_name); + if ((error = packed_write(ref->owner)) < GIT_SUCCESS) + goto rollback; + } else { + git_path_join(aux_path, ref->owner->path_repository, old_name); + if ((error = p_unlink(aux_path)) < GIT_SUCCESS) + goto cleanup; + + git_hashtable_remove(ref->owner->references.loose_cache, old_name); + } + + /* build new path */ + git_path_join(aux_path, ref->owner->path_repository, new_name); + + if (git_futils_exists(aux_path) == GIT_SUCCESS) { + if (git_futils_isdir(aux_path) == GIT_SUCCESS) { + if ((error = git_futils_rmdir_r(aux_path, 0)) < GIT_SUCCESS) + goto rollback; + } else goto rollback; + } + + /* + * Crude hack: delete any logs till we support proper reflogs. + * Otherwise git.git will possibly fail and leave a mess. git.git + * writes reflogs by default in any repo with a working directory: + * + * "We only enable reflogs in repositories that have a working directory + * associated with them, as shared/bare repositories do not have + * an easy means to prune away old log entries, or may fail logging + * entirely if the user's gecos information is not valid during a push. + * This heuristic was suggested on the mailing list by Junio." + * + * Shawn O. Pearce - 0bee59186976b1d9e6b2dd77332480c9480131d5 + * + * TODO + * + */ + + git_path_join_n(aux_path, 3, ref->owner->path_repository, "logs", old_name); + if (git_futils_isfile(aux_path) == GIT_SUCCESS) { + if ((error = p_unlink(aux_path)) < GIT_SUCCESS) + goto rollback; + } + + /* + * Finally we can create the new reference. + */ + if (ref->type & GIT_REF_SYMBOLIC) { + if ((error = git_reference_create_symbolic(&new_ref, ref->owner, new_name, target_ref, 0)) < GIT_SUCCESS) + goto rollback; + } else { + if ((error = git_reference_create_oid(&new_ref, ref->owner, new_name, target_oid, 0)) < GIT_SUCCESS) + goto rollback; + } + + free(ref->name); + ref->name = new_ref->name; + + /* + * No need in new_ref anymore. We created it to fix the change on disk. + * TODO: Refactoring required. + */ + new_ref->name = NULL; + reference_free(new_ref); + + if ((error = git_hashtable_insert2(ref->owner->references.loose_cache, ref->name, ref, (void **)&old_ref)) < GIT_SUCCESS) + goto rollback; + + /* + * Check if we have to update HEAD. + */ + + if ((error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE)) < GIT_SUCCESS) + goto cleanup; + + head_target = git_reference_target(head); + + if (head_target && !strcmp(head_target, old_name)) + if ((error = git_reference_create_symbolic(&head, ref->owner, "HEAD", ref->name, 1)) < GIT_SUCCESS) + goto rollback; + +cleanup: + free(old_name); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference"); + +rollback: + /* + * Try to create the old reference again. + */ + if (ref->type & GIT_REF_SYMBOLIC) + error = git_reference_create_symbolic(&new_ref, ref->owner, old_name, target_ref, 0); + else + error = git_reference_create_oid(&new_ref, ref->owner, old_name, target_oid, 0); + + ref->name = old_name; + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Failed to rollback"); +} + /* * Delete a reference. * @@ -1435,15 +1459,17 @@ int git_reference_delete(git_reference *ref) if (ref->type & GIT_REF_PACKED) { /* load the existing packfile */ if ((error = packed_load(ref->owner)) < GIT_SUCCESS) - return error; - - git_hashtable_remove(ref->owner->references.packfile, ref->name); + return git__rethrow(error, "Failed to delete reference"); + + if (git_hashtable_remove(ref->owner->references.packfile, ref->name) < GIT_SUCCESS) + return git__throw(GIT_ENOTFOUND, "Reference not found"); + error = packed_write(ref->owner); } else { char full_path[GIT_PATH_MAX]; - git__joinpath(full_path, ref->owner->path_repository, ref->name); + git_path_join(full_path, ref->owner->path_repository, ref->name); git_hashtable_remove(ref->owner->references.loose_cache, ref->name); - error = gitfo_unlink(full_path); + error = p_unlink(full_path); if (error < GIT_SUCCESS) goto cleanup; @@ -1458,17 +1484,7 @@ int git_reference_delete(git_reference *ref) cleanup: reference_free(ref); - return error; -} - -int git_reference_rename(git_reference *ref, const char *new_name) -{ - return reference_rename(ref, new_name, 0); -} - -int git_reference_rename_f(git_reference *ref, const char *new_name) -{ - return reference_rename(ref, new_name, 1); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to delete reference"); } int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) @@ -1480,8 +1496,8 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) *resolved_ref = NULL; if ((error = loose_update(ref)) < GIT_SUCCESS) - return error; - + return git__rethrow(error, "Failed to resolve reference"); + repo = ref->owner; for (i = 0; i < MAX_NESTING_LEVEL; ++i) { @@ -1489,16 +1505,15 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) *resolved_ref = ref; - if (ref->type & GIT_REF_OID) { + if (ref->type & GIT_REF_OID) return GIT_SUCCESS; - } ref_sym = (reference_symbolic *)ref; if ((error = git_reference_lookup(&ref, repo, ref_sym->target)) < GIT_SUCCESS) return error; } - return GIT_ETOONESTEDSYMREF; + return git__throw(GIT_ENOMEM, "Failed to resolve reference. Reference is too nested"); } int git_reference_packall(git_repository *repo) @@ -1507,17 +1522,17 @@ int git_reference_packall(git_repository *repo) /* load the existing packfile */ if ((error = packed_load(repo)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to pack references"); /* update it in-memory with all the loose references */ if ((error = packed_loadloose(repo)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to pack references"); /* write it back to disk */ return packed_write(repo); } -int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload) +int git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload) { int error; struct dirent_list_data data; @@ -1529,11 +1544,11 @@ int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*ca void *GIT_UNUSED(_unused); if ((error = packed_load(repo)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to list references"); GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused, if ((error = callback(ref_name, payload)) < GIT_SUCCESS) - return error; + return git__throw(error, "Failed to list references. User callback failed"); ); } @@ -1547,8 +1562,8 @@ int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*ca data.callback_payload = payload; - git__joinpath(refs_path, repo->path_repository, GIT_REFS_DIR); - return gitfo_dirent(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); + git_path_join(refs_path, repo->path_repository, GIT_REFS_DIR); + return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data); } int cb__reflist_add(const char *ref, void *data) @@ -1569,7 +1584,7 @@ int git_reference_listall(git_strarray *array, git_repository *repo, unsigned in if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS) return GIT_ENOMEM; - error = git_reference_listcb(repo, list_flags, &cb__reflist_add, (void *)&ref_list); + error = git_reference_foreach(repo, list_flags, &cb__reflist_add, (void *)&ref_list); if (error < GIT_SUCCESS) { git_vector_free(&ref_list); @@ -1594,10 +1609,11 @@ int git_repository__refcache_init(git_refcache *refs) refs->loose_cache = git_hashtable_alloc( default_table_size, reftable_hash, - (git_hash_keyeq_ptr)strcmp); + (git_hash_keyeq_ptr)(&git__strcmp_cb)); /* packfile loaded lazily */ refs->packfile = NULL; + refs->packfile_time = 0; return (refs->loose_cache) ? GIT_SUCCESS : GIT_ENOMEM; } @@ -1631,7 +1647,7 @@ void git_repository__refcache_free(git_refcache *refs) *****************************************/ static int check_valid_ref_char(char ch) { - if (ch <= ' ') + if ((unsigned) ch <= ' ') return GIT_ERROR; switch (ch) { @@ -1643,48 +1659,48 @@ static int check_valid_ref_char(char ch) case '[': case '*': return GIT_ERROR; - break; - default: return GIT_SUCCESS; } } -static int normalize_name(char *buffer_out, const char *name, int is_oid_ref) +static int normalize_name(char *buffer_out, size_t out_size, const char *name, int is_oid_ref) { - int error = GIT_SUCCESS; const char *name_end, *buffer_out_start; - char *current; + const char *current; int contains_a_slash = 0; assert(name && buffer_out); buffer_out_start = buffer_out; - current = (char *)name; + current = name; name_end = name + strlen(name); + /* Terminating null byte */ + out_size--; + /* A refname can not be empty */ if (name_end == name) - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name is empty"); /* A refname can not end with a dot or a slash */ if (*(name_end - 1) == '.' || *(name_end - 1) == '/') - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with dot or slash"); - while (current < name_end) { + while (current < name_end && out_size) { if (check_valid_ref_char(*current)) - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains invalid characters"); if (buffer_out > buffer_out_start) { char prev = *(buffer_out - 1); /* A refname can not start with a dot nor contain a double dot */ if (*current == '.' && ((prev == '.') || (prev == '/'))) - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name starts with a dot or contains a double dot"); /* '@{' is forbidden within a refname */ if (*current == '{' && prev == '@') - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains '@{'"); /* Prevent multiple slashes from being added to the output */ if (*current == '/' && prev == '/') { @@ -1697,17 +1713,21 @@ static int normalize_name(char *buffer_out, const char *name, int is_oid_ref) contains_a_slash = 1; *buffer_out++ = *current++; + out_size--; } + if (!out_size) + return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long"); + /* Object id refname have to contain at least one slash, except * for HEAD in a detached state or MERGE_HEAD if we're in the * middle of a merge */ if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE))) - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name contains no slashes"); /* A refname can not end with ".lock" */ if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION)) - return GIT_EINVALIDREFNAME; + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name ends with '.lock'"); *buffer_out = '\0'; @@ -1715,22 +1735,19 @@ static int normalize_name(char *buffer_out, const char *name, int is_oid_ref) * For object id references, name has to start with refs/. Again, * we need to allow HEAD to be in a detached state. */ - if (is_oid_ref && - !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) || - strcmp(buffer_out_start, GIT_HEAD_FILE))) - return GIT_EINVALIDREFNAME; + if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) || + strcmp(buffer_out_start, GIT_HEAD_FILE))) + return git__throw(GIT_EINVALIDREFNAME, "Failed to normalize name. Reference name does not start with 'refs/'"); - return error; + return GIT_SUCCESS; } -int git_reference__normalize_name(char *buffer_out, const char *name) +int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name) { - return normalize_name(buffer_out, name, 0); + return normalize_name(buffer_out, out_size, name, 0); } -int git_reference__normalize_name_oid(char *buffer_out, const char *name) +int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name) { - return normalize_name(buffer_out, name, 1); + return normalize_name(buffer_out, out_size, name, 1); } - - diff --git a/vendor/libgit2/src/refs.h b/vendor/libgit2/src/refs.h index b8f3e2f6d..dfac455e0 100644 --- a/vendor/libgit2/src/refs.h +++ b/vendor/libgit2/src/refs.h @@ -11,15 +11,18 @@ #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" +#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF" + #define GIT_SYMREF "ref: " #define GIT_PACKEDREFS_FILE "packed-refs" #define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled " -#define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100 #define GIT_HEAD_FILE "HEAD" #define GIT_MERGE_HEAD_FILE "MERGE_HEAD" #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master" +#define GIT_REFNAME_MAX 1024 + struct git_reference { git_repository *owner; char *name; @@ -37,7 +40,7 @@ typedef struct { void git_repository__refcache_free(git_refcache *refs); int git_repository__refcache_init(git_refcache *refs); -int git_reference__normalize_name(char *buffer_out, const char *name); -int git_reference__normalize_name_oid(char *buffer_out, const char *name); +int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name); +int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name); #endif diff --git a/vendor/libgit2/src/refspec.c b/vendor/libgit2/src/refspec.c new file mode 100644 index 000000000..8500e07ea --- /dev/null +++ b/vendor/libgit2/src/refspec.c @@ -0,0 +1,108 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "git2/errors.h" + +#include "common.h" +#include "refspec.h" +#include "util.h" + +int git_refspec_parse(git_refspec *refspec, const char *str) +{ + char *delim; + + memset(refspec, 0x0, sizeof(git_refspec)); + + if (*str == '+') { + refspec->force = 1; + str++; + } + + delim = strchr(str, ':'); + if (delim == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'"); + + refspec->src = git__strndup(str, delim - str); + if (refspec->src == NULL) + return GIT_ENOMEM; + + refspec->dst = git__strdup(delim + 1); + if (refspec->dst == NULL) { + free(refspec->src); + refspec->src = NULL; + return GIT_ENOMEM; + } + + return GIT_SUCCESS; +} + +const char *git_refspec_src(const git_refspec *refspec) +{ + return refspec->src; +} + +const char *git_refspec_dst(const git_refspec *refspec) +{ + return refspec->dst; +} + +int git_refspec_src_match(const git_refspec *refspec, const char *refname) +{ + return git__fnmatch(refspec->src, refname, 0); +} + +int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name) +{ + size_t baselen, namelen; + + baselen = strlen(spec->dst); + if (outlen <= baselen) + return git__throw(GIT_EINVALIDREFNAME, "Reference name too long"); + + /* + * No '*' at the end means that it's mapped to one specific local + * branch, so no actual transformation is needed. + */ + if (spec->dst[baselen - 1] != '*') { + memcpy(out, spec->dst, baselen + 1); /* include '\0' */ + return GIT_SUCCESS; + } + + /* There's a '*' at the end, so remove its length */ + baselen--; + + /* skip the prefix, -1 is for the '*' */ + name += strlen(spec->src) - 1; + + namelen = strlen(name); + + if (outlen <= baselen + namelen) + return git__throw(GIT_EINVALIDREFNAME, "Reference name too long"); + + memcpy(out, spec->dst, baselen); + memcpy(out + baselen, name, namelen + 1); + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/refspec.h b/vendor/libgit2/src/refspec.h new file mode 100644 index 000000000..230135a4a --- /dev/null +++ b/vendor/libgit2/src/refspec.h @@ -0,0 +1,14 @@ +#ifndef INCLUDE_refspec_h__ +#define INCLUDE_refspec_h__ + +#include "git2/refspec.h" + +struct git_refspec { + int force; + char *src; + char *dst; +}; + +int git_refspec_parse(struct git_refspec *refspec, const char *str); + +#endif diff --git a/vendor/libgit2/src/remote.c b/vendor/libgit2/src/remote.c new file mode 100644 index 000000000..297789a69 --- /dev/null +++ b/vendor/libgit2/src/remote.c @@ -0,0 +1,283 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "git2/remote.h" +#include "git2/config.h" +#include "git2/types.h" + +#include "config.h" +#include "repository.h" +#include "remote.h" +#include "fetch.h" +#include "refs.h" + +static int refspec_parse(git_refspec *refspec, const char *str) +{ + char *delim; + + memset(refspec, 0x0, sizeof(git_refspec)); + + if (*str == '+') { + refspec->force = 1; + str++; + } + + delim = strchr(str, ':'); + if (delim == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'"); + + refspec->src = git__strndup(str, delim - str); + if (refspec->src == NULL) + return GIT_ENOMEM; + + refspec->dst = git__strdup(delim + 1); + if (refspec->dst == NULL) { + free(refspec->src); + refspec->src = NULL; + return GIT_ENOMEM; + } + + return GIT_SUCCESS; +} + +static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var) +{ + const char *val; + int error; + + error = git_config_get_string(cfg, var, &val); + if (error < GIT_SUCCESS) + return error; + + return refspec_parse(refspec, val); +} + +int git_remote_new(git_remote **out, git_repository *repo, const char *url) +{ + git_remote *remote; + + remote = git__malloc(sizeof(git_remote)); + if (remote == NULL) + return GIT_ENOMEM; + + memset(remote, 0x0, sizeof(git_remote)); + remote->repo = repo; + remote->url = git__strdup(url); + if (remote->url == NULL) { + free(remote); + return GIT_ENOMEM; + } + + *out = remote; + return GIT_SUCCESS; +} + +int git_remote_get(git_remote **out, git_config *cfg, const char *name) +{ + git_remote *remote; + char *buf = NULL; + const char *val; + int ret, error, buf_len; + + remote = git__malloc(sizeof(git_remote)); + if (remote == NULL) + return GIT_ENOMEM; + + memset(remote, 0x0, sizeof(git_remote)); + remote->name = git__strdup(name); + if (remote->name == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + /* "fetch" is the longest var name we're interested in */ + buf_len = strlen("remote.") + strlen(".fetch") + strlen(name) + 1; + buf = git__malloc(buf_len); + if (buf == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "url"); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to build config var name"); + goto cleanup; + } + + error = git_config_get_string(cfg, buf, &val); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Remote's url doesn't exist"); + goto cleanup; + } + + remote->repo = cfg->repo; + remote->url = git__strdup(val); + if (remote->url == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "fetch"); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to build config var name"); + goto cleanup; + } + + error = parse_remote_refspec(cfg, &remote->fetch, buf); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to get fetch refspec"); + goto cleanup; + } + + ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "push"); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to build config var name"); + goto cleanup; + } + + error = parse_remote_refspec(cfg, &remote->push, buf); + /* Not finding push is fine */ + if (error == GIT_ENOTFOUND) + error = GIT_SUCCESS; + + if (error < GIT_SUCCESS) + goto cleanup; + + *out = remote; + +cleanup: + free(buf); + if (error < GIT_SUCCESS) + git_remote_free(remote); + + return error; +} + +const char *git_remote_name(struct git_remote *remote) +{ + return remote->name; +} + +const char *git_remote_url(struct git_remote *remote) +{ + return remote->url; +} + +const git_refspec *git_remote_fetchspec(struct git_remote *remote) +{ + return &remote->fetch; +} + +const git_refspec *git_remote_pushspec(struct git_remote *remote) +{ + return &remote->push; +} + +int git_remote_connect(git_remote *remote, int direction) +{ + int error; + git_transport *t; + + error = git_transport_new(&t, remote->url); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create transport"); + + error = t->connect(t, direction); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to connect the transport"); + goto cleanup; + } + + remote->transport = t; + +cleanup: + if (error < GIT_SUCCESS) + t->free(t); + + return error; +} + +int git_remote_ls(git_remote *remote, git_headarray *refs) +{ + return remote->transport->ls(remote->transport, refs); +} + +int git_remote_negotiate(git_remote *remote) +{ + return git_fetch_negotiate(remote); +} + +int git_remote_download(char **filename, git_remote *remote) +{ + return git_fetch_download_pack(filename, remote); +} + +git_headarray *git_remote_tips(git_remote *remote) +{ + return &remote->refs; +} + +int git_remote_update_tips(struct git_remote *remote) +{ + int error = GIT_SUCCESS; + unsigned int i; + char refname[GIT_PATH_MAX]; + git_headarray *refs = &remote->refs; + git_remote_head *head; + git_reference *ref; + struct git_refspec *spec = &remote->fetch; + + memset(refname, 0x0, sizeof(refname)); + + for (i = 0; i < refs->len; ++i) { + head = refs->heads[i]; + error = git_refspec_transform(refname, sizeof(refname), spec, head->name); + if (error < GIT_SUCCESS) + return error; + + error = git_reference_create_oid(&ref, remote->repo, refname, &head->oid, 1); + if (error < GIT_SUCCESS) + return error; + } + + return GIT_SUCCESS; +} + +void git_remote_free(git_remote *remote) +{ + free(remote->fetch.src); + free(remote->fetch.dst); + free(remote->push.src); + free(remote->push.dst); + free(remote->url); + free(remote->name); + if (remote->transport != NULL) { + if (remote->transport->connected) + remote->transport->close(remote->transport); + + remote->transport->free(remote->transport); + } + free(remote); +} diff --git a/vendor/libgit2/src/remote.h b/vendor/libgit2/src/remote.h new file mode 100644 index 000000000..21313acd4 --- /dev/null +++ b/vendor/libgit2/src/remote.h @@ -0,0 +1,19 @@ +#ifndef INCLUDE_remote_h__ +#define INCLUDE_remote_h__ + +#include "refspec.h" +#include "transport.h" +#include "repository.h" + +struct git_remote { + char *name; + char *url; + git_headarray refs; + struct git_refspec fetch; + struct git_refspec push; + git_transport *transport; + git_repository *repo; + int need_pack:1; +}; + +#endif diff --git a/vendor/libgit2/src/repository.c b/vendor/libgit2/src/repository.c index 8cc2644ca..1b06c4f03 100644 --- a/vendor/libgit2/src/repository.c +++ b/vendor/libgit2/src/repository.c @@ -32,18 +32,15 @@ #include "tag.h" #include "blob.h" #include "fileops.h" - +#include "config.h" #include "refs.h" #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/" #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/" -#define GIT_BRANCH_MASTER "master" +#define GIT_FILE_CONTENT_PREFIX "gitdir: " -typedef struct { - char *path_repository; - unsigned is_bare:1, has_been_reinit:1; -} repo_init; +#define GIT_BRANCH_MASTER "master" /* * Git repository open methods @@ -63,11 +60,11 @@ static int assign_repository_dirs( assert(repo); if (git_dir == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to open repository. Git dir not found"); - error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_dir); + error = git_path_prettify_dir(path_aux, git_dir, NULL); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to open repository"); /* store GIT_DIR */ repo->path_repository = git__strdup(path_aux); @@ -76,11 +73,11 @@ static int assign_repository_dirs( /* path to GIT_OBJECT_DIRECTORY */ if (git_object_directory == NULL) - git__joinpath(path_aux, repo->path_repository, GIT_OBJECTS_DIR); + git_path_join(path_aux, repo->path_repository, GIT_OBJECTS_DIR); else { - error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_object_directory); + error = git_path_prettify_dir(path_aux, git_object_directory, NULL); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to open repository"); } /* Store GIT_OBJECT_DIRECTORY */ @@ -92,9 +89,9 @@ static int assign_repository_dirs( if (git_work_tree == NULL) repo->is_bare = 1; else { - error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_work_tree); + error = git_path_prettify_dir(path_aux, git_work_tree, NULL); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to open repository"); /* Store GIT_WORK_TREE */ repo->path_workdir = git__strdup(path_aux); @@ -103,11 +100,11 @@ static int assign_repository_dirs( /* Path to GIT_INDEX_FILE */ if (git_index_file == NULL) - git__joinpath(path_aux, repo->path_repository, GIT_INDEX_FILE); + git_path_join(path_aux, repo->path_repository, GIT_INDEX_FILE); else { - error = gitfo_prettify_file_path(path_aux, sizeof(path_aux), git_index_file); + error = git_path_prettify(path_aux, git_index_file, NULL); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to open repository"); } /* store GIT_INDEX_FILE */ @@ -115,7 +112,7 @@ static int assign_repository_dirs( if (repo->path_index == NULL) return GIT_ENOMEM; } - + return GIT_SUCCESS; } @@ -123,17 +120,17 @@ static int check_repository_dirs(git_repository *repo) { char path_aux[GIT_PATH_MAX]; - if (gitfo_isdir(repo->path_repository) < GIT_SUCCESS) - return GIT_ENOTAREPO; + if (git_futils_isdir(repo->path_repository) < GIT_SUCCESS) + return git__throw(GIT_ENOTAREPO, "`%s` is not a folder", repo->path_repository); /* Ensure GIT_OBJECT_DIRECTORY exists */ - if (gitfo_isdir(repo->path_odb) < GIT_SUCCESS) - return GIT_ENOTAREPO; + if (git_futils_isdir(repo->path_odb) < GIT_SUCCESS) + return git__throw(GIT_ENOTAREPO, "`%s` does not exist", repo->path_odb); /* Ensure HEAD file exists */ - git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE); - if (gitfo_exists(path_aux) < 0) - return GIT_ENOTAREPO; + git_path_join(path_aux, repo->path_repository, GIT_HEAD_FILE); + if (git_futils_isfile(path_aux) < 0) + return git__throw(GIT_ENOTAREPO, "HEAD file is missing"); return GIT_SUCCESS; } @@ -144,28 +141,54 @@ static int guess_repository_dirs(git_repository *repo, const char *repository_pa const char *path_work_tree = NULL; /* Git directory name */ - if (git__basename_r(buffer, sizeof(buffer), repository_path) < 0) - return GIT_EINVALIDPATH; + if (git_path_basename_r(buffer, sizeof(buffer), repository_path) < 0) + return git__throw(GIT_EINVALIDPATH, "Unable to parse folder name from `%s`", repository_path); if (strcmp(buffer, DOT_GIT) == 0) { /* Path to working dir */ - if (git__dirname_r(buffer, sizeof(buffer), repository_path) < 0) - return GIT_EINVALIDPATH; + if (git_path_dirname_r(buffer, sizeof(buffer), repository_path) < 0) + return git__throw(GIT_EINVALIDPATH, "Unable to parse parent folder name from `%s`", repository_path); path_work_tree = buffer; } return assign_repository_dirs(repo, repository_path, NULL, NULL, path_work_tree); } +static int quickcheck_repository_dir(const char *repository_path) +{ + char path_aux[GIT_PATH_MAX]; + + /* Ensure HEAD file exists */ + git_path_join(path_aux, repository_path, GIT_HEAD_FILE); + if (git_futils_isfile(path_aux) < 0) + return GIT_ERROR; + + git_path_join(path_aux, repository_path, GIT_OBJECTS_DIR); + if (git_futils_isdir(path_aux) < 0) + return GIT_ERROR; + + git_path_join(path_aux, repository_path, GIT_REFS_DIR); + if (git_futils_isdir(path_aux) < 0) + return GIT_ERROR; + + return GIT_SUCCESS; +} + static git_repository *repository_alloc() { + int error; + git_repository *repo = git__malloc(sizeof(git_repository)); if (!repo) return NULL; memset(repo, 0x0, sizeof(git_repository)); - git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); + error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); + if (error < GIT_SUCCESS) { + free(repo); + return NULL; + } if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { free(repo); @@ -192,14 +215,14 @@ int git_repository_open3(git_repository **repo_out, assert(repo_out); if (object_database == NULL) - return GIT_ERROR; + return git__throw(GIT_EINVALIDARGS, "Failed to open repository. `object_database` can't be null"); repo = repository_alloc(); if (repo == NULL) return GIT_ENOMEM; - error = assign_repository_dirs(repo, - git_dir, + error = assign_repository_dirs(repo, + git_dir, NULL, git_index_file, git_work_tree); @@ -218,7 +241,7 @@ int git_repository_open3(git_repository **repo_out, cleanup: git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to open repository"); } @@ -238,7 +261,7 @@ int git_repository_open2(git_repository **repo_out, return GIT_ENOMEM; error = assign_repository_dirs(repo, - git_dir, + git_dir, git_object_directory, git_index_file, git_work_tree); @@ -259,9 +282,64 @@ int git_repository_open2(git_repository **repo_out, cleanup: git_repository_free(repo); + return git__rethrow(error, "Failed to open repository"); +} + +int git_repository_config( + git_config **out, + git_repository *repo, + const char *user_config_path, + const char *system_config_path) +{ + char config_path[GIT_PATH_MAX]; + int error; + + assert(out && repo); + + error = git_config_new(out); + if (error < GIT_SUCCESS) + return error; + + git_path_join(config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO); + error = git_config_add_file_ondisk(*out, config_path, 3); + if (error < GIT_SUCCESS) + goto cleanup; + + if (user_config_path != NULL) { + error = git_config_add_file_ondisk(*out, user_config_path, 2); + if (error < GIT_SUCCESS) + goto cleanup; + } + + if (system_config_path != NULL) { + error = git_config_add_file_ondisk(*out, system_config_path, 1); + if (error < GIT_SUCCESS) + goto cleanup; + } + + (*out)->repo = repo; + return GIT_SUCCESS; + +cleanup: + git_config_free(*out); return error; } +static int discover_repository_dirs(git_repository *repo, const char *path) +{ + int error; + + error = guess_repository_dirs(repo, path); + if (error < GIT_SUCCESS) + return error; + + error = check_repository_dirs(repo); + if (error < GIT_SUCCESS) + return error; + + return GIT_SUCCESS; +} + int git_repository_open(git_repository **repo_out, const char *path) { git_repository *repo; @@ -273,11 +351,7 @@ int git_repository_open(git_repository **repo_out, const char *path) if (repo == NULL) return GIT_ENOMEM; - error = guess_repository_dirs(repo, path); - if (error < GIT_SUCCESS) - goto cleanup; - - error = check_repository_dirs(repo); + error = discover_repository_dirs(repo, path); if (error < GIT_SUCCESS) goto cleanup; @@ -290,46 +364,225 @@ int git_repository_open(git_repository **repo_out, const char *path) cleanup: git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to open repository"); } -void git_repository_free(git_repository *repo) +static int retrieve_device(dev_t *device_out, const char *path) { - if (repo == NULL) - return; + struct stat path_info; - git_cache_free(&repo->objects); - git_repository__refcache_free(&repo->references); + assert(device_out); + + if (p_lstat(path, &path_info)) + return git__throw(GIT_EOSERR, "Failed to get file informations: %s", path); + + *device_out = path_info.st_dev; + + return GIT_SUCCESS; +} + +static int retrieve_ceiling_directories_offset(const char *path, const char *ceiling_directories) +{ + char buf[GIT_PATH_MAX + 1]; + char buf2[GIT_PATH_MAX + 1]; + const char *ceil, *sep; + int len, max_len = -1; + int min_len; + + assert(path); + + min_len = git_path_root(path) + 1; + + if (ceiling_directories == NULL || min_len == 0) + return min_len; + + for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) { + for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); + len = sep - ceil; + + if (len == 0 || len > GIT_PATH_MAX || git_path_root(ceil) == -1) + continue; + + strncpy(buf, ceil, len); + buf[len] = '\0'; + + if (p_realpath(buf, buf2) == NULL) + continue; + + len = strlen(buf2); + if (len > 0 && buf2[len-1] == '/') + buf[--len] = '\0'; + + if (!strncmp(path, buf2, len) && + path[len] == '/' && + len > max_len) + { + max_len = len; + } + } + + return max_len <= min_len ? min_len : max_len; +} + +static int read_gitfile(char *path_out, const char *file_path, const char *base_path) +{ + git_fbuffer file; + int error; + size_t end_offset; + char *data; + + assert(path_out && file_path && base_path); + + error = git_futils_readbuffer(&file, file_path); + + if (error < GIT_SUCCESS) + return error; + + data = (char*)(file.data); + + if (git__prefixcmp(data, GIT_FILE_CONTENT_PREFIX)) { + git_futils_freebuffer(&file); + return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path); + } + + end_offset = strlen(data) - 1; + + for (;data[end_offset] == '\r' || data[end_offset] == '\n'; --end_offset); + data[end_offset + 1] = '\0'; + + if (strlen(GIT_FILE_CONTENT_PREFIX) == end_offset + 1) { + git_futils_freebuffer(&file); + return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path); + } + data = data + strlen(GIT_FILE_CONTENT_PREFIX); + error = git_path_prettify_dir(path_out, data, base_path); + git_futils_freebuffer(&file); + + if (error == 0 && git_futils_exists(path_out) == 0) + return GIT_SUCCESS; + + return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to an inexisting path"); +} + +static void git_repository__free_dirs(git_repository *repo) +{ free(repo->path_workdir); + repo->path_workdir = NULL; free(repo->path_index); + repo->path_index = NULL; free(repo->path_repository); + repo->path_repository = NULL; free(repo->path_odb); + repo->path_odb = NULL; +} + +void git_repository_free(git_repository *repo) +{ + if (repo == NULL) + return; + + git_cache_free(&repo->objects); + git_repository__refcache_free(&repo->references); + git_repository__free_dirs(repo); if (repo->db != NULL) git_odb_close(repo->db); - if (repo->index != NULL) - git_index_free(repo->index); - free(repo); } -int git_repository_index(git_index **index_out, git_repository *repo) +int git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs) { - int error; + int error, ceiling_offset; + char bare_path[GIT_PATH_MAX]; + char normal_path[GIT_PATH_MAX]; + char *found_path; + dev_t current_device = 0; - assert(index_out && repo); + assert(start_path && repository_path); - if (repo->index == NULL) { - error = git_index_open_inrepo(&repo->index, repo); + error = git_path_prettify_dir(bare_path, start_path, NULL); + if (error < GIT_SUCCESS) + return error; + + if (!across_fs) { + error = retrieve_device(¤t_device, bare_path); if (error < GIT_SUCCESS) return error; + } + + ceiling_offset = retrieve_ceiling_directories_offset(bare_path, ceiling_dirs); + git_path_join(normal_path, bare_path, DOT_GIT); + + while(1) { + /** + * If the `.git` file is regular instead of + * a directory, it should contain the path of the actual git repository + */ + if (git_futils_isfile(normal_path) == GIT_SUCCESS) { + error = read_gitfile(repository_path, normal_path, bare_path); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Unable to read git file `%s`", normal_path); + + error = quickcheck_repository_dir(repository_path); + if (error < GIT_SUCCESS) + return git__throw(GIT_ENOTFOUND, "The `.git` file found at '%s' points" + "to an inexisting Git folder", normal_path); + + return GIT_SUCCESS; + } - assert(repo->index != NULL); + /** + * If the `.git` file is a folder, we check inside of it + */ + if (git_futils_isdir(normal_path) == GIT_SUCCESS) { + error = quickcheck_repository_dir(normal_path); + if (error == GIT_SUCCESS) { + found_path = normal_path; + break; + } + } + + /** + * Otherwise, the repository may be bare, let's check + * the root anyway + */ + error = quickcheck_repository_dir(bare_path); + if (error == GIT_SUCCESS) { + found_path = bare_path; + break; + } + + if (git_path_dirname_r(normal_path, sizeof(normal_path), bare_path) < GIT_SUCCESS) + return git__throw(GIT_EOSERR, "Failed to dirname '%s'", bare_path); + + if (!across_fs) { + dev_t new_device; + error = retrieve_device(&new_device, normal_path); + + if (error < GIT_SUCCESS || current_device != new_device) { + return git__throw(GIT_ENOTAREPO,"Not a git repository (or any parent up to mount parent %s)\n" + "Stopping at filesystem boundary.", bare_path); + } + current_device = new_device; + } + + strcpy(bare_path, normal_path); + git_path_join(normal_path, bare_path, DOT_GIT); + + // nothing has been found, lets try the parent directory + if (bare_path[ceiling_offset] == '\0') { + return git__throw(GIT_ENOTAREPO,"Not a git repository (or any of the parent directories): %s", start_path); + } } - *index_out = repo->index; + if (size < (strlen(found_path) + 2) * sizeof(char)) { + return git__throw(GIT_EOVERFLOW, "The repository buffer is not long enough to handle the repository path `%s`", found_path); + } + + git_path_join(repository_path, found_path, ""); return GIT_SUCCESS; } @@ -339,78 +592,65 @@ git_odb *git_repository_database(git_repository *repo) return repo->db; } -static int repo_init_reinit(repo_init *results) +static int repo_init_reinit(const char *repository_path, int is_bare) { /* TODO: reinit the repository */ - results->has_been_reinit = 1; - return GIT_ENOTIMPLEMENTED; + return git__throw(GIT_ENOTIMPLEMENTED, + "Failed to reinitialize the %srepository at '%s'. " + "This feature is not yet implemented", + is_bare ? "bare" : "", repository_path); } static int repo_init_createhead(git_repository *repo) { git_reference *head_reference; - return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); + return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE, 0); } -static int repo_init_check_head_existence(char * repository_path) -{ - char temp_path[GIT_PATH_MAX]; - - git__joinpath(temp_path, repository_path, GIT_HEAD_FILE); - return gitfo_exists(temp_path); -} - -static int repo_init_structure(repo_init *results) +static int repo_init_structure(const char *git_dir, int is_bare) { const int mode = 0755; /* or 0777 ? */ + int error; char temp_path[GIT_PATH_MAX]; - char *git_dir = results->path_repository; - if (gitfo_mkdir_recurs(git_dir, mode)) - return GIT_ERROR; + if (git_futils_mkdir_r(git_dir, mode)) + return git__throw(GIT_ERROR, "Failed to initialize repository structure. Could not mkdir"); + + /* Hides the ".git" directory */ + if (!is_bare) { +#ifdef GIT_WIN32 + error = p_hide_directory__w32(git_dir); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to initialize repository structure"); +#endif + } /* Creates the '/objects/info/' directory */ - git__joinpath(temp_path, git_dir, GIT_OBJECTS_INFO_DIR); - if (gitfo_mkdir_recurs(temp_path, mode) < GIT_SUCCESS) - return GIT_ERROR; + git_path_join(temp_path, git_dir, GIT_OBJECTS_INFO_DIR); + error = git_futils_mkdir_r(temp_path, mode); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to initialize repository structure"); /* Creates the '/objects/pack/' directory */ - git__joinpath(temp_path, git_dir, GIT_OBJECTS_PACK_DIR); - if (gitfo_mkdir(temp_path, mode)) - return GIT_ERROR; + git_path_join(temp_path, git_dir, GIT_OBJECTS_PACK_DIR); + error = p_mkdir(temp_path, mode); + if (error < GIT_SUCCESS) + return git__throw(error, "Unable to create `%s` folder", temp_path); /* Creates the '/refs/heads/' directory */ - git__joinpath(temp_path, git_dir, GIT_REFS_HEADS_DIR); - if (gitfo_mkdir_recurs(temp_path, mode)) - return GIT_ERROR; + git_path_join(temp_path, git_dir, GIT_REFS_HEADS_DIR); + error = git_futils_mkdir_r(temp_path, mode); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to initialize repository structure"); /* Creates the '/refs/tags/' directory */ - git__joinpath(temp_path, git_dir, GIT_REFS_TAGS_DIR); - if (gitfo_mkdir(temp_path, mode)) - return GIT_ERROR; - - /* TODO: what's left? templates? */ - - return GIT_SUCCESS; -} - -static int repo_init_find_dir(repo_init *results, const char* path) -{ - char temp_path[GIT_PATH_MAX]; - int error = GIT_SUCCESS; - - error = gitfo_prettify_dir_path(temp_path, sizeof(temp_path), path); + git_path_join(temp_path, git_dir, GIT_REFS_TAGS_DIR); + error = p_mkdir(temp_path, mode); if (error < GIT_SUCCESS) - return error; - - if (!results->is_bare) { - git__joinpath(temp_path, temp_path, GIT_DIR); - } + return git__throw(error, "Unable to create `%s` folder", temp_path); - results->path_repository = git__strdup(temp_path); - if (results->path_repository == NULL) - return GIT_ENOMEM; + /* TODO: what's left? templates? */ return GIT_SUCCESS; } @@ -419,21 +659,18 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is { int error = GIT_SUCCESS; git_repository *repo = NULL; - repo_init results; - - assert(repo_out && path); + char repository_path[GIT_PATH_MAX]; - results.path_repository = NULL; - results.is_bare = is_bare; + assert(repo_out && path); - error = repo_init_find_dir(&results, path); - if (error < GIT_SUCCESS) - goto cleanup; + git_path_join(repository_path, path, is_bare ? "" : GIT_DIR); - if (!repo_init_check_head_existence(results.path_repository)) - return repo_init_reinit(&results); + if (git_futils_isdir(repository_path)) { + if (quickcheck_repository_dir(repository_path) == GIT_SUCCESS) + return repo_init_reinit(repository_path, is_bare); + } - error = repo_init_structure(&results); + error = repo_init_structure(repository_path, is_bare); if (error < GIT_SUCCESS) goto cleanup; @@ -443,7 +680,7 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is goto cleanup; } - error = guess_repository_dirs(repo, results.path_repository); + error = guess_repository_dirs(repo, repository_path); if (error < GIT_SUCCESS) goto cleanup; @@ -460,14 +697,53 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is /* should never fail */ assert(check_repository_dirs(repo) == GIT_SUCCESS); - free(results.path_repository); *repo_out = repo; return GIT_SUCCESS; cleanup: - free(results.path_repository); git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to (re)init the repository `%s`", path); +} + +int git_repository_head_detached(git_repository *repo) +{ + git_reference *ref; + int error; + size_t GIT_UNUSED(_size); + git_otype type; + + error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) + return 0; + + error = git_odb_read_header(&_size, &type, repo->db, git_reference_oid(ref)); + if (error < GIT_SUCCESS) + return error; + + if (type != GIT_OBJ_COMMIT) + return git__throw(GIT_EOBJCORRUPTED, "HEAD is not a commit"); + + return 1; +} + +int git_repository_head_orphan(git_repository *repo) +{ + git_reference *ref; + int error; + + error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); + if (error < GIT_SUCCESS) + return error; + + if (git_reference_type(ref) == GIT_REF_OID) + return 0; + + error = git_reference_resolve(&ref, ref); + + return error == GIT_ENOTFOUND ? 1 : error; } int git_repository_is_empty(git_repository *repo) @@ -477,22 +753,42 @@ int git_repository_is_empty(git_repository *repo) error = git_reference_lookup(&head, repo, "HEAD"); if (error < GIT_SUCCESS) - return error; + return git__throw(error, "Corrupted repository. HEAD does not exist"); if (git_reference_type(head) != GIT_REF_SYMBOLIC) - return GIT_EOBJCORRUPTED; + return 0; + + if (strcmp(git_reference_target(head), "refs/heads/master") != 0) + return 0; - return git_reference_resolve(&branch, head) == GIT_SUCCESS ? 0 : 1; + error = git_reference_resolve(&branch, head); + return error == GIT_ENOTFOUND ? 1 : error; } -const char *git_repository_path(git_repository *repo) +const char *git_repository_path(git_repository *repo, git_repository_pathid id) { assert(repo); - return repo->path_repository; + + switch (id) { + case GIT_REPO_PATH: + return repo->path_repository; + + case GIT_REPO_PATH_INDEX: + return repo->path_index; + + case GIT_REPO_PATH_ODB: + return repo->path_odb; + + case GIT_REPO_PATH_WORKDIR: + return repo->path_workdir; + + default: + return NULL; + } } -const char *git_repository_workdir(git_repository *repo) +int git_repository_is_bare(git_repository *repo) { assert(repo); - return repo->path_workdir; + return repo->is_bare; } diff --git a/vendor/libgit2/src/repository.h b/vendor/libgit2/src/repository.h index 813cac942..2e0b9e352 100644 --- a/vendor/libgit2/src/repository.h +++ b/vendor/libgit2/src/repository.h @@ -11,6 +11,7 @@ #include "index.h" #include "cache.h" #include "refs.h" +#include "buffer.h" #define DOT_GIT ".git" #define GIT_DIR DOT_GIT "/" @@ -25,7 +26,6 @@ struct git_object { struct git_repository { git_odb *db; - git_index *index; git_cache objects; git_refcache references; @@ -43,7 +43,7 @@ struct git_repository { * export */ void git_object__free(void *object); -int git__parse_oid(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); -int git__write_oid(git_odb_stream *src, const char *header, const git_oid *oid); +int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header); +void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid); #endif diff --git a/vendor/libgit2/src/revwalk.c b/vendor/libgit2/src/revwalk.c index 78798480f..538928cb1 100644 --- a/vendor/libgit2/src/revwalk.c +++ b/vendor/libgit2/src/revwalk.c @@ -115,9 +115,8 @@ static int commit_time_cmp(void *a, void *b) static uint32_t object_table_hash(const void *key, int hash_id) { uint32_t r; - git_oid *id; + const git_oid *id = key; - id = (git_oid *)key; memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); return r; } @@ -184,7 +183,7 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid) static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw) { - const int parent_len = STRLEN("parent ") + GIT_OID_HEXSZ + 1; + const int parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1; unsigned char *buffer = raw->data; unsigned char *buffer_end = buffer + raw->len; @@ -193,10 +192,10 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo int i, parents = 0; long commit_time; - buffer += STRLEN("tree ") + GIT_OID_HEXSZ + 1; + buffer += strlen("tree ") + GIT_OID_HEXSZ + 1; parents_start = buffer; - while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", STRLEN("parent ")) == 0) { + while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) { parents++; buffer += parent_len; } @@ -209,8 +208,8 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo for (i = 0; i < parents; ++i) { git_oid oid; - if (git_oid_mkstr(&oid, (char *)buffer + STRLEN("parent ")) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < GIT_SUCCESS) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Parent object is corrupted"); commit->parents[i] = commit_lookup(walk, &oid); if (commit->parents[i] == NULL) @@ -222,14 +221,14 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo commit->out_degree = (unsigned short)parents; if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Object is corrupted"); buffer = memchr(buffer, '>', buffer_end - buffer); if (buffer == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't find author"); if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't parse commit time"); commit->time = (time_t)commit_time; commit->parsed = 1; @@ -245,16 +244,16 @@ static int commit_parse(git_revwalk *walk, commit_object *commit) return GIT_SUCCESS; if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to parse commit. Can't read object"); if (obj->raw.type != GIT_OBJ_COMMIT) { git_odb_object_close(obj); - return GIT_EOBJTYPE; + return git__throw(GIT_EOBJTYPE, "Failed to parse commit. Object is no commit object"); } error = commit_quick_parse(walk, commit, &obj->raw); git_odb_object_close(obj); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse commit"); } static void mark_uninteresting(commit_object *commit) @@ -269,20 +268,20 @@ static void mark_uninteresting(commit_object *commit) mark_uninteresting(commit->parents[i]); } -static int process_commit(git_revwalk *walk, commit_object *commit) +static int process_commit(git_revwalk *walk, commit_object *commit, int hide) { int error; + if (hide) + mark_uninteresting(commit); + if (commit->seen) return GIT_SUCCESS; commit->seen = 1; if ((error = commit_parse(walk, commit)) < GIT_SUCCESS) - return error; - - if (commit->uninteresting) - mark_uninteresting(commit); + return git__rethrow(error, "Failed to process commit"); return walk->enqueue(walk, commit); } @@ -293,10 +292,10 @@ static int process_commit_parents(git_revwalk *walk, commit_object *commit) int error = GIT_SUCCESS; for (i = 0; i < commit->out_degree && error == GIT_SUCCESS; ++i) { - error = process_commit(walk, commit->parents[i]); + error = process_commit(walk, commit->parents[i], commit->uninteresting); } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to process commit parents"); } static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) @@ -305,11 +304,9 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) commit = commit_lookup(walk, oid); if (commit == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to push commit. Object not found"); - commit->uninteresting = uninteresting; - - return process_commit(walk, commit); + return process_commit(walk, commit, uninteresting); } int git_revwalk_push(git_revwalk *walk, const git_oid *oid) @@ -341,7 +338,7 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk) while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to load next revision"); if (!next->uninteresting) { *object_out = next; @@ -349,7 +346,7 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk) } } - return GIT_EREVWALKOVER; + return git__throw(GIT_EREVWALKOVER, "Failed to load next revision"); } static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) @@ -359,7 +356,7 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) { if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to load next revision"); if (!next->uninteresting) { *object_out = next; @@ -367,7 +364,7 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) } } - return GIT_EREVWALKOVER; + return git__throw(GIT_EREVWALKOVER, "Failed to load next revision"); } static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) @@ -378,7 +375,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) for (;;) { next = commit_list_pop(&walk->iterator_topo); if (next == NULL) - return GIT_EREVWALKOVER; + return git__throw(GIT_EREVWALKOVER, "Failed to load next revision"); if (next->in_degree > 0) { next->topo_delay = 1; @@ -424,7 +421,7 @@ static int prepare_walk(git_revwalk *walk) } if (error != GIT_EREVWALKOVER) - return error; + return git__rethrow(error, "Failed to prepare revision walk"); walk->get_next = &revwalk_next_toposort; } @@ -435,7 +432,7 @@ static int prepare_walk(git_revwalk *walk) commit_list_insert(next, &walk->iterator_reverse); if (error != GIT_EREVWALKOVER) - return error; + return git__rethrow(error, "Failed to prepare revision walk"); walk->get_next = &revwalk_next_reverse; } @@ -542,16 +539,19 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) if (!walk->walking) { if ((error = prepare_walk(walk)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to load next revision"); } error = walk->get_next(&next, walk); - if (error < GIT_SUCCESS) { - if (error == GIT_EREVWALKOVER) - git_revwalk_reset(walk); - return error; + + if (error == GIT_EREVWALKOVER) { + git_revwalk_reset(walk); + return GIT_EREVWALKOVER; } + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to load next revision"); + git_oid_cpy(oid, &next->oid); return GIT_SUCCESS; } diff --git a/vendor/libgit2/src/sha1.c b/vendor/libgit2/src/sha1.c new file mode 100644 index 000000000..0bccb4953 --- /dev/null +++ b/vendor/libgit2/src/sha1.c @@ -0,0 +1,281 @@ +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was initially based on the Mozilla SHA1 implementation, although + * none of the original Mozilla code remains. + */ + +#include "common.h" +#include "sha1.h" + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +/* + * Force usage of rol or ror by selecting the one with the smaller constant. + * It _can_ generate slightly smaller code (a constant of 1 is special), but + * perhaps more importantly it's possibly faster on any uarch that does a + * rotate with a loop. + */ + +#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; }) +#define SHA_ROL(x,n) SHA_ASM("rol", x, n) +#define SHA_ROR(x,n) SHA_ASM("ror", x, n) + +#else + +#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r))) +#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n)) +#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n) + +#endif + +/* + * If you have 32 registers or more, the compiler can (and should) + * try to change the array[] accesses into registers. However, on + * machines with less than ~25 registers, that won't really work, + * and at least gcc will make an unholy mess of it. + * + * So to avoid that mess which just slows things down, we force + * the stores to memory to actually happen (we might be better off + * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as + * suggested by Artur Skawina - that will also make gcc unable to + * try to do the silly "optimize away loads" part because it won't + * see what the value will be). + * + * Ben Herrenschmidt reports that on PPC, the C version comes close + * to the optimized asm with this (ie on PPC you don't want that + * 'volatile', since there are lots of registers). + * + * On ARM we get the best code generation by forcing a full memory barrier + * between each SHA_ROUND, otherwise gcc happily get wild with spilling and + * the stack frame size simply explode and performance goes down the drain. + */ + +#if defined(__i386__) || defined(__x86_64__) + #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val)) +#elif defined(__GNUC__) && defined(__arm__) + #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0) +#else + #define setW(x, val) (W(x) = (val)) +#endif + +/* + * Performance might be improved if the CPU architecture is OK with + * unaligned 32-bit loads and a fast ntohl() is available. + * Otherwise fall back to byte loads and shifts which is portable, + * and is faster on architectures with memory alignment issues. + */ + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__s390__) || defined(__s390x__) + +#define get_be32(p) ntohl(*(const unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#else + +#define get_be32(p) ( \ + (*((const unsigned char *)(p) + 0) << 24) | \ + (*((const unsigned char *)(p) + 1) << 16) | \ + (*((const unsigned char *)(p) + 2) << 8) | \ + (*((const unsigned char *)(p) + 3) << 0) ) +#define put_be32(p, v) do { \ + unsigned int __v = (v); \ + *((unsigned char *)(p) + 0) = __v >> 24; \ + *((unsigned char *)(p) + 1) = __v >> 16; \ + *((unsigned char *)(p) + 2) = __v >> 8; \ + *((unsigned char *)(p) + 3) = __v >> 0; } while (0) + +#endif + +/* This "rolls" over the 512-bit array */ +#define W(x) (array[(x)&15]) + +/* + * Where do we get the source from? The first 16 iterations get it from + * the input data, the next mix it from the 512-bit array. + */ +#define SHA_SRC(t) get_be32(data + t) +#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1) + +#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \ + unsigned int TEMP = input(t); setW(t, TEMP); \ + E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \ + B = SHA_ROR(B, 2); } while (0) + +#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E ) +#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E ) +#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E ) +#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E ) + +static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data) +{ + unsigned int A,B,C,D,E; + unsigned int array[16]; + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + + /* Round 1 - iterations 0-16 take their input from 'data' */ + T_0_15( 0, A, B, C, D, E); + T_0_15( 1, E, A, B, C, D); + T_0_15( 2, D, E, A, B, C); + T_0_15( 3, C, D, E, A, B); + T_0_15( 4, B, C, D, E, A); + T_0_15( 5, A, B, C, D, E); + T_0_15( 6, E, A, B, C, D); + T_0_15( 7, D, E, A, B, C); + T_0_15( 8, C, D, E, A, B); + T_0_15( 9, B, C, D, E, A); + T_0_15(10, A, B, C, D, E); + T_0_15(11, E, A, B, C, D); + T_0_15(12, D, E, A, B, C); + T_0_15(13, C, D, E, A, B); + T_0_15(14, B, C, D, E, A); + T_0_15(15, A, B, C, D, E); + + /* Round 1 - tail. Input from 512-bit mixing array */ + T_16_19(16, E, A, B, C, D); + T_16_19(17, D, E, A, B, C); + T_16_19(18, C, D, E, A, B); + T_16_19(19, B, C, D, E, A); + + /* Round 2 */ + T_20_39(20, A, B, C, D, E); + T_20_39(21, E, A, B, C, D); + T_20_39(22, D, E, A, B, C); + T_20_39(23, C, D, E, A, B); + T_20_39(24, B, C, D, E, A); + T_20_39(25, A, B, C, D, E); + T_20_39(26, E, A, B, C, D); + T_20_39(27, D, E, A, B, C); + T_20_39(28, C, D, E, A, B); + T_20_39(29, B, C, D, E, A); + T_20_39(30, A, B, C, D, E); + T_20_39(31, E, A, B, C, D); + T_20_39(32, D, E, A, B, C); + T_20_39(33, C, D, E, A, B); + T_20_39(34, B, C, D, E, A); + T_20_39(35, A, B, C, D, E); + T_20_39(36, E, A, B, C, D); + T_20_39(37, D, E, A, B, C); + T_20_39(38, C, D, E, A, B); + T_20_39(39, B, C, D, E, A); + + /* Round 3 */ + T_40_59(40, A, B, C, D, E); + T_40_59(41, E, A, B, C, D); + T_40_59(42, D, E, A, B, C); + T_40_59(43, C, D, E, A, B); + T_40_59(44, B, C, D, E, A); + T_40_59(45, A, B, C, D, E); + T_40_59(46, E, A, B, C, D); + T_40_59(47, D, E, A, B, C); + T_40_59(48, C, D, E, A, B); + T_40_59(49, B, C, D, E, A); + T_40_59(50, A, B, C, D, E); + T_40_59(51, E, A, B, C, D); + T_40_59(52, D, E, A, B, C); + T_40_59(53, C, D, E, A, B); + T_40_59(54, B, C, D, E, A); + T_40_59(55, A, B, C, D, E); + T_40_59(56, E, A, B, C, D); + T_40_59(57, D, E, A, B, C); + T_40_59(58, C, D, E, A, B); + T_40_59(59, B, C, D, E, A); + + /* Round 4 */ + T_60_79(60, A, B, C, D, E); + T_60_79(61, E, A, B, C, D); + T_60_79(62, D, E, A, B, C); + T_60_79(63, C, D, E, A, B); + T_60_79(64, B, C, D, E, A); + T_60_79(65, A, B, C, D, E); + T_60_79(66, E, A, B, C, D); + T_60_79(67, D, E, A, B, C); + T_60_79(68, C, D, E, A, B); + T_60_79(69, B, C, D, E, A); + T_60_79(70, A, B, C, D, E); + T_60_79(71, E, A, B, C, D); + T_60_79(72, D, E, A, B, C); + T_60_79(73, C, D, E, A, B); + T_60_79(74, B, C, D, E, A); + T_60_79(75, A, B, C, D, E); + T_60_79(76, E, A, B, C, D); + T_60_79(77, D, E, A, B, C); + T_60_79(78, C, D, E, A, B); + T_60_79(79, B, C, D, E, A); + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +void git__blk_SHA1_Init(blk_SHA_CTX *ctx) +{ + ctx->size = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) */ + ctx->H[0] = 0x67452301; + ctx->H[1] = 0xefcdab89; + ctx->H[2] = 0x98badcfe; + ctx->H[3] = 0x10325476; + ctx->H[4] = 0xc3d2e1f0; +} + +void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len) +{ + unsigned int lenW = ctx->size & 63; + + ctx->size += len; + + /* Read the data into W and process blocks as they get full */ + if (lenW) { + unsigned int left = 64 - lenW; + if (len < left) + left = len; + memcpy(lenW + (char *)ctx->W, data, left); + lenW = (lenW + left) & 63; + len -= left; + data = ((const char *)data + left); + if (lenW) + return; + blk_SHA1_Block(ctx, ctx->W); + } + while (len >= 64) { + blk_SHA1_Block(ctx, data); + data = ((const char *)data + 64); + len -= 64; + } + if (len) + memcpy(ctx->W, data, len); +} + +void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx) +{ + static const unsigned char pad[64] = { 0x80 }; + unsigned int padlen[2]; + int i; + + /* Pad with a binary 1 (ie 0x80), then zeroes, then length */ + padlen[0] = htonl((uint32_t)(ctx->size >> 29)); + padlen[1] = htonl((uint32_t)(ctx->size << 3)); + + i = ctx->size & 63; + git__blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i))); + git__blk_SHA1_Update(ctx, padlen, 8); + + /* Output hash */ + for (i = 0; i < 5; i++) + put_be32(hashout + i*4, ctx->H[i]); +} diff --git a/vendor/libgit2/src/sha1.h b/vendor/libgit2/src/sha1.h new file mode 100644 index 000000000..558d6aece --- /dev/null +++ b/vendor/libgit2/src/sha1.h @@ -0,0 +1,22 @@ +/* + * SHA1 routine optimized to do word accesses rather than byte accesses, + * and to avoid unnecessary copies into the context array. + * + * This was initially based on the Mozilla SHA1 implementation, although + * none of the original Mozilla code remains. + */ + +typedef struct { + unsigned long long size; + unsigned int H[5]; + unsigned int W[16]; +} blk_SHA_CTX; + +void git__blk_SHA1_Init(blk_SHA_CTX *ctx); +void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len); +void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx); + +#define SHA_CTX blk_SHA_CTX +#define SHA1_Init git__blk_SHA1_Init +#define SHA1_Update git__blk_SHA1_Update +#define SHA1_Final git__blk_SHA1_Final diff --git a/vendor/libgit2/src/sha1_lookup.c b/vendor/libgit2/src/sha1_lookup.c new file mode 100644 index 000000000..6ac00c5aa --- /dev/null +++ b/vendor/libgit2/src/sha1_lookup.c @@ -0,0 +1,196 @@ +/* + * This file is basically taken from git code. + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include "sha1_lookup.h" +#include "common.h" + +/* + * Conventional binary search loop looks like this: + * + * unsigned lo, hi; + * do { + * unsigned mi = (lo + hi) / 2; + * int cmp = "entry pointed at by mi" minus "target"; + * if (!cmp) + * return (mi is the wanted one) + * if (cmp > 0) + * hi = mi; "mi is larger than target" + * else + * lo = mi+1; "mi is smaller than target" + * } while (lo < hi); + * + * The invariants are: + * + * - When entering the loop, lo points at a slot that is never + * above the target (it could be at the target), hi points at a + * slot that is guaranteed to be above the target (it can never + * be at the target). + * + * - We find a point 'mi' between lo and hi (mi could be the same + * as lo, but never can be as same as hi), and check if it hits + * the target. There are three cases: + * + * - if it is a hit, we are happy. + * + * - if it is strictly higher than the target, we set it to hi, + * and repeat the search. + * + * - if it is strictly lower than the target, we update lo to + * one slot after it, because we allow lo to be at the target. + * + * If the loop exits, there is no matching entry. + * + * When choosing 'mi', we do not have to take the "middle" but + * anywhere in between lo and hi, as long as lo <= mi < hi is + * satisfied. When we somehow know that the distance between the + * target and lo is much shorter than the target and hi, we could + * pick mi that is much closer to lo than the midway. + * + * Now, we can take advantage of the fact that SHA-1 is a good hash + * function, and as long as there are enough entries in the table, we + * can expect uniform distribution. An entry that begins with for + * example "deadbeef..." is much likely to appear much later than in + * the midway of the table. It can reasonably be expected to be near + * 87% (222/256) from the top of the table. + * + * However, we do not want to pick "mi" too precisely. If the entry at + * the 87% in the above example turns out to be higher than the target + * we are looking for, we would end up narrowing the search space down + * only by 13%, instead of 50% we would get if we did a simple binary + * search. So we would want to hedge our bets by being less aggressive. + * + * The table at "table" holds at least "nr" entries of "elem_size" + * bytes each. Each entry has the SHA-1 key at "key_offset". The + * table is sorted by the SHA-1 key of the entries. The caller wants + * to find the entry with "key", and knows that the entry at "lo" is + * not higher than the entry it is looking for, and that the entry at + * "hi" is higher than the entry it is looking for. + */ +int sha1_entry_pos(const void *table, + size_t elem_size, + size_t key_offset, + unsigned lo, unsigned hi, unsigned nr, + const unsigned char *key) +{ + const unsigned char *base = (const unsigned char*)table; + const unsigned char *hi_key, *lo_key; + unsigned ofs_0; + + if (!nr || lo >= hi) + return -1; + + if (nr == hi) + hi_key = NULL; + else + hi_key = base + elem_size * hi + key_offset; + lo_key = base + elem_size * lo + key_offset; + + ofs_0 = 0; + do { + int cmp; + unsigned ofs, mi, range; + unsigned lov, hiv, kyv; + const unsigned char *mi_key; + + range = hi - lo; + if (hi_key) { + for (ofs = ofs_0; ofs < 20; ofs++) + if (lo_key[ofs] != hi_key[ofs]) + break; + ofs_0 = ofs; + /* + * byte 0 thru (ofs-1) are the same between + * lo and hi; ofs is the first byte that is + * different. + */ + hiv = hi_key[ofs_0]; + if (ofs_0 < 19) + hiv = (hiv << 8) | hi_key[ofs_0+1]; + } else { + hiv = 256; + if (ofs_0 < 19) + hiv <<= 8; + } + lov = lo_key[ofs_0]; + kyv = key[ofs_0]; + if (ofs_0 < 19) { + lov = (lov << 8) | lo_key[ofs_0+1]; + kyv = (kyv << 8) | key[ofs_0+1]; + } + assert(lov < hiv); + + if (kyv < lov) + return -1 - lo; + if (hiv < kyv) + return -1 - hi; + + /* + * Even if we know the target is much closer to 'hi' + * than 'lo', if we pick too precisely and overshoot + * (e.g. when we know 'mi' is closer to 'hi' than to + * 'lo', pick 'mi' that is higher than the target), we + * end up narrowing the search space by a smaller + * amount (i.e. the distance between 'mi' and 'hi') + * than what we would have (i.e. about half of 'lo' + * and 'hi'). Hedge our bets to pick 'mi' less + * aggressively, i.e. make 'mi' a bit closer to the + * middle than we would otherwise pick. + */ + kyv = (kyv * 6 + lov + hiv) / 8; + if (lov < hiv - 1) { + if (kyv == lov) + kyv++; + else if (kyv == hiv) + kyv--; + } + mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo; + +#ifdef INDEX_DEBUG_LOOKUP + printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi); + printf("ofs %u lov %x, hiv %x, kyv %x\n", + ofs_0, lov, hiv, kyv); +#endif + + if (!(lo <= mi && mi < hi)) { + return git__throw(GIT_ERROR, "Assertion failure. Binary search invariant is false"); + } + + mi_key = base + elem_size * mi + key_offset; + cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0); + if (!cmp) + return mi; + if (cmp > 0) { + hi = mi; + hi_key = mi_key; + } else { + lo = mi + 1; + lo_key = mi_key + elem_size; + } + } while (lo < hi); + return -((int)lo)-1; +} diff --git a/vendor/libgit2/src/sha1_lookup.h b/vendor/libgit2/src/sha1_lookup.h new file mode 100644 index 000000000..5caa2f5ed --- /dev/null +++ b/vendor/libgit2/src/sha1_lookup.h @@ -0,0 +1,12 @@ +#ifndef INCLUDE_sha1_lookup_h__ +#define INCLUDE_sha1_lookup_h__ + +#include + +int sha1_entry_pos(const void *table, + size_t elem_size, + size_t key_offset, + unsigned lo, unsigned hi, unsigned nr, + const unsigned char *key); + +#endif diff --git a/vendor/libgit2/src/signature.c b/vendor/libgit2/src/signature.c index 62bd28b9a..327efe247 100644 --- a/vendor/libgit2/src/signature.c +++ b/vendor/libgit2/src/signature.c @@ -38,43 +38,111 @@ void git_signature_free(git_signature *sig) free(sig); } -git_signature *git_signature_new(const char *name, const char *email, git_time_t time, int offset) +static const char *skip_leading_spaces(const char *buffer, const char *buffer_end) { + while (*buffer == ' ' && buffer < buffer_end) + buffer++; + + return buffer; +} + +static const char *skip_trailing_spaces(const char *buffer_start, const char *buffer_end) +{ + while (*buffer_end == ' ' && buffer_end > buffer_start) + buffer_end--; + + return buffer_end; +} + +static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty) +{ + const char *left, *right; + int trimmed_input_length; + + left = skip_leading_spaces(input, input_end); + right = skip_trailing_spaces(input, input_end - 1); + + if (right < left) { + if (fail_when_empty) + return git__throw(GIT_EINVALIDARGS, "Failed to trim. Input is either empty or only contains spaces"); + else + right = left - 1; + } + + trimmed_input_length = right - left + 1; + + *storage = git__malloc(trimmed_input_length + 1); + if (*storage == NULL) + return GIT_ENOMEM; + + memcpy(*storage, left, trimmed_input_length); + (*storage)[trimmed_input_length] = 0; + + return GIT_SUCCESS; +} + +int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset) +{ + int error; git_signature *p = NULL; - if ((p = git__malloc(sizeof(git_signature))) == NULL) + assert(name && email); + + *sig_out = NULL; + + if ((p = git__malloc(sizeof(git_signature))) == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + memset(p, 0x0, sizeof(git_signature)); + + error = process_trimming(name, &p->name, name + strlen(name), 1); + if (error < GIT_SUCCESS) { + git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'name' argument is invalid"); + goto cleanup; + } + + error = process_trimming(email, &p->email, email + strlen(email), 1); + if (error < GIT_SUCCESS) { + git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'email' argument is invalid"); goto cleanup; + } - p->name = git__strdup(name); - p->email = git__strdup(email); p->when.time = time; p->when.offset = offset; - if (p->name == NULL || p->email == NULL) - goto cleanup; + *sig_out = p; - return p; + return error; cleanup: git_signature_free(p); - return NULL; + return error; } git_signature *git_signature_dup(const git_signature *sig) { - return git_signature_new(sig->name, sig->email, sig->when.time, sig->when.offset); + git_signature *new; + if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < GIT_SUCCESS) + return NULL; + return new; } -git_signature *git_signature_now(const char *name, const char *email) +int git_signature_now(git_signature **sig_out, const char *name, const char *email) { + int error; time_t now; time_t offset; struct tm *utc_tm, *local_tm; + git_signature *sig; #ifndef GIT_WIN32 struct tm _utc, _local; #endif + *sig_out = NULL; + time(&now); /** @@ -96,18 +164,23 @@ git_signature *git_signature_now(const char *name, const char *email) if (local_tm->tm_isdst) offset += 60; - return git_signature_new(name, email, now, (int)offset); + if ((error = git_signature_new(&sig, name, email, now, (int)offset)) < GIT_SUCCESS) + return error; + + *sig_out = sig; + + return error; } -static int parse_timezone_offset(const char *buffer, long *offset_out) +static int parse_timezone_offset(const char *buffer, int *offset_out) { - long offset, dec_offset; - int mins, hours; + long dec_offset; + int mins, hours, offset; const char *offset_start; const char *offset_end; - offset_start = buffer + 1; + offset_start = buffer; if (*offset_start == '\n') { *offset_out = 0; @@ -117,17 +190,23 @@ static int parse_timezone_offset(const char *buffer, long *offset_out) if (offset_start[0] != '-' && offset_start[0] != '+') return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'"); + if (offset_start[1] < '0' || offset_start[1] > '9') + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset."); + if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number"); if (offset_end - offset_start != 5) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length"); + if (dec_offset > 1400) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large"); + hours = dec_offset / 100; mins = dec_offset % 100; - if (hours > 14) // see http://www.worldtimezone.com/faq.html - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");; + if (hours > 14) // see http://www.worldtimezone.com/faq.html + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large"); if (mins > 59) return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large"); @@ -136,109 +215,139 @@ static int parse_timezone_offset(const char *buffer, long *offset_out) if (offset_start[0] == '-') offset *= -1; - + *offset_out = offset; return GIT_SUCCESS; } +int process_next_token(const char **buffer_out, char **storage, + const char *token_end, const char *right_boundary) +{ + int error = process_trimming(*buffer_out, storage, token_end, 0); + if (error < GIT_SUCCESS) + return error; -int git_signature__parse(git_signature *sig, const char **buffer_out, - const char *buffer_end, const char *header) + *buffer_out = token_end + 1; + + if (*buffer_out > right_boundary) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short"); + + return GIT_SUCCESS; +} + +const char *scan_for_previous_token(const char *buffer, const char *left_boundary) { - const size_t header_len = strlen(header); + const char *start; + + if (buffer <= left_boundary) + return NULL; + + start = skip_trailing_spaces(left_boundary, buffer); + + /* Search for previous occurence of space */ + while (start[-1] != ' ' && start > left_boundary) + start--; + + return start; +} + +int parse_time(git_time_t *time_out, const char *buffer) +{ + long time; + int error; + + if (*buffer == '+' || *buffer == '-') + return git__throw(GIT_ERROR, "Failed while parsing time. '%s' rather look like a timezone offset.", buffer); + + error = git__strtol32(&time, buffer, &buffer, 10); + + if (error < GIT_SUCCESS) + return error; + + *time_out = (git_time_t)time; - int name_length, email_length; + return GIT_SUCCESS; +} + +int git_signature__parse(git_signature *sig, const char **buffer_out, + const char *buffer_end, const char *header, char ender) +{ const char *buffer = *buffer_out; - const char *line_end, *name_end, *email_end; - long offset = 0, time; + const char *line_end, *name_end, *email_end, *tz_start, *time_start; + int error = GIT_SUCCESS; memset(sig, 0x0, sizeof(git_signature)); - line_end = memchr(buffer, '\n', buffer_end - buffer); - if (!line_end) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline found");; + if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline given"); - if (buffer + (header_len + 1) > line_end) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short"); - - if (memcmp(buffer, header, header_len) != 0) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header); + if (header) { + const size_t header_len = strlen(header); - buffer += header_len; + if (memcmp(buffer, header, header_len) != 0) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header); - /* Parse name */ - if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start"); + buffer += header_len; + } - name_length = name_end - buffer - 1; - sig->name = git__malloc(name_length + 1); - if (sig->name == NULL) - return GIT_ENOMEM; + if (buffer > line_end) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short"); - memcpy(sig->name, buffer, name_length); - sig->name[name_length] = 0; - buffer = name_end + 1; + if ((name_end = strchr(buffer, '<')) == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '<' in signature"); - if (buffer >= line_end) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); + if ((email_end = strchr(buffer, '>')) == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '>' in signature"); - /* Parse email */ - if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end"); + if (email_end < name_end) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Malformed e-mail"); - email_length = email_end - buffer; - sig->email = git__malloc(email_length + 1); - if (sig->name == NULL) - return GIT_ENOMEM; + error = process_next_token(&buffer, &sig->name, name_end, line_end); + if (error < GIT_SUCCESS) + return error; - memcpy(sig->email, buffer, email_length); - sig->email[email_length] = 0; - buffer = email_end + 1; + error = process_next_token(&buffer, &sig->email, email_end, line_end); + if (error < GIT_SUCCESS) + return error; - if (buffer >= line_end) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); + tz_start = scan_for_previous_token(line_end - 1, buffer); - if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number"); + if (tz_start == NULL) + goto clean_exit; /* No timezone nor date */ - sig->when.time = (time_t)time; + time_start = scan_for_previous_token(tz_start - 1, buffer); + if (time_start == NULL || parse_time(&sig->when.time, time_start) < GIT_SUCCESS) { + /* The tz_start might point at the time */ + parse_time(&sig->when.time, tz_start); + goto clean_exit; + } - if (parse_timezone_offset(buffer, &offset) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; - - sig->when.offset = offset; + if (parse_timezone_offset(tz_start, &sig->when.offset) < GIT_SUCCESS) { + sig->when.time = 0; /* Bogus timezone, we reset the time */ + } - *buffer_out = (line_end + 1); +clean_exit: + *buffer_out = line_end + 1; return GIT_SUCCESS; } -int git_signature__write(char **signature, const char *header, const git_signature *sig) +void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig) { int offset, hours, mins; - char sig_buffer[2048]; - int sig_buffer_len; char sign; offset = sig->when.offset; sign = (sig->when.offset < 0) ? '-' : '+'; - + if (offset < 0) offset = -offset; hours = offset / 60; mins = offset % 60; - sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer), - "%s %s <%s> %u %c%02d%02d\n", - header, sig->name, sig->email, + git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n", + header ? header : "", sig->name, sig->email, (unsigned)sig->when.time, sign, hours, mins); - - if (sig_buffer_len < 0 || (size_t)sig_buffer_len > sizeof(sig_buffer)) - return GIT_ENOMEM; - - *signature = git__strdup(sig_buffer); - return sig_buffer_len; } - diff --git a/vendor/libgit2/src/signature.h b/vendor/libgit2/src/signature.h index feba6578d..2fe6cd7c9 100644 --- a/vendor/libgit2/src/signature.h +++ b/vendor/libgit2/src/signature.h @@ -6,7 +6,7 @@ #include "repository.h" #include -int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header); -int git_signature__write(char **signature, const char *header, const git_signature *sig); +int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender); +void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig); #endif diff --git a/vendor/libgit2/src/status.c b/vendor/libgit2/src/status.c new file mode 100644 index 000000000..d9613c129 --- /dev/null +++ b/vendor/libgit2/src/status.c @@ -0,0 +1,361 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "common.h" +#include "git2.h" +#include "fileops.h" +#include "hash.h" +#include "vector.h" +#include "tree.h" +#include "git2/status.h" + +struct status_entry { + char path[GIT_PATH_MAX]; + + git_index_time mtime; + + git_oid head_oid; + git_oid index_oid; + git_oid wt_oid; + + unsigned int status_flags:6; +}; + +static int status_cmp(const void *a, const void *b) +{ + const struct status_entry *entry_a = (const struct status_entry *)(a); + const struct status_entry *entry_b = (const struct status_entry *)(b); + + return strcmp(entry_a->path, entry_b->path); +} + +static int status_srch(const void *key, const void *array_member) +{ + const char *path = (const char *)key; + const struct status_entry *entry = (const struct status_entry *)(array_member); + + return strcmp(path, entry->path); +} + +static int find_status_entry(git_vector *entries, const char *path) +{ + return git_vector_bsearch2(entries, status_srch, path); +} + +static struct status_entry *new_status_entry(git_vector *entries, const char *path) +{ + struct status_entry *e = git__malloc(sizeof(struct status_entry)); + memset(e, 0x0, sizeof(struct status_entry)); + if (entries != NULL) + git_vector_insert(entries, e); + strcpy(e->path, path); + return e; +} + +static void recurse_tree_entries(git_tree *tree, git_vector *entries, char *path) +{ + int i, cnt, idx; + struct status_entry *e; + char file_path[GIT_PATH_MAX]; + git_tree *subtree; + + cnt = git_tree_entrycount(tree); + for (i = 0; i < cnt; ++i) { + const git_tree_entry *tree_entry = git_tree_entry_byindex(tree, i); + + git_path_join(file_path, path, tree_entry->filename); + + if (git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid) == GIT_SUCCESS) { + recurse_tree_entries(subtree, entries, file_path); + git_tree_close(subtree); + continue; + } + + if ((idx = find_status_entry(entries, file_path)) != GIT_ENOTFOUND) + e = (struct status_entry *)git_vector_get(entries, idx); + else + e = new_status_entry(entries, file_path); + + git_oid_cpy(&e->head_oid, &tree_entry->oid); + } +} + +static void recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path) +{ + char *dir_sep; + char buffer[GIT_PATH_MAX]; + const git_tree_entry *tree_entry; + git_tree *subtree; + + strcpy(buffer, path); + + dir_sep = strchr(buffer, '/'); + if (dir_sep) { + *dir_sep = '\0'; + + tree_entry = git_tree_entry_byname(tree, buffer); + if (tree_entry != NULL) { + if (git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid) == GIT_SUCCESS) { + recurse_tree_entry(subtree, e, dir_sep+1); + git_tree_close(subtree); + return; + } + } + } + + tree_entry = git_tree_entry_byname(tree, path); + if (tree_entry != NULL) { + git_oid_cpy(&e->head_oid, &tree_entry->oid); + } +} + +struct status_st { + union { + git_vector *vector; + struct status_entry *e; + } entry; + + int workdir_path_len; +}; + +static int dirent_cb(void *state, char *full_path) +{ + int idx; + struct status_entry *e; + struct status_st *st = (struct status_st *)state; + char *file_path = full_path + st->workdir_path_len; + struct stat filest; + git_oid oid; + + if ((git_futils_isdir(full_path) == GIT_SUCCESS) && (!strcmp(".git", file_path))) + return 0; + + if (git_futils_isdir(full_path) == GIT_SUCCESS) + return git_futils_direach(full_path, GIT_PATH_MAX, dirent_cb, state); + + if ((idx = find_status_entry(st->entry.vector, file_path)) != GIT_ENOTFOUND) { + e = (struct status_entry *)git_vector_get(st->entry.vector, idx); + + if (p_stat(full_path, &filest) < 0) + return git__throw(GIT_EOSERR, "Failed to read file %s", full_path); + + if (e->mtime.seconds == (git_time_t)filest.st_mtime) { + git_oid_cpy(&e->wt_oid, &e->index_oid); + return 0; + } + } else { + e = new_status_entry(st->entry.vector, file_path); + } + + git_odb_hashfile(&oid, full_path, GIT_OBJ_BLOB); + git_oid_cpy(&e->wt_oid, &oid); + + return 0; +} + +static int single_dirent_cb(void *state, char *full_path) +{ + struct status_st *st = (struct status_st *)state; + struct status_entry *e = st->entry.e; + + char *file_path = full_path + st->workdir_path_len; + struct stat filest; + git_oid oid; + + if ((git_futils_isdir(full_path) == GIT_SUCCESS) && (!strcmp(".git", file_path))) + return 0; + + if (git_futils_isdir(full_path) == GIT_SUCCESS) + return git_futils_direach(full_path, GIT_PATH_MAX, single_dirent_cb, state); + + if (!strcmp(file_path, e->path)) { + if (p_stat(full_path, &filest) < 0) + return git__throw(GIT_EOSERR, "Failed to read file %s", full_path); + + if (e->mtime.seconds == (git_time_t)filest.st_mtime) { + git_oid_cpy(&e->wt_oid, &e->index_oid); + return 1; + } + + git_odb_hashfile(&oid, full_path, GIT_OBJ_BLOB); + git_oid_cpy(&e->wt_oid, &oid); + return 1; + } + + return 0; +} + +static int set_status_flags(struct status_entry *e) +{ + git_oid zero; + int head_zero, index_zero, wt_zero; + + memset(&zero, 0x0, sizeof(git_oid)); + + head_zero = git_oid_cmp(&zero, &e->head_oid); + index_zero = git_oid_cmp(&zero, &e->index_oid); + wt_zero = git_oid_cmp(&zero, &e->wt_oid); + + if (head_zero == 0 && index_zero == 0 && wt_zero == 0) + return GIT_ENOTFOUND; + + if (head_zero == 0 && index_zero != 0) + e->status_flags |= GIT_STATUS_INDEX_NEW; + else if (index_zero == 0 && head_zero != 0) + e->status_flags |= GIT_STATUS_INDEX_DELETED; + else if (git_oid_cmp(&e->head_oid, &e->index_oid) != 0) + e->status_flags |= GIT_STATUS_INDEX_MODIFIED; + + if (index_zero == 0 && wt_zero != 0) + e->status_flags |= GIT_STATUS_WT_NEW; + else if (wt_zero == 0 && index_zero != 0) + e->status_flags |= GIT_STATUS_WT_DELETED; + else if (git_oid_cmp(&e->index_oid, &e->wt_oid) != 0) + e->status_flags |= GIT_STATUS_WT_MODIFIED; + + return GIT_SUCCESS; +} + +int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload) +{ + git_vector entries; + struct status_entry *e; + git_index *index; + unsigned int i, cnt; + git_index_entry *index_entry; + char temp_path[GIT_PATH_MAX]; + int error; + git_tree *tree; + struct status_st dirent_st; + + git_reference *head_ref, *resolved_head_ref; + git_commit *head_commit; + + git_repository_index(&index, repo); + + cnt = git_index_entrycount(index); + git_vector_init(&entries, cnt, status_cmp); + for (i = 0; i < cnt; ++i) { + index_entry = git_index_get(index, i); + + e = new_status_entry(&entries, index_entry->path); + git_oid_cpy(&e->index_oid, &index_entry->oid); + e->mtime = index_entry->mtime; + } + git_index_free(index); + + git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE); + git_reference_resolve(&resolved_head_ref, head_ref); + + git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref)); + + // recurse through tree entries + git_commit_tree(&tree, head_commit); + recurse_tree_entries(tree, &entries, ""); + git_tree_close(tree); + git_commit_close(head_commit); + + dirent_st.workdir_path_len = strlen(repo->path_workdir); + dirent_st.entry.vector = &entries; + strcpy(temp_path, repo->path_workdir); + git_futils_direach(temp_path, GIT_PATH_MAX, dirent_cb, &dirent_st); + + for (i = 0; i < entries.length; ++i) { + e = (struct status_entry *)git_vector_get(&entries, i); + + set_status_flags(e); + } + + + for (i = 0; i < entries.length; ++i) { + e = (struct status_entry *)git_vector_get(&entries, i); + + if ((error = callback(e->path, e->status_flags, payload)) < GIT_SUCCESS) + return git__throw(error, "Failed to list statuses. User callback failed"); + } + + for (i = 0; i < entries.length; ++i) { + e = (struct status_entry *)git_vector_get(&entries, i); + free(e); + } + git_vector_free(&entries); + + return GIT_SUCCESS; +} + +int git_status_file(unsigned int *status_flags, git_repository *repo, const char *path) +{ + struct status_entry *e; + git_index *index; + git_index_entry *index_entry; + char temp_path[GIT_PATH_MAX]; + int idx, error; + git_tree *tree; + git_reference *head_ref, *resolved_head_ref; + git_commit *head_commit; + struct status_st dirent_st; + + assert(status_flags); + + e = new_status_entry(NULL, path); + + // Find file in Index + git_repository_index(&index, repo); + idx = git_index_find(index, path); + if (idx >= 0) { + index_entry = git_index_get(index, idx); + git_oid_cpy(&e->index_oid, &index_entry->oid); + e->mtime = index_entry->mtime; + } + git_index_free(index); + + // Find file in HEAD + git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE); + git_reference_resolve(&resolved_head_ref, head_ref); + + git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref)); + + git_commit_tree(&tree, head_commit); + recurse_tree_entry(tree, e, path); + git_tree_close(tree); + git_commit_close(head_commit); + + // Find file in Workdir + dirent_st.workdir_path_len = strlen(repo->path_workdir); + dirent_st.entry.e = e; + strcpy(temp_path, repo->path_workdir); + git_futils_direach(temp_path, GIT_PATH_MAX, single_dirent_cb, &dirent_st); + + if ((error = set_status_flags(e)) < GIT_SUCCESS) { + free(e); + return git__throw(error, "Nonexistent file"); + } + + *status_flags = e->status_flags; + + free(e); + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/tag.c b/vendor/libgit2/src/tag.c index 849429b7e..21bc3c726 100644 --- a/vendor/libgit2/src/tag.c +++ b/vendor/libgit2/src/tag.c @@ -89,7 +89,7 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer char *search; int error; - if ((error = git__parse_oid(&tag->target, &buffer, buffer_end, "object ")) < 0) + if ((error = git_oid__parse(&tag->target, &buffer, buffer_end, "object ")) < 0) return git__rethrow(error, "Failed to parse tag. Object field invalid"); if (buffer + 5 >= buffer_end) @@ -144,12 +144,15 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer if (tag->tagger == NULL) return GIT_ENOMEM; - if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) { + if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n')) != 0) { free(tag->tag_name); git_signature_free(tag->tagger); - return error; + return git__rethrow(error, "Failed to parse tag"); } + if( *buffer != '\n' ) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. No new line before message"); + text_len = buffer_end - ++buffer; tag->message = git__malloc(text_len + 1); @@ -162,212 +165,214 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer return GIT_SUCCESS; } -static int retreive_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name) +static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name) { git_reference *tag_ref; int error; - git__joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name); + git_path_join(ref_name_out, GIT_REFS_TAGS_DIR, tag_name); error = git_reference_lookup(&tag_ref, repo, ref_name_out); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to retrieve tag reference"); *tag_reference_out = tag_ref; return GIT_SUCCESS; } -static int tag_create( +static int write_tag_annotation( git_oid *oid, git_repository *repo, const char *tag_name, - const git_oid *target, - git_otype target_type, + const git_object *target, const git_signature *tagger, - const char *message, - int allow_ref_overwrite) + const char *message) { - size_t final_size = 0; - git_odb_stream *stream; - - const char *type_str; - char *tagger_str; - git_reference *new_ref; - - char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; - - int type_str_len, tag_name_len, tagger_str_len, message_len; - int error, should_update_ref = 0; - - /** Ensure the tag name doesn't conflict with an already existing - reference unless overwriting has explictly been requested **/ - error = retreive_tag_reference(&new_ref, ref_name, repo, tag_name); - - switch (error) { - case GIT_SUCCESS: - if (!allow_ref_overwrite) - return GIT_EEXISTS; - should_update_ref = 1; - - /* Fall trough */ - - case GIT_ENOTFOUND: - break; - - default: - return error; - } - - if (!git_odb_exists(repo->db, target)) - return git__throw(GIT_ENOTFOUND, "Failed to create tag. Object to tag doesn't exist"); - - /* Try to find out what the type is */ - if (target_type == GIT_OBJ_ANY) { - size_t _unused; - error = git_odb_read_header(&_unused, &target_type, repo->db, target); - if (error < GIT_SUCCESS) - return error; + int error = GIT_SUCCESS; + git_buf tag = GIT_BUF_INIT; + + git_oid__writebuf(&tag, "object ", git_object_id(target)); + git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target))); + git_buf_printf(&tag, "tag %s\n", tag_name); + git_signature__writebuf(&tag, "tagger ", tagger); + git_buf_putc(&tag, '\n'); + git_buf_puts(&tag, message); + + if (git_buf_oom(&tag)) { + git_buf_free(&tag); + return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data"); } - type_str = git_object_type2string(target_type); + error = git_odb_write(oid, git_repository_database(repo), tag.ptr, tag.size, GIT_OBJ_TAG); + git_buf_free(&tag); - tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger); - - type_str_len = strlen(type_str); - tag_name_len = strlen(tag_name); - message_len = strlen(message); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create tag annotation"); - final_size += GIT_OID_LINE_LENGTH("object"); - final_size += STRLEN("type ") + type_str_len + 1; - final_size += STRLEN("tag ") + tag_name_len + 1; - final_size += tagger_str_len; - final_size += 1 + message_len; + return error; +} - if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_TAG)) < GIT_SUCCESS) - return error; +static int git_tag_create__internal( + git_oid *oid, + git_repository *repo, + const char *tag_name, + const git_object *target, + const git_signature *tagger, + const char *message, + int allow_ref_overwrite, + int create_tag_annotation) +{ + git_reference *new_ref = NULL; + char ref_name[GIT_REFNAME_MAX]; - git__write_oid(stream, "object", target); + int error, should_update_ref = 0; - stream->write(stream, "type ", STRLEN("type ")); - stream->write(stream, type_str, type_str_len); + assert(repo && tag_name && target); + assert(!create_tag_annotation || (tagger && message)); - stream->write(stream, "\ntag ", STRLEN("\ntag ")); - stream->write(stream, tag_name, tag_name_len); - stream->write(stream, "\n", 1); + if (git_object_owner(target) != repo) + return git__throw(GIT_EINVALIDARGS, "The given target does not belong to this repository"); - stream->write(stream, tagger_str, tagger_str_len); - free(tagger_str); + error = retrieve_tag_reference(&new_ref, ref_name, repo, tag_name); - stream->write(stream, "\n", 1); - stream->write(stream, message, message_len); + switch (error) { + case GIT_SUCCESS: + case GIT_ENOTFOUND: + break; + default: + return git__rethrow(error, "Failed to create tag"); + } - error = stream->finalize_write(oid, stream); - stream->free(stream); + /** Ensure the tag name doesn't conflict with an already existing + * reference unless overwriting has explictly been requested **/ + if (new_ref != NULL) { + if (!allow_ref_overwrite) { + git_oid_cpy(oid, git_reference_oid(new_ref)); + return git__throw(GIT_EEXISTS, "Tag already exists"); + } else { + should_update_ref = 1; + } + } - if (error < GIT_SUCCESS) - return error; + if (create_tag_annotation) { + if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) + return error; + } else + git_oid_cpy(oid, git_object_id(target)); if (!should_update_ref) - error = git_reference_create_oid(&new_ref, repo, ref_name, oid); + error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0); else error = git_reference_set_oid(new_ref, oid); - - return error; -} - -int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer) -{ - git_tag tag; - int error; - - assert(oid && buffer); - - memset(&tag, 0, sizeof(tag)); - - if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS) - return error; - - error = git_tag_create(oid, repo, tag.tag_name, &tag.target, tag.type, tag.tagger, tag.message); - - git_signature_free(tag.tagger); - free(tag.tag_name); - free(tag.message); - return error; -} - -int git_tag_create_o( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_object *target, - const git_signature *tagger, - const char *message) -{ - return tag_create( - oid, repo, tag_name, - git_object_id(target), - git_object_type(target), - tagger, message, 0); + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } int git_tag_create( git_oid *oid, git_repository *repo, const char *tag_name, - const git_oid *target, - git_otype target_type, + const git_object *target, const git_signature *tagger, - const char *message) + const char *message, + int allow_ref_overwrite) { - return tag_create( - oid, repo, tag_name, - target, - target_type, - tagger, message, 0); + return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1); } -int git_tag_create_fo( +int git_tag_create_lightweight( git_oid *oid, git_repository *repo, const char *tag_name, const git_object *target, - const git_signature *tagger, - const char *message) + int allow_ref_overwrite) { - return tag_create( - oid, repo, tag_name, - git_object_id(target), - git_object_type(target), - tagger, message, 1); + return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0); } -int git_tag_create_f( - git_oid *oid, - git_repository *repo, - const char *tag_name, - const git_oid *target, - git_otype target_type, - const git_signature *tagger, - const char *message) +int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite) { - return tag_create( - oid, repo, tag_name, - target, - target_type, - tagger, message, 1); + git_tag tag; + int error, should_update_ref = 0; + git_odb_stream *stream; + git_odb_object *target_obj; + + git_reference *new_ref; + char ref_name[GIT_REFNAME_MAX]; + + assert(oid && buffer); + + memset(&tag, 0, sizeof(tag)); + + /* validate the buffer */ + if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS) + return git__rethrow(error, "Failed to create tag"); + + /* validate the target */ + if ((error = git_odb_read(&target_obj, repo->db, &tag.target)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to create tag"); + + if (tag.type != target_obj->raw.type) + return git__throw(error, "The type for the given target is invalid"); + + git_odb_object_close(target_obj); + + error = retrieve_tag_reference(&new_ref, ref_name, repo, tag.tag_name); + + switch (error) { + case GIT_SUCCESS: + case GIT_ENOTFOUND: + break; + + default: + return git__rethrow(error, "Failed to create tag"); + } + + /** Ensure the tag name doesn't conflict with an already existing + * reference unless overwriting has explictly been requested **/ + if (new_ref != NULL) { + if (!allow_ref_overwrite) { + git_oid_cpy(oid, git_reference_oid(new_ref)); + return git__throw(GIT_EEXISTS, "Tag already exists"); + } else { + should_update_ref = 1; + } + } + + /* write the buffer */ + if ((error = git_odb_open_wstream(&stream, repo->db, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) + return git__rethrow(error, "Failed to create tag"); + + stream->write(stream, buffer, strlen(buffer)); + + error = stream->finalize_write(oid, stream); + stream->free(stream); + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create tag"); + + if (!should_update_ref) + error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0); + else + error = git_reference_set_oid(new_ref, oid); + + git_signature_free(tag.tagger); + free(tag.tag_name); + free(tag.message); + + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); } int git_tag_delete(git_repository *repo, const char *tag_name) { int error; git_reference *tag_ref; - char ref_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char ref_name[GIT_REFNAME_MAX]; - error = retreive_tag_reference(&tag_ref, ref_name, repo, tag_name); + error = retrieve_tag_reference(&tag_ref, ref_name, repo, tag_name); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to delete tag"); return git_reference_delete(tag_ref); } @@ -378,29 +383,53 @@ int git_tag__parse(git_tag *tag, git_odb_object *obj) return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len); } +typedef struct { + git_vector *taglist; + const char *pattern; +} tag_filter_data; + +#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR) + static int tag_list_cb(const char *tag_name, void *payload) { - if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) == 0) - return git_vector_insert((git_vector *)payload, git__strdup(tag_name)); + tag_filter_data *filter; + + if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0) + return GIT_SUCCESS; + + filter = (tag_filter_data *)payload; + if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == GIT_SUCCESS) + return git_vector_insert(filter->taglist, git__strdup(tag_name)); return GIT_SUCCESS; } -int git_tag_list(git_strarray *tag_names, git_repository *repo) +int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo) { int error; + tag_filter_data filter; git_vector taglist; + assert(tag_names && repo && pattern); + if (git_vector_init(&taglist, 8, NULL) < GIT_SUCCESS) return GIT_ENOMEM; - error = git_reference_listcb(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&taglist); + filter.taglist = &taglist; + filter.pattern = pattern; + + error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter); if (error < GIT_SUCCESS) { git_vector_free(&taglist); - return error; + return git__rethrow(error, "Failed to list tags"); } tag_names->strings = (char **)taglist.contents; tag_names->count = taglist.length; return GIT_SUCCESS; } + +int git_tag_list(git_strarray *tag_names, git_repository *repo) +{ + return git_tag_list_match(tag_names, "", repo); +} diff --git a/vendor/libgit2/src/transport.c b/vendor/libgit2/src/transport.c new file mode 100644 index 000000000..91f621c46 --- /dev/null +++ b/vendor/libgit2/src/transport.c @@ -0,0 +1,71 @@ +#include "common.h" +#include "git2/types.h" +#include "git2/transport.h" +#include "git2/net.h" +#include "transport.h" + +struct { + char *prefix; + git_transport_cb fn; +} transports[] = { + {"git://", git_transport_git}, + {"http://", git_transport_dummy}, + {"https://", git_transport_dummy}, + {"file://", git_transport_local}, + {"git+ssh://", git_transport_dummy}, + {"ssh+git://", git_transport_dummy}, + {NULL, 0} +}; + +static git_transport_cb transport_new_fn(const char *url) +{ + int i = 0; + + while (1) { + if (transports[i].prefix == NULL) + break; + + if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix))) + return transports[i].fn; + + ++i; + } + + /* + * If we still haven't found the transport, we assume we mean a + * local file. + * TODO: Parse "example.com:project.git" as an SSH URL + */ + return git_transport_local; +} + +/************** + * Public API * + **************/ + +int git_transport_dummy(git_transport **GIT_UNUSED(transport)) +{ + GIT_UNUSED_ARG(transport); + return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry"); +} + +int git_transport_new(git_transport **out, const char *url) +{ + git_transport_cb fn; + git_transport *transport; + int error; + + fn = transport_new_fn(url); + + error = fn(&transport); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to create new transport"); + + transport->url = git__strdup(url); + if (transport->url == NULL) + return GIT_ENOMEM; + + *out = transport; + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/transport.h b/vendor/libgit2/src/transport.h new file mode 100644 index 000000000..9489ac803 --- /dev/null +++ b/vendor/libgit2/src/transport.h @@ -0,0 +1,106 @@ +#ifndef INCLUDE_transport_h__ +#define INCLUDE_transport_h__ + +#include "git2/transport.h" +#include "git2/net.h" +#include "vector.h" + +#define GIT_CAP_OFS_DELTA "ofs-delta" + +typedef struct git_transport_caps { + int common:1, + ofs_delta:1; +} git_transport_caps; + +/* + * A day in the life of a network operation + * ======================================== + * + * The library gets told to ls-remote/push/fetch on/to/from some + * remote. We look at the URL of the remote and fill the function + * table with whatever is appropriate (the remote may be git over git, + * ssh or http(s). It may even be an hg or svn repository, the library + * at this level doesn't care, it just calls the helpers. + * + * The first call is to ->connect() which connects to the remote, + * making use of the direction if necessary. This function must also + * store the remote heads and any other information it needs. + * + * The next useful step is to call ->ls() to get the list of + * references available to the remote. These references may have been + * collected on connect, or we may build them now. For ls-remote, + * nothing else is needed other than closing the connection. + * Otherwise, the higher leves decide which objects we want to + * have. ->send_have() is used to tell the other end what we have. If + * we do need to download a pack, ->download_pack() is called. + * + * When we're done, we call ->close() to close the + * connection. ->free() takes care of freeing all the resources. + */ + +struct git_transport { + /** + * Where the repo lives + */ + char *url; + /** + * Whether we want to push or fetch + */ + int direction : 1, /* 0 fetch, 1 push */ + connected : 1; + /** + * Connect and store the remote heads + */ + int (*connect)(struct git_transport *transport, int dir); + /** + * Give a list of references, useful for ls-remote + */ + int (*ls)(struct git_transport *transport, git_headarray *headarray); + /** + * Push the changes over + */ + int (*push)(struct git_transport *transport); + /** + * Send the list of 'want' refs + */ + int (*send_wants)(struct git_transport *transport, git_headarray *list); + /** + * Send the list of 'have' refs + */ + int (*send_have)(struct git_transport *transport, git_oid *oid); + /** + * Send a 'done' message + */ + int (*send_done)(struct git_transport *transport); + /** + * Negotiate the minimal amount of objects that need to be + * retrieved + */ + int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, git_headarray *list); + /** + * Send a flush + */ + int (*send_flush)(struct git_transport *transport); + /** + * Download the packfile + */ + int (*download_pack)(char **out, struct git_transport *transport, git_repository *repo); + /** + * Fetch the changes + */ + int (*fetch)(struct git_transport *transport); + /** + * Close the connection + */ + int (*close)(struct git_transport *transport); + /** + * Free the associated resources + */ + void (*free)(struct git_transport *transport); +}; + +int git_transport_local(struct git_transport **transport); +int git_transport_git(struct git_transport **transport); +int git_transport_dummy(struct git_transport **transport); + +#endif diff --git a/vendor/libgit2/src/transport_git.c b/vendor/libgit2/src/transport_git.c new file mode 100644 index 000000000..7b0edcfef --- /dev/null +++ b/vendor/libgit2/src/transport_git.c @@ -0,0 +1,606 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "git2/net.h" +#include "git2/common.h" +#include "git2/types.h" +#include "git2/errors.h" +#include "git2/net.h" +#include "git2/revwalk.h" + +#include "vector.h" +#include "transport.h" +#include "pkt.h" +#include "common.h" +#include "netops.h" +#include "filebuf.h" +#include "repository.h" + +typedef struct { + git_transport parent; + int socket; + git_vector refs; + git_remote_head **heads; + git_transport_caps caps; +} transport_git; + +/* + * Create a git procol request. + * + * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0 + */ +static int gen_proto(char **out, int *outlen, const char *cmd, const char *url) +{ + char *delim, *repo, *ptr; + char default_command[] = "git-upload-pack"; + char host[] = "host="; + int len; + + delim = strchr(url, '/'); + if (delim == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL"); + + repo = delim; + + delim = strchr(url, ':'); + if (delim == NULL) + delim = strchr(url, '/'); + + if (cmd == NULL) + cmd = default_command; + + len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 2; + + *out = git__malloc(len); + if (*out == NULL) + return GIT_ENOMEM; + + *outlen = len - 1; + ptr = *out; + memset(ptr, 0x0, len); + /* We expect the return value to be > len - 1 so don't bother checking it */ + snprintf(ptr, len -1, "%04x%s %s%c%s%s", len - 1, cmd, repo, 0, host, url); + + return GIT_SUCCESS; +} + +static int send_request(int s, const char *cmd, const char *url) +{ + int error, len; + char *msg = NULL; + + error = gen_proto(&msg, &len, cmd, url); + if (error < GIT_SUCCESS) + goto cleanup; + + error = gitno_send(s, msg, len, 0); + +cleanup: + free(msg); + return error; +} + +/* The URL should already have been stripped of the protocol */ +static int extract_host_and_port(char **host, char **port, const char *url) +{ + char *colon, *slash, *delim; + int error = GIT_SUCCESS; + + colon = strchr(url, ':'); + slash = strchr(url, '/'); + + if (slash == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /"); + + if (colon == NULL) { + *port = git__strdup(GIT_DEFAULT_PORT); + } else { + *port = git__strndup(colon + 1, slash - colon - 1); + } + if (*port == NULL) + return GIT_ENOMEM;; + + + delim = colon == NULL ? slash : colon; + *host = git__strndup(url, delim - url); + if (*host == NULL) { + free(*port); + error = GIT_ENOMEM; + } + + return error; +} + +/* + * Parse the URL and connect to a server, storing the socket in + * out. For convenience this also takes care of asking for the remote + * refs + */ +static int do_connect(transport_git *t, const char *url) +{ + int s = -1; + char *host, *port; + const char prefix[] = "git://"; + int error, connected = 0; + + if (!git__prefixcmp(url, prefix)) + url += strlen(prefix); + + error = extract_host_and_port(&host, &port, url); + if (error < GIT_SUCCESS) + return error; + s = gitno_connect(host, port); + connected = 1; + error = send_request(s, NULL, url); + t->socket = s; + + free(host); + free(port); + + if (error < GIT_SUCCESS && s > 0) + close(s); + if (!connected) + error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses"); + + return error; +} + +/* + * Read from the socket and store the references in the vector + */ +static int store_refs(transport_git *t) +{ + gitno_buffer buf; + int s = t->socket; + git_vector *refs = &t->refs; + int error = GIT_SUCCESS; + char buffer[1024]; + const char *line_end, *ptr; + git_pkt *pkt; + + gitno_buffer_setup(&buf, buffer, sizeof(buffer), s); + + while (1) { + error = gitno_recv(&buf); + if (error < GIT_SUCCESS) + return git__rethrow(GIT_EOSERR, "Failed to receive data"); + if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */ + return GIT_SUCCESS; + + ptr = buf.data; + while (1) { + if (buf.offset == 0) + break; + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); + /* + * If the error is GIT_ESHORTBUFFER, it means the buffer + * isn't long enough to satisfy the request. Break out and + * wait for more input. + * On any other error, fail. + */ + if (error == GIT_ESHORTBUFFER) { + break; + } + if (error < GIT_SUCCESS) { + return error; + } + + /* Get rid of the part we've used already */ + gitno_consume(&buf, line_end); + + error = git_vector_insert(refs, pkt); + if (error < GIT_SUCCESS) + return error; + + if (pkt->type == GIT_PKT_FLUSH) + return GIT_SUCCESS; + + } + } + + return error; +} + +static int detect_caps(transport_git *t) +{ + git_vector *refs = &t->refs; + git_pkt_ref *pkt; + git_transport_caps *caps = &t->caps; + const char *ptr; + + pkt = git_vector_get(refs, 0); + /* No refs or capabilites, odd but not a problem */ + if (pkt == NULL || pkt->capabilities == NULL) + return GIT_SUCCESS; + + ptr = pkt->capabilities; + while (ptr != NULL && *ptr != '\0') { + if (*ptr == ' ') + ptr++; + + if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { + caps->common = caps->ofs_delta = 1; + ptr += strlen(GIT_CAP_OFS_DELTA); + continue; + } + + /* We don't know this capability, so skip it */ + ptr = strchr(ptr, ' '); + } + + return GIT_SUCCESS; +} + +/* + * Since this is a network connection, we need to parse and store the + * pkt-lines at this stage and keep them there. + */ +static int git_connect(git_transport *transport, int direction) +{ + transport_git *t = (transport_git *) transport; + int error = GIT_SUCCESS; + + if (direction == GIT_DIR_PUSH) + return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol"); + + t->parent.direction = direction; + error = git_vector_init(&t->refs, 16, NULL); + if (error < GIT_SUCCESS) + goto cleanup; + + /* Connect and ask for the refs */ + error = do_connect(t, transport->url); + if (error < GIT_SUCCESS) + return error; + + t->parent.connected = 1; + error = store_refs(t); + if (error < GIT_SUCCESS) + return error; + + error = detect_caps(t); + +cleanup: + if (error < GIT_SUCCESS) { + git_vector_free(&t->refs); + } + + return error; +} + +static int git_ls(git_transport *transport, git_headarray *array) +{ + transport_git *t = (transport_git *) transport; + git_vector *refs = &t->refs; + int len = 0; + unsigned int i; + + array->heads = git__calloc(refs->length, sizeof(git_remote_head *)); + if (array->heads == NULL) + return GIT_ENOMEM; + + for (i = 0; i < refs->length; ++i) { + git_pkt *p = git_vector_get(refs, i); + if (p->type != GIT_PKT_REF) + continue; + + ++len; + array->heads[i] = &(((git_pkt_ref *) p)->head); + } + array->len = len; + t->heads = array->heads; + + return GIT_SUCCESS; +} + +static int git_send_wants(git_transport *transport, git_headarray *array) +{ + transport_git *t = (transport_git *) transport; + + return git_pkt_send_wants(array, &t->caps, t->socket); +} + +static int git_send_have(git_transport *transport, git_oid *oid) +{ + transport_git *t = (transport_git *) transport; + + return git_pkt_send_have(oid, t->socket); +} + +static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *GIT_UNUSED(list)) +{ + transport_git *t = (transport_git *) transport; + git_revwalk *walk; + git_reference *ref; + git_strarray refs; + git_oid oid; + int error; + unsigned int i; + char buff[128]; + gitno_buffer buf; + GIT_UNUSED_ARG(list); + + gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket); + + error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); + if (error < GIT_ERROR) + return git__rethrow(error, "Failed to list all references"); + + error = git_revwalk_new(&walk, repo); + if (error < GIT_ERROR) { + error = git__rethrow(error, "Failed to list all references"); + goto cleanup; + } + git_revwalk_sorting(walk, GIT_SORT_TIME); + + for (i = 0; i < refs.count; ++i) { + /* No tags */ + if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) + continue; + + error = git_reference_lookup(&ref, repo, refs.strings[i]); + if (error < GIT_ERROR) { + error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]); + goto cleanup; + } + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) + continue; + error = git_revwalk_push(walk, git_reference_oid(ref)); + if (error < GIT_ERROR) { + error = git__rethrow(error, "Failed to push %s", refs.strings[i]); + goto cleanup; + } + } + git_strarray_free(&refs); + + /* + * We don't support any kind of ACK extensions, so the negotiation + * boils down to sending what we have and listening for an ACK + * every once in a while. + */ + i = 0; + while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) { + error = git_pkt_send_have(&oid, t->socket); + i++; + if (i % 20 == 0) { + const char *ptr = buf.data, *line_end; + git_pkt *pkt; + git_pkt_send_flush(t->socket); + while (1) { + /* Wait for max. 1 second */ + error = gitno_select_in(&buf, 1, 0); + if (error < GIT_SUCCESS) { + error = git__throw(GIT_EOSERR, "Error in select"); + } else if (error == 0) { + /* + * Some servers don't respond immediately, so if this + * happens, we keep sending information until it + * answers. + */ + break; + } + + error = gitno_recv(&buf); + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Error receiving data"); + goto cleanup; + } + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); + if (error == GIT_ESHORTBUFFER) + continue; + if (error < GIT_SUCCESS) { + error = git__rethrow(error, "Failed to get answer"); + goto cleanup; + } + + gitno_consume(&buf, line_end); + + if (pkt->type == GIT_PKT_ACK) { + error = GIT_SUCCESS; + goto done; + } else if (pkt->type == GIT_PKT_NAK) { + break; + } else { + error = git__throw(GIT_ERROR, "Got unexpected pkt type"); + goto cleanup; + } + } + } + } + if (error == GIT_EREVWALKOVER) + error = GIT_SUCCESS; + +done: + git_pkt_send_flush(t->socket); + git_pkt_send_done(t->socket); + +cleanup: + git_revwalk_free(walk); + return error; +} + +static int git_send_flush(git_transport *transport) +{ + transport_git *t = (transport_git *) transport; + + return git_pkt_send_flush(t->socket); +} + +static int git_send_done(git_transport *transport) +{ + transport_git *t = (transport_git *) transport; + + return git_pkt_send_done(t->socket); +} + +static int store_pack(char **out, gitno_buffer *buf, git_repository *repo) +{ + git_filebuf file; + int error; + char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-received\0"; + off_t off = 0; + + strcpy(path, repo->path_repository); + off += strlen(repo->path_repository); + strcat(path, suff); + //memcpy(path + off, suff, GIT_PATH_MAX - off - strlen(suff) - 1); + + if (memcmp(buf->data, "PACK", strlen("PACK"))) { + return git__throw(GIT_ERROR, "The pack doesn't start with the signature"); + } + + error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY); + if (error < GIT_SUCCESS) + goto cleanup; + + while (1) { + /* Part of the packfile has been received, don't loose it */ + error = git_filebuf_write(&file, buf->data, buf->offset); + if (error < GIT_SUCCESS) + goto cleanup; + + gitno_consume_n(buf, buf->offset); + error = gitno_recv(buf); + if (error < GIT_SUCCESS) + goto cleanup; + if (error == 0) /* Orderly shutdown */ + break; + } + + *out = git__strdup(file.path_lock); + if (*out == NULL) { + error = GIT_ENOMEM; + goto cleanup; + } + + /* A bit dodgy, but we need to keep the pack at the temporary path */ + error = git_filebuf_commit_at(&file, file.path_lock); +cleanup: + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&file); + + return error; +} + +static int git_download_pack(char **out, git_transport *transport, git_repository *repo) +{ + transport_git *t = (transport_git *) transport; + int s = t->socket, error = GIT_SUCCESS; + gitno_buffer buf; + char buffer[1024]; + git_pkt *pkt; + const char *line_end, *ptr; + + gitno_buffer_setup(&buf, buffer, sizeof(buffer), s); + /* + * For now, we ignore everything and wait for the pack + */ + while (1) { + error = gitno_recv(&buf); + if (error < GIT_SUCCESS) + return git__rethrow(GIT_EOSERR, "Failed to receive data"); + if (error == 0) /* Orderly shutdown */ + return GIT_SUCCESS; + + ptr = buf.data; + /* Whilst we're searching for the pack */ + while (1) { + if (buf.offset == 0) + break; + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf.offset); + if (error == GIT_ESHORTBUFFER) + break; + if (error < GIT_SUCCESS) + return error; + + if (pkt->type == GIT_PKT_PACK) + return store_pack(out, &buf, repo); + + /* For now we don't care about anything */ + free(pkt); + gitno_consume(&buf, line_end); + } + } +} + + +static int git_close(git_transport *transport) +{ + transport_git *t = (transport_git*) transport; + int s = t->socket; + int error; + + /* Can't do anything if there's an error, so don't bother checking */ + git_pkt_send_flush(s); + error = close(s); + if (error < 0) + error = git__throw(GIT_EOSERR, "Failed to close socket"); + + return error; +} + +static void git_free(git_transport *transport) +{ + transport_git *t = (transport_git *) transport; + git_vector *refs = &t->refs; + unsigned int i; + + for (i = 0; i < refs->length; ++i) { + git_pkt *p = git_vector_get(refs, i); + git_pkt_free(p); + } + + git_vector_free(refs); + free(t->heads); + free(t->parent.url); + free(t); +} + +int git_transport_git(git_transport **out) +{ + transport_git *t; + + t = git__malloc(sizeof(transport_git)); + if (t == NULL) + return GIT_ENOMEM; + + memset(t, 0x0, sizeof(transport_git)); + + t->parent.connect = git_connect; + t->parent.ls = git_ls; + t->parent.send_wants = git_send_wants; + t->parent.send_have = git_send_have; + t->parent.negotiate_fetch = git_negotiate_fetch; + t->parent.send_flush = git_send_flush; + t->parent.send_done = git_send_done; + t->parent.download_pack = git_download_pack; + t->parent.close = git_close; + t->parent.free = git_free; + + *out = (git_transport *) t; + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/transport_local.c b/vendor/libgit2/src/transport_local.c new file mode 100644 index 000000000..ab0922cf2 --- /dev/null +++ b/vendor/libgit2/src/transport_local.c @@ -0,0 +1,234 @@ +#include "common.h" +#include "git2/types.h" +#include "git2/transport.h" +#include "git2/net.h" +#include "git2/repository.h" +#include "git2/object.h" +#include "git2/tag.h" +#include "refs.h" +#include "transport.h" +#include "posix.h" + +typedef struct { + git_transport parent; + git_repository *repo; + git_vector *refs; + git_headarray wants_list; +} transport_local; + +/* + * Try to open the url as a git directory. The direction doesn't + * matter in this case because we're calulating the heads ourselves. + */ +static int local_connect(git_transport *transport, int GIT_UNUSED(direction)) +{ + git_repository *repo; + int error; + transport_local *t = (transport_local *) transport; + const char *path; + const char file_prefix[] = "file://"; + GIT_UNUSED_ARG(direction); + + /* The repo layer doesn't want the prefix */ + if (!git__prefixcmp(transport->url, file_prefix)) + path = transport->url + strlen(file_prefix); + else + path = transport->url; + + error = git_repository_open(&repo, path); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to open remote"); + + t->repo = repo; + t->parent.connected = 1; + + return GIT_SUCCESS; +} + +static int add_ref(const char *name, git_repository *repo, git_vector *vec) +{ + const char peeled[] = "^{}"; + git_remote_head *head; + git_reference *ref; + git_object *obj = NULL; + int error = GIT_SUCCESS, peel_len, ret; + + head = git__malloc(sizeof(git_remote_head)); + if (head == NULL) + return GIT_ENOMEM; + + head->name = git__strdup(name); + if (head->name == NULL) { + error = GIT_ENOMEM; + goto out; + } + + error = git_reference_lookup(&ref, repo, name); + if (error < GIT_SUCCESS) + goto out; + + error = git_reference_resolve(&ref, ref); + if (error < GIT_SUCCESS) + goto out; + + git_oid_cpy(&head->oid, git_reference_oid(ref)); + + error = git_vector_insert(vec, head); + if (error < GIT_SUCCESS) + goto out; + + /* If it's not a tag, we don't need to try to peel it */ + if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) + goto out; + + error = git_object_lookup(&obj, repo, &head->oid, GIT_OBJ_ANY); + if (error < GIT_SUCCESS) { + git__rethrow(error, "Failed to lookup object"); + } + + /* If it's not an annotated tag, just get out */ + if (git_object_type(obj) != GIT_OBJ_TAG) + goto out; + + /* And if it's a tag, peel it, and add it to the list */ + head = git__malloc(sizeof(git_remote_head)); + peel_len = strlen(name) + strlen(peeled); + head->name = git__malloc(peel_len + 1); + ret = p_snprintf(head->name, peel_len + 1, "%s%s", name, peeled); + if (ret >= peel_len + 1) { + error = git__throw(GIT_ERROR, "The string is magically to long"); + } + + git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj)); + + error = git_vector_insert(vec, head); + if (error < GIT_SUCCESS) + goto out; + + out: + git_object_close(obj); + if (error < GIT_SUCCESS) { + free(head->name); + free(head); + } + return error; +} + +static int local_ls(git_transport *transport, git_headarray *array) +{ + int error; + unsigned int i; + git_repository *repo; + git_vector *vec; + git_strarray refs; + transport_local *t = (transport_local *) transport; + + assert(transport && transport->connected); + + repo = t->repo; + + error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to list remote heads"); + + vec = git__malloc(sizeof(git_vector)); + if (vec == NULL) { + error = GIT_ENOMEM; + goto out; + } + + error = git_vector_init(vec, refs.count, NULL); + if (error < GIT_SUCCESS) + return error; + + /* Sort the references first */ + git__tsort((void **)refs.strings, refs.count, &git__strcmp_cb); + + /* Add HEAD */ + error = add_ref(GIT_HEAD_FILE, repo, vec); + if (error < GIT_SUCCESS) + goto out; + + for (i = 0; i < refs.count; ++i) { + error = add_ref(refs.strings[i], repo, vec); + if (error < GIT_SUCCESS) + goto out; + } + + array->len = vec->length; + array->heads = (git_remote_head **)vec->contents; + + t->refs = vec; + + out: + + git_strarray_free(&refs); + + return error; +} + +static int local_send_wants(git_transport *transport, git_headarray *array) +{ + transport_local *t = (transport_local *) transport; + git_headarray *wants = &t->wants_list; + + /* + * We need to store the list of wanted references so we can figure + * out what to transmit later. + */ + wants->len = array->len; + wants->heads = array->heads; + + /* We're local anyway, so we don't need this */ + return GIT_SUCCESS; +} + +static int local_close(git_transport *GIT_UNUSED(transport)) +{ + /* Nothing to do */ + GIT_UNUSED_ARG(transport); + return GIT_SUCCESS; +} + +static void local_free(git_transport *transport) +{ + unsigned int i; + transport_local *t = (transport_local *) transport; + git_vector *vec = t->refs; + + assert(transport); + + for (i = 0; i < vec->length; ++i) { + git_remote_head *h = git_vector_get(vec, i); + free(h->name); + free(h); + } + git_vector_free(vec); + free(vec); + git_repository_free(t->repo); + free(t->parent.url); + free(t); +} + +/************** + * Public API * + **************/ + +int git_transport_local(git_transport **out) +{ + transport_local *t; + + t = git__malloc(sizeof(transport_local)); + if (t == NULL) + return GIT_ENOMEM; + + t->parent.connect = local_connect; + t->parent.ls = local_ls; + t->parent.send_wants = local_send_wants; + t->parent.close = local_close; + t->parent.free = local_free; + + *out = (git_transport *) t; + + return GIT_SUCCESS; +} diff --git a/vendor/libgit2/src/tree.c b/vendor/libgit2/src/tree.c index b7daf39c4..d993d549a 100644 --- a/vendor/libgit2/src/tree.c +++ b/vendor/libgit2/src/tree.c @@ -34,32 +34,51 @@ #define MAX_FILEMODE_BYTES 6 static int valid_attributes(const int attributes) { - return attributes >= 0 && attributes <= MAX_FILEMODE; + return attributes >= 0 && attributes <= MAX_FILEMODE; } +struct tree_key_search { + const char *filename; + size_t filename_len; +}; + int entry_search_cmp(const void *key, const void *array_member) { - const char *filename = (const char *)key; - const git_tree_entry *entry = *(const git_tree_entry **)(array_member); + const struct tree_key_search *ksearch = key; + const git_tree_entry *entry = array_member; - return strcmp(filename, entry->filename); -} + int result = + git_futils_cmp_path( + ksearch->filename, ksearch->filename_len, entry->attr & 040000, + entry->filename, entry->filename_len, entry->attr & 040000); -#if 0 -static int valid_attributes(const int attributes) { - return attributes >= 0 && attributes <= MAX_FILEMODE; + return result ? result : ((int)ksearch->filename_len - (int)entry->filename_len); } -#endif int entry_sort_cmp(const void *a, const void *b) { - const git_tree_entry *entry_a = *(const git_tree_entry **)(a); - const git_tree_entry *entry_b = *(const git_tree_entry **)(b); + const git_tree_entry *entry_a = (const git_tree_entry *)(a); + const git_tree_entry *entry_b = (const git_tree_entry *)(b); + + return git_futils_cmp_path( + entry_a->filename, entry_a->filename_len, entry_a->attr & 040000, + entry_b->filename, entry_b->filename_len, entry_b->attr & 040000); +} + +static int build_ksearch(struct tree_key_search *ksearch, const char *path) +{ + size_t len = strlen(path); + + if (len && path[len - 1] == '/') + len--; + + if (len == 0 || memchr(path, '/', len) != NULL) + return GIT_ERROR; + + ksearch->filename = path; + ksearch->filename_len = len; - return gitfo_cmp_path(entry_a->filename, strlen(entry_a->filename), - entry_a->attr & 040000, - entry_b->filename, strlen(entry_b->filename), - entry_b->attr & 040000); + return GIT_SUCCESS; } void git_tree__free(git_tree *tree) @@ -100,6 +119,18 @@ const git_oid *git_tree_entry_id(const git_tree_entry *entry) return &entry->oid; } +git_otype git_tree_entry_type(const git_tree_entry *entry) +{ + assert(entry); + + if (S_ISGITLINK(entry->attr)) + return GIT_OBJ_COMMIT; + else if (S_ISDIR(entry->attr)) + return GIT_OBJ_TREE; + else + return GIT_OBJ_BLOB; +} + int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry) { assert(entry && object_out); @@ -109,23 +140,27 @@ int git_tree_entry_2object(git_object **object_out, git_repository *repo, const const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename) { int idx; + struct tree_key_search ksearch; assert(tree && filename); - idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename); + if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) + return NULL; + + idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, &ksearch); if (idx == GIT_ENOTFOUND) return NULL; return git_vector_get(&tree->entries, idx); } -const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx) +const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx) { assert(tree); - return git_vector_get(&tree->entries, (unsigned int)idx); + return git_vector_get(&tree->entries, idx); } -size_t git_tree_entrycount(git_tree *tree) +unsigned int git_tree_entrycount(git_tree *tree) { assert(tree); return tree->entries.length; @@ -151,15 +186,15 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf return GIT_ENOMEM; if (git__strtol32((long *)&entry->attr, buffer, &buffer, 8) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes"); if (*buffer++ != ' ') { - error = GIT_EOBJCORRUPTED; + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); break; } if (memchr(buffer, 0, buffer_end - buffer) == NULL) { - error = GIT_EOBJCORRUPTED; + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); break; } @@ -171,11 +206,11 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf buffer++; - git_oid_mkraw(&entry->oid, (const unsigned char *)buffer); + git_oid_fromraw(&entry->oid, (const unsigned char *)buffer); buffer += GIT_OID_RAWSZ; } - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer"); } int git_tree__parse(git_tree *tree, git_odb_object *obj) @@ -203,9 +238,9 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas buffer = git__malloc(size); if (buffer == NULL) return GIT_ENOMEM; - + offset = 0; - + for (nr = entry_no; nr < maxentries; ++nr) { git_index_entry *entry = git_index_get(index, nr); @@ -215,11 +250,11 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas unsigned int write_mode; git_oid subtree_oid; git_oid *write_oid; - + /* Did we hit the end of the directory? Return how many we wrote */ if (baselen >= pathlen || memcmp(base, pathname, baselen) != 0) break; - + /* Do we have _further_ subdirectories? */ filename = pathname + baselen; dirname = strchr(filename, '/'); @@ -242,9 +277,9 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas free(buffer); return subdir_written; } - + nr = subdir_written - 1; - + /* Now we need to write out the directory entry into this tree.. */ pathlen = dirname - pathname; write_oid = &subtree_oid; @@ -255,18 +290,18 @@ static int write_index(git_oid *oid, git_index *index, const char *base, int bas if (offset + entrylen + 32 > size) { size = alloc_nr(offset + entrylen + 32); buffer = git__realloc(buffer, size); - + if (buffer == NULL) return GIT_ENOMEM; } offset += write_index_entry(buffer + offset, write_mode, filename, entrylen, write_oid); } - + error = git_odb_write(oid, index->repository->db, buffer, offset, GIT_OBJ_TREE); free(buffer); - return (error == GIT_SUCCESS) ? nr : error; + return (error == GIT_SUCCESS) ? nr : git__rethrow(error, "Failed to write index"); } int git_tree_create_fromindex(git_oid *oid, git_index *index) @@ -274,10 +309,10 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index) int error; if (index->repository == NULL) - return GIT_EBAREINDEX; + return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository"); error = write_index(oid, index, "", 0, 0, git_index_entrycount(index)); - return (error < GIT_SUCCESS) ? error : GIT_SUCCESS; + return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS; } static void sort_entries(git_treebuilder *bld) @@ -288,7 +323,7 @@ static void sort_entries(git_treebuilder *bld) int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) { git_treebuilder *bld; - size_t i, source_entries = DEFAULT_TREE_SIZE; + unsigned int i, source_entries = DEFAULT_TREE_SIZE; assert(builder_p); @@ -339,13 +374,17 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con { git_tree_entry *entry; int pos; + struct tree_key_search ksearch; assert(bld && id && filename); if (!valid_attributes(attributes)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes"); + + if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) + return git__throw(GIT_ERROR, "Failed to insert entry. Invalid filename '%s'", filename); - if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename)) != GIT_ENOTFOUND) { + if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, &ksearch)) != GIT_ENOTFOUND) { entry = git_vector_get(&bld->entries, pos); if (entry->removed) { entry->removed = 0; @@ -376,15 +415,18 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con return GIT_SUCCESS; } -const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename) +static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename) { int idx; git_tree_entry *entry; + struct tree_key_search ksearch; assert(bld && filename); - sort_entries(bld); - idx = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename); + if (build_ksearch(&ksearch, filename) < GIT_SUCCESS) + return NULL; + + idx = git_vector_bsearch2(&bld->entries, entry_search_cmp, &ksearch); if (idx == GIT_ENOTFOUND) return NULL; @@ -395,12 +437,17 @@ const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *file return entry; } +const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename) +{ + return treebuilder_get(bld, filename); +} + int git_treebuilder_remove(git_treebuilder *bld, const char *filename) { - git_tree_entry *remove_ptr = (git_tree_entry *)git_treebuilder_get(bld, filename); + git_tree_entry *remove_ptr = treebuilder_get(bld, filename); if (remove_ptr == NULL || remove_ptr->removed) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree"); remove_ptr->removed = 1; bld->entry_count--; @@ -409,51 +456,42 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) { - size_t i, size = 0; - char filemode[MAX_FILEMODE_BYTES + 1 + 1]; - git_odb_stream *stream; + unsigned int i; int error; + git_buf tree = GIT_BUF_INIT; assert(bld); sort_entries(bld); + /* Grow the buffer beforehand to an estimated size */ + git_buf_grow(&tree, bld->entries.length * 72); + for (i = 0; i < bld->entries.length; ++i) { git_tree_entry *entry = bld->entries.contents[i]; if (entry->removed) continue; - snprintf(filemode, sizeof(filemode), "%o ", entry->attr); - size += strlen(filemode); - size += entry->filename_len + 1; - size += GIT_OID_RAWSZ; + git_buf_printf(&tree, "%o ", entry->attr); + git_buf_put(&tree, entry->filename, entry->filename_len + 1); + git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ); } - if ((error = git_odb_open_wstream(&stream, git_repository_database(repo), size, GIT_OBJ_TREE)) < GIT_SUCCESS) - return error; - - for (i = 0; i < bld->entries.length; ++i) { - git_tree_entry *entry = bld->entries.contents[i]; - - if (entry->removed) - continue; - - snprintf(filemode, sizeof(filemode), "%o ", entry->attr); - stream->write(stream, filemode, strlen(filemode)); - stream->write(stream, entry->filename, entry->filename_len + 1); - stream->write(stream, (char *)entry->oid.id, GIT_OID_RAWSZ); - } + if (git_buf_oom(&tree)) { + git_buf_free(&tree); + return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data"); + } - error = stream->finalize_write(oid, stream); - stream->free(stream); + error = git_odb_write(oid, git_repository_database(repo), tree.ptr, tree.size, GIT_OBJ_TREE); + git_buf_free(&tree); - return error; + return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree"); } void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload) { - size_t i; + unsigned int i; assert(bld && filter); @@ -466,7 +504,7 @@ void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_e void git_treebuilder_clear(git_treebuilder *bld) { - size_t i; + unsigned int i; assert(bld); for (i = 0; i < bld->entries.length; ++i) { diff --git a/vendor/libgit2/src/tsort.c b/vendor/libgit2/src/tsort.c new file mode 100644 index 000000000..14b15c232 --- /dev/null +++ b/vendor/libgit2/src/tsort.c @@ -0,0 +1,359 @@ + +#include "common.h" + +/** + * An array-of-pointers implementation of Python's Timsort + * Based on code by Christopher Swenson under the MIT license + * + * Copyright (c) 2010 Christopher Swenson + * Copyright (c) 2011 Vicent Marti + */ + +#ifndef MAX +# define MAX(x,y) (((x) > (y) ? (x) : (y))) +#endif + +#ifndef MIN +# define MIN(x,y) (((x) < (y) ? (x) : (y))) +#endif + +typedef int (*cmp_ptr_t)(const void *, const void *); + +static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp) +{ + int l, c, r; + void *lx, *cx; + + l = 0; + r = size - 1; + c = r >> 1; + lx = dst[l]; + + /* check for beginning conditions */ + if (cmp(x, lx) < 0) + return 0; + + else if (cmp(x, lx) == 0) { + int i = 1; + while (cmp(x, dst[i]) == 0) + i++; + return i; + } + + /* guaranteed not to be >= rx */ + cx = dst[c]; + while (1) { + const int val = cmp(x, cx); + if (val < 0) { + if (c - l <= 1) return c; + r = c; + } else if (val > 0) { + if (r - c <= 1) return c + 1; + l = c; + lx = cx; + } else { + do { + cx = dst[++c]; + } while (cmp(x, cx) == 0); + return c; + } + c = l + ((r - l) >> 1); + cx = dst[c]; + } +} + +/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */ +static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp) +{ + size_t i; + void *x; + int location; + + for (i = start; i < size; i++) { + int j; + /* If this entry is already correct, just move along */ + if (cmp(dst[i - 1], dst[i]) <= 0) + continue; + + /* Else we need to find the right place, shift everything over, and squeeze in */ + x = dst[i]; + location = binsearch(dst, x, i, cmp); + for (j = i - 1; j >= location; j--) { + dst[j + 1] = dst[j]; + } + dst[location] = x; + } +} + + +/* timsort implementation, based on timsort.txt */ +struct tsort_run { + ssize_t start; + ssize_t length; +}; + +struct tsort_store { + size_t alloc; + cmp_ptr_t cmp; + void **storage; +}; + +static void reverse_elements(void **dst, int start, int end) +{ + while (start < end) { + void *tmp = dst[start]; + dst[start] = dst[end]; + dst[end] = tmp; + + start++; + end--; + } +} + +static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store) +{ + ssize_t curr = start + 2; + + if (size - start == 1) + return 1; + + if (start >= size - 2) { + if (store->cmp(dst[size - 2], dst[size - 1]) > 0) { + void *tmp = dst[size - 1]; + dst[size - 1] = dst[size - 2]; + dst[size - 2] = tmp; + } + + return 2; + } + + if (store->cmp(dst[start], dst[start + 1]) <= 0) { + while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0) + curr++; + + return curr - start; + } else { + while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0) + curr++; + + /* reverse in-place */ + reverse_elements(dst, start, curr - 1); + return curr - start; + } +} + +static int compute_minrun(size_t n) +{ + int r = 0; + while (n >= 64) { + r |= n & 1; + n >>= 1; + } + return n + r; +} + +static int check_invariant(struct tsort_run *stack, int stack_curr) +{ + if (stack_curr < 2) + return 1; + + else if (stack_curr == 2) { + const int A = stack[stack_curr - 2].length; + const int B = stack[stack_curr - 1].length; + return (A > B); + } else { + const int A = stack[stack_curr - 3].length; + const int B = stack[stack_curr - 2].length; + const int C = stack[stack_curr - 1].length; + return !((A <= B + C) || (B <= C)); + } +} + +static int resize(struct tsort_store *store, size_t new_size) +{ + if (store->alloc < new_size) { + void **tempstore = realloc(store->storage, new_size * sizeof(void *)); + + /** + * Do not propagate on OOM; this will abort the sort and + * leave the array unsorted, but no error code will be + * raised + */ + if (tempstore == NULL) + return -1; + + store->storage = tempstore; + store->alloc = new_size; + } + + return 0; +} + +static void merge(void **dst, const struct tsort_run *stack, int stack_curr, struct tsort_store *store) +{ + const ssize_t A = stack[stack_curr - 2].length; + const ssize_t B = stack[stack_curr - 1].length; + const ssize_t curr = stack[stack_curr - 2].start; + + void **storage; + ssize_t i, j, k; + + if (resize(store, MIN(A, B)) < 0) + return; + + storage = store->storage; + + /* left merge */ + if (A < B) { + memcpy(storage, &dst[curr], A * sizeof(void *)); + i = 0; + j = curr + A; + + for (k = curr; k < curr + A + B; k++) { + if ((i < A) && (j < curr + A + B)) { + if (store->cmp(storage[i], dst[j]) <= 0) + dst[k] = storage[i++]; + else + dst[k] = dst[j++]; + } else if (i < A) { + dst[k] = storage[i++]; + } else + dst[k] = dst[j++]; + } + } else { + memcpy(storage, &dst[curr + A], B * sizeof(void *)); + i = B - 1; + j = curr + A - 1; + + for (k = curr + A + B - 1; k >= curr; k--) { + if ((i >= 0) && (j >= curr)) { + if (store->cmp(dst[j], storage[i]) > 0) + dst[k] = dst[j--]; + else + dst[k] = storage[i--]; + } else if (i >= 0) + dst[k] = storage[i--]; + else + dst[k] = dst[j--]; + } + } +} + +static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size) +{ + ssize_t A, B, C; + + while (1) { + /* if the stack only has one thing on it, we are done with the collapse */ + if (stack_curr <= 1) + break; + + /* if this is the last merge, just do it */ + if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) { + merge(dst, stack, stack_curr, store); + stack[0].length += stack[1].length; + stack_curr--; + break; + } + + /* check if the invariant is off for a stack of 2 elements */ + else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) { + merge(dst, stack, stack_curr, store); + stack[0].length += stack[1].length; + stack_curr--; + break; + } + else if (stack_curr == 2) + break; + + A = stack[stack_curr - 3].length; + B = stack[stack_curr - 2].length; + C = stack[stack_curr - 1].length; + + /* check first invariant */ + if (A <= B + C) { + if (A < C) { + merge(dst, stack, stack_curr - 1, store); + stack[stack_curr - 3].length += stack[stack_curr - 2].length; + stack[stack_curr - 2] = stack[stack_curr - 1]; + stack_curr--; + } else { + merge(dst, stack, stack_curr, store); + stack[stack_curr - 2].length += stack[stack_curr - 1].length; + stack_curr--; + } + } else if (B <= C) { + merge(dst, stack, stack_curr, store); + stack[stack_curr - 2].length += stack[stack_curr - 1].length; + stack_curr--; + } else + break; + } + + return stack_curr; +} + +#define PUSH_NEXT() do {\ + len = count_run(dst, curr, size, store);\ + run = minrun;\ + if (run < minrun) run = minrun;\ + if (run > (ssize_t)size - curr) run = size - curr;\ + if (run > len) {\ + bisort(&dst[curr], len, run, cmp);\ + len = run;\ + }\ + run_stack[stack_curr].start = curr;\ + run_stack[stack_curr++].length = len;\ + curr += len;\ + if (curr == (ssize_t)size) {\ + /* finish up */ \ + while (stack_curr > 1) { \ + merge(dst, run_stack, stack_curr, store); \ + run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \ + stack_curr--; \ + } \ + if (store->storage != NULL) {\ + free(store->storage);\ + store->storage = NULL;\ + }\ + return;\ + }\ +}\ +while (0) + +void git__tsort(void **dst, size_t size, cmp_ptr_t cmp) +{ + struct tsort_store _store, *store = &_store; + struct tsort_run run_stack[128]; + + ssize_t stack_curr = 0; + ssize_t len, run; + ssize_t curr = 0; + ssize_t minrun; + + if (size < 64) { + bisort(dst, 1, size, cmp); + return; + } + + /* compute the minimum run length */ + minrun = compute_minrun(size); + + /* temporary storage for merges */ + store->alloc = 0; + store->storage = NULL; + store->cmp = cmp; + + PUSH_NEXT(); + PUSH_NEXT(); + PUSH_NEXT(); + + while (1) { + if (!check_invariant(run_stack, stack_curr)) { + stack_curr = collapse(dst, run_stack, stack_curr, store, size); + continue; + } + + PUSH_NEXT(); + } +} diff --git a/vendor/libgit2/src/unix/map.c b/vendor/libgit2/src/unix/map.c index 4780bd23c..5192c8e4c 100644 --- a/vendor/libgit2/src/unix/map.c +++ b/vendor/libgit2/src/unix/map.c @@ -1,10 +1,12 @@ +#include + +#ifndef GIT_WIN32 #include "map.h" #include #include - -int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) { int mprot = 0; int mflag = 0; @@ -13,7 +15,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if ((out == NULL) || (len == 0)) { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length"); } out->data = NULL; @@ -25,7 +27,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o mprot = PROT_READ; else { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters"); } if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) @@ -35,27 +37,28 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if (flags & GIT_MAP_FIXED) { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set"); } out->data = mmap(NULL, len, mprot, mflag, fd, offset); if (!out->data || out->data == MAP_FAILED) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to mmap. Could not write data"); out->len = len; return GIT_SUCCESS; } -int git__munmap(git_map *map) +int p_munmap(git_map *map) { assert(map != NULL); if (!map) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist"); munmap(map->data, map->len); return GIT_SUCCESS; } +#endif diff --git a/vendor/libgit2/src/unix/posix.h b/vendor/libgit2/src/unix/posix.h new file mode 100644 index 000000000..a49a5cfe7 --- /dev/null +++ b/vendor/libgit2/src/unix/posix.h @@ -0,0 +1,18 @@ +#ifndef INCLUDE_posix__w32_h__ +#define INCLUDE_posix__w32_h__ + +#include + +#define p_lstat(p,b) lstat(p,b) +#define p_readlink(a, b, c) readlink(a, b, c) +#define p_link(o,n) link(o, n) +#define p_unlink(p) unlink(p) +#define p_mkdir(p,m) mkdir(p, m) +#define p_fsync(fd) fsync(fd) +#define p_realpath(p, po) realpath(p, po) +#define p_fnmatch(p, s, f) fnmatch(p, s, f) +#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) +#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) +#define p_mkstemp(p) mkstemp(p) + +#endif diff --git a/vendor/libgit2/src/util.c b/vendor/libgit2/src/util.c index 55a7ab2a9..29bf755f8 100644 --- a/vendor/libgit2/src/util.c +++ b/vendor/libgit2/src/util.c @@ -1,8 +1,20 @@ -#define GIT__NO_HIDE_MALLOC +#include #include "common.h" #include #include #include +#include "posix.h" + +#ifdef _MSC_VER +# include +#endif + +void git_libgit2_version(int *major, int *minor, int *rev) +{ + *major = LIBGIT2_VER_MAJOR; + *minor = LIBGIT2_VER_MINOR; + *rev = LIBGIT2_VER_REVISION; +} void git_strarray_free(git_strarray *array) { @@ -13,6 +25,21 @@ void git_strarray_free(git_strarray *array) free(array->strings); } +int git__fnmatch(const char *pattern, const char *name, int flags) +{ + int ret; + + ret = p_fnmatch(pattern, name, flags); + switch (ret) { + case 0: + return GIT_SUCCESS; + case FNM_NOMATCH: + return GIT_ENOMATCH; + default: + return git__throw(GIT_EOSERR, "Error trying to match path"); + } +} + int git__strtol32(long *result, const char *nptr, const char **endptr, int base) { const char *p; @@ -79,29 +106,30 @@ int git__strtol32(long *result, const char *nptr, const char **endptr, int base) Return: if (ndig == 0) - return GIT_ENOTNUM; + return git__throw(GIT_ENOTNUM, "Failed to convert string to long. Not a number"); if (endptr) *endptr = p; if (ovfl) - return GIT_EOVERFLOW; + return git__throw(GIT_EOVERFLOW, "Failed to convert string to long. Overflow error"); *result = neg ? -n : n; return GIT_SUCCESS; } -int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...) +void git__strntolower(char *str, int len) { - va_list va; - int r; - - va_start(va, fmt); - r = vsnprintf(buf, buf_sz, fmt, va); - va_end(va); - if (r < 0 || ((size_t) r) >= buf_sz) - return GIT_ERROR; - return r; + int i; + + for (i = 0; i < len; ++i) { + str[i] = (char) tolower(str[i]); + } +} + +void git__strtolower(char *str) +{ + git__strntolower(str, strlen(str)); } int git__prefixcmp(const char *str, const char *prefix) @@ -124,226 +152,29 @@ int git__suffixcmp(const char *str, const char *suffix) return strcmp(str + (a - b), suffix); } -/* - * Based on the Android implementation, BSD licensed. - * Check http://android.git.kernel.org/ - */ -int git__basename_r(char *buffer, size_t bufflen, const char *path) +char *git__strtok(char **end, const char *sep) { - const char *endp, *startp; - int len, result; - - /* Empty or NULL string gets treated as "." */ - if (path == NULL || *path == '\0') { - startp = "."; - len = 1; - goto Exit; - } + char *ptr = *end; - /* Strip trailing slashes */ - endp = path + strlen(path) - 1; - while (endp > path && *endp == '/') - endp--; + while (*ptr && strchr(sep, *ptr)) + ++ptr; - /* All slashes becomes "/" */ - if (endp == path && *endp == '/') { - startp = "/"; - len = 1; - goto Exit; - } - - /* Find the start of the base */ - startp = endp; - while (startp > path && *(startp - 1) != '/') - startp--; - - len = endp - startp +1; + if (*ptr) { + char *start = ptr; + *end = start + 1; -Exit: - result = len; - if (buffer == NULL) { - return result; - } - if (len > (int)bufflen-1) { - len = (int)bufflen-1; - result = GIT_ENOMEM; - } + while (**end && !strchr(sep, **end)) + ++*end; - if (len >= 0) { - memmove(buffer, startp, len); - buffer[len] = 0; - } - return result; -} - -/* - * Based on the Android implementation, BSD licensed. - * Check http://android.git.kernel.org/ - */ -int git__dirname_r(char *buffer, size_t bufflen, const char *path) -{ - const char *endp; - int result, len; - - /* Empty or NULL string gets treated as "." */ - if (path == NULL || *path == '\0') { - path = "."; - len = 1; - goto Exit; - } - - /* Strip trailing slashes */ - endp = path + strlen(path) - 1; - while (endp > path && *endp == '/') - endp--; - - /* Find the start of the dir */ - while (endp > path && *endp != '/') - endp--; - - /* Either the dir is "/" or there are no slashes */ - if (endp == path) { - path = (*endp == '/') ? "/" : "."; - len = 1; - goto Exit; - } - - do { - endp--; - } while (endp > path && *endp == '/'); - - len = endp - path +1; - -Exit: - result = len; - if (len+1 > GIT_PATH_MAX) { - return GIT_ENOMEM; - } - if (buffer == NULL) - return result; - - if (len > (int)bufflen-1) { - len = (int)bufflen-1; - result = GIT_ENOMEM; - } - - if (len >= 0) { - memmove(buffer, path, len); - buffer[len] = 0; - } - return result; -} - - -char *git__dirname(const char *path) -{ - char *dname = NULL; - int len; - - len = (path ? strlen(path) : 0) + 2; - dname = (char *)git__malloc(len); - if (dname == NULL) - return NULL; - - if (git__dirname_r(dname, len, path) < GIT_SUCCESS) { - free(dname); - return NULL; - } - - return dname; -} - -char *git__basename(const char *path) -{ - char *bname = NULL; - int len; - - len = (path ? strlen(path) : 0) + 2; - bname = (char *)git__malloc(len); - if (bname == NULL) - return NULL; - - if (git__basename_r(bname, len, path) < GIT_SUCCESS) { - free(bname); - return NULL; - } - - return bname; -} - - -const char *git__topdir(const char *path) -{ - size_t len; - int i; - - assert(path); - len = strlen(path); - - if (!len || path[len - 1] != '/') - return NULL; - - for (i = len - 2; i >= 0; --i) - if (path[i] == '/') - break; - - return &path[i + 1]; -} - -void git__joinpath_n(char *buffer_out, int count, ...) -{ - va_list ap; - int i; - char *buffer_start = buffer_out; - - va_start(ap, count); - for (i = 0; i < count; ++i) { - const char *path; - int len; - - path = va_arg(ap, const char *); - - assert((i == 0) || path != buffer_start); - - if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/') - path++; - - if (!*path) - continue; - - len = strlen(path); - memmove(buffer_out, path, len); - buffer_out = buffer_out + len; + if (**end) { + **end = '\0'; + ++*end; + } - if (i < count - 1 && buffer_out[-1] != '/') - *buffer_out++ = '/'; + return start; } - va_end(ap); - - *buffer_out = '\0'; -} -static char *strtok_raw(char *output, char *src, char *delimit, int keep) -{ - while (*src && strchr(delimit, *src) == NULL) - *output++ = *src++; - - *output = 0; - - if (keep) - return src; - else - return *src ? src+1 : src; -} - -char *git__strtok(char *output, char *src, char *delimit) -{ - return strtok_raw(output, src, delimit, 0); -} - -char *git__strtok_keep(char *output, char *src, char *delimit) -{ - return strtok_raw(output, src, delimit, 1); + return NULL; } void git__hexdump(const char *buffer, size_t len) @@ -403,17 +234,17 @@ uint32_t git__hash(const void *key, int len, unsigned int seed) while(len >= 4) { uint32_t k = *(uint32_t *)data; - k *= m; - k ^= k >> r; - k *= m; - - h *= m; + k *= m; + k ^= k >> r; + k *= m; + + h *= m; h ^= k; data += 4; len -= 4; } - + switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; @@ -426,7 +257,7 @@ uint32_t git__hash(const void *key, int len, unsigned int seed) h ^= h >> 15; return h; -} +} #else /* Cross-platform version of Murmurhash3 @@ -484,5 +315,43 @@ uint32_t git__hash(const void *key, int len, uint32_t seed) h1 ^= h1 >> 16; return h1; -} +} #endif + +/** + * A modified `bsearch` from the BSD glibc. + * + * Copyright (c) 1990 Regents of the University of California. + * All rights reserved. + */ +void **git__bsearch(const void *key, void **base, size_t nmemb, int (*compar)(const void *, const void *)) +{ + int lim, cmp; + void **p; + + for (lim = nmemb; lim != 0; lim >>= 1) { + p = base + (lim >> 1); + cmp = (*compar)(key, *p); + if (cmp > 0) { /* key > p: move right */ + base = p + 1; + lim--; + } else if (cmp == 0) { + return (void **)p; + } /* else move left */ + } + return NULL; +} + +/** + * A strcmp wrapper + * + * We don't want direct pointers to the CRT on Windows, we may + * get stdcall conflicts. + */ +int git__strcmp_cb(const void *a, const void *b) +{ + const char *stra = (const char *)a; + const char *strb = (const char *)b; + + return strcmp(stra, strb); +} diff --git a/vendor/libgit2/src/util.h b/vendor/libgit2/src/util.h index 6724e8d41..78f9f8276 100644 --- a/vendor/libgit2/src/util.h +++ b/vendor/libgit2/src/util.h @@ -4,8 +4,11 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define bitsizeof(x) (CHAR_BIT * sizeof(x)) #define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits)))) +#ifndef min +# define min(a,b) ((a) < (b) ? (a) : (b)) +#endif -/* +/* * Custom memory allocation wrappers * that set error code and error message * on allocation failure @@ -34,6 +37,27 @@ GIT_INLINE(char *) git__strdup(const char *str) return ptr; } +GIT_INLINE(char *) git__strndup(const char *str, size_t n) +{ + size_t length; + char *ptr; + + length = strlen(str); + if (n < length) + length = n; + + ptr = (char*)malloc(length + 1); + if (!ptr) { + git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string"); + return NULL; + } + + memcpy(ptr, str, length); + ptr[length] = '\0'; + + return ptr; +} + GIT_INLINE(void *) git__realloc(void *ptr, size_t size) { void *new_ptr = realloc(ptr, size); @@ -42,70 +66,14 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size) return new_ptr; } -extern int git__fmt(char *, size_t, const char *, ...) - GIT_FORMAT_PRINTF(3, 4); extern int git__prefixcmp(const char *str, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); extern int git__strtol32(long *n, const char *buff, const char **end_buf, int base); -/* - * The dirname() function shall take a pointer to a character string - * that contains a pathname, and return a pointer to a string that is a - * pathname of the parent directory of that file. Trailing '/' characters - * in the path are not counted as part of the path. - * - * If path does not contain a '/', then dirname() shall return a pointer to - * the string ".". If path is a null pointer or points to an empty string, - * dirname() shall return a pointer to the string "." . - * - * The `git__dirname` implementation is thread safe. The returned - * string must be manually free'd. - * - * The `git__dirname_r` implementation expects a string allocated - * by the user with big enough size. - */ -extern char *git__dirname(const char *path); -extern int git__dirname_r(char *buffer, size_t bufflen, const char *path); - -/* - * This function returns the basename of the file, which is the last - * part of its full name given by fname, with the drive letter and - * leading directories stripped off. For example, the basename of - * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo. - * - * Trailing slashes and backslashes are significant: the basename of - * c:/foo/bar/ is an empty string after the rightmost slash. - * - * The `git__basename` implementation is thread safe. The returned - * string must be manually free'd. - * - * The `git__basename_r` implementation expects a string allocated - * by the user with big enough size. - */ -extern char *git__basename(const char *path); -extern int git__basename_r(char *buffer, size_t bufflen, const char *path); - -extern const char *git__topdir(const char *path); - -/** - * Join two paths together. Takes care of properly fixing the - * middle slashes and everything - * - * The paths are joined together into buffer_out; this is expected - * to be an user allocated buffer of `GIT_PATH_MAX` size - */ -extern void git__joinpath_n(char *buffer_out, int npath, ...); - -GIT_INLINE(void) git__joinpath(char *buffer_out, const char *path_a, const char *path_b) -{ - git__joinpath_n(buffer_out, 2, path_a, path_b); -} - extern void git__hexdump(const char *buffer, size_t n); extern uint32_t git__hash(const void *key, int len, uint32_t seed); - /** @return true if p fits into the range of a size_t */ GIT_INLINE(int) git__is_sizet(git_off_t p) { @@ -120,12 +88,12 @@ GIT_INLINE(int) git__is_sizet(git_off_t p) # define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s)))) #endif -extern char *git__strtok(char *output, char *src, char *delimit); -extern char *git__strtok_keep(char *output, char *src, char *delimit); +extern char *git__strtok(char **end, const char *sep); -#define STRLEN(str) (sizeof(str) - 1) +extern void git__strntolower(char *str, int len); +extern void git__strtolower(char *str); -#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1) +extern int git__fnmatch(const char *pattern, const char *name, int flags); /* * Realloc the buffer pointed at by variable 'x' so that it can hold @@ -146,4 +114,10 @@ extern char *git__strtok_keep(char *output, char *src, char *delimit); } \ } while (0) +extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *)); +extern void **git__bsearch(const void *key, void **base, size_t nmemb, + int (*compar)(const void *, const void *)); + +extern int git__strcmp_cb(const void *a, const void *b); + #endif /* INCLUDE_util_h__ */ diff --git a/vendor/libgit2/src/vector.c b/vendor/libgit2/src/vector.c index d0b0c5c56..0b83b8b0d 100644 --- a/vendor/libgit2/src/vector.c +++ b/vendor/libgit2/src/vector.c @@ -61,7 +61,7 @@ int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp v->_alloc_size = initial_size; v->_cmp = cmp; - + v->length = 0; v->sorted = 1; @@ -94,7 +94,7 @@ void git_vector_sort(git_vector *v) if (v->sorted || v->_cmp == NULL) return; - qsort(v->contents, v->length, sizeof(void *), v->_cmp); + git__tsort(v->contents, v->length, v->_cmp); v->sorted = 1; } @@ -106,15 +106,15 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke /* need comparison function to sort the vector */ if (v->_cmp == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't sort vector. No comparison function set"); git_vector_sort(v); - find = bsearch(key, v->contents, v->length, sizeof(void *), key_lookup); + find = git__bsearch(key, v->contents, v->length, key_lookup); if (find != NULL) return (int)(find - v->contents); - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't find element"); } int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key) @@ -128,7 +128,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key return i; } - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't find element"); } static int strict_comparison(const void *a, const void *b) @@ -143,9 +143,6 @@ int git_vector_search(git_vector *v, const void *entry) int git_vector_bsearch(git_vector *v, const void *key) { - if (v->_cmp == NULL) - return GIT_ENOTFOUND; - return git_vector_bsearch2(v, v->_cmp, key); } @@ -156,7 +153,7 @@ int git_vector_remove(git_vector *v, unsigned int idx) assert(v); if (idx >= v->length || v->length == 0) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't remove element. Index out of bounds"); for (i = idx; i < v->length - 1; ++i) v->contents[i] = v->contents[i + 1]; @@ -165,6 +162,26 @@ int git_vector_remove(git_vector *v, unsigned int idx) return GIT_SUCCESS; } +void git_vector_uniq(git_vector *v) +{ + git_vector_cmp cmp; + unsigned int i, j; + + if (v->length <= 1) + return; + + git_vector_sort(v); + cmp = v->_cmp ? v->_cmp : strict_comparison; + + for (i = 0, j = 1 ; j < v->length; ++j) + if (!cmp(v->contents[i], v->contents[j])) + v->contents[i] = v->contents[j]; + else + v->contents[++i] = v->contents[j]; + + v->length -= j - i - 1; +} + void git_vector_clear(git_vector *v) { assert(v); diff --git a/vendor/libgit2/src/vector.h b/vendor/libgit2/src/vector.h index 256452ee5..c43a7ce07 100644 --- a/vendor/libgit2/src/vector.h +++ b/vendor/libgit2/src/vector.h @@ -30,7 +30,10 @@ GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position) return (position < v->length) ? v->contents[position] : NULL; } +#define git_vector_foreach(v, iter, elem) \ + for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ ) + int git_vector_insert(git_vector *v, void *element); int git_vector_remove(git_vector *v, unsigned int idx); - +void git_vector_uniq(git_vector *v); #endif diff --git a/vendor/libgit2/src/win32/fnmatch.c b/vendor/libgit2/src/win32/fnmatch.c new file mode 100644 index 000000000..de2f3e74f --- /dev/null +++ b/vendor/libgit2/src/win32/fnmatch.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +#include +#include +#include + +#include "fnmatch.h" + +#define EOS '\0' + +#define RANGE_MATCH 1 +#define RANGE_NOMATCH 0 +#define RANGE_ERROR (-1) + +static int rangematch(const char *, char, int, char **); + +int +p_fnmatch(const char *pattern, const char *string, int flags) +{ + const char *stringstart; + char *newp; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) { + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + } else if (c == '/' && (flags & FNM_PATHNAME)) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!p_fnmatch(pattern, string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && (flags & FNM_PATHNAME)) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + switch (rangematch(pattern, *string, flags, &newp)) { + case RANGE_ERROR: + /* not a good range, treat as normal text */ + goto normal; + case RANGE_MATCH: + pattern = newp; + break; + case RANGE_NOMATCH: + return (FNM_NOMATCH); + } + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + normal: + if (c != *string && !((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string)))) + return (FNM_NOMATCH); + ++string; + break; + } + /* NOTREACHED */ +} + +static int +rangematch(const char *pattern, char test, int flags, char **newp) +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ((negate = (*pattern == '!' || *pattern == '^')) != 0) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = (char)tolower((unsigned char)test); + + /* + * A right bracket shall lose its special meaning and represent + * itself in a bracket expression if it occurs first in the list. + * -- POSIX.2 2.8.3.2 + */ + ok = 0; + c = *pattern++; + do { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (RANGE_ERROR); + if (c == '/' && (flags & FNM_PATHNAME)) + return (RANGE_NOMATCH); + if ((flags & FNM_CASEFOLD)) + c = (char)tolower((unsigned char)c); + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (RANGE_ERROR); + if (flags & FNM_CASEFOLD) + c2 = (char)tolower((unsigned char)c2); + if (c <= test && test <= c2) + ok = 1; + } else if (c == test) + ok = 1; + } while ((c = *pattern++) != ']'); + + *newp = (char *)pattern; + return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH); +} + diff --git a/vendor/libgit2/src/win32/fnmatch.h b/vendor/libgit2/src/win32/fnmatch.h new file mode 100644 index 000000000..1309c6e9a --- /dev/null +++ b/vendor/libgit2/src/win32/fnmatch.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef INCLUDE_fnmatch__w32_h__ +#define INCLUDE_fnmatch__w32_h__ + +#include "common.h" + +#define FNM_NOMATCH 1 /* Match failed. */ +#define FNM_NOSYS 2 /* Function not supported (unused). */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ +#define FNM_LEADING_DIR 0x08 /* Ignore / after Imatch. */ +#define FNM_CASEFOLD 0x10 /* Case insensitive search. */ + +#define FNM_IGNORECASE FNM_CASEFOLD +#define FNM_FILE_NAME FNM_PATHNAME + +extern int p_fnmatch(const char *pattern, const char *string, int flags); + +#endif /* _FNMATCH_H */ + diff --git a/vendor/libgit2/src/win32/map.c b/vendor/libgit2/src/win32/map.c index e5f8e559a..76b926490 100644 --- a/vendor/libgit2/src/win32/map.c +++ b/vendor/libgit2/src/win32/map.c @@ -16,7 +16,7 @@ static DWORD get_page_size(void) return page_size; } -int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) { HANDLE fh = (HANDLE)_get_osfhandle(fd); DWORD page_size = get_page_size(); @@ -31,7 +31,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if ((out == NULL) || (len == 0)) { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length"); } out->data = NULL; @@ -40,7 +40,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if (fh == INVALID_HANDLE_VALUE) { errno = EBADF; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value"); } if (prot & GIT_PROT_WRITE) @@ -49,7 +49,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o fmap_prot |= PAGE_READONLY; else { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters"); } if (prot & GIT_PROT_WRITE) @@ -59,7 +59,7 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if (flags & GIT_MAP_FIXED) { errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set"); } page_start = (offset / page_size) * page_size; @@ -67,14 +67,14 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o if (page_offset != 0) { /* offset must be multiple of page size */ errno = EINVAL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. Offset must be multiple of page size"); } out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL); if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) { /* errno = ? */ out->fmh = NULL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value"); } assert(sizeof(git_off_t) == 8); @@ -85,19 +85,19 @@ int git__mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t o /* errno = ? */ CloseHandle(out->fmh); out->fmh = NULL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to mmap. No data written"); } out->len = len; return GIT_SUCCESS; } -int git__munmap(git_map *map) +int p_munmap(git_map *map) { assert(map != NULL); if (!map) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist"); if (map->data) { if (!UnmapViewOfFile(map->data)) { @@ -105,7 +105,7 @@ int git__munmap(git_map *map) CloseHandle(map->fmh); map->data = NULL; map->fmh = NULL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to munmap. Could not unmap view of file"); } map->data = NULL; } @@ -114,7 +114,7 @@ int git__munmap(git_map *map) if (!CloseHandle(map->fmh)) { /* errno = ? */ map->fmh = NULL; - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to munmap. Could not close handle"); } map->fmh = NULL; } diff --git a/vendor/libgit2/src/win32/mingw-compat.h b/vendor/libgit2/src/win32/mingw-compat.h new file mode 100644 index 000000000..64d780b16 --- /dev/null +++ b/vendor/libgit2/src/win32/mingw-compat.h @@ -0,0 +1,18 @@ +#ifndef INCLUDE_mingw_compat__ +#define INCLUDE_mingw_compat__ + +#if defined(__MINGW32__) + +/* use a 64-bit file offset type */ +# define lseek _lseeki64 +# define stat _stati64 +# define fstat _fstati64 + +/* stat: file mode type testing macros */ +# define _S_IFLNK 0120000 +# define S_IFLNK _S_IFLNK +# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) + +#endif + +#endif /* INCLUDE_mingw_compat__ */ diff --git a/vendor/libgit2/src/win32/msvc-compat.h b/vendor/libgit2/src/win32/msvc-compat.h new file mode 100644 index 000000000..df3e62d11 --- /dev/null +++ b/vendor/libgit2/src/win32/msvc-compat.h @@ -0,0 +1,52 @@ +#ifndef INCLUDE_msvc_compat__ +#define INCLUDE_msvc_compat__ + +#if defined(_MSC_VER) + +/* access() mode parameter #defines */ +# define F_OK 0 /* existence check */ +# define W_OK 2 /* write mode check */ +# define R_OK 4 /* read mode check */ + +# define lseek _lseeki64 +# define stat _stat64 +# define fstat _fstat64 + +/* stat: file mode type testing macros */ +# define _S_IFLNK 0120000 +# define S_IFLNK _S_IFLNK + +# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) +# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) + +# define mode_t unsigned short + +/* case-insensitive string comparison */ +# define strcasecmp _stricmp +# define strncasecmp _strnicmp + +#if (_MSC_VER >= 1600) +# include +#else +/* add some missing typedef's */ +typedef signed char int8_t; +typedef unsigned char uint8_t; + +typedef short int16_t; +typedef unsigned short uint16_t; + +typedef long int32_t; +typedef unsigned long uint32_t; + +typedef long long int64_t; +typedef unsigned long long uint64_t; + +typedef long long intmax_t; +typedef unsigned long long uintmax_t; +#endif + +#endif + +#endif /* INCLUDE_msvc_compat__ */ diff --git a/vendor/libgit2/src/win32/posix.c b/vendor/libgit2/src/win32/posix.c new file mode 100644 index 000000000..be6a7c0d0 --- /dev/null +++ b/vendor/libgit2/src/win32/posix.c @@ -0,0 +1,248 @@ +#include "posix.h" +#include "path.h" +#include +#include + +int p_unlink(const char *path) +{ + chmod(path, 0666); + return unlink(path); +} + +int p_fsync(int fd) +{ + HANDLE fh = (HANDLE)_get_osfhandle(fd); + + if (fh == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + if (!FlushFileBuffers(fh)) { + DWORD code = GetLastError(); + + if (code == ERROR_INVALID_HANDLE) + errno = EINVAL; + else + errno = EIO; + + return -1; + } + + return 0; +} + +GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft) +{ + long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; + winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */ + winTime /= 10000000; /* Nano to seconds resolution */ + return (time_t)winTime; +} + +static int do_lstat(const char *file_name, struct stat *buf) +{ + WIN32_FILE_ATTRIBUTE_DATA fdata; + + if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) { + int fMode = S_IREAD; + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + fMode |= S_IFDIR; + else + fMode |= S_IFREG; + + if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + fMode |= S_IWRITE; + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) + fMode |= S_IFLNK; + + buf->st_ino = 0; + buf->st_gid = 0; + buf->st_uid = 0; + buf->st_nlink = 1; + buf->st_mode = (mode_t)fMode; + buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */ + buf->st_dev = buf->st_rdev = (_getdrive() - 1); + buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); + buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); + buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); + return GIT_SUCCESS; + } + + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_SHARING_BUFFER_EXCEEDED: + return GIT_EOSERR; + + case ERROR_BUFFER_OVERFLOW: + case ERROR_NOT_ENOUGH_MEMORY: + return GIT_ENOMEM; + + default: + return GIT_EINVALIDPATH; + } +} + +int p_lstat(const char *file_name, struct stat *buf) +{ + int namelen, error; + char alt_name[GIT_PATH_MAX]; + + if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS) + return GIT_SUCCESS; + + /* if file_name ended in a '/', Windows returned ENOENT; + * try again without trailing slashes + */ + if (error != GIT_EINVALIDPATH) + return git__throw(GIT_EOSERR, "Failed to lstat file"); + + namelen = strlen(file_name); + if (namelen && file_name[namelen-1] != '/') + return git__throw(GIT_EOSERR, "Failed to lstat file"); + + while (namelen && file_name[namelen-1] == '/') + --namelen; + + if (!namelen || namelen >= GIT_PATH_MAX) + return git__throw(GIT_ENOMEM, "Failed to lstat file"); + + memcpy(alt_name, file_name, namelen); + alt_name[namelen] = 0; + return do_lstat(alt_name, buf); +} + +int p_readlink(const char *link, char *target, size_t target_len) +{ + typedef DWORD (WINAPI *fpath_func)(HANDLE, LPTSTR, DWORD, DWORD); + static fpath_func pGetFinalPath = NULL; + HANDLE hFile; + DWORD dwRet; + + /* + * Try to load the pointer to pGetFinalPath dynamically, because + * it is not available in platforms older than Vista + */ + if (pGetFinalPath == NULL) { + HINSTANCE library = LoadLibrary("kernel32"); + + if (library != NULL) + pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleA"); + + if (pGetFinalPath == NULL) + return git__throw(GIT_EOSERR, + "'GetFinalPathNameByHandleA' is not available in this platform"); + } + + hFile = CreateFile(link, // file to open + GENERIC_READ, // open for reading + FILE_SHARE_READ, // share for reading + NULL, // default security + OPEN_EXISTING, // existing file only + FILE_FLAG_BACKUP_SEMANTICS, // normal file + NULL); // no attr. template + + if (hFile == INVALID_HANDLE_VALUE) + return GIT_EOSERR; + + dwRet = pGetFinalPath(hFile, target, target_len, 0x0); + if (dwRet >= target_len) + return GIT_ENOMEM; + + CloseHandle(hFile); + + if (dwRet > 4) { + /* Skip first 4 characters if they are "\\?\" */ + if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') { + char tmp[GIT_PATH_MAX]; + unsigned int offset = 4; + dwRet -= 4; + + /* \??\UNC\ */ + if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') { + offset += 2; + dwRet -= 2; + target[offset] = '\\'; + } + + memcpy(tmp, target + offset, dwRet); + memcpy(target, tmp, dwRet); + } + } + + target[dwRet] = '\0'; + return dwRet; +} + +int p_hide_directory__w32(const char *path) +{ + int error; + + error = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) != 0 ? + GIT_SUCCESS : GIT_ERROR; /* MSDN states a "non zero" value indicates a success */ + + if (error < GIT_SUCCESS) + error = git__throw(GIT_EOSERR, "Failed to hide directory '%s'", path); + + return error; +} + +char *p_realpath(const char *orig_path, char *buffer) +{ + int ret, alloc = 0; + + if (buffer == NULL) { + buffer = (char *)git__malloc(GIT_PATH_MAX); + alloc = 1; + } + + ret = GetFullPathName(orig_path, GIT_PATH_MAX, buffer, NULL); + if (!ret || ret > GIT_PATH_MAX) { + if (alloc) free(buffer); + return NULL; + } + + git_path_mkposix(buffer); + return buffer; +} + +int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) +{ +#ifdef _MSC_VER + int len = _vsnprintf(buffer, count, format, argptr); + return (len < 0) ? _vscprintf(format, argptr) : len; +#else /* MinGW */ + return vsnprintf(buffer, count, format, argptr); +#endif +} + +int p_snprintf(char *buffer, size_t count, const char *format, ...) +{ + va_list va; + int r; + + va_start(va, format); + r = p_vsnprintf(buffer, count, format, va); + va_end(va); + + return r; +} + +extern int p_creat(const char *path, int mode); + +int p_mkstemp(char *tmp_path) +{ +#if defined(_MSC_VER) + if (_mktemp_s(tmp_path, GIT_PATH_MAX) != 0) + return GIT_EOSERR; +#else + if (_mktemp(tmp_path) == NULL) + return GIT_EOSERR; +#endif + + return p_creat(tmp_path, 0744); +} diff --git a/vendor/libgit2/src/win32/posix.h b/vendor/libgit2/src/win32/posix.h new file mode 100644 index 000000000..28d978959 --- /dev/null +++ b/vendor/libgit2/src/win32/posix.h @@ -0,0 +1,30 @@ +#ifndef INCLUDE_posix__w32_h__ +#define INCLUDE_posix__w32_h__ + +#include "common.h" +#include "fnmatch.h" + +GIT_INLINE(int) p_link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new)) +{ + GIT_UNUSED_ARG(old) + GIT_UNUSED_ARG(new) + errno = ENOSYS; + return -1; +} + +GIT_INLINE(int) p_mkdir(const char *path, int GIT_UNUSED(mode)) +{ + GIT_UNUSED_ARG(mode) + return mkdir(path); +} + +extern int p_unlink(const char *path); +extern int p_lstat(const char *file_name, struct stat *buf); +extern int p_readlink(const char *link, char *target, size_t target_len); +extern int p_hide_directory__w32(const char *path); +extern char *p_realpath(const char *orig_path, char *buffer); +extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr); +extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4); +extern int p_mkstemp(char *tmp_path); + +#endif diff --git a/vendor/libgit2/src/win32/pthread.c b/vendor/libgit2/src/win32/pthread.c index 7e17b6bdf..41cf5b35b 100644 --- a/vendor/libgit2/src/win32/pthread.c +++ b/vendor/libgit2/src/win32/pthread.c @@ -33,7 +33,7 @@ int pthread_create(pthread_t *GIT_RESTRICT thread, { GIT_UNUSED_ARG(attr); *thread = (pthread_t) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL); - return *thread ? GIT_SUCCESS : GIT_EOSERR; + return *thread ? GIT_SUCCESS : git__throw(GIT_EOSERR, "Failed to create pthread"); } int pthread_join(pthread_t thread, void **value_ptr) diff --git a/vendor/libgit2/tests/NAMING b/vendor/libgit2/tests/NAMING index ab425e23e..c2da0163f 100644 --- a/vendor/libgit2/tests/NAMING +++ b/vendor/libgit2/tests/NAMING @@ -23,14 +23,30 @@ Categories 04__: Parsing and loading commit data -05__: To be described +05__: Revision walking -06__: To be described +06__: Index reading, writing and searching -07__: To be described +07__: Tests for the internal hashtable code -08__: To be described +08__: Tag reading and writing -09__: To be described +09__: Reading tree objects -10__: Symbolic, loose and packed references reading and writing. \ No newline at end of file +10__: Symbolic, loose and packed references reading and writing. + +11__: SQLite backend + +12__: Repository init and opening + +13__: Threads, empty as of now + +14__: Redis backend + +15__: Configuration parsing + +16__: Remotes + +17__: Buffers + +18__: File Status diff --git a/vendor/libgit2/tests/resources/.gitignore b/vendor/libgit2/tests/resources/.gitignore new file mode 100644 index 000000000..43a19cc9d --- /dev/null +++ b/vendor/libgit2/tests/resources/.gitignore @@ -0,0 +1 @@ +discover.git diff --git a/vendor/libgit2/tests/resources/config/.gitconfig b/vendor/libgit2/tests/resources/config/.gitconfig new file mode 100644 index 000000000..fa72bddfc --- /dev/null +++ b/vendor/libgit2/tests/resources/config/.gitconfig @@ -0,0 +1,3 @@ +[core] + repositoryformatversion = 5 + something = 2 \ No newline at end of file diff --git a/vendor/libgit2/tests/resources/config/config0 b/vendor/libgit2/tests/resources/config/config0 new file mode 100644 index 000000000..85235c501 --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config0 @@ -0,0 +1,7 @@ +# This is a test +; of different comments +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true \ No newline at end of file diff --git a/vendor/libgit2/tests/resources/config/config1 b/vendor/libgit2/tests/resources/config/config1 new file mode 100644 index 000000000..211dc9e7d --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config1 @@ -0,0 +1,5 @@ +# This one checks for case sensitivity +[this "that"] + other = true +[this "That"] + other = yes diff --git a/vendor/libgit2/tests/resources/config/config10 b/vendor/libgit2/tests/resources/config/config10 new file mode 100644 index 000000000..dde17911b --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config10 @@ -0,0 +1 @@ +[empty] diff --git a/vendor/libgit2/tests/resources/config/config2 b/vendor/libgit2/tests/resources/config/config2 new file mode 100644 index 000000000..60a389827 --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config2 @@ -0,0 +1,5 @@ +; This one tests for multiline values +[this "That"] + and = one one one \ +two two \ +three three \ No newline at end of file diff --git a/vendor/libgit2/tests/resources/config/config3 b/vendor/libgit2/tests/resources/config/config3 new file mode 100644 index 000000000..44a5e50ea --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config3 @@ -0,0 +1,3 @@ +# A [section.subsection] header is case-insensitive +[section.SuBsection] + var = hello diff --git a/vendor/libgit2/tests/resources/config/config4 b/vendor/libgit2/tests/resources/config/config4 new file mode 100644 index 000000000..741fa0ffd --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config4 @@ -0,0 +1,3 @@ +# A variable name on its own is valid +[some.section] + variable diff --git a/vendor/libgit2/tests/resources/config/config5 b/vendor/libgit2/tests/resources/config/config5 new file mode 100644 index 000000000..8ab60ccec --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config5 @@ -0,0 +1,9 @@ +# Test for number suffixes +[number] + simple = 1 + k = 1k + kk = 1K + m = 1m + mm = 1M + g = 1g + gg = 1G diff --git a/vendor/libgit2/tests/resources/config/config6 b/vendor/libgit2/tests/resources/config/config6 new file mode 100644 index 000000000..0f8f90ac9 --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config6 @@ -0,0 +1,5 @@ +[valid "subsection"] + something = true + +[something "else"] + something = false diff --git a/vendor/libgit2/tests/resources/config/config7 b/vendor/libgit2/tests/resources/config/config7 new file mode 100644 index 000000000..6af6fcf25 --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config7 @@ -0,0 +1,5 @@ +[valid "subsection"] + something = a +; we don't allow anything after closing " +[sec "subsec"x] + bleh = blah diff --git a/vendor/libgit2/tests/resources/config/config8 b/vendor/libgit2/tests/resources/config/config8 new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/libgit2/tests/resources/config/config9 b/vendor/libgit2/tests/resources/config/config9 new file mode 100644 index 000000000..34fdc07a5 --- /dev/null +++ b/vendor/libgit2/tests/resources/config/config9 @@ -0,0 +1,3 @@ +[core] + dummy2 = 42 + dummy = 1 diff --git a/vendor/libgit2/tests/resources/status/.gitted/COMMIT_EDITMSG b/vendor/libgit2/tests/resources/status/.gitted/COMMIT_EDITMSG new file mode 100644 index 000000000..ff887ba13 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/COMMIT_EDITMSG @@ -0,0 +1 @@ +add subdir diff --git a/vendor/libgit2/tests/resources/status/.gitted/HEAD b/vendor/libgit2/tests/resources/status/.gitted/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/vendor/libgit2/tests/resources/status/.gitted/ORIG_HEAD b/vendor/libgit2/tests/resources/status/.gitted/ORIG_HEAD new file mode 100644 index 000000000..c2805f422 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/ORIG_HEAD @@ -0,0 +1 @@ +0017bd4ab1ec30440b17bae1680cff124ab5f1f6 diff --git a/vendor/libgit2/tests/resources/status/.gitted/config b/vendor/libgit2/tests/resources/status/.gitted/config new file mode 100644 index 000000000..af107929f --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/vendor/libgit2/tests/resources/status/.gitted/description b/vendor/libgit2/tests/resources/status/.gitted/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/vendor/libgit2/tests/resources/status/.gitted/index b/vendor/libgit2/tests/resources/status/.gitted/index new file mode 100644 index 000000000..5c4b18841 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/index differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/info/exclude b/vendor/libgit2/tests/resources/status/.gitted/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/vendor/libgit2/tests/resources/status/.gitted/logs/HEAD b/vendor/libgit2/tests/resources/status/.gitted/logs/HEAD new file mode 100644 index 000000000..e876bd80b --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/logs/HEAD @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny 1308050070 -0400 commit (initial): initial +0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny 1308954538 -0400 commit: add subdir diff --git a/vendor/libgit2/tests/resources/status/.gitted/logs/refs/heads/master b/vendor/libgit2/tests/resources/status/.gitted/logs/refs/heads/master new file mode 100644 index 000000000..e876bd80b --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny 1308050070 -0400 commit (initial): initial +0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny 1308954538 -0400 commit: add subdir diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/vendor/libgit2/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 new file mode 100644 index 000000000..b256d95a3 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 @@ -0,0 +1,2 @@ +xA E]sй€fh)‰1]»ò +#SÀTºðö¶Wp÷ßK^~¨9§šÜ¡-"àC'Ø…)FvõbƒvÉ Þ"¶wŽŽ¼EÅk{Ö®ü©nRÊίÞû6ã#sšO¡æ 舄pDƒ¨6»6ù3W©¤–xV?¨Å9é \ No newline at end of file diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/vendor/libgit2/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 new file mode 100644 index 000000000..82e02cb0e Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/vendor/libgit2/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 new file mode 100644 index 000000000..e3cad2f02 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/vendor/libgit2/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 new file mode 100644 index 000000000..2d5e711b9 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/vendor/libgit2/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c new file mode 100644 index 000000000..7fca67be8 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/vendor/libgit2/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a new file mode 100644 index 000000000..5b47461e9 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/vendor/libgit2/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a new file mode 100644 index 000000000..615009ad0 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/vendor/libgit2/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 new file mode 100644 index 000000000..cdb7e961a Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/vendor/libgit2/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f new file mode 100644 index 000000000..a72dff646 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/vendor/libgit2/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 new file mode 100644 index 000000000..72807f3d0 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/vendor/libgit2/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 new file mode 100644 index 000000000..3665a8f7c Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/vendor/libgit2/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 new file mode 100644 index 000000000..08e6fd246 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/vendor/libgit2/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea new file mode 100644 index 000000000..8f3fa89e5 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/vendor/libgit2/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 new file mode 100644 index 000000000..bb732b08e Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/vendor/libgit2/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 new file mode 100644 index 000000000..7a96618ff Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/vendor/libgit2/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 new file mode 100644 index 000000000..20a3c497e Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/vendor/libgit2/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e new file mode 100644 index 000000000..a1789c9a6 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/vendor/libgit2/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 new file mode 100644 index 000000000..cc1f377b3 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/vendor/libgit2/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b new file mode 100644 index 000000000..c47298347 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/vendor/libgit2/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 new file mode 100644 index 000000000..a4669ccbb Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/vendor/libgit2/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 new file mode 100644 index 000000000..3e3c03c96 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/vendor/libgit2/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca new file mode 100644 index 000000000..1266d3eac Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/vendor/libgit2/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd new file mode 100644 index 000000000..8fa8c1707 Binary files /dev/null and b/vendor/libgit2/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd differ diff --git a/vendor/libgit2/tests/resources/status/.gitted/refs/heads/master b/vendor/libgit2/tests/resources/status/.gitted/refs/heads/master new file mode 100644 index 000000000..b46871fd6 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/.gitted/refs/heads/master @@ -0,0 +1 @@ +735b6a258cd196a8f7c9428419b02c1dca93fd75 diff --git a/vendor/libgit2/tests/resources/status/current_file b/vendor/libgit2/tests/resources/status/current_file new file mode 100644 index 000000000..a0de7e0ac --- /dev/null +++ b/vendor/libgit2/tests/resources/status/current_file @@ -0,0 +1 @@ +current_file diff --git a/vendor/libgit2/tests/resources/status/modified_file b/vendor/libgit2/tests/resources/status/modified_file new file mode 100644 index 000000000..0a5396305 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/modified_file @@ -0,0 +1,2 @@ +modified_file +modified_file diff --git a/vendor/libgit2/tests/resources/status/new_file b/vendor/libgit2/tests/resources/status/new_file new file mode 100644 index 000000000..d4fa8600b --- /dev/null +++ b/vendor/libgit2/tests/resources/status/new_file @@ -0,0 +1 @@ +new_file diff --git a/vendor/libgit2/tests/resources/status/staged_changes b/vendor/libgit2/tests/resources/status/staged_changes new file mode 100644 index 000000000..55d316c9b --- /dev/null +++ b/vendor/libgit2/tests/resources/status/staged_changes @@ -0,0 +1,2 @@ +staged_changes +staged_changes diff --git a/vendor/libgit2/tests/resources/status/staged_changes_modified_file b/vendor/libgit2/tests/resources/status/staged_changes_modified_file new file mode 100644 index 000000000..011c3440d --- /dev/null +++ b/vendor/libgit2/tests/resources/status/staged_changes_modified_file @@ -0,0 +1,3 @@ +staged_changes_modified_file +staged_changes_modified_file +staged_changes_modified_file diff --git a/vendor/libgit2/tests/resources/status/staged_delete_modified_file b/vendor/libgit2/tests/resources/status/staged_delete_modified_file new file mode 100644 index 000000000..dabc8af9b --- /dev/null +++ b/vendor/libgit2/tests/resources/status/staged_delete_modified_file @@ -0,0 +1 @@ +staged_delete_modified_file diff --git a/vendor/libgit2/tests/resources/status/staged_new_file b/vendor/libgit2/tests/resources/status/staged_new_file new file mode 100644 index 000000000..529a16e8e --- /dev/null +++ b/vendor/libgit2/tests/resources/status/staged_new_file @@ -0,0 +1 @@ +staged_new_file diff --git a/vendor/libgit2/tests/resources/status/staged_new_file_modified_file b/vendor/libgit2/tests/resources/status/staged_new_file_modified_file new file mode 100644 index 000000000..8b090c06d --- /dev/null +++ b/vendor/libgit2/tests/resources/status/staged_new_file_modified_file @@ -0,0 +1,2 @@ +staged_new_file_modified_file +staged_new_file_modified_file diff --git a/vendor/libgit2/tests/resources/status/subdir/current_file b/vendor/libgit2/tests/resources/status/subdir/current_file new file mode 100644 index 000000000..53ace0d1c --- /dev/null +++ b/vendor/libgit2/tests/resources/status/subdir/current_file @@ -0,0 +1 @@ +subdir/current_file diff --git a/vendor/libgit2/tests/resources/status/subdir/modified_file b/vendor/libgit2/tests/resources/status/subdir/modified_file new file mode 100644 index 000000000..57274b75e --- /dev/null +++ b/vendor/libgit2/tests/resources/status/subdir/modified_file @@ -0,0 +1,2 @@ +subdir/modified_file +subdir/modified_file diff --git a/vendor/libgit2/tests/resources/status/subdir/new_file b/vendor/libgit2/tests/resources/status/subdir/new_file new file mode 100644 index 000000000..80a86a693 --- /dev/null +++ b/vendor/libgit2/tests/resources/status/subdir/new_file @@ -0,0 +1 @@ +subdir/new_file diff --git a/vendor/libgit2/tests/resources/testrepo.git/config b/vendor/libgit2/tests/resources/testrepo.git/config new file mode 100644 index 000000000..1a5aacdfa --- /dev/null +++ b/vendor/libgit2/tests/resources/testrepo.git/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true + logallrefupdates = true +[remote "test"] + url = git://github.com/libgit2/libgit2 + fetch = +refs/heads/*:refs/remotes/test/* diff --git a/vendor/libgit2/tests/t00-core.c b/vendor/libgit2/tests/t00-core.c index ab9a683a7..6d63d1ce1 100644 --- a/vendor/libgit2/tests/t00-core.c +++ b/vendor/libgit2/tests/t00-core.c @@ -26,6 +26,7 @@ #include "vector.h" #include "fileops.h" +#include "filebuf.h" BEGIN_TEST(string0, "compare prefixes") must_be_true(git__prefixcmp("", "") == 0); @@ -72,14 +73,44 @@ BEGIN_TEST(vector1, "don't read past array bounds on remove()") git_vector_free(&x); END_TEST +static int test_cmp(const void *a, const void *b) +{ + return *(const int *)a - *(const int *)b; +} + +BEGIN_TEST(vector2, "remove duplicates") + git_vector x; + int *ptrs[2]; + + ptrs[0] = git__malloc(sizeof(int)); + ptrs[1] = git__malloc(sizeof(int)); + + *ptrs[0] = 2; + *ptrs[1] = 1; + + must_pass(git_vector_init(&x, 5, test_cmp)); + must_pass(git_vector_insert(&x, ptrs[0])); + must_pass(git_vector_insert(&x, ptrs[1])); + must_pass(git_vector_insert(&x, ptrs[1])); + must_pass(git_vector_insert(&x, ptrs[0])); + must_pass(git_vector_insert(&x, ptrs[1])); + must_be_true(x.length == 5); + git_vector_uniq(&x); + must_be_true(x.length == 2); + git_vector_free(&x); + + free(ptrs[0]); + free(ptrs[1]); +END_TEST + BEGIN_TEST(path0, "get the dirname of a path") char dir[64], *dir2; #define DIRNAME_TEST(A, B) { \ - must_be_true(git__dirname_r(dir, sizeof(dir), A) >= 0); \ + must_be_true(git_path_dirname_r(dir, sizeof(dir), A) >= 0); \ must_be_true(strcmp(dir, B) == 0); \ - must_be_true((dir2 = git__dirname(A)) != NULL); \ + must_be_true((dir2 = git_path_dirname(A)) != NULL); \ must_be_true(strcmp(dir2, B) == 0); \ free(dir2); \ } @@ -106,9 +137,9 @@ BEGIN_TEST(path1, "get the base name of a path") char base[64], *base2; #define BASENAME_TEST(A, B) { \ - must_be_true(git__basename_r(base, sizeof(base), A) >= 0); \ + must_be_true(git_path_basename_r(base, sizeof(base), A) >= 0); \ must_be_true(strcmp(base, B) == 0); \ - must_be_true((base2 = git__basename(A)) != NULL); \ + must_be_true((base2 = git_path_basename(A)) != NULL); \ must_be_true(strcmp(base2, B) == 0); \ free(base2); \ } @@ -131,7 +162,7 @@ BEGIN_TEST(path2, "get the latest component in a path") const char *dir; #define TOPDIR_TEST(A, B) { \ - must_be_true((dir = git__topdir(A)) != NULL); \ + must_be_true((dir = git_path_topdir(A)) != NULL); \ must_be_true(strcmp(dir, B) == 0); \ } @@ -143,218 +174,18 @@ BEGIN_TEST(path2, "get the latest component in a path") TOPDIR_TEST("/", "/"); TOPDIR_TEST("a/", "a/"); - must_be_true(git__topdir("/usr/.git") == NULL); - must_be_true(git__topdir(".") == NULL); - must_be_true(git__topdir("") == NULL); - must_be_true(git__topdir("a") == NULL); + must_be_true(git_path_topdir("/usr/.git") == NULL); + must_be_true(git_path_topdir(".") == NULL); + must_be_true(git_path_topdir("") == NULL); + must_be_true(git_path_topdir("a") == NULL); #undef TOPDIR_TEST END_TEST -typedef int (normalize_path)(char *, size_t, const char *); - -/* Assert flags */ -#define CWD_AS_PREFIX 1 -#define PATH_AS_SUFFIX 2 -#define ROOTED_PATH 4 - -static int ensure_normalized(const char *input_path, const char *expected_path, normalize_path normalizer, int assert_flags) -{ - int error = GIT_SUCCESS; - char buffer_out[GIT_PATH_MAX]; - char current_workdir[GIT_PATH_MAX]; - - error = gitfo_getcwd(current_workdir, sizeof(current_workdir)); - if (error < GIT_SUCCESS) - return error; - - error = normalizer(buffer_out, sizeof(buffer_out), input_path); - if (error < GIT_SUCCESS) - return error; - - if (expected_path == NULL) - return error; - - if ((assert_flags & PATH_AS_SUFFIX) != 0) - if (git__suffixcmp(buffer_out, expected_path)) - return GIT_ERROR; - - if ((assert_flags & CWD_AS_PREFIX) != 0) - if (git__prefixcmp(buffer_out, current_workdir)) - return GIT_ERROR; - - if ((assert_flags & ROOTED_PATH) != 0) { - error = strcmp(expected_path, buffer_out); - } - - return error; -} - -static int ensure_dir_path_normalized(const char *input_path, const char *expected_path, int assert_flags) -{ - return ensure_normalized(input_path, expected_path, gitfo_prettify_dir_path, assert_flags); -} - -static int ensure_file_path_normalized(const char *input_path, const char *expected_path, int assert_flags) -{ - return ensure_normalized(input_path, expected_path, gitfo_prettify_file_path, assert_flags); -} - -BEGIN_TEST(path3, "prettify and validate a path to a file") - must_pass(ensure_file_path_normalized("a", "a", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_file_path_normalized("./testrepo.git", "testrepo.git", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_file_path_normalized("./.git", ".git", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_file_path_normalized("./git.", "git.", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_fail(ensure_file_path_normalized("git./", NULL, 0)); - must_fail(ensure_file_path_normalized("", NULL, 0)); - must_fail(ensure_file_path_normalized(".", NULL, 0)); - must_fail(ensure_file_path_normalized("./", NULL, 0)); - must_fail(ensure_file_path_normalized("./.", NULL, 0)); - must_fail(ensure_file_path_normalized("./..", NULL, 0)); - must_fail(ensure_file_path_normalized("../.", NULL, 0)); - must_fail(ensure_file_path_normalized("./.././/", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/../..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/..///..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub///../..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub///..///..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/../../..", NULL, 0)); - must_pass(ensure_file_path_normalized("dir", "dir", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_fail(ensure_file_path_normalized("dir//", NULL, 0)); - must_pass(ensure_file_path_normalized("./dir", "dir", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_fail(ensure_file_path_normalized("dir/.", NULL, 0)); - must_fail(ensure_file_path_normalized("dir///./", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/..", NULL, 0)); - must_fail(ensure_file_path_normalized("dir//sub/..",NULL, 0)); - must_fail(ensure_file_path_normalized("dir//sub/../", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/../", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/sub/../.", NULL, 0)); - must_fail(ensure_file_path_normalized("dir/s1/../s2/", NULL, 0)); - must_fail(ensure_file_path_normalized("d1/s1///s2/..//../s3/", NULL, 0)); - must_pass(ensure_file_path_normalized("d1/s1//../s2/../../d2", "d2", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_fail(ensure_file_path_normalized("dir/sub/../", NULL, 0)); - must_pass(ensure_file_path_normalized("../a/../b/c/d/../../e", "b/e", PATH_AS_SUFFIX)); - must_fail(ensure_file_path_normalized("....", NULL, 0)); - must_fail(ensure_file_path_normalized("...", NULL, 0)); - must_fail(ensure_file_path_normalized("./...", NULL, 0)); - must_fail(ensure_file_path_normalized("d1/...", NULL, 0)); - must_fail(ensure_file_path_normalized("d1/.../", NULL, 0)); - must_fail(ensure_file_path_normalized("d1/.../d2", NULL, 0)); - - must_pass(ensure_file_path_normalized("/a", "/a", ROOTED_PATH)); - must_pass(ensure_file_path_normalized("/./testrepo.git", "/testrepo.git", ROOTED_PATH)); - must_pass(ensure_file_path_normalized("/./.git", "/.git", ROOTED_PATH)); - must_pass(ensure_file_path_normalized("/./git.", "/git.", ROOTED_PATH)); - must_fail(ensure_file_path_normalized("/git./", NULL, 0)); - must_fail(ensure_file_path_normalized("/", NULL, 0)); - must_fail(ensure_file_path_normalized("/.", NULL, 0)); - must_fail(ensure_file_path_normalized("/./", NULL, 0)); - must_fail(ensure_file_path_normalized("/./.", NULL, 0)); - must_fail(ensure_file_path_normalized("/./..", NULL, 0)); - must_fail(ensure_file_path_normalized("/../.", NULL, 0)); - must_fail(ensure_file_path_normalized("/./.././/", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/../..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/..///..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub///../..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub///..///..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/../../..", NULL, 0)); - must_pass(ensure_file_path_normalized("/dir", "/dir", 0)); - must_fail(ensure_file_path_normalized("/dir//", NULL, 0)); - must_pass(ensure_file_path_normalized("/./dir", "/dir", 0)); - must_fail(ensure_file_path_normalized("/dir/.", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir///./", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/..", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir//sub/..",NULL, 0)); - must_fail(ensure_file_path_normalized("/dir//sub/../", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/../", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/sub/../.", NULL, 0)); - must_fail(ensure_file_path_normalized("/dir/s1/../s2/", NULL, 0)); - must_fail(ensure_file_path_normalized("/d1/s1///s2/..//../s3/", NULL, 0)); - must_pass(ensure_file_path_normalized("/d1/s1//../s2/../../d2", "/d2", 0)); - must_fail(ensure_file_path_normalized("/dir/sub/../", NULL, 0)); - must_fail(ensure_file_path_normalized("/....", NULL, 0)); - must_fail(ensure_file_path_normalized("/...", NULL, 0)); - must_fail(ensure_file_path_normalized("/./...", NULL, 0)); - must_fail(ensure_file_path_normalized("/d1/...", NULL, 0)); - must_fail(ensure_file_path_normalized("/d1/.../", NULL, 0)); - must_fail(ensure_file_path_normalized("/d1/.../d2", NULL, 0)); -END_TEST - -BEGIN_TEST(path4, "validate and prettify a path to a folder") - must_pass(ensure_dir_path_normalized("./testrepo.git", "testrepo.git/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("./.git", ".git/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("./git.", "git./", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("git./", "git./", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized(".", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("./", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("./.", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/../..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/..///..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub///../..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub///..///..", "", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir//", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("./dir", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/.", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir///./", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/..", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir//sub/..", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir//sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/../.", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/s1/../s2/", "dir/s2/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("d1/s1///s2/..//../s3/", "d1/s3/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("d1/s1//../s2/../../d2", "d2/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("dir/sub/../", "dir/", CWD_AS_PREFIX | PATH_AS_SUFFIX)); - must_pass(ensure_dir_path_normalized("../a/../b/c/d/../../e", "b/e/", PATH_AS_SUFFIX)); - must_fail(ensure_dir_path_normalized("....", NULL, 0)); - must_fail(ensure_dir_path_normalized("...", NULL, 0)); - must_fail(ensure_dir_path_normalized("./...", NULL, 0)); - must_fail(ensure_dir_path_normalized("d1/...", NULL, 0)); - must_fail(ensure_dir_path_normalized("d1/.../", NULL, 0)); - must_fail(ensure_dir_path_normalized("d1/.../d2", NULL, 0)); - - must_pass(ensure_dir_path_normalized("/./testrepo.git", "/testrepo.git/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/./.git", "/.git/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/./git.", "/git./", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/git./", "/git./", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/", "/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("//", "/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("///", "/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/.", "/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/./", "/", ROOTED_PATH)); - must_fail(ensure_dir_path_normalized("/./..", NULL, 0)); - must_fail(ensure_dir_path_normalized("/../.", NULL, 0)); - must_fail(ensure_dir_path_normalized("/./.././/", NULL, 0)); - must_pass(ensure_dir_path_normalized("/dir/..", "/", 0)); - must_pass(ensure_dir_path_normalized("/dir/sub/../..", "/", 0)); - must_fail(ensure_dir_path_normalized("/dir/sub/../../..", NULL, 0)); - must_pass(ensure_dir_path_normalized("/dir", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir//", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/./dir", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir/.", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir///./", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir//sub/..", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir/sub/../", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("//dir/sub/../.", "/dir/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/dir/s1/../s2/", "/dir/s2/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/d1/s1///s2/..//../s3/", "/d1/s3/", ROOTED_PATH)); - must_pass(ensure_dir_path_normalized("/d1/s1//../s2/../../d2", "/d2/", ROOTED_PATH)); - must_fail(ensure_dir_path_normalized("/....", NULL, 0)); - must_fail(ensure_dir_path_normalized("/...", NULL, 0)); - must_fail(ensure_dir_path_normalized("/./...", NULL, 0)); - must_fail(ensure_dir_path_normalized("/d1/...", NULL, 0)); - must_fail(ensure_dir_path_normalized("/d1/.../", NULL, 0)); - must_fail(ensure_dir_path_normalized("/d1/.../d2", NULL, 0)); -END_TEST - static int ensure_joinpath(const char *path_a, const char *path_b, const char *expected_path) { char joined_path[GIT_PATH_MAX]; - git__joinpath(joined_path, path_a, path_b); + git_path_join(joined_path, path_a, path_b); return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR; } @@ -376,7 +207,7 @@ END_TEST static int ensure_joinpath_n(const char *path_a, const char *path_b, const char *path_c, const char *path_d, const char *expected_path) { char joined_path[GIT_PATH_MAX]; - git__joinpath_n(joined_path, 4, path_a, path_b, path_c, path_d); + git_path_join_n(joined_path, 4, path_a, path_b, path_c, path_d); return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR; } @@ -389,37 +220,6 @@ BEGIN_TEST(path6, "properly join path components for more than one path") must_pass(ensure_joinpath_n("a", "b", "", "/c/d", "a/b/c/d")); END_TEST -static int count_number_of_path_segments(const char *path) -{ - int number = 0; - char *current = (char *)path; - - while (*current) - { - if (*current++ == '/') - number++; - } - - assert (number > 0); - - return --number; -} - -BEGIN_TEST(path7, "prevent a path which escapes the root directory from being prettified") - char current_workdir[GIT_PATH_MAX]; - char prettified[GIT_PATH_MAX]; - int i = 0, number_to_escape; - - must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir))); - - number_to_escape = count_number_of_path_segments(current_workdir); - - for (i = 0; i < number_to_escape + 1; i++) - git__joinpath(current_workdir, current_workdir, "../"); - - must_fail(gitfo_prettify_dir_path(prettified, sizeof(prettified), current_workdir)); -END_TEST - typedef struct name_data { int count; /* return count */ char *name; /* filename */ @@ -450,24 +250,24 @@ static int setup(walk_data *d) { name_data *n; - if (gitfo_mkdir(top_dir, 0755) < 0) + if (p_mkdir(top_dir, 0755) < 0) return error("can't mkdir(\"%s\")", top_dir); - if (gitfo_chdir(top_dir) < 0) + if (p_chdir(top_dir) < 0) return error("can't chdir(\"%s\")", top_dir); if (strcmp(d->sub, ".") != 0) - if (gitfo_mkdir(d->sub, 0755) < 0) + if (p_mkdir(d->sub, 0755) < 0) return error("can't mkdir(\"%s\")", d->sub); strcpy(path_buffer, d->sub); state_loc = d; for (n = d->names; n->name; n++) { - git_file fd = gitfo_creat(n->name, 0600); + git_file fd = p_creat(n->name, 0600); if (fd < 0) return GIT_ERROR; - gitfo_close(fd); + p_close(fd); n->count = 0; } @@ -479,18 +279,18 @@ static int knockdown(walk_data *d) name_data *n; for (n = d->names; n->name; n++) { - if (gitfo_unlink(n->name) < 0) + if (p_unlink(n->name) < 0) return error("can't unlink(\"%s\")", n->name); } if (strcmp(d->sub, ".") != 0) - if (gitfo_rmdir(d->sub) < 0) + if (p_rmdir(d->sub) < 0) return error("can't rmdir(\"%s\")", d->sub); - if (gitfo_chdir("..") < 0) + if (p_chdir("..") < 0) return error("can't chdir(\"..\")"); - if (gitfo_rmdir(top_dir) < 0) + if (p_rmdir(top_dir) < 0) return error("can't rmdir(\"%s\")", top_dir); return 0; @@ -545,7 +345,7 @@ BEGIN_TEST(dirent0, "make sure that the '.' folder is not traversed") must_pass(setup(&dot)); - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), one_entry, &dot)); @@ -570,7 +370,7 @@ BEGIN_TEST(dirent1, "traverse a subfolder") must_pass(setup(&sub)); - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), one_entry, &sub)); @@ -589,7 +389,7 @@ BEGIN_TEST(dirent2, "traverse a slash-terminated subfolder") must_pass(setup(&sub_slash)); - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), one_entry, &sub_slash)); @@ -618,7 +418,7 @@ BEGIN_TEST(dirent3, "make sure that empty folders are not traversed") must_pass(setup(&empty)); - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), one_entry, &empty)); @@ -626,7 +426,7 @@ BEGIN_TEST(dirent3, "make sure that empty folders are not traversed") must_pass(check_counts(&empty)); /* make sure callback not called */ - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), dont_call_me, &empty)); @@ -651,7 +451,7 @@ BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traver must_pass(setup(&odd)); - must_pass(gitfo_dirent(path_buffer, + must_pass(git_futils_direach(path_buffer, sizeof(path_buffer), one_entry, &odd)); @@ -661,6 +461,99 @@ BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traver must_pass(knockdown(&odd)); END_TEST +BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock") + git_filebuf file; + int fd; + char test[] = "test", testlock[] = "test.lock"; + + fd = p_creat(testlock, 0744); + must_pass(fd); + must_pass(p_close(fd)); + must_fail(git_filebuf_open(&file, test, 0)); + must_pass(git_futils_exists(testlock)); + must_pass(p_unlink(testlock)); +END_TEST + +BEGIN_TEST(filebuf1, "make sure GIT_FILEBUF_APPEND works as expected") + git_filebuf file; + int fd; + char test[] = "test"; + + fd = p_creat(test, 0644); + must_pass(fd); + must_pass(p_write(fd, "libgit2 rocks\n", 14)); + must_pass(p_close(fd)); + + must_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND)); + must_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + must_pass(git_filebuf_commit(&file)); + + must_pass(p_unlink(test)); +END_TEST + +BEGIN_TEST(filebuf2, "make sure git_filebuf_write writes large buffer correctly") + git_filebuf file; + char test[] = "test"; + unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */ + + memset(buf, 0xfe, sizeof(buf)); + must_pass(git_filebuf_open(&file, test, 0)); + must_pass(git_filebuf_write(&file, buf, sizeof(buf))); + must_pass(git_filebuf_commit(&file)); + + must_pass(p_unlink(test)); +END_TEST + +static char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test"; + +static int setup_empty_tmp_dir() +{ + char path[GIT_PATH_MAX]; + + if (p_mkdir(empty_tmp_dir, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one"); + if (p_mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_one"); + if (p_mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_two"); + if (p_mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/one/two_two/three"); + if (p_mkdir(path, 0755)) + return -1; + + git_path_join(path, empty_tmp_dir, "/two"); + if (p_mkdir(path, 0755)) + return -1; + + return 0; +} + +BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively") + must_pass(setup_empty_tmp_dir()); + must_pass(git_futils_rmdir_r(empty_tmp_dir, 0)); +END_TEST + +BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively") + char file[GIT_PATH_MAX]; + int fd; + + must_pass(setup_empty_tmp_dir()); + git_path_join(file, empty_tmp_dir, "/two/file.txt"); + fd = p_creat(file, 0755); + must_pass(fd); + must_pass(p_close(fd)); + must_fail(git_futils_rmdir_r(empty_tmp_dir, 0)); + must_pass(p_unlink(file)); + must_pass(git_futils_rmdir_r(empty_tmp_dir, 0)); +END_TEST BEGIN_SUITE(core) ADD_TEST(string0); @@ -668,19 +561,24 @@ BEGIN_SUITE(core) ADD_TEST(vector0); ADD_TEST(vector1); + ADD_TEST(vector2); ADD_TEST(path0); ADD_TEST(path1); ADD_TEST(path2); - ADD_TEST(path3); - ADD_TEST(path4); ADD_TEST(path5); ADD_TEST(path6); - ADD_TEST(path7); ADD_TEST(dirent0); ADD_TEST(dirent1); ADD_TEST(dirent2); ADD_TEST(dirent3); ADD_TEST(dirent4); + + ADD_TEST(filebuf0); + ADD_TEST(filebuf1); + ADD_TEST(filebuf2); + + ADD_TEST(rmdir0); + ADD_TEST(rmdir1); END_SUITE diff --git a/vendor/libgit2/tests/t01-rawobj.c b/vendor/libgit2/tests/t01-rawobj.c index 5db9a79fc..255208532 100644 --- a/vendor/libgit2/tests/t01-rawobj.c +++ b/vendor/libgit2/tests/t01-rawobj.c @@ -44,12 +44,12 @@ END_TEST BEGIN_TEST(oid1, "fail when parsing an empty string as oid") git_oid out; - must_fail(git_oid_mkstr(&out, "")); + must_fail(git_oid_fromstr(&out, "")); END_TEST BEGIN_TEST(oid2, "fail when parsing an invalid string as oid") git_oid out; - must_fail(git_oid_mkstr(&out, "moo")); + must_fail(git_oid_fromstr(&out, "moo")); END_TEST static int from_hex(unsigned int i) @@ -79,17 +79,17 @@ BEGIN_TEST(oid3, "find all invalid characters when parsing an oid") if (from_hex(i) >= 0) { exp[19] = (unsigned char)(from_hex(i) << 4); - must_pass(git_oid_mkstr(&out, in)); + must_pass(git_oid_fromstr(&out, in)); must_be_true(memcmp(out.id, exp, sizeof(out.id)) == 0); } else { - must_fail(git_oid_mkstr(&out, in)); + must_fail(git_oid_fromstr(&out, in)); } } END_TEST BEGIN_TEST(oid4, "fail when parsing an invalid oid string") git_oid out; - must_fail(git_oid_mkstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez")); + must_fail(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez")); END_TEST BEGIN_TEST(oid5, "succeed when parsing a valid oid string") @@ -101,10 +101,10 @@ BEGIN_TEST(oid5, "succeed when parsing a valid oid string") 0xa8, 0xbd, 0x74, 0xf5, 0xe0, }; - must_pass(git_oid_mkstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0")); + must_pass(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0")); must_pass(memcmp(out.id, exp, sizeof(out.id))); - must_pass(git_oid_mkstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0")); + must_pass(git_oid_fromstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0")); must_pass(memcmp(out.id, exp, sizeof(out.id))); END_TEST @@ -117,7 +117,7 @@ BEGIN_TEST(oid6, "build a valid oid from raw bytes") 0xa8, 0xbd, 0x74, 0xf5, 0xe0, }; - git_oid_mkraw(&out, exp); + git_oid_fromraw(&out, exp); must_pass(memcmp(out.id, exp, sizeof(out.id))); END_TEST @@ -131,7 +131,7 @@ BEGIN_TEST(oid7, "properly copy an oid to another") }; memset(&b, 0, sizeof(b)); - git_oid_mkraw(&a, exp); + git_oid_fromraw(&a, exp); git_oid_cpy(&b, &a); must_pass(memcmp(a.id, exp, sizeof(a.id))); END_TEST @@ -151,8 +151,8 @@ BEGIN_TEST(oid8, "compare two oids (lesser than)") 0xa8, 0xbd, 0x74, 0xf5, 0xf0, }; - git_oid_mkraw(&a, a_in); - git_oid_mkraw(&b, b_in); + git_oid_fromraw(&a, a_in); + git_oid_fromraw(&b, b_in); must_be_true(git_oid_cmp(&a, &b) < 0); END_TEST @@ -165,8 +165,8 @@ BEGIN_TEST(oid9, "compare two oids (equal)") 0xa8, 0xbd, 0x74, 0xf5, 0xe0, }; - git_oid_mkraw(&a, a_in); - git_oid_mkraw(&b, a_in); + git_oid_fromraw(&a, a_in); + git_oid_fromraw(&b, a_in); must_be_true(git_oid_cmp(&a, &b) == 0); END_TEST @@ -185,8 +185,8 @@ BEGIN_TEST(oid10, "compare two oids (greater than)") 0xa8, 0xbd, 0x74, 0xf5, 0xd0, }; - git_oid_mkraw(&a, a_in); - git_oid_mkraw(&b, b_in); + git_oid_fromraw(&a, a_in); + git_oid_fromraw(&b, b_in); must_be_true(git_oid_cmp(&a, &b) > 0); END_TEST @@ -195,7 +195,7 @@ BEGIN_TEST(oid11, "compare formated oids") git_oid in; char out[GIT_OID_HEXSZ + 1]; - must_pass(git_oid_mkstr(&in, exp)); + must_pass(git_oid_fromstr(&in, exp)); /* Format doesn't touch the last byte */ out[GIT_OID_HEXSZ] = 'Z'; @@ -204,7 +204,7 @@ BEGIN_TEST(oid11, "compare formated oids") /* Format produced the right result */ out[GIT_OID_HEXSZ] = '\0'; - must_pass(strcmp(exp, out)); + must_be_true(strcmp(exp, out) == 0); END_TEST BEGIN_TEST(oid12, "compare oids (allocate + format)") @@ -212,11 +212,11 @@ BEGIN_TEST(oid12, "compare oids (allocate + format)") git_oid in; char *out; - must_pass(git_oid_mkstr(&in, exp)); + must_pass(git_oid_fromstr(&in, exp)); out = git_oid_allocfmt(&in); must_be_true(out); - must_pass(strcmp(exp, out)); + must_be_true(strcmp(exp, out) == 0); free(out); END_TEST @@ -226,7 +226,7 @@ BEGIN_TEST(oid13, "compare oids (path format)") git_oid in; char out[GIT_OID_HEXSZ + 2]; - must_pass(git_oid_mkstr(&in, exp1)); + must_pass(git_oid_fromstr(&in, exp1)); /* Format doesn't touch the last byte */ out[GIT_OID_HEXSZ + 1] = 'Z'; @@ -235,7 +235,7 @@ BEGIN_TEST(oid13, "compare oids (path format)") /* Format produced the right result */ out[GIT_OID_HEXSZ + 1] = '\0'; - must_pass(strcmp(exp2, out)); + must_be_true(strcmp(exp2, out) == 0); END_TEST BEGIN_TEST(oid14, "convert raw oid to string") @@ -245,7 +245,7 @@ BEGIN_TEST(oid14, "convert raw oid to string") char *str; int i; - must_pass(git_oid_mkstr(&in, exp)); + must_pass(git_oid_fromstr(&in, exp)); /* NULL buffer pointer, returns static empty string */ str = git_oid_to_string(NULL, sizeof(out), &in); @@ -279,7 +279,7 @@ BEGIN_TEST(oid14, "convert raw oid to string") /* returns out as hex formatted c-string */ str = git_oid_to_string(out, sizeof(out), &in); must_be_true(str && str == out && *(str+GIT_OID_HEXSZ) == '\0'); - must_pass(strcmp(exp, out)); + must_be_true(strcmp(exp, out) == 0); END_TEST BEGIN_TEST(oid15, "convert raw oid to string (big)") @@ -288,7 +288,7 @@ BEGIN_TEST(oid15, "convert raw oid to string (big)") char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */ char *str; - must_pass(git_oid_mkstr(&in, exp)); + must_pass(git_oid_fromstr(&in, exp)); /* place some tail material */ big[GIT_OID_HEXSZ+0] = 'W'; /* should be '\0' afterwards */ @@ -299,7 +299,7 @@ BEGIN_TEST(oid15, "convert raw oid to string (big)") /* returns big as hex formatted c-string */ str = git_oid_to_string(big, sizeof(big), &in); must_be_true(str && str == big && *(str+GIT_OID_HEXSZ) == '\0'); - must_pass(strcmp(exp, big)); + must_be_true(strcmp(exp, big) == 0); /* check tail material is untouched */ must_be_true(str && str == big && *(str+GIT_OID_HEXSZ+1) == 'X'); @@ -412,14 +412,14 @@ BEGIN_TEST(hash0, "normal hash by blocks") /* should already be init'd */ git_hash_update(ctx, hello_text, strlen(hello_text)); git_hash_final(&id2, ctx); - must_pass(git_oid_mkstr(&id1, hello_id)); + must_pass(git_oid_fromstr(&id1, hello_id)); must_be_true(git_oid_cmp(&id1, &id2) == 0); /* reinit should permit reuse */ git_hash_init(ctx); git_hash_update(ctx, bye_text, strlen(bye_text)); git_hash_final(&id2, ctx); - must_pass(git_oid_mkstr(&id1, bye_id)); + must_pass(git_oid_fromstr(&id1, bye_id)); must_be_true(git_oid_cmp(&id1, &id2) == 0); git_hash_free_ctx(ctx); @@ -428,7 +428,7 @@ END_TEST BEGIN_TEST(hash1, "hash whole buffer in a single call") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, hello_id)); + must_pass(git_oid_fromstr(&id1, hello_id)); git_hash_buf(&id2, hello_text, strlen(hello_text)); @@ -439,7 +439,7 @@ BEGIN_TEST(hash2, "hash a vector") git_oid id1, id2; git_buf_vec vec[2]; - must_pass(git_oid_mkstr(&id1, hello_id)); + must_pass(git_oid_fromstr(&id1, hello_id)); vec[0].data = hello_text; vec[0].len = 4; @@ -500,7 +500,7 @@ END_TEST BEGIN_TEST(objhash0, "hash junk data") git_oid id, id_zero; - must_pass(git_oid_mkstr(&id_zero, zero_id)); + must_pass(git_oid_fromstr(&id_zero, zero_id)); /* invalid types: */ junk_obj.data = some_data; @@ -531,7 +531,7 @@ END_TEST BEGIN_TEST(objhash1, "hash a commit object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, commit_id)); + must_pass(git_oid_fromstr(&id1, commit_id)); must_pass(hash_object(&id2, &commit_obj)); @@ -541,7 +541,7 @@ END_TEST BEGIN_TEST(objhash2, "hash a tree object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, tree_id)); + must_pass(git_oid_fromstr(&id1, tree_id)); must_pass(hash_object(&id2, &tree_obj)); @@ -551,7 +551,7 @@ END_TEST BEGIN_TEST(objhash3, "hash a tag object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, tag_id)); + must_pass(git_oid_fromstr(&id1, tag_id)); must_pass(hash_object(&id2, &tag_obj)); @@ -561,7 +561,7 @@ END_TEST BEGIN_TEST(objhash4, "hash a zero-length object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, zero_id)); + must_pass(git_oid_fromstr(&id1, zero_id)); must_pass(hash_object(&id2, &zero_obj)); @@ -571,7 +571,7 @@ END_TEST BEGIN_TEST(objhash5, "hash an one-byte long object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, one_id)); + must_pass(git_oid_fromstr(&id1, one_id)); must_pass(hash_object(&id2, &one_obj)); @@ -581,7 +581,7 @@ END_TEST BEGIN_TEST(objhash6, "hash a two-byte long object") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, two_id)); + must_pass(git_oid_fromstr(&id1, two_id)); must_pass(hash_object(&id2, &two_obj)); @@ -591,7 +591,7 @@ END_TEST BEGIN_TEST(objhash7, "hash an object several bytes long") git_oid id1, id2; - must_pass(git_oid_mkstr(&id1, some_id)); + must_pass(git_oid_fromstr(&id1, some_id)); must_pass(hash_object(&id2, &some_obj)); diff --git a/vendor/libgit2/tests/t02-objread.c b/vendor/libgit2/tests/t02-objread.c index 85b03b026..4bcff2742 100644 --- a/vendor/libgit2/tests/t02-objread.c +++ b/vendor/libgit2/tests/t02-objread.c @@ -36,12 +36,12 @@ BEGIN_TEST(existsloose0, "check if a loose object exists on the odb") must_pass(write_object_files(odb_dir, &one)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, one.id)); + must_pass(git_oid_fromstr(&id, one.id)); must_be_true(git_odb_exists(db, &id)); /* Test for a non-existant object */ - must_pass(git_oid_mkstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa")); + must_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa")); must_be_true(0 == git_odb_exists(db, &id2)); git_odb_close(db); @@ -55,7 +55,7 @@ BEGIN_TEST(readloose0, "read a loose commit") must_pass(write_object_files(odb_dir, &commit)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, commit.id)); + must_pass(git_oid_fromstr(&id, commit.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects((git_rawobj *)&obj->raw, &commit)); @@ -72,7 +72,7 @@ BEGIN_TEST(readloose1, "read a loose tree") must_pass(write_object_files(odb_dir, &tree)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, tree.id)); + must_pass(git_oid_fromstr(&id, tree.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects((git_rawobj *)&obj->raw, &tree)); @@ -89,7 +89,7 @@ BEGIN_TEST(readloose2, "read a loose tag") must_pass(write_object_files(odb_dir, &tag)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, tag.id)); + must_pass(git_oid_fromstr(&id, tag.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects((git_rawobj *)&obj->raw, &tag)); @@ -106,7 +106,7 @@ BEGIN_TEST(readloose3, "read a loose zero-bytes object") must_pass(write_object_files(odb_dir, &zero)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, zero.id)); + must_pass(git_oid_fromstr(&id, zero.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects((git_rawobj *)&obj->raw, &zero)); @@ -123,7 +123,7 @@ BEGIN_TEST(readloose4, "read a one-byte long loose object") must_pass(write_object_files(odb_dir, &one)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, one.id)); + must_pass(git_oid_fromstr(&id, one.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects(&obj->raw, &one)); @@ -140,7 +140,7 @@ BEGIN_TEST(readloose5, "read a two-bytes long loose object") must_pass(write_object_files(odb_dir, &two)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, two.id)); + must_pass(git_oid_fromstr(&id, two.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects(&obj->raw, &two)); @@ -157,7 +157,7 @@ BEGIN_TEST(readloose6, "read a loose object which is several bytes long") must_pass(write_object_files(odb_dir, &some)); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id, some.id)); + must_pass(git_oid_fromstr(&id, some.id)); must_pass(git_odb_read(&obj, db, &id)); must_pass(cmp_objects(&obj->raw, &some)); @@ -177,7 +177,7 @@ BEGIN_TEST(readpack0, "read several packed objects") git_oid id; git_odb_object *obj; - must_pass(git_oid_mkstr(&id, packed_objects[i])); + must_pass(git_oid_fromstr(&id, packed_objects[i])); must_be_true(git_odb_exists(db, &id) == 1); must_pass(git_odb_read(&obj, db, &id)); @@ -199,7 +199,7 @@ BEGIN_TEST(readheader0, "read only the header of several packed objects") size_t len; git_otype type; - must_pass(git_oid_mkstr(&id, packed_objects[i])); + must_pass(git_oid_fromstr(&id, packed_objects[i])); must_pass(git_odb_read(&obj, db, &id)); must_pass(git_odb_read_header(&len, &type, db, &id)); @@ -210,7 +210,7 @@ BEGIN_TEST(readheader0, "read only the header of several packed objects") git_odb_object_close(obj); } - git_odb_close(db); + git_odb_close(db); END_TEST BEGIN_TEST(readheader1, "read only the header of several loose objects") @@ -225,7 +225,7 @@ BEGIN_TEST(readheader1, "read only the header of several loose objects") size_t len; git_otype type; - must_pass(git_oid_mkstr(&id, loose_objects[i])); + must_pass(git_oid_fromstr(&id, loose_objects[i])); must_be_true(git_odb_exists(db, &id) == 1); diff --git a/vendor/libgit2/tests/t03-data.h b/vendor/libgit2/tests/t03-data.h new file mode 100644 index 000000000..a4b73fec3 --- /dev/null +++ b/vendor/libgit2/tests/t03-data.h @@ -0,0 +1,344 @@ + +typedef struct object_data { + char *id; /* object id (sha1) */ + char *dir; /* object store (fan-out) directory name */ + char *file; /* object store filename */ +} object_data; + +static object_data commit = { + "3d7f8a6af076c8c3f20071a8935cdbe8228594d1", + "test-objects/3d", + "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1", +}; + +static unsigned char commit_data[] = { + 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66, + 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35, + 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38, + 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32, + 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33, + 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55, + 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, + 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, + 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20, + 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, + 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, + 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34, + 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30, + 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65, + 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68, + 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67, + 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72, + 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70, + 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d, + 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20, + 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x3e, 0x0a, +}; + +static git_rawobj commit_obj = { + commit_data, + sizeof(commit_data), + GIT_OBJ_COMMIT +}; + +static object_data tree = { + "dff2da90b254e1beb889d1f1f1288be1803782df", + "test-objects/df", + "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df", +}; + +static unsigned char tree_data[] = { + 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f, + 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79, + 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b, + 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31, + 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f, + 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86, + 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8, + 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31, + 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77, + 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b, + 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd, + 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30, + 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72, + 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, + 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a, + 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91, +}; + +static git_rawobj tree_obj = { + tree_data, + sizeof(tree_data), + GIT_OBJ_TREE +}; + +static object_data tag = { + "09d373e1dfdc16b129ceec6dd649739911541e05", + "test-objects/09", + "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05", +}; + +static unsigned char tag_data[] = { + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33, + 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66, + 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66, + 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39, + 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32, + 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a, + 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20, + 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74, + 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20, + 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72, + 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e, + 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34, + 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30, + 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20, + 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74, + 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, + 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x0a, +}; + +static git_rawobj tag_obj = { + tag_data, + sizeof(tag_data), + GIT_OBJ_TAG +}; + +static object_data zero = { + "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", + "test-objects/e6", + "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391", +}; + +static unsigned char zero_data[] = { + 0x00 /* dummy data */ +}; + +static git_rawobj zero_obj = { + zero_data, + 0, + GIT_OBJ_BLOB +}; + +static object_data one = { + "8b137891791fe96927ad78e64b0aad7bded08bdc", + "test-objects/8b", + "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc", +}; + +static unsigned char one_data[] = { + 0x0a, +}; + +static git_rawobj one_obj = { + one_data, + sizeof(one_data), + GIT_OBJ_BLOB +}; + +static object_data two = { + "78981922613b2afb6025042ff6bd878ac1994e85", + "test-objects/78", + "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85", +}; + +static unsigned char two_data[] = { + 0x61, 0x0a, +}; + +static git_rawobj two_obj = { + two_data, + sizeof(two_data), + GIT_OBJ_BLOB +}; + +static object_data some = { + "fd8430bc864cfcd5f10e5590f8a447e01b942bfe", + "test-objects/fd", + "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe", +}; + +static unsigned char some_data[] = { + 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, + 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20, + 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, + 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, + 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74, + 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69, + 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72, + 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a, + 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e, + 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, + 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, + 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61, + 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, + 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20, + 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, + 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a, + 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, + 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65, + 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65, + 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e, + 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, + 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20, + 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69, + 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74, + 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67, + 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20, + 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, + 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69, + 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e, + 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a, + 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20, + 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, + 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, + 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65, + 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, + 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, + 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, + 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79, + 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63, + 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, + 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d, + 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c, + 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65, + 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20, + 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62, + 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e, + 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, + 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, + 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c, + 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65, + 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74, + 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48, + 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20, + 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59, + 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, + 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69, + 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61, + 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20, + 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, + 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54, + 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54, + 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52, + 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49, + 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55, + 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20, + 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, + 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, + 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f, + 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, + 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61, + 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55, + 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, + 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, + 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a, + 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47, + 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f, + 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20, + 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, + 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20, + 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e, + 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c, + 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46, + 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a, + 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c, + 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31, + 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20, + 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f, + 0x0a, +}; + +static git_rawobj some_obj = { + some_data, + sizeof(some_data), + GIT_OBJ_BLOB +}; diff --git a/vendor/libgit2/tests/t03-objwrite.c b/vendor/libgit2/tests/t03-objwrite.c index 773887397..31f611a5c 100644 --- a/vendor/libgit2/tests/t03-objwrite.c +++ b/vendor/libgit2/tests/t03-objwrite.c @@ -31,7 +31,7 @@ static char *odb_dir = "test-objects"; static int make_odb_dir(void) { - if (gitfo_mkdir(odb_dir, 0755) < 0) { + if (p_mkdir(odb_dir, 0755) < 0) { int err = errno; fprintf(stderr, "can't make directory \"%s\"", odb_dir); if (err == EEXIST) @@ -44,9 +44,9 @@ static int make_odb_dir(void) static int check_object_files(object_data *d) { - if (gitfo_exists(d->dir) < 0) + if (git_futils_exists(d->dir) < 0) return -1; - if (gitfo_exists(d->file) < 0) + if (git_futils_exists(d->file) < 0) return -1; return 0; } @@ -64,16 +64,16 @@ static int cmp_objects(git_rawobj *o1, git_rawobj *o2) static int remove_object_files(object_data *d) { - if (gitfo_unlink(d->file) < 0) { + if (p_unlink(d->file) < 0) { fprintf(stderr, "can't delete object file \"%s\"\n", d->file); return -1; } - if ((gitfo_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) { + if ((p_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) { fprintf(stderr, "can't remove directory \"%s\"\n", d->dir); return -1; } - if (gitfo_rmdir(odb_dir) < 0) { + if (p_rmdir(odb_dir) < 0) { fprintf(stderr, "can't remove directory \"%s\"\n", odb_dir); return -1; } @@ -104,7 +104,7 @@ BEGIN_TEST(write0, "write loose commit object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, commit.id)); + must_pass(git_oid_fromstr(&id1, commit.id)); must_pass(streaming_write(&id2, db, &commit_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -125,7 +125,7 @@ BEGIN_TEST(write1, "write loose tree object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, tree.id)); + must_pass(git_oid_fromstr(&id1, tree.id)); must_pass(streaming_write(&id2, db, &tree_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -146,7 +146,7 @@ BEGIN_TEST(write2, "write loose tag object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, tag.id)); + must_pass(git_oid_fromstr(&id1, tag.id)); must_pass(streaming_write(&id2, db, &tag_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -167,7 +167,7 @@ BEGIN_TEST(write3, "write zero-length object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, zero.id)); + must_pass(git_oid_fromstr(&id1, zero.id)); must_pass(streaming_write(&id2, db, &zero_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -188,7 +188,7 @@ BEGIN_TEST(write4, "write one-byte long object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, one.id)); + must_pass(git_oid_fromstr(&id1, one.id)); must_pass(streaming_write(&id2, db, &one_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -209,7 +209,7 @@ BEGIN_TEST(write5, "write two-byte long object") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, two.id)); + must_pass(git_oid_fromstr(&id1, two.id)); must_pass(streaming_write(&id2, db, &two_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); @@ -230,7 +230,7 @@ BEGIN_TEST(write6, "write an object which is several bytes long") must_pass(make_odb_dir()); must_pass(git_odb_open(&db, odb_dir)); - must_pass(git_oid_mkstr(&id1, some.id)); + must_pass(git_oid_fromstr(&id1, some.id)); must_pass(streaming_write(&id2, db, &some_obj)); must_be_true(git_oid_cmp(&id1, &id2) == 0); diff --git a/vendor/libgit2/tests/t04-commit.c b/vendor/libgit2/tests/t04-commit.c index 36f3e66b5..a8617ed6a 100644 --- a/vendor/libgit2/tests/t04-commit.c +++ b/vendor/libgit2/tests/t04-commit.c @@ -117,14 +117,14 @@ BEGIN_TEST(parse0, "parse the OID line in a commit") const char *ptr = string;\ const char *ptr_original = ptr;\ size_t len = strlen(ptr);\ - must_pass(git__parse_oid(&oid, &ptr, ptr + len, header));\ + must_pass(git_oid__parse(&oid, &ptr, ptr + len, header));\ must_be_true(ptr == ptr_original + len);\ } #define TEST_OID_FAIL(string, header) { \ const char *ptr = string;\ size_t len = strlen(ptr);\ - must_fail(git__parse_oid(&oid, &ptr, ptr + len, header));\ + must_fail(git_oid__parse(&oid, &ptr, ptr + len, header));\ } TEST_OID_PASS("parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent "); @@ -157,7 +157,7 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") const char *ptr = _string; \ size_t len = strlen(_string);\ git_signature person = {NULL, NULL, {0, 0}}; \ - must_pass(git_signature__parse(&person, &ptr, ptr + len, _header));\ + must_pass(git_signature__parse(&person, &ptr, ptr + len, _header, '\n'));\ must_be_true(strcmp(_name, person.name) == 0);\ must_be_true(strcmp(_email, person.email) == 0);\ must_be_true(_time == person.when.time);\ @@ -169,7 +169,7 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") const char *ptr = _string; \ size_t len = strlen(_string);\ git_signature person = {NULL, NULL, {0, 0}}; \ - must_fail(git_signature__parse(&person, &ptr, ptr + len, _header));\ + must_fail(git_signature__parse(&person, &ptr, ptr + len, _header, '\n'));\ free(person.name); free(person.email);\ } @@ -241,12 +241,179 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") 123456, -60); - TEST_SIGNATURE_FAIL( + /* Parse a signature without an author field */ + TEST_SIGNATURE_PASS( + "committer 123456 -0100 \n", + "committer ", + "", + "tanoku@gmail.com", + 123456, + -60); + + /* Parse a signature without an author field */ + TEST_SIGNATURE_PASS( + "committer 123456 -0100 \n", + "committer ", + "", + "tanoku@gmail.com", + 123456, + -60); + + /* Parse a signature with an empty author field */ + TEST_SIGNATURE_PASS( + "committer 123456 -0100 \n", + "committer ", + "", + "tanoku@gmail.com", + 123456, + -60); + + /* Parse a signature with an empty email field */ + TEST_SIGNATURE_PASS( + "committer Vicent Marti <> 123456 -0100 \n", + "committer ", + "Vicent Marti", + "", + 123456, + -60); + + /* Parse a signature with an empty email field */ + TEST_SIGNATURE_PASS( + "committer Vicent Marti < > 123456 -0100 \n", + "committer ", + "Vicent Marti", + "", + 123456, + -60); + + /* Parse a signature with empty name and email */ + TEST_SIGNATURE_PASS( + "committer <> 123456 -0100 \n", + "committer ", + "", + "", + 123456, + -60); + + /* Parse a signature with empty name and email */ + TEST_SIGNATURE_PASS( + "committer <> 123456 -0100 \n", + "committer ", + "", + "", + 123456, + -60); + + /* Parse a signature with empty name and email */ + TEST_SIGNATURE_PASS( + "committer < > 123456 -0100 \n", + "committer ", + "", + "", + 123456, + -60); + + /* Parse an obviously invalid signature */ + TEST_SIGNATURE_PASS( + "committer foo<@bar> 123456 -0100 \n", + "committer ", + "foo", + "@bar", + 123456, + -60); + + /* Parse an obviously invalid signature */ + TEST_SIGNATURE_PASS( + "committer foo<@bar>123456 -0100 \n", + "committer ", + "foo", + "@bar", + 123456, + -60); + + /* Parse an obviously invalid signature */ + TEST_SIGNATURE_PASS( + "committer <>\n", + "committer ", + "", + "", + 0, + 0); + + TEST_SIGNATURE_PASS( "committer Vicent Marti 123456 -1500 \n", - "committer "); + "committer ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); - TEST_SIGNATURE_FAIL( + TEST_SIGNATURE_PASS( "committer Vicent Marti 123456 +0163 \n", + "committer ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); + + TEST_SIGNATURE_PASS( + "author Vicent Marti notime \n", + "author ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); + + TEST_SIGNATURE_PASS( + "author Vicent Marti 123456 notimezone \n", + "author ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); + + TEST_SIGNATURE_PASS( + "author Vicent Marti notime +0100\n", + "author ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); + + TEST_SIGNATURE_PASS( + "author Vicent Marti \n", + "author ", + "Vicent Marti", + "tanoku@gmail.com", + 0, + 0); + + TEST_SIGNATURE_PASS( + "author A U Thor , C O. Miter 1234567890 -0700\n", + "author ", + "A U Thor", + "author@example.com", + 1234567890, + -420); + + TEST_SIGNATURE_PASS( + "author A U Thor and others 1234567890 -0700\n", + "author ", + "A U Thor", + "author@example.com", + 1234567890, + -420); + + TEST_SIGNATURE_PASS( + "author A U Thor and others 1234567890\n", + "author ", + "A U Thor", + "author@example.com", + 1234567890, + 0); + + TEST_SIGNATURE_FAIL( + "committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n", "committer "); TEST_SIGNATURE_FAIL( @@ -266,12 +433,8 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") "author "); TEST_SIGNATURE_FAIL( - "author Vicent Marti notime \n", - "author "); - - TEST_SIGNATURE_FAIL( - "author Vicent Marti \n", - "author "); + "committer Vicent Marti ><\n", + "committer "); TEST_SIGNATURE_FAIL( "author ", @@ -282,8 +445,61 @@ BEGIN_TEST(parse1, "parse the signature line in a commit") END_TEST +static int try_build_signature(const char *name, const char *email, git_time_t time, int offset) +{ + git_signature *sign; + int error = GIT_SUCCESS; + + if ((error = git_signature_new(&sign, name, email, time, offset)) < GIT_SUCCESS) + return error; + + git_signature_free((git_signature *)sign); + + return error; +} + +BEGIN_TEST(signature0, "creating a signature trims leading and trailing spaces") + git_signature *sign; + must_pass(git_signature_new(&sign, " nulltoken ", " emeric.fermas@gmail.com ", 1234567890, 60)); + must_be_true(strcmp(sign->name, "nulltoken") == 0); + must_be_true(strcmp(sign->email, "emeric.fermas@gmail.com") == 0); + git_signature_free((git_signature *)sign); +END_TEST + +BEGIN_TEST(signature1, "can not create a signature with empty name or email") + must_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60)); + + must_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60)); + must_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60)); + must_fail(try_build_signature("nulltoken", "", 1234567890, 60)); + must_fail(try_build_signature("nulltoken", " ", 1234567890, 60)); +END_TEST + +BEGIN_TEST(signature2, "creating a one character signature") + git_signature *sign; + must_pass(git_signature_new(&sign, "x", "foo@bar.baz", 1234567890, 60)); + must_be_true(strcmp(sign->name, "x") == 0); + must_be_true(strcmp(sign->email, "foo@bar.baz") == 0); + git_signature_free((git_signature *)sign); +END_TEST + +BEGIN_TEST(signature3, "creating a two character signature") + git_signature *sign; + must_pass(git_signature_new(&sign, "xx", "x@y.z", 1234567890, 60)); + must_be_true(strcmp(sign->name, "xx") == 0); + must_be_true(strcmp(sign->email, "x@y.z") == 0); + git_signature_free((git_signature *)sign); +END_TEST + +BEGIN_TEST(signature4, "creating a zero character signature") + git_signature *sign; + must_fail(git_signature_new(&sign, "", "x@y.z", 1234567890, 60)); + must_be_true(sign == NULL); +END_TEST + + /* External declaration for testing the buffer parsing method */ -int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags); +int commit_parse_buffer(git_commit *commit, void *data, size_t len); BEGIN_TEST(parse2, "parse a whole commit buffer") const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken); @@ -303,8 +519,7 @@ BEGIN_TEST(parse2, "parse a whole commit buffer") must_fail(commit_parse_buffer( commit, test_commits_broken[i], - strlen(test_commits_broken[i]), - 0x1) + strlen(test_commits_broken[i])) ); git_commit__free(commit); @@ -320,8 +535,7 @@ BEGIN_TEST(parse2, "parse a whole commit buffer") must_pass(commit_parse_buffer( commit, test_commits_working[i], - strlen(test_commits_working[i]), - 0x0) + strlen(test_commits_working[i])) ); git_commit__free(commit); @@ -333,8 +547,7 @@ BEGIN_TEST(parse2, "parse a whole commit buffer") must_pass(commit_parse_buffer( commit, test_commits_working[i], - strlen(test_commits_working[i]), - 0x1) + strlen(test_commits_working[i])) ); git_commit__free(commit); @@ -359,23 +572,22 @@ BEGIN_TEST(details0, "query the details on a parsed commit") git_repository *repo; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - + for (i = 0; i < commit_count; ++i) { git_oid id; git_commit *commit; const git_signature *author, *committer; - const char *message, *message_short; + const char *message; git_time_t commit_time; unsigned int parents, p; git_commit *parent = NULL, *old_parent = NULL; - git_oid_mkstr(&id, commit_ids[i]); + git_oid_fromstr(&id, commit_ids[i]); must_pass(git_commit_lookup(&commit, repo, &id)); message = git_commit_message(commit); - message_short = git_commit_message_short(commit); author = git_commit_author(commit); committer = git_commit_committer(commit); commit_time = git_commit_time(commit); @@ -386,7 +598,6 @@ BEGIN_TEST(details0, "query the details on a parsed commit") must_be_true(strcmp(committer->name, "Scott Chacon") == 0); must_be_true(strcmp(committer->email, "schacon@gmail.com") == 0); must_be_true(strchr(message, '\n') != NULL); - must_be_true(strchr(message_short, '\n') == NULL); must_be_true(commit_time > 0); must_be_true(parents <= 2); for (p = 0;p < parents;p++) { @@ -415,26 +626,26 @@ This is a commit created in memory and it will be written back to disk\n" static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; - BEGIN_TEST(write0, "write a new commit object from memory to disk") git_repository *repo; git_commit *commit; git_oid tree_id, parent_id, commit_id; - const git_signature *author, *committer; - /* char hex_oid[41]; */ + git_signature *author, *committer; + const git_signature *author1, *committer1; + git_commit *parent; + git_tree *tree; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + git_oid_fromstr(&tree_id, tree_oid); + must_pass(git_tree_lookup(&tree, repo, &tree_id)); - git_oid_mkstr(&tree_id, tree_oid); - git_oid_mkstr(&parent_id, commit_ids[4]); + git_oid_fromstr(&parent_id, commit_ids[4]); + must_pass(git_commit_lookup(&parent, repo, &parent_id)); /* create signatures */ - committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); - must_be_true(committer != NULL); - - author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90); - must_be_true(author != NULL); + must_pass(git_signature_new(&committer, COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60)); + must_pass(git_signature_new(&author, COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90)); must_pass(git_commit_create_v( &commit_id, /* out id */ @@ -442,29 +653,33 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk") NULL, /* do not update the HEAD */ author, committer, + NULL, COMMIT_MESSAGE, - &tree_id, - 1, &parent_id)); + tree, + 1, parent)); + + git_object_close((git_object *)parent); + git_object_close((git_object *)tree); - git_signature_free((git_signature *)committer); - git_signature_free((git_signature *)author); + git_signature_free(committer); + git_signature_free(author); must_pass(git_commit_lookup(&commit, repo, &commit_id)); /* Check attributes were set correctly */ - author = git_commit_author(commit); - must_be_true(author != NULL); - must_be_true(strcmp(author->name, COMMITTER_NAME) == 0); - must_be_true(strcmp(author->email, COMMITTER_EMAIL) == 0); - must_be_true(author->when.time == 987654321); - must_be_true(author->when.offset == 90); - - committer = git_commit_committer(commit); - must_be_true(committer != NULL); - must_be_true(strcmp(committer->name, COMMITTER_NAME) == 0); - must_be_true(strcmp(committer->email, COMMITTER_EMAIL) == 0); - must_be_true(committer->when.time == 123456789); - must_be_true(committer->when.offset == 60); + author1 = git_commit_author(commit); + must_be_true(author1 != NULL); + must_be_true(strcmp(author1->name, COMMITTER_NAME) == 0); + must_be_true(strcmp(author1->email, COMMITTER_EMAIL) == 0); + must_be_true(author1->when.time == 987654321); + must_be_true(author1->when.offset == 90); + + committer1 = git_commit_committer(commit); + must_be_true(committer1 != NULL); + must_be_true(strcmp(committer1->name, COMMITTER_NAME) == 0); + must_be_true(strcmp(committer1->email, COMMITTER_EMAIL) == 0); + must_be_true(committer1->when.time == 123456789); + must_be_true(committer1->when.offset == 60); must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0); @@ -482,21 +697,20 @@ BEGIN_TEST(root0, "create a root commit") git_commit *commit; git_oid tree_id, commit_id; const git_oid *branch_oid; - const git_signature *author, *committer; + git_signature *author, *committer; const char *branch_name = "refs/heads/root-commit-branch"; git_reference *head, *branch; char *head_old; + git_tree *tree; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&tree_id, tree_oid); + git_oid_fromstr(&tree_id, tree_oid); + must_pass(git_tree_lookup(&tree, repo, &tree_id)); /* create signatures */ - committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); - must_be_true(committer != NULL); - - author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90); - must_be_true(author != NULL); + must_pass(git_signature_new(&committer, COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60)); + must_pass(git_signature_new(&author, COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90)); /* First we need to update HEAD so it points to our non-existant branch */ must_pass(git_reference_lookup(&head, repo, "HEAD")); @@ -512,12 +726,14 @@ BEGIN_TEST(root0, "create a root commit") "HEAD", author, committer, + NULL, ROOT_COMMIT_MESSAGE, - &tree_id, + tree, 0)); - git_signature_free((git_signature *)committer); - git_signature_free((git_signature *)author); + git_object_close((git_object *)tree); + git_signature_free(committer); + git_signature_free(author); /* * The fact that creating a commit works has already been @@ -548,7 +764,12 @@ BEGIN_SUITE(commit) ADD_TEST(details0); ADD_TEST(write0); - //ADD_TEST(write1); ADD_TEST(root0); + + ADD_TEST(signature0); + ADD_TEST(signature1); + ADD_TEST(signature2); + ADD_TEST(signature3); + ADD_TEST(signature4); END_SUITE diff --git a/vendor/libgit2/tests/t05-revwalk.c b/vendor/libgit2/tests/t05-revwalk.c index cfcf01066..ab509ab94 100644 --- a/vendor/libgit2/tests/t05-revwalk.c +++ b/vendor/libgit2/tests/t05-revwalk.c @@ -74,7 +74,7 @@ static int get_commit_index(git_oid *raw_oid) char oid[40]; git_oid_fmt(oid, raw_oid); - + for (i = 0; i < commit_count; ++i) if (memcmp(oid, commit_ids[i], 40) == 0) return i; @@ -108,7 +108,7 @@ static int test_walk(git_revwalk *walk, const git_oid *root, }*/ } - for (i = 0; i < results_count; ++i) + for (i = 0; i < results_count; ++i) if (memcmp(possible_results[i], result_array, result_bytes) == 0) return GIT_SUCCESS; @@ -124,7 +124,7 @@ BEGIN_TEST(walk0, "do a simple walk on a repo with different sorting modes") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_revwalk_new(&walk, repo)); - git_oid_mkstr(&id, commit_head); + git_oid_fromstr(&id, commit_head); must_pass(test_walk(walk, &id, GIT_SORT_TIME, commit_sorting_time, 1)); must_pass(test_walk(walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2)); diff --git a/vendor/libgit2/tests/t06-index.c b/vendor/libgit2/tests/t06-index.c index 93ca2c04e..621e742b3 100644 --- a/vendor/libgit2/tests/t06-index.c +++ b/vendor/libgit2/tests/t06-index.c @@ -48,12 +48,9 @@ struct test_entry TEST_ENTRIES[] = { BEGIN_TEST(read0, "load an empty index") git_index *index; - must_pass(git_index_open_bare(&index, "in-memory-index")); + must_pass(git_index_open(&index, "in-memory-index")); must_be_true(index->on_disk == 0); - must_pass(git_index_read(index)); - - must_be_true(index->on_disk == 0); must_be_true(git_index_entrycount(index) == 0); must_be_true(index->entries.sorted); @@ -65,12 +62,9 @@ BEGIN_TEST(read1, "load a standard index (default test index)") unsigned int i; git_index_entry **entries; - must_pass(git_index_open_bare(&index, TEST_INDEX_PATH)); + must_pass(git_index_open(&index, TEST_INDEX_PATH)); must_be_true(index->on_disk); - must_pass(git_index_read(index)); - - must_be_true(index->on_disk); must_be_true(git_index_entrycount(index) == TEST_INDEX_ENTRY_COUNT); must_be_true(index->entries.sorted); @@ -90,12 +84,9 @@ END_TEST BEGIN_TEST(read2, "load a standard index (git.git index)") git_index *index; - must_pass(git_index_open_bare(&index, TEST_INDEX2_PATH)); + must_pass(git_index_open(&index, TEST_INDEX2_PATH)); must_be_true(index->on_disk); - must_pass(git_index_read(index)); - - must_be_true(index->on_disk); must_be_true(git_index_entrycount(index) == TEST_INDEX2_ENTRY_COUNT); must_be_true(index->entries.sorted); must_be_true(index->tree != NULL); @@ -107,8 +98,7 @@ BEGIN_TEST(find0, "find an entry on an index") git_index *index; unsigned int i; - must_pass(git_index_open_bare(&index, TEST_INDEX_PATH)); - must_pass(git_index_read(index)); + must_pass(git_index_open(&index, TEST_INDEX_PATH)); for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) { int idx = git_index_find(index, TEST_ENTRIES[i].path); @@ -122,7 +112,7 @@ BEGIN_TEST(find1, "find an entry in an empty index") git_index *index; unsigned int i; - must_pass(git_index_open_bare(&index, "fake-index")); + must_pass(git_index_open(&index, "fake-index")); for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) { int idx = git_index_find(index, TEST_ENTRIES[i].path); @@ -137,16 +127,15 @@ BEGIN_TEST(write0, "write an index back to disk") must_pass(copy_file(TEST_INDEXBIG_PATH, "index_rewrite")); - must_pass(git_index_open_bare(&index, "index_rewrite")); - must_pass(git_index_read(index)); + must_pass(git_index_open(&index, "index_rewrite")); must_be_true(index->on_disk); must_pass(git_index_write(index)); must_pass(cmp_files(TEST_INDEXBIG_PATH, "index_rewrite")); git_index_free(index); - - gitfo_unlink("index_rewrite"); + + p_unlink("index_rewrite"); END_TEST BEGIN_TEST(sort0, "sort the entires in an index") @@ -161,11 +150,10 @@ BEGIN_TEST(sort0, "sort the entires in an index") */ END_TEST - BEGIN_TEST(sort1, "sort the entires in an empty index") git_index *index; - must_pass(git_index_open_bare(&index, "fake-index")); + must_pass(git_index_open(&index, "fake-index")); /* FIXME: this test is slightly dumb */ must_be_true(index->entries.sorted); @@ -173,6 +161,47 @@ BEGIN_TEST(sort1, "sort the entires in an empty index") git_index_free(index); END_TEST +BEGIN_TEST(add0, "add a new file to the index") + git_index *index; + git_filebuf file; + git_repository *repo; + git_index_entry *entry; + git_oid id1; + + /* Intialize a new repository */ + must_pass(git_repository_init(&repo, TEMP_REPO_FOLDER "myrepo", 0)); + + /* Ensure we're the only guy in the room */ + must_pass(git_repository_index(&index, repo)); + must_pass(git_index_entrycount(index) == 0); + + /* Create a new file in the working directory */ + must_pass(git_futils_mkpath2file(TEMP_REPO_FOLDER "myrepo/test.txt")); + must_pass(git_filebuf_open(&file, TEMP_REPO_FOLDER "myrepo/test.txt", 0)); + must_pass(git_filebuf_write(&file, "hey there\n", 10)); + must_pass(git_filebuf_commit(&file)); + + /* Store the expected hash of the file/blob + * This has been generated by executing the following + * $ echo "hey there" | git hash-object --stdin + */ + must_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); + + /* Add the new file to the index */ + must_pass(git_index_add(index, "test.txt", 0)); + + /* Wow... it worked! */ + must_pass(git_index_entrycount(index) == 1); + entry = git_index_get(index, 0); + + /* And the built-in hashing mechanism worked as expected */ + must_be_true(git_oid_cmp(&id1, &entry->oid) == 0); + + git_index_free(index); + git_repository_free(repo); + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); +END_TEST + BEGIN_SUITE(index) ADD_TEST(read0); ADD_TEST(read1); @@ -185,4 +214,6 @@ BEGIN_SUITE(index) ADD_TEST(sort0); ADD_TEST(sort1); + + ADD_TEST(add0); END_SUITE diff --git a/vendor/libgit2/tests/t07-hashtable.c b/vendor/libgit2/tests/t07-hashtable.c index 0b362cafd..7313f2cc9 100644 --- a/vendor/libgit2/tests/t07-hashtable.c +++ b/vendor/libgit2/tests/t07-hashtable.c @@ -37,9 +37,8 @@ typedef struct _aux_object { uint32_t hash_func(const void *key, int hash_id) { uint32_t r; - git_oid *id; + const git_oid *id = key; - id = (git_oid *)key; memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); return r; } diff --git a/vendor/libgit2/tests/t08-tag.c b/vendor/libgit2/tests/t08-tag.c index fae2e99db..b0d4af87b 100644 --- a/vendor/libgit2/tests/t08-tag.c +++ b/vendor/libgit2/tests/t08-tag.c @@ -39,9 +39,9 @@ BEGIN_TEST(read0, "read and parse a tag from the repository") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id1, tag1_id); - git_oid_mkstr(&id2, tag2_id); - git_oid_mkstr(&id_commit, tagged_commit); + git_oid_fromstr(&id1, tag1_id); + git_oid_fromstr(&id2, tag2_id); + git_oid_fromstr(&id_commit, tagged_commit); must_pass(git_tag_lookup(&tag1, repo, &id1)); @@ -77,6 +77,35 @@ BEGIN_TEST(read1, "list all tag names from the repository") git_repository_free(repo); END_TEST +static int ensure_tag_pattern_match(git_repository *repo, const char *pattern, const size_t expected_matches) +{ + git_strarray tag_list; + int error = GIT_SUCCESS; + + if ((error = git_tag_list_match(&tag_list, pattern, repo)) < GIT_SUCCESS) + goto exit; + + if (tag_list.count != expected_matches) + error = GIT_ERROR; + +exit: + git_strarray_free(&tag_list); + return error; +} + +BEGIN_TEST(read2, "list all tag names from the repository matching a specified pattern") + git_repository *repo; + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(ensure_tag_pattern_match(repo, "", 3)); + must_pass(ensure_tag_pattern_match(repo, "*", 3)); + must_pass(ensure_tag_pattern_match(repo, "t*", 1)); + must_pass(ensure_tag_pattern_match(repo, "*b", 2)); + must_pass(ensure_tag_pattern_match(repo, "e", 0)); + must_pass(ensure_tag_pattern_match(repo, "e90810b", 1)); + must_pass(ensure_tag_pattern_match(repo, "e90810[ab]", 1)); + git_repository_free(repo); +END_TEST + #define TAGGER_NAME "Vicent Marti" #define TAGGER_EMAIL "vicent@github.com" @@ -86,38 +115,41 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") git_repository *repo; git_tag *tag; git_oid target_id, tag_id; - const git_signature *tagger; + git_signature *tagger; + const git_signature *tagger1; git_reference *ref_tag; + git_object *target; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&target_id, tagged_commit); + git_oid_fromstr(&target_id, tagged_commit); + must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT)); /* create signature */ - tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); - must_be_true(tagger != NULL); + must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60)); must_pass(git_tag_create( &tag_id, /* out id */ repo, "the-tag", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 0)); - git_signature_free((git_signature *)tagger); + git_object_close(target); + git_signature_free(tagger); must_pass(git_tag_lookup(&tag, repo, &tag_id)); must_be_true(git_oid_cmp(git_tag_target_oid(tag), &target_id) == 0); /* Check attributes were set correctly */ - tagger = git_tag_tagger(tag); - must_be_true(tagger != NULL); - must_be_true(strcmp(tagger->name, TAGGER_NAME) == 0); - must_be_true(strcmp(tagger->email, TAGGER_EMAIL) == 0); - must_be_true(tagger->when.time == 123456789); - must_be_true(tagger->when.offset == 60); + tagger1 = git_tag_tagger(tag); + must_be_true(tagger1 != NULL); + must_be_true(strcmp(tagger1->name, TAGGER_NAME) == 0); + must_be_true(strcmp(tagger1->email, TAGGER_EMAIL) == 0); + must_be_true(tagger1->when.time == 123456789); + must_be_true(tagger1->when.offset == 60); must_be_true(strcmp(git_tag_message(tag), TAGGER_MESSAGE) == 0); @@ -132,57 +164,31 @@ BEGIN_TEST(write0, "write a tag to the repository and read it again") END_TEST -BEGIN_TEST(write1, "write a tag to the repository which points to an unknown oid should fail") - git_repository *repo; - git_oid target_id, tag_id; - const git_signature *tagger; - - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - - git_oid_mkstr(&target_id, "deadbeef1b46c854b31185ea97743be6a8774479"); - - /* create signature */ - tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); - must_be_true(tagger != NULL); - - must_fail(git_tag_create( - &tag_id, /* out id */ - repo, - "the-zombie-tag", - &target_id, - GIT_OBJ_COMMIT, - tagger, - TAGGER_MESSAGE)); - - git_signature_free((git_signature *)tagger); - - git_repository_free(repo); - -END_TEST - BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already existing tag") git_repository *repo; git_oid target_id, tag_id; - const git_signature *tagger; + git_signature *tagger; + git_object *target; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&target_id, tagged_commit); + git_oid_fromstr(&target_id, tagged_commit); + must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT)); /* create signature */ - tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); - must_be_true(tagger != NULL); + must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60)); must_fail(git_tag_create( &tag_id, /* out id */ repo, "e90810b", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 0)); - git_signature_free((git_signature *)tagger); + git_object_close(target); + git_signature_free(tagger); git_repository_free(repo); @@ -191,30 +197,32 @@ END_TEST BEGIN_TEST(write3, "Replace an already existing tag") git_repository *repo; git_oid target_id, tag_id, old_tag_id; - const git_signature *tagger; + git_signature *tagger; git_reference *ref_tag; + git_object *target; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&target_id, tagged_commit); + git_oid_fromstr(&target_id, tagged_commit); + must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT)); must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag)); /* create signature */ - tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60); - must_be_true(tagger != NULL); + must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60)); - must_pass(git_tag_create_f( + must_pass(git_tag_create( &tag_id, /* out id */ repo, "e90810b", - &target_id, - GIT_OBJ_COMMIT, + target, tagger, - TAGGER_MESSAGE)); + TAGGER_MESSAGE, + 1)); - git_signature_free((git_signature *)tagger); + git_object_close(target); + git_signature_free(tagger); must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0); @@ -224,26 +232,85 @@ BEGIN_TEST(write3, "Replace an already existing tag") END_TEST -BEGIN_TEST(write4, "Delete an already existing tag") +BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again") + git_repository *repo; + git_oid target_id, object_id; + git_reference *ref_tag; + git_object *target; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + git_oid_fromstr(&target_id, tagged_commit); + must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT)); + + must_pass(git_tag_create_lightweight( + &object_id, + repo, + "light-tag", + target, + 0)); + + git_object_close(target); + + must_be_true(git_oid_cmp(&object_id, &target_id) == 0); + + must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/light-tag")); + must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &target_id) == 0); + + must_pass(git_tag_delete(repo, "light-tag")); + + git_repository_free(repo); +END_TEST + +BEGIN_TEST(write5, "Attempt to write a lightweight tag bearing the same name than an already existing tag") + git_repository *repo; + git_oid target_id, object_id, existing_object_id; + git_object *target; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + git_oid_fromstr(&target_id, tagged_commit); + must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT)); + + must_fail(git_tag_create_lightweight( + &object_id, + repo, + "e90810b", + target, + 0)); + + git_oid_fromstr(&existing_object_id, tag2_id); + must_be_true(git_oid_cmp(&object_id, &existing_object_id) == 0); + + git_object_close(target); + + git_repository_free(repo); +END_TEST + +BEGIN_TEST(delete0, "Delete an already existing tag") git_repository *repo; git_reference *ref_tag; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - must_pass(git_tag_delete(repo,"e90810b")); + must_pass(git_tag_delete(repo, "e90810b")); must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b")); close_temp_repo(repo); - END_TEST BEGIN_SUITE(tag) ADD_TEST(read0); ADD_TEST(read1); + ADD_TEST(read2); + ADD_TEST(write0); - ADD_TEST(write1); ADD_TEST(write2); ADD_TEST(write3); ADD_TEST(write4); + ADD_TEST(write5); + + ADD_TEST(delete0); + END_SUITE diff --git a/vendor/libgit2/tests/t09-tree.c b/vendor/libgit2/tests/t09-tree.c index af992fdb3..543aea8d4 100644 --- a/vendor/libgit2/tests/t09-tree.c +++ b/vendor/libgit2/tests/t09-tree.c @@ -71,7 +71,7 @@ BEGIN_TEST(read0, "acces randomly the entries on a loaded tree") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id, tree_oid); + git_oid_fromstr(&id, tree_oid); must_pass(git_tree_lookup(&tree, repo, &id)); @@ -81,7 +81,7 @@ BEGIN_TEST(read0, "acces randomly the entries on a loaded tree") must_be_true(git_tree_entry_byindex(tree, 0) != NULL); must_be_true(git_tree_entry_byindex(tree, 2) != NULL); must_be_true(git_tree_entry_byindex(tree, 3) == NULL); - must_be_true(git_tree_entry_byindex(tree, -1) == NULL); + must_be_true(git_tree_entry_byindex(tree, (unsigned int)-1) == NULL); git_tree_close(tree); git_repository_free(repo); @@ -96,7 +96,7 @@ BEGIN_TEST(read1, "read a tree from the repository") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id, tree_oid); + git_oid_fromstr(&id, tree_oid); must_pass(git_tree_lookup(&tree, repo, &id)); @@ -104,9 +104,11 @@ BEGIN_TEST(read1, "read a tree from the repository") /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */ must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_TREE) == 0); + must_be_true(obj != NULL); git_object_close(obj); + obj = NULL; must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_BLOB) == GIT_EINVALIDTYPE); - git_object_close(obj); + must_be_true(obj == NULL); entry = git_tree_entry_byname(tree, "README"); must_be_true(entry != NULL); @@ -114,6 +116,7 @@ BEGIN_TEST(read1, "read a tree from the repository") must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0); must_pass(git_tree_entry_2object(&obj, repo, entry)); + must_be_true(obj != NULL); git_object_close(obj); git_tree_close(tree); @@ -143,13 +146,18 @@ BEGIN_TEST(write2, "write a tree from a memory") git_oid id, bid, rid, id2; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id, first_tree); - git_oid_mkstr(&id2, second_tree); - git_oid_mkstr(&bid, blob_oid); + git_oid_fromstr(&id, first_tree); + git_oid_fromstr(&id2, second_tree); + git_oid_fromstr(&bid, blob_oid); //create a second tree from first tree using `git_treebuilder_insert` on REPOSITORY_FOLDER. must_pass(git_tree_lookup(&tree, repo, &id)); must_pass(git_treebuilder_create(&builder, tree)); + + must_fail(git_treebuilder_insert(NULL, builder, "", &bid, 0100644)); + must_fail(git_treebuilder_insert(NULL, builder, "/", &bid, 0100644)); + must_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt", &bid, 0100644)); + must_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644)); must_pass(git_treebuilder_write(&rid,repo,builder)); @@ -168,10 +176,10 @@ BEGIN_TEST(write3, "write a hierarchical tree from a memory") git_oid id_hiearar; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - git_oid_mkstr(&id, first_tree); - git_oid_mkstr(&id2, second_tree); - git_oid_mkstr(&id3, third_tree); - git_oid_mkstr(&bid, blob_oid); + git_oid_fromstr(&id, first_tree); + git_oid_fromstr(&id2, second_tree); + git_oid_fromstr(&id3, third_tree); + git_oid_fromstr(&bid, blob_oid); //create subtree must_pass(git_treebuilder_create(&builder, NULL)); diff --git a/vendor/libgit2/tests/t10-refs.c b/vendor/libgit2/tests/t10-refs.c index ee006a8ce..65fbdd69f 100644 --- a/vendor/libgit2/tests/t10-refs.c +++ b/vendor/libgit2/tests/t10-refs.c @@ -27,6 +27,9 @@ #include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" + static const char *loose_tag_ref_name = "refs/tags/e90810b"; static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist"; @@ -34,7 +37,7 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference") git_repository *repo; git_reference *reference; git_object *object; - char ref_name_from_tag_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char ref_name_from_tag_name[GIT_REFNAME_MAX]; must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); @@ -48,7 +51,7 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference") must_be_true(git_object_type(object) == GIT_OBJ_TAG); /* Ensure the name of the tag matches the name of the reference */ - git__joinpath(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)); + git_path_join(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)); must_be_true(strcmp(ref_name_from_tag_name, loose_tag_ref_name) == 0); git_object_close(object); @@ -89,7 +92,7 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference") must_be_true(object != NULL); must_be_true(git_object_type(object) == GIT_OBJ_COMMIT); - git_oid_mkstr(&id, current_master_tip); + git_oid_fromstr(&id, current_master_tip); must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0); git_object_close(object); @@ -116,7 +119,7 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference") must_be_true(object != NULL); must_be_true(git_object_type(object) == GIT_OBJ_COMMIT); - git_oid_mkstr(&id, current_master_tip); + git_oid_fromstr(&id, current_master_tip); must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0); git_object_close(object); @@ -204,15 +207,15 @@ BEGIN_TEST(create0, "create a new symbolic reference") const char *new_head_tracker = "another-head-tracker"; - git_oid_mkstr(&id, current_master_tip); + git_oid_fromstr(&id, current_master_tip); must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Retrieve the physical path to the symbolic ref for further cleaning */ - git__joinpath(ref_path, repo->path_repository, new_head_tracker); + git_path_join(ref_path, repo->path_repository, new_head_tracker); /* Create and write the new symbolic reference */ - must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target)); + must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0)); /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); @@ -247,12 +250,12 @@ BEGIN_TEST(create1, "create a deep symbolic reference") const char *new_head_tracker = "deep/rooted/tracker"; - git_oid_mkstr(&id, current_master_tip); + git_oid_fromstr(&id, current_master_tip); must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - git__joinpath(ref_path, repo->path_repository, new_head_tracker); - must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target)); + git_path_join(ref_path, repo->path_repository, new_head_tracker); + must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0)); must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); @@ -268,15 +271,15 @@ BEGIN_TEST(create2, "create a new OID reference") const char *new_head = "refs/heads/new-head"; - git_oid_mkstr(&id, current_master_tip); + git_oid_fromstr(&id, current_master_tip); must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Retrieve the physical path to the symbolic ref for further cleaning */ - git__joinpath(ref_path, repo->path_repository, new_head); + git_path_join(ref_path, repo->path_repository, new_head); /* Create and write the new object id reference */ - must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id)); + must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id, 0)); /* Ensure the reference can be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, new_head)); @@ -305,12 +308,12 @@ BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unkn const char *new_head = "refs/heads/new-head"; - git_oid_mkstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); + git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); /* Create and write the new object id reference */ - must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id)); + must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id, 0)); /* Ensure the reference can't be looked-up... */ must_fail(git_reference_lookup(&looked_up_ref, repo, new_head)); @@ -329,16 +332,16 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* The target needds to exist and we need to check the name has changed */ - must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name)); - must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_branch_name)); + must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_branch_name, 0)); /* Ensure it points to the right place*/ must_pass(git_reference_lookup(&ref, repo, ref_name)); must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC); must_be_true(!strcmp(git_reference_target(ref), ref_branch_name)); /* Ensure we can't create it unless we force it to */ - must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); - must_pass(git_reference_create_symbolic_f(&ref, repo, ref_name, ref_master_name)); + must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -360,15 +363,15 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") git_oid_cpy(&id, git_reference_oid(ref)); /* Create it */ - must_pass(git_reference_create_oid(&ref, repo, ref_name, &id)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); must_pass(git_reference_lookup(&ref, repo, ref_test_name)); must_be_true(ref->type & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); /* Ensure we can't overwrite unless we force it */ - must_fail(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_pass(git_reference_create_oid_f(&ref, repo, ref_name, &id)); + must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1)); /* Ensure it has been overwritten */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -388,9 +391,9 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli must_be_true(ref->type & GIT_REF_OID); git_oid_cpy(&id, git_reference_oid(ref)); - must_pass(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); - must_pass(git_reference_create_symbolic_f(&ref, repo, ref_name, ref_master_name)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -412,10 +415,10 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object git_oid_cpy(&id, git_reference_oid(ref)); /* Create the symbolic ref */ - must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name)); + must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0)); /* It shouldn't overwrite unless we tell it to */ - must_fail(git_reference_create_oid(&ref, repo, ref_name, &id)); - must_pass(git_reference_create_oid_f(&ref, repo, ref_name, &id)); + must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0)); + must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1)); /* Ensure it points to the right place */ must_pass(git_reference_lookup(&ref, repo, ref_name)); @@ -431,9 +434,9 @@ BEGIN_TEST(pack0, "create a packfile for an empty folder") const int mode = 0755; /* or 0777 ? */ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - - git__joinpath_n(temp_path, 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir"); - must_pass(gitfo_mkdir_recurs(temp_path, mode)); + + git_path_join_n(temp_path, 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir"); + must_pass(git_futils_mkdir_r(temp_path, mode)); must_pass(git_reference_packall(repo)); @@ -446,12 +449,12 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo") char temp_path[GIT_PATH_MAX]; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); - + /* Ensure a known loose ref can be looked up */ must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name)); must_be_true((reference->type & GIT_REF_PACKED) == 0); must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); - + /* * We are now trying to pack also a loose reference * called `points_to_blob`, to make sure we can properly @@ -460,8 +463,8 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo") must_pass(git_reference_packall(repo)); /* Ensure the packed-refs file exists */ - git__joinpath(temp_path, repo->path_repository, GIT_PACKEDREFS_FILE); - must_pass(gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, GIT_PACKEDREFS_FILE); + must_pass(git_futils_exists(temp_path)); /* Ensure the known ref can still be looked up but is now packed */ must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name)); @@ -469,8 +472,8 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo") must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0); /* Ensure the known ref has been removed from the loose folder structure */ - git__joinpath(temp_path, repo->path_repository, loose_tag_ref_name); - must_pass(!gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, loose_tag_ref_name); + must_pass(!git_futils_exists(temp_path)); close_temp_repo(repo); END_TEST @@ -484,8 +487,8 @@ BEGIN_TEST(rename0, "rename a loose reference") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Ensure the ref doesn't exist on the file system */ - git__joinpath(temp_path, repo->path_repository, new_name); - must_pass(!gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, new_name); + must_pass(!git_futils_exists(temp_path)); /* Retrieval of the reference to rename */ must_pass(git_reference_lookup(&looked_up_ref, repo, loose_tag_ref_name)); @@ -494,7 +497,7 @@ BEGIN_TEST(rename0, "rename a loose reference") must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); /* Now that the reference is renamed... */ - must_pass(git_reference_rename(looked_up_ref, new_name)); + must_pass(git_reference_rename(looked_up_ref, new_name, 0)); must_be_true(!strcmp(looked_up_ref->name, new_name)); /* ...It can't be looked-up with the old name... */ @@ -509,8 +512,8 @@ BEGIN_TEST(rename0, "rename a loose reference") must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); /* ...and the ref can be found in the file system */ - git__joinpath(temp_path, repo->path_repository, new_name); - must_pass(gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, new_name); + must_pass(git_futils_exists(temp_path)); close_temp_repo(repo); END_TEST @@ -524,8 +527,8 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)") must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Ensure the ref doesn't exist on the file system */ - git__joinpath(temp_path, repo->path_repository, packed_head_name); - must_pass(!gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, packed_head_name); + must_pass(!git_futils_exists(temp_path)); /* The reference can however be looked-up... */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); @@ -534,7 +537,7 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)") must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0); /* Now that the reference is renamed... */ - must_pass(git_reference_rename(looked_up_ref, brand_new_name)); + must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); must_be_true(!strcmp(looked_up_ref->name, brand_new_name)); /* ...It can't be looked-up with the old name... */ @@ -549,8 +552,8 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)") must_be_true((looked_up_ref->type & GIT_REF_PACKED) == 0); /* ...and the ref now happily lives in the file system */ - git__joinpath(temp_path, repo->path_repository, brand_new_name); - must_pass(gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, brand_new_name); + must_pass(git_futils_exists(temp_path)); close_temp_repo(repo); END_TEST @@ -564,8 +567,8 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Ensure the other reference exists on the file system */ - git__joinpath(temp_path, repo->path_repository, packed_test_head_name); - must_pass(gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, packed_test_head_name); + must_pass(git_futils_exists(temp_path)); /* Lookup the other reference */ must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name)); @@ -580,7 +583,7 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference must_be_true((looked_up_ref->type & GIT_REF_PACKED) != 0); /* Now that the reference is renamed... */ - must_pass(git_reference_rename(looked_up_ref, brand_new_name)); + must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0)); /* Lookup the other reference */ must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name)); @@ -589,7 +592,7 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference must_be_true((another_looked_up_ref->type & GIT_REF_PACKED) == 0); /* Ensure the other ref still exists on the file system */ - must_pass(gitfo_exists(temp_path)); + must_pass(git_futils_exists(temp_path)); close_temp_repo(repo); END_TEST @@ -604,7 +607,7 @@ BEGIN_TEST(rename3, "can not rename a reference with the name of an existing ref must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); /* Can not be renamed to the name of another existing reference. */ - must_fail(git_reference_rename(looked_up_ref, packed_test_head_name)); + must_fail(git_reference_rename(looked_up_ref, packed_test_head_name, 0)); /* Failure to rename it hasn't corrupted its state */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); @@ -623,10 +626,10 @@ BEGIN_TEST(rename4, "can not rename a reference with an invalid name") must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name)); /* Can not be renamed with an invalid name. */ - must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.")); + must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0)); /* Can not be renamed outside of the refs hierarchy. */ - must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you")); + must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you", 0)); /* Failure to rename it hasn't corrupted its state */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name)); @@ -635,21 +638,115 @@ BEGIN_TEST(rename4, "can not rename a reference with an invalid name") close_temp_repo(repo); END_TEST -BEGIN_TEST(rename5, "can force-rename a reference with the name of an existing reference") +BEGIN_TEST(rename5, "can force-rename a packed reference with the name of an existing loose and packed reference") git_reference *looked_up_ref; git_repository *repo; + git_oid oid; must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* An existing reference... */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); + git_oid_cpy(&oid, git_reference_oid(looked_up_ref)); - /* Can not be renamed to the name of another existing reference. */ - must_pass(git_reference_rename_f(looked_up_ref, packed_test_head_name)); + /* Can be force-renamed to the name of another existing reference. */ + must_pass(git_reference_rename(looked_up_ref, packed_test_head_name, 1)); /* Check we actually renamed it */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name)); must_be_true(!strcmp(looked_up_ref->name, packed_test_head_name)); + must_be_true(!git_oid_cmp(&oid, git_reference_oid(looked_up_ref))); + + /* And that the previous one doesn't exist any longer */ + must_fail(git_reference_lookup(&looked_up_ref, repo, packed_head_name)); + + close_temp_repo(repo); +END_TEST + +BEGIN_TEST(rename6, "can force-rename a loose reference with the name of an existing loose reference") + git_reference *looked_up_ref; + git_repository *repo; + git_oid oid; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + /* An existing reference... */ + must_pass(git_reference_lookup(&looked_up_ref, repo, "refs/heads/br2")); + git_oid_cpy(&oid, git_reference_oid(looked_up_ref)); + + /* Can be force-renamed to the name of another existing reference. */ + must_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1)); + + /* Check we actually renamed it */ + must_pass(git_reference_lookup(&looked_up_ref, repo, "refs/heads/test")); + must_be_true(!strcmp(looked_up_ref->name, "refs/heads/test")); + must_be_true(!git_oid_cmp(&oid, git_reference_oid(looked_up_ref))); + + /* And that the previous one doesn't exist any longer */ + must_fail(git_reference_lookup(&looked_up_ref, repo, "refs/heads/br2")); + + close_temp_repo(repo); +END_TEST + +static const char *ref_one_name = "refs/heads/one/branch"; +static const char *ref_one_name_new = "refs/heads/two/branch"; +static const char *ref_two_name = "refs/heads/two"; + +BEGIN_TEST(rename7, "can not overwrite name of existing reference") + git_reference *ref, *ref_one, *ref_one_new, *ref_two; + git_repository *repo; + git_oid id; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + must_pass(git_reference_lookup(&ref, repo, ref_master_name)); + must_be_true(ref->type & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + must_pass(git_reference_create_oid(&ref_one, repo, ref_one_name, &id, 0)); + must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0)); + + /* Pack everything */ + must_pass(git_reference_packall(repo)); + + /* Attempt to create illegal reference */ + must_fail(git_reference_create_oid(&ref_one_new, repo, ref_one_name_new, &id, 0)); + + /* Illegal reference couldn't be created so this is supposed to fail */ + must_fail(git_reference_lookup(&ref_one_new, repo, ref_one_name_new)); + + close_temp_repo(repo); +END_TEST + +static const char *ref_two_name_new = "refs/heads/two/two"; + +BEGIN_TEST(rename8, "can be renamed to a new name prefixed with the old name") + git_reference *ref, *ref_two, *looked_up_ref; + git_repository *repo; + git_oid id; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + must_pass(git_reference_lookup(&ref, repo, ref_master_name)); + must_be_true(ref->type & GIT_REF_OID); + + git_oid_cpy(&id, git_reference_oid(ref)); + + /* Create loose references */ + must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0)); + + /* An existing reference... */ + must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name)); + + /* Can be rename to a new name starting with the old name. */ + must_pass(git_reference_rename(looked_up_ref, ref_two_name_new, 0)); + + /* Check we actually renamed it */ + must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new)); + must_be_true(!strcmp(looked_up_ref->name, ref_two_name_new)); + must_fail(git_reference_lookup(&looked_up_ref, repo, ref_two_name)); close_temp_repo(repo); END_TEST @@ -662,8 +759,8 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Ensure the loose reference exists on the file system */ - git__joinpath(temp_path, repo->path_repository, packed_test_head_name); - must_pass(gitfo_exists(temp_path)); + git_path_join(temp_path, repo->path_repository, packed_test_head_name); + must_pass(git_futils_exists(temp_path)); /* Lookup the reference */ must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name)); @@ -678,7 +775,7 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name)); /* Ensure the loose reference doesn't exist any longer on the file system */ - must_pass(!gitfo_exists(temp_path)); + must_pass(!git_futils_exists(temp_path)); close_temp_repo(repo); END_TEST @@ -686,12 +783,12 @@ END_TEST static int ensure_refname_normalized(int is_oid_ref, const char *input_refname, const char *expected_refname) { int error = GIT_SUCCESS; - char buffer_out[GIT_PATH_MAX]; + char buffer_out[GIT_REFNAME_MAX]; if (is_oid_ref) - error = git_reference__normalize_name_oid(buffer_out, input_refname); + error = git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname); else - error = git_reference__normalize_name(buffer_out, input_refname); + error = git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname); if (error < GIT_SUCCESS) return error; @@ -772,7 +869,7 @@ BEGIN_TEST(normalize2, "tests borrowed from JGit") /* NoAsciiControlCharacters */ { char c; - char buffer[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; + char buffer[GIT_REFNAME_MAX]; for (c = '\1'; c < ' '; c++) { strncpy(buffer, "refs/heads/mast", 15); strncpy(buffer + 15, (const char *)&c, 1); @@ -881,7 +978,7 @@ BEGIN_TEST(list0, "try to list all the references in our test repo") /* We have exactly 8 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - must_be_true(ref_list.count == 8); + must_be_true(ref_list.count == 8); git_strarray_free(&ref_list); git_repository_free(repo); @@ -893,12 +990,118 @@ BEGIN_TEST(list1, "try to list only the symbolic references") must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_listall(&ref_list, repo, GIT_REF_SYMBOLIC)); - must_be_true(ref_list.count == 0); /* no symrefs in the test repo */ + must_be_true(ref_list.count == 0); /* no symrefs in the test repo */ git_strarray_free(&ref_list); git_repository_free(repo); END_TEST +static const char *new_ref = "refs/heads/test-reflog"; +#define commit_msg "commit: bla bla" + +static int assert_signature(git_signature *expected, git_signature *actual) +{ + if (actual == NULL) + return GIT_ERROR; + + if (strcmp(expected->name, actual->name) != 0) + return GIT_ERROR; + + if (strcmp(expected->email, actual->email) != 0) + return GIT_ERROR; + + if (expected->when.offset != actual->when.offset) + return GIT_ERROR; + + if (expected->when.time != actual->when.time) + return GIT_ERROR; + + return GIT_SUCCESS; +} + +BEGIN_TEST(reflog0, "write a reflog for a given reference and ensure it can be read back") + git_repository *repo, *repo2; + git_reference *ref, *lookedup_ref; + git_oid oid; + git_signature *committer; + git_reflog *reflog; + git_reflog_entry *entry; + char oid_str[GIT_OID_HEXSZ+1]; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&oid, current_master_tip); + must_pass(git_reference_create_oid(&ref, repo, new_ref, &oid, 0)); + must_pass(git_reference_lookup(&ref, repo, new_ref)); + + must_pass(git_signature_now(&committer, "foo", "foo@bar")); + + must_pass(git_reflog_write(ref, NULL, committer, NULL)); + must_fail(git_reflog_write(ref, NULL, committer, "no ancestor NULL for an existing reflog")); + must_fail(git_reflog_write(ref, NULL, committer, "no\nnewline")); + must_pass(git_reflog_write(ref, &oid, committer, commit_msg)); + + git_repository_free(repo); + + /* Reopen a new instance of the repository */ + must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER)); + + /* Lookup the preivously created branch */ + must_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref)); + + /* Read and parse the reflog for this branch */ + must_pass(git_reflog_read(&reflog, lookedup_ref)); + must_be_true(reflog->entries.length == 2); + + entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 0); + must_pass(assert_signature(committer, entry->committer)); + git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old); + must_be_true(strcmp("0000000000000000000000000000000000000000", oid_str) == 0); + git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur); + must_be_true(strcmp(current_master_tip, oid_str) == 0); + must_be_true(entry->msg == NULL); + + entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 1); + must_pass(assert_signature(committer, entry->committer)); + git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old); + must_be_true(strcmp(current_master_tip, oid_str) == 0); + git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur); + must_be_true(strcmp(current_master_tip, oid_str) == 0); + must_be_true(strcmp(commit_msg, entry->msg) == 0); + + git_signature_free(committer); + git_reflog_free(reflog); + close_temp_repo(repo2); +END_TEST + +BEGIN_TEST(reflog1, "avoid writing an obviously wrong reflog") + git_repository *repo; + git_reference *ref; + git_oid oid; + git_signature *committer; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + + /* Create a new branch pointing at the HEAD */ + git_oid_fromstr(&oid, current_master_tip); + must_pass(git_reference_create_oid(&ref, repo, new_ref, &oid, 0)); + must_pass(git_reference_lookup(&ref, repo, new_ref)); + + must_pass(git_signature_now(&committer, "foo", "foo@bar")); + + /* Write the reflog for the new branch */ + must_pass(git_reflog_write(ref, NULL, committer, NULL)); + + /* Try to update the reflog with wrong information: + * It's no new reference, so the ancestor OID cannot + * be NULL. */ + must_fail(git_reflog_write(ref, NULL, committer, NULL)); + + git_signature_free(committer); + + close_temp_repo(repo); +END_TEST BEGIN_SUITE(refs) ADD_TEST(readtag0); @@ -935,8 +1138,15 @@ BEGIN_SUITE(refs) ADD_TEST(rename3); ADD_TEST(rename4); ADD_TEST(rename5); + ADD_TEST(rename6); + ADD_TEST(rename7); + ADD_TEST(rename8); ADD_TEST(delete0); + ADD_TEST(list0); ADD_TEST(list1); + + ADD_TEST(reflog0); + ADD_TEST(reflog1); END_SUITE diff --git a/vendor/libgit2/tests/t12-repo.c b/vendor/libgit2/tests/t12-repo.c index 70dba4255..cc8942f40 100644 --- a/vendor/libgit2/tests/t12-repo.c +++ b/vendor/libgit2/tests/t12-repo.c @@ -104,10 +104,10 @@ static int ensure_repository_init( char path_odb[GIT_PATH_MAX]; git_repository *repo; - if (gitfo_isdir(working_directory) == GIT_SUCCESS) + if (git_futils_isdir(working_directory) == GIT_SUCCESS) return GIT_ERROR; - git__joinpath(path_odb, expected_path_repository, GIT_OBJECTS_DIR); + git_path_join(path_odb, expected_path_repository, GIT_OBJECTS_DIR); if (git_repository_init(&repo, working_directory, repository_kind) < GIT_SUCCESS) return GIT_ERROR; @@ -126,24 +126,36 @@ static int ensure_repository_init( if (repo->path_index != NULL || expected_path_index != NULL) { if (git__suffixcmp(repo->path_index, expected_path_index) != 0) goto cleanup; - } + +#ifdef GIT_WIN32 + if ((GetFileAttributes(repo->path_repository) & FILE_ATTRIBUTE_HIDDEN) == 0) + goto cleanup; +#endif + + if (git_repository_is_bare(repo) == 1) + goto cleanup; + } else if (git_repository_is_bare(repo) == 0) + goto cleanup; + + if (git_repository_is_empty(repo) == 0) + goto cleanup; git_repository_free(repo); - rmdir_recurs(working_directory); + git_futils_rmdir_r(working_directory, 1); return GIT_SUCCESS; cleanup: git_repository_free(repo); - rmdir_recurs(working_directory); + git_futils_rmdir_r(working_directory, 1); return GIT_ERROR; } BEGIN_TEST(init0, "initialize a standard repo") char path_index[GIT_PATH_MAX], path_repository[GIT_PATH_MAX]; - git__joinpath(path_repository, TEMP_REPO_FOLDER, GIT_DIR); - git__joinpath(path_index, path_repository, GIT_INDEX_FILE); + git_path_join(path_repository, TEMP_REPO_FOLDER, GIT_DIR); + git_path_join(path_index, path_repository, GIT_INDEX_FILE); must_pass(ensure_repository_init(TEMP_REPO_FOLDER, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER)); must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, STANDARD_REPOSITORY, path_index, path_repository, TEMP_REPO_FOLDER)); @@ -152,7 +164,7 @@ END_TEST BEGIN_TEST(init1, "initialize a bare repo") char path_repository[GIT_PATH_MAX]; - git__joinpath(path_repository, TEMP_REPO_FOLDER, ""); + git_path_join(path_repository, TEMP_REPO_FOLDER, ""); must_pass(ensure_repository_init(TEMP_REPO_FOLDER, BARE_REPOSITORY, NULL, path_repository, NULL)); must_pass(ensure_repository_init(TEMP_REPO_FOLDER_NS, BARE_REPOSITORY, NULL, path_repository, NULL)); @@ -164,10 +176,10 @@ BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping const int mode = 0755; /* or 0777 ? */ git_repository* repo; - must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir))); + must_pass(p_getcwd(current_workdir, sizeof(current_workdir))); - git__joinpath(path_repository, TEMP_REPO_FOLDER, "a/b/c/"); - must_pass(gitfo_mkdir_recurs(path_repository, mode)); + git_path_join(path_repository, TEMP_REPO_FOLDER, "a/b/c/"); + must_pass(git_futils_mkdir_r(path_repository, mode)); must_pass(chdir(path_repository)); @@ -181,11 +193,10 @@ BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping git_repository_free(repo); must_pass(chdir(current_workdir)); - rmdir_recurs(TEMP_REPO_FOLDER); + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); END_TEST -#define EMPTY_BARE_REPOSITORY_NAME "empty_bare.git" -#define EMPTY_BARE_REPOSITORY_FOLDER TEST_RESOURCES "/" EMPTY_BARE_REPOSITORY_NAME "/" +#define EMPTY_BARE_REPOSITORY_FOLDER TEST_RESOURCES "/empty_bare.git/" BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git") git_repository *repo; @@ -194,30 +205,27 @@ BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git" must_pass(remove_placeholders(TEMP_REPO_FOLDER, "dummy-marker.txt")); must_pass(git_repository_open(&repo, TEMP_REPO_FOLDER)); - must_be_true(git_repository_path(repo) != NULL); - must_be_true(git_repository_workdir(repo) == NULL); + must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL); + must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) == NULL); git_repository_free(repo); - must_pass(rmdir_recurs(TEMP_REPO_FOLDER)); + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); END_TEST -#define SOURCE_EMPTY_REPOSITORY_NAME "empty_standard_repo/.gitted" -#define EMPTY_REPOSITORY_NAME "empty_standard_repo/.git" -#define EMPTY_REPOSITORY_FOLDER TEST_RESOURCES "/" SOURCE_EMPTY_REPOSITORY_NAME "/" -#define DEST_REPOSITORY_FOLDER TEMP_REPO_FOLDER DOT_GIT "/" +#define EMPTY_REPOSITORY_FOLDER TEST_RESOURCES "/empty_standard_repo/.gitted/" BEGIN_TEST(open1, "Open a standard repository that has just been initialized by git") git_repository *repo; - must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, DEST_REPOSITORY_FOLDER)); - must_pass(remove_placeholders(DEST_REPOSITORY_FOLDER, "dummy-marker.txt")); + must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER)); + must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt")); - must_pass(git_repository_open(&repo, DEST_REPOSITORY_FOLDER)); - must_be_true(git_repository_path(repo) != NULL); - must_be_true(git_repository_workdir(repo) != NULL); + must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER)); + must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL); + must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) != NULL); git_repository_free(repo); - must_pass(rmdir_recurs(TEMP_REPO_FOLDER)); + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); END_TEST @@ -230,14 +238,14 @@ BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of t git_repository* repo; /* Setup the repository to open */ - must_pass(gitfo_getcwd(current_workdir, sizeof(current_workdir))); + must_pass(p_getcwd(current_workdir, sizeof(current_workdir))); strcpy(path_repository, current_workdir); - git__joinpath_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git"); + git_path_join_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git"); must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository)); /* Change the current working directory */ - git__joinpath(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/"); - must_pass(gitfo_mkdir_recurs(new_current_workdir, mode)); + git_path_join(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/"); + must_pass(git_futils_mkdir_r(new_current_workdir, mode)); must_pass(chdir(new_current_workdir)); must_pass(git_repository_open(&repo, "../../d/e.git")); @@ -245,7 +253,7 @@ BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of t git_repository_free(repo); must_pass(chdir(current_workdir)); - rmdir_recurs(TEMP_REPO_FOLDER); + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); END_TEST BEGIN_TEST(empty0, "test if a repository is empty or not") @@ -261,6 +269,183 @@ BEGIN_TEST(empty0, "test if a repository is empty or not") git_repository_free(repo_empty); END_TEST +BEGIN_TEST(detached0, "test if HEAD is detached") + git_repository *repo; + git_reference *ref; + git_oid oid; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + must_be_true(git_repository_head_detached(repo) == 0); + + /* detach the HEAD */ + git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"); + must_pass(git_reference_create_oid(&ref, repo, "HEAD", &oid, 1)); + must_be_true(git_repository_head_detached(repo) == 1); + + /* take the reop back to it's original state */ + must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/master", 1)); + must_be_true(git_repository_head_detached(repo) == 0); + + git_repository_free(repo); +END_TEST + +BEGIN_TEST(orphan0, "test if HEAD is orphan") + git_repository *repo; + git_reference *ref; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + must_be_true(git_repository_head_orphan(repo) == 0); + + /* orphan HEAD */ + must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/orphan", 1)); + must_be_true(git_repository_head_orphan(repo) == 1); + + /* take the reop back to it's original state */ + must_pass(git_reference_create_symbolic(&ref, repo, "HEAD", "refs/heads/master", 1)); + must_be_true(git_repository_head_orphan(repo) == 0); + + git_repository_free(repo); +END_TEST + +#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git" + +#define SUB_REPOSITORY_FOLDER_NAME "sub_repo" +#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME +#define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub" +#define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub" +#define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub" + +#define REPOSITORY_ALTERNATE_FOLDER DISCOVER_FOLDER "/alternate_sub_repo" +#define REPOSITORY_ALTERNATE_FOLDER_SUB REPOSITORY_ALTERNATE_FOLDER "/sub" +#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB "/subsub" +#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/subsubsub" + +#define ALTERNATE_MALFORMED_FOLDER1 DISCOVER_FOLDER "/alternate_malformed_repo1" +#define ALTERNATE_MALFORMED_FOLDER2 DISCOVER_FOLDER "/alternate_malformed_repo2" +#define ALTERNATE_MALFORMED_FOLDER3 DISCOVER_FOLDER "/alternate_malformed_repo3" +#define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo" + +static int ensure_repository_discover(const char *start_path, const char *ceiling_dirs, const char *expected_path) +{ + int error; + char found_path[GIT_PATH_MAX]; + + error = git_repository_discover(found_path, sizeof(found_path), start_path, 0, ceiling_dirs); + //across_fs is always 0 as we can't automate the filesystem change tests + + if (error < GIT_SUCCESS) + return error; + + return strcmp(found_path, expected_path) ? GIT_ERROR : GIT_SUCCESS; +} + +static int write_file(const char *path, const char *content) +{ + int error; + git_file file; + + if (git_futils_exists(path) == GIT_SUCCESS) { + error = p_unlink(path); + + if (error < GIT_SUCCESS) + return error; + } + + file = git_futils_creat_withpath(path, 0644); + if (file < GIT_SUCCESS) + return file; + + error = p_write(file, content, strlen(content) * sizeof(char)); + + p_close(file); + + return error; +} + +//no check is performed on ceiling_dirs length, so be sure it's long enough +static int append_ceiling_dir(char *ceiling_dirs, const char *path) +{ + int len = strlen(ceiling_dirs); + int error; + + error = git_path_prettify_dir(ceiling_dirs + len + (len ? 1 : 0), path, NULL); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to append ceiling directory."); + + if (len) + ceiling_dirs[len] = GIT_PATH_LIST_SEPARATOR; + + return GIT_SUCCESS; +} + +BEGIN_TEST(discover0, "test discover") + git_repository *repo; + char ceiling_dirs[GIT_PATH_MAX * 2] = ""; + char repository_path[GIT_PATH_MAX]; + char sub_repository_path[GIT_PATH_MAX]; + char found_path[GIT_PATH_MAX]; + int mode = 0755; + + git_futils_mkdir_r(DISCOVER_FOLDER, mode); + must_pass(append_ceiling_dir(ceiling_dirs, TEMP_REPO_FOLDER)); + + must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO); + + must_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1)); + must_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs)); + git_repository_free(repo); + + must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0)); + must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); + must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + + must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode)); + must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path)); + + must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode)); + must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT)); + must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT)); + must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../")); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path)); + + must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode)); + must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:")); + must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode)); + must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:")); + must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode)); + must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n")); + must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode)); + must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist")); + must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs)); + + must_pass(append_ceiling_dir(ceiling_dirs, SUB_REPOSITORY_FOLDER)); + //this must pass as ceiling_directories cannot predent the current + //working directory to be checked + must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); + must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); + + //.gitfile redirection should not be affected by ceiling directories + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path)); + must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path)); + + must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1)); + git_repository_free(repo); +END_TEST + BEGIN_SUITE(repository) ADD_TEST(odb0); ADD_TEST(odb1); @@ -271,5 +456,8 @@ BEGIN_SUITE(repository) ADD_TEST(open1); ADD_TEST(open2); ADD_TEST(empty0); + ADD_TEST(detached0); + ADD_TEST(orphan0); + ADD_TEST(discover0); END_SUITE diff --git a/vendor/libgit2/tests/t15-config.c b/vendor/libgit2/tests/t15-config.c new file mode 100644 index 000000000..05de25f8c --- /dev/null +++ b/vendor/libgit2/tests/t15-config.c @@ -0,0 +1,325 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "test_lib.h" +#include "test_helpers.h" + +#include +#include "filebuf.h" + +#define CONFIG_BASE TEST_RESOURCES "/config" + +/* + * This one is so we know the code isn't completely broken + */ +BEGIN_TEST(config0, "read a simple configuration") + git_config *cfg; + int i; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config0")); + must_pass(git_config_get_int(cfg, "core.repositoryformatversion", &i)); + must_be_true(i == 0); + must_pass(git_config_get_bool(cfg, "core.filemode", &i)); + must_be_true(i == 1); + must_pass(git_config_get_bool(cfg, "core.bare", &i)); + must_be_true(i == 0); + must_pass(git_config_get_bool(cfg, "core.logallrefupdates", &i)); + must_be_true(i == 1); + + git_config_free(cfg); +END_TEST + +/* + * [this "that"] and [this "That] are different namespaces. Make sure + * each returns the correct one. + */ +BEGIN_TEST(config1, "case sensitivity") + git_config *cfg; + int i; + const char *str; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config1")); + + must_pass(git_config_get_string(cfg, "this.that.other", &str)); + must_be_true(!strcmp(str, "true")); + must_pass(git_config_get_string(cfg, "this.That.other", &str)); + must_be_true(!strcmp(str, "yes")); + + must_pass(git_config_get_bool(cfg, "this.that.other", &i)); + must_be_true(i == 1); + must_pass(git_config_get_bool(cfg, "this.That.other", &i)); + must_be_true(i == 1); + + /* This one doesn't exist */ + must_fail(git_config_get_bool(cfg, "this.thaT.other", &i)); + + git_config_free(cfg); +END_TEST + +/* + * If \ is the last non-space character on the line, we read the next + * one, separating each line with SP. + */ +BEGIN_TEST(config2, "parse a multiline value") + git_config *cfg; + const char *str; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config2")); + + must_pass(git_config_get_string(cfg, "this.That.and", &str)); + must_be_true(!strcmp(str, "one one one two two three three")); + + git_config_free(cfg); +END_TEST + +/* + * This kind of subsection declaration is case-insensitive + */ +BEGIN_TEST(config3, "parse a [section.subsection] header") + git_config *cfg; + const char *str; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config3")); + + must_pass(git_config_get_string(cfg, "section.subsection.var", &str)); + must_be_true(!strcmp(str, "hello")); + + /* Avoid a false positive */ + str = "nohello"; + must_pass(git_config_get_string(cfg, "section.subSectIon.var", &str)); + must_be_true(!strcmp(str, "hello")); + + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config4, "a variable name on its own is valid") + git_config *cfg; +const char *str; +int i; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config4")); + + must_pass(git_config_get_string(cfg, "some.section.variable", &str)); + must_be_true(str == NULL); + + must_pass(git_config_get_bool(cfg, "some.section.variable", &i)); + must_be_true(i == 1); + + + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config5, "test number suffixes") + git_config *cfg; + long int i; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config5")); + + must_pass(git_config_get_long(cfg, "number.simple", &i)); + must_be_true(i == 1); + + must_pass(git_config_get_long(cfg, "number.k", &i)); + must_be_true(i == 1 * 1024); + + must_pass(git_config_get_long(cfg, "number.kk", &i)); + must_be_true(i == 1 * 1024); + + must_pass(git_config_get_long(cfg, "number.m", &i)); + must_be_true(i == 1 * 1024 * 1024); + + must_pass(git_config_get_long(cfg, "number.mm", &i)); + must_be_true(i == 1 * 1024 * 1024); + + must_pass(git_config_get_long(cfg, "number.g", &i)); + must_be_true(i == 1 * 1024 * 1024 * 1024); + + must_pass(git_config_get_long(cfg, "number.gg", &i)); + must_be_true(i == 1 * 1024 * 1024 * 1024); + + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config6, "test blank lines") + git_config *cfg; + int i; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config6")); + + must_pass(git_config_get_bool(cfg, "valid.subsection.something", &i)); + must_be_true(i == 1); + + must_pass(git_config_get_bool(cfg, "something.else.something", &i)); + must_be_true(i == 0); + + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config7, "test for invalid ext headers") + git_config *cfg; + + must_fail(git_config_open_ondisk(&cfg, CONFIG_BASE "/config7")); + +END_TEST + +BEGIN_TEST(config8, "don't fail on empty files") + git_config *cfg; + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config8")); + + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config9, "replace a value") + git_config *cfg; + int i; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_pass(git_config_set_int(cfg, "core.dummy", 5)); + git_config_free(cfg); + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_pass(git_config_get_int(cfg, "core.dummy", &i)); + must_be_true(i == 5); + git_config_free(cfg); + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_pass(git_config_set_int(cfg, "core.dummy", 1)); + git_config_free(cfg); + +END_TEST + +BEGIN_TEST(config10, "a repo's config overrides the global config") + git_repository *repo; + git_config *cfg; + int version; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, CONFIG_BASE "/.gitconfig", NULL)); + must_pass(git_config_get_int(cfg, "core.repositoryformatversion", &version)); + must_be_true(version == 0); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(config11, "fall back to the global config") + git_repository *repo; + git_config *cfg; + int num; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, CONFIG_BASE "/.gitconfig", NULL)); + must_pass(git_config_get_int(cfg, "core.something", &num)); + must_be_true(num == 2); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(config12, "delete a value") + git_config *cfg; + int i; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_pass(git_config_set_int(cfg, "core.dummy", 5)); + git_config_free(cfg); + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_pass(git_config_delete(cfg, "core.dummy")); + git_config_free(cfg); + + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_be_true(git_config_get_int(cfg, "core.dummy", &i) == GIT_ENOTFOUND); + must_pass(git_config_set_int(cfg, "core.dummy", 1)); + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config13, "can't delete a non-existent value") + git_config *cfg; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config9")); + must_be_true(git_config_delete(cfg, "core.imaginary") == GIT_ENOTFOUND); + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config14, "don't fail horribly if a section header is in the last line") + git_config *cfg; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config10")); + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config15, "add a variable in an existing section") + git_config *cfg; + int i; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config10")); + must_pass(git_config_set_int(cfg, "empty.tmp", 5)); + must_pass(git_config_get_int(cfg, "empty.tmp", &i)); + must_be_true(i == 5); + must_pass(git_config_delete(cfg, "empty.tmp")); + git_config_free(cfg); +END_TEST + +BEGIN_TEST(config16, "add a variable in a new section") + git_config *cfg; + int i; + git_filebuf buf; + + /* By freeing the config, we make sure we flush the values */ + must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config10")); + must_pass(git_config_set_int(cfg, "section.tmp", 5)); + must_pass(git_config_get_int(cfg, "section.tmp", &i)); + must_be_true(i == 5); + must_pass(git_config_delete(cfg, "section.tmp")); + git_config_free(cfg); + + /* As the section wasn't removed, owerwrite the file */ + must_pass(git_filebuf_open(&buf, CONFIG_BASE "/config10", 0)); + must_pass(git_filebuf_write(&buf, "[empty]\n", strlen("[empty]\n"))); + must_pass(git_filebuf_commit(&buf)); +END_TEST + +BEGIN_SUITE(config) + ADD_TEST(config0); + ADD_TEST(config1); + ADD_TEST(config2); + ADD_TEST(config3); + ADD_TEST(config4); + ADD_TEST(config5); + ADD_TEST(config6); + ADD_TEST(config7); + ADD_TEST(config8); + ADD_TEST(config9); + ADD_TEST(config10); + ADD_TEST(config11); + ADD_TEST(config12); + ADD_TEST(config13); + ADD_TEST(config14); + ADD_TEST(config15); + ADD_TEST(config16); +END_SUITE diff --git a/vendor/libgit2/tests/t16-remotes.c b/vendor/libgit2/tests/t16-remotes.c new file mode 100644 index 000000000..4bc2f55d7 --- /dev/null +++ b/vendor/libgit2/tests/t16-remotes.c @@ -0,0 +1,106 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "test_lib.h" +#include "test_helpers.h" + +#include + +BEGIN_TEST(remotes0, "remote parsing works") + git_remote *remote; + git_repository *repo; + git_config *cfg; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, NULL, NULL)); + must_pass(git_remote_get(&remote, cfg, "test")); + must_be_true(!strcmp(git_remote_name(remote), "test")); + must_be_true(!strcmp(git_remote_url(remote), "git://github.com/libgit2/libgit2")); + + git_remote_free(remote); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(refspec0, "remote with refspec works") + git_remote *remote; + git_repository *repo; + git_config *cfg; + const git_refspec *refspec = NULL; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, NULL, NULL)); + must_pass(git_remote_get(&remote, cfg, "test")); + refspec = git_remote_fetchspec(remote); + must_be_true(refspec != NULL); + must_be_true(!strcmp(git_refspec_src(refspec), "refs/heads/*")); + must_be_true(!strcmp(git_refspec_dst(refspec), "refs/remotes/test/*")); + git_remote_free(remote); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(refspec1, "remote fnmatch works as expected") + git_remote *remote; + git_repository *repo; + git_config *cfg; + const git_refspec *refspec = NULL; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, NULL, NULL)); + must_pass(git_remote_get(&remote, cfg, "test")); + refspec = git_remote_fetchspec(remote); + must_be_true(refspec != NULL); + must_pass(git_refspec_src_match(refspec, "refs/heads/master")); + must_pass(git_refspec_src_match(refspec, "refs/heads/multi/level/branch")); + git_remote_free(remote); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_TEST(refspec2, "refspec transform") + git_remote *remote; + git_repository *repo; + git_config *cfg; + const git_refspec *refspec = NULL; + char ref[1024] = {0}; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_config(&cfg, repo, NULL, NULL)); + must_pass(git_remote_get(&remote, cfg, "test")); + refspec = git_remote_fetchspec(remote); + must_be_true(refspec != NULL); + must_pass(git_refspec_transform(ref, sizeof(ref), refspec, "refs/heads/master")); + must_be_true(!strcmp(ref, "refs/remotes/test/master")); + git_remote_free(remote); + git_config_free(cfg); + git_repository_free(repo); +END_TEST + +BEGIN_SUITE(remotes) + ADD_TEST(remotes0) + ADD_TEST(refspec0) + ADD_TEST(refspec1) + ADD_TEST(refspec2) +END_SUITE diff --git a/vendor/libgit2/tests/t17-bufs.c b/vendor/libgit2/tests/t17-bufs.c new file mode 100644 index 000000000..2cbd8c87a --- /dev/null +++ b/vendor/libgit2/tests/t17-bufs.c @@ -0,0 +1,61 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "test_lib.h" +#include "test_helpers.h" + +#include +#include "buffer.h" + +const char *test_string = "Have you seen that? Have you seeeen that??"; + +BEGIN_TEST(buf0, "check that resizing works properly") + git_buf buf = GIT_BUF_INIT; + git_buf_puts(&buf, test_string); + + must_be_true(git_buf_oom(&buf) == 0); + must_be_true(strcmp(git_buf_cstr(&buf), test_string) == 0); + + git_buf_puts(&buf, test_string); + must_be_true(strlen(git_buf_cstr(&buf)) == strlen(test_string) * 2); + git_buf_free(&buf); +END_TEST + +BEGIN_TEST(buf1, "check that printf works properly") + git_buf buf = GIT_BUF_INIT; + + git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23); + must_be_true(git_buf_oom(&buf) == 0); + must_be_true(strcmp(git_buf_cstr(&buf), "shoop da 23 ") == 0); + + git_buf_printf(&buf, "%s %d", "woop", 42); + must_be_true(git_buf_oom(&buf) == 0); + must_be_true(strcmp(git_buf_cstr(&buf), "shoop da 23 woop 42") == 0); + git_buf_free(&buf); +END_TEST + +BEGIN_SUITE(buffers) + ADD_TEST(buf0) + ADD_TEST(buf1) +END_SUITE diff --git a/vendor/libgit2/tests/t18-status.c b/vendor/libgit2/tests/t18-status.c new file mode 100644 index 000000000..c30c541df --- /dev/null +++ b/vendor/libgit2/tests/t18-status.c @@ -0,0 +1,184 @@ +/* + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, + * as published by the Free Software Foundation. + * + * In addition to the permissions in the GNU General Public License, + * the authors give you unlimited permission to link the compiled + * version of this file into combinations with other programs, + * and to distribute those combinations without any restriction + * coming from the use of this file. (The General Public License + * restrictions do apply in other respects; for example, they cover + * modification of the file, and distribution when not linked into + * a combined executable.) + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "test_lib.h" +#include "test_helpers.h" +#include "fileops.h" +#include "git2/status.h" + +static const char *test_blob_oid = "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a"; + +#define STATUS_WORKDIR_FOLDER TEST_RESOURCES "/status/" +#define STATUS_REPOSITORY_TEMP_FOLDER TEMP_REPO_FOLDER ".gitted/" + +BEGIN_TEST(file0, "test retrieving OID from a file apart from the ODB") + git_oid expected_id, actual_id; + char filename[] = "new_file"; + int fd; + + fd = p_creat(filename, 0644); + must_pass(fd); + must_pass(p_write(fd, "new_file\n", 9)); + must_pass(p_close(fd)); + + must_pass(git_odb_hashfile(&actual_id, filename, GIT_OBJ_BLOB)); + + must_pass(git_oid_fromstr(&expected_id, test_blob_oid)); + must_be_true(git_oid_cmp(&expected_id, &actual_id) == 0); + + must_pass(p_unlink(filename)); +END_TEST + +static const char *entry_paths[] = { + "current_file", + "file_deleted", + "modified_file", + "new_file", + "staged_changes", + "staged_changes_file_deleted", + "staged_changes_modified_file", + "staged_delete_file_deleted", + "staged_delete_modified_file", + "staged_new_file", + "staged_new_file_deleted_file", + "staged_new_file_modified_file", + + "subdir/current_file", + "subdir/deleted_file", + "subdir/modified_file", + "subdir/new_file", +}; +static const unsigned int entry_statuses[] = { + GIT_STATUS_CURRENT, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + GIT_STATUS_INDEX_MODIFIED, + GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_DELETED, + GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_DELETED, + GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED, + + GIT_STATUS_CURRENT, + GIT_STATUS_WT_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, +}; +#define ENTRY_COUNT 16 + +static unsigned int get_expected_entry_status(const char *path) +{ + int i; + + for (i = 0; i < ENTRY_COUNT; ++i) + if (!strcmp(path, entry_paths[i])) + return entry_statuses[i]; + + return (unsigned int)-1; +} + +struct status_entry_counts { + int wrong_status_flags_count; + int entry_count; +}; + +static int status_cb(const char *path, unsigned int status_flags, void *payload) +{ + unsigned int expected_status_flags = get_expected_entry_status(path); + struct status_entry_counts *counts = (struct status_entry_counts *)payload; + + counts->entry_count++; + if (status_flags != expected_status_flags) + counts->wrong_status_flags_count++; + + return GIT_SUCCESS; +} + +BEGIN_TEST(statuscb0, "test retrieving status for worktree of repository") + git_repository *repo; + struct status_entry_counts counts; + + must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER)); + must_pass(git_futils_mv_atomic(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER)); + must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER)); + + memset(&counts, 0x0, sizeof(struct status_entry_counts)); + git_status_foreach(repo, status_cb, &counts); + must_be_true(counts.entry_count == ENTRY_COUNT); + must_be_true(counts.wrong_status_flags_count == 0); + + git_repository_free(repo); + + git_futils_rmdir_r(TEMP_REPO_FOLDER, 1); +END_TEST + +BEGIN_TEST(singlestatus0, "test retrieving status for single file") + git_repository *repo; + unsigned int status_flags; + int i; + + must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER)); + must_pass(git_futils_mv_atomic(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER)); + must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER)); + + for (i = 0; i < ENTRY_COUNT; ++i) { + must_pass(git_status_file(&status_flags, repo, entry_paths[i])); + must_be_true(status_flags == entry_statuses[i]); + } + + git_repository_free(repo); + + git_futils_rmdir_r(TEMP_REPO_FOLDER, 1); +END_TEST + +BEGIN_TEST(singlestatus1, "test retrieving status for nonexistent file") + git_repository *repo; + unsigned int status_flags; + int error; + + must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER)); + must_pass(git_futils_mv_atomic(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER)); + must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER)); + + // "nonexistent" does not exist in HEAD, Index or the worktree + error = git_status_file(&status_flags, repo, "nonexistent"); + must_be_true(error == GIT_ENOTFOUND); + + git_repository_free(repo); + + git_futils_rmdir_r(TEMP_REPO_FOLDER, 1); +END_TEST + +BEGIN_SUITE(status) + ADD_TEST(file0); + + ADD_TEST(statuscb0); + + ADD_TEST(singlestatus0); + ADD_TEST(singlestatus1); +END_SUITE \ No newline at end of file diff --git a/vendor/libgit2/tests/test_helpers.c b/vendor/libgit2/tests/test_helpers.c index 760de238b..0900430e1 100644 --- a/vendor/libgit2/tests/test_helpers.c +++ b/vendor/libgit2/tests/test_helpers.c @@ -32,17 +32,17 @@ int write_object_data(char *file, void *data, size_t len) git_file fd; int ret; - if ((fd = gitfo_creat(file, S_IREAD | S_IWRITE)) < 0) + if ((fd = p_creat(file, S_IREAD | S_IWRITE)) < 0) return -1; - ret = gitfo_write(fd, data, len); - gitfo_close(fd); + ret = p_write(fd, data, len); + p_close(fd); return ret; } int write_object_files(const char *odb_dir, object_data *d) { - if (gitfo_mkdir(odb_dir, 0755) < 0) { + if (p_mkdir(odb_dir, 0755) < 0) { int err = errno; fprintf(stderr, "can't make directory \"%s\"", odb_dir); if (err == EEXIST) @@ -51,7 +51,7 @@ int write_object_files(const char *odb_dir, object_data *d) return -1; } - if ((gitfo_mkdir(d->dir, 0755) < 0) && (errno != EEXIST)) { + if ((p_mkdir(d->dir, 0755) < 0) && (errno != EEXIST)) { fprintf(stderr, "can't make object directory \"%s\"\n", d->dir); return -1; } @@ -65,16 +65,16 @@ int write_object_files(const char *odb_dir, object_data *d) int remove_object_files(const char *odb_dir, object_data *d) { - if (gitfo_unlink(d->file) < 0) { + if (p_unlink(d->file) < 0) { fprintf(stderr, "can't delete object file \"%s\"\n", d->file); return -1; } - if ((gitfo_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) { + if ((p_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) { fprintf(stderr, "can't remove object directory \"%s\"\n", d->dir); return -1; } - if (gitfo_rmdir(odb_dir) < 0) { + if (p_rmdir(odb_dir) < 0) { fprintf(stderr, "can't remove directory \"%s\"\n", odb_dir); return -1; } @@ -104,14 +104,14 @@ int remove_loose_object(const char *repository_folder, git_object *object) ptr += GIT_OID_HEXSZ + 1; *ptr = 0; - if (gitfo_unlink(full_path) < 0) { + if (p_unlink(full_path) < 0) { fprintf(stderr, "can't delete object file \"%s\"\n", full_path); return -1; } *top_folder = 0; - if ((gitfo_rmdir(full_path) < 0) && (errno != ENOTEMPTY)) { + if ((p_rmdir(full_path) < 0) && (errno != ENOTEMPTY)) { fprintf(stderr, "can't remove object directory \"%s\"\n", full_path); return -1; } @@ -134,76 +134,48 @@ int cmp_objects(git_rawobj *o, object_data *d) int copy_file(const char *src, const char *dst) { - gitfo_buf source_buf; + git_fbuffer source_buf; git_file dst_fd; int error = GIT_ERROR; - if (gitfo_read_file(&source_buf, src) < GIT_SUCCESS) + if (git_futils_readbuffer(&source_buf, src) < GIT_SUCCESS) return GIT_ENOTFOUND; - dst_fd = gitfo_creat_force(dst, 0644); + dst_fd = git_futils_creat_withpath(dst, 0644); if (dst_fd < 0) goto cleanup; - error = gitfo_write(dst_fd, source_buf.data, source_buf.len); + error = p_write(dst_fd, source_buf.data, source_buf.len); cleanup: - gitfo_free_buf(&source_buf); - gitfo_close(dst_fd); + git_futils_freebuffer(&source_buf); + p_close(dst_fd); return error; } int cmp_files(const char *a, const char *b) { - gitfo_buf buf_a, buf_b; + git_fbuffer buf_a, buf_b; int error = GIT_ERROR; - if (gitfo_read_file(&buf_a, a) < GIT_SUCCESS) + if (git_futils_readbuffer(&buf_a, a) < GIT_SUCCESS) return GIT_ERROR; - if (gitfo_read_file(&buf_b, b) < GIT_SUCCESS) { - gitfo_free_buf(&buf_a); + if (git_futils_readbuffer(&buf_b, b) < GIT_SUCCESS) { + git_futils_freebuffer(&buf_a); return GIT_ERROR; } if (buf_a.len == buf_b.len && !memcmp(buf_a.data, buf_b.data, buf_a.len)) error = GIT_SUCCESS; - gitfo_free_buf(&buf_a); - gitfo_free_buf(&buf_b); + git_futils_freebuffer(&buf_a); + git_futils_freebuffer(&buf_b); return error; } -static int remove_filesystem_element_recurs(void *GIT_UNUSED(nil), char *path) -{ - int error = GIT_SUCCESS; - - GIT_UNUSED_ARG(nil); - - error = gitfo_isdir(path); - if (error == GIT_SUCCESS) { - size_t root_size = strlen(path); - - error = gitfo_dirent(path, GIT_PATH_MAX, remove_filesystem_element_recurs, NULL); - if (error < GIT_SUCCESS) - return error; - - path[root_size] = 0; - return rmdir(path); - } - - return gitfo_unlink(path); -} - -int rmdir_recurs(const char *directory_path) -{ - char buffer[GIT_PATH_MAX]; - strcpy(buffer, directory_path); - return remove_filesystem_element_recurs(NULL, buffer); -} - typedef struct { size_t src_len, dst_len; char *dst; @@ -214,10 +186,10 @@ static int copy_filesystem_element_recurs(void *_data, char *source) copydir_data *data = (copydir_data *)_data; data->dst[data->dst_len] = 0; - git__joinpath(data->dst, data->dst, source + data->src_len); + git_path_join(data->dst, data->dst, source + data->src_len); - if (gitfo_isdir(source) == GIT_SUCCESS) - return gitfo_dirent(source, GIT_PATH_MAX, copy_filesystem_element_recurs, _data); + if (git_futils_isdir(source) == GIT_SUCCESS) + return git_futils_direach(source, GIT_PATH_MAX, copy_filesystem_element_recurs, _data); return copy_file(source, data->dst); } @@ -229,13 +201,14 @@ int copydir_recurs(const char *source_directory_path, const char *destination_di copydir_data data; /* Source has to exist, Destination hast to _not_ exist */ - if (gitfo_isdir(source_directory_path) || !gitfo_isdir(destination_directory_path)) + if (git_futils_isdir(source_directory_path) != GIT_SUCCESS || + git_futils_isdir(destination_directory_path) == GIT_SUCCESS) return GIT_EINVALIDPATH; - git__joinpath(source_buffer, source_directory_path, ""); + git_path_join(source_buffer, source_directory_path, ""); data.src_len = strlen(source_buffer); - git__joinpath(dest_buffer, destination_directory_path, ""); + git_path_join(dest_buffer, destination_directory_path, ""); data.dst = dest_buffer; data.dst_len = strlen(dest_buffer); @@ -244,9 +217,10 @@ int copydir_recurs(const char *source_directory_path, const char *destination_di int open_temp_repo(git_repository **repo, const char *path) { - int error; - if ((error = copydir_recurs(path, TEMP_REPO_FOLDER)) < GIT_SUCCESS) - return error; + if (copydir_recurs(path, TEMP_REPO_FOLDER) < GIT_SUCCESS) { + printf("\nFailed to create temporary folder. Aborting test suite.\n"); + exit(-1); + } return git_repository_open(repo, TEMP_REPO_FOLDER); } @@ -254,7 +228,10 @@ int open_temp_repo(git_repository **repo, const char *path) void close_temp_repo(git_repository *repo) { git_repository_free(repo); - rmdir_recurs(TEMP_REPO_FOLDER); + if (git_futils_rmdir_r(TEMP_REPO_FOLDER, 1) < GIT_SUCCESS) { + printf("\nFailed to remove temporary folder. Aborting test suite.\n"); + exit(-1); + } } static int remove_placeholders_recurs(void *filename, char *path) @@ -262,14 +239,14 @@ static int remove_placeholders_recurs(void *filename, char *path) char passed_filename[GIT_PATH_MAX]; char *data = (char *)filename; - if (!gitfo_isdir(path)) - return gitfo_dirent(path, GIT_PATH_MAX, remove_placeholders_recurs, data); + if (!git_futils_isdir(path)) + return git_futils_direach(path, GIT_PATH_MAX, remove_placeholders_recurs, data); - if (git__basename_r(passed_filename, sizeof(passed_filename), path) < GIT_SUCCESS) - return GIT_EINVALIDPATH; + if (git_path_basename_r(passed_filename, sizeof(passed_filename), path) < GIT_SUCCESS) + return GIT_EINVALIDPATH; if (!strcmp(data, passed_filename)) - return gitfo_unlink(path); + return p_unlink(path); return GIT_SUCCESS; } @@ -278,7 +255,7 @@ int remove_placeholders(char *directory_path, char *filename) { char buffer[GIT_PATH_MAX]; - if (gitfo_isdir(directory_path)) + if (git_futils_isdir(directory_path)) return GIT_EINVALIDPATH; strcpy(buffer, directory_path); diff --git a/vendor/libgit2/tests/test_helpers.h b/vendor/libgit2/tests/test_helpers.h index 19c8ae55c..75027dd6f 100644 --- a/vendor/libgit2/tests/test_helpers.h +++ b/vendor/libgit2/tests/test_helpers.h @@ -41,6 +41,7 @@ #define TEMP_FOLDER "" #define TEMP_REPO_FOLDER TEMP_FOLDER TEST_REPOSITORY_NAME "/" #define TEMP_REPO_FOLDER_NS TEMP_FOLDER TEST_REPOSITORY_NAME +#define TEST_STD_REPO_FOLDER TEMP_REPO_FOLDER ".git/" typedef struct object_data { unsigned char *bytes; /* (compressed) bytes stored in object store */ diff --git a/vendor/libgit2/tests/test_lib.c b/vendor/libgit2/tests/test_lib.c index 5778404c1..a4c39dfde 100755 --- a/vendor/libgit2/tests/test_lib.c +++ b/vendor/libgit2/tests/test_lib.c @@ -15,6 +15,7 @@ struct git_test { char *message; char *failed_pos; char *description; + char *error_message; git_testfunc function; unsigned failed:1, ran:1; @@ -34,6 +35,7 @@ static void test_free(git_test *t) free(t->description); free(t->failed_pos); free(t->message); + free(t->error_message); free(t); } } @@ -55,14 +57,8 @@ static git_test *create_test(git_testfunc function) { git_test *t = DO_ALLOC(git_test); - t->name = NULL; - t->failed = 0; - t->ran = 0; - t->description = NULL; - t->message = NULL; - t->failed_pos = NULL; + memset(t, 0x0, sizeof(git_test)); t->function = function; - t->jump = NULL; return t; } @@ -81,6 +77,7 @@ void git_test__init(git_test *t, const char *name, const char *description) static void fail_test(git_test *tc, const char *file, int line, const char *message) { char buf[1024]; + const char *last_error = git_lasterror(); snprintf(buf, 1024, "%s:%d", file, line); @@ -88,6 +85,9 @@ static void fail_test(git_test *tc, const char *file, int line, const char *mess tc->message = strdup(message); tc->failed_pos = strdup(buf); + if (last_error) + tc->error_message = strdup(last_error); + if (tc->jump != 0) longjmp(*(tc->jump), 0); } @@ -103,6 +103,12 @@ void git_test__assert(git_test *tc, const char *file, int line, const char *mess fail_test(tc, file, line, message); } +void git_test__assert_pass(git_test *tc, const char *file, int line, const char *message, int ret_value) +{ + if (ret_value < 0) + fail_test(tc, file, line, message); +} + /*-------------------------------------------------------------------------* * Test Suite *-------------------------------------------------------------------------*/ @@ -157,6 +163,8 @@ static void print_details(git_testsuite *ts) failCount++; printf(" %d) \"%s\" [test %s @ %s]\n\t%s\n", failCount, tc->description, tc->name, tc->failed_pos, tc->message); + if (tc->error_message) + printf("\tError: %s\n", tc->error_message); } } } @@ -177,6 +185,8 @@ int git_testsuite_run(git_testsuite *ts) putchar('F'); } else putchar('.'); + + fflush(stdout); } printf("\n "); print_details(ts); diff --git a/vendor/libgit2/tests/test_lib.h b/vendor/libgit2/tests/test_lib.h index d0b62c8d4..fc75ed771 100755 --- a/vendor/libgit2/tests/test_lib.h +++ b/vendor/libgit2/tests/test_lib.h @@ -15,7 +15,7 @@ #define BEGIN_SUITE(SNAME) \ git_testsuite *libgit2_suite_##SNAME(void) {\ git_testsuite *_gitsuite = git_testsuite_new(#SNAME); - + #define ADD_TEST(TNAME) \ git_testsuite_add(_gitsuite, _gittest__##TNAME); @@ -26,6 +26,7 @@ #define BEGIN_TEST(TNAME, DESC) \ static void _gittest__##TNAME(git_test *_gittest) { \ git_test__init(_gittest, #TNAME, DESC); \ + git_clearerror();\ {\ #define END_TEST }} @@ -38,8 +39,9 @@ typedef git_testsuite *(*libgit2_suite)(void); void git_test__init(git_test *t, const char *name, const char *description); void git_test__fail(git_test *tc, const char *file, int line, const char *message); void git_test__assert(git_test *tc, const char *file, int line, const char *message, int condition); +void git_test__assert_pass(git_test *tc, const char *file, int line, const char *message, int ret_value); -#define must_pass(expr) git_test__assert(_gittest, __FILE__, __LINE__, "Method failed: " #expr, (expr) == 0) +#define must_pass(expr) git_test__assert_pass(_gittest, __FILE__, __LINE__, "Method failed: " #expr, (expr)) #define must_fail(expr) git_test__assert(_gittest, __FILE__, __LINE__, "Expected method to fail: " #expr, (expr) < 0) #define must_be_true(expr) git_test__assert(_gittest, __FILE__, __LINE__, "Expression is not true: " #expr, !!(expr)) diff --git a/vendor/libgit2/tests/test_main.c b/vendor/libgit2/tests/test_main.c index 102688ce1..c9f8da3a4 100644 --- a/vendor/libgit2/tests/test_main.c +++ b/vendor/libgit2/tests/test_main.c @@ -40,10 +40,12 @@ DECLARE_SUITE(hashtable); DECLARE_SUITE(tag); DECLARE_SUITE(tree); DECLARE_SUITE(refs); -DECLARE_SUITE(sqlite); -DECLARE_SUITE(hiredis); DECLARE_SUITE(repository); DECLARE_SUITE(threads); +DECLARE_SUITE(config); +DECLARE_SUITE(remotes); +DECLARE_SUITE(buffers); +DECLARE_SUITE(status); static libgit2_suite suite_methods[]= { SUITE_NAME(core), @@ -57,15 +59,22 @@ static libgit2_suite suite_methods[]= { SUITE_NAME(tag), SUITE_NAME(tree), SUITE_NAME(refs), - SUITE_NAME(sqlite), SUITE_NAME(repository), SUITE_NAME(threads), - SUITE_NAME(hiredis) + SUITE_NAME(config), + SUITE_NAME(remotes), + SUITE_NAME(buffers), + SUITE_NAME(status), }; #define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods)) -int main(int GIT_UNUSED(argc), char *GIT_UNUSED(argv[])) +#ifdef GIT_WIN32 +int __cdecl +#else +int +#endif +main(int GIT_UNUSED(argc), char *GIT_UNUSED(argv[])) { unsigned int i, failures; diff --git a/wscript b/wscript index 4c6d9a816..04fc90b85 100755 --- a/wscript +++ b/wscript @@ -1,10 +1,11 @@ import Options, Utils -from subprocess import Popen import os, shutil, platform -from os import system from os.path import exists, abspath +from subprocess import Popen -VERSION = '0.0.4' +# Ensure version is updated with each new release. +VERSION = '0.0.6' +# These constants shouldn't change, probably. APPNAME = 'nodegit' srcdir = '.' blddir = 'build' @@ -21,21 +22,41 @@ def configure(conf): conf.check_tool('compiler_cxx') conf.check_tool('node_addon') - os.chdir('vendor/libgit2') - Popen('python waf configure', shell=True).wait() + # Build libgit2, create necessary folders + os.mkdir('vendor/libgit2/build') + os.chdir('vendor/libgit2/build') + + Popen('cmake -DBUILD_TESTS=OFF -DTHREADSAFE=ON .. ', shell=True).wait() - conf.env.append_value('LIBPATH_GIT2', abspath('build/shared')) + conf.env.append_value('LIBPATH_GIT2', abspath('.')) conf.env.append_value('LIB_GIT2', 'git2') def build(bld): - try: os.chdir('vendor/libgit2') + try: os.chdir('vendor/libgit2/build') except: pass - Popen('python waf build-shared', shell=True).wait() + + Popen('cmake --build .', shell=True).wait() os.chdir('../../') main = bld.new_task_gen('cxx', 'shlib', 'node_addon') main.target = 'nodegit' - main.source = 'src/base.cc src/sig.cc src/blob.cc src/error.cc src/object.cc src/reference.cc src/repo.cc src/commit.cc src/oid.cc src/revwalk.cc src/tree.cc src/tree_entry.cc' - main.rpath = abspath('vendor/libgit2/build/shared') + main.source = ''' + src/base.cc + src/sig.cc + src/blob.cc + src/error.cc + src/object.cc + src/reference.cc + src/repo.cc + src/commit.cc + src/oid.cc + src/revwalk.cc + src/tree.cc + src/tree_entry.cc + ''' + + main.includes = './vendor/libgit2/include' + main.rpath = '$ORIGIN/../../vendor/libgit2/build' main.uselib = 'GIT2' +