summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--include/git2/blob.h12
-rw-r--r--include/git2/common.h2
-rw-r--r--include/git2/errors.h1
-rw-r--r--include/git2/notes.h21
-rw-r--r--include/git2/oid.h6
-rw-r--r--include/git2/refspec.h8
-rw-r--r--include/git2/status.h33
-rw-r--r--include/git2/tree.h4
-rw-r--r--src/attr.c118
-rw-r--r--src/attr_file.c2
-rw-r--r--src/attr_file.h10
-rw-r--r--src/blob.c56
-rw-r--r--src/branch.c2
-rw-r--r--src/buffer.c30
-rw-r--r--src/buffer.h3
-rw-r--r--src/compat/fnmatch.c (renamed from src/win32/fnmatch.c)0
-rw-r--r--src/compat/fnmatch.h (renamed from src/win32/fnmatch.h)4
-rw-r--r--src/diff.c47
-rw-r--r--src/fileops.c5
-rw-r--r--src/index.c9
-rw-r--r--src/index.h2
-rw-r--r--src/indexer.c21
-rw-r--r--src/iterator.c303
-rw-r--r--src/iterator.h57
-rw-r--r--src/notes.c155
-rw-r--r--src/odb.c25
-rw-r--r--src/path.c33
-rw-r--r--src/status.c253
-rw-r--r--src/tree.c27
-rw-r--r--src/tree.h10
-rw-r--r--src/unix/posix.h10
-rw-r--r--src/util.h5
-rw-r--r--src/vector.c23
-rw-r--r--src/vector.h17
-rw-r--r--src/win32/posix.h2
-rwxr-xr-x[-rw-r--r--]tests-clar/clar0
-rw-r--r--tests-clar/core/buffer.c50
-rw-r--r--tests-clar/core/dirent.c11
-rw-r--r--tests-clar/diff/iterator.c222
-rw-r--r--tests-clar/notes/notes.c111
-rw-r--r--tests-clar/object/blob/write.c69
-rw-r--r--tests-clar/odb/mixed.c25
-rw-r--r--tests-clar/refs/branches/delete.c15
-rw-r--r--tests-clar/refs/branches/move.c10
-rw-r--r--tests-clar/resources/duplicate.git/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/duplicate.git/HEAD1
-rw-r--r--tests-clar/resources/duplicate.git/config5
-rw-r--r--tests-clar/resources/duplicate.git/description1
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/applypatch-msg.sample15
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/commit-msg.sample24
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/post-update.sample8
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-applypatch.sample14
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-commit.sample50
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-rebase.sample169
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample36
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/update.sample128
-rw-r--r--tests-clar/resources/duplicate.git/indexbin0 -> 104 bytes
-rw-r--r--tests-clar/resources/duplicate.git/info/exclude6
-rw-r--r--tests-clar/resources/duplicate.git/info/refs1
-rw-r--r--tests-clar/resources/duplicate.git/logs/HEAD1
-rw-r--r--tests-clar/resources/duplicate.git/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464abin0 -> 21 bytes
-rw-r--r--tests-clar/resources/duplicate.git/objects/info/packs2
-rw-r--r--tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idxbin0 -> 1156 bytes
-rw-r--r--tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.packbin0 -> 213 bytes
-rw-r--r--tests-clar/resources/duplicate.git/packed-refs2
-rw-r--r--tests-clar/revwalk/mergebase.c111
-rw-r--r--tests-clar/status/ignore.c69
-rw-r--r--tests-clar/status/status_data.h10
-rw-r--r--tests-clar/status/status_helpers.c49
-rw-r--r--tests-clar/status/status_helpers.h33
-rw-r--r--tests-clar/status/submodules.c23
-rw-r--r--tests-clar/status/worktree.c79
74 files changed, 2119 insertions, 555 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fbc222d5c..d30d09df9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,7 +69,7 @@ IF (MSVC)
# Precompiled headers
ELSE ()
- SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
+ SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -fvisibility=hidden -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
IF (NOT MINGW) # MinGW always does PIC and complains if we tell it to
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
@@ -97,7 +97,9 @@ FILE(GLOB SRC_H include/git2/*.h)
# On Windows use specific platform sources
IF (WIN32 AND NOT CYGWIN)
ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501)
- FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/win32/*.c)
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/win32/*.c src/compat/*.c)
+ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c src/compat/*.c)
ELSE()
FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c)
ENDIF ()
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 44b29d7eb..afa350bb1 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -103,6 +103,18 @@ GIT_EXTERN(size_t) git_blob_rawsize(git_blob *blob);
*/
GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path);
+/**
+ * Read a file from the filesystem and write its content
+ * to the Object Database as a loose blob
+ *
+ * @param oid return the id of the written blob
+ * @param repo repository where the blob will be written.
+ * this repository can be bare or not
+ * @param path file from which the blob will be created
+ * @return GIT_SUCCESS or an error code
+ */
+GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path);
+
/**
* Write an in-memory buffer to the ODB as a blob
diff --git a/include/git2/common.h b/include/git2/common.h
index a8f8d8e1e..0e9379804 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -77,7 +77,7 @@ GIT_BEGIN_DECL
#endif
/**
- * The maximum length of a git valid git path.
+ * The maximum length of a valid git path.
*/
#define GIT_PATH_MAX 4096
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 0406c165a..856343857 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -62,6 +62,7 @@ typedef enum {
GITERR_NET,
GITERR_TAG,
GITERR_TREE,
+ GITERR_INDEXER,
} git_error_class;
/**
diff --git a/include/git2/notes.h b/include/git2/notes.h
index ecb37f3ab..7b2ac1fa0 100644
--- a/include/git2/notes.h
+++ b/include/git2/notes.h
@@ -102,6 +102,27 @@ GIT_EXTERN(void) git_note_free(git_note *note);
*/
GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
+/**
+ * Loop over all the notes within a specified namespace
+ * and issue a callback for each one.
+ *
+ * @param repo Repository where to find the notes.
+ *
+ * @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits".
+ *
+ * @param note_cb Callback to invoke per found annotation.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return GIT_SUCCESS or an error code.
+ */
+GIT_EXTERN(int) git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ int (*note_cb)(const git_oid *note_oid, const git_oid *annotated_object_oid, void *payload),
+ void *payload
+);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 566d13ac1..87efbab2f 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -183,7 +183,7 @@ typedef struct git_oid_shorten git_oid_shorten;
* be unique.
* @return a `git_oid_shorten` instance, NULL if OOM
*/
-git_oid_shorten *git_oid_shorten_new(size_t min_length);
+GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
/**
* Add a new OID to set of shortened OIDs and calculate
@@ -209,14 +209,14 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length);
* added so far to the set; or an error code (<0) if an
* error occurs.
*/
-int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
+GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
/**
* Free an OID shortener instance
*
* @param os a `git_oid_shorten` instance
*/
-void git_oid_shorten_free(git_oid_shorten *os);
+GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os);
/** @} */
GIT_END_DECL
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
index 28afe652d..50aa596f9 100644
--- a/include/git2/refspec.h
+++ b/include/git2/refspec.h
@@ -25,7 +25,7 @@ GIT_BEGIN_DECL
* @param refspec the refspec
* @return the refspec's source specifier
*/
-const char *git_refspec_src(const git_refspec *refspec);
+GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
/**
* Get the destination specifier
@@ -33,7 +33,7 @@ const char *git_refspec_src(const git_refspec *refspec);
* @param refspec the refspec
* @return the refspec's destination specifier
*/
-const char *git_refspec_dst(const git_refspec *refspec);
+GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
/**
* Check if a refspec's source descriptor matches a reference
@@ -42,7 +42,7 @@ const char *git_refspec_dst(const git_refspec *refspec);
* @param refname the name of the reference to check
* @return 1 if the refspec matches, 0 otherwise
*/
-int git_refspec_src_matches(const git_refspec *refspec, const char *refname);
+GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
/**
* Transform a reference to its target following the refspec's rules
@@ -53,7 +53,7 @@ int git_refspec_src_matches(const git_refspec *refspec, const char *refname);
* @param name the name of the reference to transform
* @return GIT_SUCCESS, GIT_ESHORTBUFFER or another error
*/
-int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
+GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
GIT_END_DECL
diff --git a/include/git2/status.h b/include/git2/status.h
index f5fc95f0a..0130b4011 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -36,15 +36,18 @@ GIT_BEGIN_DECL
/**
* Gather file statuses and run a callback for each one.
*
- * The callback is passed the path of the file, the status and the data pointer
- * passed to this function. If the callback returns something other than
- * GIT_SUCCESS, this function will return that value.
+ * The callback is passed the path of the file, the status and the data
+ * pointer passed to this function. If the callback returns something other
+ * than 0, this function will return that value.
*
* @param repo a repository object
* @param callback the function to call on each file
- * @return GIT_SUCCESS or the return value of the callback which did not return GIT_SUCCESS
+ * @return 0 on success or the return value of the callback that was non-zero
*/
-GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload);
+GIT_EXTERN(int) git_status_foreach(
+ git_repository *repo,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload);
/**
* Select the files on which to report status.
@@ -115,7 +118,7 @@ typedef struct {
*/
GIT_EXTERN(int) git_status_foreach_ext(
git_repository *repo,
- git_status_options *opts,
+ const git_status_options *opts,
int (*callback)(const char *, unsigned int, void *),
void *payload);
@@ -129,7 +132,10 @@ GIT_EXTERN(int) git_status_foreach_ext(
* the file doesn't exist in any of HEAD, the index or the worktree,
* GIT_SUCCESS otherwise
*/
-GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo, const char *path);
+GIT_EXTERN(int) git_status_file(
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path);
/**
* Test if the ignore rules apply to a given file.
@@ -139,13 +145,16 @@ GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo
* would be ignored regardless of whether the file is already in the index
* or in the repository.
*
- * @param repo a repository object
- * @param path the file to check ignores for, rooted at the repo's workdir
* @param ignored boolean returning 0 if the file is not ignored, 1 if it is
- * @return GIT_SUCCESS if the ignore rules could be processed for the file
- * (regardless of whether it exists or not), or an error < 0 if they could not.
+ * @param repo a repository object
+ * @param path the file to check ignores for, rooted at the repo's workdir.
+ * @return 0 if ignore rules could be processed for the file (regardless
+ * of whether it exists or not), or an error < 0 if they could not.
*/
-GIT_EXTERN(int) git_status_should_ignore(git_repository *repo, const char *path, int *ignored);
+GIT_EXTERN(int) git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path);
/** @} */
GIT_END_DECL
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 972c3795c..f75cc1cbb 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -343,9 +343,9 @@ typedef int (*git_tree_diff_cb)(const git_tree_diff_data *ptr, void *data);
* @param data data to give to the callback
* @return GIT_SUCCESS or an error code
*/
-int git_tree_diff(git_tree *old, git_tree *newer, git_tree_diff_cb cb, void *data);
+GIT_EXTERN(int) git_tree_diff(git_tree *old, git_tree *newer, git_tree_diff_cb cb, void *data);
-int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data);
+GIT_EXTERN(int) git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data);
/** @} */
diff --git a/src/attr.c b/src/attr.c
index 616cec6ff..1aa965de3 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -235,31 +235,91 @@ bool git_attr_cache__is_cached(
return rval;
}
-static int load_attr_file(const char *filename, const char **data)
+static int load_attr_file(
+ const char **data,
+ git_attr_file_stat_sig *sig,
+ const char *filename)
{
int error;
git_buf content = GIT_BUF_INIT;
+ struct stat st;
- error = git_futils_readbuffer(&content, filename);
- *data = error ? NULL : git_buf_detach(&content);
+ if (p_stat(filename, &st) < 0)
+ return GIT_ENOTFOUND;
- return error;
+ if (sig != NULL &&
+ (git_time_t)st.st_mtime == sig->seconds &&
+ (git_off_t)st.st_size == sig->size &&
+ (unsigned int)st.st_ino == sig->ino)
+ return GIT_ENOTFOUND;
+
+ error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
+ if (error < 0)
+ return error;
+
+ if (sig != NULL) {
+ sig->seconds = (git_time_t)st.st_mtime;
+ sig->size = (git_off_t)st.st_size;
+ sig->ino = (unsigned int)st.st_ino;
+ }
+
+ *data = git_buf_detach(&content);
+
+ return 0;
}
static int load_attr_blob_from_index(
- git_repository *repo, const char *filename, git_blob **blob)
+ const char **content,
+ git_blob **blob,
+ git_repository *repo,
+ const git_oid *old_oid,
+ const char *relfile)
{
int error;
git_index *index;
git_index_entry *entry;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
- (error = git_index_find(index, filename)) < 0)
+ (error = git_index_find(index, relfile)) < 0)
return error;
entry = git_index_get(index, error);
- return git_blob_lookup(blob, repo, &entry->oid);
+ if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
+ return error;
+
+ *content = git_blob_rawcontent(*blob);
+ return 0;
+}
+
+static int load_attr_from_cache(
+ git_attr_file **file,
+ git_attr_cache *cache,
+ git_attr_file_source source,
+ const char *relative_path)
+{
+ git_buf cache_key = GIT_BUF_INIT;
+ khiter_t cache_pos;
+
+ *file = NULL;
+
+ if (!cache || !cache->files)
+ return 0;
+
+ if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)
+ return -1;
+
+ cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+
+ git_buf_free(&cache_key);
+
+ if (git_strmap_valid_index(cache->files, cache_pos))
+ *file = git_strmap_value_at(cache->files, cache_pos);
+
+ return 0;
}
int git_attr_cache__internal_file(
@@ -301,6 +361,7 @@ int git_attr_cache__push_file(
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
git_blob *blob = NULL;
+ git_attr_file_stat_sig st;
assert(filename && stack);
@@ -316,30 +377,23 @@ int git_attr_cache__push_file(
relfile += strlen(workdir);
/* check cache */
- if (cache && cache->files) {
- git_buf cache_key = GIT_BUF_INIT;
- khiter_t cache_pos;
-
- if (git_buf_printf(&cache_key, "%d#%s", (int)source, relfile) < 0)
- return -1;
+ if (load_attr_from_cache(&file, cache, source, relfile) < 0)
+ return -1;
- cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+ /* if not in cache, load data, parse, and cache */
- git_buf_free(&cache_key);
+ if (source == GIT_ATTR_FILE_FROM_FILE) {
+ if (file)
+ memcpy(&st, &file->cache_data.st, sizeof(st));
+ else
+ memset(&st, 0, sizeof(st));
- if (git_strmap_valid_index(cache->files, cache_pos)) {
- file = git_strmap_value_at(cache->files, cache_pos);
- goto finish;
- }
+ error = load_attr_file(&content, &st, filename);
+ } else {
+ error = load_attr_blob_from_index(&content, &blob,
+ repo, file ? &file->cache_data.oid : NULL, relfile);
}
- /* if not in cache, load data, parse, and cache */
-
- if (source == GIT_ATTR_FILE_FROM_FILE)
- error = load_attr_file(filename, &content);
- else
- error = load_attr_blob_from_index(repo, relfile, &blob);
-
if (error) {
/* not finding a file is not an error for this function */
if (error == GIT_ENOTFOUND) {
@@ -349,10 +403,8 @@ int git_attr_cache__push_file(
goto finish;
}
- if (blob)
- content = git_blob_rawcontent(blob);
-
- if ((error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
+ if (!file &&
+ (error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
goto finish;
if (parse && (error = parse(repo, content, file)) < 0)
@@ -362,6 +414,12 @@ int git_attr_cache__push_file(
if (error > 0)
error = 0;
+ /* remember "cache buster" file signature */
+ if (blob)
+ git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
+ else
+ memcpy(&file->cache_data.st, &st, sizeof(st));
+
finish:
/* push file onto vector if we found one*/
if (!error && file != NULL)
diff --git a/src/attr_file.c b/src/attr_file.c
index 49ff7319f..5030ad5de 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -378,7 +378,7 @@ int git_attr_fnmatch__parse(
pattern++;
}
/* remember if we see an unescaped wildcard in pattern */
- else if ((*scan == '*' || *scan == '.' || *scan == '[') &&
+ else if (git__iswildcard(*scan) &&
(scan == pattern || (*(scan - 1) != '\\')))
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index ec488c4dc..3718f4bda 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -48,10 +48,20 @@ typedef struct {
} git_attr_assignment;
typedef struct {
+ git_time_t seconds;
+ git_off_t size;
+ unsigned int ino;
+} git_attr_file_stat_sig;
+
+typedef struct {
char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool *pool;
bool pool_is_allocated;
+ union {
+ git_oid oid;
+ git_attr_file_stat_sig st;
+ } cache_data;
} git_attr_file;
typedef struct {
diff --git a/src/blob.c b/src/blob.c
index 36571c70a..e25944b91 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -148,30 +148,20 @@ static int write_symlink(
return error;
}
-int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
{
int error;
- git_buf full_path = GIT_BUF_INIT;
- git_off_t size;
struct stat st;
- const char *workdir;
git_odb *odb = NULL;
+ git_off_t size;
- workdir = git_repository_workdir(repo);
- assert(workdir); /* error to call this on bare repo */
-
- if ((error = git_buf_joinpath(&full_path, workdir, path)) < 0 ||
- (error = git_path_lstat(full_path.ptr, &st)) < 0 ||
- (error = git_repository_odb__weakptr(&odb, repo)) < 0)
- {
- git_buf_free(&full_path);
+ if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
return error;
- }
size = st.st_size;
if (S_ISLNK(st.st_mode)) {
- error = write_symlink(oid, odb, full_path.ptr, (size_t)size);
+ error = write_symlink(oid, odb, path, (size_t)size);
} else {
git_vector write_filters = GIT_VECTOR_INIT;
int filter_count;
@@ -186,10 +176,10 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
} else if (filter_count == 0) {
/* No filters need to be applied to the document: we can stream
* directly from disk */
- error = write_file_stream(oid, odb, full_path.ptr, size);
+ error = write_file_stream(oid, odb, path, size);
} else {
/* We need to apply one or more filters */
- error = write_file_filtered(oid, odb, full_path.ptr, &write_filters);
+ error = write_file_filtered(oid, odb, path, &write_filters);
}
git_filters_free(&write_filters);
@@ -209,7 +199,41 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
*/
}
+ return error;
+}
+
+int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+{
+ git_buf full_path = GIT_BUF_INIT;
+ const char *workdir;
+ int error;
+
+ workdir = git_repository_workdir(repo);
+ assert(workdir); /* error to call this on bare repo */
+
+ if (git_buf_joinpath(&full_path, workdir, path) < 0) {
+ git_buf_free(&full_path);
+ return -1;
+ }
+
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+
git_buf_free(&full_path);
return error;
}
+int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+{
+ int error;
+ git_buf full_path = GIT_BUF_INIT;
+
+ if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
+ git_buf_free(&full_path);
+ return error;
+ }
+
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+
+ git_buf_free(&full_path);
+ return error;
+}
diff --git a/src/branch.c b/src/branch.c
index c980cf08c..6d5880cb2 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -114,7 +114,7 @@ int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_
assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
- goto on_error;
+ return error;
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
diff --git a/src/buffer.c b/src/buffer.c
index 2ecb088f8..ef95839f6 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -415,3 +415,33 @@ int git_buf_cmp(const git_buf *a, const git_buf *b)
return (result != 0) ? result :
(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
}
+
+int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
+{
+ size_t i;
+ const char *str, *pfx;
+
+ git_buf_clear(buf);
+
+ if (!strings || !strings->count)
+ return 0;
+
+ /* initialize common prefix to first string */
+ if (git_buf_sets(buf, strings->strings[0]) < 0)
+ return -1;
+
+ /* go through the rest of the strings, truncating to shared prefix */
+ for (i = 1; i < strings->count; ++i) {
+
+ for (str = strings->strings[i], pfx = buf->ptr;
+ *str && *str == *pfx; str++, pfx++)
+ /* scanning */;
+
+ git_buf_truncate(buf, pfx - buf->ptr);
+
+ if (!buf->size)
+ break;
+ }
+
+ return 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
index 1f0ec1c15..af760f901 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -122,4 +122,7 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
+/* Fill buf with the common prefix of a array of strings */
+int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
+
#endif
diff --git a/src/win32/fnmatch.c b/src/compat/fnmatch.c
index 835d811bc..835d811bc 100644
--- a/src/win32/fnmatch.c
+++ b/src/compat/fnmatch.c
diff --git a/src/win32/fnmatch.h b/src/compat/fnmatch.h
index eb7c5f6f7..7faef09b3 100644
--- a/src/win32/fnmatch.h
+++ b/src/compat/fnmatch.h
@@ -4,8 +4,8 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_fnmatch__w32_h__
-#define INCLUDE_fnmatch__w32_h__
+#ifndef INCLUDE_fnmatch__compat_h__
+#define INCLUDE_fnmatch__compat_h__
#include "common.h"
diff --git a/src/diff.c b/src/diff.c
index fed22f403..c8670b53e 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -11,6 +11,25 @@
#include "config.h"
#include "attr_file.h"
+static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
+{
+ git_buf prefix = GIT_BUF_INIT;
+ const char *scan;
+
+ if (git_buf_common_prefix(&prefix, pathspec) < 0)
+ return NULL;
+
+ /* diff prefix will only be leading non-wildcards */
+ for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
+ git_buf_truncate(&prefix, scan - prefix.ptr);
+
+ if (prefix.size > 0)
+ return git_buf_detach(&prefix);
+
+ git_buf_free(&prefix);
+ return NULL;
+}
+
static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
{
const char *str;
@@ -613,13 +632,16 @@ int git_diff_tree_to_tree(
git_diff_list **diff)
{
git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && old_tree && new_tree && diff);
- if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
- git_iterator_for_tree(repo, new_tree, &b) < 0)
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
return -1;
+ git__free(prefix);
+
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -630,13 +652,16 @@ int git_diff_index_to_tree(
git_diff_list **diff)
{
git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && diff);
- if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
- git_iterator_for_index(repo, &b) < 0)
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
return -1;
+ git__free(prefix);
+
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -646,13 +671,16 @@ int git_diff_workdir_to_index(
git_diff_list **diff)
{
git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && diff);
- if (git_iterator_for_index(repo, &a) < 0 ||
- git_iterator_for_workdir(repo, &b) < 0)
+ if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
return -1;
+ git__free(prefix);
+
return diff_from_iterators(repo, opts, a, b, diff);
}
@@ -664,13 +692,16 @@ int git_diff_workdir_to_tree(
git_diff_list **diff)
{
git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
assert(repo && old_tree && diff);
- if (git_iterator_for_tree(repo, old_tree, &a) < 0 ||
- git_iterator_for_workdir(repo, &b) < 0)
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
return -1;
+ git__free(prefix);
+
return diff_from_iterators(repo, opts, a, b, diff);
}
diff --git a/src/fileops.c b/src/fileops.c
index bf95f769c..ee9d4212d 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -185,9 +185,6 @@ int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime,
p_close(fd);
- if (mtime != NULL)
- *mtime = st.st_mtime;
-
if (updated != NULL)
*updated = 1;
@@ -309,7 +306,7 @@ static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
return -1;
if (p_rmdir(path->ptr) < 0) {
- if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && errno == ENOTEMPTY)
+ if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
return 0;
giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
diff --git a/src/index.c b/src/index.c
index 216ede777..8a6ce0fd8 100644
--- a/src/index.c
+++ b/src/index.c
@@ -502,6 +502,15 @@ int git_index_find(git_index *index, const char *path)
return git_vector_bsearch2(&index->entries, index_srch, path);
}
+unsigned int git_index__prefix_position(git_index *index, const char *path)
+{
+ unsigned int pos;
+
+ git_vector_bsearch3(&pos, &index->entries, index_srch, path);
+
+ return pos;
+}
+
void git_index_uniq(git_index *index)
{
git_vector_uniq(&index->entries);
diff --git a/src/index.h b/src/index.h
index e745c8f69..8515f4fcb 100644
--- a/src/index.h
+++ b/src/index.h
@@ -33,4 +33,6 @@ struct git_index {
extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
+extern unsigned int git_index__prefix_position(git_index *index, const char *path);
+
#endif
diff --git a/src/indexer.c b/src/indexer.c
index 0baa194d6..01bec0877 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -110,12 +110,12 @@ static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
}
if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
- giterr_set(GITERR_INVALID, "Wrong pack signature");
+ giterr_set(GITERR_INDEXER, "Wrong pack signature");
return -1;
}
if (!pack_version_ok(hdr->hdr_version)) {
- giterr_set(GITERR_INVALID, "Wrong pack version");
+ giterr_set(GITERR_INDEXER, "Wrong pack version");
return -1;
}
@@ -248,7 +248,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
/* FIXME: Parse the object instead of hashing it */
if (git_odb__hashobj(&oid, obj) < 0) {
- giterr_set(GITERR_INVALID, "Failed to hash object");
+ giterr_set(GITERR_INDEXER, "Failed to hash object");
return -1;
}
@@ -441,10 +441,21 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stat
git_oid file_hash;
SHA_CTX ctx;
+ /* Test for this before resolve_deltas(), as it plays with idx->off */
+ if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
+ giterr_set(GITERR_INDEXER, "Indexing error: junk at the end of the pack");
+ return -1;
+ }
+
if (idx->deltas.length > 0)
if (resolve_deltas(idx, stats) < 0)
return -1;
+ if (stats->processed != stats->total) {
+ giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+ return -1;
+ }
+
git_vector_sort(&idx->objects);
git_buf_sets(&filename, idx->pack->pack_name);
@@ -583,7 +594,7 @@ int git_indexer_new(git_indexer **out, const char *packname)
assert(out && packname);
if (git_path_root(packname) < 0) {
- giterr_set(GITERR_INVALID, "Path is not absolute");
+ giterr_set(GITERR_INDEXER, "Path is not absolute");
return -1;
}
@@ -815,7 +826,7 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
/* FIXME: Parse the object instead of hashing it */
error = git_odb__hashobj(&oid, &obj);
if (error < 0) {
- giterr_set(GITERR_INVALID, "Failed to hash object");
+ giterr_set(GITERR_INDEXER, "Failed to hash object");
goto cleanup;
}
diff --git a/src/iterator.c b/src/iterator.c
index 646990d3f..c601a6e73 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -11,6 +11,23 @@
#include "buffer.h"
#include "git2/submodule.h"
+#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
+ (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
+ GITERR_CHECK_ALLOC(P); \
+ (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \
+ (P)->base.start = start ? git__strdup(start) : NULL; \
+ (P)->base.end = end ? git__strdup(end) : NULL; \
+ (P)->base.current = NAME_LC ## _iterator__current; \
+ (P)->base.at_end = NAME_LC ## _iterator__at_end; \
+ (P)->base.advance = NAME_LC ## _iterator__advance; \
+ (P)->base.seek = NAME_LC ## _iterator__seek; \
+ (P)->base.reset = NAME_LC ## _iterator__reset; \
+ (P)->base.free = NAME_LC ## _iterator__free; \
+ if ((start && !(P)->base.start) || (end && !(P)->base.end)) \
+ return -1; \
+ } while (0)
+
+
static int empty_iterator__no_item(
git_iterator *iter, const git_index_entry **entry)
{
@@ -31,6 +48,13 @@ static int empty_iterator__noop(git_iterator *iter)
return 0;
}
+static int empty_iterator__seek(git_iterator *iter, const char *prefix)
+{
+ GIT_UNUSED(iter);
+ GIT_UNUSED(prefix);
+ return -1;
+}
+
static void empty_iterator__free(git_iterator *iter)
{
GIT_UNUSED(iter);
@@ -45,6 +69,7 @@ int git_iterator_for_nothing(git_iterator **iter)
i->current = empty_iterator__no_item;
i->at_end = empty_iterator__at_end;
i->advance = empty_iterator__no_item;
+ i->seek = empty_iterator__seek;
i->reset = empty_iterator__noop;
i->free = empty_iterator__free;
@@ -53,10 +78,12 @@ int git_iterator_for_nothing(git_iterator **iter)
return 0;
}
+
typedef struct tree_iterator_frame tree_iterator_frame;
struct tree_iterator_frame {
tree_iterator_frame *next;
git_tree *tree;
+ char *start;
unsigned int index;
};
@@ -66,6 +93,7 @@ typedef struct {
tree_iterator_frame *stack;
git_index_entry entry;
git_buf path;
+ bool path_has_filename;
} tree_iterator;
static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
@@ -74,25 +102,62 @@ static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
}
+static char *tree_iterator__current_filename(
+ tree_iterator *ti, const git_tree_entry *te)
+{
+ if (!ti->path_has_filename) {
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return NULL;
+ ti->path_has_filename = true;
+ }
+
+ return ti->path.ptr;
+}
+
+static void tree_iterator__pop_frame(tree_iterator *ti)
+{
+ tree_iterator_frame *tf = ti->stack;
+ ti->stack = tf->next;
+ if (ti->stack != NULL) /* don't free the initial tree */
+ git_tree_free(tf->tree);
+ git__free(tf);
+}
+
+static int tree_iterator__to_end(tree_iterator *ti)
+{
+ while (ti->stack && ti->stack->next)
+ tree_iterator__pop_frame(ti);
+
+ if (ti->stack)
+ ti->stack->index = git_tree_entrycount(ti->stack->tree);
+
+ return 0;
+}
+
static int tree_iterator__current(
git_iterator *self, const git_index_entry **entry)
{
tree_iterator *ti = (tree_iterator *)self;
const git_tree_entry *te = tree_iterator__tree_entry(ti);
- *entry = NULL;
+ if (entry)
+ *entry = NULL;
if (te == NULL)
return 0;
ti->entry.mode = te->attr;
git_oid_cpy(&ti->entry.oid, &te->oid);
- if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+
+ ti->entry.path = tree_iterator__current_filename(ti, te);
+ if (ti->entry.path == NULL)
return -1;
- ti->entry.path = ti->path.ptr;
+ if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
+ return tree_iterator__to_end(ti);
- *entry = &ti->entry;
+ if (entry)
+ *entry = &ti->entry;
return 0;
}
@@ -102,11 +167,20 @@ static int tree_iterator__at_end(git_iterator *self)
return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
}
-static tree_iterator_frame *tree_iterator__alloc_frame(git_tree *tree)
+static tree_iterator_frame *tree_iterator__alloc_frame(
+ git_tree *tree, char *start)
{
tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
- if (tf != NULL)
- tf->tree = tree;
+ if (!tf)
+ return NULL;
+
+ tf->tree = tree;
+
+ if (start && *start) {
+ tf->start = start;
+ tf->index = git_tree_entry_prefix_position(tree, start);
+ }
+
return tf;
}
@@ -116,35 +190,43 @@ static int tree_iterator__expand_tree(tree_iterator *ti)
git_tree *subtree;
const git_tree_entry *te = tree_iterator__tree_entry(ti);
tree_iterator_frame *tf;
+ char *relpath;
while (te != NULL && entry_is_tree(te)) {
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return -1;
+
+ /* check that we have not passed the range end */
+ if (ti->base.end != NULL &&
+ git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
+ return tree_iterator__to_end(ti);
+
if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
return error;
- if ((tf = tree_iterator__alloc_frame(subtree)) == NULL)
+ relpath = NULL;
+
+ /* apply range start to new frame if relevant */
+ if (ti->stack->start &&
+ git__prefixcmp(ti->stack->start, te->filename) == 0)
+ {
+ size_t namelen = strlen(te->filename);
+ if (ti->stack->start[namelen] == '/')
+ relpath = ti->stack->start + namelen + 1;
+ }
+
+ if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
return -1;
tf->next = ti->stack;
ti->stack = tf;
- if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
- return -1;
-
te = tree_iterator__tree_entry(ti);
}
return 0;
}
-static void tree_iterator__pop_frame(tree_iterator *ti)
-{
- tree_iterator_frame *tf = ti->stack;
- ti->stack = tf->next;
- if (ti->stack != NULL) /* don't free the initial tree */
- git_tree_free(tf->tree);
- git__free(tf);
-}
-
static int tree_iterator__advance(
git_iterator *self, const git_index_entry **entry)
{
@@ -155,26 +237,40 @@ static int tree_iterator__advance(
if (entry != NULL)
*entry = NULL;
- while (ti->stack != NULL) {
- /* remove old entry filename */
+ if (ti->path_has_filename) {
git_buf_rtruncate_at_char(&ti->path, '/');
+ ti->path_has_filename = false;
+ }
+ while (ti->stack != NULL) {
te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
if (te != NULL)
break;
tree_iterator__pop_frame(ti);
+
+ git_buf_rtruncate_at_char(&ti->path, '/');
}
if (te && entry_is_tree(te))
error = tree_iterator__expand_tree(ti);
- if (!error && entry != NULL)
+ if (!error)
error = tree_iterator__current(self, entry);
return error;
}
+static int tree_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* pop stack until matches prefix */
+ /* seek item in current frame matching prefix */
+ /* push stack which matches prefix */
+ return -1;
+}
+
static void tree_iterator__free(git_iterator *self)
{
tree_iterator *ti = (tree_iterator *)self;
@@ -186,15 +282,25 @@ static void tree_iterator__free(git_iterator *self)
static int tree_iterator__reset(git_iterator *self)
{
tree_iterator *ti = (tree_iterator *)self;
+
while (ti->stack && ti->stack->next)
tree_iterator__pop_frame(ti);
+
if (ti->stack)
- ti->stack->index = 0;
+ ti->stack->index =
+ git_tree_entry_prefix_position(ti->stack->tree, ti->base.start);
+
+ git_buf_clear(&ti->path);
+
return tree_iterator__expand_tree(ti);
}
-int git_iterator_for_tree(
- git_repository *repo, git_tree *tree, git_iterator **iter)
+int git_iterator_for_tree_range(
+ git_iterator **iter,
+ git_repository *repo,
+ git_tree *tree,
+ const char *start,
+ const char *end)
{
int error;
tree_iterator *ti;
@@ -202,22 +308,16 @@ int git_iterator_for_tree(
if (tree == NULL)
return git_iterator_for_nothing(iter);
- ti = git__calloc(1, sizeof(tree_iterator));
- GITERR_CHECK_ALLOC(ti);
+ ITERATOR_BASE_INIT(ti, tree, TREE);
- ti->base.type = GIT_ITERATOR_TREE;
- ti->base.current = tree_iterator__current;
- ti->base.at_end = tree_iterator__at_end;
- ti->base.advance = tree_iterator__advance;
- ti->base.reset = tree_iterator__reset;
- ti->base.free = tree_iterator__free;
- ti->repo = repo;
- ti->stack = tree_iterator__alloc_frame(tree);
+ ti->repo = repo;
+ ti->stack = tree_iterator__alloc_frame(tree, ti->base.start);
if ((error = tree_iterator__expand_tree(ti)) < 0)
git_iterator_free((git_iterator *)ti);
else
*iter = (git_iterator *)ti;
+
return error;
}
@@ -232,7 +332,19 @@ static int index_iterator__current(
git_iterator *self, const git_index_entry **entry)
{
index_iterator *ii = (index_iterator *)self;
- *entry = git_index_get(ii->index, ii->current);
+ git_index_entry *ie = git_index_get(ii->index, ii->current);
+
+ if (ie != NULL &&
+ ii->base.end != NULL &&
+ git__prefixcmp(ie->path, ii->base.end) > 0)
+ {
+ ii->current = git_index_entrycount(ii->index);
+ ie = NULL;
+ }
+
+ if (entry)
+ *entry = ie;
+
return 0;
}
@@ -246,11 +358,19 @@ static int index_iterator__advance(
git_iterator *self, const git_index_entry **entry)
{
index_iterator *ii = (index_iterator *)self;
+
if (ii->current < git_index_entrycount(ii->index))
ii->current++;
- if (entry)
- *entry = git_index_get(ii->index, ii->current);
- return 0;
+
+ return index_iterator__current(self, entry);
+}
+
+static int index_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* find last item before prefix */
+ return -1;
}
static int index_iterator__reset(git_iterator *self)
@@ -267,24 +387,24 @@ static void index_iterator__free(git_iterator *self)
ii->index = NULL;
}
-int git_iterator_for_index(git_repository *repo, git_iterator **iter)
+int git_iterator_for_index_range(
+ git_iterator **iter,
+ git_repository *repo,
+ const char *start,
+ const char *end)
{
int error;
- index_iterator *ii = git__calloc(1, sizeof(index_iterator));
- GITERR_CHECK_ALLOC(ii);
+ index_iterator *ii;
- ii->base.type = GIT_ITERATOR_INDEX;
- ii->base.current = index_iterator__current;
- ii->base.at_end = index_iterator__at_end;
- ii->base.advance = index_iterator__advance;
- ii->base.reset = index_iterator__reset;
- ii->base.free = index_iterator__free;
- ii->current = 0;
+ ITERATOR_BASE_INIT(ii, index, INDEX);
if ((error = git_repository_index(&ii->index, repo)) < 0)
git__free(ii);
- else
+ else {
+ ii->current = start ? git_index__prefix_position(ii->index, start) : 0;
*iter = (git_iterator *)ii;
+ }
+
return error;
}
@@ -294,6 +414,7 @@ struct workdir_iterator_frame {
workdir_iterator_frame *next;
git_vector entries;
unsigned int index;
+ char *start;
};
typedef struct {
@@ -332,6 +453,12 @@ static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
static int workdir_iterator__update_entry(workdir_iterator *wi);
+static int workdir_iterator__entry_cmp(const void *prefix, const void *item)
+{
+ const git_path_with_stat *ps = item;
+ return git__prefixcmp((const char *)prefix, ps->path);
+}
+
static int workdir_iterator__expand_dir(workdir_iterator *wi)
{
int error;
@@ -345,6 +472,17 @@ static int workdir_iterator__expand_dir(workdir_iterator *wi)
}
git_vector_sort(&wf->entries);
+
+ if (!wi->stack)
+ wf->start = wi->base.start;
+ else if (wi->stack->start &&
+ git__prefixcmp(wi->stack->start, wi->path.ptr + wi->root_len) == 0)
+ wf->start = wi->stack->start;
+
+ if (wf->start)
+ git_vector_bsearch3(
+ &wf->index, &wf->entries, workdir_iterator__entry_cmp, wf->start);
+
wf->next = wi->stack;
wi->stack = wf;
@@ -412,6 +550,16 @@ static int workdir_iterator__advance(
return error;
}
+static int workdir_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* pop stack until matching prefix */
+ /* find prefix item in current frame */
+ /* push subdirectories as deep as possible while matching */
+ return 0;
+}
+
static int workdir_iterator__reset(git_iterator *self)
{
workdir_iterator *wi = (workdir_iterator *)self;
@@ -445,10 +593,18 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
git_buf_truncate(&wi->path, wi->root_len);
+ memset(&wi->entry, 0, sizeof(wi->entry));
+
+ if (!ps)
+ return 0;
+
if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
return -1;
- memset(&wi->entry, 0, sizeof(wi->entry));
+ if (wi->base.end &&
+ git__prefixcmp(wi->path.ptr + wi->root_len, wi->base.end) > 0)
+ return 0;
+
wi->entry.path = ps->path;
/* skip over .git directory */
@@ -495,19 +651,24 @@ static int workdir_iterator__update_entry(workdir_iterator *wi)
return 0;
}
-int git_iterator_for_workdir(git_repository *repo, git_iterator **iter)
+int git_iterator_for_workdir_range(
+ git_iterator **iter,
+ git_repository *repo,
+ const char *start,
+ const char *end)
{
int error;
- workdir_iterator *wi = git__calloc(1, sizeof(workdir_iterator));
- GITERR_CHECK_ALLOC(wi);
+ workdir_iterator *wi;
- wi->base.type = GIT_ITERATOR_WORKDIR;
- wi->base.current = workdir_iterator__current;
- wi->base.at_end = workdir_iterator__at_end;
- wi->base.advance = workdir_iterator__advance;
- wi->base.reset = workdir_iterator__reset;
- wi->base.free = workdir_iterator__free;
- wi->repo = repo;
+ if (git_repository_is_bare(repo)) {
+ giterr_set(GITERR_INVALID,
+ "Cannot scan working directory for bare repo");
+ return -1;
+ }
+
+ ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
+
+ wi->repo = repo;
if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
git_path_to_dir(&wi->path) < 0 ||
@@ -559,3 +720,21 @@ int git_iterator_advance_into_directory(
return entry ? git_iterator_current(iter, entry) : 0;
}
+
+int git_iterator_cmp(
+ git_iterator *iter, const char *path_prefix)
+{
+ const git_index_entry *entry;
+
+ /* a "done" iterator is after every prefix */
+ if (git_iterator_current(iter, &entry) < 0 ||
+ entry == NULL)
+ return 1;
+
+ /* a NULL prefix is after any valid iterator */
+ if (!path_prefix)
+ return -1;
+
+ return git__prefixcmp(entry->path, path_prefix);
+}
+
diff --git a/src/iterator.h b/src/iterator.h
index 12eb96bb0..b916a9080 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -21,23 +21,48 @@ typedef enum {
struct git_iterator {
git_iterator_type_t type;
+ char *start;
+ char *end;
int (*current)(git_iterator *, const git_index_entry **);
int (*at_end)(git_iterator *);
int (*advance)(git_iterator *, const git_index_entry **);
+ int (*seek)(git_iterator *, const char *prefix);
int (*reset)(git_iterator *);
void (*free)(git_iterator *);
};
-int git_iterator_for_nothing(git_iterator **iter);
+extern int git_iterator_for_nothing(git_iterator **iter);
-int git_iterator_for_tree(
- git_repository *repo, git_tree *tree, git_iterator **iter);
+extern int git_iterator_for_tree_range(
+ git_iterator **iter, git_repository *repo, git_tree *tree,
+ const char *start, const char *end);
-int git_iterator_for_index(
- git_repository *repo, git_iterator **iter);
+GIT_INLINE(int) git_iterator_for_tree(
+ git_iterator **iter, git_repository *repo, git_tree *tree)
+{
+ return git_iterator_for_tree_range(iter, repo, tree, NULL, NULL);
+}
+
+extern int git_iterator_for_index_range(
+ git_iterator **iter, git_repository *repo,
+ const char *start, const char *end);
+
+GIT_INLINE(int) git_iterator_for_index(
+ git_iterator **iter, git_repository *repo)
+{
+ return git_iterator_for_index_range(iter, repo, NULL, NULL);
+}
+
+extern int git_iterator_for_workdir_range(
+ git_iterator **iter, git_repository *repo,
+ const char *start, const char *end);
+
+GIT_INLINE(int) git_iterator_for_workdir(
+ git_iterator **iter, git_repository *repo)
+{
+ return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
+}
-int git_iterator_for_workdir(
- git_repository *repo, git_iterator **iter);
/* Entry is not guaranteed to be fully populated. For a tree iterator,
* we will only populate the mode, oid and path, for example. For a workdir
@@ -64,6 +89,12 @@ GIT_INLINE(int) git_iterator_advance(
return iter->advance(iter, entry);
}
+GIT_INLINE(int) git_iterator_seek(
+ git_iterator *iter, const char *prefix)
+{
+ return iter->seek(iter, prefix);
+}
+
GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
{
return iter->reset(iter);
@@ -71,7 +102,16 @@ GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
GIT_INLINE(void) git_iterator_free(git_iterator *iter)
{
+ if (iter == NULL)
+ return;
+
iter->free(iter);
+
+ git__free(iter->start);
+ git__free(iter->end);
+
+ memset(iter, 0, sizeof(*iter));
+
git__free(iter);
}
@@ -105,4 +145,7 @@ extern int git_iterator_current_is_ignored(git_iterator *iter);
extern int git_iterator_advance_into_directory(
git_iterator *iter, const git_index_entry **entry);
+extern int git_iterator_cmp(
+ git_iterator *iter, const char *path_prefix);
+
#endif
diff --git a/src/notes.c b/src/notes.c
index 4afdac0bd..1da2ac442 100644
--- a/src/notes.c
+++ b/src/notes.c
@@ -10,6 +10,7 @@
#include "git2.h"
#include "refs.h"
#include "config.h"
+#include "iterator.h"
static int find_subtree(git_tree **subtree, const git_oid *root,
git_repository *repo, const char *target, int *fanout)
@@ -282,41 +283,54 @@ static int note_get_default_ref(const char **out, git_repository *repo)
return ret;
}
+static int normalize_namespace(const char **notes_ref, git_repository *repo)
+{
+ if (*notes_ref)
+ return 0;
+
+ return note_get_default_ref(notes_ref, repo);
+}
+
+static int retrieve_note_tree_oid(git_oid *tree_oid_out, git_repository *repo, const char *notes_ref)
+{
+ int error = -1;
+ git_commit *commit = NULL;
+ git_oid oid;
+
+ if ((error = git_reference_name_to_oid(&oid, repo, notes_ref)) < 0)
+ goto cleanup;
+
+ if (git_commit_lookup(&commit, repo, &oid) < 0)
+ goto cleanup;
+
+ git_oid_cpy(tree_oid_out, git_commit_tree_oid(commit));
+
+ error = 0;
+
+cleanup:
+ git_commit_free(commit);
+ return error;
+}
+
int git_note_read(git_note **out, git_repository *repo,
const char *notes_ref, const git_oid *oid)
{
int error;
char *target;
- git_reference *ref;
- git_commit *commit;
- const git_oid *sha;
+ git_oid sha;
*out = NULL;
- if (!notes_ref && note_get_default_ref(&notes_ref, repo) < 0)
+ if (normalize_namespace(&notes_ref, repo) < 0)
return -1;
- error = git_reference_lookup(&ref, repo, notes_ref);
- if (error < 0)
+ if ((error = retrieve_note_tree_oid(&sha, repo, notes_ref)) < 0)
return error;
- assert(git_reference_type(ref) == GIT_REF_OID);
-
- sha = git_reference_oid(ref);
- error = git_commit_lookup(&commit, repo, sha);
-
- git_reference_free(ref);
-
- if (error < 0)
- return error;
-
- sha = git_commit_tree_oid(commit);
- git_commit_free(commit);
-
target = git_oid_allocfmt(oid);
GITERR_CHECK_ALLOC(target);
- error = note_lookup(out, repo, sha, target);
+ error = note_lookup(out, repo, &sha, target);
git__free(target);
return error;
@@ -334,7 +348,7 @@ int git_note_create(
git_commit *commit = NULL;
git_reference *ref;
- if (!notes_ref && note_get_default_ref(&notes_ref, repo) < 0)
+ if (normalize_namespace(&notes_ref, repo) < 0)
return -1;
error = git_reference_lookup(&ref, repo, notes_ref);
@@ -379,8 +393,7 @@ int git_note_remove(git_repository *repo, const char *notes_ref,
git_commit *commit;
git_reference *ref;
-
- if (!notes_ref && note_get_default_ref(&notes_ref, repo) < 0)
+ if (normalize_namespace(&notes_ref, repo) < 0)
return -1;
error = git_reference_lookup(&ref, repo, notes_ref);
@@ -435,3 +448,99 @@ void git_note_free(git_note *note)
git__free(note->message);
git__free(note);
}
+
+static int process_entry_path(
+ const char* entry_path,
+ git_oid note_oid,
+ int (*note_cb)(const git_oid *note_oid, const git_oid *annotated_object_oid, void *payload),
+ void *payload)
+{
+ int i = 0, j = 0, error = -1, len;
+ git_oid target_oid;
+ git_buf buf = GIT_BUF_INIT;
+
+ if (git_buf_puts(&buf, entry_path) < 0)
+ goto cleanup;
+
+ len = git_buf_len(&buf);
+
+ while (i < len) {
+ if (buf.ptr[i] == '/') {
+ i++;
+ continue;
+ }
+
+ if (git__fromhex(buf.ptr[i]) < 0) {
+ /* This is not a note entry */
+ error = 0;
+ goto cleanup;
+ }
+
+ if (i != j)
+ buf.ptr[j] = buf.ptr[i];
+
+ i++;
+ j++;
+ }
+
+ buf.ptr[j] = '\0';
+ buf.size = j;
+
+ if (j != GIT_OID_HEXSZ) {
+ /* This is not a note entry */
+ error = 0;
+ goto cleanup;
+ }
+
+ if (git_oid_fromstr(&target_oid, buf.ptr) < 0)
+ return -1;
+
+ error = note_cb(&note_oid, &target_oid, payload);
+
+cleanup:
+ git_buf_free(&buf);
+ return error;
+}
+
+int git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ int (*note_cb)(const git_oid *note_oid, const git_oid *annotated_object_oid, void *payload),
+ void *payload)
+{
+ int error = -1;
+ git_oid tree_oid;
+ git_iterator *iter = NULL;
+ git_tree *tree = NULL;
+ const git_index_entry *item;
+
+ if (normalize_namespace(&notes_ref, repo) < 0)
+ return -1;
+
+ if ((error = retrieve_note_tree_oid(&tree_oid, repo, notes_ref)) < 0)
+ goto cleanup;
+
+ if (git_tree_lookup(&tree, repo, &tree_oid) < 0)
+ goto cleanup;
+
+ if (git_iterator_for_tree(&iter, repo, tree) < 0)
+ goto cleanup;
+
+ if (git_iterator_current(iter, &item) < 0)
+ goto cleanup;
+
+ while (item) {
+ if (process_entry_path(item->path, item->oid, note_cb, payload) < 0)
+ goto cleanup;
+
+ if (git_iterator_advance(iter, &item) < 0)
+ goto cleanup;
+ }
+
+ error = 0;
+
+cleanup:
+ git_iterator_free(iter);
+ git_tree_free(tree);
+ return error;
+}
diff --git a/src/odb.c b/src/odb.c
index 934b317ed..03cd912e9 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -557,9 +557,9 @@ int git_odb_read_prefix(
{
unsigned int i;
int error = GIT_ENOTFOUND;
- git_oid full_oid;
+ git_oid found_full_oid = {{0}};
git_rawobj raw;
- int found = 0;
+ bool found = false;
assert(out && db);
@@ -575,25 +575,30 @@ int git_odb_read_prefix(
return 0;
}
- for (i = 0; i < db->backends.length && found < 2; ++i) {
+ for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->read != NULL) {
+ git_oid full_oid;
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
- if (!error)
- found++;
- else if (error != GIT_ENOTFOUND && error != GIT_EPASSTHROUGH)
+ if (error == GIT_ENOTFOUND || error == GIT_EPASSTHROUGH)
+ continue;
+
+ if (error)
return error;
+
+ if (found && git_oid_cmp(&full_oid, &found_full_oid))
+ return git_odb__error_ambiguous("multiple matches for prefix");
+ found_full_oid = full_oid;
+ found = true;
}
}
- if (found == 0)
+ if (!found)
return git_odb__error_notfound("no match for prefix", short_id);
- if (found > 1)
- return git_odb__error_ambiguous("multiple matches for prefix");
- *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw));
+ *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
return 0;
}
diff --git a/src/path.c b/src/path.c
index 9f31676b1..84edf6d89 100644
--- a/src/path.c
+++ b/src/path.c
@@ -494,7 +494,7 @@ int git_path_direach(
{
ssize_t wd_len;
DIR *dir;
- struct dirent de_buf, *de;
+ struct dirent *de, *de_buf;
if (git_path_to_dir(path) < 0)
return -1;
@@ -506,14 +506,23 @@ int git_path_direach(
return -1;
}
- while (p_readdir_r(dir, &de_buf, &de) == 0 && de != NULL) {
+#ifdef __sun
+ de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+ de_buf = git__malloc(sizeof(struct dirent));
+#endif
+
+ while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
int result;
if (is_dot_or_dotdot(de->d_name))
continue;
- if (git_buf_puts(path, de->d_name) < 0)
+ if (git_buf_puts(path, de->d_name) < 0) {
+ closedir(dir);
+ git__free(de_buf);
return -1;
+ }
result = fn(arg, path);
@@ -521,11 +530,13 @@ int git_path_direach(
if (result < 0) {
closedir(dir);
+ git__free(de_buf);
return -1;
}
}
closedir(dir);
+ git__free(de_buf);
return 0;
}
@@ -537,7 +548,7 @@ int git_path_dirload(
{
int error, need_slash;
DIR *dir;
- struct dirent de_buf, *de;
+ struct dirent *de, *de_buf;
size_t path_len;
assert(path != NULL && contents != NULL);
@@ -549,11 +560,17 @@ int git_path_dirload(
return -1;
}
+#ifdef __sun
+ de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+ de_buf = git__malloc(sizeof(struct dirent));
+#endif
+
path += prefix_len;
path_len -= prefix_len;
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
- while ((error = p_readdir_r(dir, &de_buf, &de)) == 0 && de != NULL) {
+ while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
char *entry_path;
size_t entry_len;
@@ -573,11 +590,15 @@ int git_path_dirload(
memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
entry_path[path_len + need_slash + entry_len] = '\0';
- if (git_vector_insert(contents, entry_path) < 0)
+ if (git_vector_insert(contents, entry_path) < 0) {
+ closedir(dir);
+ git__free(de_buf);
return -1;
+ }
}
closedir(dir);
+ git__free(de_buf);
if (error != 0)
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
diff --git a/src/status.c b/src/status.c
index d07b0c41c..e9ad3cfe4 100644
--- a/src/status.c
+++ b/src/status.c
@@ -70,7 +70,7 @@ static unsigned int workdir_delta2status(git_delta_t workdir_status)
int git_status_foreach_ext(
git_repository *repo,
- git_status_options *opts,
+ const git_status_options *opts,
int (*cb)(const char *, unsigned int, void *),
void *cbdata)
{
@@ -163,244 +163,71 @@ int git_status_foreach(
return git_status_foreach_ext(repo, &opts, callback, payload);
}
-
-/*
- * the old stuff
- */
-
-struct status_entry {
- git_index_time mtime;
-
- git_oid head_oid;
- git_oid index_oid;
- git_oid wt_oid;
-
- unsigned int status_flags;
-
- char path[GIT_FLEX_ARRAY]; /* more */
+struct status_file_info {
+ unsigned int count;
+ unsigned int status;
+ char *expected;
};
-static struct status_entry *status_entry_new(git_vector *entries, const char *path)
-{
- struct status_entry *e = git__calloc(sizeof(*e) + strlen(path) + 1, 1);
- if (e == NULL)
- return NULL;
-
- if (entries != NULL)
- git_vector_insert(entries, e);
-
- strcpy(e->path, path);
-
- return e;
-}
-
-GIT_INLINE(void) status_entry_update_from_tree_entry(struct status_entry *e, const git_tree_entry *tree_entry)
-{
- assert(e && tree_entry);
-
- git_oid_cpy(&e->head_oid, &tree_entry->oid);
-}
-
-GIT_INLINE(void) status_entry_update_from_index_entry(struct status_entry *e, const git_index_entry *index_entry)
-{
- assert(e && index_entry);
-
- git_oid_cpy(&e->index_oid, &index_entry->oid);
- e->mtime = index_entry->mtime;
-}
-
-static void status_entry_update_from_index(struct status_entry *e, git_index *index)
-{
- int idx;
- git_index_entry *index_entry;
-
- assert(e && index);
-
- idx = git_index_find(index, e->path);
- if (idx < 0)
- return;
-
- index_entry = git_index_get(index, idx);
-
- status_entry_update_from_index_entry(e, index_entry);
-}
-
-static int status_entry_update_from_workdir(struct status_entry *e, const char* full_path)
-{
- struct stat filest;
-
- if (p_stat(full_path, &filest) < 0) {
- giterr_set(GITERR_OS, "Cannot access file '%s'", full_path);
- return GIT_ENOTFOUND;
- }
-
- if (e->mtime.seconds == (git_time_t)filest.st_mtime)
- git_oid_cpy(&e->wt_oid, &e->index_oid);
- else
- git_odb_hashfile(&e->wt_oid, full_path, GIT_OBJ_BLOB);
-
- return 0;
-}
-
-static int status_entry_update_flags(struct status_entry *e)
-{
- git_oid zero;
- int head_zero, index_zero, wt_zero;
-
- memset(&zero, 0x0, sizeof(git_oid));
-
- head_zero = git_oid_cmp(&zero, &e->head_oid);
- index_zero = git_oid_cmp(&zero, &e->index_oid);
- wt_zero = git_oid_cmp(&zero, &e->wt_oid);
-
- if (head_zero == 0 && index_zero == 0 && wt_zero == 0)
- return GIT_ENOTFOUND;
-
- if (head_zero == 0 && index_zero != 0)
- e->status_flags |= GIT_STATUS_INDEX_NEW;
- else if (index_zero == 0 && head_zero != 0)
- e->status_flags |= GIT_STATUS_INDEX_DELETED;
- else if (git_oid_cmp(&e->head_oid, &e->index_oid) != 0)
- e->status_flags |= GIT_STATUS_INDEX_MODIFIED;
-
- if (index_zero == 0 && wt_zero != 0)
- e->status_flags |= GIT_STATUS_WT_NEW;
- else if (wt_zero == 0 && index_zero != 0)
- e->status_flags |= GIT_STATUS_WT_DELETED;
- else if (git_oid_cmp(&e->index_oid, &e->wt_oid) != 0)
- e->status_flags |= GIT_STATUS_WT_MODIFIED;
-
- return 0;
-}
-
-static int status_entry_is_ignorable(struct status_entry *e)
+static int get_one_status(const char *path, unsigned int status, void *data)
{
- /* don't ignore files that exist in head or index already */
- return (e->status_flags == GIT_STATUS_WT_NEW);
-}
+ struct status_file_info *sfi = data;
-static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignores, const char *path)
-{
- int ignored;
+ sfi->count++;
+ sfi->status = status;
- if (git_ignore__lookup(ignores, path, &ignored) < 0)
+ if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
+ giterr_set(GITERR_INVALID,
+ "Ambiguous path '%s' given to git_status_file", sfi->expected);
return -1;
-
- if (ignored)
- /* toggle off WT_NEW and on IGNORED */
- e->status_flags =
- (e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED;
-
- return 0;
-}
-
-static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path)
-{
- char *dir_sep;
- const git_tree_entry *tree_entry;
- git_tree *subtree;
- int error;
-
- dir_sep = strchr(path, '/');
- if (!dir_sep) {
- if ((tree_entry = git_tree_entry_byname(tree, path)) != NULL)
- /* The leaf exists in the tree*/
- status_entry_update_from_tree_entry(e, tree_entry);
- return 0;
- }
-
- /* Retrieve subtree name */
- *dir_sep = '\0';
-
- if ((tree_entry = git_tree_entry_byname(tree, path)) == NULL)
- return 0; /* The subtree doesn't exist in the tree*/
-
- *dir_sep = '/';
-
- /* Retreive subtree */
- error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid);
- if (!error) {
- error = recurse_tree_entry(subtree, e, dir_sep+1);
- git_tree_free(subtree);
}
- return error;
+ return 0;
}
int git_status_file(
- unsigned int *status_flags, git_repository *repo, const char *path)
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path)
{
- struct status_entry *e;
- git_index *index = NULL;
- git_buf temp_path = GIT_BUF_INIT;
- int error = 0;
- git_tree *tree = NULL;
- const char *workdir;
+ int error;
+ git_status_options opts;
+ struct status_file_info sfi;
assert(status_flags && repo && path);
- if ((workdir = git_repository_workdir(repo)) == NULL) {
- giterr_set(GITERR_INVALID, "Cannot get file status from bare repo");
+ memset(&sfi, 0, sizeof(sfi));
+ if ((sfi.expected = git__strdup(path)) == NULL)
return -1;
- }
-
- if (git_buf_joinpath(&temp_path, workdir, path) < 0)
- return -1;
-
- if (git_path_isdir(temp_path.ptr)) {
- giterr_set(GITERR_INVALID, "Cannot get file status for directory '%s'", temp_path.ptr);
- git_buf_free(&temp_path);
- return -1;
- }
-
- e = status_entry_new(NULL, path);
- GITERR_CHECK_ALLOC(e);
-
- /* Find file in Workdir */
- if (git_path_exists(temp_path.ptr) == true &&
- (error = status_entry_update_from_workdir(e, temp_path.ptr)) < 0)
- goto cleanup;
-
- /* Find file in Index */
- if ((error = git_repository_index__weakptr(&index, repo)) < 0)
- goto cleanup;
- status_entry_update_from_index(e, index);
- /* Try to find file in HEAD */
- if ((error = git_repository_head_tree(&tree, repo)) < 0)
- goto cleanup;
-
- if (tree != NULL) {
- if ((error = git_buf_sets(&temp_path, path)) < 0 ||
- (error = recurse_tree_entry(tree, e, temp_path.ptr)) < 0)
- goto cleanup;
- }
-
- /* Determine status */
- if ((error = status_entry_update_flags(e)) < 0)
- giterr_set(GITERR_OS, "Cannot find file '%s' to determine status", path);
-
- if (!error && status_entry_is_ignorable(e)) {
- git_ignores ignores;
+ memset(&opts, 0, sizeof(opts));
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &sfi.expected;
- if ((error = git_ignore__for_path(repo, path, &ignores)) == 0)
- error = status_entry_update_ignore(e, &ignores, path);
+ error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
- git_ignore__free(&ignores);
+ if (!error && !sfi.count) {
+ giterr_set(GITERR_INVALID,
+ "Attempt to get status of nonexistent file '%s'", path);
+ error = GIT_ENOTFOUND;
}
- if (!error)
- *status_flags = e->status_flags;
+ *status_flags = sfi.status;
-cleanup:
- git_buf_free(&temp_path);
- git_tree_free(tree);
- git__free(e);
+ git__free(sfi.expected);
return error;
}
-int git_status_should_ignore(git_repository *repo, const char *path, int *ignored)
+int git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path)
{
int error;
git_ignores ignores;
diff --git a/src/tree.c b/src/tree.c
index 7e2bfc102..adbf97498 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -195,6 +195,33 @@ const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
return git_vector_get(&tree->entries, idx);
}
+int git_tree_entry_prefix_position(git_tree *tree, const char *path)
+{
+ git_vector *entries = &tree->entries;
+ struct tree_key_search ksearch;
+ unsigned int at_pos;
+
+ ksearch.filename = path;
+ ksearch.filename_len = strlen(path);
+
+ /* Find tree entry with appropriate prefix */
+ git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);
+
+ for (; at_pos < entries->length; ++at_pos) {
+ const git_tree_entry *entry = entries->contents[at_pos];
+ if (homing_search_cmp(&ksearch, entry) < 0)
+ break;
+ }
+
+ for (; at_pos > 0; --at_pos) {
+ const git_tree_entry *entry = entries->contents[at_pos - 1];
+ if (homing_search_cmp(&ksearch, entry) > 0)
+ break;
+ }
+
+ return at_pos;
+}
+
unsigned int git_tree_entrycount(git_tree *tree)
{
assert(tree);
diff --git a/src/tree.h b/src/tree.h
index fd00afde5..a5b7f6323 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -38,4 +38,14 @@ GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e)
void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree, git_odb_object *obj);
+/**
+ * Lookup the first position in the tree with a given prefix.
+ *
+ * @param tree a previously loaded tree.
+ * @param prefix the beginning of a path to find in the tree.
+ * @return index of the first item at or after the given prefix.
+ */
+int git_tree_entry_prefix_position(git_tree *tree, const char *prefix);
+
+
#endif
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 9973acf30..48b492941 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -7,7 +7,14 @@
#ifndef INCLUDE_posix__w32_h__
#define INCLUDE_posix__w32_h__
-#include <fnmatch.h>
+#ifndef __sun
+# include <fnmatch.h>
+# define p_fnmatch(p, s, f) fnmatch(p, s, f)
+#else
+# include "compat/fnmatch.h"
+#endif
+
+#include <stdio.h>
#define p_lstat(p,b) lstat(p,b)
#define p_readlink(a, b, c) readlink(a, b, c)
@@ -16,7 +23,6 @@
#define p_mkdir(p,m) mkdir(p, m)
#define p_fsync(fd) fsync(fd)
#define p_realpath(p, po) realpath(p, po)
-#define p_fnmatch(p, s, f) fnmatch(p, s, f)
#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p)
diff --git a/src/util.h b/src/util.h
index 2081f29f9..cb5e83ce9 100644
--- a/src/util.h
+++ b/src/util.h
@@ -209,4 +209,9 @@ GIT_INLINE(bool) git__isspace(int c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
}
+GIT_INLINE(bool) git__iswildcard(int c)
+{
+ return (c == '*' || c == '?' || c == '[');
+}
+
#endif /* INCLUDE_util_h__ */
diff --git a/src/vector.c b/src/vector.c
index 304f324f0..6f9aacccf 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -116,8 +116,13 @@ void git_vector_sort(git_vector *v)
v->sorted = 1;
}
-int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *key)
+int git_vector_bsearch3(
+ unsigned int *at_pos,
+ git_vector *v,
+ git_vector_cmp key_lookup,
+ const void *key)
{
+ int rval;
size_t pos;
assert(v && key && key_lookup);
@@ -127,13 +132,16 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke
git_vector_sort(v);
- if (git__bsearch(v->contents, v->length, key, key_lookup, &pos) >= 0)
- return (int)pos;
+ rval = git__bsearch(v->contents, v->length, key, key_lookup, &pos);
- return GIT_ENOTFOUND;
+ if (at_pos != NULL)
+ *at_pos = (unsigned int)pos;
+
+ return (rval >= 0) ? (int)pos : GIT_ENOTFOUND;
}
-int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key)
+int git_vector_search2(
+ git_vector *v, git_vector_cmp key_lookup, const void *key)
{
unsigned int i;
@@ -157,11 +165,6 @@ int git_vector_search(git_vector *v, const void *entry)
return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);
}
-int git_vector_bsearch(git_vector *v, const void *key)
-{
- return git_vector_bsearch2(v, v->_cmp, key);
-}
-
int git_vector_remove(git_vector *v, unsigned int idx)
{
unsigned int i;
diff --git a/src/vector.h b/src/vector.h
index 5bc27914a..9139db345 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -26,13 +26,24 @@ void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v);
void git_vector_swap(git_vector *a, git_vector *b);
+void git_vector_sort(git_vector *v);
+
int git_vector_search(git_vector *v, const void *entry);
int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
-int git_vector_bsearch(git_vector *v, const void *entry);
-int git_vector_bsearch2(git_vector *v, git_vector_cmp cmp, const void *key);
+int git_vector_bsearch3(
+ unsigned int *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
-void git_vector_sort(git_vector *v);
+GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)
+{
+ return git_vector_bsearch3(NULL, v, v->_cmp, key);
+}
+
+GIT_INLINE(int) git_vector_bsearch2(
+ git_vector *v, git_vector_cmp cmp, const void *key)
+{
+ return git_vector_bsearch3(NULL, v, cmp, key);
+}
GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
{
diff --git a/src/win32/posix.h b/src/win32/posix.h
index 2666fccb4..baa4a3b4e 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -8,7 +8,7 @@
#define INCLUDE_posix__w32_h__
#include "common.h"
-#include "fnmatch.h"
+#include "compat/fnmatch.h"
#include "utf-conv.h"
GIT_INLINE(int) p_link(const char *old, const char *new)
diff --git a/tests-clar/clar b/tests-clar/clar
index 873dc3b0c..873dc3b0c 100644..100755
--- a/tests-clar/clar
+++ b/tests-clar/clar
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index 9294ccdfd..6a718f459 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -561,3 +561,53 @@ void test_core_buffer__10(void)
git_buf_free(&a);
}
+
+void test_core_buffer__11(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_strarray t;
+ char *t1[] = { "nothing", "in", "common" };
+ char *t2[] = { "something", "something else", "some other" };
+ char *t3[] = { "something", "some fun", "no fun" };
+ char *t4[] = { "happy", "happier", "happiest" };
+ char *t5[] = { "happiest", "happier", "happy" };
+ char *t6[] = { "no", "nope", "" };
+ char *t7[] = { "", "doesn't matter" };
+
+ t.strings = t1;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t2;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "some");
+
+ t.strings = t3;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t4;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t5;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t6;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t7;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ git_buf_free(&a);
+}
diff --git a/tests-clar/core/dirent.c b/tests-clar/core/dirent.c
index 9c366bf97..5a7859d1b 100644
--- a/tests-clar/core/dirent.c
+++ b/tests-clar/core/dirent.c
@@ -222,3 +222,14 @@ void test_core_dirent__traverse_weird_filenames(void)
check_counts(&odd);
}
+
+/* test filename length limits */
+void test_core_dirent__length_limits(void)
+{
+ char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
+ memset(big_filename, 'a', FILENAME_MAX + 1);
+ big_filename[FILENAME_MAX] = 0;
+
+ cl_must_fail(p_creat(big_filename, 0666));
+ git__free(big_filename);
+}
diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c
index 0ec2326eb..be29bea66 100644
--- a/tests-clar/diff/iterator.c
+++ b/tests-clar/diff/iterator.c
@@ -22,6 +22,8 @@ void test_diff_iterator__cleanup(void)
static void tree_iterator_test(
const char *sandbox,
const char *treeish,
+ const char *start,
+ const char *end,
int expected_count,
const char **expected_values)
{
@@ -32,7 +34,7 @@ static void tree_iterator_test(
git_repository *repo = cl_git_sandbox_init(sandbox);
cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
- cl_git_pass(git_iterator_for_tree(repo, t, &i));
+ cl_git_pass(git_iterator_for_tree_range(&i, repo, t, start, end));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -74,7 +76,7 @@ const char *expected_tree_0[] = {
void test_diff_iterator__tree_0(void)
{
- tree_iterator_test("attr", "605812a", 16, expected_tree_0);
+ tree_iterator_test("attr", "605812a", NULL, NULL, 16, expected_tree_0);
}
/* results of: git ls-tree -r --name-only 6bab5c79 */
@@ -97,7 +99,7 @@ const char *expected_tree_1[] = {
void test_diff_iterator__tree_1(void)
{
- tree_iterator_test("attr", "6bab5c79cd5", 13, expected_tree_1);
+ tree_iterator_test("attr", "6bab5c79cd5", NULL, NULL, 13, expected_tree_1);
}
/* results of: git ls-tree -r --name-only 26a125ee1 */
@@ -119,7 +121,7 @@ const char *expected_tree_2[] = {
void test_diff_iterator__tree_2(void)
{
- tree_iterator_test("status", "26a125ee1", 12, expected_tree_2);
+ tree_iterator_test("status", "26a125ee1", NULL, NULL, 12, expected_tree_2);
}
/* $ git ls-tree -r --name-only 0017bd4ab1e */
@@ -136,7 +138,7 @@ const char *expected_tree_3[] = {
void test_diff_iterator__tree_3(void)
{
- tree_iterator_test("status", "0017bd4ab1e", 8, expected_tree_3);
+ tree_iterator_test("status", "0017bd4ab1e", NULL, NULL, 8, expected_tree_3);
}
/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */
@@ -170,14 +172,77 @@ const char *expected_tree_4[] = {
void test_diff_iterator__tree_4(void)
{
tree_iterator_test(
- "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b", NULL, NULL,
23, expected_tree_4);
}
+void test_diff_iterator__tree_4_ranged(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub", "sub",
+ 11, &expected_tree_4[12]);
+}
+
+const char *expected_tree_ranged_0[] = {
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ NULL
+};
+
+void test_diff_iterator__tree_ranged_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "git", "root",
+ 7, expected_tree_ranged_0);
+}
+
+const char *expected_tree_ranged_1[] = {
+ "sub/subdir_test2.txt",
+ NULL
+};
+
+void test_diff_iterator__tree_ranged_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub/subdir_test2.txt", "sub/subdir_test2.txt",
+ 1, expected_tree_ranged_1);
+}
+
+void test_diff_iterator__tree_range_empty_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "empty", "empty", 0, NULL);
+}
+
+void test_diff_iterator__tree_range_empty_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "z_empty_after", NULL, 0, NULL);
+}
+
+void test_diff_iterator__tree_range_empty_2(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ NULL, ".aaa_empty_before", 0, NULL);
+}
+
/* -- INDEX ITERATOR TESTS -- */
static void index_iterator_test(
const char *sandbox,
+ const char *start,
+ const char *end,
int expected_count,
const char **expected_names,
const char **expected_oids)
@@ -187,7 +252,7 @@ static void index_iterator_test(
int count = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
- cl_git_pass(git_iterator_for_index(repo, &i));
+ cl_git_pass(git_iterator_for_index_range(&i, repo, start, end));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -197,7 +262,7 @@ static void index_iterator_test(
if (expected_oids != NULL) {
git_oid oid;
cl_git_pass(git_oid_fromstr(&oid, expected_oids[count]));
- cl_assert(git_oid_cmp(&oid, &entry->oid) == 0);
+ cl_assert_equal_i(git_oid_cmp(&oid, &entry->oid), 0);
}
count++;
@@ -206,7 +271,7 @@ static void index_iterator_test(
git_iterator_free(i);
- cl_assert(count == expected_count);
+ cl_assert_equal_i(expected_count, count);
}
static const char *expected_index_0[] = {
@@ -263,7 +328,46 @@ static const char *expected_index_oids_0[] = {
void test_diff_iterator__index_0(void)
{
- index_iterator_test("attr", 23, expected_index_0, expected_index_oids_0);
+ index_iterator_test(
+ "attr", NULL, NULL, 23, expected_index_0, expected_index_oids_0);
+}
+
+static const char *expected_index_range[] = {
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+};
+
+static const char *expected_index_oids_range[] = {
+ "45141a79a77842c59a63229403220a4e4be74e3d",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
+};
+
+void test_diff_iterator__index_range(void)
+{
+ index_iterator_test(
+ "attr", "root", "root", 4, expected_index_range, expected_index_oids_range);
+}
+
+void test_diff_iterator__index_range_empty_0(void)
+{
+ index_iterator_test(
+ "attr", "empty", "empty", 0, NULL, NULL);
+}
+
+void test_diff_iterator__index_range_empty_1(void)
+{
+ index_iterator_test(
+ "attr", "z_empty_after", NULL, 0, NULL, NULL);
+}
+
+void test_diff_iterator__index_range_empty_2(void)
+{
+ index_iterator_test(
+ "attr", NULL, ".aaa_empty_before", 0, NULL, NULL);
}
static const char *expected_index_1[] = {
@@ -300,7 +404,8 @@ static const char* expected_index_oids_1[] = {
void test_diff_iterator__index_1(void)
{
- index_iterator_test("status", 13, expected_index_1, expected_index_oids_1);
+ index_iterator_test(
+ "status", NULL, NULL, 13, expected_index_1, expected_index_oids_1);
}
@@ -308,6 +413,8 @@ void test_diff_iterator__index_1(void)
static void workdir_iterator_test(
const char *sandbox,
+ const char *start,
+ const char *end,
int expected_count,
int expected_ignores,
const char **expected_names,
@@ -318,7 +425,7 @@ static void workdir_iterator_test(
int count = 0, count_all = 0;
git_repository *repo = cl_git_sandbox_init(sandbox);
- cl_git_pass(git_iterator_for_workdir(repo, &i));
+ cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
cl_git_pass(git_iterator_current(i, &entry));
while (entry != NULL) {
@@ -350,7 +457,7 @@ static void workdir_iterator_test(
void test_diff_iterator__workdir_0(void)
{
- workdir_iterator_test("attr", 25, 2, NULL, "ign");
+ workdir_iterator_test("attr", NULL, NULL, 25, 2, NULL, "ign");
}
static const char *status_paths[] = {
@@ -372,5 +479,92 @@ static const char *status_paths[] = {
void test_diff_iterator__workdir_1(void)
{
- workdir_iterator_test("status", 12, 1, status_paths, "ignored_file");
+ workdir_iterator_test(
+ "status", NULL, NULL, 12, 1, status_paths, "ignored_file");
+}
+
+static const char *status_paths_range_0[] = {
+ "staged_changes",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_0(void)
+{
+ workdir_iterator_test(
+ "status", "staged", "staged", 5, 0, status_paths_range_0, NULL);
+}
+
+static const char *status_paths_range_1[] = {
+ "modified_file", NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_1(void)
+{
+ workdir_iterator_test(
+ "status", "modified_file", "modified_file",
+ 1, 0, status_paths_range_1, NULL);
+}
+
+static const char *status_paths_range_3[] = {
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_3(void)
+{
+ workdir_iterator_test(
+ "status", "subdir", "subdir/modified_file",
+ 3, 0, status_paths_range_3, NULL);
+}
+
+static const char *status_paths_range_4[] = {
+ "subdir/current_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_4(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/", NULL, 3, 0, status_paths_range_4, NULL);
+}
+
+static const char *status_paths_range_5[] = {
+ "subdir/modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_5(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/modified_file", "subdir/modified_file",
+ 1, 0, status_paths_range_5, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_0(void)
+{
+ workdir_iterator_test(
+ "status", "z_does_not_exist", NULL,
+ 0, 0, NULL, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_1(void)
+{
+ workdir_iterator_test(
+ "status", "empty", "empty",
+ 0, 0, NULL, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_2(void)
+{
+ workdir_iterator_test(
+ "status", NULL, "aaaa_empty_before",
+ 0, 0, NULL, NULL);
}
diff --git a/tests-clar/notes/notes.c b/tests-clar/notes/notes.c
index ca82ab29c..9c50f1acb 100644
--- a/tests-clar/notes/notes.c
+++ b/tests-clar/notes/notes.c
@@ -1,43 +1,46 @@
#include "clar_libgit2.h"
static git_repository *_repo;
-static git_note *_note;
-static git_blob *_blob;
static git_signature *_sig;
void test_notes_notes__initialize(void)
{
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+ _repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
}
void test_notes_notes__cleanup(void)
{
- git_note_free(_note);
- git_blob_free(_blob);
git_signature_free(_sig);
+ cl_git_sandbox_cleanup();
+}
+
+static void create_note(git_oid *note_oid, const char *canonical_namespace, const char *target_sha, const char *message)
+{
+ git_oid oid;
- git_repository_free(_repo);
- cl_fixture_cleanup("testrepo.git");
+ cl_git_pass(git_oid_fromstr(&oid, target_sha));
+ cl_git_pass(git_note_create(note_oid, _repo, _sig, _sig, canonical_namespace, &oid, message));
}
void test_notes_notes__1(void)
{
git_oid oid, note_oid;
+ static git_note *note;
+ static git_blob *blob;
- cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
cl_git_pass(git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479"));
cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &oid, "hello world\n"));
cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &oid, "hello world\n"));
- cl_git_pass(git_note_read(&_note, _repo, NULL, &oid));
+ cl_git_pass(git_note_read(&note, _repo, NULL, &oid));
- cl_assert_equal_s(git_note_message(_note), "hello world\n");
- cl_assert(!git_oid_cmp(git_note_oid(_note), &note_oid));
+ cl_assert_equal_s(git_note_message(note), "hello world\n");
+ cl_assert(!git_oid_cmp(git_note_oid(note), &note_oid));
- cl_git_pass(git_blob_lookup(&_blob, _repo, &note_oid));
- cl_assert_equal_s(git_note_message(_note), git_blob_rawcontent(_blob));
+ cl_git_pass(git_blob_lookup(&blob, _repo, &note_oid));
+ cl_assert_equal_s(git_note_message(note), git_blob_rawcontent(blob));
cl_git_fail(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &oid, "hello world\n"));
cl_git_fail(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &oid, "hello world\n"));
@@ -47,4 +50,84 @@ void test_notes_notes__1(void)
cl_git_fail(git_note_remove(_repo, NULL, _sig, _sig, &note_oid));
cl_git_fail(git_note_remove(_repo, "refs/notes/some/namespace", _sig, _sig, &oid));
+
+ git_note_free(note);
+ git_blob_free(blob);
+}
+
+static struct {
+ const char *note_sha;
+ const char *annotated_object_sha;
+} list_expectations[] = {
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045" },
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "9fd738e8f7967c078dceed8190330fc8648ee56a" },
+ { "257b43746b6b46caa4aa788376c647cce0a33e2b", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750" },
+ { "1ec1c8e03f461f4f5d3f3702172483662e7223f3", "c47800c7266a2be04c571c04d5a6614691ea99bd" },
+ { NULL, NULL }
+};
+
+#define EXPECTATIONS_COUNT (sizeof(list_expectations)/sizeof(list_expectations[0])) - 1
+
+static int note_list_cb(const git_oid *note_oid, const git_oid *annotated_object_oid, void *payload)
+{
+ git_oid expected_note_oid, expected_target_oid;
+
+ unsigned int *count = (unsigned int *)payload;
+
+ cl_assert(*count < EXPECTATIONS_COUNT);
+
+ cl_git_pass(git_oid_fromstr(&expected_note_oid, list_expectations[*count].note_sha));
+ cl_assert(git_oid_cmp(&expected_note_oid, note_oid) == 0);
+
+ cl_git_pass(git_oid_fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha));
+ cl_assert(git_oid_cmp(&expected_target_oid, annotated_object_oid) == 0);
+
+ (*count)++;
+
+ return 0;
+}
+
+/*
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate a65f" a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate c478" c47800c7266a2be04c571c04d5a6614691ea99bd
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git notes --ref i-can-see-dead-notes list
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git ls-tree refs/notes/i-can-see-dead-notes
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 100644 blob 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 100644 blob 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+*/
+void test_notes_notes__can_retrieve_a_list_of_notes_for_a_given_namespace(void)
+{
+ git_oid note_oid1, note_oid2, note_oid3, note_oid4;
+ unsigned int retrieved_notes = 0;
+
+ create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
+ create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
+ create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
+ create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes));
+
+ cl_assert_equal_i(4, retrieved_notes);
+}
+
+void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void)
+{
+ int error;
+ unsigned int retrieved_notes = 0;
+
+ error = git_note_foreach(_repo, "refs/notes/i-am-not", note_list_cb, &retrieved_notes);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ cl_assert_equal_i(0, retrieved_notes);
}
diff --git a/tests-clar/object/blob/write.c b/tests-clar/object/blob/write.c
new file mode 100644
index 000000000..722c7b956
--- /dev/null
+++ b/tests-clar/object/blob/write.c
@@ -0,0 +1,69 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+
+#define WORKDIR "empty_standard_repo"
+#define BARE_REPO "testrepo.git"
+#define ELSEWHERE "elsewhere"
+
+typedef int (*blob_creator_fn)(
+ git_oid *,
+ git_repository *,
+ const char *);
+
+void test_object_blob_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_blob_creation(const char *path_to_file, const char *blob_from_path, blob_creator_fn creator)
+{
+ git_oid oid;
+ cl_git_mkfile(path_to_file, "1..2...3... Can you hear me?\n");
+
+ cl_must_pass(creator(&oid, repo, blob_from_path));
+ cl_assert(git_oid_streq(&oid, "da5e4f20c91c81b44a7e298f3d3fb3fe2f178e32") == 0);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_file_located_in_the_working_directory(void)
+{
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ assert_blob_creation(WORKDIR "/test.txt", "test.txt", &git_blob_create_fromfile);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolute_filepath_pointing_outside_of_the_working_directory(void)
+{
+ git_buf full_path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_buf_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
+
+ git_buf_free(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void)
+{
+ git_buf full_path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init(BARE_REPO);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_buf_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
+
+ git_buf_free(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
diff --git a/tests-clar/odb/mixed.c b/tests-clar/odb/mixed.c
new file mode 100644
index 000000000..7c28a434e
--- /dev/null
+++ b/tests-clar/odb/mixed.c
@@ -0,0 +1,25 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "pack_data.h"
+
+static git_odb *_odb;
+
+void test_odb_mixed__initialize(void)
+{
+ cl_git_pass(git_odb_open(&_odb, cl_fixture("duplicate.git/objects")));
+}
+
+void test_odb_mixed__cleanup(void)
+{
+ git_odb_free(_odb);
+}
+
+void test_odb_mixed__dup_oid(void) {
+ const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
+ git_oid oid;
+ git_odb_object *obj;
+ cl_git_pass(git_oid_fromstr(&oid, hex));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
+ git_odb_object_free(obj);
+}
+
diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c
index 095893020..8ccfaf32f 100644
--- a/tests-clar/refs/branches/delete.c
+++ b/tests-clar/refs/branches/delete.c
@@ -74,3 +74,18 @@ void test_refs_branches_delete__can_delete_a_remote_branch(void)
{
cl_git_pass(git_branch_delete(repo, "nulltoken/master", GIT_BRANCH_REMOTE));
}
+
+static void assert_non_exisitng_branch_removal(const char *branch_name, git_branch_type branch_type)
+{
+ int error;
+ error = git_branch_delete(repo, branch_name, branch_type);
+
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+void test_refs_branches_delete__deleting_a_non_existing_branch_returns_ENOTFOUND(void)
+{
+ assert_non_exisitng_branch_removal("i-do-not-locally-exist", GIT_BRANCH_LOCAL);
+ assert_non_exisitng_branch_removal("neither/remotely", GIT_BRANCH_REMOTE);
+}
diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c
index 208bb460e..242e5cd01 100644
--- a/tests-clar/refs/branches/move.c
+++ b/tests-clar/refs/branches/move.c
@@ -60,3 +60,13 @@ void test_refs_branches_move__can_not_move_a_branch_through_its_canonical_name(v
{
cl_git_fail(git_branch_move(repo, "refs/heads/br2", NEW_BRANCH_NAME, 1));
}
+
+void test_refs_branches_move__moving_a_non_exisiting_branch_returns_ENOTFOUND(void)
+{
+ int error;
+
+ error = git_branch_move(repo, "where/am/I", NEW_BRANCH_NAME, 0);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
diff --git a/tests-clar/resources/duplicate.git/COMMIT_EDITMSG b/tests-clar/resources/duplicate.git/COMMIT_EDITMSG
new file mode 100644
index 000000000..01f9a2aac
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/COMMIT_EDITMSG
@@ -0,0 +1 @@
+commit
diff --git a/tests-clar/resources/duplicate.git/HEAD b/tests-clar/resources/duplicate.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/duplicate.git/config b/tests-clar/resources/duplicate.git/config
new file mode 100644
index 000000000..515f48362
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
diff --git a/tests-clar/resources/duplicate.git/description b/tests-clar/resources/duplicate.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample b/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample
new file mode 100755
index 000000000..8b2a2fe84
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit. The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/commit-msg" &&
+ exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+:
diff --git a/tests-clar/resources/duplicate.git/hooks/commit-msg.sample b/tests-clar/resources/duplicate.git/hooks/commit-msg.sample
new file mode 100755
index 000000000..b58d1184a
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message. The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit. The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
diff --git a/tests-clar/resources/duplicate.git/hooks/post-update.sample b/tests-clar/resources/duplicate.git/hooks/post-update.sample
new file mode 100755
index 000000000..ec17ec193
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample b/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample
new file mode 100755
index 000000000..b1f187c2e
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+ exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+:
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-commit.sample b/tests-clar/resources/duplicate.git/hooks/pre-commit.sample
new file mode 100755
index 000000000..18c482976
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-commit.sample
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ascii filenames set this variable to true.
+allownonascii=$(git config hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ascii filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ echo "Error: Attempt to add a non-ascii file name."
+ echo
+ echo "This can cause problems if you want to work"
+ echo "with people on other platforms."
+ echo
+ echo "To be portable it is advisable to rename the file ..."
+ echo
+ echo "If you know what you are doing you can disable this"
+ echo "check using:"
+ echo
+ echo " git config hooks.allownonascii true"
+ echo
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample b/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample
new file mode 100755
index 000000000..9773ed4cb
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+ topic="refs/heads/$2"
+else
+ topic=`git symbolic-ref HEAD` ||
+ exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+ ;;
+*)
+ exit 0 ;# we do not interrupt others.
+ ;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master. Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+ echo >&2 "No such branch $topic"
+ exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+ echo >&2 "$topic is fully merged to master; better remove it."
+ exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next? If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+ not_in_topic=`git rev-list "^$topic" master`
+ if test -z "$not_in_topic"
+ then
+ echo >&2 "$topic is already up-to-date with master"
+ exit 1 ;# we could allow it, but there is no point.
+ else
+ exit 0
+ fi
+else
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ /usr/bin/perl -e '
+ my $topic = $ARGV[0];
+ my $msg = "* $topic has commits already merged to public branch:\n";
+ my (%not_in_next) = map {
+ /^([0-9a-f]+) /;
+ ($1 => 1);
+ } split(/\n/, $ARGV[1]);
+ for my $elem (map {
+ /^([0-9a-f]+) (.*)$/;
+ [$1 => $2];
+ } split(/\n/, $ARGV[2])) {
+ if (!exists $not_in_next{$elem->[0]}) {
+ if ($msg) {
+ print STDERR $msg;
+ undef $msg;
+ }
+ print STDERR " $elem->[1]\n";
+ }
+ }
+ ' "$topic" "$not_in_next" "$not_in_master"
+ exit 1
+fi
+
+exit 0
+
+################################################################
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+ merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+ it is deleted. If you need to build on top of it to correct
+ earlier mistakes, a new topic branch is created by forking at
+ the tip of the "master". This is not strictly necessary, but
+ it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+ branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next". Young
+ topic branches can have stupid mistakes you would rather
+ clean up before publishing, and things that have not been
+ merged into other branches can be easily rebased without
+ affecting other people. But once it is published, you would
+ not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+ Then you can delete it. More importantly, you should not
+ build on top of it -- other people may already want to
+ change things related to the topic as patches against your
+ "master", so if you need further changes, it is better to
+ fork the topic (perhaps with the same name) afresh from the
+ tip of "master".
+
+Let's look at this example:
+
+ o---o---o---o---o---o---o---o---o---o "next"
+ / / / /
+ / a---a---b A / /
+ / / / /
+ / / c---c---c---c B /
+ / / / \ /
+ / / / b---b C \ /
+ / / / / \ /
+ ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished. It has been fully merged up to "master" and "next",
+ and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+ git rev-list ^master ^topic next
+ git rev-list ^master next
+
+ if these match, topic has not merged in next at all.
+
+To compute (2):
+
+ git rev-list master..topic
+
+ if this is empty, it is fully merged to "master".
diff --git a/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample b/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample
new file mode 100755
index 000000000..f093a02ec
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source. The hook's purpose is to edit the commit
+# message file. If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first comments out the
+# "Conflicts:" part of a merge commit.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output. It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited. This is rarely a good idea.
+
+case "$2,$3" in
+ merge,)
+ /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+
+# ,|template,)
+# /usr/bin/perl -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$1" ;;
+
+ *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/tests-clar/resources/duplicate.git/hooks/update.sample b/tests-clar/resources/duplicate.git/hooks/update.sample
new file mode 100755
index 000000000..71ab04edc
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/update.sample
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# An example hook script to blocks unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+# This boolean sets whether unannotated tags will be allowed into the
+# repository. By default they won't be.
+# hooks.allowdeletetag
+# This boolean sets whether deleting tags will be allowed in the
+# repository. By default they won't be.
+# hooks.allowmodifytag
+# This boolean sets whether a tag may be modified after creation. By default
+# it won't be.
+# hooks.allowdeletebranch
+# This boolean sets whether deleting branches will be allowed in the
+# repository. By default they won't be.
+# hooks.denycreatebranch
+# This boolean sets whether remotely creating branches will be denied
+# in the repository. By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+ echo "Don't run this script from the command line." >&2
+ echo " (if you want, you could supply GIT_DIR then run" >&2
+ echo " $0 <ref> <oldrev> <newrev>)" >&2
+ exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+ echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
+ exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+ ;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+ newrev_type=delete
+else
+ newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+ refs/tags/*,commit)
+ # un-annotated tag
+ short_refname=${refname##refs/tags/}
+ if [ "$allowunannotated" != "true" ]; then
+ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,delete)
+ # delete tag
+ if [ "$allowdeletetag" != "true" ]; then
+ echo "*** Deleting a tag is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+ then
+ echo "*** Tag '$refname' already exists." >&2
+ echo "*** Modifying a tag is not allowed in this repository." >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+ echo "*** Creating a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,delete)
+ # delete branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ ;;
+ refs/remotes/*,delete)
+ # delete tracking branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+ exit 1
+ ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/tests-clar/resources/duplicate.git/index b/tests-clar/resources/duplicate.git/index
new file mode 100644
index 000000000..a61e1c5ca
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/index
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/info/exclude b/tests-clar/resources/duplicate.git/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests-clar/resources/duplicate.git/info/refs b/tests-clar/resources/duplicate.git/info/refs
new file mode 100644
index 000000000..3b5bb03be
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/info/refs
@@ -0,0 +1 @@
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests-clar/resources/duplicate.git/logs/HEAD b/tests-clar/resources/duplicate.git/logs/HEAD
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests-clar/resources/duplicate.git/logs/refs/heads/master b/tests-clar/resources/duplicate.git/logs/refs/heads/master
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
new file mode 100644
index 000000000..6802d4949
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/info/packs b/tests-clar/resources/duplicate.git/objects/info/packs
new file mode 100644
index 000000000..3696a7d36
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
new file mode 100644
index 000000000..fd8abee98
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
new file mode 100644
index 000000000..9879e0869
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/packed-refs b/tests-clar/resources/duplicate.git/packed-refs
new file mode 100644
index 000000000..9f0d4e434
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests-clar/revwalk/mergebase.c b/tests-clar/revwalk/mergebase.c
index 91bc6ae8c..e807e3ad2 100644
--- a/tests-clar/revwalk/mergebase.c
+++ b/tests-clar/revwalk/mergebase.c
@@ -35,3 +35,114 @@ void test_revwalk_mergebase__single2(void)
cl_git_pass(git_merge_base(&result, _repo, &one, &two));
cl_assert(git_oid_cmp(&result, &expected) == 0);
}
+
+void test_revwalk_mergebase__merged_branch(void)
+{
+ git_oid result, one, two, expected;
+
+ git_oid_fromstr(&one, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ git_oid_fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+
+ cl_git_pass(git_merge_base(&result, _repo, &two, &one));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+}
+
+void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
+{
+ git_oid result, one, two, expected;
+ int error;
+
+ git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af");
+ git_oid_fromstr(&two, "e90810b8df3e80c413d903f631643c716887138d");
+ git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd");
+
+ error = git_merge_base(&result, _repo, &one, &two);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+/*
+ * $ git log --graph --all
+ * * commit 763d71aadf09a7951596c9746c024e7eece7c7af
+ * | Author: nulltoken <emeric.fermas@gmail.com>
+ * | Date: Sun Oct 9 12:54:47 2011 +0200
+ * |
+ * | Add some files into subdirectories
+ * |
+ * | * commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Tue Aug 9 19:33:46 2011 -0700
+ * | |
+ * | * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ * | |\ Merge: 9fd738e c47800c
+ * | |/ Author: Scott Chacon <schacon@gmail.com>
+ * |/| Date: Tue May 25 11:58:27 2010 -0700
+ * | |
+ * | | Merge branch 'br2'
+ * | |
+ * | | * commit e90810b8df3e80c413d903f631643c716887138d
+ * | | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | | Date: Thu Aug 5 18:42:20 2010 +0200
+ * | | |
+ * | | | Test commit 2
+ * | | |
+ * | | * commit 6dcf9bf7541ee10456529833502442f385010c3d
+ * | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | Date: Thu Aug 5 18:41:33 2010 +0200
+ * | |
+ * | | Test commit 1
+ * | |
+ * | | * commit a4a7dce85cf63874e984719f4fdd239f5145052f
+ * | | |\ Merge: c47800c 9fd738e
+ * | |/ / Author: Scott Chacon <schacon@gmail.com>
+ * |/| / Date: Tue May 25 12:00:23 2010 -0700
+ * | |/
+ * | | Merge branch 'master' into br2
+ * | |
+ * | * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:19 2010 -0700
+ * | |
+ * | | a fourth commit
+ * | |
+ * | * commit 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:04 2010 -0700
+ * | |
+ * | | a third commit
+ * | |
+ * * | commit c47800c7266a2be04c571c04d5a6614691ea99bd
+ * |/ Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 25 11:58:14 2010 -0700
+ * |
+ * | branch commit one
+ * |
+ * * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:38:42 2010 -0700
+ * |
+ * | another commit
+ * |
+ * * commit 8496071c1b46c854b31185ea97743be6a8774479
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Sat May 8 16:13:06 2010 -0700
+ *
+ * testing
+ *
+ * * commit 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:40:41 2010 -0700
+ * |
+ * | packed commit two
+ * |
+ * * commit 5001298e0c09ad9c34e4249bc5801c75e9754fa5
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Tue May 11 13:40:23 2010 -0700
+ *
+ * packed commit one
+ */
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index e92d6a577..369b25bda 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -2,12 +2,12 @@
#include "fileops.h"
#include "git2/attr.h"
#include "attr.h"
+#include "status_helpers.h"
static git_repository *g_repo = NULL;
void test_status_ignore__initialize(void)
{
- g_repo = cl_git_sandbox_init("attr");
}
void test_status_ignore__cleanup(void)
@@ -40,9 +40,11 @@ void test_status_ignore__0(void)
{ NULL, 0 }
}, *one_test;
+ g_repo = cl_git_sandbox_init("attr");
+
for (one_test = test_cases; one_test->path != NULL; one_test++) {
int ignored;
- cl_git_pass(git_status_should_ignore(g_repo, one_test->path, &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, one_test->path));
cl_assert_(ignored == one_test->expected, one_test->path);
}
@@ -56,25 +58,76 @@ void test_status_ignore__1(void)
{
int ignored;
+ g_repo = cl_git_sandbox_init("attr");
+
cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
git_attr_cache_flush(g_repo);
- cl_git_pass(git_status_should_ignore(g_repo, "root_test4.txt", &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "root_test4.txt"));
cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(g_repo, "sub/subdir_test2.txt", &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/subdir_test2.txt"));
cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(g_repo, "dir", &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir"));
cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(g_repo, "dir/", &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir/"));
cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(g_repo, "sub/dir", &ignored));
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir/"));
+ cl_assert(!ignored);
+}
+
+
+void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
+{
+ status_entry_single st;
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/look-ma.txt", "I'm going to be ignored!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 1);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(g_repo, "sub/dir/", &ignored));
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
cl_assert(!ignored);
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
+ cl_assert(ignored);
}
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
index 7f078bf60..f109717e8 100644
--- a/tests-clar/status/status_data.h
+++ b/tests-clar/status/status_data.h
@@ -1,12 +1,4 @@
-
-struct status_entry_counts {
- size_t wrong_status_flags_count;
- size_t wrong_sorted_path;
- size_t entry_count;
- const unsigned int* expected_statuses;
- const char** expected_paths;
- size_t expected_entry_count;
-};
+#include "status_helpers.h"
/* entries for a plain copy of tests/resources/status */
diff --git a/tests-clar/status/status_helpers.c b/tests-clar/status/status_helpers.c
new file mode 100644
index 000000000..3dbf43a5b
--- /dev/null
+++ b/tests-clar/status/status_helpers.c
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+#include "status_helpers.h"
+
+int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ status_entry_counts *counts = payload;
+
+ if (counts->entry_count >= counts->expected_entry_count) {
+ counts->wrong_status_flags_count++;
+ goto exit;
+ }
+
+ if (strcmp(path, counts->expected_paths[counts->entry_count])) {
+ counts->wrong_sorted_path++;
+ goto exit;
+ }
+
+ if (status_flags != counts->expected_statuses[counts->entry_count])
+ counts->wrong_status_flags_count++;
+
+exit:
+ counts->entry_count++;
+ return 0;
+}
+
+int cb_status__count(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return 0;
+}
+
+int cb_status__single(const char *p, unsigned int s, void *payload)
+{
+ status_entry_single *data = (status_entry_single *)payload;
+
+ GIT_UNUSED(p);
+
+ data->count++;
+ data->status = s;
+
+ return 0;
+}
diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h
new file mode 100644
index 000000000..cffca66a5
--- /dev/null
+++ b/tests-clar/status/status_helpers.h
@@ -0,0 +1,33 @@
+#ifndef INCLUDE_cl_status_helpers_h__
+#define INCLUDE_cl_status_helpers_h__
+
+typedef struct {
+ size_t wrong_status_flags_count;
+ size_t wrong_sorted_path;
+ size_t entry_count;
+ const unsigned int* expected_statuses;
+ const char** expected_paths;
+ size_t expected_entry_count;
+} status_entry_counts;
+
+/* cb_status__normal takes payload of "status_entry_counts *" */
+
+extern int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload);
+
+
+/* cb_status__count takes payload of "int *" */
+
+extern int cb_status__count(const char *p, unsigned int s, void *payload);
+
+
+typedef struct {
+ int count;
+ unsigned int status;
+} status_entry_single;
+
+/* cb_status__single takes payload of "status_entry_single *" */
+
+extern int cb_status__single(const char *p, unsigned int s, void *payload);
+
+#endif
diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c
index 969158825..9423e8490 100644
--- a/tests-clar/status/submodules.c
+++ b/tests-clar/status/submodules.c
@@ -2,6 +2,7 @@
#include "buffer.h"
#include "path.h"
#include "posix.h"
+#include "status_helpers.h"
static git_repository *g_repo = NULL;
@@ -43,19 +44,6 @@ void test_status_submodules__api(void)
cl_assert_equal_s("testrepo", sm->path);
}
-static int
-cb_status__submodule_count(const char *p, unsigned int s, void *payload)
-{
- volatile int *count = (int *)payload;
-
- GIT_UNUSED(p);
- GIT_UNUSED(s);
-
- (*count)++;
-
- return 0;
-}
-
void test_status_submodules__0(void)
{
int counts = 0;
@@ -65,7 +53,7 @@ void test_status_submodules__0(void)
cl_assert(git_path_isfile("submodules/.gitmodules"));
cl_git_pass(
- git_status_foreach(g_repo, cb_status__submodule_count, &counts)
+ git_status_foreach(g_repo, cb_status__count, &counts)
);
cl_assert(counts == 6);
@@ -115,3 +103,10 @@ void test_status_submodules__1(void)
cl_assert(index == 6);
}
+
+void test_status_submodules__single_file(void)
+{
+ unsigned int status;
+ cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
+ cl_assert(status == 0);
+}
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index 4ac556aa6..e36f7e2ea 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -7,45 +7,6 @@
#include "path.h"
/**
- * Auxiliary methods
- */
-static int
-cb_status__normal( const char *path, unsigned int status_flags, void *payload)
-{
- struct status_entry_counts *counts = payload;
-
- if (counts->entry_count >= counts->expected_entry_count) {
- counts->wrong_status_flags_count++;
- goto exit;
- }
-
- if (strcmp(path, counts->expected_paths[counts->entry_count])) {
- counts->wrong_sorted_path++;
- goto exit;
- }
-
- if (status_flags != counts->expected_statuses[counts->entry_count])
- counts->wrong_status_flags_count++;
-
-exit:
- counts->entry_count++;
- return 0;
-}
-
-static int
-cb_status__count(const char *p, unsigned int s, void *payload)
-{
- volatile int *count = (int *)payload;
-
- GIT_UNUSED(p);
- GIT_UNUSED(s);
-
- (*count)++;
-
- return 0;
-}
-
-/**
* Initializer
*
* Not all of the tests in this file use the same fixtures, so we allow each
@@ -72,10 +33,10 @@ void test_status_worktree__cleanup(void)
/* this test is equivalent to t18-status.c:statuscb0 */
void test_status_worktree__whole_repository(void)
{
- struct status_entry_counts counts;
+ status_entry_counts counts;
git_repository *repo = cl_git_sandbox_init("status");
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = entry_count0;
counts.expected_paths = entry_paths0;
counts.expected_statuses = entry_statuses0;
@@ -120,7 +81,7 @@ static int remove_file_cb(void *data, git_buf *file)
/* this test is equivalent to t18-status.c:statuscb2 */
void test_status_worktree__purged_worktree(void)
{
- struct status_entry_counts counts;
+ status_entry_counts counts;
git_repository *repo = cl_git_sandbox_init("status");
git_buf workdir = GIT_BUF_INIT;
@@ -130,7 +91,7 @@ void test_status_worktree__purged_worktree(void)
git_buf_free(&workdir);
/* now get status */
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = entry_count2;
counts.expected_paths = entry_paths2;
counts.expected_statuses = entry_statuses2;
@@ -147,7 +108,7 @@ void test_status_worktree__purged_worktree(void)
/* this test is similar to t18-status.c:statuscb3 */
void test_status_worktree__swap_subdir_and_file(void)
{
- struct status_entry_counts counts;
+ status_entry_counts counts;
git_repository *repo = cl_git_sandbox_init("status");
git_status_options opts;
@@ -161,7 +122,7 @@ void test_status_worktree__swap_subdir_and_file(void)
cl_git_mkfile("status/README.md", "dummy");
/* now get status */
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = entry_count3;
counts.expected_paths = entry_paths3;
counts.expected_statuses = entry_statuses3;
@@ -182,7 +143,7 @@ void test_status_worktree__swap_subdir_and_file(void)
void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
{
- struct status_entry_counts counts;
+ status_entry_counts counts;
git_repository *repo = cl_git_sandbox_init("status");
git_status_options opts;
@@ -196,7 +157,7 @@ void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
cl_git_mkfile("status/zzz_new_file", "dummy");
/* now get status */
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
+ memset(&counts, 0x0, sizeof(status_entry_counts));
counts.expected_entry_count = entry_count4;
counts.expected_paths = entry_paths4;
counts.expected_statuses = entry_statuses4;
@@ -286,18 +247,18 @@ void test_status_worktree__ignores(void)
for (i = 0; i < (int)entry_count0; i++) {
cl_git_pass(
- git_status_should_ignore(repo, entry_paths0[i], &ignored)
+ git_status_should_ignore(&ignored, repo, entry_paths0[i])
);
cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED));
}
cl_git_pass(
- git_status_should_ignore(repo, "nonexistent_file", &ignored)
+ git_status_should_ignore(&ignored, repo, "nonexistent_file")
);
cl_assert(!ignored);
cl_git_pass(
- git_status_should_ignore(repo, "ignored_nonexistent_file", &ignored)
+ git_status_should_ignore(&ignored, repo, "ignored_nonexistent_file")
);
cl_assert(ignored);
}
@@ -402,24 +363,6 @@ void test_status_worktree__cannot_retrieve_the_status_of_a_bare_repository(void)
git_repository_free(repo);
}
-typedef struct {
- int count;
- unsigned int status;
-} status_entry_single;
-
-static int
-cb_status__single(const char *p, unsigned int s, void *payload)
-{
- status_entry_single *data = (status_entry_single *)payload;
-
- GIT_UNUSED(p);
-
- data->count++;
- data->status = s;
-
- return 0;
-}
-
void test_status_worktree__first_commit_in_progress(void)
{
git_repository *repo;