Commit 72a3fe42 by Vicent Marti

I broke your bindings

Hey. Apologies in advance -- I broke your bindings.

This is a major commit that includes a long-overdue redesign of the
whole object-database structure. This is expected to be the last major
external API redesign of the library until the first non-alpha release.

Please get your bindings up to date with these changes. They will be
included in the next minor release. Sorry again!

Major features include:

	- Real caching and refcounting on parsed objects
	- Real caching and refcounting on objects read from the ODB
	- Streaming writes & reads from the ODB
	- Single-method writes for all object types
	- The external API is now partially thread-safe

The speed increases are significant in all aspects, specially when
reading an object several times from the ODB (revwalking) and when
writing big objects to the ODB.

Here's a full changelog for the external API:

blob.h
------

	- Remove `git_blob_new`
	- Remove `git_blob_set_rawcontent`
	- Remove `git_blob_set_rawcontent_fromfile`
	- Rename `git_blob_writefile` -> `git_blob_create_fromfile`
	- Change `git_blob_create_fromfile`:
		The `path` argument is now relative to the repository's working dir
	- Add `git_blob_create_frombuffer`

commit.h
--------

	- Remove `git_commit_new`
	- Remove `git_commit_add_parent`
	- Remove `git_commit_set_message`
	- Remove `git_commit_set_committer`
	- Remove `git_commit_set_author`
	- Remove `git_commit_set_tree`

	- Add `git_commit_create`
	- Add `git_commit_create_v`
	- Add `git_commit_create_o`
	- Add `git_commit_create_ov`

tag.h
-----

	- Remove `git_tag_new`
	- Remove `git_tag_set_target`
	- Remove `git_tag_set_name`
	- Remove `git_tag_set_tagger`
	- Remove `git_tag_set_message`

	- Add `git_tag_create`
	- Add `git_tag_create_o`

tree.h
------

	- Change `git_tree_entry_2object`:
		New signature is `(git_object **object_out, git_repository *repo, git_tree_entry *entry)`

	- Remove `git_tree_new`
	- Remove `git_tree_add_entry`
	- Remove `git_tree_remove_entry_byindex`
	- Remove `git_tree_remove_entry_byname`
	- Remove `git_tree_clearentries`
	- Remove `git_tree_entry_set_id`
	- Remove `git_tree_entry_set_name`
	- Remove `git_tree_entry_set_attributes`

