]> git.proxmox.com Git - libgit2.git/commitdiff
Merge pull request #216 from glesserd/development
authorVicent Martí <tanoku@gmail.com>
Wed, 8 Jun 2011 15:11:26 +0000 (08:11 -0700)
committerVicent Martí <tanoku@gmail.com>
Wed, 8 Jun 2011 15:11:26 +0000 (08:11 -0700)
git_tag_create{,_o,_frombuffer} correction and improvement

50 files changed:
include/git2/blob.h
include/git2/commit.h
include/git2/config.h
include/git2/errors.h
include/git2/index.h
include/git2/object.h
include/git2/odb.h
include/git2/odb_backend.h
include/git2/oid.h
include/git2/repository.h
include/git2/tag.h
include/git2/thread-utils.h
include/git2/tree.h
src/backends/hiredis.c
src/backends/sqlite.c
src/blob.c
src/commit.c
src/config_file.c
src/errors.c
src/filebuf.c
src/fileops.c
src/fileops.h
src/index.c
src/msvc-compat.h
src/object.c
src/odb.c
src/odb_loose.c
src/odb_pack.c
src/oid.c
src/oid.h [new file with mode: 0644]
src/refs.c
src/repository.c
src/repository.h
src/sha1_lookup.c [new file with mode: 0644]
src/sha1_lookup.h [new file with mode: 0644]
src/signature.c
src/tag.c
src/tree.c
src/util.c
src/util.h
tests/resources/.gitignore [new file with mode: 0644]
tests/resources/config/config6 [new file with mode: 0644]
tests/resources/config/config7 [new file with mode: 0644]
tests/resources/config/config8 [new file with mode: 0644]
tests/t00-core.c
tests/t06-index.c
tests/t10-refs.c
tests/t12-repo.c
tests/t15-config.c
tests/test_lib.c

index 0e05d6f89b46176ff73c256bab59cee6362b54cb..e366ce8806832cf1abf1fb2c33f35f6cecf12b7d 100644 (file)
@@ -52,6 +52,23 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
        return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB);
 }
 
+/**
+ * Lookup a blob object from a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param blob pointer to the looked up blob
+ * @param repo the repo to use when locating the blob.
+ * @param id identity of the blob to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len)
+{
+       return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
+}
+
 /**
  * Close an open blob
  *
index 3687d946033ea0477545ebdc542f10867dfb7c8a..cf14cd937440f5e0f38e2e90cd49d8913022c10a 100644 (file)
@@ -53,6 +53,24 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
        return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT);
 }
 
+/**
+ * Lookup a commit object from a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param commit pointer to the looked up commit
+ * @param repo the repo to use when locating the commit.
+ * @param id identity of the commit to locate.  If the object is
+ *        an annotated tag it will be peeled back to the commit.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len)
+{
+       return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
+}
+
 /**
  * Close an open commit
  *
index 78a1f622a4fe09f5670950d045ba2816df5cf2be..4167bf8e5b20aa040c84b8cc5323190d06afd32e 100644 (file)
@@ -56,7 +56,9 @@ struct git_config_file {
  * 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
@@ -64,13 +66,21 @@ struct git_config_file {
 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
  */
@@ -86,17 +96,17 @@ GIT_EXTERN(int) git_config_open_global(git_config **cfg);
 /**
  * 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
  */
index 7e957b80342b6146426078f3bdd3cc8ab0a7ca22..3ed5cd68072c5d715624f1629e258d8cff50e29e 100644 (file)
@@ -119,6 +119,9 @@ typedef enum {
 
        /** The specified object has its data corrupted */
        GIT_EOBJCORRUPTED = -28,
+
+       /** The given short oid is ambiguous */
+       GIT_EAMBIGUOUSOIDPREFIX = -29,
 } git_error;
 
 /**
index 57401a94c30fa46f2af9a61a3e5c44c0a3e9e972..83a18e1607c2338846705e1680a010f84ac3d55a 100644 (file)
@@ -109,29 +109,24 @@ typedef struct git_index_entry_unmerged {
 } 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.
@@ -262,7 +257,7 @@ GIT_EXTERN(int) git_index_remove(git_index *index, int position);
  * @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
@@ -290,8 +285,32 @@ GIT_EXTERN(unsigned int) git_index_entrycount_unmerged(git_index *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
index 16dde8e56d8a582255c368c1c77e625c8441bc7c..83d37a263bdc6517bcae3a174d4c2e0eec3cba53 100644 (file)
@@ -56,7 +56,45 @@ GIT_BEGIN_DECL
  * @param type the type of the object
  * @return a reference to the object
  */
-GIT_EXTERN(int) git_object_lookup(git_object **object, git_repository *repo, const git_oid *id, git_otype type);
+GIT_EXTERN(int) git_object_lookup(
+               git_object **object,
+               git_repository *repo,
+               const git_oid *id,
+               git_otype type);
+
+/**
+ * Lookup a reference to one of the objects in a repostory,
+ * given a prefix of its identifier (short id).
+ *
+ * The object obtained will be so that its identifier
+ * matches the first 'len' hexadecimal characters
+ * (packets of 4 bits) of the given 'id'.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN, and
+ * long enough to identify a unique object matching
+ * the prefix; otherwise the method will fail.
+ *
+ * The generated reference is owned by the repository and
+ * should be closed with the `git_object_close` method
+ * instead of free'd manually.
+ *
+ * The 'type' parameter must match the type of the object
+ * in the odb; the method will fail otherwise.
+ * The special value 'GIT_OBJ_ANY' may be passed to let
+ * the method guess the object's type.
+ *
+ * @param object 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
@@ -77,6 +115,12 @@ GIT_EXTERN(git_otype) git_object_type(const git_object *obj);
 /**
  * Get the repository that owns this object
  *
+ * Freeing or calling `git_repository_close` on the
+ * returned pointer will invalidate the actual object.
+ *
+ * Any other operation may be run on the repository without
+ * affecting the object.
+ *
  * @param obj the object
  * @return the repository who owns this object
  */
