diff options
Diffstat (limited to 'src/refs.c')
-rw-r--r-- | src/refs.c | 1395 |
1 files changed, 0 insertions, 1395 deletions
diff --git a/src/refs.c b/src/refs.c deleted file mode 100644 index 8acfa84a5..000000000 --- a/src/refs.c +++ /dev/null @@ -1,1395 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "refs.h" - -#include "hash.h" -#include "repository.h" -#include "futils.h" -#include "filebuf.h" -#include "pack.h" -#include "reflog.h" -#include "refdb.h" - -#include <git2/tag.h> -#include <git2/object.h> -#include <git2/oid.h> -#include <git2/branch.h> -#include <git2/refs.h> -#include <git2/refdb.h> -#include <git2/sys/refs.h> -#include <git2/signature.h> -#include <git2/commit.h> - -bool git_reference__enable_symbolic_ref_target_validation = true; - -enum { - GIT_PACKREF_HAS_PEEL = 1, - GIT_PACKREF_WAS_LOOSE = 2 -}; - -static git_reference *alloc_ref(const char *name) -{ - git_reference *ref = NULL; - size_t namelen = strlen(name), reflen; - - if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) && - !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) && - (ref = git__calloc(1, reflen)) != NULL) - memcpy(ref->name, name, namelen + 1); - - return ref; -} - -git_reference *git_reference__alloc_symbolic( - const char *name, const char *target) -{ - git_reference *ref; - - GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(target, NULL); - - ref = alloc_ref(name); - if (!ref) - return NULL; - - ref->type = GIT_REFERENCE_SYMBOLIC; - - if ((ref->target.symbolic = git__strdup(target)) == NULL) { - git__free(ref); - return NULL; - } - - return ref; -} - -git_reference *git_reference__alloc( - const char *name, - const git_oid *oid, - const git_oid *peel) -{ - git_reference *ref; - - GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(oid, NULL); - - ref = alloc_ref(name); - if (!ref) - return NULL; - - ref->type = GIT_REFERENCE_DIRECT; - git_oid_cpy(&ref->target.oid, oid); - - if (peel != NULL) - git_oid_cpy(&ref->peel, peel); - - return ref; -} - -git_reference *git_reference__realloc( - git_reference **ptr_to_ref, const char *name) -{ - size_t namelen, reflen; - git_reference *rewrite = NULL; - - GIT_ASSERT_ARG_WITH_RETVAL(ptr_to_ref, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(name, NULL); - - namelen = strlen(name); - - if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) && - !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) && - (rewrite = git__realloc(*ptr_to_ref, reflen)) != NULL) - memcpy(rewrite->name, name, namelen + 1); - - *ptr_to_ref = NULL; - - return rewrite; -} - -int git_reference_dup(git_reference **dest, git_reference *source) -{ - if (source->type == GIT_REFERENCE_SYMBOLIC) - *dest = git_reference__alloc_symbolic(source->name, source->target.symbolic); - else - *dest = git_reference__alloc(source->name, &source->target.oid, &source->peel); - - GIT_ERROR_CHECK_ALLOC(*dest); - - (*dest)->db = source->db; - GIT_REFCOUNT_INC((*dest)->db); - - return 0; -} - -void git_reference_free(git_reference *reference) -{ - if (reference == NULL) - return; - - if (reference->type == GIT_REFERENCE_SYMBOLIC) - git__free(reference->target.symbolic); - - if (reference->db) - GIT_REFCOUNT_DEC(reference->db, git_refdb__free); - - git__free(reference); -} - -int git_reference_delete(git_reference *ref) -{ - const git_oid *old_id = NULL; - const char *old_target = NULL; - - if (!strcmp(ref->name, "HEAD")) { - git_error_set(GIT_ERROR_REFERENCE, "cannot delete HEAD"); - return GIT_ERROR; - } - - if (ref->type == GIT_REFERENCE_DIRECT) - old_id = &ref->target.oid; - else - old_target = ref->target.symbolic; - - return git_refdb_delete(ref->db, ref->name, old_id, old_target); -} - -int git_reference_remove(git_repository *repo, const char *name) -{ - git_refdb *db; - int error; - - if ((error = git_repository_refdb__weakptr(&db, repo)) < 0) - return error; - - return git_refdb_delete(db, name, NULL, NULL); -} - -int git_reference_lookup(git_reference **ref_out, - git_repository *repo, const char *name) -{ - return git_reference_lookup_resolved(ref_out, repo, name, 0); -} - -int git_reference_name_to_id( - git_oid *out, git_repository *repo, const char *name) -{ - int error; - git_reference *ref; - - if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0) - return error; - - git_oid_cpy(out, git_reference_target(ref)); - git_reference_free(ref); - return 0; -} - -static int reference_normalize_for_repo( - git_refname_t out, - git_repository *repo, - const char *name, - bool validate) -{ - int precompose; - unsigned int flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL; - - if (!git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) && - precompose) - flags |= GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE; - - if (!validate) - flags |= GIT_REFERENCE_FORMAT__VALIDATION_DISABLE; - - return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags); -} - -int git_reference_lookup_resolved( - git_reference **ref_out, - git_repository *repo, - const char *name, - int max_nesting) -{ - git_refname_t normalized; - git_refdb *refdb; - int error = 0; - - GIT_ASSERT_ARG(ref_out); - GIT_ASSERT_ARG(repo); - GIT_ASSERT_ARG(name); - - if ((error = reference_normalize_for_repo(normalized, repo, name, true)) < 0 || - (error = git_repository_refdb__weakptr(&refdb, repo)) < 0 || - (error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0) - return error; - - /* - * The resolved reference may be a symbolic reference in case its - * target doesn't exist. If the user asked us to resolve (e.g. - * `max_nesting != 0`), then we need to return an error in case we got - * a symbolic reference back. - */ - if (max_nesting && git_reference_type(*ref_out) == GIT_REFERENCE_SYMBOLIC) { - git_reference_free(*ref_out); - *ref_out = NULL; - return GIT_ENOTFOUND; - } - - return 0; -} - -int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname) -{ - int error = 0, i, valid; - bool fallbackmode = true, foundvalid = false; - git_reference *ref; - git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT; - - static const char *formatters[] = { - "%s", - GIT_REFS_DIR "%s", - GIT_REFS_TAGS_DIR "%s", - GIT_REFS_HEADS_DIR "%s", - GIT_REFS_REMOTES_DIR "%s", - GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE, - NULL - }; - - if (*refname) - git_buf_puts(&name, refname); - else { - git_buf_puts(&name, GIT_HEAD_FILE); - fallbackmode = false; - } - - for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) { - - git_buf_clear(&refnamebuf); - - if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0 || - (error = git_reference_name_is_valid(&valid, git_buf_cstr(&refnamebuf))) < 0) - goto cleanup; - - if (!valid) { - error = GIT_EINVALIDSPEC; - continue; - } - foundvalid = true; - - error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1); - - if (!error) { - *out = ref; - error = 0; - goto cleanup; - } - - if (error != GIT_ENOTFOUND) - goto cleanup; - } - -cleanup: - if (error && !foundvalid) { - /* never found a valid reference name */ - git_error_set(GIT_ERROR_REFERENCE, - "could not use '%s' as valid reference name", git_buf_cstr(&name)); - } - - if (error == GIT_ENOTFOUND) - git_error_set(GIT_ERROR_REFERENCE, "no reference found for shorthand '%s'", refname); - - git_buf_dispose(&name); - git_buf_dispose(&refnamebuf); - return error; -} - -/** - * Getters - */ -git_reference_t git_reference_type(const git_reference *ref) -{ - GIT_ASSERT_ARG(ref); - return ref->type; -} - -const char *git_reference_name(const git_reference *ref) -{ - GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); - return ref->name; -} - -git_repository *git_reference_owner(const git_reference *ref) -{ - GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); - return ref->db->repo; -} - -const git_oid *git_reference_target(const git_reference *ref) -{ - GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); - - if (ref->type != GIT_REFERENCE_DIRECT) - return NULL; - - return &ref->target.oid; -} - -const git_oid *git_reference_target_peel(const git_reference *ref) -{ - GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); - - if (ref->type != GIT_REFERENCE_DIRECT || git_oid_is_zero(&ref->peel)) - return NULL; - - return &ref->peel; -} - -const char *git_reference_symbolic_target(const git_reference *ref) -{ - GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL); - - if (ref->type != GIT_REFERENCE_SYMBOLIC) - return NULL; - - return ref->target.symbolic; -} - -static int reference__create( - git_reference **ref_out, - git_repository *repo, - const char *name, - const git_oid *oid, - const char *symbolic, - int force, - const git_signature *signature, - const char *log_message, - const git_oid *old_id, - const char *old_target) -{ - git_refname_t normalized; - git_refdb *refdb; - git_reference *ref = NULL; - int error = 0; - - GIT_ASSERT_ARG(repo); - GIT_ASSERT_ARG(name); - GIT_ASSERT_ARG(symbolic || signature); - - if (ref_out) - *ref_out = NULL; - - error = reference_normalize_for_repo(normalized, repo, name, true); - if (error < 0) - return error; - - error = git_repository_refdb__weakptr(&refdb, repo); - if (error < 0) - return error; - - if (oid != NULL) { - GIT_ASSERT(symbolic == NULL); - - if (!git_object__is_valid(repo, oid, GIT_OBJECT_ANY)) { - git_error_set(GIT_ERROR_REFERENCE, - "target OID for the reference doesn't exist on the repository"); - return -1; - } - - ref = git_reference__alloc(normalized, oid, NULL); - } else { - git_refname_t normalized_target; - - error = reference_normalize_for_repo(normalized_target, repo, - symbolic, git_reference__enable_symbolic_ref_target_validation); - - if (error < 0) - return error; - - ref = git_reference__alloc_symbolic(normalized, normalized_target); - } - - GIT_ERROR_CHECK_ALLOC(ref); - - if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) { - git_reference_free(ref); - return error; - } - - if (ref_out == NULL) - git_reference_free(ref); - else - *ref_out = ref; - - return 0; -} - -static int refs_configured_ident(git_signature **out, const git_repository *repo) -{ - if (repo->ident_name && repo->ident_email) - return git_signature_now(out, repo->ident_name, repo->ident_email); - - /* if not configured let us fall-through to the next method */ - return -1; -} - -int git_reference__log_signature(git_signature **out, git_repository *repo) -{ - int error; - git_signature *who; - - if(((error = refs_configured_ident(&who, repo)) < 0) && - ((error = git_signature_default(&who, repo)) < 0) && - ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) - return error; - - *out = who; - return 0; -} - -int git_reference_create_matching( - git_reference **ref_out, - git_repository *repo, - const char *name, - const git_oid *id, - int force, - const git_oid *old_id, - const char *log_message) - -{ - int error; - git_signature *who = NULL; - - GIT_ASSERT_ARG(id); - - if ((error = git_reference__log_signature(&who, repo)) < 0) - return error; - - error = reference__create( - ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL); - - git_signature_free(who); - return error; -} - -int git_reference_create( - git_reference **ref_out, - git_repository *repo, - const char *name, - const git_oid *id, - int force, - const char *log_message) -{ - return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message); -} - -int git_reference_symbolic_create_matching( - git_reference **ref_out, - git_repository *repo, - const char *name, - const char *target, - int force, - const char *old_target, - const char *log_message) -{ - int error; - git_signature *who = NULL; - - GIT_ASSERT_ARG(target); - - if ((error = git_reference__log_signature(&who, repo)) < 0) - return error; - - error = reference__create( - ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target); - - git_signature_free(who); - return error; -} - -int git_reference_symbolic_create( - git_reference **ref_out, - git_repository *repo, - const char *name, - const char *target, - int force, - const char *log_message) -{ - return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message); -} - -static int ensure_is_an_updatable_direct_reference(git_reference *ref) -{ - if (ref->type == GIT_REFERENCE_DIRECT) - return 0; - - git_error_set(GIT_ERROR_REFERENCE, "cannot set OID on symbolic reference"); - return -1; -} - -int git_reference_set_target( - git_reference **out, - git_reference *ref, - const git_oid *id, - const char *log_message) -{ - int error; - git_repository *repo; - - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(ref); - GIT_ASSERT_ARG(id); - - repo = ref->db->repo; - - if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) - return error; - - return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message); -} - -static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) -{ - if (ref->type == GIT_REFERENCE_SYMBOLIC) - return 0; - - git_error_set(GIT_ERROR_REFERENCE, "cannot set symbolic target on a direct reference"); - return -1; -} - -int git_reference_symbolic_set_target( - git_reference **out, - git_reference *ref, - const char *target, - const char *log_message) -{ - int error; - - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(ref); - GIT_ASSERT_ARG(target); - - if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) - return error; - - return git_reference_symbolic_create_matching( - out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message); -} - -typedef struct { - const char *old_name; - git_refname_t new_name; -} refs_update_head_payload; - -static int refs_update_head(git_repository *worktree, void *_payload) -{ - refs_update_head_payload *payload = (refs_update_head_payload *)_payload; - git_reference *head = NULL, *updated = NULL; - int error; - - if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) - goto out; - - if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC || - git__strcmp(git_reference_symbolic_target(head), payload->old_name) != 0) - goto out; - - /* Update HEAD if it was pointing to the reference being renamed */ - if ((error = git_reference_symbolic_set_target(&updated, head, payload->new_name, NULL)) < 0) { - git_error_set(GIT_ERROR_REFERENCE, "failed to update HEAD after renaming reference"); - goto out; - } - -out: - git_reference_free(updated); - git_reference_free(head); - return error; -} - -int git_reference_rename( - git_reference **out, - git_reference *ref, - const char *new_name, - int force, - const char *log_message) -{ - refs_update_head_payload payload; - git_signature *signature = NULL; - git_repository *repo; - int error; - - GIT_ASSERT_ARG(out); - GIT_ASSERT_ARG(ref); - - repo = git_reference_owner(ref); - - if ((error = git_reference__log_signature(&signature, repo)) < 0 || - (error = reference_normalize_for_repo(payload.new_name, repo, new_name, true)) < 0 || - (error = git_refdb_rename(out, ref->db, ref->name, payload.new_name, force, signature, log_message)) < 0) - goto out; - - payload.old_name = ref->name; - - /* We may have to update any HEAD that was pointing to the renamed reference. */ - if ((error = git_repository_foreach_worktree(repo, refs_update_head, &payload)) < 0) - goto out; - -out: - git_signature_free(signature); - return error; -} - -int git_reference_resolve(git_reference **ref_out, const git_reference *ref) -{ - switch (git_reference_type(ref)) { - case GIT_REFERENCE_DIRECT: - return git_reference_lookup(ref_out, ref->db->repo, ref->name); - - case GIT_REFERENCE_SYMBOLIC: - return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1); - - default: - git_error_set(GIT_ERROR_REFERENCE, "invalid reference"); - return -1; - } -} - -int git_reference_foreach( - git_repository *repo, - git_reference_foreach_cb callback, - void *payload) -{ - git_reference_iterator *iter; - git_reference *ref; - int error; - - if ((error = git_reference_iterator_new(&iter, repo)) < 0) - return error; - - while (!(error = git_reference_next(&ref, iter))) { - if ((error = callback(ref, payload)) != 0) { - git_error_set_after_callback(error); - break; - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_reference_iterator_free(iter); - return error; -} - -int git_reference_foreach_name( - git_repository *repo, - git_reference_foreach_name_cb callback, - void *payload) -{ - git_reference_iterator *iter; - const char *refname; - int error; - - if ((error = git_reference_iterator_new(&iter, repo)) < 0) - return error; - - while (!(error = git_reference_next_name(&refname, iter))) { - if ((error = callback(refname, payload)) != 0) { - git_error_set_after_callback(error); - break; - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_reference_iterator_free(iter); - return error; -} - -int git_reference_foreach_glob( - git_repository *repo, - const char *glob, - git_reference_foreach_name_cb callback, - void *payload) -{ - git_reference_iterator *iter; - const char *refname; - int error; - - if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) - return error; - - while (!(error = git_reference_next_name(&refname, iter))) { - if ((error = callback(refname, payload)) != 0) { - git_error_set_after_callback(error); - break; - } - } - - if (error == GIT_ITEROVER) - error = 0; - - git_reference_iterator_free(iter); - return error; -} - -int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo) -{ - git_refdb *refdb; - - if (git_repository_refdb__weakptr(&refdb, repo) < 0) - return -1; - - return git_refdb_iterator(out, refdb, NULL); -} - -int git_reference_iterator_glob_new( - git_reference_iterator **out, git_repository *repo, const char *glob) -{ - git_refdb *refdb; - - if (git_repository_refdb__weakptr(&refdb, repo) < 0) - return -1; - - return git_refdb_iterator(out, refdb, glob); -} - -int git_reference_next(git_reference **out, git_reference_iterator *iter) -{ - return git_refdb_iterator_next(out, iter); -} - -int git_reference_next_name(const char **out, git_reference_iterator *iter) -{ - return git_refdb_iterator_next_name(out, iter); -} - -void git_reference_iterator_free(git_reference_iterator *iter) -{ - if (iter == NULL) - return; - - git_refdb_iterator_free(iter); -} - -static int cb__reflist_add(const char *ref, void *data) -{ - char *name = git__strdup(ref); - GIT_ERROR_CHECK_ALLOC(name); - return git_vector_insert((git_vector *)data, name); -} - -int git_reference_list( - git_strarray *array, - git_repository *repo) -{ - git_vector ref_list; - - GIT_ASSERT_ARG(array); - GIT_ASSERT_ARG(repo); - - array->strings = NULL; - array->count = 0; - - if (git_vector_init(&ref_list, 8, NULL) < 0) - return -1; - - if (git_reference_foreach_name( - repo, &cb__reflist_add, (void *)&ref_list) < 0) { - git_vector_free(&ref_list); - return -1; - } - - array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list); - - return 0; -} - -static int is_valid_ref_char(char ch) -{ - if ((unsigned) ch <= ' ') - return 0; - - switch (ch) { - case '~': - case '^': - case ':': - case '\\': - case '?': - case '[': - return 0; - default: - return 1; - } -} - -static int ensure_segment_validity(const char *name, char may_contain_glob) -{ - const char *current = name; - char prev = '\0'; - const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION); - int segment_len; - - if (*current == '.') - return -1; /* Refname starts with "." */ - - for (current = name; ; current++) { - if (*current == '\0' || *current == '/') - break; - - if (!is_valid_ref_char(*current)) - return -1; /* Illegal character in refname */ - - if (prev == '.' && *current == '.') - return -1; /* Refname contains ".." */ - - if (prev == '@' && *current == '{') - return -1; /* Refname contains "@{" */ - - if (*current == '*') { - if (!may_contain_glob) - return -1; - may_contain_glob = 0; - } - - prev = *current; - } - - segment_len = (int)(current - name); - - /* A refname component can not end with ".lock" */ - if (segment_len >= lock_len && - !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len)) - return -1; - - return segment_len; -} - -static bool is_all_caps_and_underscore(const char *name, size_t len) -{ - size_t i; - char c; - - GIT_ASSERT_ARG(name); - GIT_ASSERT_ARG(len > 0); - - for (i = 0; i < len; i++) - { - c = name[i]; - if ((c < 'A' || c > 'Z') && c != '_') - return false; - } - - if (*name == '_' || name[len - 1] == '_') - return false; - - return true; -} - -/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */ -int git_reference__normalize_name( - git_buf *buf, - const char *name, - unsigned int flags) -{ - const char *current; - int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC; - unsigned int process_flags; - bool normalize = (buf != NULL); - bool validate = (flags & GIT_REFERENCE_FORMAT__VALIDATION_DISABLE) == 0; - -#ifdef GIT_USE_ICONV - git_path_iconv_t ic = GIT_PATH_ICONV_INIT; -#endif - - GIT_ASSERT_ARG(name); - - process_flags = flags; - current = (char *)name; - - if (validate && *current == '/') - goto cleanup; - - if (normalize) - git_buf_clear(buf); - -#ifdef GIT_USE_ICONV - if ((flags & GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE) != 0) { - size_t namelen = strlen(current); - if ((error = git_path_iconv_init_precompose(&ic)) < 0 || - (error = git_path_iconv(&ic, ¤t, &namelen)) < 0) - goto cleanup; - error = GIT_EINVALIDSPEC; - } -#endif - - if (!validate) { - git_buf_sets(buf, current); - - error = git_buf_oom(buf) ? -1 : 0; - goto cleanup; - } - - while (true) { - char may_contain_glob = process_flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN; - - segment_len = ensure_segment_validity(current, may_contain_glob); - if (segment_len < 0) - goto cleanup; - - if (segment_len > 0) { - /* - * There may only be one glob in a pattern, thus we reset - * the pattern-flag in case the current segment has one. - */ - if (memchr(current, '*', segment_len)) - process_flags &= ~GIT_REFERENCE_FORMAT_REFSPEC_PATTERN; - - if (normalize) { - size_t cur_len = git_buf_len(buf); - - git_buf_joinpath(buf, git_buf_cstr(buf), current); - git_buf_truncate(buf, - cur_len + segment_len + (segments_count ? 1 : 0)); - - if (git_buf_oom(buf)) { - error = -1; - goto cleanup; - } - } - - segments_count++; - } - - /* No empty segment is allowed when not normalizing */ - if (segment_len == 0 && !normalize) - goto cleanup; - - if (current[segment_len] == '\0') - break; - - current += segment_len + 1; - } - - /* A refname can not be empty */ - if (segment_len == 0 && segments_count == 0) - goto cleanup; - - /* A refname can not end with "." */ - if (current[segment_len - 1] == '.') - goto cleanup; - - /* A refname can not end with "/" */ - if (current[segment_len - 1] == '/') - goto cleanup; - - if ((segments_count == 1 ) && !(flags & GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) - goto cleanup; - - if ((segments_count == 1 ) && - !(flags & GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND) && - !(is_all_caps_and_underscore(name, (size_t)segment_len) || - ((flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) - goto cleanup; - - if ((segments_count > 1) - && (is_all_caps_and_underscore(name, strchr(name, '/') - name))) - goto cleanup; - - error = 0; - -cleanup: - if (error == GIT_EINVALIDSPEC) - git_error_set( - GIT_ERROR_REFERENCE, - "the given reference name '%s' is not valid", name); - - if (error && normalize) - git_buf_dispose(buf); - -#ifdef GIT_USE_ICONV - git_path_iconv_clear(&ic); -#endif - - return error; -} - -int git_reference_normalize_name( - char *buffer_out, - size_t buffer_size, - const char *name, - unsigned int flags) -{ - git_buf buf = GIT_BUF_INIT; - int error; - - if ((error = git_reference__normalize_name(&buf, name, flags)) < 0) - goto cleanup; - - if (git_buf_len(&buf) > buffer_size - 1) { - git_error_set( - GIT_ERROR_REFERENCE, - "the provided buffer is too short to hold the normalization of '%s'", name); - error = GIT_EBUFS; - goto cleanup; - } - - if ((error = git_buf_copy_cstr(buffer_out, buffer_size, &buf)) < 0) - goto cleanup; - - error = 0; - -cleanup: - git_buf_dispose(&buf); - return error; -} - -#define GIT_REFERENCE_TYPEMASK (GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC) - -int git_reference_cmp( - const git_reference *ref1, - const git_reference *ref2) -{ - git_reference_t type1, type2; - - GIT_ASSERT_ARG(ref1); - GIT_ASSERT_ARG(ref2); - - type1 = git_reference_type(ref1); - type2 = git_reference_type(ref2); - - /* let's put symbolic refs before OIDs */ - if (type1 != type2) - return (type1 == GIT_REFERENCE_SYMBOLIC) ? -1 : 1; - - if (type1 == GIT_REFERENCE_SYMBOLIC) - return strcmp(ref1->target.symbolic, ref2->target.symbolic); - - return git_oid__cmp(&ref1->target.oid, &ref2->target.oid); -} - -/* - * Starting with the reference given by `ref_name`, follows symbolic - * references until a direct reference is found and updated the OID - * on that direct reference to `oid`. - */ -int git_reference__update_terminal( - git_repository *repo, - const char *ref_name, - const git_oid *oid, - const git_signature *sig, - const char *log_message) -{ - git_reference *ref = NULL, *ref2 = NULL; - git_signature *who = NULL; - git_refdb *refdb = NULL; - const git_signature *to_use; - int error = 0; - - if (!sig && (error = git_reference__log_signature(&who, repo)) < 0) - goto out; - - to_use = sig ? sig : who; - - if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) - goto out; - - if ((error = git_refdb_resolve(&ref, refdb, ref_name, -1)) < 0) { - if (error == GIT_ENOTFOUND) { - git_error_clear(); - error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use, - log_message, NULL, NULL); - } - goto out; - } - - /* In case the resolved reference is symbolic, then it's a dangling symref. */ - if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) { - error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use, - log_message, NULL, NULL); - } else { - error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use, - log_message, &ref->target.oid, NULL); - } - -out: - git_reference_free(ref2); - git_reference_free(ref); - git_signature_free(who); - return error; -} - -static const char *commit_type(const git_commit *commit) -{ - unsigned int count = git_commit_parentcount(commit); - - if (count >= 2) - return " (merge)"; - else if (count == 0) - return " (initial)"; - else - return ""; -} - -int git_reference__update_for_commit( - git_repository *repo, - git_reference *ref, - const char *ref_name, - const git_oid *id, - const char *operation) -{ - git_reference *ref_new = NULL; - git_commit *commit = NULL; - git_buf reflog_msg = GIT_BUF_INIT; - const git_signature *who; - int error; - - if ((error = git_commit_lookup(&commit, repo, id)) < 0 || - (error = git_buf_printf(&reflog_msg, "%s%s: %s", - operation ? operation : "commit", - commit_type(commit), - git_commit_summary(commit))) < 0) - goto done; - - who = git_commit_committer(commit); - - if (ref) { - if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) - return error; - - error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who, - git_buf_cstr(&reflog_msg), &ref->target.oid, NULL); - } - else - error = git_reference__update_terminal( - repo, ref_name, id, who, git_buf_cstr(&reflog_msg)); - -done: - git_reference_free(ref_new); - git_buf_dispose(&reflog_msg); - git_commit_free(commit); - return error; -} - -int git_reference_has_log(git_repository *repo, const char *refname) -{ - int error; - git_refdb *refdb; - - GIT_ASSERT_ARG(repo); - GIT_ASSERT_ARG(refname); - - if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) - return error; - - return git_refdb_has_log(refdb, refname); -} - -int git_reference_ensure_log(git_repository *repo, const char *refname) -{ - int error; - git_refdb *refdb; - - GIT_ASSERT_ARG(repo); - GIT_ASSERT_ARG(refname); - - if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) - return error; - - return git_refdb_ensure_log(refdb, refname); -} - -int git_reference__is_branch(const char *ref_name) -{ - return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; -} - -int git_reference_is_branch(const git_reference *ref) -{ - GIT_ASSERT_ARG(ref); - return git_reference__is_branch(ref->name); -} - -int git_reference__is_remote(const char *ref_name) -{ - return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0; -} - -int git_reference_is_remote(const git_reference *ref) -{ - GIT_ASSERT_ARG(ref); - return git_reference__is_remote(ref->name); -} - -int git_reference__is_tag(const char *ref_name) -{ - return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0; -} - -int git_reference_is_tag(const git_reference *ref) -{ - GIT_ASSERT_ARG(ref); - return git_reference__is_tag(ref->name); -} - -int git_reference__is_note(const char *ref_name) -{ - return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0; -} - -int git_reference_is_note(const git_reference *ref) -{ - GIT_ASSERT_ARG(ref); - return git_reference__is_note(ref->name); -} - -static int peel_error(int error, const git_reference *ref, const char *msg) -{ - git_error_set( - GIT_ERROR_INVALID, - "the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg); - return error; -} - -int git_reference_peel( - git_object **peeled, - const git_reference *ref, - git_object_t target_type) -{ - const git_reference *resolved = NULL; - git_reference *allocated = NULL; - git_object *target = NULL; - int error; - - GIT_ASSERT_ARG(ref); - - if (ref->type == GIT_REFERENCE_DIRECT) { - resolved = ref; - } else { - if ((error = git_reference_resolve(&allocated, ref)) < 0) - return peel_error(error, ref, "Cannot resolve reference"); - - resolved = allocated; - } - - /* - * If we try to peel an object to a tag, we cannot use - * the fully peeled object, as that will always resolve - * to a commit. So we only want to use the peeled value - * if it is not zero and the target is not a tag. - */ - if (target_type != GIT_OBJECT_TAG && !git_oid_is_zero(&resolved->peel)) { - error = git_object_lookup(&target, - git_reference_owner(ref), &resolved->peel, GIT_OBJECT_ANY); - } else { - error = git_object_lookup(&target, - git_reference_owner(ref), &resolved->target.oid, GIT_OBJECT_ANY); - } - - if (error < 0) { - peel_error(error, ref, "Cannot retrieve reference target"); - goto cleanup; - } - - if (target_type == GIT_OBJECT_ANY && git_object_type(target) != GIT_OBJECT_TAG) - error = git_object_dup(peeled, target); - else - error = git_object_peel(peeled, target, target_type); - -cleanup: - git_object_free(target); - git_reference_free(allocated); - - return error; -} - -int git_reference__name_is_valid( - int *valid, - const char *refname, - unsigned int flags) -{ - int error; - - GIT_ASSERT(valid && refname); - - *valid = 0; - - error = git_reference__normalize_name(NULL, refname, flags); - - if (!error) - *valid = 1; - else if (error == GIT_EINVALIDSPEC) - error = 0; - - return error; -} - -int git_reference_name_is_valid(int *valid, const char *refname) -{ - return git_reference__name_is_valid(valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL); -} - -const char *git_reference__shorthand(const char *name) -{ - if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) - return name + strlen(GIT_REFS_HEADS_DIR); - else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR)) - return name + strlen(GIT_REFS_TAGS_DIR); - else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR)) - return name + strlen(GIT_REFS_REMOTES_DIR); - else if (!git__prefixcmp(name, GIT_REFS_DIR)) - return name + strlen(GIT_REFS_DIR); - - /* No shorthands are available, so just return the name. */ - return name; -} - -const char *git_reference_shorthand(const git_reference *ref) -{ - return git_reference__shorthand(ref->name); -} - -int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo) -{ - int error; - git_reference *tmp_ref; - - GIT_ASSERT_ARG(unborn); - GIT_ASSERT_ARG(ref); - GIT_ASSERT_ARG(repo); - - if (ref->type == GIT_REFERENCE_DIRECT) { - *unborn = 0; - return 0; - } - - error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1); - git_reference_free(tmp_ref); - - if (error != 0 && error != GIT_ENOTFOUND) - return error; - else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0) - *unborn = true; - else - *unborn = false; - - return 0; -} - -/* Deprecated functions */ - -#ifndef GIT_DEPRECATE_HARD - -int git_reference_is_valid_name(const char *refname) -{ - int valid = 0; - - git_reference__name_is_valid(&valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL); - - return valid; -} - -#endif |