/* * 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_cRuggedSubmodule; VALUE rb_cRuggedSubmoduleCollection; /* * call-seq: * SubmoduleCollection.new(repo) -> submodules * * Creates and returns a new collection of submodules for the given +repo+. */ static VALUE rb_git_submodule_collection_initialize(VALUE self, VALUE rb_repo) { rugged_check_repo(rb_repo); rugged_set_owner(self, rb_repo); return self; } static void rb_git_submodule__free(git_submodule *submodule) { git_submodule_free(submodule); } VALUE rugged_submodule_new(VALUE owner, git_submodule *submodule) { VALUE rb_submodule; rb_submodule = Data_Wrap_Struct( rb_cRuggedSubmodule, NULL, &rb_git_submodule__free, submodule); rugged_set_owner(rb_submodule, owner); return rb_submodule; } /* * call-seq: * submodules[name] -> submodule or nil * * Lookup +submodule+ by +name+ or +path+ (they are usually the same) in * +repository+. * * Returns +nil+ if submodule does not exist. * * Raises Rugged::SubmoduleError if submodule exists only in working * directory (i.e. there is a subdirectory that is a valid self-contained git * repository) and is not mentioned in the +HEAD+, the index and the config. */ static VALUE rb_git_submodule_collection_aref(VALUE self, VALUE rb_name) { git_repository *repo; git_submodule *submodule; int error; VALUE rb_repo = rugged_owner(self); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); error = git_submodule_lookup( &submodule, repo, StringValueCStr(rb_name) ); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_submodule_new(rb_repo, submodule); } static int cb_submodule__each(git_submodule *submodule, const char *name, void *data) { struct rugged_cb_payload *payload = data; git_repository *repo; git_submodule *dummy_sm; VALUE rb_repo; rb_repo = payload->rb_data; Data_Get_Struct(rb_repo, git_repository, repo); /* The submodule passed here has it's refcount decreased just after * the foreach finishes. The only way to increase that refcount is * to lookup the submodule. * * This should not return an error as the submodule name here is valid * and exists, as it was just passed to this callback. */ git_submodule_lookup(&dummy_sm, repo, git_submodule_name(submodule)); rb_protect( rb_yield, rugged_submodule_new(rb_repo, dummy_sm), &payload->exception ); return (payload->exception) ? GIT_ERROR : GIT_OK; } /* * call-seq: * submodules.each { |submodule| block } * submodules.each -> enumerator * * Iterate through all the tracked submodules in the collection's +repository+. * * The given +block+ will be called once with each +submodule+ * as a Rugged::Submodule instance. * If no block is given, an enumerator will be returned. */ static VALUE rb_git_submodule_collection_each(VALUE self) { git_repository *repo; int error; struct rugged_cb_payload payload; VALUE rb_repo = rugged_owner(self); Data_Get_Struct(rb_repo, git_repository, repo); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each")); payload.exception = 0; payload.rb_data = rb_repo; error = git_submodule_foreach(repo, &cb_submodule__each, &payload); if (payload.exception) rb_jump_tag(payload.exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * submodules.setup_add(url, path[, options]) -> submodule * * Setup a new +submodule+ for checkout in +repository+. * * This does "git submodule add" up to the fetch and checkout of the * submodule contents. It prepares a new submodule, creates an entry in * +.gitmodules+ and creates an empty initialized repository either at the * given +path+ in the working directory or in +.git/modules+ with a gitlink * from the working directory to the new repository. * * To fully emulate "git submodule add" call this function, then open * the submodule repository and perform the clone step as needed. * Lastly, call Submodule#finalize_add to wrap up adding the new submodule and * +.gitmodules+ to the index to be ready to commit. * * - +url+: URL for the submodule's remote * - +path+: path at which the submodule should be created * * The following options can be passed in the +options+ Hash: * :gitlink :: * (defaults to +true+) should workdir contain a * gitlink to the repository in +.git/modules+ vs. repository * directly in workdir. * * Returns the newly created +submodule+ */ static VALUE rb_git_submodule_setup_add(int argc, VALUE *argv, VALUE self) { git_submodule *submodule; git_repository *repo; int error; int use_gitlink = 1; VALUE rb_repo, rb_url, rb_path, rb_options; rb_scan_args(argc, argv, "20:", &rb_url, &rb_path, &rb_options); Check_Type(rb_url, T_STRING); Check_Type(rb_path, T_STRING); rb_repo = rugged_owner(self); Data_Get_Struct(rb_repo, git_repository, repo); if (!NIL_P(rb_options)) { VALUE rb_val; rb_val = rb_hash_aref(rb_options, CSTR2SYM("gitlink")); use_gitlink = (rb_val != Qfalse); } error = git_submodule_add_setup( &submodule, repo, StringValueCStr(rb_url), StringValueCStr(rb_path), use_gitlink ); rugged_exception_check(error); return rugged_submodule_new(rb_repo, submodule); } static git_submodule_ignore_t rb_git_subm_ignore_rule_toC(VALUE rb_ignore_rule) { ID id_ignore_rule; Check_Type(rb_ignore_rule, T_SYMBOL); id_ignore_rule = SYM2ID(rb_ignore_rule); if (id_ignore_rule == rb_intern("none")) { return GIT_SUBMODULE_IGNORE_NONE; } else if (id_ignore_rule == rb_intern("untracked")) { return GIT_SUBMODULE_IGNORE_UNTRACKED; } else if (id_ignore_rule == rb_intern("dirty")) { return GIT_SUBMODULE_IGNORE_DIRTY; } else if (id_ignore_rule == rb_intern("all")) { return GIT_SUBMODULE_IGNORE_ALL; } else { rb_raise(rb_eArgError, "Invalid submodule ignore rule type."); } } static git_submodule_update_t rb_git_subm_update_rule_toC(VALUE rb_update_rule) { ID id_update_rule; Check_Type(rb_update_rule, T_SYMBOL); id_update_rule = SYM2ID(rb_update_rule); if (id_update_rule == rb_intern("checkout")) { return GIT_SUBMODULE_UPDATE_CHECKOUT; } else if (id_update_rule == rb_intern("rebase")) { return GIT_SUBMODULE_UPDATE_REBASE; } else if (id_update_rule == rb_intern("merge")) { return GIT_SUBMODULE_UPDATE_MERGE; } else if (id_update_rule == rb_intern("none")) { return GIT_SUBMODULE_UPDATE_NONE; } else { rb_raise(rb_eArgError, "Invalid submodule update rule type."); } } /* * call-seq: * submodules.update(submodule, settings) -> nil * submodules.update(name, settings) -> nil * * Update settings for the given submodule in the submodule config. * * Existing `Rugged::Submodule` instances are not updated, but can be * reloaded by calling `#reload`. * * The following options can be passed in the +settings+ Hash: * * :url :: * Updates the URL for the submodule. * * :ignore_rule :: * See `Rugged::Submodule#ignore_rule` for a list of accepted rules. * * :update_rule :: * See `Rugged::Submodule#update_rule` for a list of accepted rules. * * :fetch_recurse_submodules :: * Updates the +fetchRecurseSubmodules+ rule. */ static VALUE rb_git_submodule_update(VALUE self, VALUE rb_name_or_submodule, VALUE rb_settings) { git_repository *repo; git_submodule_ignore_t ignore_rule = GIT_SUBMODULE_IGNORE_UNSPECIFIED; git_submodule_update_t update_rule = GIT_SUBMODULE_UPDATE_DEFAULT; const char *submodule_name; int fetch_recurse_submodules = 0; VALUE rb_repo = rugged_owner(self); VALUE rb_url, rb_fetch_recurse_submodules, rb_ignore_rule, rb_update_rule; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if (rb_obj_is_kind_of(rb_name_or_submodule, rb_cRuggedSubmodule)) rb_name_or_submodule = rb_funcall(rb_name_or_submodule, rb_intern("name"), 0); if (TYPE(rb_name_or_submodule) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Submodule instance"); rb_url = rb_hash_aref(rb_settings, CSTR2SYM("url")); rb_fetch_recurse_submodules = rb_hash_aref(rb_settings, CSTR2SYM("fetch_recurse_submodules")); rb_ignore_rule = rb_hash_aref(rb_settings, CSTR2SYM("ignore_rule")); rb_update_rule = rb_hash_aref(rb_settings, CSTR2SYM("update_rule")); if (!NIL_P(rb_url)) { Check_Type(rb_url, T_STRING); } if (!NIL_P(rb_fetch_recurse_submodules)) { fetch_recurse_submodules = rugged_parse_bool(rb_fetch_recurse_submodules); } if (!NIL_P(rb_ignore_rule)) { ignore_rule = rb_git_subm_ignore_rule_toC(rb_ignore_rule); } if (!NIL_P(rb_update_rule)) { update_rule = rb_git_subm_update_rule_toC(rb_update_rule); } submodule_name = StringValueCStr(rb_name_or_submodule); if (!NIL_P(rb_url)) { rugged_exception_check( git_submodule_set_url(repo, submodule_name, StringValueCStr(rb_url) ) ); } if (!NIL_P(rb_fetch_recurse_submodules)) { rugged_exception_check( git_submodule_set_fetch_recurse_submodules(repo, submodule_name, fetch_recurse_submodules ) ); } if (!NIL_P(rb_ignore_rule)) { rugged_exception_check( git_submodule_set_ignore(repo, submodule_name, ignore_rule ) ); } if (!NIL_P(rb_update_rule)) { rugged_exception_check( git_submodule_set_update(repo, submodule_name, update_rule ) ); } return Qnil; } void Init_rugged_submodule_collection(void) { rb_cRuggedSubmoduleCollection = rb_define_class_under(rb_mRugged, "SubmoduleCollection", rb_cObject); rb_include_module(rb_cRuggedSubmoduleCollection, rb_mEnumerable); rb_define_method(rb_cRuggedSubmoduleCollection, "initialize", rb_git_submodule_collection_initialize, 1); rb_define_method(rb_cRuggedSubmoduleCollection, "[]", rb_git_submodule_collection_aref, 1); rb_define_method(rb_cRuggedSubmoduleCollection, "each", rb_git_submodule_collection_each, 0); rb_define_method(rb_cRuggedSubmoduleCollection, "update", rb_git_submodule_update, 2); rb_define_method(rb_cRuggedSubmoduleCollection, "setup_add", rb_git_submodule_setup_add, -1); }