index 1d351beeae0751ab5c36c6de323f69269d9b871f..42961463c0d05503b3d5bf1ad1056e683c750bf7 100644 (file)
@@ -109,7 +109,7 @@ GIT_EXTERN(void) git_odb_close(git_odb *db);
 /**
  * Read an object from the database.
  *
- * This method queries all avaiable ODB backends
+ * This method queries all available ODB backends
  * trying to read the given OID.
  *
  * The returned object is reference counted and
@@ -125,6 +125,37 @@ GIT_EXTERN(void) git_odb_close(git_odb *db);
  */
 GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
 
+/**
+ * Read an object from the database, given a prefix
+ * of its identifier.
+ *
+ * This method queries all available ODB backends
+ * trying to match the 'len' first hexadecimal
+ * characters of the 'short_id'.
+ * The remaining (GIT_OID_HEXSZ-len)*4 bits of
+ * 'short_id' must be 0s.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN,
+ * and the prefix must be long enough to identify
+ * a unique object in all the backends; the
+ * method will fail otherwise.
+ *
+ * The returned object is reference counted and
+ * internally cached, so it should be closed
+ * by the user once it's no longer in use.
+ *
+ * @param out_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.
index ba41f726c6e8bc5584ab48f714b8c544188e8b3b..796d2b9dae0e43b66a9d7fd1d0b950bad556a16b 100644 (file)
@@ -49,6 +49,19 @@ struct git_odb_backend {
                        struct git_odb_backend *,
                        const git_oid *);
 
+       /* To find a unique object given a prefix
+        * of its oid.
+        * The oid given must be so that the
+        * remaining (GIT_OID_HEXSZ - len)*4 bits
+        * are 0s.
+        */
+       int (* read_prefix)(
+                       git_oid *,
+                       void **, size_t *, git_otype *,
+                       struct git_odb_backend *,
+                       const git_oid *,
+                       unsigned int);
+
        int (* read_header)(
                        size_t *, git_otype *,
                        struct git_odb_backend *,
index 4538c614777274b706636652287c451400781e6e..d87d8f6ba27ebdbe102dfa4c3c63c21c776c26ca 100644 (file)
@@ -43,6 +43,10 @@ GIT_BEGIN_DECL
 /** Size (in bytes) of a hex formatted oid */
 #define GIT_OID_HEXSZ (GIT_OID_RAWSZ * 2)
 
+/** Minimum length (in number of hex characters,
+ * i.e. packets of 4 bits) of an oid prefix */
+#define GIT_OID_MINPREFIXLEN 4
+
 /** Unique identity of any object (commit, tree, blob, tag). */
 typedef struct {
        /** raw binary formatted id */
@@ -132,6 +136,16 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src);
  */
 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
  */
index c47fcfc9af284a1ed274b8b1072b5df26cfc3129..2cb6bfa7baae7ba30ac3b1872fd8707aface54c8 100644 (file)
@@ -132,6 +132,34 @@ GIT_EXTERN(int) git_repository_open3(git_repository **repository,
                const char *git_index_file,
                const char *git_work_tree);
 
+/**
+ * Look for a git repository and copy its path in the given buffer. The lookup start
+ * from base_path and walk across parent directories if nothing has been found. The
+ * lookup ends when the first repository is found, or when reaching a directory
+ * referenced in ceiling_dirs or when the filesystem changes (in case across_fs
+ * is true).
+ *
+ * The method will automatically detect if the repository is bare (if there is
+ * a repository).
+ *
+ * @param repository_path The user allocated buffer which will contain the found path.
+ *
+ * @param size repository_path size
+ *
+ * @param start_path The base path where the lookup starts.
+ *
+ * @param across_fs If true, then the lookup will not stop when a filesystem device change
+ * is detected while exploring parent directories.
+ *
+ * @param ceiling_dirs A 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
  *
@@ -141,10 +169,18 @@ GIT_EXTERN(int) git_repository_open3(git_repository **repository,
 GIT_EXTERN(git_odb *) git_repository_database(git_repository *repo);
 
 /**
- * Get the Index file of a Git repository
+ * Open the Index file of a Git repository
  *
- * This is a cheap operation; the index is only opened on the first call,
- * and subsequent calls only retrieve the previous pointer.
+ * This returns a new and unique `git_index` object representing the
+ * active index for the repository.
+ *
+ * This method may be called more than once (e.g. on different threads).
+ *
+ * Each returned `git_index` object is independent and suffers no race
+ * conditions: synchronization is done at the FS level.
+ *
+ * Each returned `git_index` object must be manually freed by the user,
+ * using `git_index_free`.
  *
  * @param index Pointer where to store the index
  * @param repo a repository object
@@ -195,22 +231,39 @@ GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path,
 GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
 
 /**
- * Get the normalized path to the git repository.
- *
- * @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
index bc05a0c1f89d1e41136fce9727de505d91ff84be..49836a1f5a19cd86068544a2cf54dbfac66b0929 100644 (file)
@@ -52,6 +52,23 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi
        return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG);
 }
 
+/**
+ * Lookup a tag object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param tag pointer to the looked up tag
+ * @param repo the repo to use when locating the tag.
+ * @param id identity of the tag to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len)
+{
+       return git_object_lookup_prefix((git_object **)tag, repo, id, len, (git_otype)GIT_OBJ_TAG);
+}
+
 /**
  * Close an open tag
  *
index e26876beaf13d64d96f53e9cb41290e9bbf1dddf..62e6199a4013c278a3c520dc12b95bc309680b69 100644 (file)
  *          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__ */
index 0caf60a48f2352cc428835448288f516c5c737e1..80b90604ff0a06501395cf57fde3d1aaa99f9edf 100644 (file)
@@ -52,6 +52,23 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git
        return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE);
 }
 
