/* * The MIT License * * Copyright (c) 2014 GitHub, Inc * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedTagAnnotation; extern VALUE rb_cRuggedTree; extern VALUE rb_cRuggedCommit; extern VALUE rb_cRuggedBlob; extern VALUE rb_cRuggedRepo; VALUE rb_cRuggedObject; git_otype rugged_otype_get(VALUE self) { git_otype type = GIT_OBJ_BAD; if (NIL_P(self)) return GIT_OBJ_ANY; switch (TYPE(self)) { case T_STRING: type = git_object_string2type(StringValueCStr(self)); break; case T_FIXNUM: type = FIX2INT(self); break; case T_SYMBOL: { ID t = SYM2ID(self); if (t == rb_intern("commit")) type = GIT_OBJ_COMMIT; else if (t == rb_intern("tree")) type = GIT_OBJ_TREE; else if (t == rb_intern("tag")) type = GIT_OBJ_TAG; else if (t == rb_intern("blob")) type = GIT_OBJ_BLOB; } } if (!git_object_typeisloose(type)) rb_raise(rb_eTypeError, "Invalid Git object type specifier"); return type; } VALUE rugged_otype_new(git_otype t) { switch (t) { case GIT_OBJ_COMMIT: return CSTR2SYM("commit"); case GIT_OBJ_TAG: return CSTR2SYM("tag"); case GIT_OBJ_TREE: return CSTR2SYM("tree"); case GIT_OBJ_BLOB: return CSTR2SYM("blob"); default: return Qnil; } } int rugged_oid_get(git_oid *oid, git_repository *repo, VALUE p) { git_object *object; int error; if (rb_obj_is_kind_of(p, rb_cRuggedObject)) { Data_Get_Struct(p, git_object, object); git_oid_cpy(oid, git_object_id(object)); } else { Check_Type(p, T_STRING); /* Fast path: see if the 40-char string is an OID */ if (RSTRING_LEN(p) == 40 && git_oid_fromstr(oid, RSTRING_PTR(p)) == 0) return GIT_OK; if ((error = git_revparse_single(&object, repo, StringValueCStr(p)))) return error; git_oid_cpy(oid, git_object_id(object)); git_object_free(object); } return GIT_OK; } git_object *rugged_object_get(git_repository *repo, VALUE object_value, git_otype type) { git_object *object = NULL; if (rb_obj_is_kind_of(object_value, rb_cRuggedObject)) { git_object *owned_obj = NULL; Data_Get_Struct(object_value, git_object, owned_obj); git_object_dup(&object, owned_obj); } else { int error; Check_Type(object_value, T_STRING); /* Fast path: if we have a 40-char string, just perform the lookup directly */ if (RSTRING_LEN(object_value) == 40) { git_oid oid; /* If it's not an OID, we can still try the revparse */ if (git_oid_fromstr(&oid, RSTRING_PTR(object_value)) == 0) { error = git_object_lookup(&object, repo, &oid, type); rugged_exception_check(error); return object; } } /* Otherwise, assume the string is a revlist and try to parse it */ error = git_revparse_single(&object, repo, StringValueCStr(object_value)); rugged_exception_check(error); } assert(object); if (type != GIT_OBJ_ANY && git_object_type(object) != type) rb_raise(rb_eArgError, "Object is not of the required type"); return object; } static void rb_git_object__free(git_object *object) { git_object_free(object); } VALUE rugged_object_new(VALUE owner, git_object *object) { VALUE klass, rb_object; switch (git_object_type(object)) { case GIT_OBJ_COMMIT: klass = rb_cRuggedCommit; break; case GIT_OBJ_TAG: klass = rb_cRuggedTagAnnotation; break; case GIT_OBJ_TREE: klass = rb_cRuggedTree; break; case GIT_OBJ_BLOB: klass = rb_cRuggedBlob; break; default: rb_raise(rb_eTypeError, "Invalid type for Rugged::Object"); return Qnil; /* never reached */ } rb_object = Data_Wrap_Struct(klass, NULL, &rb_git_object__free, object); rugged_set_owner(rb_object, owner); return rb_object; } static git_otype class2otype(VALUE klass) { if (RTEST(rb_class_inherited_p(klass, rb_cRuggedCommit))) return GIT_OBJ_COMMIT; if (RTEST(rb_class_inherited_p(klass, rb_cRuggedTagAnnotation))) return GIT_OBJ_TAG; if (RTEST(rb_class_inherited_p(klass, rb_cRuggedBlob))) return GIT_OBJ_BLOB; if (RTEST(rb_class_inherited_p(klass, rb_cRuggedTree))) return GIT_OBJ_TREE; return GIT_OBJ_BAD; } /* * call-seq: * Object.new(repo, oid) -> object * Object.lookup(repo, oid) -> object * * Find and return the git object inside +repo+ with the given +oid+. * * +oid+ can either have be the complete, 40 character string or any * unique prefix. */ VALUE rb_git_object_lookup(VALUE klass, VALUE rb_repo, VALUE rb_hex) { git_object *object; git_otype type; git_oid oid; int error; int oid_length; git_repository *repo; type = class2otype(klass); if (type == GIT_OBJ_BAD) type = GIT_OBJ_ANY; Check_Type(rb_hex, T_STRING); oid_length = (int)RSTRING_LEN(rb_hex); rugged_check_repo(rb_repo); if (oid_length > GIT_OID_HEXSZ) rb_raise(rb_eTypeError, "The given OID is too long"); Data_Get_Struct(rb_repo, git_repository, repo); error = git_oid_fromstrn(&oid, RSTRING_PTR(rb_hex), oid_length); rugged_exception_check(error); if (oid_length < GIT_OID_HEXSZ) error = git_object_lookup_prefix(&object, repo, &oid, oid_length, type); else error = git_object_lookup(&object, repo, &oid, type); rugged_exception_check(error); return rugged_object_new(rb_repo, object); } VALUE rugged_object_rev_parse(VALUE rb_repo, VALUE rb_spec, int as_obj) { git_object *object; const char *spec; int error; git_repository *repo; VALUE ret; Check_Type(rb_spec, T_STRING); spec = RSTRING_PTR(rb_spec); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_revparse_single(&object, repo, spec); rugged_exception_check(error); if (as_obj) { return rugged_object_new(rb_repo, object); } ret = rugged_create_oid(git_object_id(object)); git_object_free(object); return ret; } /* * call-seq: Object.rev_parse(repo, str) -> object * * Find and return a single object inside +repo+ as specified by the * git revision string +str+. * * See http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions or * man gitrevisions for information on the accepted syntax. * * Raises a Rugged::InvalidError if +str+ does not contain a valid revision string. */ VALUE rb_git_object_rev_parse(VALUE klass, VALUE rb_repo, VALUE rb_spec) { return rugged_object_rev_parse(rb_repo, rb_spec, 1); } /* * call-seq: Object.rev_parse_oid(repo, str) -> oid * * Find and return the id of the object inside +repo+ as specified by the * git revision string +str+. * * See http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions or * man gitrevisions for information on the accepted syntax. * * Raises a Rugged::InvalidError if +str+ does not contain a valid revision string. */ VALUE rb_git_object_rev_parse_oid(VALUE klass, VALUE rb_repo, VALUE rb_spec) { return rugged_object_rev_parse(rb_repo, rb_spec, 0); } /* * call-seq: object == other * * Returns true only if +object+ and other are both instances or subclasses of * Rugged::Object and have the same object id, false otherwise. */ static VALUE rb_git_object_equal(VALUE self, VALUE other) { git_object *a, *b; if (!rb_obj_is_kind_of(other, rb_cRuggedObject)) return Qfalse; Data_Get_Struct(self, git_object, a); Data_Get_Struct(other, git_object, b); return git_oid_cmp(git_object_id(a), git_object_id(b)) == 0 ? Qtrue : Qfalse; } /* * call-seq: object.oid -> oid * * Return the Object ID (a 40 character SHA1 hash) for +object+. */ static VALUE rb_git_object_oid_GET(VALUE self) { git_object *object; Data_Get_Struct(self, git_object, object); return rugged_create_oid(git_object_id(object)); } /* * call-seq: object.type -> type * * Returns the object's type. Can be one of +:commit+, +:tag+, +:tree+ or +:blob+. */ static VALUE rb_git_object_type_GET(VALUE self) { git_object *object; Data_Get_Struct(self, git_object, object); return rugged_otype_new(git_object_type(object)); } /* * call-seq: object.read_raw -> raw_object * * Returns the git object as a Rugged::OdbObject instance. */ static VALUE rb_git_object_read_raw(VALUE self) { git_object *object; Data_Get_Struct(self, git_object, object); return rugged_raw_read(git_object_owner(object), git_object_id(object)); } void Init_rugged_object(void) { rb_cRuggedObject = rb_define_class_under(rb_mRugged, "Object", rb_cObject); rb_define_singleton_method(rb_cRuggedObject, "lookup", rb_git_object_lookup, 2); rb_define_singleton_method(rb_cRuggedObject, "rev_parse", rb_git_object_rev_parse, 2); rb_define_singleton_method(rb_cRuggedObject, "rev_parse_oid", rb_git_object_rev_parse_oid, 2); rb_define_singleton_method(rb_cRuggedObject, "new", rb_git_object_lookup, 2); rb_define_method(rb_cRuggedObject, "read_raw", rb_git_object_read_raw, 0); rb_define_method(rb_cRuggedObject, "==", rb_git_object_equal, 1); rb_define_method(rb_cRuggedObject, "oid", rb_git_object_oid_GET, 0); rb_define_method(rb_cRuggedObject, "type", rb_git_object_type_GET, 0); }