return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB);
}
+/**
+ * Lookup a blob object from a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param blob pointer to the looked up blob
+ * @param repo the repo to use when locating the blob.
+ * @param id identity of the blob to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len)
+{
+ return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
+}
+
/**
* Close an open blob
*
return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT);
}
+/**
+ * Lookup a commit object from a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param commit pointer to the looked up commit
+ * @param repo the repo to use when locating the commit.
+ * @param id identity of the commit to locate. If the object is
+ * an annotated tag it will be peeled back to the commit.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len)
+{
+ return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
+}
+
/**
* Close an open commit
*
* Create a configuration file backend for ondisk files
*
* These are the normal `.gitconfig` files that Core Git
- * processes.
+ * processes. Note that you first have to add this file to a
+ * configuration object before you can query it for configuration
+ * variables.
*
* @param out the new backend
* @path where the config file is located
GIT_EXTERN(int) git_config_file__ondisk(struct git_config_file **out, const char *path);
/**
- * Allocate a new configuration
+ * Allocate a new configuration object
+ *
+ * This object is empty, so you have to add a file to it before you
+ * can do anything with it.
+ *
+ * @param out pointer to the new configuration
*/
GIT_EXTERN(int) git_config_new(git_config **out);
/**
* Open a configuration file
*
+ * This creates a new configuration object and adds the specified file
+ * to it.
+ *
* @param cfg_out pointer to the configuration data
* @param path where to load the confiration from
*/
/**
* Add a config backend to an existing instance
*
- * Note that the configuration will call the backend's ->free()
- * function.
+ * Note that the configuration object will free the file
+ * automatically.
*
- * @param cfg the configuration to add the backend to
- * @param backend the backend to add
+ * @param cfg the configuration to add the file to
+ * @param file the configuration file (backend) to add
* @param priority the priority the backend should have
*/
GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority);
/**
- * Free the configuration and its associated memory
+ * Free the configuration and its associated memory and files
*
* @param cfg the configuration to free
*/
/** The specified object has its data corrupted */
GIT_EOBJCORRUPTED = -28,
+
+ /** The given short oid is ambiguous */
+ GIT_EAMBIGUOUSOIDPREFIX = -29,
} git_error;
/**
} git_index_entry_unmerged;
/**
- * Create a new Git index object as a memory representation
+ * Create a new bare Git index object as a memory representation
* of the Git index file in 'index_path', without a repository
* to back it.
*
- * Since there is no ODB behind this index, any Index methods
- * which rely on the ODB (e.g. index_add) will fail with the
- * GIT_EBAREINDEX error code.
+ * Since there is no ODB or working directory behind this index,
+ * any Index methods which rely on these (e.g. index_add) will
+ * fail with the GIT_EBAREINDEX error code.
*
- * @param index the pointer for the new index
- * @param index_path the path to the index file in disk
- * @return 0 on success; error code otherwise
- */
-GIT_EXTERN(int) git_index_open_bare(git_index **index, const char *index_path);
-
-/**
- * Open the Index inside the git repository pointed
- * by 'repo'.
+ * If you need to access the index of an actual repository,
+ * use the `git_repository_index` wrapper.
+ *
+ * The index must be freed once it's no longer in use.
*
* @param index the pointer for the new index
- * @param repo the git repo which owns the index
+ * @param index_path the path to the index file in disk
* @return 0 on success; error code otherwise
*/
-GIT_EXTERN(int) git_index_open_inrepo(git_index **index, git_repository *repo);
+GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path);
/**
* Clear the contents (all the entries) of an index object.
* @param n the position of the entry
* @return a pointer to the entry; NULL if out of bounds
*/
-GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, int n);
+GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, unsigned int n);
/**
* Get the count of entries currently in the index
* @param path path to search
* @return the unmerged entry; NULL if not found
*/
-GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged(git_index *index, const char *path);
+GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_bypath(git_index *index, const char *path);
+/**
+ * Get an unmerged entry from the index.
+ *
+ * The returned entry is read-only and should not be modified
+ * of freed by the caller.
+ *
+ * @param index an existing index object
+ * @param n the position of the entry
+ * @return a pointer to the unmerged entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, unsigned int n);
+
+/**
+ * Return the stage number from a git index entry
+ *
+ * This entry is calculated from the entrie's flag
+ * attribute like this:
+ *
+ * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ *
+ * @param entry The entry
+ * @returns the stage number
+ */
+GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
/** @} */
GIT_END_DECL
* @param type the type of the object
* @return a reference to the object
*/
-GIT_EXTERN(int) git_object_lookup(git_object **object, git_repository *repo, const git_oid *id, git_otype type);
+GIT_EXTERN(int) git_object_lookup(
+ git_object **object,
+ git_repository *repo,
+ const git_oid *id,
+ git_otype type);
+
+/**
+ * Lookup a reference to one of the objects in a repostory,
+ * given a prefix of its identifier (short id).
+ *
+ * The object obtained will be so that its identifier
+ * matches the first 'len' hexadecimal characters
+ * (packets of 4 bits) of the given 'id'.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN, and
+ * long enough to identify a unique object matching
+ * the prefix; otherwise the method will fail.
+ *
+ * The generated reference is owned by the repository and
+ * should be closed with the `git_object_close` method
+ * instead of free'd manually.
+ *
+ * The 'type' parameter must match the type of the object
+ * in the odb; the method will fail otherwise.
+ * The special value 'GIT_OBJ_ANY' may be passed to let
+ * the method guess the object's type.
+ *
+ * @param object pointer to the looked-up object
+ * @param repo the repository to look up the object
+ * @param id a short identifier for the object
+ * @param len the length of the short identifier
+ * @param type the type of the object
+ * @return a reference to the object
+ */
+GIT_EXTERN(int) git_object_lookup_prefix(
+ git_object **object_out,
+ git_repository *repo,
+ const git_oid *id,
+ unsigned int len,
+ git_otype type);
/**
* Get the id (SHA1) of a repository object
/**
* Get the repository that owns this object
*
+ * Freeing or calling `git_repository_close` on the
+ * returned pointer will invalidate the actual object.
+ *
+ * Any other operation may be run on the repository without
+ * affecting the object.
+ *
* @param obj the object
* @return the repository who owns this object
*/
/**
* Read an object from the database.
*
- * This method queries all avaiable ODB backends
+ * This method queries all available ODB backends
* trying to read the given OID.
*
* The returned object is reference counted and
*/
GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
+/**
+ * Read an object from the database, given a prefix
+ * of its identifier.
+ *
+ * This method queries all available ODB backends
+ * trying to match the 'len' first hexadecimal
+ * characters of the 'short_id'.
+ * The remaining (GIT_OID_HEXSZ-len)*4 bits of
+ * 'short_id' must be 0s.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN,
+ * and the prefix must be long enough to identify
+ * a unique object in all the backends; the
+ * method will fail otherwise.
+ *
+ * The returned object is reference counted and
+ * internally cached, so it should be closed
+ * by the user once it's no longer in use.
+ *
+ * @param out_oid the oid of the unique object matching
+ * the short id
+ * @param out pointer where to store the read object
+ * @param db database to search for the object in.
+ * @param short_id a prefix of the id of the object to read.
+ * @param len the length of the prefix
+ * @return
+ * - GIT_SUCCESS if the object was read;
+ * - GIT_ENOTFOUND if the object is not in the database.
+ * - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
+ */
+GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len);
+
/**
* Read the header of an object from the database, without
* reading its full contents.
struct git_odb_backend *,
const git_oid *);
+ /* To find a unique object given a prefix
+ * of its oid.
+ * The oid given must be so that the
+ * remaining (GIT_OID_HEXSZ - len)*4 bits
+ * are 0s.
+ */
+ int (* read_prefix)(
+ git_oid *,
+ void **, size_t *, git_otype *,
+ struct git_odb_backend *,
+ const git_oid *,
+ unsigned int);
+
int (* read_header)(
size_t *, git_otype *,
struct git_odb_backend *,
/** Size (in bytes) of a hex formatted oid */
#define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2)
+/** Minimum length (in number of hex characters,
+ * i.e. packets of 4 bits) of an oid prefix */
+#define GIT_OID_MINPREFIXLEN 4
+
/** Unique identity of any object (commit, tree, blob, tag). */
typedef struct {
/** raw binary formatted id */
*/
GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
+/**
+ * Compare the first 'len' hexadecimal characters (packets of 4 bits)
+ * of two oid structures.
+ * @param len the number of hex chars to compare
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return 0 in case of a match
+ */
+GIT_EXTERN(int) gid_oid_ncmp(unsigned int len, git_oid *a, git_oid *b);
+
/**
* OID Shortener object
*/
const char *git_index_file,
const char *git_work_tree);
+/**
+ * Look for a git repository and copy its path in the given buffer. The lookup start
+ * from base_path and walk across parent directories if nothing has been found. The
+ * lookup ends when the first repository is found, or when reaching a directory
+ * referenced in ceiling_dirs or when the filesystem changes (in case across_fs
+ * is true).
+ *
+ * The method will automatically detect if the repository is bare (if there is
+ * a repository).
+ *
+ * @param repository_path The user allocated buffer which will contain the found path.
+ *
+ * @param size repository_path size
+ *
+ * @param start_path The base path where the lookup starts.
+ *
+ * @param across_fs If true, then the lookup will not stop when a filesystem device change
+ * is detected while exploring parent directories.
+ *
+ * @param ceiling_dirs A colon separated of absolute symbolic link free paths. The lookup will
+ * stop when any of this paths is reached. Note that the lookup always performs on start_path
+ * no matter start_path appears in ceiling_dirs
+ * ceiling_dirs might be NULL (which is equivalent to an empty string)
+ *
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs);
+
/**
* Get the object database behind a Git repository
*
GIT_EXTERN(git_odb *) git_repository_database(git_repository *repo);
/**
- * Get the Index file of a Git repository
+ * Open the Index file of a Git repository
*
- * This is a cheap operation; the index is only opened on the first call,
- * and subsequent calls only retrieve the previous pointer.
+ * This returns a new and unique `git_index` object representing the
+ * active index for the repository.
+ *
+ * This method may be called more than once (e.g. on different threads).
+ *
+ * Each returned `git_index` object is independent and suffers no race
+ * conditions: synchronization is done at the FS level.
+ *
+ * Each returned `git_index` object must be manually freed by the user,
+ * using `git_index_free`.
*
* @param index Pointer where to store the index
* @param repo a repository object
GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
/**
- * Get the normalized path to the git repository.
- *
- * @param repo a repository object
- * @return absolute path to the git directory
+ * Internal path identifiers for a repository
*/
-GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
+typedef enum {
+ GIT_REPO_PATH,
+ GIT_REPO_PATH_INDEX,
+ GIT_REPO_PATH_ODB,
+ GIT_REPO_PATH_WORKDIR
+} git_repository_pathid;
/**
- * Get the normalized path to the working directory of the repository.
+ * Get one of the paths to the repository
+ *
+ * Possible values for `id`:
*
- * If the repository is bare, there is no working directory and NULL we be returned.
+ * GIT_REPO_PATH: return the path to the repository
+ * GIT_REPO_PATH_INDEX: return the path to the index
+ * GIT_REPO_PATH_ODB: return the path to the ODB
+ * GIT_REPO_PATH_WORKDIR: return the path to the working
+ * directory
*
* @param repo a repository object
- * @return NULL if the repository is bare; absolute path to the working directory otherwise.
+ * @param id The ID of the path to return
+ * @return absolute path of the requested id
+ */
+GIT_EXTERN(const char *) git_repository_path(git_repository *repo, git_repository_pathid id);
+
+/**
+ * Check if a repository is bare
+ *
+ * @param repo Repo to test
+ * @return 1 if the repository is empty, 0 otherwise.
*/
-GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
/** @} */
GIT_END_DECL
return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG);
}
+/**
+ * Lookup a tag object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param tag pointer to the looked up tag
+ * @param repo the repo to use when locating the tag.
+ * @param id identity of the tag to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len)
+{
+ return git_object_lookup_prefix((git_object **)tag, repo, id, len, (git_otype)GIT_OBJ_TAG);
+}
+
/**
* Close an open tag
*
* http://predef.sourceforge.net/precomp.html
*/
-#define GIT_HAS_TLS 1
+#ifdef GIT_THREADS
+# define GIT_HAS_TLS 1
-#if defined(__APPLE__) && defined(__MACH__)
-# undef GIT_TLS
-# define GIT_TLS
+/* No TLS in Cygwin */
+# if defined(__CHECKER__) || defined(__CYGWIN__)
+# undef GIT_HAS_TLS
+# define GIT_TLS
-#elif defined(__GNUC__) || \
- defined(__SUNPRO_C) || \
- defined(__SUNPRO_CC) || \
- defined(__xlc__) || \
- defined(__xlC__)
-# define GIT_TLS __thread
+/* No TLS in Mach binaries for Mac OS X */
+# elif defined(__APPLE__) && defined(__MACH__)
+# undef GIT_TLS
+# define GIT_TLS
-#elif defined(__INTEL_COMPILER)
-# if defined(_WIN32) || defined(_WIN32_CE)
-# define GIT_TLS __declspec(thread)
-# else
-# define GIT_TLS __thread
-# endif
+/* Normal TLS for GCC */
+# elif defined(__GNUC__) || \
+ defined(__SUNPRO_C) || \
+ defined(__SUNPRO_CC) || \
+ defined(__xlc__) || \
+ defined(__xlC__)
+# define GIT_TLS __thread
-#elif defined(_WIN32) || \
- defined(_WIN32_CE) || \
- defined(__BORLANDC__)
-# define GIT_TLS __declspec(thread)
+/* ICC may run on Windows or Linux */
+# elif defined(__INTEL_COMPILER)
+# if defined(_WIN32) || defined(_WIN32_CE)
+# define GIT_TLS __declspec(thread)
+# else
+# define GIT_TLS __thread
+# endif
-#else
-# undef GIT_HAS_TLS
-# define GIT_TLS /* nothing: tls vars are thread-global */
-#endif
+/* Declspec for MSVC in Win32 */
+# elif defined(_WIN32) || \
+ defined(_WIN32_CE) || \
+ defined(__BORLANDC__)
+# define GIT_TLS __declspec(thread)
-/* sparse and cygwin don't grok thread-local variables */
-#if defined(__CHECKER__) || defined(__CYGWIN__)
-# undef GIT_HAS_TLS
-# undef GIT_TLS
-# define GIT_TLS
-#endif
+/* Other platform; no TLS */
+# else
+# undef GIT_HAS_TLS
+# define GIT_TLS /* nothing: tls vars are thread-global */
+# endif
+#else /* Disable TLS if libgit2 is not threadsafe */
+# define GIT_TLS
+#endif /* GIT_THREADS */
#endif /* INCLUDE_git_thread_utils_h__ */
return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE);
}
+/**
+ * Lookup a tree object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param tree pointer to the looked up tree
+ * @param repo the repo to use when locating the tree.
+ * @param id identity of the tree to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len)
+{
+ return git_object_lookup_prefix((git_object **)tree, repo, id, len, GIT_OBJ_TREE);
+}
+
/**
* Close an open tree
*
* @param tree a previously loaded tree.
* @return the number of entries in the tree
*/
-GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree);
+GIT_EXTERN(unsigned int) git_tree_entrycount(git_tree *tree);
/**
* Lookup a tree entry by its filename
* @param idx the position in the entry list
* @return the tree entry; NULL if not found
*/
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx);
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, unsigned int idx);
/**
* Get the UNIX file attributes of a tree entry
*/
GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry);
+/**
+ * Get the type of the object pointed by the entry
+ *
+ * @param entry a tree entry
+ * @return the type of the pointed object
+ */
+GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
+
/**
* Convert a tree entry to the git_object it points too.
*
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read backend");
}
+int hiredis_backend__read_prefix(git_oid *out_oid, void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend,
+ const git_oid *short_oid, unsigned int len) {
+ if (len >= GIT_OID_HEXSZ) {
+ /* Just match the full identifier */
+ int error = hiredis_backend__read(data_p, len_p, type_p, backend, short_oid);
+ if (error == GIT_SUCCESS)
+ git_oid_cpy(out_oid, short_oid);
+
+ return error;
+ } else if (len < GIT_OID_HEXSZ) {
+ /* TODO */
+ return git__throw(GIT_ENOTIMPLEMENTED, "Hiredis backend cannot search objects from short oid");
+ }
+}
+
int hiredis_backend__exists(git_odb_backend *_backend, const git_oid *oid) {
hiredis_backend *backend;
int found;
goto cleanup;
backend->parent.read = &hiredis_backend__read;
+ backend->parent.read_prefix = &hiredis_backend__read_prefix;
backend->parent.read_header = &hiredis_backend__read_header;
backend->parent.write = &hiredis_backend__write;
backend->parent.exists = &hiredis_backend__exists;
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "SQLite backend: Failed to read");
}
+int sqlite_backend__read_prefix(git_oid *out_oid, void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend,
+ const git_oid *short_oid, unsigned int len) {
+ if (len >= GIT_OID_HEXSZ) {
+ /* Just match the full identifier */
+ int error = sqlite_backend__read(data_p, len_p, type_p, _backend, short_oid);
+ if (error == GIT_SUCCESS)
+ git_oid_cpy(out_oid, short_oid);
+
+ return error;
+ } else if (len < GIT_OID_HEXSZ) {
+ /* TODO */
+ return git__throw(GIT_ENOTIMPLEMENTED, "Sqlite backend cannot search objects from short oid");
+ }
+}
+
int sqlite_backend__exists(git_odb_backend *_backend, const git_oid *oid)
{
sqlite_backend *backend;
goto cleanup;
backend->parent.read = &sqlite_backend__read;
+ backend->parent.read_prefix = &sqlite_backend__read_prefix;
backend->parent.read_header = &sqlite_backend__read_header;
backend->parent.write = &sqlite_backend__write;
backend->parent.exists = &sqlite_backend__exists;
if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create blob");
- stream->write(stream, buffer, len);
+ if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) {
+ stream->free(stream);
+ return error;
+ }
error = stream->finalize_write(oid, stream);
stream->free(stream);
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
- int error, fd;
+ int error, islnk;
+ int fd = 0;
char full_path[GIT_PATH_MAX];
char buffer[2048];
git_off_t size;
git_odb_stream *stream;
+ struct stat st;
+
+ gitfo_lstat(path, &st);
+
+ islnk = S_ISLNK(st.st_mode);
if (repo->path_workdir == NULL)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
git__joinpath(full_path, repo->path_workdir, path);
- if ((fd = gitfo_open(full_path, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path);
+ if (!islnk) {
+ if ((fd = gitfo_open(full_path, O_RDONLY)) < 0)
+ return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path);
- if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) {
- gitfo_close(fd);
- return git__throw(GIT_EOSERR, "Failed to create blob. '%s' appears to be corrupted", full_path);
+ if ((size = gitfo_size(fd)) < 0 || !git__is_sizet(size)) {
+ gitfo_close(fd);
+ return git__throw(GIT_EOSERR, "Failed to create blob. '%s' appears to be corrupted", full_path);
+ }
+ } else {
+ size = st.st_size;
}
if ((error = git_odb_open_wstream(&stream, repo->db, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) {
- gitfo_close(fd);
+ if (!islnk)
+ gitfo_close(fd);
return git__rethrow(error, "Failed to create blob");
}
while (size > 0) {
ssize_t read_len;
- read_len = read(fd, buffer, sizeof(buffer));
+ if (!islnk)
+ read_len = gitfo_read(fd, buffer, sizeof(buffer));
+ else
+ read_len = gitfo_readlink(full_path, buffer, sizeof(buffer));
if (read_len < 0) {
- gitfo_close(fd);
+ if (!islnk)
+ gitfo_close(fd);
stream->free(stream);
return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
}
error = stream->finalize_write(oid, stream);
stream->free(stream);
- gitfo_close(fd);
+ if (!islnk)
+ gitfo_close(fd);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create blob");
if (buffer < buffer_end) {
const char *line_end;
+ unsigned int i;
size_t message_len;
/* Long message */
commit->message[message_len] = 0;
/* Short message */
- if((line_end = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- line_end = buffer_end;
+ if((line_end = strstr(buffer, "\n\n")) == NULL) {
+ /* Cut the last '\n' if there is one */
+ if (message_len && buffer[message_len - 1] == '\n')
+ line_end = buffer_end - 1;
+ else
+ line_end = buffer_end;
+ }
message_len = line_end - buffer;
-
commit->message_short = git__malloc(message_len + 1);
- memcpy(commit->message_short, buffer, message_len);
+ for (i = 0; i < message_len; ++i) {
+ commit->message_short[i] = (buffer[i] == '\n') ? ' ' : buffer[i];
+ }
commit->message_short[message_len] = 0;
}
cleanup:
cvar_list_free(&b->var_list);
gitfo_free_buf(&b->reader.buffer);
- free(cfg);
return git__rethrow(error, "Failed to open config");
}
return ret;
}
-static const char *LINEBREAK_UNIX = "\\\n";
-static const char *LINEBREAK_WIN32 = "\\\r\n";
-
-static int is_linebreak(const char *pos)
-{
- return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 ||
- memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0;
-}
-
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
int line_len;
line_src = cfg->reader.read_ptr;
+
+ /* Skip empty empty lines */
+ while (isspace(*line_src))
+ ++line_src;
+
line_end = strchr(line_src, '\n');
/* no newline at EOF */
if (line_end == NULL)
line_end = strchr(line_src, 0);
- else
- while (is_linebreak(line_end))
- line_end = strchr(line_end + 1, '\n');
-
- while (line_src < line_end && isspace(*line_src))
- line_src++;
+ line_len = line_end - line_src;
- line = (char *)git__malloc((size_t)(line_end - line_src) + 1);
+ line = git__malloc(line_len + 1);
if (line == NULL)
return NULL;
- line_len = 0;
- while (line_src < line_end) {
-
- if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) {
- line_src += sizeof(LINEBREAK_UNIX);
- continue;
- }
-
- if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) {
- line_src += sizeof(LINEBREAK_WIN32);
- continue;
- }
-
- line[line_len++] = *line_src++;
- }
+ memcpy(line, line_src, line_len);
line[line_len] = '\0';
* added to the string. In case of error, jump to out
*/
do {
+ if (quote_marks == 2) {
+ error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
+ goto out;
+
+ }
+
switch (c) {
case '"':
- if (quote_marks++ >= 2)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Too many quotes");
+ ++quote_marks;
break;
case '\\':
c = line[rpos++];
error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
goto out;
}
+ break;
default:
break;
}
cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
cfg_file->reader.eof = 0;
+ /* If the file is empty, there's nothing for us to do */
+ if (*cfg_file->reader.read_ptr == '\0')
+ return GIT_SUCCESS;
+
skip_bom(cfg_file);
while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
case '[': /* section header, new section begins */
free(current_section);
+ current_section = NULL;
error = parse_section_header(cfg_file, ¤t_section);
break;
}
}
- if (current_section)
- free(current_section);
+ free(current_section);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
}
{GIT_EEXISTS, "A reference with this name already exists"},
{GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
{GIT_ENOTNUM, "The given literal is not a valid number"},
+ {GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
};
const char *git_strerror(int num)
/* create path to the file buffer is required */
if (flags & GIT_FILEBUF_FORCE) {
- file->fd = gitfo_creat_force(file->path_lock, 0644);
+ file->fd = gitfo_creat_locked_force(file->path_lock, 0644);
} else {
- file->fd = gitfo_creat(file->path_lock, 0644);
+ file->fd = gitfo_creat_locked(file->path_lock, 0644);
}
if (file->fd < 0)
return git__throw(GIT_EOSERR, "Failed to create lock");
- /* TODO: do a flock() in the descriptor file_lock */
-
if ((flags & GIT_FILEBUF_APPEND) && gitfo_exists(file->path_original) == 0) {
git_file source;
char buffer[2048];
if (file->fd >= 0)
gitfo_close(file->fd);
- if (file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS)
+ if (file->fd >= 0 && file->path_lock && gitfo_exists(file->path_lock) == GIT_SUCCESS)
gitfo_unlink(file->path_lock);
if (file->digest)
int len, error;
va_start(arglist, format);
-
len = vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
+ va_end(arglist);
if (len < 0 || (size_t)len >= space_left) {
if ((error = flush_buffer(file)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to output to buffer");
+ space_left = file->buf_size - file->buf_pos;
+
+ va_start(arglist, format);
len = vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
+ va_end(arglist);
+
if (len < 0 || (size_t)len > file->buf_size)
return GIT_ENOMEM;
}
return gitfo_creat(path, mode);
}
+int gitfo_creat_locked(const char *path, int mode)
+{
+ int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path);
+}
+
+int gitfo_creat_locked_force(const char *path, int mode)
+{
+ if (gitfo_mkdir_2file(path) < GIT_SUCCESS)
+ return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
+
+ return gitfo_creat_locked(path, mode);
+}
+
int gitfo_read(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
return git__throw(GIT_ENOTFOUND, "%s does not exist", path);
if (!S_ISDIR(st.st_mode))
+ return git__throw(GIT_ENOTFOUND, "%s is not a directory", path);
+
+ return GIT_SUCCESS;
+}
+
+int gitfo_isfile(const char *path)
+{
+ struct stat st;
+ int stat_error;
+
+ if (!path)
+ return git__throw(GIT_ENOTFOUND, "No path given to gitfo_isfile");
+
+ stat_error = gitfo_stat(path, &st);
+
+ if (stat_error < GIT_SUCCESS)
+ return git__throw(GIT_ENOTFOUND, "%s does not exist", path);
+
+ if (!S_ISREG(st.st_mode))
return git__throw(GIT_ENOTFOUND, "%s is not a file", path);
return GIT_SUCCESS;
return GIT_SUCCESS;
}
+#if GIT_PLATFORM_PATH_SEP == '/'
+void gitfo_posixify_path(char *GIT_UNUSED(path))
+{
+ /* nothing to do*/
+}
+#else
+void gitfo_posixify_path(char *path)
+{
+ while (*path) {
+ if (*path == GIT_PLATFORM_PATH_SEP)
+ *path = '/';
-int retrieve_path_root_offset(const char *path)
+ path++;
+ }
+}
+#endif
+
+int gitfo_retrieve_path_root_offset(const char *path)
{
int offset = 0;
return -1; /* Not a real error. Rather a signal than the path is not rooted */
}
-
int gitfo_mkdir_recurs(const char *path, int mode)
{
int error, root_path_offset;
error = GIT_SUCCESS;
pp = path_copy;
- root_path_offset = retrieve_path_root_offset(pp);
+ root_path_offset = gitfo_retrieve_path_root_offset(pp);
if (root_path_offset > 0)
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
- while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != 0) {
+ while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
if (sp != pp && gitfo_isdir(path_copy) < GIT_SUCCESS) {
*sp = 0;
error = gitfo_mkdir(path_copy, mode);
pp = sp + 1;
}
- if (*(pp - 1) != '/' && error == GIT_SUCCESS)
+ if (*pp != '\0' && error == GIT_SUCCESS) {
error = gitfo_mkdir(path, mode);
+ if (errno == EEXIST)
+ error = GIT_SUCCESS;
+ }
free(path_copy);
{
int offset, len, root_offset, start = 0;
- root_offset = retrieve_path_root_offset(path);
+ root_offset = gitfo_retrieve_path_root_offset(path);
if (root_offset > -1)
start += root_offset;
return offset;
}
-int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
+int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path, const char *base_path)
{
int len = 0, segment_len, only_dots, root_path_offset, error = GIT_SUCCESS;
char *current;
buffer_end = path + strlen(path);
buffer_out_start = buffer_out;
- root_path_offset = retrieve_path_root_offset(path);
+ root_path_offset = gitfo_retrieve_path_root_offset(path);
if (root_path_offset < 0) {
- error = gitfo_getcwd(buffer_out, size);
- if (error < GIT_SUCCESS)
- return error; /* The callee already takes care of setting the correct error message. */
+ if (base_path == NULL) {
+ error = gitfo_getcwd(buffer_out, size);
+ if (error < GIT_SUCCESS)
+ return error; /* The callee already takes care of setting the correct error message. */
+ } else {
+ if (size < (strlen(base_path) + 1) * sizeof(char))
+ return git__throw(GIT_EOVERFLOW, "Failed to prettify dir path: the base path is too long for the buffer.");
+
+ strcpy(buffer_out, base_path);
+ gitfo_posixify_path(buffer_out);
+ git__joinpath(buffer_out, buffer_out, "");
+ }
len = strlen(buffer_out);
buffer_out += len;
return GIT_SUCCESS;
}
-int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path)
+int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path, const char *base_path)
{
- int error, path_len, i;
+ int error, path_len, i, root_offset;
const char* pattern = "/..";
path_len = strlen(path);
return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
}
- error = gitfo_prettify_dir_path(buffer_out, size, path);
+ error = gitfo_prettify_dir_path(buffer_out, size, path, base_path);
if (error < GIT_SUCCESS)
return error; /* The callee already takes care of setting the correct error message. */
path_len = strlen(buffer_out);
- if (path_len < 2) /* TODO: Fixme. We should also take of detecting Windows rooted path (probably through usage of retrieve_path_root_offset) */
+ root_offset = gitfo_retrieve_path_root_offset(buffer_out) + 1;
+ if (path_len == root_offset)
return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path);
/* Remove the trailing slash */
return 0;
}
-static void posixify_path(char *path)
-{
- while (*path) {
- if (*path == '\\')
- *path = '/';
-
- path++;
- }
-}
-
int gitfo_getcwd(char *buffer_out, size_t size)
{
char *cwd_buffer;
if (cwd_buffer == NULL)
return git__throw(GIT_EOSERR, "Failed to retrieve current working directory");
- posixify_path(buffer_out);
+ gitfo_posixify_path(buffer_out);
git__joinpath(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash
return GIT_SUCCESS;
}
+
+#ifdef GIT_WIN32
+static inline time_t filetime_to_time_t(const FILETIME *ft)
+{
+ long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
+ winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+ winTime /= 10000000; /* Nano to seconds resolution */
+ return (time_t)winTime;
+}
+
+static int do_lstat(const char *file_name, struct stat *buf)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+ if (GetFileAttributesExA(file_name, GetFileExInfoStandard, &fdata)) {
+ int fMode = S_IREAD;
+
+ if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ fMode |= S_IFDIR;
+ else
+ fMode |= S_IFREG;
+
+ if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+ fMode |= S_IWRITE;
+
+ if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ fMode |= _S_IFLNK;
+
+ buf->st_ino = 0;
+ buf->st_gid = 0;
+ buf->st_uid = 0;
+ buf->st_nlink = 1;
+ buf->st_mode = fMode;
+ buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
+ buf->st_dev = buf->st_rdev = (_getdrive() - 1);
+ buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
+ buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
+ buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+ return GIT_SUCCESS;
+ }
+
+ switch (GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ return GIT_EOSERR;
+
+ case ERROR_BUFFER_OVERFLOW:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return GIT_ENOMEM;
+
+ default:
+ return GIT_EINVALIDPATH;
+ }
+}
+
+int gitfo_lstat__w32(const char *file_name, struct stat *buf)
+{
+ int namelen, error;
+ char alt_name[GIT_PATH_MAX];
+
+ if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS)
+ return GIT_SUCCESS;
+
+ /* if file_name ended in a '/', Windows returned ENOENT;
+ * try again without trailing slashes
+ */
+ if (error != GIT_EINVALIDPATH)
+ return git__throw(GIT_EOSERR, "Failed to lstat file");
+
+ namelen = strlen(file_name);
+ if (namelen && file_name[namelen-1] != '/')
+ return git__throw(GIT_EOSERR, "Failed to lstat file");
+
+ while (namelen && file_name[namelen-1] == '/')
+ --namelen;
+
+ if (!namelen || namelen >= GIT_PATH_MAX)
+ return git__throw(GIT_ENOMEM, "Failed to lstat file");
+
+ memcpy(alt_name, file_name, namelen);
+ alt_name[namelen] = 0;
+ return do_lstat(alt_name, buf);
+}
+int gitfo_readlink__w32(const char *link, char *target, size_t target_len)
+{
+ HANDLE hFile;
+ DWORD dwRet;
+
+ hFile = CreateFile(link, // file to open
+ GENERIC_READ, // open for reading
+ FILE_SHARE_READ, // share for reading
+ NULL, // default security
+ OPEN_EXISTING, // existing file only
+ FILE_FLAG_BACKUP_SEMANTICS, // normal file
+ NULL); // no attr. template
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ return GIT_EOSERR;
+
+ dwRet = GetFinalPathNameByHandleA(hFile, target, target_len, VOLUME_NAME_DOS);
+ if (dwRet >= target_len)
+ return GIT_ENOMEM;
+
+ CloseHandle(hFile);
+
+ if (dwRet > 4) {
+ /* Skip first 4 characters if they are "\\?\" */
+ if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') {
+ char tmp[MAXPATHLEN];
+ unsigned int offset = 4;
+ dwRet -= 4;
+
+ /* \??\UNC\ */
+ if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
+ offset += 2;
+ dwRet -= 2;
+ target[offset] = '\\';
+ }
+
+ memcpy(tmp, target + offset, dwRet);
+ memcpy(target, tmp, dwRet);
+ }
+ }
+
+ target[dwRet] = '\0';
+ return dwRet;
+}
+
+#endif
#include <fcntl.h>
#include <time.h>
+#define GIT_PATH_LIST_SEPARATOR ':'
+
+#ifdef GIT_WIN32
+#define GIT_PLATFORM_PATH_SEP '\\'
+#else
+#define GIT_PLATFORM_PATH_SEP '/'
+#endif
+
+#define S_IFGITLINK 0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+
#ifdef GIT_WIN32
GIT_INLINE(int) link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new))
{
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_locked(const char *path, int mode);
+extern int gitfo_creat_locked_force(const char *path, int mode);
extern int gitfo_mktemp(char *path_out, const char *filename);
extern int gitfo_isdir(const char *path);
+extern int gitfo_isfile(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)
#define gitfo_stat(p,b) stat(p, b)
#define gitfo_fstat(f,b) fstat(f, b)
+#ifdef GIT_WIN32
+# define gitfo_lstat(p,b) gitfo_lstat__w32(p,b)
+# define gitfo_readlink(a, b, c) gitfo_readlink__w32(a, b, c)
+#else
+# define gitfo_lstat(p,b) lstat(p,b)
+# define gitfo_readlink(a, b, c) readlink(a, b, c)
+#endif
+
#define gitfo_unlink(p) unlink(p)
#define gitfo_rmdir(p) rmdir(p)
#define gitfo_chdir(p) chdir(p)
* - GIT_SUCCESS on success;
* - GIT_ERROR when the input path is invalid or escapes the current directory.
*/
-int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path);
+int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path, const char *base_path);
/**
* Clean up a provided absolute or relative file path.
* - GIT_SUCCESS on success;
* - GIT_ERROR when the input path is invalid or escapes the current directory.
*/
-int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path);
+int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path, const char *base_path);
+
+void gitfo_posixify_path(char *path);
+
+int gitfo_retrieve_path_root_offset(const char *path);
-int retrieve_path_root_offset(const char *path);
#endif /* INCLUDE_fileops_h__ */
static int read_header(struct index_header *dest, const void *buffer);
static int read_tree(git_index *index, const char *buffer, size_t buffer_size);
-static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *);
+static int read_tree_internal(git_index_tree **, const char **, const char *, git_index_tree *);
static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
static int is_index_extended(git_index *index);
return strcmp(info_a->path, info_b->path);
}
+unsigned int index_create_mode(unsigned int mode)
+{
+ if (S_ISLNK(mode))
+ return S_IFLNK;
+ if (S_ISDIR(mode) || (mode & S_IFMT) == 0160000)
+ return 0160000;
+ return S_IFREG | ((mode & 0100) ? 0755 : 0644);
+}
+
static int index_initialize(git_index **index_out, git_repository *owner, const char *index_path)
{
git_index *index;
return git_index_read(index);
}
-int git_index_open_bare(git_index **index_out, const char *index_path)
+int git_index_open(git_index **index_out, const char *index_path)
{
+ assert(index_out && index_path);
return index_initialize(index_out, NULL, index_path);
}
-int git_index_open_inrepo(git_index **index_out, git_repository *repo)
+/*
+ * Moved from `repository.c`
+ */
+int git_repository_index(git_index **index_out, git_repository *repo)
{
+ assert(index_out && repo);
+
if (repo->is_bare)
return git__throw(GIT_EBAREINDEX, "Failed to open index. Repository is bare");
void git_index_free(git_index *index)
{
- if (index == NULL || index->repository != NULL)
+ if (index == NULL)
return;
git_index_clear(index);
git_vector_free(&index->entries);
+ git_vector_free(&index->unmerged);
free(index->index_file_path);
free(index);
free(e);
}
+ for (i = 0; i < index->unmerged.length; ++i) {
+ git_index_entry_unmerged *e;
+ e = git_vector_get(&index->unmerged, i);
+ free((char *)e->path);
+ free(e);
+ }
+
git_vector_clear(&index->entries);
+ git_vector_clear(&index->unmerged);
index->last_modified = 0;
free_tree(index->tree);
return index->unmerged.length;
}
-git_index_entry *git_index_get(git_index *index, int n)
+git_index_entry *git_index_get(git_index *index, unsigned int n)
{
assert(index);
sort_index(index);
- return git_vector_get(&index->entries, (unsigned int)n);
+ return git_vector_get(&index->entries, n);
}
static void sort_index(git_index *index)
git__joinpath(full_path, index->repository->path_workdir, rel_path);
- if (gitfo_exists(full_path) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to initialize entry. %s does not exist", full_path);
-
- if (gitfo_stat(full_path, &st) < 0)
- return git__throw(GIT_EOSERR, "Failed to initialize entry. %s appears to be corrupted", full_path);
+ if (gitfo_lstat(full_path, &st) < 0)
+ return git__throw(GIT_EOSERR, "Failed to initialize entry. '%s' cannot be opened", full_path);
if (stage < 0 || stage > 3)
return git__throw(GIT_ERROR, "Failed to initialize entry. Invalid stage %i", stage);
/* entry.ctime.nanoseconds = st.st_ctimensec; */
entry->dev= st.st_rdev;
entry->ino = st.st_ino;
- entry->mode = st.st_mode;
+ entry->mode = index_create_mode(st.st_mode);
entry->uid = st.st_uid;
entry->gid = st.st_gid;
entry->file_size = st.st_size;
return git_vector_bsearch2(&index->entries, index_srch, path);
}
-const git_index_entry_unmerged *git_index_get_unmerged(git_index *index, const char *path)
+const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index, const char *path)
{
int pos;
assert(index && path);
return git_vector_get(&index->unmerged, pos);
}
+const git_index_entry_unmerged *git_index_get_unmerged_byindex(git_index *index, unsigned int n)
+{
+ assert(index);
+ return git_vector_get(&index->unmerged, n);
+}
+
-static git_index_tree *read_tree_internal(
+static int read_tree_internal(git_index_tree **out,
const char **buffer_in, const char *buffer_end, git_index_tree *parent)
{
git_index_tree *tree;
const char *name_start, *buffer;
long count;
+ int error = GIT_SUCCESS;
if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
- return NULL;
+ return GIT_ENOMEM;
memset(tree, 0x0, sizeof(git_index_tree));
tree->parent = parent;
buffer = name_start = *buffer_in;
- if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
- goto error_cleanup;
+ if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
/* NUL-terminated tree name */
tree->name = git__strdup(name_start);
- if (++buffer >= buffer_end)
- goto error_cleanup;
+ if (tree->name == NULL) {
+ error = GIT_ENOMEM;
+ goto cleanup;
+ }
+
+ if (++buffer >= buffer_end) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
/* Blank-terminated ASCII decimal number of entries in this tree */
- if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
- count < 0)
- goto error_cleanup;
+ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || count < -1) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
+
+ /* Invalidated TREE. Free the tree but report success */
+ if (count == -1) {
+ /* FIXME: return buffer_end or the end position for
+ * this single tree entry */
+ *buffer_in = buffer_end;
+ *out = NULL;
+ free_tree(tree); /* Needs to be done manually */
+ return GIT_SUCCESS;
+ }
tree->entries = (size_t)count;
- if (*buffer != ' ' || ++buffer >= buffer_end)
- goto error_cleanup;
+ if (*buffer != ' ' || ++buffer >= buffer_end) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
/* Number of children of the tree, newline-terminated */
if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
- count < 0)
- goto error_cleanup;
+ count < 0) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
tree->children_count = (size_t)count;
- if (*buffer != '\n' || ++buffer >= buffer_end)
- goto error_cleanup;
+ if (*buffer != '\n' || ++buffer >= buffer_end) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
/* 160-bit SHA-1 for this tree and it's children */
- if (buffer + GIT_OID_RAWSZ > buffer_end)
- goto error_cleanup;
+ if (buffer + GIT_OID_RAWSZ > buffer_end) {
+ error = GIT_EOBJCORRUPTED;
+ goto cleanup;
+ }
git_oid_mkraw(&tree->oid, (const unsigned char *)buffer);
buffer += GIT_OID_RAWSZ;
/* Parse children: */
if (tree->children_count > 0) {
unsigned int i;
+ int err;
tree->children = git__malloc(tree->children_count * sizeof(git_index_tree *));
if (tree->children == NULL)
- goto error_cleanup;
+ goto cleanup;
for (i = 0; i < tree->children_count; ++i) {
- tree->children[i] = read_tree_internal(&buffer, buffer_end, tree);
+ err = read_tree_internal(&tree->children[i], &buffer, buffer_end, tree);
- if (tree->children[i] == NULL)
- goto error_cleanup;
+ if (err < GIT_SUCCESS)
+ goto cleanup;
}
}
*buffer_in = buffer;
- return tree;
+ *out = tree;
+ return GIT_SUCCESS;
-error_cleanup:
+ cleanup:
free_tree(tree);
- return NULL;
+ return error;
}
static int read_tree(git_index *index, const char *buffer, size_t buffer_size)
{
const char *buffer_end = buffer + buffer_size;
+ int error;
+
+ error = read_tree_internal(&index->tree, &buffer, buffer_end, NULL);
+
+ if (buffer < buffer_end)
+ return GIT_EOBJCORRUPTED;
- index->tree = read_tree_internal(&buffer, buffer_end, NULL);
- return (index->tree != NULL && buffer == buffer_end) ? GIT_SUCCESS : GIT_EOBJCORRUPTED;
+ return error;
}
static int read_unmerged(git_index *index, const char *buffer, size_t size)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write index");
}
+
+int git_index_entry_stage(const git_index_entry *entry)
+{
+ return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
+}
# define stat _stat64
# define fstat _fstat64
+#define _S_IFLNK 0120000
+
/* stat: file mode type testing macros */
# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
/* case-insensitive string comparison */
# define strcasecmp _stricmp
return GIT_SUCCESS;
}
-int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
+int git_object_lookup_prefix(git_object **object_out, git_repository *repo, const git_oid *id, unsigned int len, git_otype type)
{
git_object *object = NULL;
git_odb_object *odb_obj;
assert(repo && object_out && id);
- object = git_cache_get(&repo->objects, id);
- if (object != NULL) {
- if (type != GIT_OBJ_ANY && type != object->type)
- return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
-
- *object_out = object;
- return GIT_SUCCESS;
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX,
+ "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+ if (len > GIT_OID_HEXSZ)
+ len = GIT_OID_HEXSZ;
+
+ if (len == GIT_OID_HEXSZ) {
+ /* We want to match the full id : we can first look up in the cache,
+ * since there is no need to check for non ambiguousity
+ */
+ object = git_cache_get(&repo->objects, id);
+ if (object != NULL) {
+ if (type != GIT_OBJ_ANY && type != object->type)
+ return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
+
+ *object_out = object;
+ return GIT_SUCCESS;
+ }
+
+ /* Object was not found in the cache, let's explore the backends.
+ * We could just use git_odb_read_unique_short_oid,
+ * it is the same cost for packed and loose object backends,
+ * but it may be much more costly for sqlite and hiredis.
+ */
+ error = git_odb_read(&odb_obj, repo->db, id);
+ } else {
+ git_oid short_oid;
+
+ /* We copy the first len*4 bits from id and fill the remaining with 0s */
+ memcpy(short_oid.id, id->id, (len + 1) / 2);
+ if (len % 2)
+ short_oid.id[len / 2] &= 0xF0;
+ memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);
+
+ /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
+ * 2 options :
+ * - We always search in the cache first. If we find that short oid is
+ * ambiguous, we can stop. But in all the other cases, we must then
+ * explore all the backends (to find an object if there was match,
+ * or to check that oid is not ambiguous if we have found 1 match in
+ * the cache)
+ * - We never explore the cache, go right to exploring the backends
+ * We chose the latter : we explore directly the backends.
+ */
+ error = git_odb_read_prefix(&odb_obj, repo->db, &short_oid, len);
}
- error = git_odb_read(&odb_obj, repo->db, id);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to lookup object");
return git__rethrow(error, "Failed to lookup object");
/* Initialize parent object */
- git_oid_cpy(&object->cached.oid, id);
+ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
object->repo = repo;
switch (type) {
return GIT_SUCCESS;
}
+int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
+ return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
+}
+
void git_object__free(void *_obj)
{
git_object *object = (git_object *)_obj;
{
fake_wstream *stream = (fake_wstream *)_stream;
- if (stream->written + len >= stream->size)
+ if (stream->written + len > stream->size)
return GIT_ENOMEM;
memcpy(stream->buffer + stream->written, data, len);
static int load_alternates(git_odb *odb, const char *objects_dir)
{
char alternates_path[GIT_PATH_MAX];
- char alternate[GIT_PATH_MAX];
- char *buffer;
+ char *buffer, *alternate;
gitfo_buf alternates_buf = GITFO_BUF_INIT;
int error;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
- while ((error == GIT_SUCCESS) && (buffer = git__strtok(alternate, buffer, "\r\n")) != NULL)
- error = add_default_backends(odb, alternate, 1);
+ while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
+ char full_path[GIT_PATH_MAX];
+
+ if (*alternate == '\0' || *alternate == '#')
+ continue;
+
+ /* relative path: build based on the current `objects` folder */
+ if (*alternate == '.') {
+ git__joinpath(full_path, objects_dir, alternate);
+ alternate = full_path;
+ }
+
+ if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS)
+ break;
+ }
gitfo_free_buf(&alternates_buf);
if (error < GIT_SUCCESS)
return error;
}
+int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+{
+ unsigned int i;
+ int error = GIT_ENOTFOUND;
+ git_oid full_oid;
+ git_rawobj raw;
+ int found = 0;
+
+ assert(out && db);
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+ if (len > GIT_OID_HEXSZ)
+ len = GIT_OID_HEXSZ;
+
+ if (len == GIT_OID_HEXSZ) {
+ *out = git_cache_get(&db->cache, short_id);
+ if (*out != NULL)
+ return GIT_SUCCESS;
+ }
+
+ for (i = 0; i < db->backends.length && found < 2; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (b->read != NULL) {
+ error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
+ switch (error) {
+ case GIT_SUCCESS:
+ found++;
+ break;
+ case GIT_ENOTFOUND:
+ break;
+ case GIT_EAMBIGUOUSOIDPREFIX:
+ return git__rethrow(error, "Failed to read object. Ambiguous sha1 prefix");
+ default:
+ return git__rethrow(error, "Failed to read object");
+ }
+ }
+ }
+
+ if (found == 1) {
+ *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw));
+ } else if (found > 1) {
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read object. Ambiguous sha1 prefix");
+ } else {
+ return git__throw(GIT_ENOTFOUND, "Failed to read object. Object not found");
+ }
+
+ return GIT_SUCCESS;
+}
+
int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
{
unsigned int i;
#include "fileops.h"
#include "hash.h"
#include "odb.h"
+#include "oid.h"
#include "delta-apply.h"
#include "filebuf.h"
char *objects_dir;
} loose_backend;
+/* State structure for exploring directories,
+ * in order to locate objects matching a short oid.
+ */
+typedef struct {
+ size_t dir_len;
+ unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
+ unsigned int short_oid_len;
+ int found; /* number of matching
+ * objects already found */
+ unsigned char res_oid[GIT_OID_HEXSZ]; /* hex formatted oid of
+ * the object found */
+} loose_locate_object_state;
+
+
/***********************************************************
*
return gitfo_exists(object_location);
}
+/* Explore an entry of a directory and see if it matches a short oid */
+int fn_locate_object_short_oid(void *state, char *pathbuf) {
+ loose_locate_object_state *sstate = (loose_locate_object_state *)state;
+
+ size_t pathbuf_len = strlen(pathbuf);
+ if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) {
+ /* Entry cannot be an object. Continue to next entry */
+ return GIT_SUCCESS;
+ }
+
+ if (!gitfo_exists(pathbuf) && gitfo_isdir(pathbuf)) {
+ /* We are already in the directory matching the 2 first hex characters */
+ if (!git_oid_ncmp_hex(sstate->short_oid_len-2, sstate->short_oid+2, (unsigned char *)pathbuf + sstate->dir_len)) {
+ if (!sstate->found) {
+ sstate->res_oid[0] = sstate->short_oid[0];
+ sstate->res_oid[1] = sstate->short_oid[1];
+ memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2);
+ }
+ sstate->found++;
+ }
+ }
+ if (sstate->found > 1)
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects");
+
+ return GIT_SUCCESS;
+}
+
+/* Locate an object matching a given short oid */
+static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len)
+{
+ char *objects_dir = backend->objects_dir;
+ size_t dir_len = strlen(objects_dir);
+ loose_locate_object_state state;
+ int error;
+
+ if (dir_len+43 > GIT_PATH_MAX)
+ return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long");
+
+ strcpy(object_location, objects_dir);
+
+ /* Add a separator if not already there */
+ if (object_location[dir_len-1] != '/')
+ object_location[dir_len++] = '/';
+
+ /* Convert raw oid to hex formatted oid */
+ git_oid_fmt((char *)state.short_oid, short_oid);
+ /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
+ sprintf(object_location+dir_len, "%.2s/", state.short_oid);
+
+ /* Check that directory exists */
+ if (gitfo_exists(object_location) || gitfo_isdir(object_location))
+ return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
+
+ state.dir_len = dir_len+3;
+ state.short_oid_len = len;
+ state.found = 0;
+ /* Explore directory to find a unique object matching short_oid */
+ error = gitfo_dirent(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state);
+ if (error) {
+ return git__rethrow(error, "Failed to locate object from short oid");
+ }
+ if (!state.found) {
+ return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
+ }
+
+ /* Convert obtained hex formatted oid to raw */
+ error = git_oid_mkstr(res_oid, (char *)state.res_oid);
+ if (error) {
+ return git__rethrow(error, "Failed to locate object from short oid");
+ }
+
+ /* Update the location according to the oid obtained */
+ git_oid_pathfmt(object_location+dir_len, res_oid);
+
+ return GIT_SUCCESS;
+}
+
return GIT_SUCCESS;
}
+int loose_backend__read_prefix(
+ git_oid *out_oid,
+ void **buffer_p,
+ size_t *len_p,
+ git_otype *type_p,
+ git_odb_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len)
+{
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+ if (len >= GIT_OID_HEXSZ) {
+ /* We can fall back to regular read method */
+ int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ if (error == GIT_SUCCESS)
+ git_oid_cpy(out_oid, short_oid);
+
+ return error;
+ } else {
+ char object_path[GIT_PATH_MAX];
+ git_rawobj raw;
+ int error;
+
+ assert(backend && short_oid);
+
+ if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) {
+ return git__rethrow(error, "Failed to read loose backend");
+ }
+
+ if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to read loose backend");
+
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ }
+
+ return GIT_SUCCESS;
+}
+
int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
char object_path[GIT_PATH_MAX];
backend->fsync_object_files = 0;
backend->parent.read = &loose_backend__read;
+ backend->parent.read_prefix = &loose_backend__read_prefix;
backend->parent.read_header = &loose_backend__read_header;
backend->parent.writestream = &loose_backend__stream;
backend->parent.exists = &loose_backend__exists;
#include "fileops.h"
#include "hash.h"
#include "odb.h"
+#include "oid.h"
#include "delta-apply.h"
+#include "sha1_lookup.h"
#include "git2/odb_backend.h"
static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n);
-static int pack_entry_find_offset(off_t *offset_out,
- struct pack_file *p, const git_oid *oid);
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
+ * is ambiguous within the pack.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
+ */
+static int pack_entry_find_offset(
+ off_t *offset_out,
+ git_oid *found_oid,
+ struct pack_file *p,
+ const git_oid *short_oid,
+ unsigned int len);
-static int pack_entry_find1(struct pack_entry *e,
- struct pack_file *p, const git_oid *oid);
+static int pack_entry_find1(
+ struct pack_entry *e,
+ struct pack_file *p,
+ const git_oid *short_oid,
+ unsigned int len);
static int pack_entry_find(struct pack_entry *e,
struct pack_backend *backend, const git_oid *oid);
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
+ * is ambiguous.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
+ */
+static int pack_entry_find_prefix(struct pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len);
+
static off_t get_delta_base(struct pack_backend *backend,
struct pack_file *p, struct pack_window **w_curs,
off_t *curpos, git_otype type,
static int pack_entry_find_offset(
off_t *offset_out,
+ git_oid *found_oid,
struct pack_file *p,
- const git_oid *oid)
+ const git_oid *short_oid,
+ unsigned int len)
{
const uint32_t *level1_ofs = p->index_map.data;
const unsigned char *index = p->index_map.data;
unsigned hi, lo, stride;
+ int pos, found = 0;
+ const unsigned char *current = 0;
*offset_out = 0;
}
index += 4 * 256;
- hi = ntohl(level1_ofs[(int)oid->id[0]]);
- lo = ((oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)oid->id[0] - 1]));
+ hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
+ lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
if (p->index_version > 1) {
stride = 20;
#ifdef INDEX_DEBUG_LOOKUP
printf("%02x%02x%02x... lo %u hi %u nr %d\n",
- oid->id[0], oid->id[1], oid->id[2], lo, hi, p->num_objects);
+ short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
-#ifdef GIT2_INDEX_LOOKUP /* TODO: use the advanced lookup method from git.git */
+ /* Use git.git lookup code */
+ pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
- int pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, oid);
- if (pos < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found");
-
- *offset_out = nth_packed_object_offset(p, pos);
- return GIT_SUCCESS;
-
-#else /* use an old and boring binary search */
+ if (pos >= 0) {
+ /* An object matching exactly the oid was found */
+ found = 1;
+ current = index + pos * stride;
+ } else {
+ /* No object was found */
+ /* pos refers to the object with the "closest" oid to short_oid */
+ pos = - 1 - pos;
+ if (pos < (int)p->num_objects) {
+ current = index + pos * stride;
+
+ if (!git_oid_ncmp_raw(len, short_oid->id, current)) {
+ found = 1;
+ }
+ }
+ }
- do {
- unsigned mi = (lo + hi) / 2;
- int cmp = memcmp(index + mi * stride, oid->id, GIT_OID_RAWSZ);
+ if (found && pos + 1 < (int)p->num_objects) {
+ /* Check for ambiguousity */
+ const unsigned char *next = current + stride;
- if (!cmp) {
- *offset_out = nth_packed_object_offset(p, mi);
- return GIT_SUCCESS;
+ if (!git_oid_ncmp_raw(len, short_oid->id, next)) {
+ found = 2;
}
+ }
- if (cmp > 0)
- hi = mi;
- else
- lo = mi+1;
-
- } while (lo < hi);
+ if (!found) {
+ return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found");
+ } else if (found > 1) {
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find offset for pack entry. Ambiguous sha1 prefix within pack");
+ } else {
+ *offset_out = nth_packed_object_offset(p, pos);
+ git_oid_mkraw(found_oid, current);
- return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found");
+#ifdef INDEX_DEBUG_LOOKUP
+ unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
+ git_oid_fmt(hex_sha1, found_oid);
+ hex_sha1[GIT_OID_HEXSZ] = '\0';
+ printf("found lo=%d %s\n", lo, hex_sha1);
#endif
+ return GIT_SUCCESS;
+ }
}
static int pack_entry_find1(
struct pack_entry *e,
struct pack_file *p,
- const git_oid *oid)
+ const git_oid *short_oid,
+ unsigned int len)
{
off_t offset;
+ git_oid found_oid;
+ int error;
assert(p);
- if (p->num_bad_objects) {
+ if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
- if (git_oid_cmp(oid, &p->bad_object_sha1[i]) == 0)
+ if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)
return git__throw(GIT_ERROR, "Failed to find pack entry. Bad object found");
}
- if (pack_entry_find_offset(&offset, p, oid) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Failed to find pack entry. Couldn't find offset");
-
- /* we found an entry in the index;
+ error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to find pack entry. Couldn't find offset");
+
+ /* we found a unique entry in the index;
* make sure the packfile backing the index
* still exists on disk */
if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS)
e->offset = offset;
e->p = p;
- git_oid_cpy(&e->sha1, oid);
+ git_oid_cpy(&e->sha1, &found_oid);
return GIT_SUCCESS;
}
return git__rethrow(error, "Failed to find pack entry");
if (backend->last_found &&
- pack_entry_find1(e, backend->last_found, oid) == GIT_SUCCESS)
+ pack_entry_find1(e, backend->last_found, oid, GIT_OID_HEXSZ) == GIT_SUCCESS)
return GIT_SUCCESS;
for (i = 0; i < backend->packs.length; ++i) {
if (p == backend->last_found)
continue;
- if (pack_entry_find1(e, p, oid) == GIT_SUCCESS) {
+ if (pack_entry_find1(e, p, oid, GIT_OID_HEXSZ) == GIT_SUCCESS) {
backend->last_found = p;
return GIT_SUCCESS;
}
return git__throw(GIT_ENOTFOUND, "Failed to find pack entry");
}
+static int pack_entry_find_prefix(
+ struct pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len)
+{
+ int error;
+ size_t i;
+ unsigned found = 0;
+
+ if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to find pack entry");
+
+ if (backend->last_found) {
+ error = pack_entry_find1(e, backend->last_found, short_oid, len);
+ if (error == GIT_EAMBIGUOUSOIDPREFIX) {
+ return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
+ } else if (error == GIT_SUCCESS) {
+ found = 1;
+ }
+ }
+
+ for (i = 0; i < backend->packs.length; ++i) {
+ struct pack_file *p;
+
+ p = git_vector_get(&backend->packs, i);
+ if (p == backend->last_found)
+ continue;
+
+ error = pack_entry_find1(e, p, short_oid, len);
+ if (error == GIT_EAMBIGUOUSOIDPREFIX) {
+ return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
+ } else if (error == GIT_SUCCESS) {
+ found++;
+ if (found > 1)
+ break;
+ backend->last_found = p;
+ }
+ }
+
+ if (!found) {
+ return git__rethrow(GIT_ENOTFOUND, "Failed to find pack entry");
+ } else if (found > 1) {
+ return git__rethrow(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find pack entry. Ambiguous sha1 prefix");
+ } else {
+ return GIT_SUCCESS;
+ }
+
+}
+
{
unsigned char *base_info = pack_window_open(backend, p, w_curs, *curpos, NULL);
off_t base_offset;
+ git_oid unused;
/* pack_window_open() assured us we have [base_info, base_info + 20)
* as a range that we can look at without walking off the
*curpos += used;
} else if (type == GIT_OBJ_REF_DELTA) {
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, p, (git_oid *)base_info) < GIT_SUCCESS)
+ if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < GIT_SUCCESS)
return git__throw(GIT_EPACKCORRUPTED, "Base entry delta is not in the same pack");
*curpos += 20;
} else
return GIT_SUCCESS;
}
+int pack_backend__read_prefix(
+ git_oid *out_oid,
+ void **buffer_p,
+ size_t *len_p,
+ git_otype *type_p,
+ git_odb_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len)
+{
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read pack backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+ if (len >= GIT_OID_HEXSZ) {
+ /* We can fall back to regular read method */
+ int error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ if (error == GIT_SUCCESS)
+ git_oid_cpy(out_oid, short_oid);
+
+ return error;
+ } else {
+ struct pack_entry e;
+ git_rawobj raw;
+ int error;
+
+ if ((error = pack_entry_find_prefix(&e, (struct pack_backend *)backend, short_oid, len)) < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to read pack backend");
+
+ if ((error = packfile_unpack(&raw, (struct pack_backend *)backend, e.p, e.offset)) < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to read pack backend");
+
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ git_oid_cpy(out_oid, &e.sha1);
+ }
+
+ return GIT_SUCCESS;
+}
+
int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct pack_entry e;
}
backend->parent.read = &pack_backend__read;
+ backend->parent.read_prefix = &pack_backend__read_prefix;
backend->parent.read_header = NULL;
backend->parent.exists = &pack_backend__exists;
backend->parent.free = &pack_backend__free;
}
+int git_oid_ncmp_raw(unsigned int len, const unsigned char *a, const unsigned char *b)
+{
+ do {
+ if (*a != *b)
+ return 1;
+ a++;
+ b++;
+ len -= 2;
+ } while (len > 1);
+ if (len)
+ if ((*a ^ *b) & 0xf0)
+ return 1;
+ return 0;
+}
+
+int git_oid_ncmp_hex(unsigned int len, const unsigned char *a, const unsigned char *b)
+{
+ return memcmp(a, b, len);
+}
+
+int gid_oid_ncmp(unsigned int len, git_oid *a, git_oid *b)
+{
+ return git_oid_ncmp_raw(len, a->id, b->id);
+}
+
typedef short node_index;
typedef union {
--- /dev/null
+#ifndef INCLUDE_oid_h__
+#define INCLUDE_oid_h__
+
+/**
+ * Compare the first ('len'*4) bits of two raw formatted oids.
+ * This can be useful for internal use.
+ * Return 0 if they match.
+ */
+int git_oid_ncmp_raw(unsigned int len, const unsigned char *a, const unsigned char *b);
+
+/**
+ * Compare the first 'len' characters of two hex formatted oids.
+ * Return 0 if they match.
+ */
+int git_oid_ncmp_hex(unsigned int len, const unsigned char *a, const unsigned char *b);
+
+#endif
static int reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force);
static int reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force);
static int reference_rename(git_reference *ref, const char *new_name, int force);
+static int reference_available(git_repository *repo, const char *ref, const char *old_ref);
/* name normalization */
static int check_valid_ref_char(char ch);
* this is a disaster */
assert(ref->ref.type & GIT_REF_OID);
- if ((error = packed_find_peel(ref)) < GIT_SUCCESS)
+ if ((error = packed_find_peel(ref)) < GIT_SUCCESS) {
+ error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled");
goto cleanup;
+ }
+
if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
goto cleanup;
if(git_reference_lookup(&ref, repo, name) == GIT_SUCCESS && !force)
return git__throw(GIT_EEXISTS, "Failed to create reference OID. Reference already exists");
+ if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to create reference");
+
/*
* If they old ref was of the same type, then we can just update
* it (once we've checked that the target is valid). Otherwise we
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create reference OID");
}
+static int _reference_available_cb(const char *ref, void *data)
+{
+ const char *new, *old;
+ git_vector *refs;
+
+ assert(ref && data);
+
+ refs = (git_vector *)data;
+
+ new = (const char *)git_vector_get(refs, 0);
+ old = (const char *)git_vector_get(refs, 1);
+
+ if (!old || strcmp(old, ref)) {
+ int reflen = strlen(ref);
+ int newlen = strlen(new);
+ int cmplen = reflen < newlen ? reflen : newlen;
+ const char *lead = reflen < newlen ? new : ref;
+
+ if (!strncmp(new, ref, cmplen) &&
+ lead[cmplen] == '/')
+ return GIT_EEXISTS;
+ }
+
+ return GIT_SUCCESS;
+}
+
+static int reference_available(git_repository *repo, const char *ref, const char* old_ref)
+{
+ int error;
+ git_vector refs;
+
+ if (git_vector_init(&refs, 2, NULL) < GIT_SUCCESS)
+ return GIT_ENOMEM;
+
+ git_vector_insert(&refs, (void *)ref);
+ git_vector_insert(&refs, (void *)old_ref);
+
+ error = git_reference_listcb(repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&refs);
+
+ git_vector_free(&refs);
+
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__throw(GIT_EEXISTS, "Reference name `%s` conflicts with existing reference", ref);
+}
+
/*
* Rename a reference
*
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to rename reference");
+ new_name = normalized_name;
+
/* Ensure we're not going to overwrite an existing reference
unless the user has allowed us */
error = git_reference_lookup(&looked_up_ref, ref->owner, new_name);
error != GIT_ENOTFOUND)
return git__rethrow(error, "Failed to rename reference");
+ if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
+ return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to rename reference. Reference already exists");
old_name = ref->name;
ref->name = git__strdup(new_name);
if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to delete reference");
- git_hashtable_remove(ref->owner->references.packfile, ref->name);
+ if (git_hashtable_remove(ref->owner->references.packfile, ref->name) < GIT_SUCCESS)
+ return git__throw(GIT_ENOTFOUND, "Reference not found");
+
error = packed_write(ref->owner);
} else {
char full_path[GIT_PATH_MAX];
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
+#define GIT_FILE_CONTENT_PREFIX "gitdir: "
+#define GIT_FILE_CONTENT_PREFIX_LENGTH 8
+
#define GIT_BRANCH_MASTER "master"
typedef struct {
if (git_dir == NULL)
return git__throw(GIT_ENOTFOUND, "Failed to open repository. Git dir not found");
- error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_dir);
+ error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_dir, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open repository");
if (git_object_directory == NULL)
git__joinpath(path_aux, repo->path_repository, GIT_OBJECTS_DIR);
else {
- error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_object_directory);
+ error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_object_directory, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open repository");
}
if (git_work_tree == NULL)
repo->is_bare = 1;
else {
- error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_work_tree);
+ error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_work_tree, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open repository");
if (git_index_file == NULL)
git__joinpath(path_aux, repo->path_repository, GIT_INDEX_FILE);
else {
- error = gitfo_prettify_file_path(path_aux, sizeof(path_aux), git_index_file);
+ error = gitfo_prettify_file_path(path_aux, sizeof(path_aux), git_index_file, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open repository");
}
return git__rethrow(error, "Failed to open repository");
}
+static int discover_repository_dirs(git_repository *repo, const char *path)
+{
+ int error;
+
+ error = guess_repository_dirs(repo, path);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ error = check_repository_dirs(repo);
+ if (error < GIT_SUCCESS)
+ return error;
+
+ return GIT_SUCCESS;
+}
+
int git_repository_open(git_repository **repo_out, const char *path)
{
git_repository *repo;
if (repo == NULL)
return GIT_ENOMEM;
- error = guess_repository_dirs(repo, path);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = check_repository_dirs(repo);
+ error = discover_repository_dirs(repo, path);
if (error < GIT_SUCCESS)
goto cleanup;
return git__rethrow(error, "Failed to open repository");
}
-void git_repository_free(git_repository *repo)
+static int abspath(char *buffer_out, size_t size, const char *path)
{
- if (repo == NULL)
- return;
+ assert(buffer_out && size >= GIT_PATH_MAX);
- git_cache_free(&repo->objects);
- git_repository__refcache_free(&repo->references);
+ #ifdef GIT_WIN32
+ if (_fullpath(buffer_out, path, size) == NULL)
+ return git__throw(GIT_EOSERR, "Failed to retrieve real path: %s causing errors", buffer_out);
+ #else
+ if (realpath(path, buffer_out) == NULL)
+ return git__throw(GIT_EOSERR, "Failed to retrieve real path: %s causing errors", buffer_out);
+ #endif
+
+ gitfo_posixify_path(buffer_out);
+
+ return GIT_SUCCESS;
+}
+
+static int retrieve_device(dev_t *device_out, const char *path)
+{
+ struct stat path_info;
+
+ assert(device_out);
+
+ if (gitfo_stat(path, &path_info))
+ return git__throw(GIT_EOSERR, "Failed to get file informations: %s", path);
+
+ *device_out = path_info.st_dev;
+
+ return GIT_SUCCESS;
+}
+
+static int retrieve_ceiling_directories_offset(const char *path, const char *ceiling_directories)
+{
+ char buf[GIT_PATH_MAX + 1];
+ char buf2[GIT_PATH_MAX + 1];
+ const char *ceil, *sep;
+ int len, max_len = -1;
+ int min_len;
+
+ assert(path);
+
+ min_len = gitfo_retrieve_path_root_offset(path) + 1;
+
+ if (ceiling_directories == NULL || min_len == 0)
+ return min_len;
+
+ for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
+ for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
+ len = sep - ceil;
+
+ if (len == 0 || len > GIT_PATH_MAX || gitfo_retrieve_path_root_offset(ceil) == -1)
+ continue;
+
+ strncpy(buf, ceil, len);
+ buf[len] = '\0';
+
+ if (abspath(buf2, sizeof(buf2), buf) < GIT_SUCCESS)
+ continue;
+
+ len = strlen(buf2);
+ if (len > 0 && buf2[len-1] == '/')
+ buf[--len] = '\0';
+
+ if (!strncmp(path, buf2, len) &&
+ path[len] == '/' &&
+ len > max_len)
+ {
+ max_len = len;
+ }
+ }
+
+ return max_len <= min_len ? min_len : max_len;
+}
+
+static int read_gitfile(char *path_out, size_t size, const char *file_path, const char *base_path)
+{
+ gitfo_buf file;
+ int error, end_offset;
+ char *data;
+
+ assert(file_path && path_out && size > 0);
+
+ error = gitfo_read_file(&file, file_path);
+
+ if (error < GIT_SUCCESS)
+ return error;
+
+ data = (char*)(file.data);
+
+ if (git__prefixcmp(data, GIT_FILE_CONTENT_PREFIX)) {
+ gitfo_free_buf(&file);
+ return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
+ }
+
+ end_offset = strlen(data) - 1;
+
+ for (;data[end_offset] == '\r' || data[end_offset] == '\n'; --end_offset);
+ data[end_offset + 1] = '\0';
+
+ if (GIT_FILE_CONTENT_PREFIX_LENGTH == end_offset + 1) {
+ gitfo_free_buf(&file);
+ return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
+ }
+
+ error = gitfo_prettify_dir_path(path_out, size, data + GIT_FILE_CONTENT_PREFIX_LENGTH, base_path);
+ if (error == GIT_SUCCESS) {
+ end_offset = strlen(path_out);
+
+ if (end_offset > 0 && path_out[end_offset - 1] == '/')
+ path_out[end_offset - 1 ] = '\0';
+ }
+ gitfo_free_buf(&file);
+
+ return error;
+}
+
+static void git_repository__free_dirs(git_repository *repo)
+{
free(repo->path_workdir);
+ repo->path_workdir = NULL;
free(repo->path_index);
+ repo->path_index = NULL;
free(repo->path_repository);
+ repo->path_repository = NULL;
free(repo->path_odb);
+ repo->path_odb = NULL;
+}
+
+void git_repository_free(git_repository *repo)
+{
+ if (repo == NULL)
+ return;
+
+ git_cache_free(&repo->objects);
+ git_repository__refcache_free(&repo->references);
+ git_repository__free_dirs(repo);
if (repo->db != NULL)
git_odb_close(repo->db);
- if (repo->index != NULL) {
- repo->index->repository = NULL;
- git_index_free(repo->index);
- }
-
free(repo);
}
-int git_repository_index(git_index **index_out, git_repository *repo)
+int git_repository_discover(char *repository_path, size_t size, const char *start_path, int across_fs, const char *ceiling_dirs)
{
- int error;
+ git_repository repo;
+ int error, ceiling_offset;
+ char bare_path[GIT_PATH_MAX];
+ char normal_path[GIT_PATH_MAX];
+ char *found_path;
+ dev_t current_device = 0;
- assert(index_out && repo);
+ assert(start_path && repository_path);
+ memset(&repo, 0x0, sizeof(git_repository));
+
+ error = abspath(bare_path, sizeof(bare_path), start_path);
+
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ if (!across_fs) {
+ error = retrieve_device(¤t_device, bare_path);
- if (repo->index == NULL) {
- error = git_index_open_inrepo(&repo->index, repo); /* TODO: move index.c to new error handling */
if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open repository index");
+ goto cleanup;
+ }
+
+ ceiling_offset = retrieve_ceiling_directories_offset(bare_path, ceiling_dirs);
+ git__joinpath(normal_path, bare_path, DOT_GIT);
+
+ while(1){
+ //look for .git file
+ if (gitfo_isfile(normal_path) == GIT_SUCCESS) {
+ error = read_gitfile(repository_path, size, normal_path, bare_path);
+
+ if (error < GIT_SUCCESS) {
+ git__rethrow(error, "Unable to read git file `%s`", normal_path);
+ goto cleanup;
+ }
+
+ error = discover_repository_dirs(&repo, repository_path);
+ if (error < GIT_SUCCESS)
+ goto cleanup;
- assert(repo->index != NULL);
+ git_repository__free_dirs(&repo);
+
+ return GIT_SUCCESS;
+ }
+
+ //look for .git repository
+ error = discover_repository_dirs(&repo, normal_path);
+ if (error < GIT_SUCCESS && error != GIT_ENOTAREPO)
+ goto cleanup;
+
+ if (error == GIT_SUCCESS) {
+ found_path = normal_path;
+ break;
+ }
+
+ git_repository__free_dirs(&repo);
+
+ //look for bare repository in current directory
+ error = discover_repository_dirs(&repo, bare_path);
+ if (error < GIT_SUCCESS && error != GIT_ENOTAREPO)
+ goto cleanup;
+
+ if (error == GIT_SUCCESS) {
+ found_path = bare_path;
+ break;
+ }
+
+ git_repository__free_dirs(&repo);
+
+ if (git__dirname_r(normal_path, sizeof(normal_path), bare_path) < GIT_SUCCESS)
+ goto cleanup;
+
+ if (!across_fs) {
+ dev_t new_device;
+ error = retrieve_device(&new_device, normal_path);
+
+ if (error < GIT_SUCCESS)
+ goto cleanup;
+
+ if (current_device != new_device) {
+ error = git__throw(GIT_ENOTAREPO,"Not a git repository (or any parent up to mount parent %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM_ENVIRONMENT not set).", bare_path);
+ goto cleanup;
+ }
+ current_device = new_device;
+ }
+
+ strcpy(bare_path, normal_path);
+ git__joinpath(normal_path, bare_path, DOT_GIT);
+
+ //nothing has been found, lets try the parent directory
+ if (bare_path[ceiling_offset] == '\0') {
+ error = git__throw(GIT_ENOTAREPO,"Not a git repository (or any of the parent directories): %s", start_path);
+ goto cleanup;
+ }
+
+ }
+
+ if (size < (strlen(found_path) + 1) * sizeof(char)) {
+ error = git__throw(GIT_EOVERFLOW, "The repository buffer is not long enough to handle the repository path `%s`", found_path);
+ goto cleanup;
}
- *index_out = repo->index;
+ strcpy(repository_path, found_path);
+ git_repository__free_dirs(&repo);
+
return GIT_SUCCESS;
+
+ cleanup:
+ git_repository__free_dirs(&repo);
+ return git__rethrow(error, "Failed to discover repository");
}
git_odb *git_repository_database(git_repository *repo)
char temp_path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
- error = gitfo_prettify_dir_path(temp_path, sizeof(temp_path), path);
+ error = gitfo_prettify_dir_path(temp_path, sizeof(temp_path), path, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to find directory to initialize repository");
return git_reference_resolve(&branch, head) == GIT_SUCCESS ? 0 : 1;
}
-const char *git_repository_path(git_repository *repo)
+const char *git_repository_path(git_repository *repo, git_repository_pathid id)
{
assert(repo);
- return repo->path_repository;
+
+ switch (id) {
+ case GIT_REPO_PATH:
+ return repo->path_repository;
+
+ case GIT_REPO_PATH_INDEX:
+ return repo->path_index;
+
+ case GIT_REPO_PATH_ODB:
+ return repo->path_odb;
+
+ case GIT_REPO_PATH_WORKDIR:
+ return repo->path_workdir;
+
+ default:
+ return NULL;
+ }
}
-const char *git_repository_workdir(git_repository *repo)
+int git_repository_is_bare(git_repository *repo)
{
assert(repo);
- return repo->path_workdir;
+ return repo->is_bare;
}
struct git_repository {
git_odb *db;
- git_index *index;
git_cache objects;
git_refcache references;
--- /dev/null
+/*
+ * This file is basically taken from git code.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+
+#include "sha1_lookup.h"
+#include "common.h"
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ * unsigned lo, hi;
+ * do {
+ * unsigned mi = (lo + hi) / 2;
+ * int cmp = "entry pointed at by mi" minus "target";
+ * if (!cmp)
+ * return (mi is the wanted one)
+ * if (cmp > 0)
+ * hi = mi; "mi is larger than target"
+ * else
+ * lo = mi+1; "mi is smaller than target"
+ * } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ * above the target (it could be at the target), hi points at a
+ * slot that is guaranteed to be above the target (it can never
+ * be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ * as lo, but never can be as same as hi), and check if it hits
+ * the target. There are three cases:
+ *
+ * - if it is a hit, we are happy.
+ *
+ * - if it is strictly higher than the target, we set it to hi,
+ * and repeat the search.
+ *
+ * - if it is strictly lower than the target, we update lo to
+ * one slot after it, because we allow lo to be at the target.
+ *
+ * If the loop exits, there is no matching entry.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied. When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ *
+ * Now, we can take advantage of the fact that SHA-1 is a good hash
+ * function, and as long as there are enough entries in the table, we
+ * can expect uniform distribution. An entry that begins with for
+ * example "deadbeef..." is much likely to appear much later than in
+ * the midway of the table. It can reasonably be expected to be near
+ * 87% (222/256) from the top of the table.
+ *
+ * However, we do not want to pick "mi" too precisely. If the entry at
+ * the 87% in the above example turns out to be higher than the target
+ * we are looking for, we would end up narrowing the search space down
+ * only by 13%, instead of 50% we would get if we did a simple binary
+ * search. So we would want to hedge our bets by being less aggressive.
+ *
+ * The table at "table" holds at least "nr" entries of "elem_size"
+ * bytes each. Each entry has the SHA-1 key at "key_offset". The
+ * table is sorted by the SHA-1 key of the entries. The caller wants
+ * to find the entry with "key", and knows that the entry at "lo" is
+ * not higher than the entry it is looking for, and that the entry at
+ * "hi" is higher than the entry it is looking for.
+ */
+int sha1_entry_pos(const void *table,
+ size_t elem_size,
+ size_t key_offset,
+ unsigned lo, unsigned hi, unsigned nr,
+ const unsigned char *key)
+{
+ const unsigned char *base = (const unsigned char*)table;
+ const unsigned char *hi_key, *lo_key;
+ unsigned ofs_0;
+
+ if (!nr || lo >= hi)
+ return -1;
+
+ if (nr == hi)
+ hi_key = NULL;
+ else
+ hi_key = base + elem_size * hi + key_offset;
+ lo_key = base + elem_size * lo + key_offset;
+
+ ofs_0 = 0;
+ do {
+ int cmp;
+ unsigned ofs, mi, range;
+ unsigned lov, hiv, kyv;
+ const unsigned char *mi_key;
+
+ range = hi - lo;
+ if (hi_key) {
+ for (ofs = ofs_0; ofs < 20; ofs++)
+ if (lo_key[ofs] != hi_key[ofs])
+ break;
+ ofs_0 = ofs;
+ /*
+ * byte 0 thru (ofs-1) are the same between
+ * lo and hi; ofs is the first byte that is
+ * different.
+ */
+ hiv = hi_key[ofs_0];
+ if (ofs_0 < 19)
+ hiv = (hiv << 8) | hi_key[ofs_0+1];
+ } else {
+ hiv = 256;
+ if (ofs_0 < 19)
+ hiv <<= 8;
+ }
+ lov = lo_key[ofs_0];
+ kyv = key[ofs_0];
+ if (ofs_0 < 19) {
+ lov = (lov << 8) | lo_key[ofs_0+1];
+ kyv = (kyv << 8) | key[ofs_0+1];
+ }
+ assert(lov < hiv);
+
+ if (kyv < lov)
+ return -1 - lo;
+ if (hiv < kyv)
+ return -1 - hi;
+
+ /*
+ * Even if we know the target is much closer to 'hi'
+ * than 'lo', if we pick too precisely and overshoot
+ * (e.g. when we know 'mi' is closer to 'hi' than to
+ * 'lo', pick 'mi' that is higher than the target), we
+ * end up narrowing the search space by a smaller
+ * amount (i.e. the distance between 'mi' and 'hi')
+ * than what we would have (i.e. about half of 'lo'
+ * and 'hi'). Hedge our bets to pick 'mi' less
+ * aggressively, i.e. make 'mi' a bit closer to the
+ * middle than we would otherwise pick.
+ */
+ kyv = (kyv * 6 + lov + hiv) / 8;
+ if (lov < hiv - 1) {
+ if (kyv == lov)
+ kyv++;
+ else if (kyv == hiv)
+ kyv--;
+ }
+ mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
+
+#ifdef INDEX_DEBUG_LOOKUP
+ printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
+ printf("ofs %u lov %x, hiv %x, kyv %x\n",
+ ofs_0, lov, hiv, kyv);
+#endif
+
+ if (!(lo <= mi && mi < hi)) {
+ return git__throw(GIT_ERROR, "Assertion failure. Binary search invariant is false");
+ }
+
+ mi_key = base + elem_size * mi + key_offset;
+ cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
+ if (!cmp)
+ return mi;
+ if (cmp > 0) {
+ hi = mi;
+ hi_key = mi_key;
+ } else {
+ lo = mi + 1;
+ lo_key = mi_key + elem_size;
+ }
+ } while (lo < hi);
+ return -((int)lo)-1;
+}
--- /dev/null
+#ifndef INCLUDE_sha1_lookup_h__
+#define INCLUDE_sha1_lookup_h__
+
+#include <stdlib.h>
+
+int sha1_entry_pos(const void *table,
+ size_t elem_size,
+ size_t key_offset,
+ unsigned lo, unsigned hi, unsigned nr,
+ const unsigned char *key);
+
+#endif
const char *offset_start;
const char *offset_end;
+ //we are sure that *buffer == ' '
offset_start = buffer + 1;
if (*offset_start == '\n') {
if (offset_start[0] != '-' && offset_start[0] != '+')
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
+ if (offset_start[1] < '0' || offset_start[1] > '9')
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset.");
+
if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
if (offset_end - offset_start != 5)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
+ if (dec_offset > 1400)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large");
+
hours = dec_offset / 100;
mins = dec_offset % 100;
if (hours > 14) // see http://www.worldtimezone.com/faq.html
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");;
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");
if (mins > 59)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large");
buffer += header_len;
/* Parse name */
- if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL)
+ if ((name_end = strstr(buffer, " <")) == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start");
- name_length = name_end - buffer - 1;
+ name_length = name_end - buffer;
+ if (name_length <= 0)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Missing tagger name");
+
sig->name = git__malloc(name_length + 1);
if (sig->name == NULL)
return GIT_ENOMEM;
memcpy(sig->name, buffer, name_length);
sig->name[name_length] = 0;
- buffer = name_end + 1;
+ buffer = name_end + 2;
if (buffer >= line_end)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
/* Parse email */
- if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL)
+ if ((email_end = strstr(buffer, "> ")) == NULL)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end");
email_length = email_end - buffer;
memcpy(sig->email, buffer, email_length);
sig->email[email_length] = 0;
- buffer = email_end + 1;
+ buffer = email_end + 2;
if (buffer >= line_end)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly");
+ /* verify email */
+ if (strpbrk(sig->email, "><\n ") != NULL)
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Malformed e-mail");
+
if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS)
return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number");
return git__rethrow(error, "Failed to parse tag");
}
+ if( *buffer != '\n' )
+ return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. No new line before message");
+
text_len = buffer_end - ++buffer;
tag->message = git__malloc(text_len + 1);
return &entry->oid;
}
+git_otype git_tree_entry_type(const git_tree_entry *entry)
+{
+ assert(entry);
+
+ if (S_ISGITLINK(entry->attr))
+ return GIT_OBJ_COMMIT;
+ else if (S_ISDIR(entry->attr))
+ return GIT_OBJ_TREE;
+ else
+ return GIT_OBJ_BLOB;
+}
+
int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry)
{
assert(entry && object_out);
return git_vector_get(&tree->entries, idx);
}
-const git_tree_entry *git_tree_entry_byindex(git_tree *tree, int idx)
+const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
{
assert(tree);
- return git_vector_get(&tree->entries, (unsigned int)idx);
+ return git_vector_get(&tree->entries, idx);
}
-size_t git_tree_entrycount(git_tree *tree)
+unsigned int git_tree_entrycount(git_tree *tree)
{
assert(tree);
return tree->entries.length;
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
{
git_treebuilder *bld;
- size_t i, source_entries = DEFAULT_TREE_SIZE;
+ unsigned int i, source_entries = DEFAULT_TREE_SIZE;
assert(builder_p);
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
- size_t i, size = 0;
+ unsigned int i, size = 0;
char filemode[MAX_FILEMODE_BYTES + 1 + 1];
git_odb_stream *stream;
int error;
stream->write(stream, filemode, strlen(filemode));
stream->write(stream, entry->filename, entry->filename_len + 1);
stream->write(stream, (char *)entry->oid.id, GIT_OID_RAWSZ);
- }
+ }
error = stream->finalize_write(oid, stream);
stream->free(stream);
void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
{
- size_t i;
+ unsigned int i;
assert(bld && filter);
void git_treebuilder_clear(git_treebuilder *bld)
{
- size_t i;
+ unsigned int i;
assert(bld);
for (i = 0; i < bld->entries.length; ++i) {
*buffer_out = '\0';
}
-static char *strtok_raw(char *output, char *src, char *delimit, int keep)
+char *git__strtok(char **end, const char *sep)
{
- while (*src && strchr(delimit, *src) == NULL)
- *output++ = *src++;
+ char *ptr = *end;
- *output = 0;
+ while (*ptr && strchr(sep, *ptr))
+ ++ptr;
- if (keep)
- return src;
- else
- return *src ? src+1 : src;
-}
+ if (*ptr) {
+ char *start = ptr;
+ *end = start + 1;
-char *git__strtok(char *output, char *src, char *delimit)
-{
- return strtok_raw(output, src, delimit, 0);
-}
+ while (**end && !strchr(sep, **end))
+ ++*end;
-char *git__strtok_keep(char *output, char *src, char *delimit)
-{
- return strtok_raw(output, src, delimit, 1);
+ if (**end) {
+ **end = '\0';
+ ++*end;
+ }
+
+ return start;
+ }
+
+ return NULL;
}
void git__hexdump(const char *buffer, size_t len)
# define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))
#endif
-extern char *git__strtok(char *output, char *src, char *delimit);
-extern char *git__strtok_keep(char *output, char *src, char *delimit);
+extern char *git__strtok(char **end, const char *sep);
extern void git__strntolower(char *str, int len);
extern void git__strtolower(char *str);
--- /dev/null
+discover.git
--- /dev/null
+[valid "subsection"]
+ something = true
+
+[something "else"]
+ something = false
--- /dev/null
+[valid "subsection"]
+ something = a
+; we don't allow anything after closing "
+[sec "subsec"x]
+ bleh = blah
#include "vector.h"
#include "fileops.h"
+#include "filebuf.h"
BEGIN_TEST(string0, "compare prefixes")
must_be_true(git__prefixcmp("", "") == 0);
#undef TOPDIR_TEST
END_TEST
-typedef int (normalize_path)(char *, size_t, const char *);
+typedef int (normalize_path)(char *, size_t, const char *, const char *);
/* Assert flags */
#define CWD_AS_PREFIX 1
if (error < GIT_SUCCESS)
return error;
- error = normalizer(buffer_out, sizeof(buffer_out), input_path);
+ error = normalizer(buffer_out, sizeof(buffer_out), input_path, NULL);
if (error < GIT_SUCCESS)
return error;
for (i = 0; i < number_to_escape + 1; i++)
git__joinpath(current_workdir, current_workdir, "../");
- must_fail(gitfo_prettify_dir_path(prettified, sizeof(prettified), current_workdir));
+ must_fail(gitfo_prettify_dir_path(prettified, sizeof(prettified), current_workdir, NULL));
END_TEST
typedef struct name_data {
must_pass(knockdown(&odd));
END_TEST
+BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock")
+ git_filebuf file;
+ char test[] = "test", testlock[] = "test.lock";
+
+ must_pass(gitfo_creat(testlock, 0744));
+ must_fail(git_filebuf_open(&file, test, 0));
+ must_pass(gitfo_exists(testlock));
+ must_pass(gitfo_unlink(testlock));
+END_TEST
BEGIN_SUITE(core)
ADD_TEST(string0);
ADD_TEST(dirent2);
ADD_TEST(dirent3);
ADD_TEST(dirent4);
+
+ ADD_TEST(filebuf0);
END_SUITE
BEGIN_TEST(read0, "load an empty index")
git_index *index;
- must_pass(git_index_open_bare(&index, "in-memory-index"));
+ must_pass(git_index_open(&index, "in-memory-index"));
must_be_true(index->on_disk == 0);
must_pass(git_index_read(index));
unsigned int i;
git_index_entry **entries;
- must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
+ must_pass(git_index_open(&index, TEST_INDEX_PATH));
must_be_true(index->on_disk);
must_pass(git_index_read(index));
BEGIN_TEST(read2, "load a standard index (git.git index)")
git_index *index;
- must_pass(git_index_open_bare(&index, TEST_INDEX2_PATH));
+ must_pass(git_index_open(&index, TEST_INDEX2_PATH));
must_be_true(index->on_disk);
must_pass(git_index_read(index));
git_index *index;
unsigned int i;
- must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
+ must_pass(git_index_open(&index, TEST_INDEX_PATH));
must_pass(git_index_read(index));
for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
git_index *index;
unsigned int i;
- must_pass(git_index_open_bare(&index, "fake-index"));
+ must_pass(git_index_open(&index, "fake-index"));
for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
int idx = git_index_find(index, TEST_ENTRIES[i].path);
must_pass(copy_file(TEST_INDEXBIG_PATH, "index_rewrite"));
- must_pass(git_index_open_bare(&index, "index_rewrite"));
+ must_pass(git_index_open(&index, "index_rewrite"));
must_pass(git_index_read(index));
must_be_true(index->on_disk);
BEGIN_TEST(sort1, "sort the entires in an empty index")
git_index *index;
- must_pass(git_index_open_bare(&index, "fake-index"));
+ must_pass(git_index_open(&index, "fake-index"));
/* FIXME: this test is slightly dumb */
must_be_true(index->entries.sorted);
/* An existing reference... */
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
- /* Can not be renamed to the name of another existing reference. */
+ /* Can be force-renamed to the name of another existing reference. */
must_pass(git_reference_rename_f(looked_up_ref, packed_test_head_name));
/* Check we actually renamed it */
close_temp_repo(repo);
END_TEST
+static const char *ref_one_name = "refs/heads/one/branch";
+static const char *ref_one_name_new = "refs/heads/two/branch";
+static const char *ref_two_name = "refs/heads/two";
+
+BEGIN_TEST(rename6, "can not overwrite name of existing reference")
+ git_reference *ref, *ref_one, *ref_one_new, *ref_two;
+ git_repository *repo;
+ git_oid id;
+
+ must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
+
+ must_pass(git_reference_lookup(&ref, repo, ref_master_name));
+ must_be_true(ref->type & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_oid(ref));
+
+ /* Create loose references */
+ must_pass(git_reference_create_oid(&ref_one, repo, ref_one_name, &id));
+ must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id));
+
+ /* Pack everything */
+ must_pass(git_reference_packall(repo));
+
+ /* Attempt to create illegal reference */
+ must_fail(git_reference_create_oid(&ref_one_new, repo, ref_one_name_new, &id));
+
+ /* Illegal reference couldn't be created so this is supposed to fail */
+ must_fail(git_reference_lookup(&ref_one_new, repo, ref_one_name_new));
+
+ close_temp_repo(repo);
+END_TEST
+
BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove both tracks in the filesystem")
git_reference *looked_up_ref, *another_looked_up_ref;
git_repository *repo;
ADD_TEST(rename3);
ADD_TEST(rename4);
ADD_TEST(rename5);
+ ADD_TEST(rename6);
ADD_TEST(delete0);
ADD_TEST(list0);
if (repo->path_index != NULL || expected_path_index != NULL) {
if (git__suffixcmp(repo->path_index, expected_path_index) != 0)
goto cleanup;
- }
+
+ if (git_repository_is_bare(repo) == 1)
+ goto cleanup;
+ } else if (git_repository_is_bare(repo) == 0)
+ goto cleanup;
+
+ if (git_repository_is_empty(repo) == 0)
+ goto cleanup;
git_repository_free(repo);
rmdir_recurs(working_directory);
must_pass(remove_placeholders(TEMP_REPO_FOLDER, "dummy-marker.txt"));
must_pass(git_repository_open(&repo, TEMP_REPO_FOLDER));
- must_be_true(git_repository_path(repo) != NULL);
- must_be_true(git_repository_workdir(repo) == NULL);
+ must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL);
+ must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) == NULL);
git_repository_free(repo);
must_pass(rmdir_recurs(TEMP_REPO_FOLDER));
must_pass(remove_placeholders(DEST_REPOSITORY_FOLDER, "dummy-marker.txt"));
must_pass(git_repository_open(&repo, DEST_REPOSITORY_FOLDER));
- must_be_true(git_repository_path(repo) != NULL);
- must_be_true(git_repository_workdir(repo) != NULL);
+ must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL);
+ must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) != NULL);
git_repository_free(repo);
must_pass(rmdir_recurs(TEMP_REPO_FOLDER));
git_repository_free(repo_empty);
END_TEST
+#define DISCOVER_FOLDER TEST_RESOURCES "/discover.git"
+
+#define SUB_REPOSITORY_FOLDER_NAME "sub_repo"
+#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME
+#define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub"
+
+#define REPOSITORY_ALTERNATE_FOLDER DISCOVER_FOLDER "/alternate_sub_repo"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB REPOSITORY_ALTERNATE_FOLDER "/sub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB "/subsub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/subsubsub"
+
+#define ALTERNATE_MALFORMED_FOLDER1 DISCOVER_FOLDER "/alternate_malformed_repo1"
+#define ALTERNATE_MALFORMED_FOLDER2 DISCOVER_FOLDER "/alternate_malformed_repo2"
+#define ALTERNATE_MALFORMED_FOLDER3 DISCOVER_FOLDER "/alternate_malformed_repo3"
+#define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo"
+
+static int ensure_repository_discover(const char *start_path, const char *ceiling_dirs, const char *expected_path)
+{
+ int error;
+ char found_path[GIT_PATH_MAX];
+
+ error = git_repository_discover(found_path, sizeof(found_path), start_path, 0, ceiling_dirs);
+ //across_fs is always 0 as we can't automate the filesystem change tests
+
+ if (error < GIT_SUCCESS)
+ return error;
+
+ return strcmp(found_path, expected_path) ? GIT_ERROR : GIT_SUCCESS;
+}
+
+static int write_file(const char *path, const char *content)
+{
+ int error;
+ git_file file;
+
+ if (gitfo_exists(path) == GIT_SUCCESS) {
+ error = gitfo_unlink(path);
+
+ if (error < GIT_SUCCESS)
+ return error;
+ }
+
+ file = gitfo_creat_force(path, 0644);
+ if (file < GIT_SUCCESS)
+ return file;
+
+ error = gitfo_write(file, (void*)content, strlen(content) * sizeof(char));
+
+ gitfo_close(file);
+
+ return error;
+}
+
+//no check is performed on ceiling_dirs length, so be sure it's long enough
+static int append_ceiling_dir(char *ceiling_dirs, const char *path)
+{
+ int len = strlen(ceiling_dirs);
+ int error;
+
+ error = gitfo_prettify_dir_path(ceiling_dirs + len + (len ? 1 : 0), GIT_PATH_MAX, path, NULL);
+ if (error < GIT_SUCCESS)
+ return git__rethrow(error, "Failed to append ceiling directory.");
+
+ if (len)
+ ceiling_dirs[len] = ':';
+
+ return GIT_SUCCESS;
+}
+
+BEGIN_TEST(discover0, "test discover")
+ git_repository *repo;
+ char ceiling_dirs[GIT_PATH_MAX * 2] = "";
+ char repository_path[GIT_PATH_MAX];
+ char sub_repository_path[GIT_PATH_MAX];
+ char found_path[GIT_PATH_MAX];
+ int mode = 0755;
+
+ rmdir_recurs(DISCOVER_FOLDER);
+ must_pass(append_ceiling_dir(ceiling_dirs,TEST_RESOURCES));
+ gitfo_mkdir_recurs(DISCOVER_FOLDER, mode);
+
+ must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
+
+ must_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
+ must_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs));
+
+ must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
+ must_pass(gitfo_mkdir_recurs(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
+ must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
+
+ must_pass(gitfo_mkdir_recurs(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
+ must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path));
+
+ must_pass(gitfo_mkdir_recurs(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
+ must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
+ must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
+ must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
+
+ must_pass(gitfo_mkdir_recurs(ALTERNATE_MALFORMED_FOLDER1, mode));
+ must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"));
+ must_pass(gitfo_mkdir_recurs(ALTERNATE_MALFORMED_FOLDER2, mode));
+ must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"));
+ must_pass(gitfo_mkdir_recurs(ALTERNATE_MALFORMED_FOLDER3, mode));
+ must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"));
+ must_pass(gitfo_mkdir_recurs(ALTERNATE_NOT_FOUND_FOLDER, mode));
+ must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"));
+ must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
+ must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
+ must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
+ must_be_true(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
+
+ must_pass(append_ceiling_dir(ceiling_dirs, SUB_REPOSITORY_FOLDER));
+ //this must pass as ceiling_directories cannot predent the current
+ //working directory to be checked
+ must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
+ must_be_true(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs) == GIT_ENOTAREPO);
+ must_be_true(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs) == GIT_ENOTAREPO);
+ must_be_true(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs) == GIT_ENOTAREPO);
+
+ //.gitfile redirection should not be affected by ceiling directories
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
+ must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
+
+ rmdir_recurs(DISCOVER_FOLDER);
+END_TEST
+
BEGIN_SUITE(repository)
ADD_TEST(odb0);
ADD_TEST(odb1);
ADD_TEST(open1);
ADD_TEST(open2);
ADD_TEST(empty0);
+ ADD_TEST(discover0);
END_SUITE
git_config_free(cfg);
END_TEST
+BEGIN_TEST(config6, "test blank lines")
+ git_config *cfg;
+ int i;
+
+ must_pass(git_config_open_file(&cfg, CONFIG_BASE "/config6"));
+
+ must_pass(git_config_get_bool(cfg, "valid.subsection.something", &i));
+ must_be_true(i == 1);
+
+ must_pass(git_config_get_bool(cfg, "something.else.something", &i));
+ must_be_true(i == 0);
+
+ git_config_free(cfg);
+END_TEST
+
+BEGIN_TEST(config7, "test for invalid ext headers")
+ git_config *cfg;
+
+ must_fail(git_config_open_file(&cfg, CONFIG_BASE "/config7"));
+
+END_TEST
+
+BEGIN_TEST(config8, "don't fail on empty files")
+ git_config *cfg;
+
+ must_pass(git_config_open_file(&cfg, CONFIG_BASE "/config8"));
+
+ git_config_free(cfg);
+END_TEST
BEGIN_SUITE(config)
ADD_TEST(config0);
ADD_TEST(config3);
ADD_TEST(config4);
ADD_TEST(config5);
+ ADD_TEST(config6);
+ ADD_TEST(config7);
+ ADD_TEST(config8);
END_SUITE
putchar('F');
} else
putchar('.');
+
+ fflush(stdout);
}
printf("\n ");
print_details(ts);