+/**
+ * Lookup a tree object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param tree pointer to the looked up tree
+ * @param repo the repo to use when locating the tree.
+ * @param id identity of the tree to locate.
+ * @param len the length of the short identifier
+ * @return 0 on success; error code otherwise
+ */
+GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len)
+{
+       return git_object_lookup_prefix((git_object **)tree, repo, id, len, GIT_OBJ_TREE);
+}
+
 /**
  * Close an open tree
  *
@@ -84,7 +101,7 @@ GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree);
  * @param tree a previously loaded tree.
  * @return the number of entries in the tree
  */
-GIT_EXTERN(size_t) git_tree_entrycount(git_tree *tree);
+GIT_EXTERN(unsigned int) git_tree_entrycount(git_tree *tree);
 
 /**
  * Lookup a tree entry by its filename
@@ -102,7 +119,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const c
  * @param idx the position in the entry list
  * @return the tree entry; NULL if not found
  */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, int idx);
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, unsigned int idx);
 
 /**
  * Get the UNIX file attributes of a tree entry
@@ -128,6 +145,14 @@ GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry);
  */
 GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry);
 
+/**
+ * Get the type of the object pointed by the entry
+ *
+ * @param entry a tree entry
+ * @return the type of the pointed object
+ */
+GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
+
 /**
  * Convert a tree entry to the git_object it points too.
  *
index f0c5da234c1d70812f2dadebe09810e7a5df2d47..111abf7d357cafa30119e49377896388c39a4418 100644 (file)
@@ -107,6 +107,21 @@ int hiredis_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_o
     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;
@@ -174,6 +189,7 @@ int git_odb_backend_hiredis(git_odb_backend **backend_out, const char *host, int
         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;
index abf14f180f611836e6e43bbd6f9d5fc22183b676..8626349ec51974784fbfd36eb8d1cd2c40434061 100644 (file)
@@ -103,6 +103,21 @@ int sqlite_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_od
        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;
@@ -255,6 +270,7 @@ int git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db)
                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;
index 75eda448e0bd646db7482717cbaa669f3ff9e037..c95d018e29aced1285e97b7adb8a56fede1ddcd2 100644 (file)
@@ -64,7 +64,10 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
        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);
@@ -77,37 +80,52 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
 
 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");
                }
@@ -118,7 +136,8 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
 
        error = stream->finalize_write(oid, stream);
        stream->free(stream);
-       gitfo_close(fd);
+       if (!islnk)
+               gitfo_close(fd);
 
        if (error < GIT_SUCCESS)
                return git__rethrow(error, "Failed to create blob");
index bfae0592ee677945bef6e0a24317aac6926599bd..6857eddabfb4f633e310f0cf79c57c72e114572f 100644 (file)
@@ -292,6 +292,7 @@ int commit_parse_buffer(git_commit *commit, const void *data, size_t len)
 
        if (buffer < buffer_end) {
                const char *line_end;
+               unsigned int i;
                size_t message_len;
 
                /* Long message */
@@ -301,12 +302,18 @@ int commit_parse_buffer(git_commit *commit, const void *data, size_t len)
                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;
        }
 
index ace7cc8ffba83c6e8788fc434c647a9536ee330c..d76c6024d60318159ae3c8ce756a07eb632dd9f3 100644 (file)
@@ -261,7 +261,6 @@ static int config_open(git_config_file *cfg)
  cleanup:
        cvar_list_free(&b->var_list);
        gitfo_free_buf(&b->reader.buffer);
-       free(cfg);
 
        return git__rethrow(error, "Failed to open config");
 }
@@ -484,15 +483,6 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
        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.
  */
@@ -503,38 +493,24 @@ static char *cfg_readline(diskfile_backend *cfg)
        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';
 
@@ -619,10 +595,15 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
         * 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++];
@@ -634,6 +615,7 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
                                error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
                                goto out;
                        }
+                       break;
                default:
                        break;
                }
@@ -820,6 +802,10 @@ static int config_parse(diskfile_backend *cfg_file)
        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) {
@@ -832,6 +818,7 @@ static int config_parse(diskfile_backend *cfg_file)
 
                case '[': /* section header, new section begins */
                        free(current_section);
+                       current_section = NULL;
                        error = parse_section_header(cfg_file, &current_section);
                        break;
 
@@ -871,8 +858,7 @@ static int config_parse(diskfile_backend *cfg_file)
                }
        }
 
-       if (current_section)
-               free(current_section);
+       free(current_section);
 
        return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
 }