object.h
------------

	- Remove `git_object_new
	- Remove `git_object_write`

	- Change `git_object_close`:
		This method is now *mandatory*. Not closing an object causes a
		memory leak.

odb.h
-----

	- Remove type `git_rawobj`
	- Remove `git_rawobj_close`
	- Rename `git_rawobj_hash` -> `git_odb_hash`
	- Change `git_odb_hash`:
		New signature is `(git_oid *id, const void *data, size_t len, git_otype type)`

	- Add type `git_odb_object`
	- Add `git_odb_object_close`

	- Change `git_odb_read`:
		New signature is `(git_odb_object **out, git_odb *db, const git_oid *id)`
	- Change `git_odb_read_header`:
		New signature is `(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)`
	- Remove `git_odb_write`
	- Add `git_odb_open_wstream`
	- Add `git_odb_open_rstream`

odb_backend.h
-------------

	- Change type `git_odb_backend`:
		New internal signatures are as follows

			int (* read)(void **, size_t *, git_otype *, struct git_odb_backend *, const git_oid *)
			int (* read_header)(size_t *, git_otype *, struct git_odb_backend *, const git_oid *)
			int (* writestream)(struct git_odb_stream **, struct git_odb_backend *, size_t, git_otype)
			int (* readstream)( struct git_odb_stream **, struct git_odb_backend *, const git_oid *)

	- Add type `git_odb_stream`
	- Add enum `git_odb_streammode`

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent bb3de0c4
......@@ -41,8 +41,6 @@ GIT_BEGIN_DECL
/**
* Lookup a blob object from a repository.
* The generated blob object is owned by the revision
* repo and shall not be freed by the user.
*
* @param blob pointer to the looked up blob
* @param repo the repo to use when locating the blob.
......@@ -55,49 +53,12 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
}
/**
* Create a new in-memory git_blob.
*
* The blob object must be manually filled using
* the 'set_rawcontent' methods before it can
* be written back to disk.
*
* @param blob pointer to the new blob
* @param repo The repository where the object will reside
* @return 0 on success; error code otherwise
*/
GIT_INLINE(int) git_blob_new(git_blob **blob, git_repository *repo)
{
return git_object_new((git_object **)blob, repo, GIT_OBJ_BLOB);
}
/**
* Fill a blob with the contents inside
* the pointed file.
*
* @param blob pointer to the new blob
* @param filename name of the file to read
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_blob_set_rawcontent_fromfile(git_blob *blob, const char *filename);
/**
* Fill a blob with the contents inside
* the pointed buffer
*
* @param blob pointer to the blob
* @param buffer buffer with the contents for the blob
* @param len size of the buffer
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_blob_set_rawcontent(git_blob *blob, const void *buffer, size_t len);
/**
* Get a read-only buffer with the raw content of a blob.
*
* A pointer to the raw content of a blob is returned;
* this pointer is owned internally by the object and shall
* not be free'd. The pointer may be invalidated at a later
* time (e.g. when changing the contents of the blob).
* time.
*
* @param blob pointer to the blob
* @return the pointer; NULL if the blob has no contents
......@@ -114,14 +75,28 @@ GIT_EXTERN(int) git_blob_rawsize(git_blob *blob);
/**
* Read a file from the working folder of a repository
* and write it to the Object Database as a loose blob,
* if such doesn't exist yet.
* and write it to the Object Database as a loose blob
*
* @param written_id return the id of the written blob
* @param repo repository where the blob will be written
* @param path file from which the blob will be created
* @param oid return the id of the written blob
* @param repo repository where the blob will be written.
* this repository cannot be bare
* @param path file from which the blob will be created,
* relative to the repository's working dir
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path);
/**
* Write an in-memory buffer to the ODB as a blob
*
* @param oid return the oid of the written blob
* @param repo repository where to blob will be written
* @param buffer data to be written into the blob
* @param len length of the data
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_blob_writefile(git_oid *written_id, git_repository *repo, const char *path);
GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len);
/** @} */
GIT_END_DECL
......
......@@ -41,8 +41,6 @@ GIT_BEGIN_DECL
/**
* Lookup a commit object from a repository.
* The generated commit object is owned by the revision
* repo and shall not be freed by the user.
*
* @param commit pointer to the looked up commit
* @param repo the repo to use when locating the commit.
......@@ -56,23 +54,8 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
}
/**
* Create a new in-memory git_commit.
*
* The commit object must be manually filled using
* setter methods before it can be written to its
* repository.
*
* @param commit pointer to the new commit
* @param repo The repository where the object will reside
* @return 0 on success; error code otherwise
*/
GIT_INLINE(int) git_commit_new(git_commit **commit, git_repository *repo)
{
return git_object_new((git_object **)commit, repo, GIT_OBJ_COMMIT);
}
/**
* Get the id of a commit.
*
* @param commit a previously loaded commit.
* @return object identity for the commit.
*/
......@@ -80,6 +63,7 @@ GIT_EXTERN(const git_oid *) git_commit_id(git_commit *commit);
/**
* Get the short (one line) message of a commit.
*
* @param commit a previously loaded commit.
* @return the short message of a commit
*/
......@@ -87,6 +71,7 @@ GIT_EXTERN(const char *) git_commit_message_short(git_commit *commit);
/**
* Get the full message of a commit.
*
* @param commit a previously loaded commit.
* @return the message of a commit
*/
......@@ -94,6 +79,7 @@ GIT_EXTERN(const char *) git_commit_message(git_commit *commit);
/**
* Get the commit time (i.e. committer time) of a commit.
*
* @param commit a previously loaded commit.
* @return the time of a commit
*/
......@@ -101,6 +87,7 @@ GIT_EXTERN(time_t) git_commit_time(git_commit *commit);
/**
* Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
*
* @param commit a previously loaded commit.
* @return positive or negative timezone offset, in minutes from UTC
*/
......@@ -108,6 +95,7 @@ GIT_EXTERN(int) git_commit_time_offset(git_commit *commit);
/**
* Get the committer of a commit.
*
* @param commit a previously loaded commit.
* @return the committer of a commit
*/
......@@ -115,6 +103,7 @@ GIT_EXTERN(const git_signature *) git_commit_committer(git_commit *commit);
/**
* Get the author of a commit.
*
* @param commit a previously loaded commit.
* @return the author of a commit
*/
......@@ -122,6 +111,7 @@ GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit);
/**
* Get the tree pointed to by a commit.
*
* @param tree_out pointer where to store the tree object
* @param commit a previously loaded commit.
* @return 0 on success; error code otherwise
......@@ -146,42 +136,129 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit);
*/
GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n);
/**
* Add a new parent commit to an existing commit
* @param commit the commit object
* @param new_parent the new commit which will be a parent
* Create a new commit in the repository
*
*
* @param oid Pointer where to store the OID of the
* newly created commit
*
* @param repo Repository where to store the commit
*
* @param update_ref If not NULL, name of the reference that
* will be updated to point to this commit. If the reference
* is not direct, it will be resolved to a direct reference.
* Use "HEAD" to update the HEAD of the current branch and
* make it point to this commit
*
* @param author Signature representing the author and the authory
* time of this commit
*
* @param committer Signature representing the committer and the
* commit time of this commit
*
* @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 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.
*
* @return 0 on success; error code otherwise
* The created commit will be written to the Object Database and
* the given reference will be updated to point to it
*/
GIT_EXTERN(int) git_commit_add_parent(git_commit *commit, git_commit *new_parent);
GIT_EXTERN(int) git_commit_create(
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,
const git_oid *parent_oids[]);
/**
* Set the message of a commit
* @param commit the commit object
* @param message the new message
* 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(void) git_commit_set_message(git_commit *commit, const char *message);
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,
const git_tree *tree,
int parent_count,
const git_commit *parents[]);
/**
* Set the committer of a commit
* @param commit the commit object
* @param author_sig signature of the committer
* 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 *`.
*
* The parents for the commit are specified as a variable
* list of pointers to `const git_commit *`. 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(void) git_commit_set_committer(git_commit *commit, const git_signature *committer_sig);
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,
...);
/**
* Set the author of a commit
* @param commit the commit object
* @param author_sig signature of the author
*/
GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const git_signature *author_sig);
/**
* Set the tree which is pointed to by a commit
* @param commit the commit object
* @param tree the new tree
* @param 0 on success; error code otherwise
* 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_set_tree(git_commit *commit, git_tree *tree);
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,
const git_oid *tree_oid,
int parent_count,
...);
/** @} */
GIT_END_DECL
......
......@@ -158,6 +158,9 @@
/** The state of the reference is not valid */
#define GIT_EINVALIDREFSTATE (GIT_ERROR - 21)
/** This feature has not been implemented yet */
#define GIT_ENOTIMPLEMENTED (GIT_ERROR - 22)
GIT_BEGIN_DECL
typedef struct {
......
......@@ -42,7 +42,8 @@ GIT_BEGIN_DECL
* Lookup a reference to one of the objects in a repostory.
*
* The generated reference is owned by the repository and
* should not be freed by the user.
* 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.
......@@ -58,54 +59,8 @@ GIT_BEGIN_DECL
GIT_EXTERN(int) git_object_lookup(git_object **object, git_repository *repo, const git_oid *id, git_otype type);
/**
* Create a new in-memory repository object with
* the given type.
*
* The object's attributes can be filled in using the
* corresponding setter methods.
*
* The object will be written back to given git_repository
* when the git_object_write() function is called; objects
* cannot be written to disk until all their main
* attributes have been properly filled.
*
* Objects are instantiated with no SHA1 id; their id
* will be automatically generated when writing to the
* repository.
*
* @param object pointer to the new object
* @parem repo Repository where the object belongs
* @param type Type of the object to be created
* @return the new object
*/
GIT_EXTERN(int) git_object_new(git_object **object, git_repository *repo, git_otype type);
/**
* Write back an object to disk.
*
* The object will be written to its corresponding
* repository.
*
* If the object has no changes since it was first
* read from the repository, no actions will take place.
*
* If the object has been modified since it was read from
* the repository, or it has been created from scratch
* in memory, it will be written to the repository and
* its SHA1 ID will be updated accordingly.
*
* @param object Git object to write back
* @return 0 on success; otherwise an error code
*/
GIT_EXTERN(int) git_object_write(git_object *object);
/**
* Get the id (SHA1) of a repository object
*
* In-memory objects created by git_object_new() do not
* have a SHA1 ID until they are written on a repository.
*
* @param obj the repository object
* @return the SHA1 id
*/
......@@ -137,14 +92,8 @@ GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj);
* by the repository.
*
* IMPORTANT:
* It is *not* necessary to call this method when you stop using
* an object, since all object memory is automatically reclaimed
* by the repository when it is freed.
*
* Forgetting to call `git_object_close` does not cause memory
* leaks, but it's is recommended to close as soon as possible
* the biggest objects (e.g. blobs) to prevent wasting memory
* space.
* It *is* necessary to call this method when you stop using
* an object. Failure to do so will cause a memory leak.
*
* @param object the object to close
*/
......
......@@ -28,6 +28,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
#include "odb_backend.h"
/**
* @file git2/odb.h
......@@ -100,61 +101,49 @@ GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, in
/**
* Close an open object database.
*
* @param db database pointer to close. If NULL no action is taken.
*/
GIT_EXTERN(void) git_odb_close(git_odb *db);
/** An object read from the database. */
typedef struct {
void *data; /**< Raw, decompressed object data. */
size_t len; /**< Total number of bytes in data. */
git_otype type; /**< Type of this object. */
} git_rawobj;
/**
* Read an object from the database.
*
* If GIT_ENOTFOUND then out->data is set to NULL.
* This method queries all avaiable ODB backends
* trying to read the given OID.
*
* 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 object descriptor to populate upon reading.
* @param out pointer where to store the read object
* @param db database to search for the object in.
* @param id identity of the object to read.
* @return
* - GIT_SUCCESS if the object was read;
* - GIT_ENOTFOUND if the object is not in the database.
*/
GIT_EXTERN(int) git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id);
GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
/**
* Read the header of an object from the database, without
* reading its full contents.
*
* Only the 'type' and 'len' fields of the git_rawobj structure
* are filled. The 'data' pointer will always be NULL.
* The header includes the length and the type of an object.
*
* The raw object pointed by 'out' doesn't need to be manually
* closed with git_rawobj_close().
* Note that most backends do not support reading only the header
* of an object, so the whole object will be read and then the
* header will be returned.
*
* @param out object descriptor to populate upon reading.
* @param len_p pointer where to store the length
* @param type_p pointer where to store the type
* @param db database to search for the object in.
* @param id identity of the object to read.
* @return
* - GIT_SUCCESS if the object was read;
* - GIT_ENOTFOUND if the object is not in the database.
*/
GIT_EXTERN(int) git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id);
/**
* Write an object to the database.
*
* @param id identity of the object written.
* @param db database to which the object should be written.
* @param obj object descriptor for the object to write.
* @return
* - GIT_SUCCESS if the object was written;
* - GIT_ERROR otherwise.
*/
GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj);
GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id);
/**
* Determine if the given object can be found in the object database.
......@@ -162,39 +151,89 @@ GIT_EXTERN(int) git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj);
* @param db database to be searched for the given object.
* @param id the object to search for.
* @return
* - true, if the object was found
* - false, otherwise
* - 1, if the object was found
* - 0, otherwise
*/
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/**
* Open a stream to write an object into the ODB
*
* The type and final length of the object must be specified
* when opening the stream.
*
* 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
*
* The stream must always be free'd or will leak memory.
*
* @see git_odb_stream
*
* @param stream pointer where to store the stream
* @param db object database where the stream will write
* @param size final size of the object that will be written
* @para type type of the object that will be written
* @return 0 if the stream was created; error code otherwise
*/
GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type);
/**
* Open a stream to read an object from the ODB
*
* Note that most backends do *not* support streaming reads
* because they store their objects as compressed/delta'ed blobs.
*
* It's recommended to use `git_odb_read` instead, which is
* assured to work on all backends.
*
* 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
*
* The stream must always be free'd or will leak memory.
*
* @see git_odb_stream
*
* @param stream pointer where to store the stream
* @param db object database where the stream will read from
* @param oid oid of the object the stream will read from
* @return 0 if the stream was created; error code otherwise
*/
GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid);
/**
* Determine the object-ID (sha1 hash) of the given git_rawobj.
* Determine the object-ID (sha1 hash) of a data buffer
*
* The input obj must be a valid loose object type and the data
* pointer must not be NULL, unless the len field is also zero.
* The resulting SHA-1 OID will the itentifier for the data
* buffer as if the data buffer it were to written to the ODB.
*
* @param id the resulting object-ID.
* @param obj the object whose hash is to be determined.
* @return
* - GIT_SUCCESS if the object-ID was correctly determined.
* - GIT_ERROR if the given object is malformed.
* @param data data to hash
* @param len size of the data
* @param type of the data to hash
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(int) git_rawobj_hash(git_oid *id, git_rawobj *obj);
GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type);
/**
* Release all memory used by the obj structure.
*
* As a result of this call, obj->data will be set to NULL.
* Close an ODB object
*
* If obj->data is already NULL, nothing happens.
* This method must always be called once a `git_odb_object` is no
* longer needed, otherwise memory will leak.
*
* @param obj object descriptor to free.
* @param object object to close
*/
GIT_EXTERN(void) git_rawobj_close(git_rawobj *obj);
GIT_EXTERN(void) git_odb_object_close(git_odb_object *object);
/** @} */
GIT_END_DECL
......
......@@ -39,24 +39,32 @@
*/
GIT_BEGIN_DECL
struct git_odb_stream;
/** An instance for a custom backend */
struct git_odb_backend {
git_odb *odb;
int (* read)(
git_rawobj *,
void **, size_t *, git_otype *,
struct git_odb_backend *,
const git_oid *);
int (* read_header)(
git_rawobj *,
size_t *, git_otype *,
struct git_odb_backend *,
const git_oid *);
int (* write)(
git_oid *id,
int (* writestream)(
struct git_odb_stream **,
struct git_odb_backend *,
size_t,
git_otype);
int (* readstream)(
struct git_odb_stream **,
struct git_odb_backend *,
git_rawobj *obj);
const git_oid *);
int (* exists)(
struct git_odb_backend *,
......@@ -65,12 +73,28 @@ struct git_odb_backend {
void (* free)(struct git_odb_backend *);
};
/** A stream to read/write from a backend */
struct git_odb_stream {
struct git_odb_backend *backend;
int mode;
int (*read)(struct git_odb_stream *stream, char *buffer, size_t len);
int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len);
int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream);
void (*free)(struct git_odb_stream *stream);
};
/** Streaming mode */
typedef enum {
GIT_STREAM_RDONLY = (1 << 1),
GIT_STREAM_WRONLY = (1 << 2),
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);
#ifdef GIT2_SQLITE_BACKEND
GIT_EXTERN(int) git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db);
#endif
GIT_END_DECL
......
......@@ -41,8 +41,6 @@ GIT_BEGIN_DECL
/**
* Lookup a tag object from the repository.
* The generated tag object is owned by the revision
* repo and shall not be freed by the user.
*
* @param tag pointer to the looked up tag
* @param repo the repo to use when locating the tag.
......@@ -55,23 +53,8 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi
}
/**
* Create a new in-memory git_tag.
*
* The tag object must be manually filled using
* setter methods before it can be written to its
* repository.
*
* @param tag pointer to the new tag
* @param repo The repository where the object will reside
* @return 0 on success; error code otherwise
*/
GIT_INLINE(int) git_tag_new(git_tag **tag, git_repository *repo)
{
return git_object_new((git_object **)tag, repo, (git_otype)GIT_OBJ_TAG);
}
/**
* Get the id of a tag.
*
* @param tag a previously loaded tag.
* @return object identity for the tag.
*/
......@@ -79,6 +62,10 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag);
/**
* Get the tagged object of a tag
*
* This method performs a repository lookup for the
* given object and returns it
*
* @param target pointer where to store the target
* @param tag a previously loaded tag.
* @return 0 on success; error code otherwise
......@@ -87,6 +74,7 @@ GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *t);
/**
* Get the OID of the tagged object of a tag
*
* @param tag a previously loaded tag.
* @return pointer to the OID
*/
......@@ -94,6 +82,7 @@ GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *t);
/**
* Get the type of a tag's tagged object
*
* @param tag a previously loaded tag.
* @return type of the tagged object
*/
......@@ -101,6 +90,7 @@ GIT_EXTERN(git_otype) git_tag_type(git_tag *t);
/**
* Get the name of a tag
*
* @param tag a previously loaded tag.
* @return name of the tag
*/
......@@ -108,6 +98,7 @@ GIT_EXTERN(const char *) git_tag_name(git_tag *t);
/**
* Get the tagger (author) of a tag
*
* @param tag a previously loaded tag.
* @return reference to the tag's author
*/
......@@ -115,39 +106,69 @@ GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *t);
/**
* Get the message of a tag
*
* @param tag a previously loaded tag.
* @return message of the tag
*/
GIT_EXTERN(const char *) git_tag_message(git_tag *t);
/**
* Set the target of a tag (i.e. the object that the tag points to)
* @param tag The tag to modify
* @param target the new tagged target
*/
GIT_EXTERN(int) git_tag_set_target(git_tag *tag, git_object *target);
/**
* Set the name of a tag
* @param tag The tag to modify
* @param name the new name for the tag
* Create a new tag in the repository from an OID
*
* @param oid Pointer where to store the OID of the
* newly created tag
*
* @param repo Repository where to store the tag
*
* @param tag_name Name for the tag; this name is validated
* for consistency
*
* @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 tagger Signature of the tagger for this tag, and
* of the tagging time
*
* @param message Full message for this tag
*
* @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(void) git_tag_set_name(git_tag *tag, const char *name);
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);
/**
* Set the tagger of a tag
* @param tag The tag to modify
* @param tagger_sig signature of the tagging action
* @return 0 on success; error code otherwise
*/
GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig);
/**
* Set the message of a tag
* @param tag The tag to modify
* @param message the new tagger for the tag
* 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(void) git_tag_set_message(git_tag *tag, const char *message);
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);
/** @} */
GIT_END_DECL
......
......@@ -41,8 +41,6 @@ GIT_BEGIN_DECL
/**
* Lookup a tree object from the repository.
* The generated tree object is owned by the revision
* repo and shall not be freed by the user.
*
* @param tree pointer to the looked up tree
* @param repo the repo to use when locating the tree.
......@@ -55,31 +53,16 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git
}
/**
* Create a new in-memory git_tree.
*
* The tree object must be manually filled using
* setter methods before it can be written to its
* repository.
*
* @param tree pointer to the new tree
* @param repo The repository where the object will reside
* @return 0 on success; error code otherwise
*/
GIT_INLINE(int) git_tree_new(git_tree **tree, git_repository *repo)
{
return git_object_new((git_object **)tree, repo, GIT_OBJ_TREE);
}
/**
* Get the id of a tree.
*
* @param tree a previously loaded tree.
* @return object identity for the tree.
*/
GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree);
/**
* Get the number of entries listed in a tree
*
* @param tree a previously loaded tree.
* @return the number of entries in the tree
*/
......@@ -87,6 +70,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree);
/**
* Lookup a tree entry by its filename
*
* @param tree a previously loaded tree.
* @param filename the filename of the desired entry
* @return the tree entry; NULL if not found
......@@ -95,6 +79,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *f
/**
* Lookup a tree entry by its position in the tree
*
* @param tree a previously loaded tree.
* @param idx the position in the entry list
* @return the tree entry; NULL if not found
......@@ -103,6 +88,7 @@ GIT_EXTERN(git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx);
/**
* Get the UNIX file attributes of a tree entry
*
* @param entry a tree entry
* @return attributes as an integer
*/
......@@ -110,6 +96,7 @@ GIT_EXTERN(unsigned int) git_tree_entry_attributes(git_tree_entry *entry);
/**
* Get the filename of a tree entry
*
* @param entry a tree entry
* @return the name of the file
*/
......@@ -117,6 +104,7 @@ GIT_EXTERN(const char *) git_tree_entry_name(git_tree_entry *entry);
/**
* Get the id of the object pointed by the entry
*
* @param entry a tree entry
* @return the oid of the object
*/
......@@ -126,97 +114,11 @@ GIT_EXTERN(const git_oid *) git_tree_entry_id(git_tree_entry *entry);
* Convert a tree entry to the git_object it points too.
*
* @param object pointer to the converted object
* @param repo repository where to lookup the pointed object
* @param entry a tree entry
* @return a reference to the pointed object in the repository
*/
GIT_EXTERN(int) git_tree_entry_2object(git_object **object, git_tree_entry *entry);
/**
* Add a new entry to a tree and return the new entry.
*
* This will mark the tree as modified; the new entry will
* be written back to disk on the next git_object_write()
*
* @param entry_out Pointer to the entry that just got
* created. May be NULL if you are not interested on
* getting the new entry
* @param tree Tree object to store the entry
* @iparam id OID for the tree entry
* @param filename Filename for the tree entry
* @param attributes UNIX file attributes for the entry
* @return 0 on success; otherwise error code
*/
GIT_EXTERN(int) git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid *id, const char *filename, int attributes);
/**
* Remove an entry by its index.
*
* Index must be >= 0 and < than git_tree_entrycount().
*
* This will mark the tree as modified; the modified entry will
* be written back to disk on the next git_object_write()
*
* @param tree Tree where to remove the entry
* @param idx index of the entry
* @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found
*/
GIT_EXTERN(int) git_tree_remove_entry_byindex(git_tree *tree, int idx);
/**
* Remove an entry by its filename.
*
* This will mark the tree as modified; the modified entry will
* be written back to disk on the next git_object_write()
*
* @param tree Tree where to remove the entry
* @param filename File name of the entry
* @return 0 on successful removal; GIT_ENOTFOUND if the entry wasn't found
*/
GIT_EXTERN(int) git_tree_remove_entry_byname(git_tree *tree, const char *filename);
/**
* Clear all the entries in a tree.
*
* This will mark the tree as modified; the modified entry will
* be written back to disk on the next git_object_write().
*
* @param tree Tree object whose entries are to be sorted
*/
GIT_EXTERN(void) git_tree_clear_entries(git_tree *tree);
/**
* Change the SHA1 id of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new SHA1 oid for the entry
*/
GIT_EXTERN(void) git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid);
/**
* Change the filename of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new filename for the entry
*/
GIT_EXTERN(void) git_tree_entry_set_name(git_tree_entry *entry, const char *name);
/**
* Change the attributes of a tree entry.
*
* This will mark the tree that contains the entry as modified;
* the modified entry will be written back to disk on the next git_object_write()
*
* @param entry Entry object which will be modified
* @param oid new attributes for the entry
* @return 0 if the attributes were properly set; error code otherwise
*/
GIT_EXTERN(int) git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr);
GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry);
/** @} */
GIT_END_DECL
......
......@@ -71,7 +71,6 @@ typedef time_t git_time_t;
#endif
/** Basic type (loose or packed) of any Git object. */
typedef enum {
GIT_OBJ_ANY = -2, /**< Object can be any of the following */
......@@ -92,6 +91,12 @@ typedef struct git_odb git_odb;
/** A custom backend in an ODB */
typedef struct git_odb_backend git_odb_backend;
/** An object read from the ODB */
typedef struct git_odb_object git_odb_object;
/** A stream to read/write from the ODB */
typedef struct git_odb_stream git_odb_stream;
/**
* Representation of an existing git repository,
* including all its object contents
......
......@@ -272,4 +272,13 @@ cleanup:
return GIT_ERROR;
}
#else
int git_odb_backend_sqlite(git_odb_backend **GIT_UNUSED(backend_out), const char *GIT_UNUSED(sqlite_db))
{
GIT_UNUSED_ARG(backend_out);
GIT_UNUSED_ARG(sqlite_db);
return GIT_ENOTIMPLEMENTED;
}
#endif /* HAVE_SQLITE3 */
......@@ -33,104 +33,89 @@
const void *git_blob_rawcontent(git_blob *blob)
{
assert(blob);
if (blob->content.data != NULL)
return blob->content.data;
if (blob->object.in_memory)
return NULL;
if (!blob->object.source.open && git_object__source_open((git_object *)blob) < GIT_SUCCESS)
return NULL;
return blob->object.source.raw.data;
return blob->odb_object->raw.data;
}
int git_blob_rawsize(git_blob *blob)
{
assert(blob);
if (blob->content.data != NULL)
return blob->content.len;
return blob->object.source.raw.len;
return blob->odb_object->raw.len;
}
void git_blob__free(git_blob *blob)
{
gitfo_free_buf(&blob->content);
git_odb_object_close(blob->odb_object);
free(blob);
}
int git_blob__parse(git_blob *blob)
int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
{
assert(blob);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->odb_object = odb_obj;
return GIT_SUCCESS;
}
int git_blob__writeback(git_blob *blob, git_odb_source *src)
{
assert(blob->object.modified);
if (blob->content.data == NULL)
return GIT_EMISSINGOBJDATA;
return git__source_write(src, blob->content.data, blob->content.len);
}
int git_blob_set_rawcontent(git_blob *blob, const void *buffer, size_t len)
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
{
assert(blob && buffer);
blob->object.modified = 1;
git_object__source_close((git_object *)blob);
if (blob->content.data != NULL)
gitfo_free_buf(&blob->content);
int error;
git_odb_stream *stream;
blob->content.data = git__malloc(len);
blob->content.len = len;
if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
return error;
if (blob->content.data == NULL)
return GIT_ENOMEM;
stream->write(stream, buffer, len);
memcpy(blob->content.data, buffer, len);
error = stream->finalize_write(oid, stream);
stream->free(stream);
return GIT_SUCCESS;
return error;
}
int git_blob_set_rawcontent_fromfile(git_blob *blob, const char *filename)
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
assert(blob && filename);
blob->object.modified = 1;
int error, fd;
char full_path[GIT_PATH_MAX];
char buffer[2048];
git_off_t size;
git_odb_stream *stream;
if (blob->content.data != NULL)
gitfo_free_buf(&blob->content);
return gitfo_read_file(&blob->content, filename);
}
if (repo->path_workdir == NULL)
return GIT_ENOTFOUND;
int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *path)
{
int error;
git_blob *blob;
git__joinpath(full_path, repo->path_workdir, path);
if (gitfo_exists(path) < 0)
if ((fd = gitfo_open(full_path, O_RDONLY)) < 0)
return GIT_ENOTFOUND;
if ((error = git_blob_new(&blob, repo)) < GIT_SUCCESS)
return error;
if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) {
gitfo_close(fd);
return GIT_EOSERR;
}
if ((error = git_blob_set_rawcontent_fromfile(blob, path)) < GIT_SUCCESS)
if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) {
gitfo_close(fd);
return error;
}
if ((error = git_object_write((git_object *)blob)) < GIT_SUCCESS)
return error;
while (size > 0) {
ssize_t read_len;
git_oid_cpy(written_id, git_object_id((git_object *)blob));
read_len = read(fd, buffer, sizeof(buffer));
git_object_close((git_object*)blob);
return GIT_SUCCESS;
if (read_len < 0) {
gitfo_close(fd);
stream->free(stream);
return GIT_EOSERR;
}
stream->write(stream, buffer, read_len);
size -= read_len;
}
error = stream->finalize_write(oid, stream);
stream->free(stream);
return error;
}
......@@ -3,15 +3,15 @@
#include "git2/blob.h"
#include "repository.h"
#include "odb.h"
#include "fileops.h"
struct git_blob {
git_object object;
gitfo_buf content;
git_odb_object *odb_object;
};
void git_blob__free(git_blob *blob);
int git_blob__parse(git_blob *blob);
int git_blob__writeback(git_blob *blob, git_odb_source *src);
int git_blob__parse(git_blob *blob, git_odb_object *obj);
#endif
......@@ -32,23 +32,6 @@
#define GIT_CACHE_OPENADR 3
GIT_INLINE(int) cached_obj_compare(git_cached_obj *obj, const git_oid *oid)
{
return git_oid_cmp(&obj->oid, oid);
}
GIT_INLINE(void) cached_obj_incref(git_cached_obj *obj)
{
git_atomic_inc(&obj->refcount);
}
GIT_INLINE(void) cached_obj_decref(git_cached_obj *obj, git_cached_obj_freeptr free_obj)
{
if (git_atomic_dec(&obj->refcount) == 0)
free_obj(obj);
}
void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
size_t i;
......@@ -82,7 +65,9 @@ void git_cache_free(git_cache *cache)
size_t i;
for (i = 0; i < (cache->size_mask + 1); ++i) {
cached_obj_decref(cache->nodes[i].ptr, cache->free_obj);
if (cache->nodes[i].ptr)
git_cached_obj_decref(cache->nodes[i].ptr, cache->free_obj);
git_mutex_free(&cache->nodes[i].lock);
}
......@@ -103,8 +88,8 @@ void *git_cache_get(git_cache *cache, const git_oid *oid)
git_mutex_lock(&node->lock);
{
if (cached_obj_compare(node->ptr, oid) == 0) {
cached_obj_incref(node->ptr);
if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) {
git_cached_obj_incref(node->ptr);
node->lru = ++cache->lru_count;
found = 1;
}
......@@ -121,14 +106,14 @@ 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, stored = 0;
size_t i;
oid = &((git_cached_obj*)entry)->oid;
hash = (const uint32_t *)oid->id;
/* increase the refcount on this object, because
* the cache now owns it */
cached_obj_incref(entry);
git_cached_obj_incref(entry);
for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
size_t pos = hash[i] & cache->size_mask;
......@@ -139,34 +124,35 @@ void *git_cache_try_store(git_cache *cache, void *entry)
lru_node = nodes[0];
for (i = 0; !stored && entry && i < GIT_CACHE_OPENADR; ++i) {
for (i = 0; i < GIT_CACHE_OPENADR; ++i) {
if (nodes[i]->ptr == NULL) {
nodes[i]->ptr = entry;
nodes[i]->lru = ++cache->lru_count;
stored = 1;
} else if (cached_obj_compare(nodes[i]->ptr, oid) == 0) {
cached_obj_decref(entry, cache->free_obj);
break;
} else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) {
git_cached_obj_decref(entry, cache->free_obj);
entry = nodes[i]->ptr;
stored = 1;
nodes[i]->lru = ++cache->lru_count;
break;
}
if (nodes[i]->lru < lru_node->lru)
lru_node = nodes[i];
}
if (!stored) {
if (i == GIT_CACHE_OPENADR) {
void *old_entry = lru_node->ptr;
assert(old_entry);
cached_obj_decref(old_entry, cache->free_obj);
git_cached_obj_decref(old_entry, cache->free_obj);
lru_node->ptr = entry;
lru_node->lru = ++cache->lru_count;
}
/* increase the refcount again, because we are
* returning it to the user */
cached_obj_incref(entry);
git_cached_obj_incref(entry);
for (i = 0; i < GIT_CACHE_OPENADR; ++i)
git_mutex_unlock(&nodes[i]->lock);
......
......@@ -7,6 +7,8 @@
#include "thread-utils.h"
#define GIT_DEFAULT_CACHE_SIZE 128
typedef void (*git_cached_obj_freeptr)(void *);
typedef struct {
......@@ -35,4 +37,23 @@ void git_cache_free(git_cache *cache);
void *git_cache_try_store(git_cache *cache, void *entry);
void *git_cache_get(git_cache *cache, const git_oid *oid);
GIT_INLINE(int) git_cached_obj_compare(git_cached_obj *obj, const git_oid *oid)
{
return git_oid_cmp(&obj->oid, oid);
}
GIT_INLINE(void) git_cached_obj_incref(git_cached_obj *obj)
{
git_atomic_inc(&obj->refcount);
}
GIT_INLINE(void) git_cached_obj_decref(git_cached_obj *obj, git_cached_obj_freeptr free_obj)
{
if (git_atomic_dec(&obj->refcount) == 0)
free_obj(obj);
}
#endif
......@@ -29,9 +29,12 @@
#include "git2/signature.h"
#include "common.h"
#include "odb.h"
#include "commit.h"
#include "signature.h"
#include <stdarg.h>
#define COMMIT_BASIC_PARSE 0x0
#define COMMIT_FULL_PARSE 0x1
......@@ -71,35 +74,165 @@ const git_oid *git_commit_id(git_commit *c)
return git_object_id((git_object *)c);
}
int git_commit__writeback(git_commit *commit, git_odb_source *src)
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,
...)
{
unsigned int i;
va_list ap;
int i, error;
const git_oid **oids;
git__write_oid(src, "tree", &commit->tree_oid);
oids = git__malloc(parent_count * sizeof(git_oid *));
for (i = 0; i < commit->parent_oids.length; ++i) {
git_oid *parent_oid;
va_start(ap, parent_count);
for (i = 0; i < parent_count; ++i)
oids[i] = va_arg(ap, const git_oid *);
va_end(ap);
parent_oid = git_vector_get(&commit->parent_oids, i);
git__write_oid(src, "parent", parent_oid);
}
error = git_commit_create(
oid, repo, update_ref, author, committer, message,
tree_oid, parent_count, oids);
free(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,
const git_tree *tree,
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] = git_object_id(va_arg(ap, const git_object *));
va_end(ap);
error = git_commit_create(
oid, repo, update_ref, author, committer, message,
git_object_id((git_object *)tree),
parent_count, oids);
free(oids);
return error;
}
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(oids);
return error;
}
if (commit->author == NULL)
return GIT_EMISSINGOBJDATA;
int git_commit_create(
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,
const git_oid *parents[])
{
size_t final_size = 0;
int message_length, author_length, committer_length;
char *author_str, *committer_str;
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;
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;
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);
git_signature__write(src, "author", commit->author);
for (i = 0; i < parent_count; ++i)
git__write_oid(stream, "parent", parents[i]);
if (commit->committer == NULL)
return GIT_EMISSINGOBJDATA;
stream->write(stream, author_str, author_length);
free(author_str);
git_signature__write(src, "committer", commit->committer);
stream->write(stream, committer_str, committer_length);
free(committer_str);
if (commit->message != NULL) {
git__source_write(src, "\n", 1);
git__source_write(src, commit->message, strlen(commit->message));
stream->write(stream, "\n", 1);
stream->write(stream, message, message_length);
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error == GIT_SUCCESS && update_ref != NULL) {
git_reference *head;
error = git_reference_lookup(&head, repo, update_ref);
if (error < GIT_SUCCESS)
return error;
if (git_reference_type(head) == GIT_REF_SYMBOLIC) {
if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS)
return error;
}
error = git_reference_set_oid(head, oid);
}
return GIT_SUCCESS;
return error;
}
int commit_parse_buffer(git_commit *commit, void *data, size_t len)
......@@ -110,12 +243,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len)
git_oid parent_oid;
int error;
/* first parse; the vector hasn't been initialized yet */
if (commit->parent_oids.contents == NULL) {
git_vector_init(&commit->parent_oids, 4, NULL);
}
clear_parents(commit);
git_vector_init(&commit->parent_oids, 4, NULL);
if ((error = git__parse_oid(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
return error;
......@@ -134,17 +262,11 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len)
return GIT_ENOMEM;
}
if (commit->author)
git_signature_free(commit->author);
commit->author = git__malloc(sizeof(git_signature));
if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ")) < GIT_SUCCESS)
return error;
/* Always parse the committer; we need the commit time */
if (commit->committer)
git_signature_free(commit->committer);
commit->committer = git__malloc(sizeof(git_signature));
if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ")) < GIT_SUCCESS)
return error;
......@@ -176,11 +298,10 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len)
return GIT_SUCCESS;
}
int git_commit__parse(git_commit *commit)
int git_commit__parse(git_commit *commit, git_odb_object *obj)
{
assert(commit && commit->object.source.open);
return commit_parse_buffer(commit,
commit->object.source.raw.data, commit->object.source.raw.len);
assert(commit);
return commit_parse_buffer(commit, obj->raw.data, obj->raw.len);
}
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
......@@ -218,82 +339,3 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
}
int git_commit_set_tree(git_commit *commit, git_tree *tree)
{
const git_oid *oid;
assert(commit && tree);
if ((oid = git_object_id((git_object *)tree)) == NULL)
return GIT_EMISSINGOBJDATA;
commit->object.modified = 1;
git_oid_cpy(&commit->tree_oid, oid);
return GIT_SUCCESS;
}
int git_commit_add_parent(git_commit *commit, git_commit *new_parent)
{
const git_oid *parent_oid;
git_oid *new_oid;
assert(commit && new_parent);
if ((parent_oid = git_object_id((git_object *)new_parent)) == NULL)
return GIT_EMISSINGOBJDATA;
new_oid = git__malloc(sizeof(git_oid));
if (new_oid == NULL)
return GIT_ENOMEM;
commit->object.modified = 1;
git_oid_cpy(new_oid, parent_oid);
return git_vector_insert(&commit->parent_oids, new_oid);
}
void git_commit_set_author(git_commit *commit, const git_signature *author_sig)
{
assert(commit && author_sig);
commit->object.modified = 1;
git_signature_free(commit->author);
commit->author = git_signature_dup(author_sig);
}
void git_commit_set_committer(git_commit *commit, const git_signature *committer_sig)
{
assert(commit && committer_sig);
commit->object.modified = 1;
git_signature_free(commit->committer);
commit->committer = git_signature_dup(committer_sig);
}
void git_commit_set_message(git_commit *commit, const char *message)
{
const char *line_end;
size_t message_len;
commit->object.modified = 1;
if (commit->message)
free(commit->message);
if (commit->message_short)
free(commit->message_short);
commit->message = git__strdup(message);
/* Short message */
if((line_end = strchr(message, '\n')) == NULL) {
commit->message_short = git__strdup(message);
return;
}
message_len = line_end - message;
commit->message_short = git__malloc(message_len + 1);
memcpy(commit->message_short, message, message_len);
commit->message_short[message_len] = 0;
}
......@@ -22,8 +22,6 @@ struct git_commit {
};
void git_commit__free(git_commit *c);
int git_commit__parse(git_commit *commit);
int git_commit__writeback(git_commit *commit, git_odb_source *src);
int git_commit__parse(git_commit *commit, git_odb_object *obj);
#endif
#ifndef INCLUDE_delta_apply_h__
#define INCLUDE_delta_apply_h__
#include "odb.h"
/**
* Apply a git binary delta to recover the original content.
*
......
......@@ -27,7 +27,8 @@ static struct {
{GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"},
{GIT_EINVALIDPATH, "The path is invalid" },
{GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"}
{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
{GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"}
};
const char *git_strerror(int num)
......
......@@ -77,43 +77,81 @@ void git_filebuf_cleanup(git_filebuf *file)
if (file->fd >= 0)
gitfo_close(file->fd);
if (gitfo_exists(file->path_lock) == GIT_SUCCESS)
if (file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS)
gitfo_unlink(file->path_lock);
if (file->digest)
git_hash_free_ctx(file->digest);
free(file->buffer);
free(file->z_buf);
#ifdef GIT_FILEBUF_THREADS
free(file->buffer_back);
#endif
deflateEnd(&file->zs);
free(file->path_original);
free(file->path_lock);
}
static int flush_buffer(git_filebuf *file)
GIT_INLINE(int) flush_buffer(git_filebuf *file)
{
int result = GIT_SUCCESS;
int result = file->write(file, file->buffer, file->buf_pos);
file->buf_pos = 0;
return result;
}
if (file->buf_pos > 0) {
result = gitfo_write(file->fd, file->buffer, file->buf_pos);
if (file->digest)
git_hash_update(file->digest, file->buffer, file->buf_pos);
static int write_normal(git_filebuf *file, const void *source, size_t len)
{
int result = 0;
file->buf_pos = 0;
if (len > 0) {
result = gitfo_write(file->fd, (void *)source, len);
if (file->digest)
git_hash_update(file->digest, source, len);
}
return result;
}
static int write_deflate(git_filebuf *file, const void *source, size_t len)
{
int result = Z_OK;
z_stream *zs = &file->zs;
if (len > 0 || file->flush_mode == Z_FINISH) {
zs->next_in = (void *)source;
zs->avail_in = len;
do {
int have;
zs->next_out = file->z_buf;
zs->avail_out = file->buf_size;
result = deflate(zs, file->flush_mode);
assert(result != Z_STREAM_ERROR);
have = file->buf_size - zs->avail_out;
if (gitfo_write(file->fd, file->z_buf, have) < GIT_SUCCESS)
return GIT_EOSERR;
} while (zs->avail_out == 0);
assert(zs->avail_in == 0);
if (file->digest)
git_hash_update(file->digest, source, len);
}
return GIT_SUCCESS;
}
int git_filebuf_open(git_filebuf *file, const char *path, int flags)
{
int error;
size_t path_len;
if (file == NULL || path == NULL)
if (file == NULL)
return GIT_ERROR;
memset(file, 0x0, sizeof(git_filebuf));
......@@ -122,46 +160,93 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
file->buf_pos = 0;
file->fd = -1;
path_len = strlen(path);
file->path_original = git__strdup(path);
if (file->path_original == NULL) {
/* Allocate the main cache buffer */
file->buffer = git__malloc(file->buf_size);
if (file->buffer == NULL){
error = GIT_ENOMEM;
goto cleanup;
}
file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
if (file->path_lock == NULL) {
error = GIT_ENOMEM;
goto cleanup;
/* If we are hashing on-write, allocate a new hash context */
if (flags & GIT_FILEBUF_HASH_CONTENTS) {
if ((file->digest = git_hash_new_ctx()) == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
}
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
/* If we are deflating on-write, */
if (flags & GIT_FILEBUF_DEFLATE_CONTENTS) {
file->buffer = git__malloc(file->buf_size);
if (file->buffer == NULL){
error = GIT_ENOMEM;
goto cleanup;
}
/* Initialize the ZLib stream */
if (deflateInit(&file->zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
error = GIT_EZLIB;
goto cleanup;
}
#ifdef GIT_FILEBUF_THREADS
file->buffer_back = git__malloc(file->buf_size);
if (file->buffer_back == NULL){
error = GIT_ENOMEM;
goto cleanup;
/* Allocate the Zlib cache buffer */
file->z_buf = git__malloc(file->buf_size);
if (file->z_buf == NULL){
error = GIT_ENOMEM;
goto cleanup;
}
/* Never flush */
file->flush_mode = Z_NO_FLUSH;
file->write = &write_deflate;
} else {
file->write = &write_normal;
}
#endif
if (flags & GIT_FILEBUF_HASH_CONTENTS) {
if ((file->digest = git_hash_new_ctx()) == NULL) {
/* If we are writing to a temp file */
if (flags & GIT_FILEBUF_TEMPORARY) {
char tmp_path[GIT_PATH_MAX];
/* Open the file as temporary for locking */
file->fd = gitfo_creat_tmp(tmp_path, "_filebuf_");
if (file->fd < 0) {
error = GIT_EOSERR;
goto cleanup;
}
/* No original path */
file->path_original = NULL;
file->path_lock = git__strdup(tmp_path);
if (file->path_lock == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
}
} else {
/* If the file is not temporary, make sure we have a path */
if (path == NULL) {
error = GIT_ERROR;
goto cleanup;
}
if ((error = lock_file(file, flags)) < GIT_SUCCESS)
goto cleanup;
path_len = strlen(path);
/* Save the original path of the file */
file->path_original = git__strdup(path);
if (file->path_original == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
/* create the locking path by appending ".lock" to the original */
file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
if (file->path_lock == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
/* open the file for locking */
if ((error = lock_file(file, flags)) < GIT_SUCCESS)
goto cleanup;
}
return GIT_SUCCESS;
......@@ -187,10 +272,25 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file)
return GIT_SUCCESS;
}
int git_filebuf_commit_at(git_filebuf *file, const char *path)
{
free(file->path_original);
file->path_original = git__strdup(path);
if (file->path_original == NULL)
return GIT_ENOMEM;
return git_filebuf_commit(file);
}
int git_filebuf_commit(git_filebuf *file)
{
int error;
/* tmp file cannot be commited */
if (file->path_original == NULL)
return GIT_EOSERR;
file->flush_mode = Z_FINISH;
if ((error = flush_buffer(file)) < GIT_SUCCESS)
goto cleanup;
......@@ -204,16 +304,16 @@ cleanup:
return error;
}
GIT_INLINE(void) add_to_cache(git_filebuf *file, void *buf, size_t len)
GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
{
memcpy(file->buffer + file->buf_pos, buf, len);
file->buf_pos += len;
}
int git_filebuf_write(git_filebuf *file, void *buff, size_t len)
int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
{
int error;
unsigned char *buf = buff;
const unsigned char *buf = buff;
for (;;) {
size_t space_left = file->buf_size - file->buf_pos;
......@@ -237,9 +337,9 @@ int git_filebuf_write(git_filebuf *file, void *buff, size_t len)
/* write too-large chunks immediately */
if (len > file->buf_size) {
error = gitfo_write(file->fd, buf, len);
if (file->digest)
git_hash_update(file->digest, buf, len);
error = file->write(file, buf, len);
if (error < GIT_SUCCESS)
return error;
}
}
}
......
......@@ -3,14 +3,17 @@
#include "fileops.h"
#include "hash.h"
#include "git2/zlib.h"
#ifdef GIT_THREADS
# define GIT_FILEBUF_THREADS
#endif
#define GIT_FILEBUF_HASH_CONTENTS 0x1
#define GIT_FILEBUF_APPEND 0x2
#define GIT_FILEBUF_FORCE 0x4
#define GIT_FILEBUF_HASH_CONTENTS (1 << 0)
#define GIT_FILEBUF_APPEND (1 << 2)
#define GIT_FILEBUF_FORCE (1 << 3)
#define GIT_FILEBUF_TEMPORARY (1 << 4)
#define GIT_FILEBUF_DEFLATE_CONTENTS (1 << 5)
#define GIT_FILELOCK_EXTENSION ".lock\0"
#define GIT_FILELOCK_EXTLENGTH 6
......@@ -19,12 +22,16 @@ struct git_filebuf {
char *path_original;
char *path_lock;
int (*write)(struct git_filebuf *file,
const void *source, size_t len);
git_hash_ctx *digest;
unsigned char *buffer;
#ifdef GIT_FILEBUF_THREADS
unsigned char *buffer_back;
#endif
unsigned char *z_buf;
z_stream zs;
int flush_mode;
size_t buf_size, buf_pos;
git_file fd;
......@@ -32,12 +39,13 @@ struct git_filebuf {
typedef struct git_filebuf git_filebuf;
int git_filebuf_write(git_filebuf *lock, void *buff, size_t len);
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_open(git_filebuf *lock, const char *path, int flags);
int git_filebuf_commit(git_filebuf *lock);
int git_filebuf_commit_at(git_filebuf *lock, const char *path);
void git_filebuf_cleanup(git_filebuf *lock);
int git_filebuf_hash(git_oid *oid, git_filebuf *file);
......
......@@ -2,13 +2,13 @@
#include "fileops.h"
#include <ctype.h>
static int force_path(const char *to)
int gitfo_mkdir_2file(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), to);
error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
if (error < GIT_SUCCESS)
return error;
......@@ -25,6 +25,87 @@ static int force_path(const char *to)
return GIT_SUCCESS;
}
static int creat_tempfile(char *path_out, const char *tmp_dir, const char *filename)
{
int fd;
git__joinpath(path_out, tmp_dir, filename);
strcat(path_out, "_git2_XXXXXX");
#ifdef GIT_WIN32
/* 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
return fd >= 0 ? fd : GIT_EOSERR;
}
static const char *find_tmpdir(void)
{
static int tmpdir_not_found = 0;
static char temp_dir[GIT_PATH_MAX];
static const char *env_vars[] = {
"TEMP", "TMP", "TMPDIR"
};
unsigned int i, j;
char test_file[GIT_PATH_MAX];
if (tmpdir_not_found)
return NULL;
if (temp_dir[0] != '\0')
return temp_dir;
for (i = 0; i < ARRAY_SIZE(env_vars); ++i) {
char *env_path;
env_path = getenv(env_vars[i]);
if (env_path == NULL)
continue;
strcpy(temp_dir, env_path);
/* Fix backslashes because Windows environment vars
* are probably fucked up */
for (j = 0; j < strlen(temp_dir); ++j)
if (temp_dir[j] == '\\')
temp_dir[j] = '/';
if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) {
gitfo_unlink(test_file);
return temp_dir;
}
}
/* last resort: current folder. */
strcpy(temp_dir, "./");
if (creat_tempfile(test_file, temp_dir, "writetest") >= 0) {
gitfo_unlink(test_file);
return temp_dir;
}
tmpdir_not_found = 1;
return NULL;
}
int gitfo_creat_tmp(char *path_out, const char *filename)
{
const char *tmp_dir;
tmp_dir = find_tmpdir();
if (tmp_dir == NULL)
return GIT_EOSERR;
return creat_tempfile(path_out, tmp_dir, filename);
}
int gitfo_open(const char *path, int flags)
{
int fd = open(path, flags | O_BINARY);
......@@ -39,7 +120,7 @@ int gitfo_creat(const char *path, int mode)
int gitfo_creat_force(const char *path, int mode)
{
if (force_path(path) < GIT_SUCCESS)
if (gitfo_mkdir_2file(path) < GIT_SUCCESS)
return GIT_EOSERR;
return gitfo_creat(path, mode);
......@@ -117,6 +198,7 @@ int gitfo_isdir(const char *path)
int gitfo_exists(const char *path)
{
assert(path);
return access(path, F_OK);
}
......@@ -198,7 +280,7 @@ int gitfo_mv(const char *from, const char *to)
int gitfo_mv_force(const char *from, const char *to)
{
if (force_path(to) < GIT_SUCCESS)
if (gitfo_mkdir_2file(to) < GIT_SUCCESS)
return GIT_EOSERR;
return gitfo_mv(from, to);
......
......@@ -58,8 +58,10 @@ 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_creat_tmp(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)
extern int gitfo_read(git_file fd, void *buf, size_t cnt);
......
......@@ -324,7 +324,7 @@ int git_index_add(git_index *index, const char *rel_path, int stage)
entry.file_size = st.st_size;
/* write the blob to disk and get the oid */
if ((error = git_blob_writefile(&entry.oid, index->repository, full_path)) < GIT_SUCCESS)
if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS)
return error;
entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
......
......@@ -66,153 +66,6 @@ static struct {
{ "REF_DELTA", 0, 0 }
};
/*
* Object source methods
*
* Abstract buffer methods that allow the writeback system
* to prepare the contents of any git file in-memory before
* writing them to disk.
*/
static int source_resize(git_odb_source *src)
{
size_t write_offset, new_size;
void *new_data;
write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data);
new_size = src->raw.len * 2;
if ((new_data = git__malloc(new_size)) == NULL)
return GIT_ENOMEM;
memcpy(new_data, src->raw.data, src->written_bytes);
free(src->raw.data);
src->raw.data = new_data;
src->raw.len = new_size;
src->write_ptr = (char *)new_data + write_offset;
return GIT_SUCCESS;
}
int git__source_printf(git_odb_source *source, const char *format, ...)
{
va_list arglist;
int len;
assert(source->open && source->write_ptr);
va_start(arglist, format);
len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
while (source->written_bytes + len >= source->raw.len) {
if (source_resize(source) < GIT_SUCCESS)
return GIT_ENOMEM;
len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
}
source->write_ptr = (char *)source->write_ptr + len;
source->written_bytes += len;
return GIT_SUCCESS;
}
int git__source_write(git_odb_source *source, const void *bytes, size_t len)
{
assert(source);
assert(source->open && source->write_ptr);
while (source->written_bytes + len >= source->raw.len) {
if (source_resize(source) < GIT_SUCCESS)
return GIT_ENOMEM;
}
memcpy(source->write_ptr, bytes, len);
source->write_ptr = (char *)source->write_ptr + len;
source->written_bytes += len;
return GIT_SUCCESS;
}
static void prepare_write(git_object *object)
{
if (object->source.write_ptr != NULL || object->source.open)
git_object__source_close(object);
/* TODO: proper size calculation */
object->source.raw.data = git__malloc(OBJECT_BASE_SIZE);
object->source.raw.len = OBJECT_BASE_SIZE;
object->source.write_ptr = object->source.raw.data;
object->source.written_bytes = 0;
object->source.open = 1;
}
static int write_back(git_object *object)
{
int error;
git_oid new_id;
assert(object);
assert(object->source.open);
assert(object->modified);
object->source.raw.len = object->source.written_bytes;
if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < GIT_SUCCESS)
return error;
if (object->in_memory) {
int idx = git_vector_search(&object->repo->memory_objects, object);
git_vector_remove(&object->repo->memory_objects, idx);
} else {
git_hashtable_remove(object->repo->objects, &object->id);
}
git_oid_cpy(&object->id, &new_id);
git_hashtable_insert(object->repo->objects, &object->id, object);
object->source.write_ptr = NULL;
object->source.written_bytes = 0;
object->modified = 0;
object->in_memory = 0;
git_object__source_close(object);
return GIT_SUCCESS;
}
int git_object__source_open(git_object *object)
{
int error;
assert(object && !object->in_memory);
if (object->source.open)
git_object__source_close(object);
error = git_odb_read(&object->source.raw, object->repo->db, &object->id);
if (error < GIT_SUCCESS)
return error;
object->source.open = 1;
return GIT_SUCCESS;
}
void git_object__source_close(git_object *object)
{
assert(object);
if (object->source.open) {
git_rawobj_close(&object->source.raw);
object->source.open = 0;
}
}
static int create_object(git_object **object_out, git_otype type)
{
git_object *object = NULL;
......@@ -225,43 +78,19 @@ static int create_object(git_object **object_out, git_otype type)
case GIT_OBJ_COMMIT:
case GIT_OBJ_TAG:
case GIT_OBJ_BLOB:
case GIT_OBJ_TREE:
object = git__malloc(git_object__size(type));
if (object == NULL)
return GIT_ENOMEM;
memset(object, 0x0, git_object__size(type));
break;
case GIT_OBJ_TREE:
object = (git_object *)git_tree__new();
if (object == NULL)
return GIT_ENOMEM;
break;
default:
return GIT_EINVALIDTYPE;
}
*object_out = object;
return GIT_SUCCESS;
}
int git_object_new(git_object **object_out, git_repository *repo, git_otype type)
{
git_object *object = NULL;
int error;
assert(object_out && repo);
if ((error = create_object(&object, type)) < GIT_SUCCESS)
return error;
object->repo = repo;
object->in_memory = 1;
object->modified = 1;
object->source.raw.type = type;
object->type = type;
object->lru = ++repo->lru_counter;
*object_out = object;
return GIT_SUCCESS;
}
......@@ -269,126 +98,77 @@ int git_object_new(git_object **object_out, git_repository *repo, git_otype type
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
{
git_object *object = NULL;
git_rawobj obj_file;
git_odb_object *odb_obj;
int error = GIT_SUCCESS;
assert(repo && object_out && id);
object = git_hashtable_lookup(repo->objects, id);
object = git_cache_get(&repo->objects, id);
if (object != NULL) {
if (type != GIT_OBJ_ANY && type != object->source.raw.type)
if (type != GIT_OBJ_ANY && type != object->type)
return GIT_EINVALIDTYPE;
*object_out = object;
object->lru = ++repo->lru_counter;
object->can_free = 0;
return GIT_SUCCESS;
}
error = git_odb_read(&obj_file, repo->db, id);
error = git_odb_read(&odb_obj, repo->db, id);
if (error < GIT_SUCCESS)
return error;
if (type != GIT_OBJ_ANY && type != obj_file.type) {
git_rawobj_close(&obj_file);
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
git_odb_object_close(odb_obj);
return GIT_EINVALIDTYPE;
}
type = obj_file.type;
type = odb_obj->raw.type;
if ((error = create_object(&object, type)) < GIT_SUCCESS)
return error;
/* Initialize parent object */
git_oid_cpy(&object->id, id);
git_oid_cpy(&object->cached.oid, id);
object->repo = repo;
memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
object->source.open = 1;
switch (type) {
case GIT_OBJ_COMMIT:
error = git_commit__parse((git_commit *)object);
error = git_commit__parse((git_commit *)object, odb_obj);
break;
case GIT_OBJ_TREE:
error = git_tree__parse((git_tree *)object);
error = git_tree__parse((git_tree *)object, odb_obj);
break;
case GIT_OBJ_TAG:
error = git_tag__parse((git_tag *)object);
error = git_tag__parse((git_tag *)object, odb_obj);
break;
case GIT_OBJ_BLOB:
error = git_blob__parse((git_blob *)object);
error = git_blob__parse((git_blob *)object, odb_obj);
break;
default:
break;
}
git_odb_object_close(odb_obj);
if (error < GIT_SUCCESS) {
git_object__free(object);
return error;
}
git_object__source_close(object);
git_hashtable_insert(repo->objects, &object->id, object);
object->lru = ++repo->lru_counter;
*object_out = object;
*object_out = git_cache_try_store(&repo->objects, object);
return GIT_SUCCESS;
}
int git_object_write(git_object *object)
void git_object__free(void *_obj)
{
int error;
git_odb_source *source;
git_object *object = (git_object *)_obj;
assert(object);
if (object->modified == 0)
return GIT_SUCCESS;
prepare_write(object);
source = &object->source;
switch (source->raw.type) {
case GIT_OBJ_COMMIT:
error = git_commit__writeback((git_commit *)object, source);
break;
case GIT_OBJ_TREE:
error = git_tree__writeback((git_tree *)object, source);
break;
case GIT_OBJ_TAG:
error = git_tag__writeback((git_tag *)object, source);
break;
case GIT_OBJ_BLOB:
error = git_blob__writeback((git_blob *)object, source);
break;
default:
error = GIT_ERROR;
break;
}
if (error < GIT_SUCCESS) {
git_object__source_close(object);
return error;
}
return write_back(object);
}
void git_object__free(git_object *object)
{
assert(object);
git_object__source_close(object);
switch (object->source.raw.type) {
switch (object->type) {
case GIT_OBJ_COMMIT:
git_commit__free((git_commit *)object);
break;
......@@ -416,29 +196,19 @@ void git_object_close(git_object *object)
if (object == NULL)
return;
if (object->in_memory) {
int idx = git_vector_search(&object->repo->memory_objects, object);
git_vector_remove(&object->repo->memory_objects, idx);
git_object__free(object);
} else {
object->can_free = 1;
}
git_cached_obj_decref((git_cached_obj *)object, git_object__free);
}
const git_oid *git_object_id(const git_object *obj)
{
assert(obj);
if (obj->in_memory)
return NULL;
return &obj->id;
return &obj->cached.oid;
}
git_otype git_object_type(const git_object *obj)
{
assert(obj);
return obj->source.raw.type;
return obj->type;
}
git_repository *git_object_owner(const git_object *obj)
......
......@@ -87,56 +87,49 @@ int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *ob
return GIT_SUCCESS;
}
void git_rawobj_close(git_rawobj *obj)
{
free(obj->data);
obj->data = NULL;
}
int git_rawobj_hash(git_oid *id, git_rawobj *obj)
static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)
{
char hdr[64];
int hdrlen;
git_odb_object *object = git__malloc(sizeof(git_odb_object));
memset(object, 0x0, sizeof(git_odb_object));
assert(id && obj);
git_oid_cpy(&object->cached.oid, oid);
memcpy(&object->raw, source, sizeof(git_rawobj));
return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj);
return object;
}
int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
static void free_odb_object(void *o)
{
z_stream zs;
int status = Z_OK;
memset(&zs, 0x0, sizeof(zs));
zs.next_out = out;
zs.avail_out = outlen;
zs.next_in = in;
zs.avail_in = inlen;
git_odb_object *object = (git_odb_object *)o;
if (inflateInit(&zs) < Z_OK)
return GIT_ERROR;
if (object != NULL) {
free(object->raw.data);
free(object);
}
}
while (status == Z_OK)
status = inflate(&zs, Z_FINISH);
void git_odb_object_close(git_odb_object *object)
{
git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
}
inflateEnd(&zs);
int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
{
char hdr[64];
int hdrlen;
git_rawobj raw;
if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */)
return GIT_ERROR;
assert(id);
if (zs.total_out != outlen)
return GIT_ERROR;
raw.data = (void *)data;
raw.len = len;
raw.type = type;
return GIT_SUCCESS;
return git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, &raw);
}
/***********************************************************
*
* OBJECT DATABASE PUBLIC API
......@@ -162,6 +155,8 @@ int git_odb_new(git_odb **out)
if (!db)
return GIT_ENOMEM;
git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object);
if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
free(db);
return GIT_ENOMEM;
......@@ -306,16 +301,23 @@ void git_odb_close(git_odb *db)
}
git_vector_free(&db->backends);
git_cache_free(&db->cache);
free(db);
}
int git_odb_exists(git_odb *db, const git_oid *id)
{
git_odb_object *object;
unsigned int i;
int found = 0;
assert(db && id);
if ((object = git_cache_get(&db->cache, id)) != NULL) {
git_odb_object_close(object);
return 1;
}
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
......@@ -327,19 +329,27 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return found;
}
int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id)
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{
unsigned int i;
int error = GIT_ENOTFOUND;
git_odb_object *object;
assert(out && db && id);
assert(db && id);
if ((object = git_cache_get(&db->cache, id)) != NULL) {
*len_p = object->raw.len;
*type_p = object->raw.type;
git_odb_object_close(object);
return GIT_SUCCESS;
}
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->read_header != NULL)
error = b->read_header(out, b, id);
error = b->read_header(len_p, type_p, b, id);
}
/*
......@@ -347,37 +357,50 @@ int git_odb_read_header(git_rawobj *out, git_odb *db, const git_oid *id)
* try reading the whole object and freeing the contents
*/
if (error < 0) {
error = git_odb_read(out, db, id);
git_rawobj_close(out);
if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS)
return error;
*len_p = object->raw.len;
*type_p = object->raw.len;
git_odb_object_close(object);
}
return error;
return GIT_SUCCESS;
}
int git_odb_read(git_rawobj *out, git_odb *db, const git_oid *id)
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
{
unsigned int i;
int error = GIT_ENOTFOUND;
git_rawobj raw;
assert(out && db && id);
*out = git_cache_get(&db->cache, id);
if (*out != NULL)
return GIT_SUCCESS;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->read != NULL)
error = b->read(out, b, id);
error = b->read(&raw.data, &raw.len, &raw.type, b, id);
}
if (error == GIT_SUCCESS) {
*out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
}
return error;
}
int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj)
int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
{
unsigned int i;
int error = GIT_ERROR;
assert(obj && db && id);
assert(stream && db);
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
......@@ -387,8 +410,26 @@ int git_odb_write(git_oid *id, git_odb *db, git_rawobj *obj)
if (internal->is_alternate)
continue;
if (b->write != NULL)
error = b->write(id, b, obj);
if (b->writestream != NULL)
error = b->writestream(stream, b, size, type);
}
return error;
}
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
{
unsigned int i;
int error = GIT_ERROR;
assert(stream && db);
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->readstream != NULL)
error = b->readstream(stream, b, oid);
}
return error;
......
......@@ -3,15 +3,31 @@
#include "git2/odb.h"
#include "git2/oid.h"
#include "git2/types.h"
#include "vector.h"
#include "cache.h"
/* DO NOT EXPORT */
typedef struct {
void *data; /**< Raw, decompressed object data. */
size_t len; /**< Total number of bytes in data. */
git_otype type; /**< Type of this object. */
} git_rawobj;
/* EXPORT */
struct git_odb_object {
git_cached_obj cached;
git_rawobj raw;
};
/* EXPORT */
struct git_odb {
void *_internal;
git_vector backends;
git_cache cache;
};
int git_odb__hash_obj(git_oid *id, char *hdr, size_t n, int *len, git_rawobj *obj);
int git_odb__inflate_buffer(void *in, size_t inlen, void *out, size_t outlen);
#endif
......@@ -1243,7 +1243,7 @@ static int packfile_unpack_delta(
error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type);
if (error < GIT_SUCCESS) {
git_rawobj_close(&base);
free(base.data);
return error;
}
......@@ -1252,8 +1252,8 @@ static int packfile_unpack_delta(
base.data, base.len,
delta.data, delta.len);
git_rawobj_close(&base);
git_rawobj_close(&delta);
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);
......@@ -1337,15 +1337,23 @@ int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const g
}
*/
int pack_backend__read(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
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;
git_rawobj raw;
int error;
if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS)
return error;
return packfile_unpack(obj, (struct pack_backend *)backend, e.p, e.offset);
if ((error = packfile_unpack(&raw, (struct pack_backend *)backend, e.p, e.offset)) < GIT_SUCCESS)
return error;
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
return GIT_SUCCESS;
}
int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
......@@ -1397,7 +1405,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
backend->parent.read = &pack_backend__read;
backend->parent.read_header = NULL;
backend->parent.write = NULL;
backend->parent.exists = &pack_backend__exists;
backend->parent.free = &pack_backend__free;
......
......@@ -143,14 +143,18 @@ int git__parse_oid(git_oid *oid, char **buffer_out,
return GIT_SUCCESS;
}
int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid)
int git__write_oid(git_odb_stream *stream, const char *header, const git_oid *oid)
{
char hex_oid[41];
char hex_oid[42];
git_oid_fmt(hex_oid, oid);
hex_oid[40] = 0;
git_oid_fmt(hex_oid + 1, oid);
return git__source_printf(src, "%s %s\n", header, hex_oid);
hex_oid[0] = ' ';
hex_oid[41] = '\n';
stream->write(stream, header, strlen(header));
stream->write(stream, hex_oid, 42);
return GIT_SUCCESS;
}
void git_oid_mkraw(git_oid *out, const unsigned char *raw)
......
......@@ -40,30 +40,12 @@
#define GIT_BRANCH_MASTER "master"
static const int OBJECT_TABLE_SIZE = 32;
typedef struct {
char *path_repository;
unsigned is_bare:1, has_been_reinit:1;
} repo_init;
/*
* Hash table methods
*
* Callbacks for the ODB cache, implemented
* as a hash table
*/
static uint32_t object_table_hash(const void *key, int hash_id)
{
uint32_t r;
git_oid *id;
id = (git_oid *)key;
memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
return r;
}
/*
* Git repository open methods
*
* Open a repository object from its path
......@@ -186,25 +168,9 @@ static git_repository *repository_alloc()
memset(repo, 0x0, sizeof(git_repository));
repo->objects = git_hashtable_alloc(
OBJECT_TABLE_SIZE,
object_table_hash,
(git_hash_keyeq_ptr)git_oid_cmp);
if (repo->objects == NULL) {
free(repo);
return NULL;
}
git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free);
if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) {
git_hashtable_free(repo->objects);
free(repo);
return NULL;
}
if (git_vector_init(&repo->memory_objects, 16, NULL) < GIT_SUCCESS) {
git_hashtable_free(repo->objects);
git_repository__refcache_free(&repo->references);
free(repo);
return NULL;
}
......@@ -330,51 +296,19 @@ cleanup:
return error;
}
int git_repository_gc(git_repository *repo)
{
int collected = 0;
git_object *object;
const void *_unused;
GIT_HASHTABLE_FOREACH(repo->objects, _unused, object,
if (object->can_free) {
git_object__free(object);
collected++;
}
);
return collected;
}
void git_repository_free(git_repository *repo)
{
git_object *object;
const void *_unused;
unsigned int i;
if (repo == NULL)
return;
/* force free all the objects */
GIT_HASHTABLE_FOREACH(repo->objects, _unused, object,
git_object__free(object);
);
for (i = 0; i < repo->memory_objects.length; ++i) {
object = git_vector_get(&repo->memory_objects, i);
git_object__free(object);
}
git_cache_free(&repo->objects);
git_repository__refcache_free(&repo->references);
free(repo->path_workdir);
free(repo->path_index);
free(repo->path_repository);
free(repo->path_odb);
git_hashtable_free(repo->objects);
git_vector_free(&repo->memory_objects);
git_repository__refcache_free(&repo->references);
if (repo->db != NULL)
git_odb_close(repo->db);
......
......@@ -9,6 +9,7 @@
#include "hashtable.h"
#include "index.h"
#include "cache.h"
#include "refs.h"
#define DOT_GIT ".git"
......@@ -16,28 +17,17 @@
#define GIT_OBJECTS_DIR "objects/"
#define GIT_INDEX_FILE "index"
typedef struct {
git_rawobj raw;
void *write_ptr;
size_t written_bytes;
int open:1;
} git_odb_source;
struct git_object {
git_oid id;
git_cached_obj cached;
git_repository *repo;
git_odb_source source;
unsigned int lru;
unsigned char in_memory, modified, can_free, _pad;
git_otype type;
};
struct git_repository {
git_odb *db;
git_index *index;
git_hashtable *objects;
git_vector memory_objects;
git_cache objects;
git_refcache references;
char *path_repository;
......@@ -49,17 +39,11 @@ struct git_repository {
unsigned int lru_counter;
};
int git_object__source_open(git_object *object);
void git_object__source_close(git_object *object);
/* fully free the object; internal method, do not
* export */
void git_object__free(git_object *object);
int git__source_printf(git_odb_source *source, const char *format, ...);
int git__source_write(git_odb_source *source, const void *bytes, size_t len);
void git_object__free(void *object);
int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid);
int git__write_oid(git_odb_stream *src, const char *header, const git_oid *oid);
#endif
......@@ -25,6 +25,7 @@
#include "common.h"
#include "commit.h"
#include "odb.h"
#include "hashtable.h"
#include "pqueue.h"
......@@ -236,22 +237,22 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
static int commit_parse(git_revwalk *walk, commit_object *commit)
{
git_rawobj data;
git_odb_object *obj;
int error;
if (commit->parsed)
return GIT_SUCCESS;
if ((error = git_odb_read(&data, walk->repo->db, &commit->oid)) < GIT_SUCCESS)
if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS)
return error;
if (data.type != GIT_OBJ_COMMIT) {
git_rawobj_close(&data);
if (obj->raw.type != GIT_OBJ_COMMIT) {
git_odb_object_close(obj);
return GIT_EOBJTYPE;
}
error = commit_quick_parse(walk, commit, &data);
git_rawobj_close(&data);
error = commit_quick_parse(walk, commit, &obj->raw);
git_odb_object_close(obj);
return error;
}
......
......@@ -46,13 +46,7 @@ git_signature *git_signature_new(const char *name, const char *email, time_t tim
goto cleanup;
p->name = git__strdup(name);
if (p->name == NULL)
goto cleanup;
p->email = git__strdup(email);
if (p->email == NULL)
goto cleanup;
p->when.time = time;
p->when.offset = offset;
......@@ -179,10 +173,12 @@ int git_signature__parse(git_signature *sig, char **buffer_out,
return GIT_SUCCESS;
}
int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig)
int git_signature__write(char **signature, const char *header, const git_signature *sig)
{
char sign;
int offset, hours, mins;
char sig_buffer[2048];
int sig_buffer_len;
char sign;
offset = sig->when.offset;
sign = (sig->when.offset < 0) ? '-' : '+';
......@@ -193,7 +189,16 @@ int git_signature__write(git_odb_source *src, const char *header, const git_sign
hours = offset / 60;
mins = offset % 60;
return git__source_printf(src, "%s %s <%s> %u %c%02d%02d\n", header, sig->name, sig->email, (unsigned)sig->when.time, sign, hours, mins);
sig_buffer_len = snprintf(sig_buffer, sizeof(sig_buffer),
"%s %s <%s> %u %c%02d%02d\n",
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;
}
......@@ -7,6 +7,6 @@
#include <time.h>
int git_signature__parse(git_signature *sig, char **buffer_out, const char *buffer_end, const char *header);
int git_signature__write(git_odb_source *src, const char *header, const git_signature *sig);
int git_signature__write(char **signature, const char *header, const git_signature *sig);
#endif
......@@ -56,21 +56,6 @@ const git_oid *git_tag_target_oid(git_tag *t)
return &t->target;
}
int git_tag_set_target(git_tag *tag, git_object *target)
{
const git_oid *oid;
assert(tag && target);
if ((oid = git_object_id(target)) == NULL)
return GIT_EMISSINGOBJDATA;
tag->object.modified = 1;
git_oid_cpy(&tag->target, oid);
tag->type = git_object_type(target);
return GIT_SUCCESS;
}
git_otype git_tag_type(git_tag *t)
{
assert(t);
......@@ -83,50 +68,17 @@ const char *git_tag_name(git_tag *t)
return t->tag_name;
}
void git_tag_set_name(git_tag *tag, const char *name)
{
assert(tag && name);
tag->object.modified = 1;
if (tag->tag_name)
free(tag->tag_name);
tag->tag_name = git__strdup(name);
}
const git_signature *git_tag_tagger(git_tag *t)
{
return t->tagger;
}
void git_tag_set_tagger(git_tag *tag, const git_signature *tagger_sig)
{
assert(tag && tagger_sig);
tag->object.modified = 1;
git_signature_free(tag->tagger);
tag->tagger = git_signature_dup(tagger_sig);
}
const char *git_tag_message(git_tag *t)
{
assert(t);
return t->message;
}
void git_tag_set_message(git_tag *tag, const char *message)
{
assert(tag && message);
tag->object.modified = 1;
if (tag->message)
free(tag->message);
tag->message = git__strdup(message);
}
static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
{
static const char *tag_types[] = {
......@@ -187,9 +139,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
buffer = search + 1;
if (tag->tagger != NULL)
git_signature_free(tag->tagger);
tag->tagger = git__malloc(sizeof(git_signature));
if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0)
......@@ -197,9 +146,6 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
text_len = buffer_end - ++buffer;
if (tag->message != NULL)
free(tag->message);
tag->message = git__malloc(text_len + 1);
memcpy(tag->message, buffer, text_len);
tag->message[text_len] = '\0';
......@@ -207,26 +153,90 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
return GIT_SUCCESS;
}
int git_tag__writeback(git_tag *tag, git_odb_source *src)
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)
{
if (tag->tag_name == NULL || tag->tagger == NULL)
return GIT_EMISSINGOBJDATA;
return git_tag_create(
oid, repo, tag_name,
git_object_id(target),
git_object_type(target),
tagger, message);
}
git__write_oid(src, "object", &tag->target);
git__source_printf(src, "type %s\n", git_object_type2string(tag->type));
git__source_printf(src, "tag %s\n", tag->tag_name);
git_signature__write(src, "tagger", tag->tagger);
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)
{
size_t final_size = 0;
git_odb_stream *stream;
if (tag->message != NULL)
git__source_printf(src, "\n%s", tag->message);
const char *type_str;
char *tagger_str;
return GIT_SUCCESS;
int type_str_len, tag_name_len, tagger_str_len, message_len;
int error;
type_str = git_object_type2string(target_type);
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);
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;
if ((error = git_odb_open_wstream(&stream, repo->db, final_size, GIT_OBJ_TAG)) < GIT_SUCCESS)
return error;
git__write_oid(stream, "object", target);
stream->write(stream, "type ", STRLEN("type "));
stream->write(stream, type_str, type_str_len);
stream->write(stream, "\ntag ", STRLEN("\ntag "));
stream->write(stream, tag_name, tag_name_len);
stream->write(stream, "\n", 1);
stream->write(stream, tagger_str, tagger_str_len);
free(tagger_str);
stream->write(stream, "\n", 1);
stream->write(stream, message, message_len);
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error == GIT_SUCCESS) {
char ref_name[512];
git_reference *new_ref;
git__joinpath(ref_name, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_create_oid(&new_ref, repo, ref_name, oid);
}
return error;
}
int git_tag__parse(git_tag *tag)
int git_tag__parse(git_tag *tag, git_odb_object *obj)
{
assert(tag && tag->object.source.open);
return parse_tag_buffer(tag, tag->object.source.raw.data, (char *)tag->object.source.raw.data + tag->object.source.raw.len);
assert(tag);
return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len);
}
......@@ -3,6 +3,7 @@
#include "git2/tag.h"
#include "repository.h"
#include "odb.h"
struct git_tag {
git_object object;
......@@ -16,7 +17,6 @@ struct git_tag {
};
void git_tag__free(git_tag *tag);
int git_tag__parse(git_tag *tag);
int git_tag__writeback(git_tag *tag, git_odb_source *src);
int git_tag__parse(git_tag *tag, git_odb_object *obj);
#endif
......@@ -8,7 +8,7 @@ typedef struct {
volatile int val;
} git_atomic;
static inline void git_atomic_set(git_atomic *a, int val)
GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
{
a->val = val;
}
......@@ -36,7 +36,7 @@ static inline void git_atomic_set(git_atomic *a, int val)
#define git_cond_signal(c) (void)0 //pthread_cond_signal(c)
#define git_cond_broadcast(c) (void)0 //pthread_cond_broadcast(c)
static inline int git_atomic_inc(git_atomic *a)
GIT_INLINE(int) git_atomic_inc(git_atomic *a)
{
#ifdef __GNUC__
return __sync_add_and_fetch(&a->val, 1);
......@@ -47,7 +47,7 @@ static inline int git_atomic_inc(git_atomic *a)
#endif
}
static inline int git_atomic_dec(git_atomic *a)
GIT_INLINE(int) git_atomic_dec(git_atomic *a)
{
#ifdef __GNUC__
return __sync_sub_and_fetch(&a->val, 1);
......@@ -81,12 +81,12 @@ static inline int git_atomic_dec(git_atomic *a)
#define git_cond_signal(c) (void)0
#define git_cond_broadcast(c) (void)0
static inline int git_atomic_inc(git_atomic *a)
GIT_INLINE(int) git_atomic_inc(git_atomic *a)
{
return ++a->val;
}
static inline int git_atomic_dec(git_atomic *a)
GIT_INLINE(int) git_atomic_dec(git_atomic *a)
{
return --a->val;
}
......
......@@ -41,9 +41,11 @@ int entry_search_cmp(const void *key, const void *array_member)
return strcmp(filename, entry->filename);
}
#if 0
static int valid_attributes(const int attributes) {
return attributes >= 0 && attributes <= MAX_FILEMODE;
}
#endif
int entry_sort_cmp(const void *a, const void *b)
{
......@@ -56,13 +58,10 @@ int entry_sort_cmp(const void *a, const void *b)
entry_b->attr & 040000);
}
void git_tree_clear_entries(git_tree *tree)
void git_tree__free(git_tree *tree)
{
unsigned int i;
if (tree == NULL)
return;
for (i = 0; i < tree->entries.length; ++i) {
git_tree_entry *e;
e = git_vector_get(&tree->entries, i);
......@@ -71,32 +70,6 @@ void git_tree_clear_entries(git_tree *tree)
free(e);
}
git_vector_clear(&tree->entries);
tree->object.modified = 1;
}
git_tree *git_tree__new(void)
{
git_tree *tree;
tree = git__malloc(sizeof(struct git_tree));
if (tree == NULL)
return NULL;
memset(tree, 0x0, sizeof(struct git_tree));
if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS) {
free(tree);
return NULL;
}
return tree;
}
void git_tree__free(git_tree *tree)
{
git_tree_clear_entries(tree);
git_vector_free(&tree->entries);
free(tree);
}
......@@ -106,37 +79,6 @@ const git_oid *git_tree_id(git_tree *c)
return git_object_id((git_object *)c);
}
int git_tree_entry_set_attributes(git_tree_entry *entry, unsigned int attr)
{
assert(entry && entry->owner);
if (!valid_attributes(attr)) {
return GIT_ERROR;
}
entry->attr = attr;
entry->owner->object.modified = 1;
return GIT_SUCCESS;
}
void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
{
assert(entry && entry->owner);
free(entry->filename);
entry->filename = git__strdup(name);
git_vector_sort(&entry->owner->entries);
entry->owner->object.modified = 1;
}
void git_tree_entry_set_id(git_tree_entry *entry, const git_oid *oid)
{
assert(entry && entry->owner);
git_oid_cpy(&entry->oid, oid);
entry->owner->object.modified = 1;
}
unsigned int git_tree_entry_attributes(git_tree_entry *entry)
{
return entry->attr;
......@@ -154,15 +96,10 @@ const git_oid *git_tree_entry_id(git_tree_entry *entry)
return &entry->oid;
}
int git_tree_entry_2object(git_object **object_out, git_tree_entry *entry)
int git_tree_entry_2object(git_object **object_out, git_repository *repo, git_tree_entry *entry)
{
assert(entry && object_out);
return git_object_lookup(object_out, entry->owner->object.repo, &entry->oid, GIT_OBJ_ANY);
}
static void sort_entries(git_tree *tree)
{
git_vector_sort(&tree->entries);
return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
}
git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
......@@ -171,8 +108,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
assert(tree && filename);
sort_entries(tree);
idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename);
if (idx == GIT_ENOTFOUND)
return NULL;
......@@ -183,9 +118,6 @@ git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
{
assert(tree);
sort_entries(tree);
return git_vector_get(&tree->entries, (unsigned int)idx);
}
......@@ -195,107 +127,12 @@ size_t git_tree_entrycount(git_tree *tree)
return tree->entries.length;
}
int git_tree_add_entry(git_tree_entry **entry_out, git_tree *tree, const git_oid *id, const char *filename, int attributes)
{
git_tree_entry *entry;
assert(tree && id && filename);
if (!valid_attributes(attributes)) {
return GIT_ERROR;
}
if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
return GIT_ENOMEM;
memset(entry, 0x0, sizeof(git_tree_entry));
entry->filename = git__strdup(filename);
git_oid_cpy(&entry->oid, id);
entry->attr = attributes;
entry->owner = tree;
if (git_vector_insert(&tree->entries, entry) < 0)
return GIT_ENOMEM;
if (entry_out != NULL)
*entry_out = entry;
tree->object.modified = 1;
return GIT_SUCCESS;
}
int git_tree_remove_entry_byindex(git_tree *tree, int idx)
{
git_tree_entry *remove_ptr;
assert(tree);
sort_entries(tree);
remove_ptr = git_vector_get(&tree->entries, (unsigned int)idx);
if (remove_ptr == NULL)
return GIT_ENOTFOUND;
free(remove_ptr->filename);
free(remove_ptr);
tree->object.modified = 1;
return git_vector_remove(&tree->entries, (unsigned int)idx);
}
int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
{
int idx;
assert(tree && filename);
sort_entries(tree);
idx = git_vector_bsearch2(&tree->entries, entry_search_cmp, filename);
if (idx == GIT_ENOTFOUND)
return GIT_ENOTFOUND;
return git_tree_remove_entry_byindex(tree, idx);
}
int git_tree__writeback(git_tree *tree, git_odb_source *src)
{
size_t i;
char filemode[MAX_FILEMODE_BYTES + 1 + 1];
assert(tree && src);
if (tree->entries.length == 0)
return GIT_EMISSINGOBJDATA;
sort_entries(tree);
for (i = 0; i < tree->entries.length; ++i) {
git_tree_entry *entry;
entry = git_vector_get(&tree->entries, i);
snprintf(filemode, sizeof(filemode), "%o ", entry->attr);
git__source_write(src, filemode, strlen(filemode));
git__source_write(src, entry->filename, strlen(entry->filename) + 1);
git__source_write(src, entry->oid.id, GIT_OID_RAWSZ);
}
return GIT_SUCCESS;
}
static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
{
static const size_t avg_entry_size = 40;
unsigned int expected_size;
int error = GIT_SUCCESS;
expected_size = (tree->object.source.raw.len / avg_entry_size) + 1;
git_tree_clear_entries(tree);
if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS)
return GIT_ENOMEM;
while (buffer < buffer_end) {
git_tree_entry *entry;
......@@ -309,7 +146,6 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
return GIT_ENOMEM;
entry->owner = tree;
entry->attr = strtol(buffer, &buffer, 8);
if (*buffer++ != ' ') {
......@@ -336,16 +172,9 @@ static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
return error;
}
int git_tree__parse(git_tree *tree)
int git_tree__parse(git_tree *tree, git_odb_object *obj)
{
char *buffer, *buffer_end;
assert(tree && tree->object.source.open);
assert(!tree->object.in_memory);
buffer = tree->object.source.raw.data;
buffer_end = buffer + tree->object.source.raw.len;
return tree_parse_buffer(tree, buffer, buffer_end);
assert(tree);
return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
}
......@@ -3,14 +3,13 @@
#include "git2/tree.h"
#include "repository.h"
#include "odb.h"
#include "vector.h"
struct git_tree_entry {
unsigned int attr;
char *filename;
git_oid oid;
git_tree *owner;
};
struct git_tree {
......@@ -19,8 +18,6 @@ struct git_tree {
};
void git_tree__free(git_tree *tree);
git_tree *git_tree__new(void);
int git_tree__parse(git_tree *tree);
int git_tree__writeback(git_tree *tree, git_odb_source *src);
int git_tree__parse(git_tree *tree, git_odb_object *obj);
#endif
......@@ -97,6 +97,8 @@ extern char *git__strtok_keep(char *output, char *src, char *delimit);
#define STRLEN(str) (sizeof(str) - 1)
#define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1)
/*
* Realloc the buffer pointed at by variable 'x' so that it can hold
* at least 'nr' entries; the number of entries currently allocated
......
......@@ -23,10 +23,17 @@
* Boston, MA 02110-1301, USA.
*/
#include "test_lib.h"
#include "t01-data.h"
#include "odb.h"
#include "hash.h"
#include "t01-data.h"
static int hash_object(git_oid *oid, git_rawobj *obj)
{
return git_odb_hash(oid, obj->data, obj->len, obj->type);
}
BEGIN_TEST(oid0, "validate size of oid objects")
git_oid out;
must_be_true(20 == GIT_OID_RAWSZ);
......@@ -497,28 +504,28 @@ BEGIN_TEST(objhash0, "hash junk data")
/* invalid types: */
junk_obj.data = some_data;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
junk_obj.type = GIT_OBJ__EXT1;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
junk_obj.type = GIT_OBJ__EXT2;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
junk_obj.type = GIT_OBJ_OFS_DELTA;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
junk_obj.type = GIT_OBJ_REF_DELTA;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
/* data can be NULL only if len is zero: */
junk_obj.type = GIT_OBJ_BLOB;
junk_obj.data = NULL;
must_pass(git_rawobj_hash(&id, &junk_obj));
must_pass(hash_object(&id, &junk_obj));
must_be_true(git_oid_cmp(&id, &id_zero) == 0);
junk_obj.len = 1;
must_fail(git_rawobj_hash(&id, &junk_obj));
must_fail(hash_object(&id, &junk_obj));
END_TEST
BEGIN_TEST(objhash1, "hash a commit object")
......@@ -526,7 +533,7 @@ BEGIN_TEST(objhash1, "hash a commit object")
must_pass(git_oid_mkstr(&id1, commit_id));
must_pass(git_rawobj_hash(&id2, &commit_obj));
must_pass(hash_object(&id2, &commit_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -536,7 +543,7 @@ BEGIN_TEST(objhash2, "hash a tree object")
must_pass(git_oid_mkstr(&id1, tree_id));
must_pass(git_rawobj_hash(&id2, &tree_obj));
must_pass(hash_object(&id2, &tree_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -546,7 +553,7 @@ BEGIN_TEST(objhash3, "hash a tag object")
must_pass(git_oid_mkstr(&id1, tag_id));
must_pass(git_rawobj_hash(&id2, &tag_obj));
must_pass(hash_object(&id2, &tag_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -556,7 +563,7 @@ BEGIN_TEST(objhash4, "hash a zero-length object")
must_pass(git_oid_mkstr(&id1, zero_id));
must_pass(git_rawobj_hash(&id2, &zero_obj));
must_pass(hash_object(&id2, &zero_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -566,7 +573,7 @@ BEGIN_TEST(objhash5, "hash an one-byte long object")
must_pass(git_oid_mkstr(&id1, one_id));
must_pass(git_rawobj_hash(&id2, &one_obj));
must_pass(hash_object(&id2, &one_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -576,7 +583,7 @@ BEGIN_TEST(objhash6, "hash a two-byte long object")
must_pass(git_oid_mkstr(&id1, two_id));
must_pass(git_rawobj_hash(&id2, &two_obj));
must_pass(hash_object(&id2, &two_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......@@ -586,7 +593,7 @@ BEGIN_TEST(objhash7, "hash an object several bytes long")
must_pass(git_oid_mkstr(&id1, some_id));
must_pass(git_rawobj_hash(&id2, &some_obj));
must_pass(hash_object(&id2, &some_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
END_TEST
......
......@@ -24,6 +24,7 @@
*/
#include "test_lib.h"
#include "test_helpers.h"
#include "odb.h"
#include "t02-data.h"
#include "t02-oids.h"
......@@ -50,16 +51,16 @@ END_TEST
BEGIN_TEST(readloose0, "read a loose commit")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &commit));
must_pass(cmp_objects((git_rawobj *)&obj->raw, &commit));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &commit));
END_TEST
......@@ -67,16 +68,16 @@ END_TEST
BEGIN_TEST(readloose1, "read a loose tree")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tree));
must_pass(cmp_objects((git_rawobj *)&obj->raw, &tree));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tree));
END_TEST
......@@ -84,16 +85,16 @@ END_TEST
BEGIN_TEST(readloose2, "read a loose tag")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &tag));
must_pass(cmp_objects((git_rawobj *)&obj->raw, &tag));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &tag));
END_TEST
......@@ -101,16 +102,16 @@ END_TEST
BEGIN_TEST(readloose3, "read a loose zero-bytes object")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &zero));
must_pass(cmp_objects((git_rawobj *)&obj->raw, &zero));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &zero));
END_TEST
......@@ -118,16 +119,16 @@ END_TEST
BEGIN_TEST(readloose4, "read a one-byte long loose object")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &one));
must_pass(cmp_objects(&obj->raw, &one));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &one));
END_TEST
......@@ -135,16 +136,16 @@ END_TEST
BEGIN_TEST(readloose5, "read a two-bytes long loose object")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &two));
must_pass(cmp_objects(&obj->raw, &two));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &two));
END_TEST
......@@ -152,16 +153,16 @@ END_TEST
BEGIN_TEST(readloose6, "read a loose object which is several bytes long")
git_odb *db;
git_oid id;
git_rawobj obj;
git_odb_object *obj;
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_odb_read(&obj, db, &id));
must_pass(cmp_objects(&obj, &some));
must_pass(cmp_objects(&obj->raw, &some));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(odb_dir, &some));
END_TEST
......@@ -174,13 +175,13 @@ BEGIN_TEST(readpack0, "read several packed objects")
for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
git_oid id;
git_rawobj obj;
git_odb_object *obj;
must_pass(git_oid_mkstr(&id, packed_objects[i]));
must_be_true(git_odb_exists(db, &id) == 1);
must_pass(git_odb_read(&obj, db, &id));
git_rawobj_close(&obj);
git_odb_object_close(obj);
}
git_odb_close(db);
......@@ -194,17 +195,19 @@ BEGIN_TEST(readheader0, "read only the header of several packed objects")
for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
git_oid id;
git_rawobj obj, header;
git_odb_object *obj;
size_t len;
git_otype type;
must_pass(git_oid_mkstr(&id, packed_objects[i]));
must_pass(git_odb_read(&obj, db, &id));
must_pass(git_odb_read_header(&header, db, &id));
must_pass(git_odb_read_header(&len, &type, db, &id));
must_be_true(obj.len == header.len);
must_be_true(obj.type == header.type);
must_be_true(obj->raw.len == len);
must_be_true(obj->raw.type == type);
git_rawobj_close(&obj);
git_odb_object_close(obj);
}
git_odb_close(db);
......@@ -218,19 +221,21 @@ BEGIN_TEST(readheader1, "read only the header of several loose objects")
for (i = 0; i < ARRAY_SIZE(loose_objects); ++i) {
git_oid id;
git_rawobj obj, header;
git_odb_object *obj;
size_t len;
git_otype type;
must_pass(git_oid_mkstr(&id, loose_objects[i]));
must_be_true(git_odb_exists(db, &id) == 1);
must_pass(git_odb_read(&obj, db, &id));
must_pass(git_odb_read_header(&header, db, &id));
must_pass(git_odb_read_header(&len, &type, db, &id));
must_be_true(obj.len == header.len);
must_be_true(obj.type == header.type);
must_be_true(obj->raw.len == len);
must_be_true(obj->raw.type == type);
git_rawobj_close(&obj);
git_odb_object_close(obj);
}
git_odb_close(db);
......
......@@ -24,6 +24,7 @@
*/
#include "test_lib.h"
#include "fileops.h"
#include "odb.h"
static char *odb_dir = "test-objects";
#include "t03-data.h"
......@@ -80,23 +81,39 @@ static int remove_object_files(object_data *d)
return 0;
}
static int streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
{
git_odb_stream *stream;
int error;
if ((error = git_odb_open_wstream(&stream, odb, raw->len, raw->type)) < GIT_SUCCESS)
return error;
stream->write(stream, raw->data, raw->len);
error = stream->finalize_write(oid, stream);
stream->free(stream);
return error;
}
BEGIN_TEST(write0, "write loose commit object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, commit.id));
must_pass(git_odb_write(&id2, db, &commit_obj));
must_pass(streaming_write(&id2, db, &commit_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&commit));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &commit_obj));
must_pass(cmp_objects(&obj->raw, &commit_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&commit));
END_TEST
......@@ -104,20 +121,20 @@ END_TEST
BEGIN_TEST(write1, "write loose tree object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, tree.id));
must_pass(git_odb_write(&id2, db, &tree_obj));
must_pass(streaming_write(&id2, db, &tree_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&tree));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &tree_obj));
must_pass(cmp_objects(&obj->raw, &tree_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&tree));
END_TEST
......@@ -125,20 +142,20 @@ END_TEST
BEGIN_TEST(write2, "write loose tag object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, tag.id));
must_pass(git_odb_write(&id2, db, &tag_obj));
must_pass(streaming_write(&id2, db, &tag_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&tag));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &tag_obj));
must_pass(cmp_objects(&obj->raw, &tag_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&tag));
END_TEST
......@@ -146,20 +163,20 @@ END_TEST
BEGIN_TEST(write3, "write zero-length object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, zero.id));
must_pass(git_odb_write(&id2, db, &zero_obj));
must_pass(streaming_write(&id2, db, &zero_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&zero));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &zero_obj));
must_pass(cmp_objects(&obj->raw, &zero_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&zero));
END_TEST
......@@ -167,20 +184,20 @@ END_TEST
BEGIN_TEST(write4, "write one-byte long object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, one.id));
must_pass(git_odb_write(&id2, db, &one_obj));
must_pass(streaming_write(&id2, db, &one_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&one));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &one_obj));
must_pass(cmp_objects(&obj->raw, &one_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&one));
END_TEST
......@@ -188,20 +205,20 @@ END_TEST
BEGIN_TEST(write5, "write two-byte long object")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, two.id));
must_pass(git_odb_write(&id2, db, &two_obj));
must_pass(streaming_write(&id2, db, &two_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&two));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &two_obj));
must_pass(cmp_objects(&obj->raw, &two_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&two));
END_TEST
......@@ -209,20 +226,20 @@ END_TEST
BEGIN_TEST(write6, "write an object which is several bytes long")
git_odb *db;
git_oid id1, id2;
git_rawobj obj;
git_odb_object *obj;
must_pass(make_odb_dir());
must_pass(git_odb_open(&db, odb_dir));
must_pass(git_oid_mkstr(&id1, some.id));
must_pass(git_odb_write(&id2, db, &some_obj));
must_pass(streaming_write(&id2, db, &some_obj));
must_be_true(git_oid_cmp(&id1, &id2) == 0);
must_pass(check_object_files(&some));
must_pass(git_odb_read(&obj, db, &id1));
must_pass(cmp_objects(&obj, &some_obj));
must_pass(cmp_objects(&obj->raw, &some_obj));
git_rawobj_close(&obj);
git_odb_object_close(obj);
git_odb_close(db);
must_pass(remove_object_files(&some));
END_TEST
......
......@@ -407,39 +407,42 @@ 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, *parent;
git_tree *tree;
git_oid id;
git_commit *commit;
git_oid tree_id, parent_id, commit_id;
const git_signature *author, *committer;
/* char hex_oid[41]; */
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
/* Create commit in memory */
must_pass(git_commit_new(&commit, repo));
/* Add new parent */
git_oid_mkstr(&id, commit_ids[4]);
must_pass(git_commit_lookup(&parent, repo, &id));
git_commit_add_parent(commit, parent);
git_oid_mkstr(&tree_id, tree_oid);
git_oid_mkstr(&parent_id, commit_ids[4]);
/* Set other attributes */
/* 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);
git_commit_set_committer(commit, committer);
git_commit_set_author(commit, author);
git_commit_set_message(commit, COMMIT_MESSAGE);
must_pass(git_commit_create_v(
&commit_id, /* out id */
repo,
NULL, /* do not update the HEAD */
author,
committer,
COMMIT_MESSAGE,
&tree_id,
1, &parent_id));
git_signature_free((git_signature *)committer);
git_signature_free((git_signature *)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);
......@@ -457,47 +460,6 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk")
must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0);
/* add new tree */
git_oid_mkstr(&id, tree_oid);
must_pass(git_tree_lookup(&tree, repo, &id));
git_commit_set_tree(commit, tree);
/* Test it has no OID */
must_be_true(git_commit_id(commit) == NULL);
/* Write to disk */
must_pass(git_object_write((git_object *)commit));
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
git_repository_free(repo);
END_TEST
BEGIN_TEST(write1, "load a commit object, modify it and write it back")
git_repository *repo;
git_oid id;
git_commit *commit, *parent;
const char *message;
/* char hex_oid[41]; */
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&id, commit_ids[0]);
must_pass(git_commit_lookup(&commit, repo, &id));
message = git_commit_message(commit);
git_commit_set_message(commit, "This is a new test message. Cool!\n");
git_oid_mkstr(&id, commit_ids[4]);
must_pass(git_commit_lookup(&parent, repo, &id));
git_commit_add_parent(commit, parent);
must_pass(git_object_write((git_object *)commit));
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
git_repository_free(repo);
......@@ -509,6 +471,7 @@ BEGIN_SUITE(commit)
ADD_TEST(parse1);
ADD_TEST(parse2);
ADD_TEST(details0);
ADD_TEST(write0);
ADD_TEST(write1);
//ADD_TEST(write1);
END_SUITE
......@@ -61,27 +61,62 @@ BEGIN_TEST(read0, "read and parse a tag from the repository")
git_repository_free(repo);
END_TEST
BEGIN_TEST(write0, "write back a tag to the repository")
git_oid id;
#define TAGGER_NAME "Vicent Marti"
#define TAGGER_EMAIL "vicent@github.com"
#define TAGGER_MESSAGE "This is my tag.\n\nThere are many tags, but this one is mine\n"
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_reference *ref_tag;
/* char hex_oid[41]; */
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&id, tag1_id);
git_oid_mkstr(&target_id, tagged_commit);
/* create signatures */
tagger = git_signature_new(TAGGER_NAME, TAGGER_EMAIL, 123456789, 60);
must_be_true(tagger != NULL);
must_pass(git_tag_create(
&tag_id, /* out id */
repo,
"the-tag", /* do not update the HEAD */
&target_id,
GIT_OBJ_COMMIT,
tagger,
TAGGER_MESSAGE));
must_pass(git_tag_lookup(&tag, repo, &id));
git_signature_free((git_signature *)tagger);
git_tag_set_name(tag, "This is a different tag LOL");
must_pass(git_tag_lookup(&tag, repo, &tag_id));
/* 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);
must_be_true(strcmp(git_tag_message(tag), TAGGER_MESSAGE) == 0);
must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/the-tag"));
must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0);
must_pass(git_reference_delete(ref_tag));
must_pass(git_object_write((git_object *)tag));
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag));
git_repository_free(repo);
END_TEST
BEGIN_SUITE(tag)
ADD_TEST(read0);
ADD_TEST(write0);
ADD_TEST(write0);
END_SUITE
......@@ -75,93 +75,15 @@ 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, entry));
must_pass(git_tree_entry_2object(&obj, repo, entry));
git_repository_free(repo);
END_TEST
BEGIN_TEST(write0, "add a new entry to a tree and write it back to disk")
const unsigned int entry_count = 128;
git_repository *repo;
git_tree *tree;
unsigned int i;
git_oid entry_id;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
must_pass(git_tree_new(&tree, repo));
git_oid_mkstr(&entry_id, tree_oid);
for (i = 0; i < entry_count; ++i) {
char filename[32];
git_tree_entry *ent = NULL;
sprintf(filename, "file%d.txt", i);
must_pass(git_tree_add_entry(&ent, tree, &entry_id, filename, 040000));
must_be_true(ent != NULL);
}
must_be_true(git_tree_entrycount(tree) == entry_count);
must_pass(git_object_write((git_object *)tree));
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tree));
git_repository_free(repo);
END_TEST
BEGIN_TEST(write1, "add several entries in-memory and validate that they exist; write back to disk")
git_oid id;
git_repository *repo;
git_tree *tree;
git_tree_entry *entry;
unsigned int i;
/* char hex_oid[41]; */
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
git_oid_mkstr(&id, tree_oid);
must_pass(git_tree_lookup(&tree, repo, &id));
must_be_true(git_tree_entrycount(tree) == 3);
/* check there is NP if we don't want the
* created entry back */
git_tree_add_entry(NULL, tree, &id, "zzz_test_entry.dat", 0);
git_tree_add_entry(NULL, tree, &id, "01_test_entry.txt", 0);
must_be_true(git_tree_entrycount(tree) == 5);
entry = git_tree_entry_byindex(tree, 0);
must_be_true(strcmp(git_tree_entry_name(entry), "01_test_entry.txt") == 0);
entry = git_tree_entry_byindex(tree, 4);
must_be_true(strcmp(git_tree_entry_name(entry), "zzz_test_entry.dat") == 0);
must_pass(git_tree_remove_entry_byname(tree, "README"));
must_be_true(git_tree_entrycount(tree) == 4);
for (i = 0; i < git_tree_entrycount(tree); ++i) {
entry = git_tree_entry_byindex(tree, i);
must_be_true(strcmp(git_tree_entry_name(entry), "README") != 0);
}
must_pass(git_object_write((git_object *)tree));
/*
git_oid_fmt(hex_oid, git_tree_id(tree));
hex_oid[40] = 0;
printf("TREE New SHA1: %s\n", hex_oid);
*/
must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tree));
git_repository_free(repo);
END_TEST
BEGIN_SUITE(tree)
ADD_TEST(read0);
ADD_TEST(read1);
ADD_TEST(write0);
ADD_TEST(write1);
// ADD_TEST(write0); /* TODO THREADSAFE */
// ADD_TEST(write1);
END_SUITE
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment