]> git.proxmox.com Git - libgit2.git/blobdiff - src/refs.c
Merge pull request #1203 from phkelley/reverse_dak
[libgit2.git] / src / refs.c
index c602d1b184ea09203f0e661758728295a0874374..70f12b503e4ea9d05f1d5ec85b3419fd092ce644 100644 (file)
@@ -14,6 +14,8 @@
 
 #include <git2/tag.h>
 #include <git2/object.h>
+#include <git2/oid.h>
+#include <git2/branch.h>
 
 GIT__USE_STRMAP;
 
@@ -67,11 +69,6 @@ static int reference_path_available(git_repository *repo,
 static int reference_delete(git_reference *ref);
 static int reference_lookup(git_reference *ref);
 
-/* name normalization */
-static int normalize_name(char *buffer_out, size_t out_size,
-       const char *name, int is_oid_ref);
-
-
 void git_reference_free(git_reference *reference)
 {
        if (reference == NULL)
@@ -126,7 +123,8 @@ static int reference_read(
        if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
                return -1;
 
-       result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
+       result = git_futils_readbuffer_updated(
+               file_content, path.ptr, mtime, NULL, updated);
        git_buf_free(&path);
 
        return result;
@@ -158,11 +156,26 @@ static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
 
 static int loose_parse_oid(git_oid *oid, git_buf *file_content)
 {
-       /* File format: 40 chars (OID) */
-       if (git_buf_len(file_content) == GIT_OID_HEXSZ &&
-               git_oid_fromstr(oid, git_buf_cstr(file_content)) == 0)
+       size_t len;
+       const char *str;
+
+       len = git_buf_len(file_content);
+       if (len < GIT_OID_HEXSZ)
+               goto corrupted;
+
+       /* str is guranteed to be zero-terminated */
+       str = git_buf_cstr(file_content);
+
+       /* we need to get 40 OID characters from the file */
+       if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0)
+               goto corrupted;
+
+       /* If the file is longer than 40 chars, the 41st must be a space */
+       str += GIT_OID_HEXSZ;
+       if (*str == '\0' || git__isspace(*str))
                return 0;
 
+corrupted:
        giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
        return -1;
 }
@@ -199,8 +212,6 @@ static int loose_lookup(git_reference *ref)
        if (!updated)
                return 0;
 
-       git_buf_rtrim(&ref_file);
-
        if (ref->flags & GIT_REF_SYMBOLIC) {
                git__free(ref->target.symbolic);
                ref->target.symbolic = NULL;
@@ -210,6 +221,7 @@ static int loose_lookup(git_reference *ref)
 
        if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
                ref->flags |= GIT_REF_SYMBOLIC;
+               git_buf_rtrim(&ref_file);
                result = loose_parse_symbolic(ref, &ref_file);
        } else {
                ref->flags |= GIT_REF_OID;
@@ -262,17 +274,15 @@ static int loose_write(git_reference *ref)
        git_buf ref_path = GIT_BUF_INIT;
        struct stat st;
 
-       if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
-               return -1;
-
-       /* Remove a possibly existing empty directory hierarchy 
+       /* Remove a possibly existing empty directory hierarchy
         * which name would collide with the reference name
         */
-       if (git_path_isdir(git_buf_cstr(&ref_path)) && 
-               (git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
-                       git_buf_free(&ref_path);
-                       return -1;
-               }
+       if (git_futils_rmdir_r(ref->name, ref->owner->path_repository,
+               GIT_RMDIR_SKIP_NONEMPTY) < 0)
+               return -1;
+
+       if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
+               return -1;
 
        if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
                git_buf_free(&ref_path);
@@ -363,7 +373,7 @@ static int packed_parse_oid(
 
        refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
        if (refname_end == NULL)
-               goto corrupt;
+               refname_end = buffer_end;
 
        if (refname_end[-1] == '\r')
                refname_end--;
@@ -498,6 +508,10 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
                        return 0; /* we are filtering out this reference */
        }
 
+       /* Locked references aren't returned */
+       if (!git__suffixcmp(file_path, GIT_FILELOCK_EXTENSION))
+               return 0;
+
        if (data->callback(file_path, data->callback_payload))
                data->callback_error = GIT_EUSER;
 
@@ -633,7 +647,7 @@ static int packed_find_peel(git_repository *repo, struct packref *ref)
                /*
                 * Find the object pointed at by this tag
                 */
-               git_oid_cpy(&ref->peel, git_tag_target_oid(tag));
+               git_oid_cpy(&ref->peel, git_tag_target_id(tag));
                ref->flags |= GIT_PACKREF_HAS_PEEL;
 
                /*
@@ -1060,7 +1074,7 @@ int git_reference_lookup(git_reference **ref_out,
        return git_reference_lookup_resolved(ref_out, repo, name, 0);
 }
 
-int git_reference_name_to_oid(
+int git_reference_name_to_id(
        git_oid *out, git_repository *repo, const char *name)
 {
        int error;
@@ -1069,7 +1083,7 @@ int git_reference_name_to_oid(
        if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
                return error;
 
-       git_oid_cpy(out, git_reference_oid(ref));
+       git_oid_cpy(out, git_reference_target(ref));
        git_reference_free(ref);
        return 0;
 }
@@ -1098,9 +1112,12 @@ int git_reference_lookup_resolved(
        scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
        GITERR_CHECK_ALLOC(scan->name);
 
-       if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
-               git_reference_free(scan);
-               return result;
+       if ((result = git_reference__normalize_name_lax(
+               scan->name,
+               GIT_REFNAME_MAX,
+               name)) < 0) {
+                       git_reference_free(scan);
+                       return result;
        }
 
        scan->target.symbolic = git__strdup(scan->name);
@@ -1136,7 +1153,7 @@ int git_reference_lookup_resolved(
 /**
  * Getters
  */
-git_ref_t git_reference_type(git_reference *ref)
+git_ref_t git_reference_type(const git_reference *ref)
 {
        assert(ref);
 
@@ -1155,19 +1172,19 @@ int git_reference_is_packed(git_reference *ref)
        return !!(ref->flags & GIT_REF_PACKED);
 }
 
-const char *git_reference_name(git_reference *ref)
+const char *git_reference_name(const git_reference *ref)
 {
        assert(ref);
        return ref->name;
 }
 
-git_repository *git_reference_owner(git_reference *ref)
+git_repository *git_reference_owner(const git_reference *ref)
 {
        assert(ref);
        return ref->owner;
 }
 
-const git_oid *git_reference_oid(git_reference *ref)
+const git_oid *git_reference_target(const git_reference *ref)
 {
        assert(ref);
 
@@ -1177,7 +1194,7 @@ const git_oid *git_reference_oid(git_reference *ref)
        return &ref->target.oid;
 }
 
-const char *git_reference_target(git_reference *ref)
+const char *git_reference_symbolic_target(const git_reference *ref)
 {
        assert(ref);
 
@@ -1187,7 +1204,7 @@ const char *git_reference_target(git_reference *ref)
        return ref->target.symbolic;
 }
 
-int git_reference_create_symbolic(
+int git_reference_symbolic_create(
        git_reference **ref_out,
        git_repository *repo,
        const char *name,
@@ -1196,12 +1213,16 @@ int git_reference_create_symbolic(
 {
        char normalized[GIT_REFNAME_MAX];
        git_reference *ref = NULL;
+       int error;
 
-       if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
-               return -1;
+       if ((error = git_reference__normalize_name_lax(
+               normalized,
+               sizeof(normalized),
+               name)) < 0)
+                       return error;
 
-       if (reference_can_write(repo, normalized, NULL, force) < 0)
-               return -1;
+       if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
+               return error;
 
        if (reference_alloc(&ref, repo, normalized) < 0)
                return -1;
@@ -1210,7 +1231,7 @@ int git_reference_create_symbolic(
 
        /* set the target; this will normalize the name automatically
         * and write the reference on disk */
-       if (git_reference_set_target(ref, target) < 0) {
+       if (git_reference_symbolic_set_target(ref, target) < 0) {
                git_reference_free(ref);
                return -1;
        }
@@ -1223,21 +1244,25 @@ int git_reference_create_symbolic(
        return 0;
 }
 
-int git_reference_create_oid(
+int git_reference_create(
        git_reference **ref_out,
        git_repository *repo,
        const char *name,
        const git_oid *id,
        int force)
 {
+       int error;
        git_reference *ref = NULL;
        char normalized[GIT_REFNAME_MAX];
 
-       if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
-               return -1;
+       if ((error = git_reference__normalize_name_lax(
+               normalized,
+               sizeof(normalized),
+               name)) < 0)
+                       return error;
 
-       if (reference_can_write(repo, normalized, NULL, force) < 0)
-               return -1;
+       if ((error = reference_can_write(repo, normalized, NULL, force)) < 0)
+               return error;
 
        if (reference_alloc(&ref, repo, name) < 0)
                return -1;
@@ -1245,7 +1270,7 @@ int git_reference_create_oid(
        ref->flags |= GIT_REF_OID;
 
        /* set the oid; this will write the reference on disk */
-       if (git_reference_set_oid(ref, id) < 0) {
+       if (git_reference_set_target(ref, id) < 0) {
                git_reference_free(ref);
                return -1;
        }
@@ -1267,7 +1292,7 @@ int git_reference_create_oid(
  * We do not repack packed references because of performance
  * reasons.
  */
-int git_reference_set_oid(git_reference *ref, const git_oid *id)
+int git_reference_set_target(git_reference *ref, const git_oid *id)
 {
        git_odb *odb = NULL;
 
@@ -1303,8 +1328,9 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id)
  * a pack. We just change the target in memory
  * and overwrite the file on disk.
  */
-int git_reference_set_target(git_reference *ref, const char *target)
+int git_reference_symbolic_set_target(git_reference *ref, const char *target)
 {
+       int error;
        char normalized[GIT_REFNAME_MAX];
 
        if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
@@ -1313,8 +1339,11 @@ int git_reference_set_target(git_reference *ref, const char *target)
                return -1;
        }
 
-       if (normalize_name(normalized, sizeof(normalized), target, 0))
-               return -1;
+       if ((error = git_reference__normalize_name_lax(
+               normalized,
+               sizeof(normalized),
+               target)) < 0)
+                       return error;
 
        git__free(ref->target.symbolic);
        ref->target.symbolic = git__strdup(normalized);
@@ -1326,24 +1355,36 @@ int git_reference_set_target(git_reference *ref, const char *target)
 int git_reference_rename(git_reference *ref, const char *new_name, int force)
 {
        int result;
+       unsigned int normalization_flags;
        git_buf aux_path = GIT_BUF_INIT;
        char normalized[GIT_REFNAME_MAX];
+       bool should_head_be_updated = false;
 
-       const char *head_target = NULL;
-       git_reference *head = NULL;
+       normalization_flags = ref->flags & GIT_REF_SYMBOLIC ?
+               GIT_REF_FORMAT_ALLOW_ONELEVEL
+               : GIT_REF_FORMAT_NORMAL;
 
-       if (normalize_name(normalized, sizeof(normalized),
-               new_name, ref->flags & GIT_REF_OID) < 0)
-               return -1;
+       if ((result = git_reference_normalize_name(
+               normalized,
+               sizeof(normalized),
+               new_name,
+               normalization_flags)) < 0)
+                       return result;
 
-       if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
-               return -1;
+       if ((result = reference_can_write(ref->owner, normalized, ref->name, force)) < 0)
+               return result;
 
        /* Initialize path now so we won't get an allocation failure once
         * we actually start removing things. */
        if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
                return -1;
 
+       /*
+        * Check if we have to update HEAD.
+        */
+       if ((should_head_be_updated = git_branch_is_head(ref)) < 0)
+               goto cleanup;
+
        /*
         * Now delete the old ref and remove an possibly existing directory
         * named `new_name`. Note that using the internal `reference_delete`
@@ -1357,10 +1398,10 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
         * Finally we can create the new reference.
         */
        if (ref->flags & GIT_REF_SYMBOLIC) {
-               result = git_reference_create_symbolic(
+               result = git_reference_symbolic_create(
                        NULL, ref->owner, new_name, ref->target.symbolic, force);
        } else {
-               result = git_reference_create_oid(
+               result = git_reference_create(
                        NULL, ref->owner, new_name, &ref->target.oid, force);
        }
 
@@ -1368,25 +1409,13 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
                goto rollback;
 
        /*
-        * Check if we have to update HEAD.
+        * Update HEAD it was poiting to the reference being renamed.
         */
-       if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
-               giterr_set(GITERR_REFERENCE,
-                       "Failed to update HEAD after renaming reference");
-               goto cleanup;
-       }
-
-       head_target = git_reference_target(head);
-
-       if (head_target && !strcmp(head_target, ref->name)) {
-               git_reference_free(head);
-               head = NULL;
-
-               if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
+       if (should_head_be_updated && 
+               git_repository_set_head(ref->owner, new_name) < 0) {
                        giterr_set(GITERR_REFERENCE,
                                "Failed to update HEAD after renaming reference");
                        goto cleanup;
-               }
        }
 
        /*
@@ -1404,12 +1433,10 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
        /* The reference is no longer packed */
        ref->flags &= ~GIT_REF_PACKED;
 
-       git_reference_free(head);
        git_buf_free(&aux_path);
        return 0;
 
 cleanup:
-       git_reference_free(head);
        git_buf_free(&aux_path);
        return -1;
 
@@ -1418,10 +1445,10 @@ rollback:
         * Try to create the old reference again, ignore failures
         */
        if (ref->flags & GIT_REF_SYMBOLIC)
-               git_reference_create_symbolic(
+               git_reference_symbolic_create(
                        NULL, ref->owner, ref->name, ref->target.symbolic, 0);
        else
-               git_reference_create_oid(
+               git_reference_create(
                        NULL, ref->owner, ref->name, &ref->target.oid, 0);
 
        /* The reference is no longer packed */
@@ -1431,7 +1458,7 @@ rollback:
        return -1;
 }
 
-int git_reference_resolve(git_reference **ref_out, git_reference *ref)
+int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
 {
        if (ref->flags & GIT_REF_OID)
                return git_reference_lookup(ref_out, ref->owner, ref->name);
@@ -1452,7 +1479,7 @@ int git_reference_packall(git_repository *repo)
 int git_reference_foreach(
        git_repository *repo,
        unsigned int list_flags,
-       int (*callback)(const char *, void *),
+       git_reference_foreach_cb callback,
        void *payload)
 {
        int result;
@@ -1564,112 +1591,190 @@ static int is_valid_ref_char(char ch)
        }
 }
 
-static int normalize_name(
-       char *buffer_out,
-       size_t out_size,
-       const char *name,
-       int is_oid_ref)
+static int ensure_segment_validity(const char *name)
 {
-       const char *name_end, *buffer_out_start;
-       const char *current;
-       int contains_a_slash = 0;
+       const char *current = name;
+       char prev = '\0';
 
-       assert(name && buffer_out);
+       if (*current == '.')
+               return -1; /* Refname starts with "." */
 
-       buffer_out_start = buffer_out;
-       current = name;
-       name_end = name + strlen(name);
+       for (current = name; ; current++) {
+               if (*current == '\0' || *current == '/')
+                       break;
 
-       /* Terminating null byte */
-       out_size--;
+               if (!is_valid_ref_char(*current))
+                       return -1; /* Illegal character in refname */
 
-       /* A refname can not be empty */
-       if (name_end == name)
-               goto invalid_name;
+               if (prev == '.' && *current == '.')
+                       return -1; /* Refname contains ".." */
 
-       /* A refname can not end with a dot or a slash */
-       if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
-               goto invalid_name;
+               if (prev == '@' && *current == '{')
+                       return -1; /* Refname contains "@{" */
 
-       while (current < name_end && out_size) {
-               if (!is_valid_ref_char(*current))
-                       goto invalid_name;
+               prev = *current;
+       }
 
-               if (buffer_out > buffer_out_start) {
-                       char prev = *(buffer_out - 1);
+       return (int)(current - name);
+}
 
-                       /* A refname can not start with a dot nor contain a double dot */
-                       if (*current == '.' && ((prev == '.') || (prev == '/')))
-                               goto invalid_name;
+static bool is_all_caps_and_underscore(const char *name, size_t len)
+{
+       size_t i;
+       char c;
 
-                       /* '@{' is forbidden within a refname */
-                       if (*current == '{' && prev == '@')
-                               goto invalid_name;
+       assert(name && len > 0);
 
-                       /* Prevent multiple slashes from being added to the output */
-                       if (*current == '/' && prev == '/') {
-                               current++;
-                               continue;
+       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;
+}
+
+int git_reference__normalize_name(
+       git_buf *buf,
+       const char *name,
+       unsigned int flags)
+{
+       // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
+
+       char *current;
+       int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
+       unsigned int process_flags;
+       bool normalize = (buf != NULL);
+       assert(name);
+
+       process_flags = flags;
+       current = (char *)name;
+
+       if (normalize)
+               git_buf_clear(buf);
+
+       while (true) {
+               segment_len = ensure_segment_validity(current);
+               if (segment_len < 0) {
+                       if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
+                                       current[0] == '*' &&
+                                       (current[1] == '\0' || current[1] == '/')) {
+                               /* Accept one wildcard as a full refname component. */
+                               process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
+                               segment_len = 1;
+                       } else
+                               goto cleanup;
+               }
+
+               if (segment_len > 0) {
+                       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++;
                }
 
-               if (*current == '/')
-                       contains_a_slash = 1;
+               if (current[segment_len] == '\0')
+                       break;
 
-               *buffer_out++ = *current++;
-               out_size--;
+               current += segment_len + 1;
        }
 
-       if (!out_size)
-               goto invalid_name;
+       /* A refname can not be empty */
+       if (segment_len == 0 && segments_count == 0)
+               goto cleanup;
 
-       /* Object id refname have to contain at least one slash, except
-        * 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) != 0 &&
-               strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
-               strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
-               goto invalid_name;
+       /* 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;
 
        /* A refname can not end with ".lock" */
        if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
-               goto invalid_name;
+               goto cleanup;
 
-       *buffer_out = '\0';
+       if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
+               goto cleanup;
 
-       /*
-        * For object id references, name has to start with refs/. Again,
-        * we need to allow HEAD to be in a detached state.
-        */
-       if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
-               strcmp(buffer_out_start, GIT_HEAD_FILE)))
-               goto invalid_name;
+       if ((segments_count == 1 ) &&
+               !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
+                       ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
+                       goto cleanup;
 
-       return 0;
+       if ((segments_count > 1)
+               && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
+                       goto cleanup;
 
-invalid_name:
-       giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
-       return -1;
+       error = 0;
+
+cleanup:
+       if (error == GIT_EINVALIDSPEC)
+               giterr_set(
+                       GITERR_REFERENCE,
+                       "The given reference name '%s' is not valid", name);
+
+       if (error && normalize)
+               git_buf_free(buf);
+
+       return error;
 }
 
-int git_reference__normalize_name(
+int git_reference_normalize_name(
        char *buffer_out,
-       size_t out_size,
-       const char *name)
+       size_t buffer_size,
+       const char *name,
+       unsigned int flags)
 {
-       return normalize_name(buffer_out, out_size, name, 0);
+       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) {
+               giterr_set(
+               GITERR_REFERENCE,
+               "The provided buffer is too short to hold the normalization of '%s'", name);
+               error = GIT_EBUFS;
+               goto cleanup;
+       }
+
+       git_buf_copy_cstr(buffer_out, buffer_size, &buf);
+
+       error = 0;
+
+cleanup:
+       git_buf_free(&buf);
+       return error;
 }
 
-int git_reference__normalize_name_oid(
+int git_reference__normalize_name_lax(
        char *buffer_out,
        size_t out_size,
        const char *name)
 {
-       return normalize_name(buffer_out, out_size, name, 1);
+       return git_reference_normalize_name(
+               buffer_out,
+               out_size,
+               name,
+               GIT_REF_FORMAT_ALLOW_ONELEVEL);
 }
-
 #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
 
 int git_reference_cmp(git_reference *ref1, git_reference *ref2)
@@ -1698,7 +1803,7 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char *
         * a new reference and that's it */
        if (res == GIT_ENOTFOUND) {
                giterr_clear();
-               return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
+               return git_reference_create(NULL, repo, ref_name, oid, 1);
        }
 
        if (res < 0)
@@ -1711,7 +1816,7 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char *
                const char *sym_target;
 
                /* The target pointed at by this reference */
-               sym_target = git_reference_target(ref);
+               sym_target = git_reference_symbolic_target(ref);
 
                /* resolve the reference to the target it points to */
                res = git_reference_resolve(&aux, ref);
@@ -1723,7 +1828,7 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char *
                 */
                if (res == GIT_ENOTFOUND) {
                        giterr_clear();
-                       res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
+                       res = git_reference_create(NULL, repo, sym_target, oid, 1);
                        git_reference_free(ref);
                        return res;
                }
@@ -1741,7 +1846,7 @@ int git_reference__update(git_repository *repo, const git_oid *oid, const char *
 
        /* ref is made to point to `oid`: ref is either the original reference,
         * or the target of the symbolic reference we've looked up */
-       res = git_reference_set_oid(ref, oid);
+       res = git_reference_set_target(ref, oid);
        git_reference_free(ref);
        return res;
 }
@@ -1803,6 +1908,78 @@ int git_reference_has_log(
 int git_reference_is_branch(git_reference *ref)
 {
        assert(ref);
-
        return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0;
 }
+
+int git_reference_is_remote(git_reference *ref)
+{
+       assert(ref);
+       return git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0;
+}
+
+static int peel_error(int error, git_reference *ref, const char* msg)
+{
+       giterr_set(
+               GITERR_INVALID,
+               "The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
+       return error;
+}
+
+static int reference_target(git_object **object, git_reference *ref)
+{
+       const git_oid *oid;
+
+       oid = git_reference_target(ref);
+
+       return git_object_lookup(object, git_reference_owner(ref), oid, GIT_OBJ_ANY);
+}
+
+int git_reference_peel(
+               git_object **peeled,
+               git_reference *ref,
+               git_otype target_type)
+{
+       git_reference *resolved = NULL;
+       git_object *target = NULL;
+       int error;
+
+       assert(ref);
+
+       if ((error = git_reference_resolve(&resolved, ref)) < 0)
+               return peel_error(error, ref, "Cannot resolve reference");
+
+       if ((error = reference_target(&target, resolved)) < 0) {
+               peel_error(error, ref, "Cannot retrieve reference target");
+               goto cleanup;
+       }
+
+       if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
+               error = git_object__dup(peeled, target);
+       else
+               error = git_object_peel(peeled, target, target_type);
+
+cleanup:
+       git_object_free(target);
+       git_reference_free(resolved);
+       return error;
+}
+
+int git_reference__is_valid_name(
+       const char *refname,
+       unsigned int flags)
+{
+       int error;
+
+       error = git_reference__normalize_name(NULL, refname, flags) == 0;
+       giterr_clear();
+
+       return error;
+}
+
+int git_reference_is_valid_name(
+       const char *refname)
+{
+       return git_reference__is_valid_name(
+               refname,
+               GIT_REF_FORMAT_ALLOW_ONELEVEL);
+}