index 1fb68ee65e6f73dc567361789f8c9639f89570b5..7da02b4f7d6ef108718368c47d791f3db1075561 100644 (file)
@@ -61,6 +61,7 @@ static struct {
        {GIT_EEXISTS, "A reference with this name already exists"},
        {GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
        {GIT_ENOTNUM, "The given literal is not a valid number"},
+       {GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
 };
 
 const char *git_strerror(int num)
index 63f2897cb59d61b61831d3075687d696fdebffe0..ca13e6181d8ce8f0c10a1091bf820deb9fcae6b7 100644 (file)
@@ -41,16 +41,14 @@ static int lock_file(git_filebuf *file, int flags)
 
        /* 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];
@@ -77,7 +75,7 @@ void git_filebuf_cleanup(git_filebuf *file)
        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)
@@ -365,14 +363,19 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
        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;
        }
index 292673482ca1ea95f790ab5530a5aec177440256..98fc53a50a0c9d9b30fd196737634289a47c7748 100644 (file)
@@ -66,6 +66,20 @@ int gitfo_creat_force(const char *path, int mode)
        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;
@@ -131,6 +145,25 @@ int gitfo_isdir(const char *path)
                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;
@@ -301,8 +334,24 @@ int gitfo_dirent(
        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;
 
@@ -320,7 +369,6 @@ int retrieve_path_root_offset(const char *path)
        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;
@@ -333,11 +381,11 @@ int gitfo_mkdir_recurs(const char *path, int mode)
        error = GIT_SUCCESS;
        pp = path_copy;
 
-       root_path_offset = retrieve_path_root_offset(pp);
+       root_path_offset = 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);
@@ -352,8 +400,11 @@ int gitfo_mkdir_recurs(const char *path, int 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);
 
@@ -367,7 +418,7 @@ static int retrieve_previous_path_component_start(const char *path)
 {
        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;
 
@@ -392,7 +443,7 @@ static int retrieve_previous_path_component_start(const char *path)
        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;
@@ -402,11 +453,20 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
        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;
@@ -470,9 +530,9 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path)
        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);
@@ -487,12 +547,13 @@ int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *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 */
@@ -519,16 +580,6 @@ int gitfo_cmp_path(const char *name1, int len1, int isdir1,
        return 0;
 }
 
-static void posixify_path(char *path)
-{
-       while (*path) {
-               if (*path == '\\')
-                       *path = '/';
-
-               path++;
-       }
-}
-
 int gitfo_getcwd(char *buffer_out, size_t size)
 {
        char *cwd_buffer;
@@ -544,9 +595,140 @@ int gitfo_getcwd(char *buffer_out, size_t size)
        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
index d0381b6750bbbf64a3b44075db91064dc149a61f..4e6007ad42fac8ae37d6a6c70dff78839648f6d5 100644 (file)
 #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))
 {
@@ -57,8 +68,11 @@ extern int gitfo_exists(const char *path);
 extern int gitfo_open(const char *path, int flags);
 extern int gitfo_creat(const char *path, int mode);
 extern int gitfo_creat_force(const char *path, int mode);
+extern int gitfo_creat_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)
@@ -81,6 +95,14 @@ extern int gitfo_mv_force(const char *from, const char *to);
 #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)
@@ -162,7 +184,7 @@ extern int gitfo_getcwd(char *buffer_out, size_t size);
  * - 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.
@@ -185,7 +207,10 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *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__ */
index 389780295a0fc93851c80997c7c3088511ab47cf..60b65848d749c374a1b961d25a7e154cc9b33266 100644 (file)
@@ -99,7 +99,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
 static int read_header(struct index_header *dest, const void *buffer);
 
 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);
@@ -138,6 +138,15 @@ int unmerged_cmp(const void *a, const void *b)
        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;
@@ -168,13 +177,19 @@ static int index_initialize(git_index **index_out, git_repository *owner, const
        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");
 
@@ -183,11 +198,12 @@ int git_index_open_inrepo(git_index **index_out, git_repository *repo)
 
 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);
@@ -221,7 +237,15 @@ void git_index_clear(git_index *index)
                free(e);
        }
 
+       for (i = 0; i < index->unmerged.length; ++i) {
+               git_index_entry_unmerged *e;
+               e = git_vector_get(&index->unmerged, i);
+               free((char *)e->path);
+               free(e);
+       }
+
        git_vector_clear(&index->entries);
+       git_vector_clear(&index->unmerged);
        index->last_modified = 0;
 
        free_tree(index->tree);
@@ -307,11 +331,11 @@ unsigned int git_index_entrycount_unmerged(git_index *index)
        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)
@@ -387,11 +411,8 @@ static int index_init_entry(git_index_entry *entry, git_index *index, const char
 
        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);
@@ -404,7 +425,7 @@ static int index_init_entry(git_index_entry *entry, git_index *index, const char
        /* 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;
@@ -464,7 +485,7 @@ int git_index_find(git_index *index, const char *path)
        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);
@@ -478,53 +499,88 @@ const git_index_entry_unmerged *git_index_get_unmerged(git_index *index, const c
        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;
@@ -532,33 +588,40 @@ static git_index_tree *read_tree_internal(
        /* 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)
@@ -929,3 +992,8 @@ static int write_index(git_index *index, git_filebuf *file)
 
        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;
+}
index 1ec85f91ba5d5df356c1d95fe12f8497de665d51..6f38e482dea273fa06a92210ed060d48c0ae1260 100644 (file)
 # 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
index d2e4da3590b6c961805dd495278974f2fa30d2ae..d14ca85665455f192cfd9c97c8f69bb2623bb6cd 100644 (file)
@@ -95,7 +95,7 @@ static int create_object(git_object **object_out, git_otype type)
        return GIT_SUCCESS;
 }
 
-int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
+int git_object_lookup_prefix(git_object **object_out, git_repository *repo, const git_oid *id, unsigned int len, git_otype type)
 {
        git_object *object = NULL;
        git_odb_object *odb_obj;
@@ -103,16 +103,54 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
 
        assert(repo && object_out && id);
 
-       object = git_cache_get(&repo->objects, id);
-       if (object != NULL) {
-               if (type != GIT_OBJ_ANY && type != object->type)
-                       return git__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");
 
@@ -127,7 +165,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
                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) {
@@ -162,6 +200,10 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
        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;
index c669e7c14582d78d6be8d6020818b6b4aff149f4..8953ec6581936de77772fceea5db882c58d08ac1 100644 (file)
--- a/src/odb.c
+++ b/src/odb.c
@@ -170,7 +170,7 @@ static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t
 {
        fake_wstream *stream = (fake_wstream *)_stream;
 
-       if (stream->written + len >= stream->size)
+       if (stream->written + len > stream->size)
                return GIT_ENOMEM;
 
        memcpy(stream->buffer + stream->written, data, len);
@@ -321,8 +321,7 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
 static int load_alternates(git_odb *odb, const char *objects_dir)
 {
        char alternates_path[GIT_PATH_MAX];
-       char alternate[GIT_PATH_MAX];
-       char *buffer;
+       char *buffer, *alternate;
 
        gitfo_buf alternates_buf = GITFO_BUF_INIT;
        int error;
@@ -339,8 +338,21 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
        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)
@@ -488,6 +500,59 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
        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;
index 9fb86f8bf32c26871ee610e0be9acc97189e46aa..9564ef05b406795b6a5f1624d825ab220205b633 100644 (file)
@@ -29,6 +29,7 @@
 #include "fileops.h"
 #include "hash.h"
 #include "odb.h"
+#include "oid.h"
 #include "delta-apply.h"
 #include "filebuf.h"
 
@@ -54,6 +55,20 @@ typedef struct loose_backend {
        char *objects_dir;
 } loose_backend;
 
+/* State structure for exploring directories,
+ * in order to locate objects matching a short oid.
+ */
+typedef struct {
+       size_t dir_len;
+       unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
+       unsigned int short_oid_len;
+       int found;                              /* number of matching
+                                                * objects already found */
+       unsigned char res_oid[GIT_OID_HEXSZ];   /* hex formatted oid of
+                                                * the object found */
+} loose_locate_object_state;
+
+
 
 /***********************************************************
  *
@@ -465,6 +480,83 @@ static int locate_object(char *object_location, loose_backend *backend, const gi
        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;
+}
+
 
 
 
@@ -524,6 +616,47 @@ int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_o
        return GIT_SUCCESS;
 }
 
+int loose_backend__read_prefix(
+       git_oid *out_oid,
+       void **buffer_p,
+       size_t *len_p,
+       git_otype *type_p,
+       git_odb_backend *backend,
+       const git_oid *short_oid,
+       unsigned int len)
+{
+       if (len < GIT_OID_MINPREFIXLEN)
+               return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+       if (len >= GIT_OID_HEXSZ) {
+               /* We can fall back to regular read method */
+               int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+               if (error == GIT_SUCCESS)
+                       git_oid_cpy(out_oid, short_oid);
+
+               return error;
+       } else {
+               char object_path[GIT_PATH_MAX];
+               git_rawobj raw;
+               int error;
+
+               assert(backend && short_oid);
+
+               if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) {
+                       return git__rethrow(error, "Failed to read loose backend");
+               }
+
+               if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
+                       return git__rethrow(error, "Failed to read loose backend");
+
+               *buffer_p = raw.data;
+               *len_p = raw.len;
+               *type_p = raw.type;
+       }
+
+       return GIT_SUCCESS;
+}
+
 int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
 {
        char object_path[GIT_PATH_MAX];
@@ -663,6 +796,7 @@ int git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir
        backend->fsync_object_files = 0;
 
        backend->parent.read = &loose_backend__read;
+       backend->parent.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;
index 574fbc639519a9afde44a828c8a07f474b8af963..29ceb02dc3719cc53f79d8df7e4c018827aee84e 100644 (file)
@@ -29,7 +29,9 @@
 #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"
 
@@ -262,15 +264,41 @@ static int packfile_refresh_all(struct pack_backend *backend);
 
 static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n);
 
-static int pack_entry_find_offset(off_t *offset_out,
-               struct pack_file *p, const git_oid *oid);
+/* 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,
@@ -923,12 +951,16 @@ 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,
+               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;
 
@@ -950,8 +982,8 @@ static int pack_entry_find_offset(
        }
 
        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;
@@ -962,60 +994,80 @@ static int pack_entry_find_offset(
 
 #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)
@@ -1024,7 +1076,7 @@ static int pack_entry_find1(
        e->offset = offset;
        e->p = p;
 
-       git_oid_cpy(&e->sha1, oid);
+       git_oid_cpy(&e->sha1, &found_oid);
        return GIT_SUCCESS;
 }
 
@@ -1037,7 +1089,7 @@ static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, c
                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) {
@@ -1047,7 +1099,7 @@ static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, c
                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;
                }
@@ -1056,6 +1108,56 @@ static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, c
        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;
+       }
+
+}
+
 
 
 
@@ -1190,6 +1292,7 @@ static off_t get_delta_base(
 {
        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
@@ -1214,7 +1317,7 @@ static off_t get_delta_base(
                *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
@@ -1364,6 +1467,45 @@ int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_od
        return GIT_SUCCESS;
 }
 
+int pack_backend__read_prefix(
+       git_oid *out_oid,
+       void **buffer_p,
+       size_t *len_p,
+       git_otype *type_p,
+       git_odb_backend *backend,
+       const git_oid *short_oid,
+       unsigned int len)
+{
+       if (len < GIT_OID_MINPREFIXLEN)
+               return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read pack backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+
+       if (len >= GIT_OID_HEXSZ) {
+               /* We can fall back to regular read method */
+               int error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+               if (error == GIT_SUCCESS)
+                       git_oid_cpy(out_oid, short_oid);
+
+               return error;
+       } else {
+               struct 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;
@@ -1418,6 +1560,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
        }
 
        backend->parent.read = &pack_backend__read;
