diff --git a/include/git2.h b/include/git2.h index 1db506231..ff55aef2d 100644 --- a/include/git2.h +++ b/include/git2.h @@ -28,7 +28,7 @@ #define LIBGIT2_VERSION "0.11.0" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 10 +#define LIBGIT2_VER_MINOR 11 #define LIBGIT2_VER_REVISION 0 #include "git2/common.h" diff --git a/include/git2/index.h b/include/git2/index.h index 8a84f507b..7991de92e 100644 --- a/include/git2/index.h +++ b/include/git2/index.h @@ -51,39 +51,29 @@ GIT_BEGIN_DECL * * In-memory only flags: */ -#define GIT_IDXENTRY_UPDATE (1 << 16) -#define GIT_IDXENTRY_REMOVE (1 << 17) -#define GIT_IDXENTRY_UPTODATE (1 << 18) -#define GIT_IDXENTRY_ADDED (1 << 19) +#define GIT_IDXENTRY_UPDATE (1 << 0) +#define GIT_IDXENTRY_REMOVE (1 << 1) +#define GIT_IDXENTRY_UPTODATE (1 << 2) +#define GIT_IDXENTRY_ADDED (1 << 3) -#define GIT_IDXENTRY_HASHED (1 << 20) -#define GIT_IDXENTRY_UNHASHED (1 << 21) -#define GIT_IDXENTRY_WT_REMOVE (1 << 22) /* remove in work directory */ -#define GIT_IDXENTRY_CONFLICTED (1 << 23) +#define GIT_IDXENTRY_HASHED (1 << 4) +#define GIT_IDXENTRY_UNHASHED (1 << 5) +#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ +#define GIT_IDXENTRY_CONFLICTED (1 << 7) -#define GIT_IDXENTRY_UNPACKED (1 << 24) -#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 25) +#define GIT_IDXENTRY_UNPACKED (1 << 8) +#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) /* * Extended on-disk flags: */ -#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 29) -#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 30) +#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) +#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) /* GIT_IDXENTRY_EXTENDED2 is for future extension */ -#define GIT_IDXENTRY_EXTENDED2 (1 << 31) +#define GIT_IDXENTRY_EXTENDED2 (1 << 15) #define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) -/* - * Safeguard to avoid saving wrong flags: - * - GIT_IDXENTRY_EXTENDED2 won't get saved until its semantic is known - * - Bits in 0x0000FFFF have been saved in flags already - * - Bits in 0x003F0000 are currently in-memory flags - */ -#if GIT_IDXENTRY_EXTENDED_FLAGS & 0x803FFFFF -#error "GIT_IDXENTRY_EXTENDED_FLAGS out of range" -#endif - /** Time used in a git index entry */ typedef struct { git_time_t seconds; @@ -182,7 +172,12 @@ GIT_EXTERN(int) git_index_write(git_index *index); GIT_EXTERN(int) git_index_find(git_index *index, const char *path); /** - * Add or update an index entry from a file in disk. + * Add or update an index entry from a file in disk + * + * The file `path` must be relative to the repository's + * working folder and must be readable. + * + * This method will fail in bare index instances. * * @param index an existing index object * @param path filename to add @@ -191,6 +186,54 @@ GIT_EXTERN(int) git_index_find(git_index *index, const char *path); */ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage); +/** + * Add or update an index entry from an in-memory struct + * + * A full copy (including the 'path' string) of the given + * 'source_entry' will be inserted on the index. + * + * @param index an existing index object + * @param source_entry new entry object + * @return 0 on success, otherwise an error code + */ +GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry); + +/** + * Add (append) an index entry from a file in disk + * + * A new entry will always be inserted into the index; + * if the index already contains an entry for such + * path, the old entry will **not** be replaced. + * + * The file `path` must be relative to the repository's + * working folder and must be readable. + * + * This method will fail in bare index instances. + * + * @param index an existing index object + * @param path filename to add + * @param stage stage for the entry + * @return 0 on success, otherwise an error code + */ +GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage); + +/** + * Add (append) an index entry from an in-memory struct + * + * A new entry will always be inserted into the index; + * if the index already contains an entry for the path + * in the `entry` struct, the old entry will **not** be + * replaced. + * + * A full copy (including the 'path' string) of the given + * 'source_entry' will be inserted on the index. + * + * @param index an existing index object + * @param source_entry new entry object + * @return 0 on success, otherwise an error code + */ +GIT_EXTERN(int) git_index_apppend2(git_index *index, const git_index_entry *source_entry); + /** * Remove an entry from the index * @@ -200,18 +243,6 @@ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage); */ GIT_EXTERN(int) git_index_remove(git_index *index, int position); -/** - * Insert an entry into the index. - * A full copy (including the 'path' string) of the given - * 'source_entry' will be inserted on the index; if the index - * already contains an entry for the same path, the entry - * will be updated. - * - * @param index an existing index object - * @param source_entry new entry object - * @return 0 on success, otherwise an error code - */ -GIT_EXTERN(int) git_index_insert(git_index *index, const git_index_entry *source_entry); /** * Get a pointer to one of the entries in the index diff --git a/include/git2/tag.h b/include/git2/tag.h index c751a13c0..3fc6b4499 100644 --- a/include/git2/tag.h +++ b/include/git2/tag.h @@ -275,6 +275,23 @@ GIT_EXTERN(int) git_tag_delete( git_repository *repo, const char *tag_name); +/** + * Fill a list with all the tags in the Repository + * + * The string array will be filled with the names of the + * matching tags; these values are owned by the user and + * should be free'd manually when no longer needed, using + * `git_strarray_free`. + * + * @param array Pointer to a git_strarray structure where + * the tag names will be stored + * @param repo Repository where to find the tags + * @return 0 on success; error code otherwise + */ +GIT_EXTERN(int) git_tag_list( + git_strarray *tag_names, + git_repository *repo); + /** @} */ GIT_END_DECL #endif diff --git a/src/blob.c b/src/blob.c index bc0a08a8a..5e3c22fbf 100644 --- a/src/blob.c +++ b/src/blob.c @@ -115,6 +115,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat error = stream->finalize_write(oid, stream); stream->free(stream); + gitfo_close(fd); return error; } diff --git a/src/commit.c b/src/commit.c index 9621703c3..0c37ec59b 100644 --- a/src/commit.c +++ b/src/commit.c @@ -224,9 +224,18 @@ int git_commit_create( if (error < GIT_SUCCESS) return error; - if (git_reference_type(head) == GIT_REF_SYMBOLIC) { - if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS) + error = git_reference_resolve(&head, head); + if (error < GIT_SUCCESS) { + if (error != GIT_ENOTFOUND) return error; + /* + * The target of the reference was not found. This can happen + * just after a repository has been initialized (the master + * branch doesn't exist yet, as it doesn't have anything to + * point to) or after an orphan checkout, so if the target + * branch doesn't exist yet, create it and return. + */ + return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid); } error = git_reference_set_oid(head, oid); diff --git a/src/index.c b/src/index.c index 68bb9e2b9..130d1fd36 100644 --- a/src/index.c +++ b/src/index.c @@ -101,6 +101,7 @@ static int read_tree(git_index *index, const char *buffer, size_t buffer_size); static git_index_tree *read_tree_internal(const char **, const char *, git_index_tree *); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); +static int is_index_extended(git_index *index); static void sort_index(git_index *index); static int write_index(git_index *index, git_filebuf *file); @@ -289,56 +290,12 @@ git_index_entry *git_index_get(git_index *index, int n) return git_vector_get(&index->entries, (unsigned int)n); } -int git_index_add(git_index *index, const char *rel_path, int stage) -{ - git_index_entry entry; - char full_path[GIT_PATH_MAX]; - struct stat st; - int error; - - if (index->repository == NULL) - return GIT_EBAREINDEX; - - git__joinpath(full_path, index->repository->path_workdir, rel_path); - - if (gitfo_exists(full_path) < 0) - return GIT_ENOTFOUND; - - if (gitfo_stat(full_path, &st) < 0) - return GIT_EOSERR; - - if (stage < 0 || stage > 3) - return GIT_ERROR; - - memset(&entry, 0x0, sizeof(git_index_entry)); - - entry.ctime.seconds = (git_time_t)st.st_ctime; - entry.mtime.seconds = (git_time_t)st.st_mtime; - /* entry.mtime.nanoseconds = st.st_mtimensec; */ - /* entry.ctime.nanoseconds = st.st_ctimensec; */ - entry.dev= st.st_rdev; - entry.ino = st.st_ino; - entry.mode = st.st_mode; - entry.uid = st.st_uid; - entry.gid = st.st_gid; - entry.file_size = st.st_size; - - /* write the blob to disk and get the oid */ - if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS) - return error; - - entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT); - entry.path = (char *)rel_path; /* do not duplicate; index_insert already does this */ - - return git_index_insert(index, &entry); -} - -void sort_index(git_index *index) +static void sort_index(git_index *index) { git_vector_sort(&index->entries); } -int git_index_insert(git_index *index, const git_index_entry *source_entry) +static int index_insert(git_index *index, const git_index_entry *source_entry, int replace) { git_index_entry *entry; size_t path_length; @@ -374,13 +331,15 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry) /* look if an entry with this path already exists */ position = git_index_find(index, source_entry->path); - /* if no entry exists, add the entry at the end; + /* if no entry exists and replace is not set, + * add the entry at the end; * the index is no longer sorted */ - if (position == GIT_ENOTFOUND) { + if (!replace || position == GIT_ENOTFOUND) { if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - /* if a previous entry exists, replace it */ + /* if a previous entry exists and replace is set, + * replace it */ } else { git_index_entry **entry_array = (git_index_entry **)index->entries.contents; @@ -393,6 +352,81 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry) return GIT_SUCCESS; } +static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage) +{ + char full_path[GIT_PATH_MAX]; + struct stat st; + int error; + + if (index->repository == NULL) + return GIT_EBAREINDEX; + + git__joinpath(full_path, index->repository->path_workdir, rel_path); + + if (gitfo_exists(full_path) < 0) + return GIT_ENOTFOUND; + + if (gitfo_stat(full_path, &st) < 0) + return GIT_EOSERR; + + if (stage < 0 || stage > 3) + return GIT_ERROR; + + memset(entry, 0x0, sizeof(git_index_entry)); + + entry->ctime.seconds = (git_time_t)st.st_ctime; + entry->mtime.seconds = (git_time_t)st.st_mtime; + /* entry.mtime.nanoseconds = st.st_mtimensec; */ + /* entry.ctime.nanoseconds = st.st_ctimensec; */ + entry->dev= st.st_rdev; + entry->ino = st.st_ino; + entry->mode = st.st_mode; + entry->uid = st.st_uid; + entry->gid = st.st_gid; + entry->file_size = st.st_size; + + /* write the blob to disk and get the oid */ + if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS) + return error; + + entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); + entry->path = (char *)rel_path; /* do not duplicate; index_insert already does this */ + return GIT_SUCCESS; +} + +int git_index_add(git_index *index, const char *path, int stage) +{ + int error; + git_index_entry entry; + + if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) + return error; + + return index_insert(index, &entry, 1); +} + +int git_index_append(git_index *index, const char *path, int stage) +{ + int error; + git_index_entry entry; + + if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) + return error; + + return index_insert(index, &entry, 0); +} + +int git_index_add2(git_index *index, const git_index_entry *source_entry) +{ + return index_insert(index, source_entry, 1); +} + +int git_index_apppend2(git_index *index, const git_index_entry *source_entry) +{ + return index_insert(index, source_entry, 0); +} + + int git_index_remove(git_index *index, int position) { assert(index); @@ -685,6 +719,24 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) return GIT_SUCCESS; } +static int is_index_extended(git_index *index) +{ + unsigned int i, extended; + + extended = 0; + + for (i = 0; i < index->entries.length; ++i) { + git_index_entry *entry; + entry = git_vector_get(&index->entries, i); + entry->flags &= ~GIT_IDXENTRY_EXTENDED; + if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) { + extended++; + entry->flags |= GIT_IDXENTRY_EXTENDED; + } + } + return extended; +} + static int write_disk_entry(git_filebuf *file, git_index_entry *entry) { struct entry_short *ondisk; @@ -753,12 +805,14 @@ static int write_index(git_index *index, git_filebuf *file) struct index_header header; - int is_extended = 1; + int is_extended; assert(index && file); + is_extended = is_index_extended(index); + header.signature = htonl(INDEX_HEADER_SIG); - header.version = htonl(is_extended ? INDEX_VERSION_NUMBER : INDEX_VERSION_NUMBER_EXT); + header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER); header.entry_count = htonl(index->entries.length); git_filebuf_write(file, &header, sizeof(struct index_header)); diff --git a/src/odb_loose.c b/src/odb_loose.c index 8ee01cd2c..4f475f2c3 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -434,6 +434,9 @@ static int read_header_loose(git_rawobj *out, const char *loc) if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) { set_stream_input(&zs, raw_buffer, read_bytes); z_return = inflate(&zs, 0); + } else { + z_return = Z_STREAM_END; + break; } } while (z_return == Z_OK); diff --git a/src/odb_pack.c b/src/odb_pack.c index 8c527bcf3..57ad5e34b 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -108,15 +108,12 @@ struct pack_entry { struct pack_file *p; }; -struct pack__dirent { - struct pack_backend *backend; - int is_pack_local; -}; - struct pack_backend { git_odb_backend parent; git_vector packs; struct pack_file *last_found; + char *pack_folder; + time_t pack_folder_mtime; size_t window_size; /* needs default value */ @@ -259,9 +256,9 @@ static int pack_index_open(struct pack_file *p); static struct pack_file *packfile_alloc(int extra); static int packfile_open(struct pack_file *p); -static int packfile_check(struct pack_file **pack_out, const char *path, int local); +static int packfile_check(struct pack_file **pack_out, const char *path); static int packfile_load__cb(void *_data, char *path); -static int packfile_load_all(struct pack_backend *backend, const char *odb_path, int local); +static int packfile_refresh_all(struct pack_backend *backend); static off_t nth_packed_object_offset(const struct pack_file *p, uint32_t n); @@ -790,7 +787,7 @@ cleanup: return GIT_EPACKCORRUPTED; } -static int packfile_check(struct pack_file **pack_out, const char *path, int local) +static int packfile_check(struct pack_file **pack_out, const char *path) { struct stat st; struct pack_file *p; @@ -826,7 +823,7 @@ static int packfile_check(struct pack_file **pack_out, const char *path, int loc * actually mapping the pack file. */ p->pack_size = (off_t)st.st_size; - p->pack_local = local; + p->pack_local = 1; p->mtime = (git_time_t)st.st_mtime; /* see if we can parse the sha1 oid in the packfile name */ @@ -840,22 +837,25 @@ static int packfile_check(struct pack_file **pack_out, const char *path, int loc static int packfile_load__cb(void *_data, char *path) { - struct pack__dirent *data = (struct pack__dirent *)_data; + struct pack_backend *backend = (struct pack_backend *)_data; struct pack_file *pack; int error; + size_t i; if (git__suffixcmp(path, ".idx") != 0) return GIT_SUCCESS; /* not an index */ - /* FIXME: git.git checks for duplicate packs. - * But that makes no fucking sense. Our dirent is not - * going to generate dupicate entries */ + for (i = 0; i < backend->packs.length; ++i) { + struct pack_file *p = git_vector_get(&backend->packs, i); + if (memcmp(p->pack_name, path, strlen(path) - STRLEN(".idx")) == 0) + return GIT_SUCCESS; + } - error = packfile_check(&pack, path, data->is_pack_local); + error = packfile_check(&pack, path); if (error < GIT_SUCCESS) return error; - if (git_vector_insert(&data->backend->packs, pack) < GIT_SUCCESS) { + if (git_vector_insert(&backend->packs, pack) < GIT_SUCCESS) { free(pack); return GIT_ENOMEM; } @@ -863,25 +863,29 @@ static int packfile_load__cb(void *_data, char *path) return GIT_SUCCESS; } -static int packfile_load_all(struct pack_backend *backend, const char *odb_path, int local) +static int packfile_refresh_all(struct pack_backend *backend) { int error; - char path[GIT_PATH_MAX]; - struct pack__dirent data; + struct stat st; - data.backend = backend; - data.is_pack_local = local; - - git__joinpath(path, odb_path, "pack"); - if (gitfo_isdir(path) < GIT_SUCCESS) + if (backend->pack_folder == NULL) return GIT_SUCCESS; - error = gitfo_dirent(path, GIT_PATH_MAX, packfile_load__cb, (void *)&data); - if (error < GIT_SUCCESS) - return error; + if (gitfo_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) + return GIT_ENOTFOUND; - git_vector_sort(&backend->packs); - backend->last_found = git_vector_get(&backend->packs, 0); + if (st.st_mtime != backend->pack_folder_mtime) { + char path[GIT_PATH_MAX]; + strcpy(path, backend->pack_folder); + + /* reload all packs */ + error = gitfo_dirent(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend); + if (error < GIT_SUCCESS) + return error; + + git_vector_sort(&backend->packs); + backend->pack_folder_mtime = st.st_mtime; + } return GIT_SUCCESS; } @@ -1026,8 +1030,12 @@ static int pack_entry_find1( static int pack_entry_find(struct pack_entry *e, struct pack_backend *backend, const git_oid *oid) { + int error; size_t i; + if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS) + return error; + if (backend->last_found && pack_entry_find1(e, backend->last_found, oid) == GIT_SUCCESS) return GIT_SUCCESS; @@ -1377,13 +1385,14 @@ void pack_backend__free(git_odb_backend *_backend) } git_vector_free(&backend->packs); + free(backend->pack_folder); free(backend); } int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) { - int error; struct pack_backend *backend; + char path[GIT_PATH_MAX]; backend = git__calloc(1, sizeof(struct pack_backend)); if (backend == NULL) @@ -1397,10 +1406,15 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) backend->window_size = DEFAULT_WINDOW_SIZE; backend->mapped_limit = DEFAULT_MAPPED_LIMIT; - error = packfile_load_all(backend, objects_dir, 1); - if (error < GIT_SUCCESS) { - pack_backend__free((git_odb_backend *)backend); - return error; + git__joinpath(path, objects_dir, "pack"); + if (gitfo_isdir(path) == GIT_SUCCESS) { + backend->pack_folder = git__strdup(path); + backend->pack_folder_mtime = 0; + + if (backend->pack_folder == NULL) { + free(backend); + return GIT_ENOMEM; + } } backend->parent.read = &pack_backend__read; diff --git a/src/refs.c b/src/refs.c index 00b9ff6b2..ea968196f 100644 --- a/src/refs.c +++ b/src/refs.c @@ -684,7 +684,7 @@ static int packed_loadloose(git_repository *repository) /* Remove any loose references from the cache */ { - const void *_unused; + const void *GIT_UNUSED(_unused); git_reference *reference; GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference, @@ -787,6 +787,8 @@ static int packed_find_peel(reference_oid *ref) */ } + git_object_close(object); + return GIT_SUCCESS; } @@ -868,7 +870,7 @@ static int packed_write(git_repository *repo) /* Load all the packfile into a vector */ { git_reference *reference; - const void *_unused; + const void *GIT_UNUSED(_unused); GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference, git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */ @@ -1480,8 +1482,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) for (i = 0; i < MAX_NESTING_LEVEL; ++i) { reference_symbolic *ref_sym; + *resolved_ref = ref; + if (ref->type & GIT_REF_OID) { - *resolved_ref = ref; return GIT_SUCCESS; } @@ -1518,7 +1521,7 @@ int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*ca /* list all the packed references first */ if (list_flags & GIT_REF_PACKED) { const char *ref_name; - void *_unused; + void *GIT_UNUSED(_unused); if ((error = packed_load(repo)) < GIT_SUCCESS) return error; @@ -1597,7 +1600,7 @@ int git_repository__refcache_init(git_refcache *refs) void git_repository__refcache_free(git_refcache *refs) { git_reference *reference; - const void *_unused; + const void *GIT_UNUSED(_unused); assert(refs); @@ -1692,8 +1695,9 @@ static int normalize_name(char *buffer_out, const char *name, int is_oid_ref) } /* Object id refname have to contain at least one slash, except - * for HEAD in a detached state */ - if (is_oid_ref && !contains_a_slash && strcmp(name, GIT_HEAD_FILE)) + * for HEAD in a detached state or MERGE_HEAD if we're in the + * middle of a merge */ + if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE))) return GIT_EINVALIDREFNAME; /* A refname can not end with ".lock" */ diff --git a/src/refs.h b/src/refs.h index bebb1b97d..b8f3e2f6d 100644 --- a/src/refs.h +++ b/src/refs.h @@ -17,6 +17,7 @@ #define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100 #define GIT_HEAD_FILE "HEAD" +#define GIT_MERGE_HEAD_FILE "MERGE_HEAD" #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master" struct git_reference { diff --git a/src/repository.c b/src/repository.c index c428b00af..8cc2644ca 100644 --- a/src/repository.c +++ b/src/repository.c @@ -58,7 +58,6 @@ static int assign_repository_dirs( const char *git_work_tree) { char path_aux[GIT_PATH_MAX]; - size_t git_dir_path_len; int error = GIT_SUCCESS; assert(repo); @@ -70,8 +69,6 @@ static int assign_repository_dirs( if (error < GIT_SUCCESS) return error; - git_dir_path_len = strlen(path_aux); - /* store GIT_DIR */ repo->path_repository = git__strdup(path_aux); if (repo->path_repository == NULL) diff --git a/src/revwalk.c b/src/revwalk.c index b62b09961..78798480f 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -483,7 +483,7 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) void git_revwalk_free(git_revwalk *walk) { unsigned int i; - const void *_unused; + const void *GIT_UNUSED(_unused); commit_object *commit; if (walk == NULL) @@ -558,7 +558,7 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) void git_revwalk_reset(git_revwalk *walk) { - const void *_unused; + const void *GIT_UNUSED(_unused); commit_object *commit; assert(walk); diff --git a/src/tag.c b/src/tag.c index a3400957f..148eb280c 100644 --- a/src/tag.c +++ b/src/tag.c @@ -377,3 +377,29 @@ int git_tag__parse(git_tag *tag, git_odb_object *obj) return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len); } +static int tag_list_cb(const char *tag_name, void *payload) +{ + if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) == 0) + return git_vector_insert((git_vector *)payload, git__strdup(tag_name)); + + return GIT_SUCCESS; +} + +int git_tag_list(git_strarray *tag_names, git_repository *repo) +{ + int error; + git_vector taglist; + + if (git_vector_init(&taglist, 8, NULL) < GIT_SUCCESS) + return GIT_ENOMEM; + + error = git_reference_listcb(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&taglist); + if (error < GIT_SUCCESS) { + git_vector_free(&taglist); + return error; + } + + tag_names->strings = (char **)taglist.contents; + tag_names->count = taglist.length; + return GIT_SUCCESS; +} diff --git a/src/tree.c b/src/tree.c index 64f81d780..b7daf39c4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -424,7 +424,8 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b if (entry->removed) continue; - size += (entry->attr > 0x7FF) ? 7 : 6; + snprintf(filemode, sizeof(filemode), "%o ", entry->attr); + size += strlen(filemode); size += entry->filename_len + 1; size += GIT_OID_RAWSZ; } diff --git a/tests/t04-commit.c b/tests/t04-commit.c index bcc0417c8..36f3e66b5 100644 --- a/tests/t04-commit.c +++ b/tests/t04-commit.c @@ -368,7 +368,7 @@ BEGIN_TEST(details0, "query the details on a parsed commit") const char *message, *message_short; git_time_t commit_time; unsigned int parents, p; - git_commit *parent; + git_commit *parent = NULL, *old_parent = NULL; git_oid_mkstr(&id, commit_ids[i]); @@ -390,11 +390,19 @@ BEGIN_TEST(details0, "query the details on a parsed commit") must_be_true(commit_time > 0); must_be_true(parents <= 2); for (p = 0;p < parents;p++) { + if (old_parent != NULL) + git_commit_close(old_parent); + + old_parent = parent; must_pass(git_commit_parent(&parent, commit, p)); must_be_true(parent != NULL); must_be_true(git_commit_author(parent) != NULL); // is it really a commit? } + git_commit_close(old_parent); + git_commit_close(parent); + must_fail(git_commit_parent(&parent, commit, parents)); + git_commit_close(commit); } git_repository_free(repo); @@ -462,9 +470,76 @@ BEGIN_TEST(write0, "write a new commit object from memory to disk") must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit)); + git_commit_close(commit); git_repository_free(repo); END_TEST +#define ROOT_COMMIT_MESSAGE "This is a root commit\n\ +This is a root commit and should be the only one in this branch\n" + +BEGIN_TEST(root0, "create a root commit") + git_repository *repo; + git_commit *commit; + git_oid tree_id, commit_id; + const git_oid *branch_oid; + const git_signature *author, *committer; + const char *branch_name = "refs/heads/root-commit-branch"; + git_reference *head, *branch; + char *head_old; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + + git_oid_mkstr(&tree_id, tree_oid); + + /* create signatures */ + committer = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60); + must_be_true(committer != NULL); + + author = git_signature_new(COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90); + must_be_true(author != NULL); + + /* First we need to update HEAD so it points to our non-existant branch */ + must_pass(git_reference_lookup(&head, repo, "HEAD")); + must_be_true(git_reference_type(head) == GIT_REF_SYMBOLIC); + head_old = git__strdup(git_reference_target(head)); + must_be_true(head_old != NULL); + + must_pass(git_reference_set_target(head, branch_name)); + + must_pass(git_commit_create_v( + &commit_id, /* out id */ + repo, + "HEAD", + author, + committer, + ROOT_COMMIT_MESSAGE, + &tree_id, + 0)); + + git_signature_free((git_signature *)committer); + git_signature_free((git_signature *)author); + + /* + * The fact that creating a commit works has already been + * tested. Here we just make sure it's our commit and that it was + * written as a root commit. + */ + must_pass(git_commit_lookup(&commit, repo, &commit_id)); + must_be_true(git_commit_parentcount(commit) == 0); + must_pass(git_reference_lookup(&branch, repo, branch_name)); + branch_oid = git_reference_oid(branch); + must_pass(git_oid_cmp(branch_oid, &commit_id)); + must_be_true(!strcmp(git_commit_message(commit), ROOT_COMMIT_MESSAGE)); + + /* Remove the data we just added to the repo */ + must_pass(git_reference_lookup(&head, repo, "HEAD")); + must_pass(git_reference_set_target(head, head_old)); + must_pass(git_reference_delete(branch)); + must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit)); + free(head_old); + git_commit_close(commit); + git_repository_free(repo); +END_TEST BEGIN_SUITE(commit) ADD_TEST(parse0); @@ -474,4 +549,6 @@ BEGIN_SUITE(commit) ADD_TEST(write0); //ADD_TEST(write1); + + ADD_TEST(root0); END_SUITE diff --git a/tests/t07-hashtable.c b/tests/t07-hashtable.c index 597136965..0b362cafd 100644 --- a/tests/t07-hashtable.c +++ b/tests/t07-hashtable.c @@ -155,7 +155,7 @@ BEGIN_TEST(tableit0, "iterate through all the contents of the table") const int objects_n = 32; int i; table_item *objects, *ob; - const void *_unused; + const void *GIT_UNUSED(_unused); git_hashtable *table = NULL; diff --git a/tests/t08-tag.c b/tests/t08-tag.c index de67fdd93..fae2e99db 100644 --- a/tests/t08-tag.c +++ b/tests/t08-tag.c @@ -64,6 +64,19 @@ BEGIN_TEST(read0, "read and parse a tag from the repository") git_repository_free(repo); END_TEST +BEGIN_TEST(read1, "list all tag names from the repository") + git_repository *repo; + git_strarray tag_list; + + must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_tag_list(&tag_list, repo)); + + must_be_true(tag_list.count == 3); + + git_strarray_free(&tag_list); + git_repository_free(repo); +END_TEST + #define TAGGER_NAME "Vicent Marti" #define TAGGER_EMAIL "vicent@github.com" @@ -227,6 +240,7 @@ END_TEST BEGIN_SUITE(tag) ADD_TEST(read0); + ADD_TEST(read1); ADD_TEST(write0); ADD_TEST(write1); ADD_TEST(write2); diff --git a/tests/t09-tree.c b/tests/t09-tree.c index bd88642fa..af992fdb3 100644 --- a/tests/t09-tree.c +++ b/tests/t09-tree.c @@ -32,6 +32,7 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92"; static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7"; static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1"; +static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488"; #if 0 static int print_tree(git_repository *repo, const git_oid *tree_oid, int depth) @@ -82,6 +83,7 @@ BEGIN_TEST(read0, "acces randomly the entries on a loaded tree") must_be_true(git_tree_entry_byindex(tree, 3) == NULL); must_be_true(git_tree_entry_byindex(tree, -1) == NULL); + git_tree_close(tree); git_repository_free(repo); END_TEST @@ -102,7 +104,9 @@ BEGIN_TEST(read1, "read a tree from the repository") /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */ must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_TREE) == 0); + git_object_close(obj); must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_BLOB) == GIT_EINVALIDTYPE); + git_object_close(obj); entry = git_tree_entry_byname(tree, "README"); must_be_true(entry != NULL); @@ -111,6 +115,8 @@ BEGIN_TEST(read1, "read a tree from the repository") must_pass(git_tree_entry_2object(&obj, repo, entry)); + git_object_close(obj); + git_tree_close(tree); git_repository_free(repo); END_TEST @@ -148,9 +154,50 @@ BEGIN_TEST(write2, "write a tree from a memory") must_pass(git_treebuilder_write(&rid,repo,builder)); must_be_true(git_oid_cmp(&rid, &id2) == 0); + + git_treebuilder_free(builder); + git_tree_close(tree); close_temp_repo(repo); END_TEST +BEGIN_TEST(write3, "write a hierarchical tree from a memory") + git_repository *repo; + git_treebuilder *builder; + git_tree *tree; + git_oid id, bid, subtree_id, id2, id3; + git_oid id_hiearar; + + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); + git_oid_mkstr(&id, first_tree); + git_oid_mkstr(&id2, second_tree); + git_oid_mkstr(&id3, third_tree); + git_oid_mkstr(&bid, blob_oid); + + //create subtree + must_pass(git_treebuilder_create(&builder, NULL)); + must_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644)); + must_pass(git_treebuilder_write(&subtree_id,repo,builder)); + git_treebuilder_free(builder); + + // create parent tree + must_pass(git_tree_lookup(&tree, repo, &id)); + must_pass(git_treebuilder_create(&builder, tree)); + must_pass(git_treebuilder_insert(NULL,builder,"new",&subtree_id,040000)); + must_pass(git_treebuilder_write(&id_hiearar,repo,builder)); + git_treebuilder_free(builder); + git_tree_close(tree); + + must_be_true(git_oid_cmp(&id_hiearar, &id3) == 0); + + // check data is correct + must_pass(git_tree_lookup(&tree, repo, &id_hiearar)); + must_be_true(2 == git_tree_entrycount(tree)); + git_tree_close(tree); + + close_temp_repo(repo); + +END_TEST + BEGIN_SUITE(tree) //ADD_TEST(print0); ADD_TEST(read0); @@ -158,5 +205,6 @@ BEGIN_SUITE(tree) //ADD_TEST(write0); //ADD_TEST(write1); ADD_TEST(write2); + ADD_TEST(write3); END_SUITE diff --git a/tests/t10-refs.c b/tests/t10-refs.c index a6a560193..db767a107 100644 --- a/tests/t10-refs.c +++ b/tests/t10-refs.c @@ -51,6 +51,7 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference") git__joinpath(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)); must_be_true(strcmp(ref_name_from_tag_name, loose_tag_ref_name) == 0); + git_object_close(object); git_repository_free(repo); END_TEST @@ -91,6 +92,7 @@ BEGIN_TEST(readsym0, "lookup a symbolic reference") git_oid_mkstr(&id, current_master_tip); must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0); + git_object_close(object); git_repository_free(repo); END_TEST @@ -117,6 +119,7 @@ BEGIN_TEST(readsym1, "lookup a nested symbolic reference") git_oid_mkstr(&id, current_master_tip); must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0); + git_object_close(object); git_repository_free(repo); END_TEST @@ -175,6 +178,7 @@ BEGIN_TEST(readpacked0, "lookup a packed reference") must_be_true(object != NULL); must_be_true(git_object_type(object) == GIT_OBJ_COMMIT); + git_object_close(object); git_repository_free(repo); END_TEST @@ -194,7 +198,7 @@ END_TEST BEGIN_TEST(create0, "create a new symbolic reference") git_reference *new_reference, *looked_up_ref, *resolved_ref; - git_repository *repo; + git_repository *repo, *repo2; git_oid id; char ref_path[GIT_PATH_MAX]; @@ -202,7 +206,7 @@ BEGIN_TEST(create0, "create a new symbolic reference") git_oid_mkstr(&id, current_master_tip); - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Retrieve the physical path to the symbolic ref for further cleaning */ git__joinpath(ref_path, repo->path_repository, new_head_tracker); @@ -226,14 +230,13 @@ BEGIN_TEST(create0, "create a new symbolic reference") git_repository_free(repo); /* Similar test with a fresh new repository */ - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER)); - must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker)); + must_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker)); must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); - git_reference_delete(looked_up_ref); - git_repository_free(repo); + close_temp_repo(repo2); END_TEST BEGIN_TEST(create1, "create a deep symbolic reference") @@ -246,7 +249,7 @@ BEGIN_TEST(create1, "create a deep symbolic reference") git_oid_mkstr(&id, current_master_tip); - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); git__joinpath(ref_path, repo->path_repository, new_head_tracker); must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target)); @@ -254,13 +257,12 @@ BEGIN_TEST(create1, "create a deep symbolic reference") must_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0); - git_reference_delete(looked_up_ref); - git_repository_free(repo); + close_temp_repo(repo); END_TEST BEGIN_TEST(create2, "create a new OID reference") git_reference *new_reference, *looked_up_ref; - git_repository *repo; + git_repository *repo, *repo2; git_oid id; char ref_path[GIT_PATH_MAX]; @@ -268,7 +270,7 @@ BEGIN_TEST(create2, "create a new OID reference") git_oid_mkstr(&id, current_master_tip); - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* Retrieve the physical path to the symbolic ref for further cleaning */ git__joinpath(ref_path, repo->path_repository, new_head); @@ -288,13 +290,12 @@ BEGIN_TEST(create2, "create a new OID reference") git_repository_free(repo); /* Similar test with a fresh new repository */ - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER)); must_pass(git_reference_lookup(&looked_up_ref, repo, new_head)); must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0); - git_reference_delete(looked_up_ref); - git_repository_free(repo); + close_temp_repo(repo2); END_TEST BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id") @@ -325,7 +326,7 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference") git_reference *ref, *branch_ref; git_repository *repo; - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); /* The target needds to exist and we need to check the name has changed */ must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name)); @@ -344,9 +345,7 @@ BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference") must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC); must_be_true(!strcmp(git_reference_target(ref), ref_master_name)); - must_pass(git_reference_delete(ref)); - must_pass(git_reference_delete(branch_ref)); - git_repository_free(repo); + close_temp_repo(repo); END_TEST BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") @@ -354,7 +353,7 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") git_repository *repo; git_oid id; - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); must_be_true(ref->type & GIT_REF_OID); @@ -375,8 +374,7 @@ BEGIN_TEST(overwrite1, "Overwrite an existing object id reference") must_pass(git_reference_lookup(&ref, repo, ref_name)); must_be_true(!git_oid_cmp(&id, git_reference_oid(ref))); - git_reference_delete(ref); - git_repository_free(repo); + close_temp_repo(repo); END_TEST BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symbolic one") @@ -384,7 +382,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli git_repository *repo; git_oid id; - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); must_be_true(ref->type & GIT_REF_OID); @@ -399,8 +397,7 @@ BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symboli must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC); must_be_true(!strcmp(git_reference_target(ref), ref_master_name)); - git_reference_delete(ref); - git_repository_free(repo); + close_temp_repo(repo); END_TEST BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object id one") @@ -408,7 +405,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object git_repository *repo; git_oid id; - must_pass(git_repository_open(&repo, REPOSITORY_FOLDER)); + must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER)); must_pass(git_reference_lookup(&ref, repo, ref_master_name)); must_be_true(ref->type & GIT_REF_OID); @@ -425,8 +422,7 @@ BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object must_be_true(git_reference_type(ref) & GIT_REF_OID); must_be_true(!git_oid_cmp(git_reference_oid(ref), &id)); - git_reference_delete(ref); - git_repository_free(repo); + close_temp_repo(repo); END_TEST BEGIN_TEST(pack0, "create a packfile for an empty folder") diff --git a/tests/test_lib.c b/tests/test_lib.c index c9c6141c6..5778404c1 100755 --- a/tests/test_lib.c +++ b/tests/test_lib.c @@ -130,6 +130,7 @@ static void free_suite(git_testsuite *ts) if (ts->list[n]) test_free(ts->list[n]); + free(ts->name); free(ts); } diff --git a/wscript b/wscript index f0b81a023..f4f8da989 100644 --- a/wscript +++ b/wscript @@ -4,7 +4,7 @@ from waflib.Build import BuildContext, CleanContext, \ InstallContext, UninstallContext # Unix flags -CFLAGS_UNIX = ["-O2", "-Wall", "-Wextra"] +CFLAGS_UNIX = ["-O2", "-Wall", "-Wextra", "-fPIC"] CFLAGS_UNIX_DBG = ['-g', '-O0'] # Windows MSVC flags @@ -65,6 +65,9 @@ def configure(conf): else: conf.env.PLATFORM = 'unix' + if conf.env.DEST_OS == 'sunos': + conf.env.DEFINES += ['NO_VIZ'] + if conf.options.threadsafe: if conf.env.PLATFORM == 'unix': conf.check_cc(lib='pthread', uselib_store='pthread')