+       backend->parent.read_prefix = &pack_backend__read_prefix;
        backend->parent.read_header = NULL;
        backend->parent.exists = &pack_backend__exists;
        backend->parent.free = &pack_backend__free;
index 5c5238bb17e316253b90d052dc5565062861958f..aab93cb69553faf58cfdd8b9d0bd01657b34b49b 100644 (file)
--- a/src/oid.c
+++ b/src/oid.c
@@ -173,6 +173,31 @@ int git_oid_cmp(const git_oid *a, const git_oid *b)
 }
 
 
+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 {
diff --git a/src/oid.h b/src/oid.h
new file mode 100644 (file)
index 0000000..afdfcf9
--- /dev/null
+++ b/src/oid.h
@@ -0,0 +1,17 @@
+#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
index c21c9583d28ef047ce2535b7ce1ae5b2a65dcf1e..555650a4c79bcdada0cfd1f72f0ad73c66598f19 100644 (file)
@@ -83,6 +83,7 @@ static int packed_write(git_repository *repo);
 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);
@@ -903,8 +904,11 @@ static int packed_write(git_repository *repo)
                 * this is a disaster */
                assert(ref->ref.type & GIT_REF_OID);
 
-               if ((error = packed_find_peel(ref)) < GIT_SUCCESS)
+               if ((error = packed_find_peel(ref)) < GIT_SUCCESS) {
+                       error = git__throw(GIT_EOBJCORRUPTED, "A reference cannot be peeled");
                        goto cleanup;
+               }
+
 
                if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
                        goto cleanup;
@@ -1003,6 +1007,9 @@ static int reference_create_oid(git_reference **ref_out, git_repository *repo, c
        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
@@ -1042,6 +1049,50 @@ cleanup:
        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
  *
@@ -1069,6 +1120,8 @@ static int reference_rename(git_reference *ref, const char *new_name, int force)
        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);
@@ -1079,6 +1132,8 @@ static int reference_rename(git_reference *ref, const char *new_name, int force)
            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);
@@ -1437,7 +1492,9 @@ int git_reference_delete(git_reference *ref)
                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];
index 468c88851ad0c5f1f66d322a2638ab435d05b424..c9678f1855096e0f5baf119e08da7de7fd86c742 100644 (file)
@@ -38,6 +38,9 @@
 #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 {
@@ -65,7 +68,7 @@ static int assign_repository_dirs(
        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");
 
@@ -78,7 +81,7 @@ static int assign_repository_dirs(
        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");
        }
@@ -92,7 +95,7 @@ static int assign_repository_dirs(
        if (git_work_tree == NULL)
                repo->is_bare = 1;
        else {
-               error = gitfo_prettify_dir_path(path_aux, sizeof(path_aux), git_work_tree);
+               error = 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");
 
@@ -105,7 +108,7 @@ static int assign_repository_dirs(
                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");
                }
@@ -268,6 +271,21 @@ cleanup:
        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;
@@ -279,11 +297,7 @@ int git_repository_open(git_repository **repo_out, const char *path)
        if (repo == NULL)
                return GIT_ENOMEM;
 
-       error = guess_repository_dirs(repo, path);
-       if (error < GIT_SUCCESS)
-               goto cleanup;
-
-       error = check_repository_dirs(repo);
+       error = discover_repository_dirs(repo, path);
        if (error < GIT_SUCCESS)
                goto cleanup;
 
@@ -299,46 +313,262 @@ 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(&current_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)
@@ -413,7 +643,7 @@ static int repo_init_find_dir(repo_init *results, const char* path)
        char temp_path[GIT_PATH_MAX];
        int error = GIT_SUCCESS;
 
-       error = gitfo_prettify_dir_path(temp_path, sizeof(temp_path), path);
+       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");
 
@@ -498,14 +728,30 @@ int git_repository_is_empty(git_repository *repo)
        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;
 }
index 813cac942d8e977efe647d778e6be4053aeae8db..bcf9b2bc82789c0d3e7f00a5902778b3d93a1c02 100644 (file)
@@ -25,7 +25,6 @@ struct git_object {
 
 struct git_repository {
        git_odb *db;
-       git_index *index;
 
        git_cache objects;
        git_refcache references;
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
new file mode 100644 (file)
index 0000000..6ac00c5
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * This file is basically taken from git code.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file.  (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <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;
+}
diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h
new file mode 100644 (file)
index 0000000..5caa2f5
--- /dev/null
@@ -0,0 +1,12 @@
+#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
index 683264108b80d9d8a3da6aa4f0bd68b51fe56a6c..51a2fff47965ceb8cdabaab5c29c89909cb4fb4a 100644 (file)
@@ -107,6 +107,7 @@ static int parse_timezone_offset(const char *buffer, long *offset_out)
        const char *offset_start;
        const char *offset_end;
 
+       //we are sure that *buffer == ' ' 
        offset_start = buffer + 1;
 
        if (*offset_start == '\n') {
@@ -117,17 +118,23 @@ static int parse_timezone_offset(const char *buffer, long *offset_out)
        if (offset_start[0] != '-' && offset_start[0] != '+')
                return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
 
+       if (offset_start[1] < '0' || offset_start[1] > '9')
+               return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset.");
+
        if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
                return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
 
        if (offset_end - offset_start != 5)
                return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
 
+       if (dec_offset > 1400)
+               return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large");
+
        hours = dec_offset / 100;
        mins = dec_offset % 100;
 
        if (hours > 14) // see http://www.worldtimezone.com/faq.html 
-               return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");;
+               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");
@@ -168,23 +175,26 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
        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;
@@ -194,11 +204,15 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
 
        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");
 
index b00c9252020072bdab561f7c76dc9cf20219552e..10ed9c0c2b41a16c8f466c78fbac6c862defbfe0 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -150,6 +150,9 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
                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);
index 60413e276a36cca2bd78b24cbfbd31240a530fbc..fabcfd80c042168b1e9fb48072cb0f2754824852 100644 (file)
@@ -100,6 +100,18 @@ const git_oid *git_tree_entry_id(const git_tree_entry *entry)
        return &entry->oid;
 }
 
+git_otype git_tree_entry_type(const git_tree_entry *entry)
+{
+       assert(entry);
+
+       if (S_ISGITLINK(entry->attr))
+               return GIT_OBJ_COMMIT;
+       else if (S_ISDIR(entry->attr))
+               return GIT_OBJ_TREE;
+       else
+               return GIT_OBJ_BLOB;
+}
+
 int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry)
 {
        assert(entry && object_out);
@@ -119,13 +131,13 @@ const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename
        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;
@@ -288,7 +300,7 @@ static void sort_entries(git_treebuilder *bld)
 int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
 {
        git_treebuilder *bld;
-       size_t i, source_entries = DEFAULT_TREE_SIZE;
+       unsigned int i, source_entries = DEFAULT_TREE_SIZE;
 
        assert(builder_p);
 
@@ -409,7 +421,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
 
 int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
 {
-       size_t i, size = 0;
+       unsigned int i, size = 0;
        char filemode[MAX_FILEMODE_BYTES + 1 + 1];
        git_odb_stream *stream;
        int error;
@@ -443,7 +455,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
                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);
@@ -453,7 +465,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
 
 void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
 {
-       size_t i;
+       unsigned int i;
 
        assert(bld && filter);
 
@@ -466,7 +478,7 @@ void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_e
 
 void git_treebuilder_clear(git_treebuilder *bld)
 {
-       size_t i;
+       unsigned int i;
        assert(bld);
 
        for (i = 0; i < bld->entries.length; ++i) {
index ebc1f43d5bfa2e7d2998f60a02d7ee5b19bd94e7..560c40dbb0c5093f929701546f44cd1b94a1319a 100644 (file)
@@ -337,27 +337,29 @@ void git__joinpath_n(char *buffer_out, int count, ...)
        *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)
index e5a2ebe5f87b7b839893f12d3508fd350f921741..72e3a9c683068c8c19deb14123694d10979215b2 100644 (file)
@@ -139,8 +139,7 @@ GIT_INLINE(int) git__is_sizet(git_off_t p)
 #      define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))
 #endif
 
-extern char *git__strtok(char *output, char *src, char *delimit);
-extern char *git__strtok_keep(char *output, char *src, char *delimit);
+extern char *git__strtok(char **end, const char *sep);
 
 extern void git__strntolower(char *str, int len);
 extern void git__strtolower(char *str);
diff --git a/tests/resources/.gitignore b/tests/resources/.gitignore
new file mode 100644 (file)
index 0000000..43a19cc
--- /dev/null
@@ -0,0 +1 @@
+discover.git
diff --git a/tests/resources/config/config6 b/tests/resources/config/config6
new file mode 100644 (file)
index 0000000..0f8f90a
--- /dev/null
@@ -0,0 +1,5 @@
+[valid "subsection"]
+    something = true
+
+[something "else"]
+    something = false
diff --git a/tests/resources/config/config7 b/tests/resources/config/config7
new file mode 100644 (file)
index 0000000..6af6fcf
--- /dev/null
@@ -0,0 +1,5 @@
+[valid "subsection"]
+    something = a
+; we don't allow anything after closing "
+[sec "subsec"x]
+    bleh = blah
diff --git a/tests/resources/config/config8 b/tests/resources/config/config8
new file mode 100644 (file)
index 0000000..e69de29
index ab9a683a7988bcf1e77b89918d8ff9327acd6baf..5cd4025d32b597142b50f02c7ae3689ba1a91d8f 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "vector.h"
 #include "fileops.h"
+#include "filebuf.h"
 
 BEGIN_TEST(string0, "compare prefixes")
        must_be_true(git__prefixcmp("", "") == 0);
@@ -151,7 +152,7 @@ BEGIN_TEST(path2, "get the latest component in a path")
 #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
@@ -168,7 +169,7 @@ static int ensure_normalized(const char *input_path, const char *expected_path,
        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;
 
@@ -417,7 +418,7 @@ BEGIN_TEST(path7, "prevent a path which escapes the root directory from being pr
        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 {
@@ -661,6 +662,15 @@ BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traver
        must_pass(knockdown(&odd));
 END_TEST
 
+BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock")
+       git_filebuf file;
+       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);
@@ -683,4 +693,6 @@ BEGIN_SUITE(core)
        ADD_TEST(dirent2);
        ADD_TEST(dirent3);
        ADD_TEST(dirent4);
+
+       ADD_TEST(filebuf0);
 END_SUITE
index 93ca2c04e8e6eadbe175c9384ee7a95c49737473..25adbf7c38ab43e7f2026114dbaf867bac07ea01 100644 (file)
@@ -48,7 +48,7 @@ struct test_entry TEST_ENTRIES[] = {
 BEGIN_TEST(read0, "load an empty index")
        git_index *index;
 
-       must_pass(git_index_open_bare(&index, "in-memory-index"));
+       must_pass(git_index_open(&index, "in-memory-index"));
        must_be_true(index->on_disk == 0);
 
        must_pass(git_index_read(index));
@@ -65,7 +65,7 @@ BEGIN_TEST(read1, "load a standard index (default test index)")
        unsigned int i;
        git_index_entry **entries;
 
-       must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
+       must_pass(git_index_open(&index, TEST_INDEX_PATH));
        must_be_true(index->on_disk);
 
        must_pass(git_index_read(index));
@@ -90,7 +90,7 @@ END_TEST
 BEGIN_TEST(read2, "load a standard index (git.git index)")
        git_index *index;
 
-       must_pass(git_index_open_bare(&index, TEST_INDEX2_PATH));
+       must_pass(git_index_open(&index, TEST_INDEX2_PATH));
        must_be_true(index->on_disk);
 
        must_pass(git_index_read(index));
@@ -107,7 +107,7 @@ BEGIN_TEST(find0, "find an entry on an index")
        git_index *index;
        unsigned int i;
 
-       must_pass(git_index_open_bare(&index, TEST_INDEX_PATH));
+       must_pass(git_index_open(&index, TEST_INDEX_PATH));
        must_pass(git_index_read(index));
 
        for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
@@ -122,7 +122,7 @@ BEGIN_TEST(find1, "find an entry in an empty index")
        git_index *index;
        unsigned int i;
 
-       must_pass(git_index_open_bare(&index, "fake-index"));
+       must_pass(git_index_open(&index, "fake-index"));
 
        for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
                int idx = git_index_find(index, TEST_ENTRIES[i].path);
@@ -137,7 +137,7 @@ BEGIN_TEST(write0, "write an index back to disk")
 
        must_pass(copy_file(TEST_INDEXBIG_PATH, "index_rewrite"));
 
-       must_pass(git_index_open_bare(&index, "index_rewrite"));
+       must_pass(git_index_open(&index, "index_rewrite"));
        must_pass(git_index_read(index));
        must_be_true(index->on_disk);
 
@@ -165,7 +165,7 @@ END_TEST
 BEGIN_TEST(sort1, "sort the entires in an empty index")
        git_index *index;
 
-       must_pass(git_index_open_bare(&index, "fake-index"));
+       must_pass(git_index_open(&index, "fake-index"));
 
        /* FIXME: this test is slightly dumb */
        must_be_true(index->entries.sorted);
index ee006a8ce4451fe756da8692918d2878ce9a6754..3c9df434c2b59418ee7d31a67b0a219f51e2bc07 100644 (file)
@@ -644,7 +644,7 @@ BEGIN_TEST(rename5, "can force-rename a reference with the name of an existing r
        /* 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 */
@@ -654,6 +654,38 @@ BEGIN_TEST(rename5, "can force-rename a reference with the name of an existing r
        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;
@@ -935,6 +967,7 @@ BEGIN_SUITE(refs)
        ADD_TEST(rename3);
        ADD_TEST(rename4);
        ADD_TEST(rename5);
+       ADD_TEST(rename6);
 
        ADD_TEST(delete0);
        ADD_TEST(list0);
index 70dba425533242295e5a7b2cff83ccd9cbe34f1a..d59095ff409efd8c970ee8bc1b06cd917c2323f2 100644 (file)
@@ -126,7 +126,14 @@ static int ensure_repository_init(
        if (repo->path_index != NULL || expected_path_index != NULL) {
                if (git__suffixcmp(repo->path_index, expected_path_index) != 0)
                        goto cleanup;
-       }
+
+               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);
@@ -194,8 +201,8 @@ BEGIN_TEST(open0, "Open a bare repository that has just been initialized by git"
        must_pass(remove_placeholders(TEMP_REPO_FOLDER, "dummy-marker.txt"));
 
        must_pass(git_repository_open(&repo, TEMP_REPO_FOLDER));
-       must_be_true(git_repository_path(repo) != NULL);
-       must_be_true(git_repository_workdir(repo) == NULL);
+       must_be_true(git_repository_path(repo, GIT_REPO_PATH) != NULL);
+       must_be_true(git_repository_path(repo, GIT_REPO_PATH_WORKDIR) == NULL);
 
        git_repository_free(repo);
        must_pass(rmdir_recurs(TEMP_REPO_FOLDER));
@@ -213,8 +220,8 @@ BEGIN_TEST(open1, "Open a standard repository that has just been initialized by
        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));
@@ -261,6 +268,142 @@ BEGIN_TEST(empty0, "test if a repository is empty or not")
        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);
@@ -271,5 +414,6 @@ BEGIN_SUITE(repository)
        ADD_TEST(open1);
        ADD_TEST(open2);
        ADD_TEST(empty0);
+       ADD_TEST(discover0);
 END_SUITE
 
index c2e146cf1af4f97dfcdf326dd52d88fc4dcc392b..08a2cdbf22852e1454335d2a735d0d67b27bd063 100644 (file)
@@ -159,6 +159,35 @@ BEGIN_TEST(config5, "test number suffixes")
        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);
@@ -167,4 +196,7 @@ BEGIN_SUITE(config)
         ADD_TEST(config3);
         ADD_TEST(config4);
         ADD_TEST(config5);
+        ADD_TEST(config6);
+        ADD_TEST(config7);
+        ADD_TEST(config8);
 END_SUITE
index 026428f2188635e6902f389fc567e6bff40ff8dc..aaacdff652939c9129b207db7914b20384f1186e 100755 (executable)
@@ -189,6 +189,8 @@ int git_testsuite_run(git_testsuite *ts)
                        putchar('F');
                } else
                        putchar('.');
+
+               fflush(stdout);
        }
        printf("\n  ");
        print_details(ts);