rugged-0.26.0/0000755000175000017500000000000013147033070013200 5ustar abhijithabhijithrugged-0.26.0/ext/0000755000175000017500000000000013147033070014000 5ustar abhijithabhijithrugged-0.26.0/ext/rugged/0000755000175000017500000000000013147033070015255 5ustar abhijithabhijithrugged-0.26.0/ext/rugged/rugged_tree.c0000644000175000017500000006204013147033070017717 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" #include extern VALUE rb_mRugged; extern VALUE rb_cRuggedObject; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedDiff; extern VALUE rb_cRuggedIndex; extern VALUE rb_cRuggedCommit; VALUE rb_cRuggedTree; VALUE rb_cRuggedTreeBuilder; static VALUE rb_git_treeentry_fromC(const git_tree_entry *entry) { VALUE rb_entry; VALUE type; if (!entry) return Qnil; rb_entry = rb_hash_new(); rb_hash_aset(rb_entry, CSTR2SYM("name"), rb_str_new_utf8(git_tree_entry_name(entry))); rb_hash_aset(rb_entry, CSTR2SYM("oid"), rugged_create_oid(git_tree_entry_id(entry))); rb_hash_aset(rb_entry, CSTR2SYM("filemode"), INT2FIX(git_tree_entry_filemode(entry))); switch(git_tree_entry_type(entry)) { case GIT_OBJ_TREE: type = CSTR2SYM("tree"); break; case GIT_OBJ_BLOB: type = CSTR2SYM("blob"); break; case GIT_OBJ_COMMIT: type = CSTR2SYM("commit"); break; default: type = Qnil; break; } rb_hash_aset(rb_entry, CSTR2SYM("type"), type); return rb_entry; } /* * Rugged Tree */ /* * call-seq: * tree.count -> count * tree.length -> count * * Return the number of entries contained in the tree. * * Note that this only applies to entries in the root of the tree, * not any other entries contained in sub-folders. */ static VALUE rb_git_tree_entrycount(VALUE self) { git_tree *tree; Data_Get_Struct(self, git_tree, tree); return INT2FIX(git_tree_entrycount(tree)); } struct rugged_treecount_cb_payload { int count; int limit; }; static int rugged__treecount_cb(const char *root, const git_tree_entry *entry, void *data) { struct rugged_treecount_cb_payload *payload = data; if (payload->limit >= 0 && payload->count >= payload->limit) { return -1; } else if(git_tree_entry_type(entry) == GIT_OBJ_TREE) { return 0; } else { ++(payload->count); return 1; } } /* * call-seq: * tree.count_recursive(limit=nil) -> count * * `limit` - The maximum number of blobs to the count in the repository. * Rugged will stop walking the tree after `limit` items to avoid long * execution times. * * Return the number of blobs (up to the limit) contained in the tree and * all subtrees. */ static VALUE rb_git_tree_entrycount_recursive(int argc, VALUE* argv, VALUE self) { git_tree *tree; int error; struct rugged_treecount_cb_payload payload; VALUE rb_limit; Data_Get_Struct(self, git_tree, tree); rb_scan_args(argc, argv, "01", &rb_limit); payload.limit = -1; payload.count = 0; if (!NIL_P(rb_limit)) { Check_Type(rb_limit, T_FIXNUM); payload.limit = FIX2INT(rb_limit); } error = git_tree_walk(tree, GIT_TREEWALK_PRE, &rugged__treecount_cb, (void *)&payload); if (error && giterr_last()->klass == GITERR_CALLBACK) { giterr_clear(); error = 0; } rugged_exception_check(error); return INT2FIX(payload.count); } /* * call-seq: * tree[e] -> entry * tree.get_entry(e) -> entry * * Return one of the entries from a tree as a +Hash+. If +e+ is a number, the +e+nth entry * from the tree will be returned. If +e+ is a string, the entry with that name * will be returned. * * If the entry doesn't exist, +nil+ will be returned. * * tree[3] #=> {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0} * tree['bar.txt'] #=> {:name => "bar.txt", :type => :blob, :oid => "de5ba987198bcf2518885f0fc1350e5172cded78", :filemode => 0} * tree['baz.txt'] #=> nil */ static VALUE rb_git_tree_get_entry(VALUE self, VALUE entry_id) { git_tree *tree; Data_Get_Struct(self, git_tree, tree); if (TYPE(entry_id) == T_FIXNUM) return rb_git_treeentry_fromC(git_tree_entry_byindex(tree, FIX2INT(entry_id))); else if (TYPE(entry_id) == T_STRING) return rb_git_treeentry_fromC(git_tree_entry_byname(tree, StringValueCStr(entry_id))); else rb_raise(rb_eTypeError, "entry_id must be either an index or a filename"); } /* * call-seq: * tree.get_entry_by_oid(rb_oid) -> entry * * Return one of the entries from a tree as a +Hash+, based off the oid SHA. * * If the entry doesn't exist, +nil+ will be returned. * * This does a full traversal of the every element in the tree, so this method * is not especially fast. * * tree.get_entry_by_oid("d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f") * #=> {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0} * */ static VALUE rb_git_tree_get_entry_by_oid(VALUE self, VALUE rb_oid) { git_tree *tree; git_oid oid; Data_Get_Struct(self, git_tree, tree); Check_Type(rb_oid, T_STRING); rugged_exception_check(git_oid_fromstr(&oid, StringValueCStr(rb_oid))); return rb_git_treeentry_fromC(git_tree_entry_byid(tree, &oid)); } /* * call-seq: * tree.each { |entry| block } * tree.each -> enumerator * * Call +block+ with each of the entries of the subtree as a +Hash+. If no +block+ * is given, an +enumerator+ is returned instead. * * Note that only the entries in the root of the tree are yielded; if you need to * list also entries in subfolders, use +tree.walk+ instead. * * tree.each { |entry| puts entry.inspect } * * generates: * * {:name => "foo.txt", :type => :blob, :oid => "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f", :filemode => 0} * {:name => "bar.txt", :type => :blob, :oid => "de5ba987198bcf2518885f0fc1350e5172cded78", :filemode => 0} * ... */ static VALUE rb_git_tree_each(VALUE self) { git_tree *tree; size_t i, count; Data_Get_Struct(self, git_tree, tree); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 0); count = git_tree_entrycount(tree); for (i = 0; i < count; ++i) { const git_tree_entry *entry = git_tree_entry_byindex(tree, i); rb_yield(rb_git_treeentry_fromC(entry)); } return Qnil; } static int rugged__treewalk_cb(const char *root, const git_tree_entry *entry, void *payload) { int *exception = (int *)payload; VALUE rb_result, rb_args = rb_ary_new2(2); rb_ary_push(rb_args, rb_str_new_utf8(root)); rb_ary_push(rb_args, rb_git_treeentry_fromC(entry)); rb_result = rb_protect(rb_yield_splat, rb_args, exception); if (*exception) return -1; /* skip entry when 'false' is returned */ if (TYPE(rb_result) == T_FALSE) return 1; /* otherwise continue normal iteration */ return 0; } /* * call-seq: * tree.walk(mode) { |root, entry| block } * tree.walk(mode) -> Enumerator * * Walk +tree+ with the given mode (either +:preorder+ or +:postorder+) and yield * to +block+ every entry in +tree+ and all its subtrees, as a +Hash+. The +block+ * also takes a +root+, the relative path in the traversal, starting from the root * of the original tree. * * If the +block+ returns a falsy value, that entry and its sub-entries (in the case * of a folder) will be skipped for the iteration. * * If no +block+ is given, an +Enumerator+ is returned instead. * * tree.walk(:postorder) { |root, entry| puts "#{root}#{entry[:name]} [#{entry[:oid]}]" } * * generates: * * USAGE.rb [02bae86c91f96b5fdb6b1cf06f5aa3612139e318] * ext [23f135b3c576b6ac4785821888991d7089f35db1] * ext/rugged [25c88faa9302e34e16664eb9c990deb2bcf77849] * ext/rugged/extconf.rb [40c1aa8a8cec8ca444ed5758e3f00ecff093070a] * ... */ static VALUE rb_git_tree_walk(VALUE self, VALUE rb_mode) { git_tree *tree; int error, mode = 0, exception = 0; ID id_mode; Data_Get_Struct(self, git_tree, tree); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 2, CSTR2SYM("walk"), rb_mode); Check_Type(rb_mode, T_SYMBOL); id_mode = SYM2ID(rb_mode); if (id_mode == rb_intern("preorder")) mode = GIT_TREEWALK_PRE; else if (id_mode == rb_intern("postorder")) mode = GIT_TREEWALK_POST; else rb_raise(rb_eTypeError, "Invalid iteration mode. Expected `:preorder` or `:postorder`"); error = git_tree_walk(tree, mode, &rugged__treewalk_cb, (void *)&exception); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * tree.path(path) -> entry * * Retrieve and return a tree entry by its relative path. */ static VALUE rb_git_tree_path(VALUE self, VALUE rb_path) { int error; git_tree *tree; git_tree_entry *entry; VALUE rb_entry; Data_Get_Struct(self, git_tree, tree); Check_Type(rb_path, T_STRING); error = git_tree_entry_bypath(&entry, tree, StringValueCStr(rb_path)); rugged_exception_check(error); rb_entry = rb_git_treeentry_fromC(entry); git_tree_entry_free(entry); return rb_entry; } static VALUE rb_git_diff_tree_to_index(VALUE self, VALUE rb_repo, VALUE rb_self, VALUE rb_other, VALUE rb_options) { git_tree *tree = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_repository *repo = NULL; git_diff *diff = NULL; git_index *index; int error; Data_Get_Struct(rb_repo, git_repository, repo); Data_Get_Struct(rb_other, git_index, index); rugged_parse_diff_options(&opts, rb_options); if (RTEST(rb_self)) { Data_Get_Struct(rb_self, git_tree, tree); } error = git_diff_tree_to_index(&diff, repo, tree, index, &opts); xfree(opts.pathspec.strings); rugged_exception_check(error); return rugged_diff_new(rb_cRuggedDiff, rb_repo, diff); } struct nogvl_diff_args { git_repository * repo; git_tree * tree; git_tree * other_tree; git_diff_options * opts; int error; }; static void * rb_git_diff_tree_to_tree_nogvl(void * _args) { struct nogvl_diff_args * args; git_diff *diff = NULL; args = (struct nogvl_diff_args *)_args; args->error = git_diff_tree_to_tree(&diff, args->repo, args->tree, args->other_tree, args->opts); return diff; } static VALUE rb_git_diff_tree_to_tree(VALUE self, VALUE rb_repo, VALUE rb_tree, VALUE rb_other_tree, VALUE rb_options) { git_tree *tree = NULL; git_tree *other_tree = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_repository *repo = NULL; git_diff *diff = NULL; struct nogvl_diff_args args; Data_Get_Struct(rb_repo, git_repository, repo); if(RTEST(rb_tree)) Data_Get_Struct(rb_tree, git_tree, tree); if(RTEST(rb_other_tree)) Data_Get_Struct(rb_other_tree, git_tree, other_tree); rugged_parse_diff_options(&opts, rb_options); args.repo = repo; args.tree = tree; args.other_tree = other_tree; args.opts = &opts; diff = rb_thread_call_without_gvl(rb_git_diff_tree_to_tree_nogvl, &args, RUBY_UBF_PROCESS, NULL); xfree(opts.pathspec.strings); rugged_exception_check(args.error); return rugged_diff_new(rb_cRuggedDiff, rb_repo, diff); } /* * call-seq: * tree.diff_workdir([options]) -> diff * * Returns a diff between a tree and the current workdir. * * The +tree+ object will be used as the "old file" side of the diff, while the * content of the current workdir will be used for the "new file" side. * * See Rugged::Tree#diff for a list of options that can be passed. */ static VALUE rb_git_tree_diff_workdir(int argc, VALUE *argv, VALUE self) { git_tree *tree; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_repository *repo; git_diff *diff; VALUE owner, rb_options; int error; rb_scan_args(argc, argv, "00:", &rb_options); rugged_parse_diff_options(&opts, rb_options); Data_Get_Struct(self, git_tree, tree); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); error = git_diff_tree_to_workdir(&diff, repo, tree, &opts); xfree(opts.pathspec.strings); rugged_exception_check(error); return rugged_diff_new(rb_cRuggedDiff, owner, diff); } void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options) { if (!NIL_P(rb_options)) { VALUE rb_value; Check_Type(rb_options, T_HASH); rb_value = rb_hash_aref(rb_options, CSTR2SYM("rename_threshold")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->rename_threshold = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("target_limit")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->target_limit = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("favor")); if (!NIL_P(rb_value)) { ID id_favor; Check_Type(rb_value, T_SYMBOL); id_favor = SYM2ID(rb_value); if (id_favor == rb_intern("normal")) { opts->file_favor = GIT_MERGE_FILE_FAVOR_NORMAL; } else if (id_favor == rb_intern("ours")) { opts->file_favor = GIT_MERGE_FILE_FAVOR_OURS; } else if (id_favor == rb_intern("theirs")) { opts->file_favor = GIT_MERGE_FILE_FAVOR_THEIRS; } else if (id_favor == rb_intern("union")) { opts->file_favor = GIT_MERGE_FILE_FAVOR_UNION; } else { rb_raise(rb_eTypeError, "Invalid favor mode. Expected `:normal`, `:ours`, `:theirs` or `:union`"); } } if (rb_hash_aref(rb_options, CSTR2SYM("renames")) == Qfalse) { opts->flags &= ~GIT_MERGE_FIND_RENAMES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("fail_on_conflict")))) { opts->flags |= GIT_MERGE_FAIL_ON_CONFLICT; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("skip_reuc")))) { opts->flags |= GIT_MERGE_SKIP_REUC; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("no_recursive")))) { opts->flags |= GIT_MERGE_NO_RECURSIVE; } } } /* * tree.merge(other_tree[, ancestor_tree[, options]]) -> Rugged::Index * tree.merge(other_tree[, options]) -> Rugged::Index * * Merges two trees and returns the a Rugged::Index object that reflects * the result of the merge. * * The following options can be passed in the +options+ Hash: * * :renames :: * If true, looking for renames will be enabled (`--find-renames`), * set to false to disable (default true). * * :rename_threshold :: * An integer specifying the minimum similarity of a file to be * seen as an eligible rename source (default 50). * * :target_limit :: * An integer specifying the maximum byte size of a file before a it will * be treated as binary. The default value is 512MB. * * :favor :: * Specifies how and if conflicts are auto-resolved by favoring a specific * file output. Can be one of `:normal`, `:ours`, `:theirs` or `:union`. * */ static VALUE rb_git_tree_merge(int argc, VALUE *argv, VALUE self) { VALUE rb_other_tree, rb_ancestor_tree, rb_options; VALUE rb_repo = rugged_owner(self); git_tree *tree, *other_tree, *ancestor_tree; git_repository *repo; git_index *index; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; int error; if (rb_scan_args(argc, argv, "12", &rb_other_tree, &rb_ancestor_tree, &rb_options) == 2) { if (TYPE(rb_ancestor_tree) == T_HASH) { rb_options = rb_ancestor_tree; rb_ancestor_tree = Qnil; } } if (!NIL_P(rb_options)) { Check_Type(rb_options, T_HASH); rugged_parse_merge_options(&opts, rb_options); } if (!rb_obj_is_kind_of(rb_other_tree, rb_cRuggedTree)) rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance"); else if (!NIL_P(rb_ancestor_tree) && !rb_obj_is_kind_of(rb_ancestor_tree, rb_cRuggedTree)) rb_raise(rb_eTypeError, "Expecting a Rugged::Tree instance"); Data_Get_Struct(self, git_tree, tree); Data_Get_Struct(rb_repo, git_repository, repo); Data_Get_Struct(rb_other_tree, git_tree, other_tree); if (!NIL_P(rb_ancestor_tree)) Data_Get_Struct(rb_ancestor_tree, git_tree, ancestor_tree); else ancestor_tree = NULL; error = git_merge_trees(&index, repo, ancestor_tree, tree, other_tree, &opts); if (error == GIT_EMERGECONFLICT) return Qnil; rugged_exception_check(error); return rugged_index_new(rb_cRuggedIndex, rb_repo, index); } static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }}; /* * call-seq: * Tree.empty(repo) -> tree * * Look up the empty tree in the given repository +repo+. The empty * tree's id is hard-coded to exist in a repository. * * Returns a new instance of the empty tree. */ static VALUE rb_git_tree_empty(VALUE self, VALUE rb_repo) { git_repository *repo; git_tree *tree; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); rugged_exception_check(git_tree_lookup(&tree, repo, &empty_tree)); return rugged_object_new(rb_repo, (git_object *) tree); } /** * Parse the updates and convert them into libgit2 ones. They will be * heap-allocated and returned in 'out'. The strings will also be * heap-allocated and will be stored in 'strings'. */ static void parse_tree_updates(git_tree_update **out, int *nupdates_out, VALUE rb_updates) { int i, nupdates; git_tree_update *updates; Check_Type(rb_updates, T_ARRAY); nupdates = RARRAY_LEN(rb_updates); updates = xcalloc(nupdates, sizeof(git_tree_update)); for (i = 0; i < nupdates; i++) { VALUE rb_update = rb_ary_entry(rb_updates, i); VALUE rb_action, rb_oid, rb_filemode, rb_path; ID action; git_tree_update *update = &updates[i]; if (!RB_TYPE_P(rb_update, T_HASH)) goto on_error; rb_action = rb_hash_aref(rb_update, CSTR2SYM("action")); rb_oid = rb_hash_aref(rb_update, CSTR2SYM("oid")); rb_filemode = rb_hash_aref(rb_update, CSTR2SYM("filemode")); rb_path = rb_hash_aref(rb_update, CSTR2SYM("path")); if (!SYMBOL_P(rb_action) || !RB_TYPE_P(rb_path, T_STRING)) goto on_error; update->path = StringValueCStr(rb_path); action = SYM2ID(rb_action); if (action == rb_intern("upsert")) { if (!RB_TYPE_P(rb_oid, T_STRING) ||!RB_TYPE_P(rb_filemode, T_FIXNUM)) goto on_error; update->action = GIT_TREE_UPDATE_UPSERT; update->filemode = NUM2INT(rb_filemode); if (git_oid_fromstr(&update->id, StringValueCStr(rb_oid)) < 0) goto on_error; } else if (action == rb_intern("remove")) { update->action = GIT_TREE_UPDATE_REMOVE; } else { goto on_error; } } *out = updates; *nupdates_out = nupdates; return; on_error: xfree(updates); rb_raise(rb_eTypeError, "Invalid type for tree update object"); } /* * call-seq: * tree.update(updates) * * Create a new Rugged::Tree based on the curent one by applying the * changes described in +updates+. * * The updates are given as a list of +Hash+ containing: * * :action :: * +:upsert+ or +:remove+ to add/insert an entry, or to remove it resp. * * :oid :: * The +oid+ of the entry. This is ignored for removals. * * :filemode :: * The octal filemode for the entry. This is ignored for remvals. * * :path :: * The path of the entry. This may contain slashes and the * intermediate trees will be created. * */ static VALUE rb_git_tree_update(VALUE self, VALUE rb_updates) { git_repository *repo; git_tree *tree = NULL; git_tree_update *updates; int nupdates, error; git_oid id; Data_Get_Struct(self, git_tree, tree); repo = git_tree_owner(tree); parse_tree_updates(&updates, &nupdates, rb_updates); error = git_tree_create_updated(&id, repo, tree, nupdates, updates); xfree(updates); rugged_exception_check(error); return rugged_create_oid(&id); } static void rb_git_treebuilder_free(git_treebuilder *bld) { git_treebuilder_free(bld); } /* * call-seq: * Tree::Builder.new(repository, [tree]) * * Create a new Rugged::Tree::Builder instance to write a tree to * the given +repository+. * * If an optional +tree+ is given, the returned Tree::Builder will be * initialized with the entry of +tree+. Otherwise, the Tree::Builder * will be empty and has to be filled manually. */ static VALUE rb_git_treebuilder_new(int argc, VALUE *argv, VALUE klass) { git_treebuilder *builder; git_repository *repo; git_tree *tree = NULL; VALUE rb_object, rb_builder, rb_repo; int error; if (rb_scan_args(argc, argv, "11", &rb_repo, &rb_object) == 2) { if (!rb_obj_is_kind_of(rb_object, rb_cRuggedTree)) rb_raise(rb_eTypeError, "A Rugged::Tree instance is required"); Data_Get_Struct(rb_object, git_tree, tree); } rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_treebuilder_new(&builder, repo, tree); rugged_exception_check(error); rb_builder = Data_Wrap_Struct(klass, NULL, &rb_git_treebuilder_free, builder); rugged_set_owner(rb_builder, rb_repo); return rb_builder; } /* * call-seq: * builder.clear -> nil * * Clear all entries in +builder+. */ static VALUE rb_git_treebuilder_clear(VALUE self) { git_treebuilder *builder; Data_Get_Struct(self, git_treebuilder, builder); git_treebuilder_clear(builder); return Qnil; } /* * call-seq: * builder[path] -> entry * * Return an entry from +builder+ based on its relative path. */ static VALUE rb_git_treebuilder_get(VALUE self, VALUE path) { git_treebuilder *builder; Data_Get_Struct(self, git_treebuilder, builder); Check_Type(path, T_STRING); return rb_git_treeentry_fromC(git_treebuilder_get(builder, StringValueCStr(path))); } /* * call-seq: * builder << entry -> nil * builder.insert(entry) -> nil * * Inser a new entry into +builder+. */ static VALUE rb_git_treebuilder_insert(VALUE self, VALUE rb_entry) { git_treebuilder *builder; VALUE rb_path, rb_oid, rb_attr; git_oid oid; int error; Data_Get_Struct(self, git_treebuilder, builder); Check_Type(rb_entry, T_HASH); rb_path = rb_hash_aref(rb_entry, CSTR2SYM("name")); Check_Type(rb_path, T_STRING); rb_oid = rb_hash_aref(rb_entry, CSTR2SYM("oid")); Check_Type(rb_oid, T_STRING); rugged_exception_check(git_oid_fromstr(&oid, StringValueCStr(rb_oid))); rb_attr = rb_hash_aref(rb_entry, CSTR2SYM("filemode")); Check_Type(rb_attr, T_FIXNUM); error = git_treebuilder_insert(NULL, builder, StringValueCStr(rb_path), &oid, FIX2INT(rb_attr)); rugged_exception_check(error); return Qnil; } /* * call-seq: * builder.remove(path) -> true or false * * Remove an entry from +builder+ by its relative +path+. * * Returns +true+ if the entry was successfully removed, * or +false+ if the entry was not found. */ static VALUE rb_git_treebuilder_remove(VALUE self, VALUE path) { git_treebuilder *builder; int error; Data_Get_Struct(self, git_treebuilder, builder); Check_Type(path, T_STRING); error = git_treebuilder_remove(builder, StringValueCStr(path)); if (error == GIT_ENOTFOUND) { return Qfalse; } else if (error == GIT_ERROR && giterr_last()->klass == GITERR_TREE) { return Qfalse; } rugged_exception_check(error); return Qtrue; } /* * call-seq: * builder.write -> oid * * Write +builder+'s content as a tree to the repository * that owns the builder and return the +oid+ for the * newly created tree. */ static VALUE rb_git_treebuilder_write(VALUE self) { git_treebuilder *builder; git_oid written_id; int error; Data_Get_Struct(self, git_treebuilder, builder); error = git_treebuilder_write(&written_id, builder); rugged_exception_check(error); return rugged_create_oid(&written_id); } static int treebuilder_cb(const git_tree_entry *entry, void *opaque) { VALUE proc = (VALUE)opaque; VALUE ret = rb_funcall(proc, rb_intern("call"), 1, rb_git_treeentry_fromC(entry)); return rugged_parse_bool(ret); } /* * call-seq: * builder.reject! { |entry| block } -> nil * * Deletes every tree +entry+ from +builder+ for which * the given +block+ evaluates to true. */ static VALUE rb_git_treebuilder_filter(VALUE self) { git_treebuilder *builder; rb_need_block(); Data_Get_Struct(self, git_treebuilder, builder); git_treebuilder_filter(builder, &treebuilder_cb, (void *)rb_block_proc()); return Qnil; } void Init_rugged_tree(void) { /* * Tree */ rb_cRuggedTree = rb_define_class_under(rb_mRugged, "Tree", rb_cRuggedObject); rb_define_method(rb_cRuggedTree, "count", rb_git_tree_entrycount, 0); rb_define_method(rb_cRuggedTree, "count_recursive", rb_git_tree_entrycount_recursive, -1); rb_define_method(rb_cRuggedTree, "length", rb_git_tree_entrycount, 0); rb_define_method(rb_cRuggedTree, "get_entry", rb_git_tree_get_entry, 1); rb_define_method(rb_cRuggedTree, "get_entry_by_oid", rb_git_tree_get_entry_by_oid, 1); rb_define_method(rb_cRuggedTree, "path", rb_git_tree_path, 1); rb_define_method(rb_cRuggedTree, "diff_workdir", rb_git_tree_diff_workdir, -1); rb_define_method(rb_cRuggedTree, "[]", rb_git_tree_get_entry, 1); rb_define_method(rb_cRuggedTree, "each", rb_git_tree_each, 0); rb_define_method(rb_cRuggedTree, "walk", rb_git_tree_walk, 1); rb_define_method(rb_cRuggedTree, "merge", rb_git_tree_merge, -1); rb_define_method(rb_cRuggedTree, "update", rb_git_tree_update, 1); rb_define_singleton_method(rb_cRuggedTree, "empty", rb_git_tree_empty, 1); rb_define_private_method(rb_singleton_class(rb_cRuggedTree), "diff_tree_to_index", rb_git_diff_tree_to_index, 4); rb_define_private_method(rb_singleton_class(rb_cRuggedTree), "diff_tree_to_tree", rb_git_diff_tree_to_tree, 4); rb_cRuggedTreeBuilder = rb_define_class_under(rb_cRuggedTree, "Builder", rb_cObject); rb_define_singleton_method(rb_cRuggedTreeBuilder, "new", rb_git_treebuilder_new, -1); rb_define_method(rb_cRuggedTreeBuilder, "clear", rb_git_treebuilder_clear, 0); rb_define_method(rb_cRuggedTreeBuilder, "[]", rb_git_treebuilder_get, 1); rb_define_method(rb_cRuggedTreeBuilder, "insert", rb_git_treebuilder_insert, 1); rb_define_method(rb_cRuggedTreeBuilder, "<<", rb_git_treebuilder_insert, 1); rb_define_method(rb_cRuggedTreeBuilder, "remove", rb_git_treebuilder_remove, 1); rb_define_method(rb_cRuggedTreeBuilder, "write", rb_git_treebuilder_write, 0); rb_define_method(rb_cRuggedTreeBuilder, "reject!", rb_git_treebuilder_filter, 0); } rugged-0.26.0/ext/rugged/rugged_tag.c0000644000175000017500000001400313147033070017527 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedObject; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedReference; VALUE rb_cRuggedTag; VALUE rb_cRuggedTagAnnotation; /* * call-seq: * annotation.target -> object * * Return the +object+ pointed at by this tag annotation, as a Rugged::Object * instance. * * annotation.target #=> # */ static VALUE rb_git_tag_annotation_target(VALUE self) { git_tag *tag; git_object *target; int error; VALUE owner; Data_Get_Struct(self, git_tag, tag); owner = rugged_owner(self); error = git_tag_target(&target, tag); rugged_exception_check(error); return rugged_object_new(owner, target); } /* * call-seq: * annotation.target_oid -> oid * annotation.target_id -> oid * * Return the oid pointed at by this tag annotation, as a String * instance. * * annotation.target_id #=> "2cb831a8aea28b2c1b9c63385585b864e4d3bad1" */ static VALUE rb_git_tag_annotation_target_id(VALUE self) { git_tag *tag; const git_oid *target_oid; Data_Get_Struct(self, git_tag, tag); target_oid = git_tag_target_id(tag); return rugged_create_oid(target_oid); } /* * call-seq: * annotation.type -> t * * Return a symbol representing the type of the objeced pointed at by * this +annotation+. Possible values are +:blob+, +:commit+, +:tree+ and +:tag+. * * This is always the same as the +type+ of the returned annotation.target * * annotation.type #=> :commit * annotation.target.type == annotation.type #=> true */ static VALUE rb_git_tag_annotation_target_type(VALUE self) { git_tag *tag; Data_Get_Struct(self, git_tag, tag); return rugged_otype_new(git_tag_target_type(tag)); } /* * call-seq: * annotation.name -> name * * Return a string with the name of this tag +annotation+. * * annotation.name #=> "v0.16.0" */ static VALUE rb_git_tag_annotation_name(VALUE self) { git_tag *tag; Data_Get_Struct(self, git_tag, tag); return rb_str_new_utf8(git_tag_name(tag)); } /* * call-seq: * annotation.tagger -> signature * * Return the signature for the author of this tag +annotation+. The signature * is returned as a +Hash+ containing +:name+, +:email+ of the author * and +:time+ of the tagging. * * annotation.tagger #=> {:email=>"tanoku@gmail.com", :time=>Tue Jan 24 05:42:45 UTC 2012, :name=>"Vicent Mart\303\255"} */ static VALUE rb_git_tag_annotation_tagger(VALUE self) { git_tag *tag; const git_signature *tagger; Data_Get_Struct(self, git_tag, tag); tagger = git_tag_tagger(tag); if (!tagger) return Qnil; return rugged_signature_new(tagger, NULL); } /* * call-seq: * annotation.message -> msg * * Return the message of this tag +annotation+. This includes the full body of the * message and any optional footers or signatures after it. * * annotation.message #=> "Release v0.16.0, codename 'broken stuff'" */ static VALUE rb_git_tag_annotation_message(VALUE self) { git_tag *tag; const char *message; Data_Get_Struct(self, git_tag, tag); message = git_tag_message(tag); if (!message) return Qnil; return rb_str_new_utf8(message); } /* * call-seq: * tag.annotation -> annotation or nil */ static VALUE rb_git_tag_annotation(VALUE self) { git_reference *ref, *resolved_ref; git_repository *repo; git_object *target; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(self, git_reference, ref); Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_resolve(&resolved_ref, ref); rugged_exception_check(error); error = git_object_lookup(&target, repo, git_reference_target(resolved_ref), GIT_OBJ_TAG); git_reference_free(resolved_ref); if (error == GIT_ENOTFOUND) return Qnil; return rugged_object_new(rb_repo, target); } /* * call-seq: * tag.target -> git_object */ static VALUE rb_git_tag_target(VALUE self) { git_reference *ref, *resolved_ref; git_repository *repo; git_object *target; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(self, git_reference, ref); Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_resolve(&resolved_ref, ref); rugged_exception_check(error); error = git_object_lookup(&target, repo, git_reference_target(resolved_ref), GIT_OBJ_ANY); git_reference_free(resolved_ref); rugged_exception_check(error); if (git_object_type(target) == GIT_OBJ_TAG) { git_object *annotation_target; error = git_tag_target(&annotation_target, (git_tag *)target); git_object_free(target); rugged_exception_check(error); return rugged_object_new(rb_repo, annotation_target); } else { return rugged_object_new(rb_repo, target); } } static VALUE rb_git_tag_annotated_p(VALUE self) { return RTEST(rb_git_tag_annotation(self)) ? Qtrue : Qfalse; } void Init_rugged_tag(void) { rb_cRuggedTag = rb_define_class_under(rb_mRugged, "Tag", rb_cRuggedReference); rb_define_method(rb_cRuggedTag, "annotation", rb_git_tag_annotation, 0); rb_define_method(rb_cRuggedTag, "annotated?", rb_git_tag_annotated_p, 0); rb_define_method(rb_cRuggedTag, "target", rb_git_tag_target, 0); rb_cRuggedTagAnnotation = rb_define_class_under(rb_cRuggedTag, "Annotation", rb_cRuggedObject); rb_define_method(rb_cRuggedTagAnnotation, "message", rb_git_tag_annotation_message, 0); rb_define_method(rb_cRuggedTagAnnotation, "name", rb_git_tag_annotation_name, 0); rb_define_method(rb_cRuggedTagAnnotation, "target", rb_git_tag_annotation_target, 0); rb_define_method(rb_cRuggedTagAnnotation, "target_oid", rb_git_tag_annotation_target_id, 0); rb_define_method(rb_cRuggedTagAnnotation, "target_id", rb_git_tag_annotation_target_id, 0); rb_define_method(rb_cRuggedTagAnnotation, "target_type", rb_git_tag_annotation_target_type, 0); rb_define_method(rb_cRuggedTagAnnotation, "tagger", rb_git_tag_annotation_tagger, 0); } rugged-0.26.0/ext/rugged/rugged.c0000644000175000017500000004113713147033070016704 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" const char *RUGGED_ERROR_NAMES[] = { "None", /* GITERR_NONE */ "NoMemError", /* GITERR_NOMEMORY */ "OSError", /* GITERR_OS */ "InvalidError", /* GITERR_INVALID */ "ReferenceError", /* GITERR_REFERENCE */ "ZlibError", /* GITERR_ZLIB */ "RepositoryError", /* GITERR_REPOSITORY */ "ConfigError", /* GITERR_CONFIG */ "RegexError", /* GITERR_REGEX */ "OdbError", /* GITERR_ODB */ "IndexError", /* GITERR_INDEX */ "ObjectError", /* GITERR_OBJECT */ "NetworkError", /* GITERR_NET */ "TagError", /* GITERR_TAG */ "TreeError", /* GITERR_TREE */ "IndexerError", /* GITERR_INDEXER */ "SslError", /* GITERR_SSL */ "SubmoduleError", /* GITERR_SUBMODULE */ "ThreadError", /* GITERR_THREAD */ "StashError", /* GITERR_STASH */ "CheckoutError", /* GITERR_CHECKOUT */ "FetchheadError", /* GITERR_FETCHHEAD */ "MergeError", /* GITERR_MERGE */ "SshError", /* GITERR_SSH */ "FilterError", /* GITERR_FILTER */ "RevertError", /* GITERR_REVERT */ "CallbackError", /* GITERR_CALLBACK */ "CherrypickError", /* GITERR_CHERRYPICK */ "DescribeError", /* GITERR_DESCRIBE */ "RebaseError", /* GITERR_REBASE */ "FilesystemError", /* GITERR_FILESYSTEM */ }; #define RUGGED_ERROR_COUNT (int)((sizeof(RUGGED_ERROR_NAMES)/sizeof(RUGGED_ERROR_NAMES[0]))) VALUE rb_mRugged; VALUE rb_eRuggedError; VALUE rb_eRuggedErrors[RUGGED_ERROR_COUNT]; static VALUE rb_mShutdownHook; /* * call-seq: * Rugged.libgit2_version -> version * * Returns an array representing the current libgit2 version in use. Using * the array makes it easier for the end-user to take conditional actions * based on each respective version attribute: major, minor, rev. * * Rugged.libgit2_version #=> [0, 17, 0] */ static VALUE rb_git_libgit2_version(VALUE self) { int major; int minor; int rev; git_libgit2_version(&major, &minor, &rev); // We return an array of three elements to represent the version components return rb_ary_new3(3, INT2NUM(major), INT2NUM(minor), INT2NUM(rev)); } /* * call-seq: * Rugged.features -> [feature, ...] * * Returns an array representing the features that libgit2 was compiled * with — this includes `:threads` (thread support), `:https` and `:ssh`. * * Rugged.features #=> [:threads, :https] */ static VALUE rb_git_features(VALUE self) { VALUE ret_arr = rb_ary_new(); int caps = git_libgit2_features(); if (caps & GIT_FEATURE_THREADS) rb_ary_push(ret_arr, CSTR2SYM("threads")); if (caps & GIT_FEATURE_HTTPS) rb_ary_push(ret_arr, CSTR2SYM("https")); if (caps & GIT_FEATURE_SSH) rb_ary_push(ret_arr, CSTR2SYM("ssh")); return ret_arr; } /* * call-seq: * Rugged.valid_full_oid?(oid) -> true or false * * Checks to see if a string contains a full 40-character sha1. * * Rugged.valid_full_oid?('d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f') * #=> true */ static VALUE rb_git_valid_full_oid(VALUE self, VALUE hex) { git_oid oid; int errorcode; Check_Type(hex, T_STRING); errorcode = git_oid_fromstr(&oid, StringValueCStr(hex)); if (errorcode < 0) { return Qfalse; } else { return Qtrue; } } /* * call-seq: * Rugged.hex_to_raw(oid) -> raw_buffer * * Turn a string of 40 hexadecimal characters into the buffer of * 20 bytes it represents. * * Rugged.hex_to_raw('d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f') * #=> "\330xk\374\227H^\215{\031\262\037\270\214\216\361\361\231\374?" */ static VALUE rb_git_hex_to_raw(VALUE self, VALUE hex) { git_oid oid; Check_Type(hex, T_STRING); rugged_exception_check(git_oid_fromstr(&oid, StringValueCStr(hex))); return rb_str_new((const char *)oid.id, 20); } /* * call-seq: * Rugged.raw_to_hex(buffer) -> hex_oid * * Turn a buffer of 20 bytes (representing a SHA1 OID) into its * readable hexadecimal representation. * * Rugged.raw_to_hex("\330xk\374\227H^\215{\031\262\037\270\214\216\361\361\231\374?") * #=> "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f" */ static VALUE rb_git_raw_to_hex(VALUE self, VALUE raw) { git_oid oid; char out[40]; Check_Type(raw, T_STRING); if (RSTRING_LEN(raw) != GIT_OID_RAWSZ) rb_raise(rb_eTypeError, "Invalid buffer size for an OID"); git_oid_fromraw(&oid, (const unsigned char *)RSTRING_PTR(raw)); git_oid_fmt(out, &oid); return rb_usascii_str_new(out, 40); } /* * call-seq: * Rugged.prettify_message(message, strip_comments = '#') -> clean_message * * Process a commit or tag message into standard form, by stripping trailing spaces and * comments, and making sure that the message has a proper header line. */ static VALUE rb_git_prettify_message(int argc, VALUE *argv, VALUE self) { char comment_char = '#'; int strip_comments = 1; git_buf message = { NULL }; VALUE rb_message, rb_strip; int error; VALUE result = Qnil; rb_scan_args(argc, argv, "11", &rb_message, &rb_strip); Check_Type(rb_message, T_STRING); switch (TYPE(rb_strip)) { case T_FALSE: strip_comments = 0; break; case T_STRING: if (RSTRING_LEN(rb_strip) > 0) comment_char = RSTRING_PTR(rb_strip)[0]; break; case T_TRUE: case T_NIL: default: break; } error = git_message_prettify(&message, StringValueCStr(rb_message), strip_comments, comment_char); if (!error) result = rb_enc_str_new(message.ptr, message.size, rb_utf8_encoding()); git_buf_free(&message); rugged_exception_check(error); return result; } static VALUE minimize_cb(VALUE rb_oid, git_oid_shorten *shortener) { Check_Type(rb_oid, T_STRING); git_oid_shorten_add(shortener, RSTRING_PTR(rb_oid)); return Qnil; } static VALUE minimize_yield(VALUE rb_oid, VALUE *data) { rb_funcall(data[0], rb_intern("call"), 1, rb_str_substr(rb_oid, 0, FIX2INT(data[1]))); return Qnil; } /* * call-seq: * Rugged.minimize_oid(oid_iterator, min_length = 7) { |short_oid| block } * Rugged.minimize_oid(oid_iterator, min_length = 7) -> min_length * * Iterate through +oid_iterator+, which should yield any number of SHA1 OIDs * (represented as 40-character hexadecimal strings), and tries to minify them. * * Minifying a set of a SHA1 strings means finding the shortest root substring * for each string that uniquely identifies it. * * If no +block+ is given, the function will return the minimal length as an * integer value: * * oids = [ * 'd8786bfc974aaaaaaaaaaaaaaaaaaaaaaaaaaaaa', * 'd8786bfc974bbbbbbbbbbbbbbbbbbbbbbbbbbbbb', * 'd8786bfc974ccccccccccccccccccccccccccccc', * '68d041ee999cb07c6496fbdd4f384095de6ca9e1', * ] * * Rugged.minimize_oids(oids) #=> 12 * * If a +block+ is given, it will be called with each OID from +iterator+ * in its minified form: * * Rugged.minimize_oid(oids) { |oid| puts oid } * * produces: * * d8786bfc974a * d8786bfc974b * d8786bfc974c * 68d041ee999c * * The optional +min_length+ argument allows you to specify a lower bound for * the minified strings; returned strings won't be shorter than the given value, * even if they would still be uniquely represented. * * Rugged.minimize_oid(oids, 18) #=> 18 */ static VALUE rb_git_minimize_oid(int argc, VALUE *argv, VALUE self) { git_oid_shorten *shrt; int length, minlen = 7; VALUE rb_enum, rb_minlen, rb_block; rb_scan_args(argc, argv, "11&", &rb_enum, &rb_minlen, &rb_block); if (!NIL_P(rb_minlen)) { Check_Type(rb_minlen, T_FIXNUM); minlen = FIX2INT(rb_minlen); } if (!rb_respond_to(rb_enum, rb_intern("each"))) rb_raise(rb_eTypeError, "Expecting an Enumerable instance"); shrt = git_oid_shorten_new(minlen); rb_iterate(rb_each, rb_enum, &minimize_cb, (VALUE)shrt); length = git_oid_shorten_add(shrt, NULL); git_oid_shorten_free(shrt); rugged_exception_check(length); if (!NIL_P(rb_block)) { VALUE yield_data[2]; yield_data[0] = rb_block; yield_data[1] = INT2FIX(length); rb_iterate(rb_each, rb_enum, &minimize_yield, (VALUE)yield_data); return Qnil; } return INT2FIX(length); } static void cleanup_cb(void *unused) { (void)unused; git_libgit2_shutdown(); } void rugged_exception_raise(void) { VALUE err_klass, err_obj; const git_error *error; const char *err_message; error = giterr_last(); if (error && error->klass > 0 && error->klass < RUGGED_ERROR_COUNT) { err_klass = rb_eRuggedErrors[error->klass]; err_message = error->message; } else { err_klass = rb_eRuntimeError; err_message = "Rugged operation failed"; } err_obj = rb_exc_new2(err_klass, err_message); giterr_clear(); rb_exc_raise(err_obj); } VALUE rugged__block_yield_splat(VALUE args) { VALUE block = rb_ary_shift(args); int n = RARRAY_LENINT(args); if (n == 0) { return rb_funcall(block, rb_intern("call"), 0); } else { int i; VALUE *argv; argv = ALLOCA_N(VALUE, n); for (i=0; i < n; i++) { argv[i] = rb_ary_entry(args, i); } return rb_funcall2(block, rb_intern("call"), n, argv); } } /* * call-seq: * Rugged.__cache_usage__ -> [current, max] * * Returns an array representing the current bytes in the internal * libgit2 cache and the maximum size of the cache. */ static VALUE rb_git_cache_usage(VALUE self) { int64_t used, max; git_libgit2_opts(GIT_OPT_GET_CACHED_MEMORY, &used, &max); return rb_ary_new3(2, LL2NUM(used), LL2NUM(max)); } /* * call-seq: * Rugged.signature_from_buffer(buffer[, encoding_name]) -> signature * * Parse the signature from the given buffer. If an encoding is given, the * strings will be tagged with that encoding. * * commit.author #=> {:email=>"tanoku@gmail.com", :time=>Tue Jan 24 05:42:45 UTC 2012, :name=>"Vicent Mart\303\255"} */ static VALUE rb_git_signature_from_buffer(int argc, VALUE *argv, VALUE self) { VALUE rb_buffer, rb_encoding_name; const char *buffer, *encoding_name = NULL; rb_scan_args(argc, argv, "11", &rb_buffer, &rb_encoding_name); buffer = StringValueCStr(rb_buffer); if (!NIL_P(rb_encoding_name)) encoding_name = StringValueCStr(rb_encoding_name); return rugged_signature_from_buffer(buffer, encoding_name); } VALUE rugged_strarray_to_rb_ary(git_strarray *str_array) { VALUE rb_array = rb_ary_new2(str_array->count); size_t i; for (i = 0; i < str_array->count; ++i) { rb_ary_push(rb_array, rb_str_new_utf8(str_array->strings[i])); } return rb_array; } void rugged_rb_ary_to_strarray(VALUE rb_array, git_strarray *str_array) { int i; str_array->strings = NULL; str_array->count = 0; if (NIL_P(rb_array)) return; if (TYPE(rb_array) == T_STRING) { str_array->count = 1; str_array->strings = xmalloc(sizeof(char *)); str_array->strings[0] = StringValueCStr(rb_array); return; } Check_Type(rb_array, T_ARRAY); for (i = 0; i < RARRAY_LEN(rb_array); ++i) Check_Type(rb_ary_entry(rb_array, i), T_STRING); str_array->count = RARRAY_LEN(rb_array); str_array->strings = xmalloc(str_array->count * sizeof(char *)); for (i = 0; i < RARRAY_LEN(rb_array); ++i) { VALUE rb_string = rb_ary_entry(rb_array, i); str_array->strings[i] = StringValueCStr(rb_string); } } void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options) { VALUE rb_value; Check_Type(rb_options, T_HASH); rb_value = rb_hash_aref(rb_options, CSTR2SYM("ancestor_label")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); opts->ancestor_label = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("our_label")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); opts->our_label = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("their_label")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); opts->their_label = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("favor")); if (!NIL_P(rb_value)) { ID id_favor; Check_Type(rb_value, T_SYMBOL); id_favor = SYM2ID(rb_value); if (id_favor == rb_intern("normal")) { opts->favor = GIT_MERGE_FILE_FAVOR_NORMAL; } else if (id_favor == rb_intern("ours")) { opts->favor = GIT_MERGE_FILE_FAVOR_OURS; } else if (id_favor == rb_intern("theirs")) { opts->favor = GIT_MERGE_FILE_FAVOR_THEIRS; } else if (id_favor == rb_intern("union")) { opts->favor = GIT_MERGE_FILE_FAVOR_UNION; } else { rb_raise(rb_eTypeError, "Invalid favor mode. Expected `:normal`, `:ours`, `:theirs` or `:union`"); } } rb_value = rb_hash_aref(rb_options, CSTR2SYM("style")); if (!NIL_P(rb_value)) { ID id_style; Check_Type(rb_value, T_SYMBOL); id_style = SYM2ID(rb_value); if (id_style == rb_intern("standard")) { opts->flags |= GIT_MERGE_FILE_STYLE_MERGE; } else if (id_style == rb_intern("diff3")) { opts->flags |= GIT_MERGE_FILE_STYLE_DIFF3; } else { rb_raise(rb_eTypeError, "Invalid style mode. Expected `:standard`, or `:diff3`"); } } else { opts->flags |= GIT_MERGE_FILE_STYLE_MERGE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("simplify")))) { opts->flags |= GIT_MERGE_FILE_SIMPLIFY_ALNUM; } } VALUE rb_merge_file_result_fromC(const git_merge_file_result *result) { VALUE rb_result = rb_hash_new(); rb_hash_aset(rb_result, CSTR2SYM("automergeable"), result->automergeable ? Qtrue : Qfalse); rb_hash_aset(rb_result, CSTR2SYM("path"), result->path ? rb_str_new_utf8(result->path) : Qnil); rb_hash_aset(rb_result, CSTR2SYM("filemode"), INT2FIX(result->mode)); rb_hash_aset(rb_result, CSTR2SYM("data"), rb_str_new(result->ptr, result->len)); return rb_result; } void Init_rugged(void) { rb_mRugged = rb_define_module("Rugged"); /* Initialize the Error classes */ { int i; rb_eRuggedError = rb_define_class_under(rb_mRugged, "Error", rb_eStandardError); rb_eRuggedErrors[0] = Qnil; /* 0 return value -- no exception */ rb_eRuggedErrors[1] = rb_define_class_under(rb_mRugged, RUGGED_ERROR_NAMES[1], rb_eNoMemError); rb_eRuggedErrors[2] = rb_define_class_under(rb_mRugged, RUGGED_ERROR_NAMES[2], rb_eIOError); rb_eRuggedErrors[3] = rb_define_class_under(rb_mRugged, RUGGED_ERROR_NAMES[3], rb_eArgError); for (i = 4; i < RUGGED_ERROR_COUNT; ++i) { rb_eRuggedErrors[i] = rb_define_class_under(rb_mRugged, RUGGED_ERROR_NAMES[i], rb_eRuggedError); } } rb_define_module_function(rb_mRugged, "libgit2_version", rb_git_libgit2_version, 0); rb_define_module_function(rb_mRugged, "features", rb_git_features, 0); rb_define_module_function(rb_mRugged, "valid_full_oid?", rb_git_valid_full_oid, 1); rb_define_module_function(rb_mRugged, "hex_to_raw", rb_git_hex_to_raw, 1); rb_define_module_function(rb_mRugged, "raw_to_hex", rb_git_raw_to_hex, 1); rb_define_module_function(rb_mRugged, "minimize_oid", rb_git_minimize_oid, -1); rb_define_module_function(rb_mRugged, "prettify_message", rb_git_prettify_message, -1); rb_define_module_function(rb_mRugged, "__cache_usage__", rb_git_cache_usage, 0); rb_define_module_function(rb_mRugged, "signature_from_buffer", rb_git_signature_from_buffer, -1); Init_rugged_reference(); Init_rugged_reference_collection(); Init_rugged_object(); Init_rugged_commit(); Init_rugged_tree(); Init_rugged_tag(); Init_rugged_tag_collection(); Init_rugged_blob(); Init_rugged_index(); Init_rugged_repo(); Init_rugged_revwalk(); Init_rugged_branch(); Init_rugged_branch_collection(); Init_rugged_config(); Init_rugged_remote(); Init_rugged_remote_collection(); Init_rugged_notes(); Init_rugged_settings(); Init_rugged_submodule(); Init_rugged_submodule_collection(); Init_rugged_diff(); Init_rugged_patch(); Init_rugged_diff_delta(); Init_rugged_diff_hunk(); Init_rugged_diff_line(); Init_rugged_blame(); Init_rugged_cred(); Init_rugged_backend(); Init_rugged_rebase(); /* * Sort the output with the same default time-order method from git. * This is the default sorting for new walkers. */ rb_define_const(rb_mRugged, "SORT_NONE", INT2FIX(GIT_SORT_NONE)); /* * Sort the repository contents in topological order (parents before * children); this sorting mode can be combined with time sorting to * produce git's "time-order". */ rb_define_const(rb_mRugged, "SORT_TOPO", INT2FIX(GIT_SORT_TOPOLOGICAL)); /* * Sort the repository contents by commit time; * this sorting mode can be combined with * topological sorting. */ rb_define_const(rb_mRugged, "SORT_DATE", INT2FIX(GIT_SORT_TIME)); /* * Iterate through the repository contents in reverse * order; this sorting mode can be combined with * any of the above. */ rb_define_const(rb_mRugged, "SORT_REVERSE", INT2FIX(GIT_SORT_REVERSE)); /* Initialize libgit2 */ git_libgit2_init(); /* Hook a global object to cleanup the library * on shutdown */ rb_mShutdownHook = Data_Wrap_Struct(rb_cObject, NULL, &cleanup_cb, NULL); rb_global_variable(&rb_mShutdownHook); } rugged-0.26.0/ext/rugged/rugged_signature.c0000644000175000017500000000465213147033070020766 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" VALUE rugged_signature_new(const git_signature *sig, const char *encoding_name) { VALUE rb_sig, rb_time; rb_encoding *encoding = rb_utf8_encoding(); if (encoding_name != NULL) encoding = rb_enc_find(encoding_name); rb_sig = rb_hash_new(); /* Allocate the time with a the given timezone */ rb_time = rb_funcall( rb_time_new(sig->when.time, 0), rb_intern("getlocal"), 1, INT2FIX(sig->when.offset * 60) ); rb_hash_aset(rb_sig, CSTR2SYM("name"), rb_enc_str_new(sig->name, strlen(sig->name), encoding)); rb_hash_aset(rb_sig, CSTR2SYM("email"), rb_enc_str_new(sig->email, strlen(sig->email), encoding)); rb_hash_aset(rb_sig, CSTR2SYM("time"), rb_time); return rb_sig; } VALUE rugged_signature_from_buffer(const char *buffer, const char *encoding_name) { git_signature *sig; VALUE rb_ret; rugged_exception_check(git_signature_from_buffer(&sig, buffer)); rb_ret = rugged_signature_new(sig, encoding_name); git_signature_free(sig); return rb_ret; } git_signature *rugged_signature_get(VALUE rb_sig, git_repository *repo) { int error; VALUE rb_time, rb_unix_t, rb_offset, rb_name, rb_email, rb_time_offset; git_signature *sig; if (NIL_P(rb_sig)) { rugged_exception_check( git_signature_default(&sig, repo) ); return sig; } Check_Type(rb_sig, T_HASH); rb_name = rb_hash_fetch(rb_sig, CSTR2SYM("name")); rb_email = rb_hash_fetch(rb_sig, CSTR2SYM("email")); rb_time = rb_hash_aref(rb_sig, CSTR2SYM("time")); rb_time_offset = rb_hash_aref(rb_sig, CSTR2SYM("time_offset")); Check_Type(rb_name, T_STRING); Check_Type(rb_email, T_STRING); if (NIL_P(rb_time)) { error = git_signature_now(&sig, StringValueCStr(rb_name), StringValueCStr(rb_email)); } else { if (!rb_obj_is_kind_of(rb_time, rb_cTime)) rb_raise(rb_eTypeError, "expected Time object"); rb_unix_t = rb_funcall(rb_time, rb_intern("tv_sec"), 0); if (NIL_P(rb_time_offset)) { rb_offset = rb_funcall(rb_time, rb_intern("utc_offset"), 0); } else { Check_Type(rb_time_offset, T_FIXNUM); rb_offset = rb_time_offset; } error = git_signature_new(&sig, StringValueCStr(rb_name), StringValueCStr(rb_email), NUM2LONG(rb_unix_t), FIX2INT(rb_offset) / 60); } rugged_exception_check(error); return sig; } rugged-0.26.0/ext/rugged/rugged_settings.c0000644000175000017500000000642513147033070020625 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" #if !defined(NUM2SIZET) # if SIZEOF_SIZE_T == SIZEOF_LONG # define NUM2SIZET(n) ((size_t)NUM2ULONG(n)) # define SIZET2NUM(n) ((size_t)ULONG2NUM(n)) # else # define NUM2SIZET(n) ((size_t)NUM2ULL(n)) # define SIZET2NUM(n) ((size_t)ULL2NUM(n)) # endif #endif /* ! defined(NUM2SIZET) */ extern VALUE rb_mRugged; static void set_search_path(int level, VALUE value) { const char *path; Check_Type(value, T_STRING); path = StringValueCStr(value); rugged_exception_check(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, level, path)); } static VALUE get_search_path(int level) { git_buf buf = {NULL}; VALUE ret; rugged_exception_check(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, &buf)); ret = rb_str_new_utf8(buf.ptr); git_buf_free(&buf); return ret; } /* * call-seq: * Settings[option] = value * * Sets a libgit2 library option. */ static VALUE rb_git_set_option(VALUE self, VALUE option, VALUE value) { const char *opt; Check_Type(option, T_STRING); opt = StringValueCStr(option); if (strcmp(opt, "mwindow_size") == 0) { size_t val; Check_Type(value, T_FIXNUM); val = NUM2SIZET(value); git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, val); } else if (strcmp(opt, "mwindow_mapped_limit") == 0) { size_t val; Check_Type(value, T_FIXNUM); val = NUM2SIZET(value); git_libgit2_opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, val); } else if (strcmp(opt, "search_path_global") == 0) { set_search_path(GIT_CONFIG_LEVEL_GLOBAL, value); } else if (strcmp(opt, "search_path_xdg") == 0) { set_search_path(GIT_CONFIG_LEVEL_XDG, value); } else if (strcmp(opt, "search_path_system") == 0) { set_search_path(GIT_CONFIG_LEVEL_SYSTEM, value); } else if (strcmp(opt, "strict_object_creation") == 0) { int strict = RTEST(value) ? 1 : 0; git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, strict); } else { rb_raise(rb_eArgError, "Unknown option specified"); } return Qnil; } /* * call-seq: * Settings[option] -> value * * Gets the value of a libgit2 library option. */ static VALUE rb_git_get_option(VALUE self, VALUE option) { const char *opt; Check_Type(option, T_STRING); opt = StringValueCStr(option); if (strcmp(opt, "mwindow_size") == 0) { size_t val; git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &val); return SIZET2NUM(val); } else if (strcmp(opt, "mwindow_mapped_limit") == 0) { size_t val; git_libgit2_opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, &val); return SIZET2NUM(val); } else if (strcmp(opt, "search_path_global") == 0) { return get_search_path(GIT_CONFIG_LEVEL_GLOBAL); } else if (strcmp(opt, "search_path_xdg") == 0) { return get_search_path(GIT_CONFIG_LEVEL_XDG); } else if (strcmp(opt, "search_path_system") == 0) { return get_search_path(GIT_CONFIG_LEVEL_SYSTEM); } else { rb_raise(rb_eArgError, "Unknown option specified"); } } void Init_rugged_settings(void) { VALUE rb_cRuggedSettings = rb_define_class_under(rb_mRugged, "Settings", rb_cObject); rb_define_module_function(rb_cRuggedSettings, "[]=", rb_git_set_option, 2); rb_define_module_function(rb_cRuggedSettings, "[]", rb_git_get_option, 1); } rugged-0.26.0/ext/rugged/rugged_index.c0000644000175000017500000007306413147033070020077 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" VALUE rb_cRuggedIndex; extern VALUE rb_mRugged; extern VALUE rb_cRuggedCommit; extern VALUE rb_cRuggedDiff; extern VALUE rb_cRuggedTree; static void rb_git_indexentry_toC(git_index_entry *entry, VALUE rb_entry); static VALUE rb_git_indexentry_fromC(const git_index_entry *entry); /* * Index */ static void rb_git_index__free(git_index *index) { git_index_free(index); } VALUE rugged_index_new(VALUE klass, VALUE owner, git_index *index) { VALUE rb_index = Data_Wrap_Struct(klass, NULL, &rb_git_index__free, index); rugged_set_owner(rb_index, owner); return rb_index; } /* * call-seq: * Index.new([path]) * * Create a bare index object based on the index file at +path+. * * Any index methods that rely on the ODB or a working directory (e.g. #add) * will raise a Rugged::IndexError. */ static VALUE rb_git_index_new(int argc, VALUE *argv, VALUE klass) { git_index *index; int error; VALUE rb_path; const char *path = NULL; if (rb_scan_args(argc, argv, "01", &rb_path) == 1) { Check_Type(rb_path, T_STRING); path = StringValueCStr(rb_path); } error = git_index_open(&index, path); rugged_exception_check(error); return rugged_index_new(klass, Qnil, index); } /* * call-seq: * index.clear -> nil * * Clear the contents (remove all entries) of the index object. Changes are in-memory only * and can be saved by calling #write. */ static VALUE rb_git_index_clear(VALUE self) { git_index *index; Data_Get_Struct(self, git_index, index); git_index_clear(index); return Qnil; } /* * call-seq: * index.reload -> nil * * Reloads the index contents from the disk, discarding any changes that * have not been saved through #write. */ static VALUE rb_git_index_read(VALUE self) { git_index *index; int error; Data_Get_Struct(self, git_index, index); error = git_index_read(index, 0); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.write -> nil * * Writes the index object from memory back to the disk, persisting all changes. */ static VALUE rb_git_index_write(VALUE self) { git_index *index; int error; Data_Get_Struct(self, git_index, index); error = git_index_write(index); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.count -> int * * Returns the number of entries currently in the index. */ static VALUE rb_git_index_count(VALUE self) { git_index *index; Data_Get_Struct(self, git_index, index); return INT2FIX(git_index_entrycount(index)); } /* * call-seq: * index[path[, stage = 0]] -> entry or nil * index[position] -> entry or nil * index.get(path[, stage = 0]) -> entry or nil * index.get(position) -> entry or nil * * Return a specific entry in the index. * * The first two forms returns entries based on their +path+ in the index and an optional +stage+, * while the last two forms return entries based on their position in the index. */ static VALUE rb_git_index_get(int argc, VALUE *argv, VALUE self) { git_index *index; const git_index_entry *entry = NULL; VALUE rb_entry, rb_stage; Data_Get_Struct(self, git_index, index); rb_scan_args(argc, argv, "11", &rb_entry, &rb_stage); if (TYPE(rb_entry) == T_STRING) { int stage = 0; if (!NIL_P(rb_stage)) { Check_Type(rb_stage, T_FIXNUM); stage = FIX2INT(rb_stage); } entry = git_index_get_bypath(index, StringValueCStr(rb_entry), stage); } else if (TYPE(rb_entry) == T_FIXNUM) { if (argc > 1) { rb_raise(rb_eArgError, "Too many arguments when trying to lookup entry by index"); } entry = git_index_get_byindex(index, FIX2INT(rb_entry)); } else { rb_raise(rb_eArgError, "Invalid type for `entry`: expected String or Fixnum"); } return entry ? rb_git_indexentry_fromC(entry) : Qnil; } /* * call-seq: * index.each { |entry| } -> nil * index.each -> Enumerator * * Passes each entry of the index to the given block. * * If no block is given, an enumerator is returned instead. */ static VALUE rb_git_index_each(VALUE self) { git_index *index; unsigned int i, count; Data_Get_Struct(self, git_index, index); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 0); count = (unsigned int)git_index_entrycount(index); for (i = 0; i < count; ++i) { const git_index_entry *entry = git_index_get_byindex(index, i); if (entry) rb_yield(rb_git_indexentry_fromC(entry)); } return Qnil; } /* * call-seq: * index.remove(path[, stage = 0]) -> nil * * Removes the entry at the given +path+ with the given +stage+ * from the index. */ static VALUE rb_git_index_remove(int argc, VALUE *argv, VALUE self) { git_index *index; int error, stage = 0; VALUE rb_entry, rb_stage; Data_Get_Struct(self, git_index, index); if (rb_scan_args(argc, argv, "11", &rb_entry, &rb_stage) > 1) { Check_Type(rb_stage, T_FIXNUM); stage = FIX2INT(rb_stage); } Check_Type(rb_entry, T_STRING); error = git_index_remove(index, StringValueCStr(rb_entry), stage); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.remove_dir(dir[, stage = 0]) -> nil * * Removes all entries under the given +dir+ with the given +stage+ * from the index. */ static VALUE rb_git_index_remove_directory(int argc, VALUE *argv, VALUE self) { git_index *index; int error, stage = 0; VALUE rb_dir, rb_stage; Data_Get_Struct(self, git_index, index); if (rb_scan_args(argc, argv, "11", &rb_dir, &rb_stage) > 1) { Check_Type(rb_stage, T_FIXNUM); stage = FIX2INT(rb_stage); } Check_Type(rb_dir, T_STRING); error = git_index_remove_directory(index, StringValueCStr(rb_dir), stage); rugged_exception_check(error); return Qnil; } /* * call-seq: * index << entry -> nil * index << path -> nil * index.add(entry) -> nil * index.add(path) -> nil * index.update(entry) -> nil * index.update(path) -> nil * * Add a new entry to the index or update an existing entry in the index. * * If passed a +path+ to an existing, readable file relative to the workdir, * creates a new index entry based on this file. * * Alternatively, a new index entry can be created by passing a Hash containing * all key/value pairs of an index entry. * * Any gitignore rules that might match +path+ (or the +:path+ value of the * entry hash) are ignored. * * If the index entry at +path+ (or +:path+) currently contains a merge conflict, * it will no longer be marked as conflicting and the data about the conflict * will be moved into the "resolve undo" (REUC) section of the index. */ static VALUE rb_git_index_add(VALUE self, VALUE rb_entry) { git_index *index; int error = 0; Data_Get_Struct(self, git_index, index); if (TYPE(rb_entry) == T_HASH) { git_index_entry entry; rb_git_indexentry_toC(&entry, rb_entry); error = git_index_add(index, &entry); } else if (TYPE(rb_entry) == T_STRING) { error = git_index_add_bypath(index, StringValueCStr(rb_entry)); } else { rb_raise(rb_eTypeError, "Expecting a hash defining an Index Entry or a path to a file in the repository"); } rugged_exception_check(error); return Qnil; } int rugged__index_matched_path_cb(const char *path, const char *matched_pathspec, void *payload) { int *exception = (int *)payload; VALUE rb_result, rb_args = rb_ary_new2(2); rb_ary_push(rb_args, rb_str_new2(path)); rb_ary_push(rb_args, matched_pathspec == NULL ? Qnil : rb_str_new2(matched_pathspec)); rb_result = rb_protect(rb_yield_splat, rb_args, exception); if (*exception) return GIT_ERROR; return RTEST(rb_result) ? 0 : 1; } /* * call-seq: * index.add_all(pathspec = [][, options]) -> nil * index.add_all(pathspec = [][, options]) { |path, pathspec| block } -> nil * * Add or update index entries matching files in the working directory. * * Searches the working directory for files that +pathspec+ and adds them * to +index+ (by updating an existing entry or adding a new entry). * * +pathspec+ can either be a String, or an Array of Strings. * If +pathspec+ is empty, all entries in the index will be matched. * * Files that are ignored due to +.gitignore+ rules will be skipped, * unless they're already have an entry in +index+. * * Files that are marked as the result of a merge request, will have this * marking removed and the merge conflict information will be moved into the * "resolve undo" (REUC) section of +index+. * * If a block is given, each matched +path+ and the +pathspec+ that matched * it will be passed to the block. If the return value of +block+ is * falsy, the matching item will not be added to the index. * * This method will fail in bare index instances. * * The following options can be passed in the +options+ Hash: * * :force :: * If +true+, any +.gitignore+ rules will be ignored. * * :disable_pathspec_match :: * If +true+, glob expansion will be disabled and exact matching will be forced. * * :check_pathspec :: * If +true+, and the +:force+ options is +false+ or not given, exact matches * of ignored files or files that are not already in +index+ will raise a * Rugged::InvalidError. This emulates git add -A. */ static VALUE rb_git_index_add_all(int argc, VALUE *argv, VALUE self) { VALUE rb_pathspecs, rb_options; git_index *index; git_strarray pathspecs; int error, exception = 0; unsigned int flags = GIT_INDEX_ADD_DEFAULT; Data_Get_Struct(self, git_index, index); if (rb_scan_args(argc, argv, "02", &rb_pathspecs, &rb_options) > 1) { Check_Type(rb_options, T_HASH); if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("force")))) flags |= GIT_INDEX_ADD_FORCE; if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("disable_pathspec_match")))) flags |= GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH; if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("check_pathspec")))) flags |= GIT_INDEX_ADD_CHECK_PATHSPEC; } rugged_rb_ary_to_strarray(rb_pathspecs, &pathspecs); error = git_index_add_all(index, &pathspecs, flags, rb_block_given_p() ? rugged__index_matched_path_cb : NULL, &exception); xfree(pathspecs.strings); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.update_all(pathspec = []) -> nil * index.update_all(pathspec = []) { |path, pathspec| block } -> nil * * Update all index entries to match the working directory. * * Searches +index+ for entries that match +pathspec+ and synchronizes * them with the content of the working directory. * * +pathspec+ can either be a String, or an Array of Strings. * If +pathspec+ is empty, all entries in the index will be matched. * * Entries where the corresponding working directory file no longer exists * get deleted, all other matched entries will get updated to reflect their * working directory state (the latest version of the a file's content will * automatically be added to the ODB). * * If a block is given, each matched +path+ and the +pathspec+ that matched * it will be passed to the block. If the return value of +block+ is * falsy, the matching item will not be updated in the index. * * This method will fail in bare index instances. */ static VALUE rb_git_index_update_all(int argc, VALUE *argv, VALUE self) { VALUE rb_pathspecs = rb_ary_new(); git_index *index; git_strarray pathspecs; int error, exception = 0; Data_Get_Struct(self, git_index, index); rb_scan_args(argc, argv, "01", &rb_pathspecs); rugged_rb_ary_to_strarray(rb_pathspecs, &pathspecs); error = git_index_update_all(index, &pathspecs, rb_block_given_p() ? rugged__index_matched_path_cb : NULL, &exception); xfree(pathspecs.strings); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.remove_all(pathspec = []) -> nil * index.remove_all(pathspec = []) { |path, pathspec| block } -> nil * * Remove all matching index entries. * * Searches +index+ for entries that match +pathspec+ and removes them * from the index. * * +pathspec+ can either be a String, or an Array of Strings. * If +pathspec+ is empty, all entries in the index will be matched. * * If a block is given, each matched +path+ and the +pathspec+ that matched * it will be passed to the block. If the return value of +block+ is * falsy, the matching item will not be removed from the index. */ static VALUE rb_git_index_remove_all(int argc, VALUE *argv, VALUE self) { VALUE rb_pathspecs = rb_ary_new(); git_index *index; git_strarray pathspecs; int error, exception = 0; Data_Get_Struct(self, git_index, index); rb_scan_args(argc, argv, "01", &rb_pathspecs); if (NIL_P(rb_pathspecs)) rb_pathspecs = rb_ary_new(); rugged_rb_ary_to_strarray(rb_ary_to_ary(rb_pathspecs), &pathspecs); error = git_index_remove_all(index, &pathspecs, rb_block_given_p() ? rugged__index_matched_path_cb : NULL, &exception); xfree(pathspecs.strings); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } static VALUE rb_git_indexentry_fromC(const git_index_entry *entry) { VALUE rb_entry, rb_mtime, rb_ctime; unsigned int valid, stage; if (!entry) return Qnil; rb_entry = rb_hash_new(); rb_hash_aset(rb_entry, CSTR2SYM("path"), rb_str_new_utf8(entry->path)); rb_hash_aset(rb_entry, CSTR2SYM("oid"), rugged_create_oid(&entry->id)); rb_hash_aset(rb_entry, CSTR2SYM("dev"), INT2FIX(entry->dev)); rb_hash_aset(rb_entry, CSTR2SYM("ino"), INT2FIX(entry->ino)); rb_hash_aset(rb_entry, CSTR2SYM("mode"), INT2FIX(entry->mode)); rb_hash_aset(rb_entry, CSTR2SYM("gid"), INT2FIX(entry->gid)); rb_hash_aset(rb_entry, CSTR2SYM("uid"), INT2FIX(entry->uid)); rb_hash_aset(rb_entry, CSTR2SYM("file_size"), INT2FIX(entry->file_size)); valid = (entry->flags & GIT_IDXENTRY_VALID); rb_hash_aset(rb_entry, CSTR2SYM("valid"), valid ? Qtrue : Qfalse); stage = (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT; rb_hash_aset(rb_entry, CSTR2SYM("stage"), INT2FIX(stage)); rb_mtime = rb_time_new(entry->mtime.seconds, entry->mtime.nanoseconds / 1000); rb_ctime = rb_time_new(entry->ctime.seconds, entry->ctime.nanoseconds / 1000); rb_hash_aset(rb_entry, CSTR2SYM("ctime"), rb_ctime); rb_hash_aset(rb_entry, CSTR2SYM("mtime"), rb_mtime); return rb_entry; } static inline uint32_t default_entry_value(VALUE rb_entry, const char *key) { VALUE val = rb_hash_aref(rb_entry, CSTR2SYM(key)); if (NIL_P(val)) return 0; Check_Type(val, T_FIXNUM); return FIX2INT(val); } static void rb_git_indexentry_toC(git_index_entry *entry, VALUE rb_entry) { VALUE val; Check_Type(rb_entry, T_HASH); val = rb_hash_aref(rb_entry, CSTR2SYM("path")); Check_Type(val, T_STRING); entry->path = StringValueCStr(val); val = rb_hash_aref(rb_entry, CSTR2SYM("oid")); Check_Type(val, T_STRING); rugged_exception_check( git_oid_fromstr(&entry->id, StringValueCStr(val)) ); entry->dev = default_entry_value(rb_entry, "dev"); entry->ino = default_entry_value(rb_entry, "ino"); entry->mode = default_entry_value(rb_entry, "mode"); entry->gid = default_entry_value(rb_entry, "gid"); entry->uid = default_entry_value(rb_entry, "uid"); entry->file_size = default_entry_value(rb_entry, "file_size"); if ((val = rb_hash_aref(rb_entry, CSTR2SYM("mtime"))) != Qnil) { if (!rb_obj_is_kind_of(val, rb_cTime)) rb_raise(rb_eTypeError, ":mtime must be a Time instance"); entry->mtime.seconds = NUM2INT(rb_funcall(val, rb_intern("to_i"), 0)); entry->mtime.nanoseconds = NUM2INT(rb_funcall(val, rb_intern("usec"), 0)) * 1000; } else { entry->mtime.seconds = entry->mtime.nanoseconds = 0; } if ((val = rb_hash_aref(rb_entry, CSTR2SYM("ctime"))) != Qnil) { if (!rb_obj_is_kind_of(val, rb_cTime)) rb_raise(rb_eTypeError, ":ctime must be a Time instance"); entry->ctime.seconds = NUM2INT(rb_funcall(val, rb_intern("to_i"), 0)); entry->ctime.nanoseconds = NUM2INT(rb_funcall(val, rb_intern("usec"), 0)) * 1000; } else { entry->ctime.seconds = entry->ctime.nanoseconds = 0; } entry->flags = 0x0; entry->flags_extended = 0x0; val = rb_hash_aref(rb_entry, CSTR2SYM("stage")); if (!NIL_P(val)) { unsigned int stage = NUM2INT(val); entry->flags &= ~GIT_IDXENTRY_STAGEMASK; entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT) & GIT_IDXENTRY_STAGEMASK; } val = rb_hash_aref(rb_entry, CSTR2SYM("valid")); if (!NIL_P(val)) { entry->flags &= ~GIT_IDXENTRY_VALID; if (rugged_parse_bool(val)) entry->flags |= GIT_IDXENTRY_VALID; } else { entry->flags |= GIT_IDXENTRY_VALID; } } /* * call-seq: * index.write_tree([repo]) -> oid * * Write the index to a tree, either in the index's repository, or in * the given +repo+. * * If the index contains any files in conflict, writing the tree will fail. * * Returns the OID string of the written tree object. */ static VALUE rb_git_index_writetree(int argc, VALUE *argv, VALUE self) { git_index *index; git_oid tree_oid; int error; VALUE rb_repo; Data_Get_Struct(self, git_index, index); if (rb_scan_args(argc, argv, "01", &rb_repo) == 1) { git_repository *repo = NULL; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_index_write_tree_to(&tree_oid, index, repo); } else { error = git_index_write_tree(&tree_oid, index); } rugged_exception_check(error); return rugged_create_oid(&tree_oid); } /* * call-seq: * index.read_tree(tree) * * Clear the current index and start the index again on top of +tree+ * * Further index operations (+add+, +update+, +remove+, etc) will * be considered changes on top of +tree+. */ static VALUE rb_git_index_readtree(VALUE self, VALUE rb_tree) { git_index *index; git_tree *tree; int error; Data_Get_Struct(self, git_index, index); Data_Get_Struct(rb_tree, git_tree, tree); if (!rb_obj_is_kind_of(rb_tree, rb_cRuggedTree)) { rb_raise(rb_eTypeError, "A Rugged::Tree instance is required"); } error = git_index_read_tree(index, tree); rugged_exception_check(error); return Qnil; } static VALUE rb_git_diff_tree_to_index(VALUE self, VALUE rb_other, VALUE rb_options) { git_index *index; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_repository *repo; git_diff *diff = NULL; VALUE owner; int error; git_tree *other_tree; rugged_parse_diff_options(&opts, rb_options); Data_Get_Struct(self, git_index, index); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); // Need to flip the reverse option, so that the index is by default // the "old file" side of the diff. opts.flags ^= GIT_DIFF_REVERSE; Data_Get_Struct(rb_other, git_tree, other_tree); error = git_diff_tree_to_index(&diff, repo, other_tree, index, &opts); xfree(opts.pathspec.strings); rugged_exception_check(error); return rugged_diff_new(rb_cRuggedDiff, owner, diff); } static VALUE rb_git_diff_index_to_workdir(VALUE self, VALUE rb_options) { git_index *index; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_repository *repo; git_diff *diff = NULL; VALUE owner; int error; rugged_parse_diff_options(&opts, rb_options); Data_Get_Struct(self, git_index, index); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); error = git_diff_index_to_workdir(&diff, repo, index, &opts); xfree(opts.pathspec.strings); rugged_exception_check(error); return rugged_diff_new(rb_cRuggedDiff, owner, diff); } /* * call-seq: * index.conflicts? -> true or false * * Determines if the index contains entries representing conflicts. */ static VALUE rb_git_index_conflicts_p(VALUE self) { git_index *index; Data_Get_Struct(self, git_index, index); return git_index_has_conflicts(index) ? Qtrue : Qfalse; } /* * call-seq: * index.conflict_add(conflict) -> nil * * Add or update index entries that represent a conflict. * * +conflict+ has to be a hash containing +:ancestor+, +:ours+ and * +:theirs+ key/value pairs. Any of those paris can be +nil+ (or left out) * to indicate that the file was not present in the respective tree during * the merge. */ static VALUE rb_git_conflict_add(VALUE self, VALUE rb_conflict) { VALUE rb_ancestor, rb_ours, rb_theirs; git_index *index; git_index_entry ancestor, ours, theirs; int error; Check_Type(rb_conflict, T_HASH); rb_ancestor = rb_hash_aref(rb_conflict, CSTR2SYM("ancestor")); rb_ours = rb_hash_aref(rb_conflict, CSTR2SYM("ours")); rb_theirs = rb_hash_aref(rb_conflict, CSTR2SYM("theirs")); if (!NIL_P(rb_ancestor)) rb_git_indexentry_toC(&ancestor, rb_ancestor); if (!NIL_P(rb_ours)) rb_git_indexentry_toC(&ours, rb_ours); if (!NIL_P(rb_theirs)) rb_git_indexentry_toC(&theirs, rb_theirs); Data_Get_Struct(self, git_index, index); error = git_index_conflict_add(index, NIL_P(rb_ancestor) ? NULL : &ancestor, NIL_P(rb_theirs) ? NULL : &ours, NIL_P(rb_ours) ? NULL : &theirs); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.conflict_remove(path) -> nil * * Removes the index entries that represent the conflict at +path+. */ static VALUE rb_git_conflict_remove(VALUE self, VALUE rb_path) { git_index *index; int error; Check_Type(rb_path, T_STRING); Data_Get_Struct(self, git_index, index); error = git_index_conflict_remove(index, StringValueCStr(rb_path)); rugged_exception_check(error); return Qnil; } /* * call-seq: * index.conflict_get(path) -> conflict or nil * * Return index entries from the ancestor, our side and their side of * the conflict at +path+. * * If +:ancestor+, +:ours+ or +:theirs+ is +nil+, that indicates that +path+ * did not exist in the respective tree. * * Returns nil if no conflict is present at +path+. */ static VALUE rb_git_conflict_get(VALUE self, VALUE rb_path) { VALUE rb_result = rb_hash_new(); git_index *index; const git_index_entry *ancestor, *ours, *theirs; int error; Check_Type(rb_path, T_STRING); Data_Get_Struct(self, git_index, index); error = git_index_conflict_get(&ancestor, &ours, &theirs, index, StringValueCStr(rb_path)); if (error == GIT_ENOTFOUND) return Qnil; else rugged_exception_check(error); rb_hash_aset(rb_result, CSTR2SYM("ancestor"), rb_git_indexentry_fromC(ancestor)); rb_hash_aset(rb_result, CSTR2SYM("ours"), rb_git_indexentry_fromC(ours)); rb_hash_aset(rb_result, CSTR2SYM("theirs"), rb_git_indexentry_fromC(theirs)); return rb_result; } /* * call-seq: * index.merge_file(path[, options]) -> merge_file or nil * index.merge_file(path) -> merge_file or nil * * Return merge_file (in memory) from the ancestor, our side and their side of * the conflict at +path+. * * If +:ancestor+, +:ours+ or +:theirs+ is +nil+, that indicates that +path+ * did not exist in the respective tree. * * Returns nil if no conflict is present at +path+. * The following options can be passed in the +options+ Hash: * * :ancestor_label :: * The name of the ancestor branch used to decorate conflict markers. * * :our_label :: * The name of our branch used to decorate conflict markers. * * :their_label :: * The name of their branch used to decorate conflict markers. * * :favor :: * Specifies how and if conflicts are auto-resolved by favoring a specific * file output. Can be one of `:normal`, `:ours`, `:theirs` or `:union`. * Defaults to `:normal`. * * :style :: * Specifies the type of merge file to produce. Can be one of `:standard`, `:diff3`. Defaults to `:standard` * * :simplify :: * If true, the merge file is simplified by condensing non-alphanumeric regions. * */ static VALUE rb_git_merge_file(int argc, VALUE *argv, VALUE self) { VALUE rb_path, rb_options, rb_result; VALUE rb_repo = rugged_owner(self); git_repository *repo; git_index *index; const git_index_entry *ancestor, *ours, *theirs; git_merge_file_result merge_file_result = {0}; git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; int error; rb_scan_args(argc, argv, "1:", &rb_path, &rb_options); if (!NIL_P(rb_options)) rugged_parse_merge_file_options(&opts, rb_options); Check_Type(rb_path, T_STRING); Data_Get_Struct(self, git_index, index); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_index_conflict_get(&ancestor, &ours, &theirs, index, StringValueCStr(rb_path)); if (error == GIT_ENOTFOUND) return Qnil; else rugged_exception_check(error); if (ours == NULL) rb_raise(rb_eRuntimeError, "The conflict does not have a stage 2 entry"); else if (theirs == NULL) rb_raise(rb_eRuntimeError, "The conflict does not have a stage 3 entry"); error = git_merge_file_from_index(&merge_file_result, repo, ancestor, ours, theirs, &opts); rugged_exception_check(error); rb_result = rb_merge_file_result_fromC(&merge_file_result); git_merge_file_result_free(&merge_file_result); return rb_result; } /* * call-seq: * index.conflict_cleanup -> nil * * Remove all conflicting entries (entries with a stage greater than 0) * from the index. */ static VALUE rb_git_conflict_cleanup(VALUE self) { git_index *index; Data_Get_Struct(self, git_index, index); git_index_conflict_cleanup(index); return Qnil; } /* * call-seq: * index.conflicts -> conflicts * * Return all conflicts in +index+. * * Each conflict is represented as a Hash with +:ancestor+, +:ours+ or * +:theirs+ key-value pairs, each containing index entry data. * * If the value of the +:ancestor+, +:ours+ or +:theirs+ key is +nil+, * that indicates that file in conflict did not exists in the respective tree. */ static VALUE rb_git_index_conflicts(VALUE self) { VALUE rb_conflicts = rb_ary_new(); git_index *index; git_index_conflict_iterator *iter; const git_index_entry *ancestor, *ours, *theirs; int error; Data_Get_Struct(self, git_index, index); error = git_index_conflict_iterator_new(&iter, index); rugged_exception_check(error); while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iter)) == GIT_OK) { VALUE rb_conflict = rb_hash_new(); rb_hash_aset(rb_conflict, CSTR2SYM("ancestor"), rb_git_indexentry_fromC(ancestor)); rb_hash_aset(rb_conflict, CSTR2SYM("ours"), rb_git_indexentry_fromC(ours)); rb_hash_aset(rb_conflict, CSTR2SYM("theirs"), rb_git_indexentry_fromC(theirs)); rb_ary_push(rb_conflicts, rb_conflict); } git_index_conflict_iterator_free(iter); if (error != GIT_ITEROVER) rugged_exception_check(error); return rb_conflicts; } /* * Document-class: Rugged::Index * * == Index Entries * * Index entries are represented as Hash instances with the following key/value pairs: * * path: :: * The entry's path in the index. * * oid: :: * The oid of the entry's git object (blob / tree). * * dev: :: * The device for the index entry. * * ino: :: * The inode for the index entry. * * mode: :: * The current permissions of the index entry. * * gid: :: * Group ID of the index entry's owner. * * uid: :: * User ID of the index entry's owner. * * file_size: :: * The index entry's size, in bytes. * * valid: :: * +true+ if the index entry is valid, +false+ otherwise. * * stage: :: * The current stage of the index entry. * * mtime: :: * A Time instance representing the index entry's time of last modification. * * mtime: :: * A Time instance representing the index entry's time of last status change * (ie. change of owner, group, mode, etc.). */ void Init_rugged_index(void) { /* * Index */ rb_cRuggedIndex = rb_define_class_under(rb_mRugged, "Index", rb_cObject); rb_define_singleton_method(rb_cRuggedIndex, "new", rb_git_index_new, -1); rb_define_method(rb_cRuggedIndex, "count", rb_git_index_count, 0); rb_define_method(rb_cRuggedIndex, "reload", rb_git_index_read, 0); rb_define_method(rb_cRuggedIndex, "clear", rb_git_index_clear, 0); rb_define_method(rb_cRuggedIndex, "write", rb_git_index_write, 0); rb_define_method(rb_cRuggedIndex, "get", rb_git_index_get, -1); rb_define_method(rb_cRuggedIndex, "[]", rb_git_index_get, -1); rb_define_method(rb_cRuggedIndex, "each", rb_git_index_each, 0); rb_define_private_method(rb_cRuggedIndex, "diff_tree_to_index", rb_git_diff_tree_to_index, 2); rb_define_private_method(rb_cRuggedIndex, "diff_index_to_workdir", rb_git_diff_index_to_workdir, 1); rb_define_method(rb_cRuggedIndex, "conflicts?", rb_git_index_conflicts_p, 0); rb_define_method(rb_cRuggedIndex, "conflicts", rb_git_index_conflicts, 0); rb_define_method(rb_cRuggedIndex, "conflict_get", rb_git_conflict_get, 1); rb_define_method(rb_cRuggedIndex, "conflict_add", rb_git_conflict_add, 1); rb_define_method(rb_cRuggedIndex, "conflict_remove", rb_git_conflict_remove, 1); rb_define_method(rb_cRuggedIndex, "conflict_cleanup", rb_git_conflict_cleanup, 0); rb_define_method(rb_cRuggedIndex, "merge_file", rb_git_merge_file, -1); rb_define_method(rb_cRuggedIndex, "add", rb_git_index_add, 1); rb_define_method(rb_cRuggedIndex, "update", rb_git_index_add, 1); rb_define_method(rb_cRuggedIndex, "<<", rb_git_index_add, 1); rb_define_method(rb_cRuggedIndex, "remove", rb_git_index_remove, -1); rb_define_method(rb_cRuggedIndex, "remove_dir", rb_git_index_remove_directory, -1); rb_define_method(rb_cRuggedIndex, "add_all", rb_git_index_add_all, -1); rb_define_method(rb_cRuggedIndex, "update_all", rb_git_index_update_all, -1); rb_define_method(rb_cRuggedIndex, "remove_all", rb_git_index_remove_all, -1); rb_define_method(rb_cRuggedIndex, "write_tree", rb_git_index_writetree, -1); rb_define_method(rb_cRuggedIndex, "read_tree", rb_git_index_readtree, 1); rb_const_set(rb_cRuggedIndex, rb_intern("ENTRY_FLAGS_STAGE"), INT2FIX(GIT_IDXENTRY_STAGEMASK)); rb_const_set(rb_cRuggedIndex, rb_intern("ENTRY_FLAGS_STAGE_SHIFT"), INT2FIX(GIT_IDXENTRY_STAGESHIFT)); rb_const_set(rb_cRuggedIndex, rb_intern("ENTRY_FLAGS_VALID"), INT2FIX(GIT_IDXENTRY_VALID)); } rugged-0.26.0/ext/rugged/rugged_diff_hunk.c0000644000175000017500000000515413147033070020720 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_cRuggedDiff; VALUE rb_cRuggedDiffHunk; VALUE rugged_diff_hunk_new(VALUE owner, size_t hunk_idx, const git_diff_hunk *hunk, size_t lines_in_hunk) { VALUE rb_hunk = rb_class_new_instance(0, NULL, rb_cRuggedDiffHunk); rugged_set_owner(rb_hunk, owner); rb_iv_set(rb_hunk, "@header", rb_str_new(hunk->header, hunk->header_len)); rb_iv_set(rb_hunk, "@line_count", INT2FIX(lines_in_hunk)); rb_iv_set(rb_hunk, "@hunk_index", INT2FIX(hunk_idx)); rb_iv_set(rb_hunk, "@old_start", INT2FIX(hunk->old_start)); rb_iv_set(rb_hunk, "@old_lines", INT2FIX(hunk->old_lines)); rb_iv_set(rb_hunk, "@new_start", INT2FIX(hunk->new_start)); rb_iv_set(rb_hunk, "@new_lines", INT2FIX(hunk->new_lines)); return rb_hunk; } /* * call-seq: * hunk.each_line { |line| } -> self * hunk.each_line -> Enumerator * * If given a block, yields each line that is part of the current hunk. * * If no block is given, an enumerator is returned instead. */ static VALUE rb_git_diff_hunk_each_line(VALUE self) { git_patch *patch; int error = 0, l, lines_count, hunk_idx; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_line"), self); } Data_Get_Struct(rugged_owner(self), git_patch, patch); lines_count = FIX2INT(rb_iv_get(self, "@line_count")); hunk_idx = FIX2INT(rb_iv_get(self, "@hunk_index")); for (l = 0; l < lines_count; ++l) { const git_diff_line *line; error = git_patch_get_line_in_hunk(&line, patch, hunk_idx, l); if (error) break; rb_yield(rugged_diff_line_new(line)); } rugged_exception_check(error); return self; } void Init_rugged_diff_hunk(void) { rb_cRuggedDiffHunk = rb_define_class_under(rb_cRuggedDiff, "Hunk", rb_cObject); rb_include_module(rb_cRuggedDiffHunk, rb_mEnumerable); rb_define_method(rb_cRuggedDiffHunk, "each", rb_git_diff_hunk_each_line, 0); rb_define_method(rb_cRuggedDiffHunk, "each_line", rb_git_diff_hunk_each_line, 0); rb_define_attr(rb_cRuggedDiffHunk, "header", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "line_count", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "hunk_index", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "old_start", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "old_lines", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "new_start", 1, 0); rb_define_attr(rb_cRuggedDiffHunk, "new_lines", 1, 0); rb_define_alias(rb_cRuggedDiffHunk, "count", "line_count"); rb_define_alias(rb_cRuggedDiffHunk, "size", "line_count"); } rugged-0.26.0/ext/rugged/rugged_diff_line.c0000644000175000017500000000366013147033070020702 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_cRuggedDiff; VALUE rb_cRuggedDiffLine; VALUE rugged_diff_line_new(const git_diff_line *line) { VALUE rb_line = rb_class_new_instance(0, NULL, rb_cRuggedDiffLine), rb_line_origin; switch(line->origin) { case GIT_DIFF_LINE_CONTEXT: rb_line_origin = CSTR2SYM("context"); break; case GIT_DIFF_LINE_ADDITION: rb_line_origin = CSTR2SYM("addition"); break; case GIT_DIFF_LINE_DELETION: rb_line_origin = CSTR2SYM("deletion"); break; case GIT_DIFF_LINE_CONTEXT_EOFNL: /* neither file has newline at the end */ rb_line_origin = CSTR2SYM("eof_no_newline"); break; case GIT_DIFF_LINE_ADD_EOFNL: /* added at end of old file */ rb_line_origin = CSTR2SYM("eof_newline_added"); break; case GIT_DIFF_LINE_DEL_EOFNL: /* removed at end of old file */ rb_line_origin = CSTR2SYM("eof_newline_removed"); break; case GIT_DIFF_LINE_FILE_HDR: rb_line_origin = CSTR2SYM("file_header"); break; case GIT_DIFF_LINE_HUNK_HDR: rb_line_origin = CSTR2SYM("hunk_header"); break; case GIT_DIFF_LINE_BINARY: rb_line_origin = CSTR2SYM("binary"); break; default: /* FIXME: raise here instead? */ rb_line_origin = CSTR2SYM("unknown"); } rb_iv_set(rb_line, "@line_origin", rb_line_origin); rb_iv_set(rb_line, "@content", rb_str_new(line->content, line->content_len)); rb_iv_set(rb_line, "@old_lineno", INT2FIX(line->old_lineno)); rb_iv_set(rb_line, "@new_lineno", INT2FIX(line->new_lineno)); if (line->content_offset == -1) rb_iv_set(rb_line, "@content_offset", Qnil); else rb_iv_set(rb_line, "@content_offset", INT2FIX(line->content_offset)); return rb_line; } void Init_rugged_diff_line(void) { rb_cRuggedDiffLine = rb_define_class_under(rb_cRuggedDiff, "Line", rb_cObject); } rugged-0.26.0/ext/rugged/rugged_patch.c0000644000175000017500000002451213147033070020061 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedDiffDelta; VALUE rb_cRuggedPatch; /* * call-seq: * Patch.from_strings(old_content = nil, new_content = nil, options = {}) -> patch * * Directly generate a Rugged::Patch from the difference between the content of * the two strings `old_content` and `new_content`. * * The following options can be passed in the +options+ Hash: * * :old_path :: * An optional string to treat +blob+ as if it had this filename. * * :new_path :: * An optional string to treat +other+ as if it had this filename. * * Additionally, `options` can also contain all other valid diff options * (see Rugged::Tree#diff for a complete list). */ VALUE rb_git_patch_from_strings(int argc, VALUE *argv, VALUE self) { git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_patch *patch; char * old_path = NULL, * new_path = NULL; VALUE rb_old_buffer, rb_new_buffer, rb_options; rb_scan_args(argc, argv, "02:", &rb_old_buffer, &rb_new_buffer, &rb_options); if (!NIL_P(rb_options)) { VALUE rb_value; rb_value = rb_hash_aref(rb_options, CSTR2SYM("old_path")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); old_path = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("new_path")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); new_path = StringValueCStr(rb_value); } rugged_parse_diff_options(&opts, rb_options); } rugged_exception_check(git_patch_from_buffers(&patch, NIL_P(rb_old_buffer) ? NULL : StringValuePtr(rb_old_buffer), NIL_P(rb_old_buffer) ? 0 : RSTRING_LEN(rb_old_buffer), old_path, NIL_P(rb_new_buffer) ? NULL : StringValuePtr(rb_new_buffer), NIL_P(rb_new_buffer) ? 0 : RSTRING_LEN(rb_new_buffer), new_path, &opts )); return rugged_patch_new(self, patch); } VALUE rugged_patch_new(VALUE owner, git_patch *patch) { VALUE rb_patch = Data_Wrap_Struct(rb_cRuggedPatch, NULL, &git_patch_free, patch); rugged_set_owner(rb_patch, owner); return rb_patch; } /* * call-seq: * patch.each_hunk { |hunk| } -> self * patch.each_hunk -> enumerator * * If given a block, yields each hunk that is part of the patch. * * If no block is given, an enumerator is returned instead. */ static VALUE rb_git_diff_patch_each_hunk(VALUE self) { git_patch *patch; const git_diff_hunk *hunk; size_t lines_in_hunk; int error = 0; size_t hunks_count, h; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_hunk"), self); } Data_Get_Struct(self, git_patch, patch); hunks_count = git_patch_num_hunks(patch); for (h = 0; h < hunks_count; ++h) { error = git_patch_get_hunk(&hunk, &lines_in_hunk, patch, h); if (error) break; rb_yield(rugged_diff_hunk_new(self, h, hunk, lines_in_hunk)); } rugged_exception_check(error); return self; } /* * call-seq: * patch.hunk_count -> int * * Returns the number of hunks in the patch. */ static VALUE rb_git_diff_patch_hunk_count(VALUE self) { git_patch *patch; Data_Get_Struct(self, git_patch, patch); return INT2FIX(git_patch_num_hunks(patch)); } /* * call-seq: * patch.delta -> delta * * Returns the delta object associated with the patch. */ static VALUE rb_git_diff_patch_delta(VALUE self) { git_patch *patch; Data_Get_Struct(self, git_patch, patch); return rugged_diff_delta_new(rugged_owner(self), git_patch_get_delta(patch)); } /* * call-seq: * patch.stat -> int, int * * Returns the number of additions and deletions in the patch. */ static VALUE rb_git_diff_patch_stat(VALUE self) { git_patch *patch; size_t additions, deletions; Data_Get_Struct(self, git_patch, patch); git_patch_line_stats(NULL, &additions, &deletions, patch); return rb_ary_new3(2, INT2FIX(additions), INT2FIX(deletions)); } enum { EXCLUDE_CONTEXT = (1u << 0), EXCLUDE_ADDITIONS = (1u << 1), EXCLUDE_DELETIONS = (1u << 2), EXCLUDE_EOFNL = (1u << 3) }; /* * call-seq: * patch.lines(options = {}) -> int * * The following options can be passed in the +options+ Hash: * * :exclude_context :: * Boolean value specifying that context line counts should be excluded from * the returned total. * * :exclude_additions :: * Boolean value specifying that addition line counts should be excluded from * the returned total. * * :exclude_deletions :: * Boolean value specifying that deletion line counts should be excluded from * the returned total. * * :exclude_eofnl :: * Boolean value specifying that end-of-file newline change lines should * be excluded from the returned total. * * Returns the total number of lines in the patch, depending on the options * specified. */ static VALUE rb_git_diff_patch_lines(int argc, VALUE *argv, VALUE self) { git_patch *patch; size_t lines = 0; int options = 0; VALUE rb_options; Data_Get_Struct(self, git_patch, patch); rb_scan_args(argc, argv, "0:", &rb_options); if (!NIL_P(rb_options)) { if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_context")))) { options |= EXCLUDE_CONTEXT; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_additions")))) { options |= EXCLUDE_ADDITIONS; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_deletions")))) { options |= EXCLUDE_DELETIONS; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_eofnl")))) { options |= EXCLUDE_EOFNL; } } if (options == 0) { size_t i = 0, hunks_count = git_patch_num_hunks(patch); for (i = 0; i < hunks_count; ++i) { lines += git_patch_num_lines_in_hunk(patch, i); } } else { size_t i = 0, hunks_count = git_patch_num_hunks(patch); for (i = 0; i < hunks_count; ++i) { size_t lines_in_hunk = git_patch_num_lines_in_hunk(patch, i), l = 0; for (l = 0; l < lines_in_hunk; ++l) { const git_diff_line *line; rugged_exception_check( git_patch_get_line_in_hunk(&line, patch, i, l) ); switch (line->origin) { case GIT_DIFF_LINE_CONTEXT: if (options & EXCLUDE_CONTEXT) continue; break; case GIT_DIFF_LINE_ADDITION: if (options & EXCLUDE_ADDITIONS) continue; break; case GIT_DIFF_LINE_DELETION: if (options & EXCLUDE_DELETIONS) continue; break; case GIT_DIFF_LINE_ADD_EOFNL: case GIT_DIFF_LINE_DEL_EOFNL: if (options & EXCLUDE_EOFNL) continue; break; } lines += 1; } } } return INT2FIX(lines); } /* * call-seq: * patch.bytesize(options = {}) -> int * * The following options can be passed in the +options+ Hash: * * :exclude_context :: * Boolean value specifying that context lines should be excluded when * counting the number of bytes in the patch. * * :exclude_hunk_headers :: * Boolean value specifying that hunk headers should be excluded when * counting the number of bytes in the patch. * * :exclude_file_headers :: * Boolean value specifying that file headers should be excluded when * counting the number of bytes in the patch. * * Returns the number of bytes in the patch, depending on which options are * specified. */ static VALUE rb_git_diff_patch_bytesize(int argc, VALUE *argv, VALUE self) { git_patch *patch; size_t bytesize; VALUE rb_options; int include_context, include_hunk_headers, include_file_headers; Data_Get_Struct(self, git_patch, patch); include_context = include_hunk_headers = include_file_headers = 1; rb_scan_args(argc, argv, "0:", &rb_options); if (!NIL_P(rb_options)) { if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_context")))) { include_context = 0; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_hunk_headers")))) { include_hunk_headers = 0; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_file_headers")))) { include_file_headers = 0; } } bytesize = git_patch_size(patch, include_context, include_hunk_headers, include_file_headers); return INT2FIX(bytesize); } static int patch_print_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { VALUE rb_buffer = (VALUE)payload; switch (line->origin) { case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_ADDITION: case GIT_DIFF_LINE_DELETION: rb_ary_push(rb_buffer, rb_str_new(&line->origin, 1)); } rb_ary_push(rb_buffer, rb_str_new(line->content, line->content_len)); return GIT_OK; } static int patch_print_header_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { VALUE rb_buffer = (VALUE)payload; if (line->origin == GIT_DIFF_LINE_FILE_HDR) { rb_ary_push(rb_buffer, rb_str_new(line->content, line->content_len)); return GIT_OK; } else { return GIT_ITEROVER; } } /* * call-seq: * patch.to_s -> str * * Returns the contents of the patch as a single diff string. */ static VALUE rb_git_diff_patch_to_s(VALUE self) { git_patch *patch; VALUE rb_buffer = rb_ary_new(); Data_Get_Struct(self, git_patch, patch); rugged_exception_check(git_patch_print(patch, patch_print_cb, (void*)rb_buffer)); return rb_ary_join(rb_buffer, Qnil); } /* * call-seq: * patch.header -> str * * Returns only the header of the patch as a string. */ static VALUE rb_git_diff_patch_header(VALUE self) { git_patch *patch; int error = 0; VALUE rb_buffer = rb_ary_new(); Data_Get_Struct(self, git_patch, patch); error = git_patch_print(patch, patch_print_header_cb, (void*)rb_buffer); if (error && error != GIT_ITEROVER) rugged_exception_check(error); return rb_ary_join(rb_buffer, Qnil); } void Init_rugged_patch(void) { rb_cRuggedPatch = rb_define_class_under(rb_mRugged, "Patch", rb_cObject); rb_define_singleton_method(rb_cRuggedPatch, "from_strings", rb_git_patch_from_strings, -1); rb_define_method(rb_cRuggedPatch, "stat", rb_git_diff_patch_stat, 0); rb_define_method(rb_cRuggedPatch, "lines", rb_git_diff_patch_lines, -1); rb_define_method(rb_cRuggedPatch, "bytesize", rb_git_diff_patch_bytesize, -1); rb_define_method(rb_cRuggedPatch, "delta", rb_git_diff_patch_delta, 0); rb_define_method(rb_cRuggedPatch, "header", rb_git_diff_patch_header, 0); rb_define_method(rb_cRuggedPatch, "to_s", rb_git_diff_patch_to_s, 0); rb_define_method(rb_cRuggedPatch, "each_hunk", rb_git_diff_patch_each_hunk, 0); rb_define_method(rb_cRuggedPatch, "hunk_count", rb_git_diff_patch_hunk_count, 0); } rugged-0.26.0/ext/rugged/rugged_blame.c0000644000175000017500000001747713147033070020056 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_cRuggedBlame; static VALUE rb_git_blame_hunk_fromC(const git_blame_hunk *hunk) { VALUE rb_hunk; if (!hunk) { return Qnil; } rb_hunk = rb_hash_new(); rb_hash_aset(rb_hunk, CSTR2SYM("lines_in_hunk"), UINT2NUM(hunk->lines_in_hunk)); rb_hash_aset(rb_hunk, CSTR2SYM("final_commit_id"), rugged_create_oid(&(hunk->final_commit_id))); rb_hash_aset(rb_hunk, CSTR2SYM("final_start_line_number"), UINT2NUM(hunk->final_start_line_number)); rb_hash_aset(rb_hunk, CSTR2SYM("final_signature"), hunk->final_signature ? rugged_signature_new(hunk->final_signature, NULL) : Qnil); rb_hash_aset(rb_hunk, CSTR2SYM("orig_commit_id"), rugged_create_oid(&(hunk->orig_commit_id))); rb_hash_aset(rb_hunk, CSTR2SYM("orig_path"), hunk->orig_path ? rb_str_new2(hunk->orig_path) : Qnil); rb_hash_aset(rb_hunk, CSTR2SYM("orig_start_line_number"), UINT2NUM(hunk->orig_start_line_number)); rb_hash_aset(rb_hunk, CSTR2SYM("orig_signature"), hunk->orig_signature ? rugged_signature_new(hunk->orig_signature, NULL) : Qnil); rb_hash_aset(rb_hunk, CSTR2SYM("boundary"), hunk->boundary ? Qtrue : Qfalse); return rb_hunk; } static void rugged_parse_blame_options(git_blame_options *opts, git_repository *repo, VALUE rb_options) { if (!NIL_P(rb_options)) { VALUE rb_value; Check_Type(rb_options, T_HASH); rb_value = rb_hash_aref(rb_options, CSTR2SYM("min_line")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->min_line = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("max_line")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->max_line = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("newest_commit")); if (!NIL_P(rb_value)) { int error = rugged_oid_get(&opts->newest_commit, repo, rb_value); rugged_exception_check(error); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("oldest_commit")); if (!NIL_P(rb_value)) { int error = rugged_oid_get(&opts->oldest_commit, repo, rb_value); rugged_exception_check(error); } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("track_copies_same_file")))) { opts->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("track_copies_same_commit_moves")))) { opts->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("track_copies_same_commit_copies")))) { opts->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("track_copies_any_commit_copies")))) { opts->flags |= GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES; } } } /* * call-seq: * Blame.new(repo, path, options = {}) -> blame * * Get blame data for the file at +path+ in +repo+. * * The following options can be passed in the +options+ Hash: * * :newest_commit :: * The ID of the newest commit to consider in the blame. Defaults to +HEAD+. * This can either be a Rugged::Object instance, or a full or abbreviated * SHA1 id. * * :oldest_commit :: * The id of the oldest commit to consider. Defaults to the first commit * encountered with a NULL parent. This can either be a Rugged::Object * instance, or a full or abbreviated SHA1 id. * * :min_line :: * The first line in the file to blame. Line numbers start with 1. * Defaults to +1+. * * :max_line :: * The last line in the file to blame. Defaults to the last line in * the file. * * :track_copies_same_file :: * If this value is +true+, lines that have moved within a file will be * tracked (like `git blame -M`). * * :track_copies_same_commit_moves :: * If this value is +true+, lines that have moved across files in the same * commit will be tracked (like `git blame -C`). * * :track_copies_same_commit_copies :: * If this value is +true+, lines that have been copied from another file * that exists in the same commit will be tracked (like `git blame -CC`). * * :track_copies_any_commit_copies :: * If this value is +true+, lines that have been copied from another file * that exists in *any* commit will be tracked (like `git blame -CCC`). * */ static VALUE rb_git_blame_new(int argc, VALUE *argv, VALUE klass) { VALUE rb_repo, rb_path, rb_options; git_repository *repo; git_blame *blame; git_blame_options opts = GIT_BLAME_OPTIONS_INIT; rb_scan_args(argc, argv, "20:", &rb_repo, &rb_path, &rb_options); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_path, T_STRING); rugged_parse_blame_options(&opts, repo, rb_options); rugged_exception_check(git_blame_file( &blame, repo, StringValueCStr(rb_path), &opts )); return Data_Wrap_Struct(klass, NULL, &git_blame_free, blame); } /* * call-seq: * blame.for_line(line_no) -> hunk * * Returns the blame hunk data for the given +line_no+ in +blame+. * Line number counting starts with +1+. */ static VALUE rb_git_blame_for_line(VALUE self, VALUE rb_line_no) { git_blame *blame; int line_no; Data_Get_Struct(self, git_blame, blame); Check_Type(rb_line_no, T_FIXNUM); line_no = NUM2INT(rb_line_no); if (line_no < 0) { rb_raise(rb_eArgError, "line number can't be negative"); } return rb_git_blame_hunk_fromC( git_blame_get_hunk_byline(blame, (uint32_t)line_no) ); } /* * call-seq: * blame.count -> count * blame.size -> count * * Returns the total +count+ of blame hunks in +blame+. */ static VALUE rb_git_blame_count(VALUE self) { git_blame *blame; Data_Get_Struct(self, git_blame, blame); return UINT2NUM(git_blame_get_hunk_count(blame)); } /* * call-seq: * blame[index] -> hunk * * Returns the blame hunk data at the given +index+ in +blame+. * * Negative indices count backward from the end of the blame hunks (-1 is the last * element). * * Returns +nil+ if no blame hunk exists at the given +index+. */ static VALUE rb_git_blame_get_by_index(VALUE self, VALUE rb_index) { git_blame *blame; int index; uint32_t blame_count; Data_Get_Struct(self, git_blame, blame); Check_Type(rb_index, T_FIXNUM); index = NUM2INT(rb_index); blame_count = git_blame_get_hunk_count(blame); if (index < 0) { if ((uint32_t)(-index) > blame_count) { return Qnil; } return rb_git_blame_hunk_fromC( git_blame_get_hunk_byindex(blame, (uint32_t)(blame_count + index)) ); } if ((uint32_t)index > blame_count) return Qnil; return rb_git_blame_hunk_fromC( git_blame_get_hunk_byindex(blame, (uint32_t)index) ); } /* * call-seq: * blame.each { |hunk| ... } -> blame * blame.each -> enumerator * * If given a block, yields each +hunk+ that is part of +blame+. * If no block is given, an enumerator will be returned. */ static VALUE rb_git_blame_each(VALUE self) { git_blame *blame; uint32_t i, blame_count; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each"), self); } Data_Get_Struct(self, git_blame, blame); blame_count = git_blame_get_hunk_count(blame); for (i = 0; i < blame_count; ++i) { rb_yield(rb_git_blame_hunk_fromC( git_blame_get_hunk_byindex(blame, i) )); } return self; } void Init_rugged_blame(void) { rb_cRuggedBlame = rb_define_class_under(rb_mRugged, "Blame", rb_cObject); rb_include_module(rb_cRuggedBlame, rb_mEnumerable); rb_define_singleton_method(rb_cRuggedBlame, "new", rb_git_blame_new, -1); rb_define_method(rb_cRuggedBlame, "[]", rb_git_blame_get_by_index, 1); rb_define_method(rb_cRuggedBlame, "for_line", rb_git_blame_for_line, 1); rb_define_method(rb_cRuggedBlame, "count", rb_git_blame_count, 0); rb_define_method(rb_cRuggedBlame, "size", rb_git_blame_count, 0); rb_define_method(rb_cRuggedBlame, "each", rb_git_blame_each, 0); } rugged-0.26.0/ext/rugged/rugged_reference.c0000644000175000017500000002376613147033070020732 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; VALUE rb_cRuggedReference; void rb_git_ref__free(git_reference *ref) { git_reference_free(ref); } VALUE rugged_ref_new(VALUE klass, VALUE owner, git_reference *ref) { VALUE rb_ref = Data_Wrap_Struct(klass, NULL, &rb_git_ref__free, ref); rugged_set_owner(rb_ref, owner); return rb_ref; } const char * rugged_refname_from_string_or_ref(VALUE rb_name_or_ref) { if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference)) rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_ref) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); return StringValueCStr(rb_name_or_ref); } /* * call-seq: * Reference.valid_name?(ref_name) -> true or false * * Check if a reference name is well-formed. * * Valid reference names must follow one of two patterns: * * 1. Top-level names must contain only capital letters and underscores, * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). * 2. Names prefixed with "refs/" can be almost anything. You must avoid * the characters '~', '^', ':', '\\', '?', '[', and '*', and the * sequences ".." and "@{" which have special meaning to revparse. * * Returns true if the reference name is valid, false if not. */ static VALUE rb_git_ref_valid_name(VALUE klass, VALUE rb_name) { Check_Type(rb_name, T_STRING); return git_reference_is_valid_name(StringValueCStr(rb_name)) == 1 ? Qtrue : Qfalse; } /* * call-seq: * ref.peel -> oid * * Peels tag objects to the sha that they point at. Replicates * +git show-ref --dereference+. */ static VALUE rb_git_ref_peel(VALUE self) { /* Leave room for \0 */ git_reference *ref; git_object *object; char oid[GIT_OID_HEXSZ + 1]; int error; Data_Get_Struct(self, git_reference, ref); error = git_reference_peel(&object, ref, GIT_OBJ_ANY); if (error == GIT_ENOTFOUND) return Qnil; else rugged_exception_check(error); if (git_reference_type(ref) == GIT_REF_OID && !git_oid_cmp(git_object_id(object), git_reference_target(ref))) { git_object_free(object); return Qnil; } else { git_oid_tostr(oid, sizeof(oid), git_object_id(object)); git_object_free(object); return rb_str_new_utf8(oid); } } /* * call-seq: * reference.target_id -> id * reference.target_id -> ref_name * * Return the target of +reference+. * * If +reference+ is a symbolic reference, it returns the target * reference object. * * If +reference+ is a direct reference, it returns the target object. * * ref1.type #=> :symbolic * ref1.target #=> # * * ref2.type #=> :direct * ref2.target #=> # */ static VALUE rb_git_ref_target(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); if (git_reference_type(ref) == GIT_REF_OID) { git_object *target; rugged_exception_check( git_object_lookup(&target, git_reference_owner(ref), git_reference_target(ref), GIT_OBJ_ANY) ); return rugged_object_new(rugged_owner(self), target); } else { git_reference *target; rugged_exception_check( git_reference_lookup(&target, git_reference_owner(ref), git_reference_symbolic_target(ref)) ); return rugged_ref_new(rb_cRuggedReference, rugged_owner(self), target); } } /* * call-seq: * reference.target_id -> id * reference.target_id -> ref_name * * Return the target identifier of +reference+. * * If +reference+ is a symbolic reference, it returns the canonical * name of the target reference. * * If +reference+ is a direct reference, it returns the sha id of the target. * * ref1.type #=> :symbolic * ref1.target_id #=> "refs/heads/master" * * ref2.type #=> :direct * ref2.target_id #=> "de5ba987198bcf2518885f0fc1350e5172cded78" */ static VALUE rb_git_ref_target_id(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); if (git_reference_type(ref) == GIT_REF_OID) { return rugged_create_oid(git_reference_target(ref)); } else { return rb_str_new_utf8(git_reference_symbolic_target(ref)); } } /* * call-seq: * reference.type -> :symbolic or :direct * * Return whether the reference is +:symbolic+ or +:direct+ */ static VALUE rb_git_ref_type(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); switch (git_reference_type(ref)) { case GIT_REF_OID: return CSTR2SYM("direct"); case GIT_REF_SYMBOLIC: return CSTR2SYM("symbolic"); default: return Qnil; } } /* * call-seq: * reference.name -> name * reference.canonical_name -> name * * Returns the fully qualified name of the reference. * * +name+ gets overwritten in subclasess like Rugged::Branch or Rugged::Tag * to return "nicer" names for presentational purposes, while +canonical_name+ * is always supposed to return the fully qualified reference path. * * reference.name #=> 'HEAD' */ static VALUE rb_git_ref_name(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); return rb_str_new_utf8(git_reference_name(ref)); } /* * call-seq: * reference.resolve -> peeled_ref * * Peel a symbolic reference to its target reference. * * r1.type #=> :symbolic * r1.name #=> 'HEAD' * r1.target #=> 'refs/heads/master' * * r2 = r1.resolve #=> # * r2.target #=> '9d09060c850defbc7711d08b57def0d14e742f4e' */ static VALUE rb_git_ref_resolve(VALUE self) { git_reference *ref; git_reference *resolved; int error; Data_Get_Struct(self, git_reference, ref); error = git_reference_resolve(&resolved, ref); rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, rugged_owner(self), resolved); } static VALUE reflog_entry_new(const git_reflog_entry *entry) { VALUE rb_entry = rb_hash_new(); const char *message; rb_hash_aset(rb_entry, CSTR2SYM("id_old"), rugged_create_oid(git_reflog_entry_id_old(entry)) ); rb_hash_aset(rb_entry, CSTR2SYM("id_new"), rugged_create_oid(git_reflog_entry_id_new(entry)) ); rb_hash_aset(rb_entry, CSTR2SYM("committer"), rugged_signature_new(git_reflog_entry_committer(entry), NULL) ); if ((message = git_reflog_entry_message(entry)) != NULL) { rb_hash_aset(rb_entry, CSTR2SYM("message"), rb_str_new_utf8(message)); } return rb_entry; } /* * call-seq: * reference.log -> [reflog_entry, ...] * * Return an array with the log of all modifications to this reference * * Each +reflog_entry+ is a hash with the following keys: * * - +:id_old+: previous OID before the change * - +:id_new+: OID after the change * - +:committer+: author of the change * - +:message+: message for the change * * Example: * * reference.log #=> [ * # { * # :id_old => nil, * # :id_new => '9d09060c850defbc7711d08b57def0d14e742f4e', * # :committer => {:name => 'Vicent Marti', :email => {'vicent@github.com'}}, * # :message => 'created reference' * # }, ... ] */ static VALUE rb_git_reflog(VALUE self) { git_reflog *reflog; git_reference *ref; int error; VALUE rb_log; size_t i, ref_count; Data_Get_Struct(self, git_reference, ref); error = git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)); rugged_exception_check(error); ref_count = git_reflog_entrycount(reflog); rb_log = rb_ary_new2(ref_count); for (i = 0; i < ref_count; ++i) { const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, ref_count - i - 1); rb_ary_push(rb_log, reflog_entry_new(entry)); } git_reflog_free(reflog); return rb_log; } /* * call-seq: * reference.log? -> true or false * * Return +true+ if the reference has a reflog, +false+ otherwise. */ static VALUE rb_git_has_reflog(VALUE self) { git_reference *ref; git_repository *repo; Data_Get_Struct(self, git_reference, ref); repo = git_reference_owner(ref); return git_reference_has_log(repo, git_reference_name(ref)) ? Qtrue : Qfalse; } /* * call-seq: * reference.branch? -> true or false * * Returns +true+ if +reference+ is a local branch, false otherwise. */ static VALUE rb_git_ref_is_branch(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); return git_reference_is_branch(ref) ? Qtrue : Qfalse; } /* * call-seq: * reference.remote? -> true or false * * Returns +true+ if +reference+ is a remote branch, false otherwise. */ static VALUE rb_git_ref_is_remote(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); return git_reference_is_remote(ref) ? Qtrue : Qfalse; } /* * call-seq: * reference.tag? -> true or false * * Returns +true+ if +reference+ is a tag, false otherwise. */ static VALUE rb_git_ref_is_tag(VALUE self) { git_reference *ref; Data_Get_Struct(self, git_reference, ref); return git_reference_is_tag(ref) ? Qtrue : Qfalse; } void Init_rugged_reference(void) { rb_cRuggedReference = rb_define_class_under(rb_mRugged, "Reference", rb_cObject); rb_define_singleton_method(rb_cRuggedReference, "valid_name?", rb_git_ref_valid_name, 1); rb_define_method(rb_cRuggedReference, "target", rb_git_ref_target, 0); rb_define_method(rb_cRuggedReference, "target_id", rb_git_ref_target_id, 0); rb_define_method(rb_cRuggedReference, "peel", rb_git_ref_peel, 0); rb_define_method(rb_cRuggedReference, "type", rb_git_ref_type, 0); rb_define_method(rb_cRuggedReference, "name", rb_git_ref_name, 0); rb_define_method(rb_cRuggedReference, "canonical_name", rb_git_ref_name, 0); rb_define_method(rb_cRuggedReference, "resolve", rb_git_ref_resolve, 0); rb_define_method(rb_cRuggedReference, "branch?", rb_git_ref_is_branch, 0); rb_define_method(rb_cRuggedReference, "remote?", rb_git_ref_is_remote, 0); rb_define_method(rb_cRuggedReference, "tag?", rb_git_ref_is_tag, 0); rb_define_method(rb_cRuggedReference, "log", rb_git_reflog, 0); rb_define_method(rb_cRuggedReference, "log?", rb_git_has_reflog, 0); } rugged-0.26.0/ext/rugged/rugged.h0000644000175000017500000001266213147033070016712 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #ifndef __H_RUGGED_BINDINGS__ #define __H_RUGGED_BINDINGS__ // tell rbx not to use it's caching compat layer // by doing this we're making a promize to RBX that // we'll never modify the pointers we get back from RSTRING_PTR #define RSTRING_NOT_MODIFIED #include #ifndef HAVE_RUBY_ENCODING_H #error "Rugged requires Ruby 1.9+ to build" #else #include #endif #include #include #include #define rb_str_new_utf8(str) rb_enc_str_new(str, strlen(str), rb_utf8_encoding()) #define CSTR2SYM(s) (ID2SYM(rb_intern((s)))) /* * Initialization functions */ void Init_rugged_object(void); void Init_rugged_branch(void); void Init_rugged_branch_collection(void); void Init_rugged_commit(void); void Init_rugged_tree(void); void Init_rugged_tag(void); void Init_rugged_tag_collection(void); void Init_rugged_blob(void); void Init_rugged_index(void); void Init_rugged_repo(void); void Init_rugged_revwalk(void); void Init_rugged_reference(void); void Init_rugged_reference_collection(void); void Init_rugged_config(void); void Init_rugged_remote(void); void Init_rugged_remote_collection(void); void Init_rugged_notes(void); void Init_rugged_settings(void); void Init_rugged_submodule(void); void Init_rugged_submodule_collection(void); void Init_rugged_diff(void); void Init_rugged_patch(void); void Init_rugged_diff_delta(void); void Init_rugged_diff_hunk(void); void Init_rugged_diff_line(void); void Init_rugged_blame(void); void Init_rugged_cred(void); void Init_rugged_backend(void); void Init_rugged_rebase(void); VALUE rb_git_object_init(git_otype type, int argc, VALUE *argv, VALUE self); VALUE rugged_raw_read(git_repository *repo, const git_oid *oid); VALUE rugged_signature_new(const git_signature *sig, const char *encoding_name); VALUE rugged_repo_new(VALUE klass, git_repository *repo); VALUE rugged_index_new(VALUE klass, VALUE owner, git_index *index); VALUE rugged_config_new(VALUE klass, VALUE owner, git_config *cfg); VALUE rugged_object_new(VALUE owner, git_object *object); VALUE rugged_object_rev_parse(VALUE rb_repo, VALUE rb_spec, int as_obj); VALUE rugged_ref_new(VALUE klass, VALUE owner, git_reference *ref); VALUE rugged_diff_new(VALUE klass, VALUE owner, git_diff *diff); VALUE rugged_patch_new(VALUE owner, git_patch *patch); VALUE rugged_diff_delta_new(VALUE owner, const git_diff_delta *delta); VALUE rugged_diff_hunk_new(VALUE owner, size_t hunk_idx, const git_diff_hunk *hunk, size_t lines_in_hunk); VALUE rugged_diff_line_new(const git_diff_line *line); VALUE rugged_remote_new(VALUE owner, git_remote *remote); VALUE rb_git_delta_file_fromC(const git_diff_file *file); VALUE rb_merge_file_result_fromC(const git_merge_file_result *results); void rugged_parse_diff_options(git_diff_options *opts, VALUE rb_options); void rugged_parse_merge_options(git_merge_options *opts, VALUE rb_options); void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options); void rugged_parse_merge_file_options(git_merge_file_options *opts, VALUE rb_options); void rugged_cred_extract(git_cred **cred, int allowed_types, VALUE rb_credential); VALUE rugged_otype_new(git_otype t); git_otype rugged_otype_get(VALUE rb_type); git_signature *rugged_signature_get(VALUE rb_person, git_repository *repo); git_object *rugged_object_get(git_repository *repo, VALUE object_value, git_otype type); int rugged_oid_get(git_oid *oid, git_repository *repo, VALUE p); const char * rugged_refname_from_string_or_ref(VALUE rb_name_or_ref); VALUE rugged_signature_from_buffer(const char *buffer, const char *encoding_name); void rugged_rb_ary_to_strarray(VALUE rb_array, git_strarray *str_array); VALUE rugged_strarray_to_rb_ary(git_strarray *str_array); static inline void rugged_set_owner(VALUE object, VALUE owner) { rb_iv_set(object, "@owner", owner); } static inline VALUE rugged_owner(VALUE object) { return rb_iv_get(object, "@owner"); } extern void rugged_exception_raise(void); static inline void rugged_exception_check(int errorcode) { if (errorcode < 0) rugged_exception_raise(); } static inline int rugged_parse_bool(VALUE boolean) { if (TYPE(boolean) != T_TRUE && TYPE(boolean) != T_FALSE) rb_raise(rb_eTypeError, "Expected boolean value"); return boolean ? 1 : 0; } extern VALUE rb_cRuggedRepo; VALUE rugged__block_yield_splat(VALUE args); struct rugged_cb_payload { VALUE rb_data; int exception; }; struct rugged_remote_cb_payload { VALUE progress; VALUE completion; VALUE transfer_progress; VALUE update_tips; VALUE credentials; VALUE certificate_check; VALUE result; int exception; }; void rugged_remote_init_callbacks_and_payload_from_options( VALUE rb_options, git_remote_callbacks *callbacks, struct rugged_remote_cb_payload *payload); static inline void rugged_check_repo(VALUE rb_repo) { if (!rb_obj_is_kind_of(rb_repo, rb_cRuggedRepo)) rb_raise(rb_eTypeError, "Expecting a Rugged Repository"); } static inline VALUE rugged_create_oid(const git_oid *oid) { char out[40]; git_oid_fmt(out, oid); return rb_usascii_str_new(out, 40); } typedef struct _rugged_backend { int (* odb_backend)(git_odb_backend **backend_out, struct _rugged_backend *backend, const char* path); int (* refdb_backend)(git_refdb_backend **backend_out, struct _rugged_backend *backend, const char* path); } rugged_backend; #endif rugged-0.26.0/ext/rugged/rugged_note.c0000644000175000017500000002216113147033070017725 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedObject; static VALUE rugged_git_note_message(const git_note *note) { const char *message; message = git_note_message(note); /* * assume the note message is utf8 compatible, because that's * the sensible thing to do. */ return rb_str_new_utf8(message); } static VALUE rugged_git_note_oid(const git_note* note) { const git_oid *oid; oid = git_note_id(note); return rugged_create_oid(oid); } /* * call-seq: * obj.notes(notes_ref = 'refs/notes/commits') -> hash * * Lookup a note for +obj+ from +notes_ref+: * - +notes_ref+: (optional): canonical name of the reference to use, defaults to "refs/notes/commits" * * Returns a new Hash object. * * obj.notes #=> {:message=>"note text\n", :oid=>"94eca2de348d5f672faf56b0decafa5937e3235e"} */ static VALUE rb_git_note_lookup(int argc, VALUE *argv, VALUE self) { git_repository *repo; const char *notes_ref = NULL; VALUE rb_notes_ref; VALUE rb_note_hash; VALUE owner; git_note *note; git_object *object; int error; rb_scan_args(argc, argv, "01", &rb_notes_ref); if (!NIL_P(rb_notes_ref)) { Check_Type(rb_notes_ref, T_STRING); notes_ref = StringValueCStr(rb_notes_ref); } Data_Get_Struct(self, git_object, object); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); error = git_note_read(¬e, repo, notes_ref, git_object_id(object)); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); rb_note_hash = rb_hash_new(); rb_hash_aset(rb_note_hash, CSTR2SYM("message"), rugged_git_note_message(note)); rb_hash_aset(rb_note_hash, CSTR2SYM("oid"), rugged_git_note_oid(note)); git_note_free(note); return rb_note_hash; } /* * call-seq: * obj.create_note(data = {}) -> oid * * Write a new +note+ to +object+, with the given +data+ * arguments, passed as a +Hash+: * * - +:message+: the content of the note to add to the object * - +:committer+: (optional) a hash with the signature for the committer * defaults to the signature from the configuration * - +:author+: (optional) a hash with the signature for the author * defaults to the signature from the configuration * - +:ref+: (optional): canonical name of the reference to use, defaults to "refs/notes/commits" * - +:force+: (optional): overwrite existing note (disabled by default) * * When the note is successfully written to disk, its +oid+ will be * returned as a hex +String+. * * author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} * * obj.create_note( * :author => author, * :committer => author, * :message => "Hello world\n\n", * :ref => 'refs/notes/builds' * ) */ static VALUE rb_git_note_create(VALUE self, VALUE rb_data) { VALUE rb_ref, rb_message, rb_force; git_repository *repo = NULL; const char *notes_ref = NULL; VALUE owner; git_signature *author, *committer; git_oid note_oid; git_object *target = NULL; int error = 0; int force = 0; Check_Type(rb_data, T_HASH); Data_Get_Struct(self, git_object, target); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); rb_ref = rb_hash_aref(rb_data, CSTR2SYM("ref")); rb_force = rb_hash_aref(rb_data, CSTR2SYM("force")); if (!NIL_P(rb_force)) force = rugged_parse_bool(rb_force); if (!NIL_P(rb_ref)) { Check_Type(rb_ref, T_STRING); notes_ref = StringValueCStr(rb_ref); } rb_message = rb_hash_aref(rb_data, CSTR2SYM("message")); Check_Type(rb_message, T_STRING); committer = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("committer")), repo ); author = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("author")), repo ); error = git_note_create( ¬e_oid, repo, notes_ref, author, committer, git_object_id(target), StringValueCStr(rb_message), force); git_signature_free(author); git_signature_free(committer); rugged_exception_check(error); return rugged_create_oid(¬e_oid); } /* * call-seq: * obj.remove_note(data = {}) -> boolean * * Removes a +note+ from +object+, with the given +data+ * arguments, passed as a +Hash+: * * - +:committer+ (optional): a hash with the signature for the committer, * defaults to the signature from the configuration * - +:author+ (optional): a hash with the signature for the author, * defaults to the signature from the configuration * - +:ref+: (optional): canonical name of the reference to use, defaults to "refs/notes/commits" * * When the note is successfully removed +true+ will be * returned as a +Boolean+. * * author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} * * obj.remove_note( * :author => author, * :committer => author, * :ref => 'refs/notes/builds' * ) */ static VALUE rb_git_note_remove(int argc, VALUE *argv, VALUE self) { int error = 0; const char *notes_ref = NULL; git_repository *repo = NULL; git_signature *author, *committer; git_object *target = NULL; VALUE rb_data; VALUE rb_ref = Qnil; VALUE rb_author = Qnil; VALUE rb_committer = Qnil; VALUE owner; Data_Get_Struct(self, git_object, target); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); rb_scan_args(argc, argv, "01", &rb_data); if (!NIL_P(rb_data)) { Check_Type(rb_data, T_HASH); rb_ref = rb_hash_aref(rb_data, CSTR2SYM("ref")); if (!NIL_P(rb_ref)) { Check_Type(rb_ref, T_STRING); notes_ref = StringValueCStr(rb_ref); } rb_committer = rb_hash_aref(rb_data, CSTR2SYM("committer")); rb_author = rb_hash_aref(rb_data, CSTR2SYM("author")); } committer = rugged_signature_get(rb_committer, repo); author = rugged_signature_get(rb_author, repo); error = git_note_remove( repo, notes_ref, author, committer, git_object_id(target)); git_signature_free(author); git_signature_free(committer); if (error == GIT_ENOTFOUND) return Qfalse; rugged_exception_check(error); return Qtrue; } static int cb_note__each(const git_oid *blob_id, const git_oid *annotated_object_id, void *data) { VALUE rb_args = rb_ary_new2(2); struct rugged_cb_payload *payload = data; git_object *annotated_object; git_object *note_blob; git_repository *repo; Data_Get_Struct(payload->rb_data, git_repository, repo); rugged_exception_check( git_object_lookup(&annotated_object, repo, annotated_object_id, GIT_OBJ_ANY) ); rugged_exception_check( git_object_lookup(¬e_blob, repo, blob_id, GIT_OBJ_BLOB) ); rb_ary_push(rb_args, rugged_object_new(payload->rb_data, note_blob)); rb_ary_push(rb_args, rugged_object_new(payload->rb_data, annotated_object)); rb_protect(rb_yield_splat, rb_args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } /* * call-seq: * repo.each_note(notes_ref = "refs/notes/commits") { |note_blob, annotated_object| block } * repo.each_note(notes_ref = "refs/notes/commits") -> an_enumerator * * Call the given block once for each note_blob/annotated_object pair in +repository+ * - +notes_ref+: (optional): canonical name of the reference to use defaults to "refs/notes/commits" * * If no block is given, an +Enumerator+ is returned. * * @repo.each_note do |note_blob, annotated_object| * puts "#{note_blob.oid} => #{annotated_object.oid}" * end */ static VALUE rb_git_note_each(int argc, VALUE *argv, VALUE self) { git_repository *repo; const char *notes_ref = NULL; int error; struct rugged_cb_payload payload = { self, 0 }; VALUE rb_notes_ref; rb_scan_args(argc, argv, "01", &rb_notes_ref); if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 3, CSTR2SYM("each_note"), self, rb_notes_ref); } if (!NIL_P(rb_notes_ref)) { Check_Type(rb_notes_ref, T_STRING); notes_ref = StringValueCStr(rb_notes_ref); } Data_Get_Struct(self, git_repository, repo); error = git_note_foreach(repo, notes_ref, &cb_note__each, &payload); if (payload.exception) rb_jump_tag(payload.exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.notes_default_ref() -> string * * Get the default notes reference for a +repository+: * * Returns a new String object. * * repo.default_notes_ref #=> "refs/notes/commits" */ static VALUE rb_git_note_default_ref_GET(VALUE self) { git_repository *repo = NULL; git_buf ref_name = { 0 }; VALUE rb_result; Data_Get_Struct(self, git_repository, repo); rugged_exception_check( git_note_default_ref(&ref_name, repo) ); rb_result = rb_enc_str_new(ref_name.ptr, ref_name.size, rb_utf8_encoding()); git_buf_free(&ref_name); return rb_result; } void Init_rugged_notes(void) { rb_define_method(rb_cRuggedObject, "notes", rb_git_note_lookup, -1); rb_define_method(rb_cRuggedObject, "create_note", rb_git_note_create, 1); rb_define_method(rb_cRuggedObject, "remove_note", rb_git_note_remove, -1); rb_define_method(rb_cRuggedRepo, "each_note", rb_git_note_each, -1); rb_define_method(rb_cRuggedRepo, "default_notes_ref", rb_git_note_default_ref_GET, 0); } rugged-0.26.0/ext/rugged/rugged_rebase.c0000644000175000017500000002546013147033070020226 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedIndex; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedCommit; extern VALUE rb_cRuggedReference; VALUE rb_cRuggedRebase; static VALUE rebase_operation_type(git_rebase_operation *operation); static void parse_rebase_options(git_rebase_options *ret, VALUE rb_options) { VALUE val; if (NIL_P(rb_options)) return; val = rb_hash_aref(rb_options, CSTR2SYM("quiet")); ret->quiet = RTEST(val); val = rb_hash_aref(rb_options, CSTR2SYM("inmemory")); ret->inmemory = RTEST(val); val = rb_hash_aref(rb_options, CSTR2SYM("rewrite_notes_ref")); if (!NIL_P(val)) { Check_Type(val, T_STRING); ret->rewrite_notes_ref = StringValueCStr(val); } rugged_parse_checkout_options(&ret->checkout_options, rb_options); rugged_parse_merge_options(&ret->merge_options, rb_options); } void rb_git_rebase__free(git_rebase *rebase) { git_rebase_free(rebase); } VALUE rugged_rebase_new(VALUE klass, VALUE owner, git_rebase *rebase) { VALUE rb_rebase = Data_Wrap_Struct(klass, NULL, &rb_git_rebase__free, rebase); rugged_set_owner(rb_rebase, owner); return rb_rebase; } struct get_annotated_commit_args { git_annotated_commit **annotated_commit; VALUE rb_repo; VALUE rb_value; }; static void get_annotated_commit(git_annotated_commit **annotated_commit, VALUE rb_repo, VALUE rb_value) { git_repository *repo; int error; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if (rb_obj_is_kind_of(rb_value, rb_cRuggedCommit)) { const git_commit * commit; const git_oid * oid; Data_Get_Struct(rb_value, git_commit, commit); oid = git_commit_id(commit); error = git_annotated_commit_lookup(annotated_commit, repo, oid); } else if (rb_obj_is_kind_of(rb_value, rb_cRuggedReference)) { const git_reference * ref; Data_Get_Struct(rb_value, git_reference, ref); error = git_annotated_commit_from_ref(annotated_commit, repo, ref); } else if (TYPE(rb_value) == T_STRING) { error = git_annotated_commit_from_revspec(annotated_commit, repo, StringValueCStr(rb_value)); } else { rb_raise(rb_eTypeError, "Expecting a Rugged::Reference, Rugged::Commit or String instance"); } rugged_exception_check(error); } static void get_annotated_commit_wrapper(struct get_annotated_commit_args *args) { get_annotated_commit(args->annotated_commit, args->rb_repo, args->rb_value); } static int rugged_get_annotated_commit( git_annotated_commit ** annotated_commit, VALUE rb_repo, VALUE rb_value) { struct get_annotated_commit_args args; int exception; args.annotated_commit = annotated_commit; args.rb_repo = rb_repo; args.rb_value = rb_value; rb_protect((VALUE (*)(VALUE))get_annotated_commit_wrapper, (VALUE)&args, &exception); return exception; } /* * call-seq: * Rebase.new(repo, branch, upstream[, onto][, options]) -> new_rebase * * Initialize a new rebase operation. This will put +repo+ in a * rebase state. * * +branch+ is the branch to be rebased, and +upstream+ is the branch * which is to be the new base. If +onto+ is specified, this will be * the one base, and +upstream+ is used to determine which commits * from +branch+ to use for the rebase. * * You can pass merge and checkout options for the options hash, plus * a few rebase-specific ones: * * :quiet :: * If true, a flag will be set to ask tools to activate quiet * mode. This does not do anything for libgit2/rugged but can be * used to interact with other implementations. * * :inmemory :: * Do not put the repository in a rebase state but perform all the * operations in-memory. In case of conflicts, the rebase operation * Hash returned by #next will contain the index which can be used to * resolve conflicts. * * :rewrite_notes_ref :: * Name of the notes reference used to rewrite notes for rebased * commits when finishing the rebase. If +nil+, it will be taken * from the configuration. */ static VALUE rb_git_rebase_new(int argc, VALUE* argv, VALUE klass) { int error = 0, exception = 0; git_rebase *rebase = NULL; git_repository *repo; git_annotated_commit *branch = NULL, *upstream = NULL, *onto = NULL; VALUE rb_repo, rb_branch, rb_upstream, rb_onto, rb_options; git_rebase_options options = GIT_REBASE_OPTIONS_INIT; rb_scan_args(argc, argv, "31:", &rb_repo, &rb_branch, &rb_upstream, &rb_onto, &rb_options); Data_Get_Struct(rb_repo, git_repository, repo); if ((exception = rugged_get_annotated_commit(&branch, rb_repo, rb_branch))) goto cleanup; if ((exception = rugged_get_annotated_commit(&upstream, rb_repo, rb_upstream))) goto cleanup; if (!NIL_P(rb_onto)) { if ((exception = rugged_get_annotated_commit(&onto, rb_repo, rb_onto))) goto cleanup; } parse_rebase_options(&options, rb_options); error = git_rebase_init(&rebase, repo, branch, upstream, onto, &options); cleanup: git_annotated_commit_free(branch); git_annotated_commit_free(upstream); git_annotated_commit_free(onto); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return rugged_rebase_new(klass, rb_repo, rebase); } /* * call-seq: * Rebase.next() -> operation or nil * * Perform the next step in the rebase. The returned operation is a * Hash with its details or nil if there are no more operations to * perform. The Hash contains some of the following entries: * * :type :: * The type of operation being done. Can be one of +:pick+, * +:reword+, +:edit+, +:squash+, +:fixup+ or +:exec+. * * :id :: * The id of the commit being cherry-picked. Exists for all but * +:exec+ operations. * * :exec :: * If the operatin is +:exec+ this is what the user asked to be * executed. */ static VALUE rb_git_rebase_next(VALUE self) { int error; git_rebase *rebase; git_rebase_operation *operation; VALUE hash, val; Data_Get_Struct(self, git_rebase, rebase); error = git_rebase_next(&operation, rebase); if (error == GIT_ITEROVER) return Qnil; rugged_exception_check(error); /* Create the operation hash out of the relevant details */ hash = rb_hash_new(); val = rebase_operation_type(operation); rb_hash_aset(hash, CSTR2SYM("type"), val); if (operation->type != GIT_REBASE_OPERATION_EXEC) { val = rugged_create_oid(&operation->id); rb_hash_aset(hash, CSTR2SYM("id"), val); } if (operation->exec) { val = rb_str_new_utf8(operation->exec); rb_hash_aset(hash, CSTR2SYM("exec"), val); } return hash; } /* * call-seq: * rebase.inmemory_index -> index * * Gets the index produced by the last operation, which is the result * of +next+ and which will be committed by the next invocation of * +commit+. This is useful for resolving conflicts in an in-memory * rebase before committing them. * * This is only applicable for in-memory rebases; for rebases within * a working directory, the changes were applied to the repository's * index. */ static VALUE rb_git_rebase_inmemory_index(VALUE self) { git_rebase *rebase; git_index *index; Data_Get_Struct(self, git_rebase, rebase); rugged_exception_check(git_rebase_inmemory_index(&index, rebase)); return rugged_index_new(rb_cRuggedIndex, self, index); } /* * call-seq: * rebase.commit(author: nil, committer: committer, message: nil) -> oid or nil * * Commit the current patch. Any conflicts must have been resolved. * * If +author+ is +nil+, the existing author for the commit will be * used. If +message+ is +nil+, the existing message will be used. * * Returns a string containing the oid of the newly created commit, * or +nil+ if there are no changes to be committed. */ static VALUE rb_git_rebase_commit(int argc, VALUE *argv, VALUE self) { int error; git_oid id; git_rebase *rebase; git_signature *author = NULL, *committer; const char *message = NULL; VALUE rb_options, rb_author, rb_committer, rb_message; Data_Get_Struct(self, git_rebase, rebase); rb_scan_args(argc, argv, ":", &rb_options); Check_Type(rb_options, T_HASH); rb_author = rb_hash_aref(rb_options, CSTR2SYM("author")); rb_committer = rb_hash_aref(rb_options, CSTR2SYM("committer")); rb_message = rb_hash_aref(rb_options, CSTR2SYM("message")); if (!NIL_P(rb_message)) { Check_Type(rb_message, T_STRING); message = StringValueCStr(rb_message); } if (NIL_P(rb_committer)) rb_raise(rb_eArgError, "Expected non-nil committer"); else committer = rugged_signature_get(rb_committer, NULL); if (!NIL_P(rb_author)) author = rugged_signature_get(rb_author, NULL); error = git_rebase_commit(&id, rebase, author, committer, NULL, message); git_signature_free(author); git_signature_free(committer); if (error == GIT_EAPPLIED) { giterr_clear(); return Qnil; } rugged_exception_check(error); return rugged_create_oid(&id); } /* * call-seq: * rebase.abort -> nil * * Abort the rebase currently in process, resetting the repository * and working directory to their state before the rebase began. */ static VALUE rb_git_rebase_abort(VALUE self) { git_rebase *rebase; Data_Get_Struct(self, git_rebase, rebase); rugged_exception_check(git_rebase_abort(rebase)); return Qnil; } /* * call-seq: * rebase.finish -> nil * * Finish the rebase currently in progress once all patches have been * applied. */ static VALUE rb_git_rebase_finish(VALUE self, VALUE rb_sig) { git_rebase *rebase; git_signature *sig; int error; Data_Get_Struct(self, git_rebase, rebase); sig = rugged_signature_get(rb_sig, NULL); error = git_rebase_finish(rebase, sig); git_signature_free(sig); rugged_exception_check(error); return Qnil; } static VALUE rebase_operation_type(git_rebase_operation *operation) { VALUE rb_type; switch (operation->type) { case GIT_REBASE_OPERATION_PICK: rb_type = CSTR2SYM("pick"); break; case GIT_REBASE_OPERATION_REWORD: rb_type = CSTR2SYM("reword"); break; case GIT_REBASE_OPERATION_EDIT: rb_type = CSTR2SYM("edit"); break; case GIT_REBASE_OPERATION_SQUASH: rb_type = CSTR2SYM("squash"); break; case GIT_REBASE_OPERATION_FIXUP: rb_type = CSTR2SYM("fixup"); break; case GIT_REBASE_OPERATION_EXEC: rb_type = CSTR2SYM("exec"); break; default: rb_raise(rb_eTypeError, "Invalid rebase operation type found"); break; } return rb_type; } void Init_rugged_rebase(void) { rb_cRuggedRebase = rb_define_class_under(rb_mRugged, "Rebase", rb_cObject); rb_define_singleton_method(rb_cRuggedRebase, "new", rb_git_rebase_new, -1); rb_define_method(rb_cRuggedRebase, "next", rb_git_rebase_next, 0); rb_define_method(rb_cRuggedRebase, "inmemory_index", rb_git_rebase_inmemory_index, 0); rb_define_method(rb_cRuggedRebase, "commit", rb_git_rebase_commit, -1); rb_define_method(rb_cRuggedRebase, "abort", rb_git_rebase_abort, 0); rb_define_method(rb_cRuggedRebase, "finish", rb_git_rebase_finish, 1); } rugged-0.26.0/ext/rugged/rugged_blob.c0000644000175000017500000004563613147033070017712 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" #include #include extern VALUE rb_mRugged; extern VALUE rb_cRuggedObject; extern VALUE rb_cRuggedRepo; static ID id_read; VALUE rb_cRuggedBlob; VALUE rb_cRuggedBlobSig; /* * call-seq: * blob.text(max_lines = -1, encoding = Encoding.default_external) -> string * * Return up to +max_lines+ of text from a blob as a +String+. * If +max_lines+ is less than 0, the full string is returned. * * The string is created with the given +encoding+, defaulting to * Encoding.default_external. * * When limiting the size of the text with +max_lines+, the string is * expected to have an ASCII-compatible encoding, and is checked * for the newline +\n+ character. */ static VALUE rb_git_blob_text_GET(int argc, VALUE *argv, VALUE self) { git_blob *blob; size_t size; const char *content; VALUE rb_max_lines, rb_encoding; Data_Get_Struct(self, git_blob, blob); rb_scan_args(argc, argv, "02", &rb_max_lines, &rb_encoding); content = git_blob_rawcontent(blob); size = git_blob_rawsize(blob); if (!NIL_P(rb_max_lines)) { size_t i = 0; int lines = 0, maxlines; Check_Type(rb_max_lines, T_FIXNUM); maxlines = FIX2INT(rb_max_lines); if (maxlines >= 0) { while (i < size && lines < maxlines) { if (content[i++] == '\n') lines++; } size = (size_t)i; } } if (!NIL_P(rb_encoding)) { return rb_enc_str_new(content, size, rb_to_encoding(rb_encoding)); } return rb_external_str_new(content, size); } /* * call-seq: * blob.content(max_bytes=-1) -> string * * Return up to +max_bytes+ from the contents of a blob as bytes +String+. * If +max_bytes+ is less than 0, the full string is returned. * * This string is tagged with the ASCII-8BIT encoding: the bytes are * returned as-is, since Git is encoding agnostic. */ static VALUE rb_git_blob_content_GET(int argc, VALUE *argv, VALUE self) { git_blob *blob; size_t size; const char *content; VALUE rb_max_bytes; Data_Get_Struct(self, git_blob, blob); rb_scan_args(argc, argv, "01", &rb_max_bytes); content = git_blob_rawcontent(blob); size = git_blob_rawsize(blob); if (!NIL_P(rb_max_bytes)) { int maxbytes; Check_Type(rb_max_bytes, T_FIXNUM); maxbytes = FIX2INT(rb_max_bytes); if (maxbytes >= 0 && (size_t)maxbytes < size) size = (size_t)maxbytes; } /* * since we don't really ever know the encoding of a blob * lets default to the binary encoding (ascii-8bit) */ return rb_str_new(content, size); } /* * call-seq: * blob.rawsize -> int * * Return the size in bytes of the blob. This is the real, * uncompressed size and the length of +blob.content+, not * the compressed size. */ static VALUE rb_git_blob_rawsize(VALUE self) { git_blob *blob; Data_Get_Struct(self, git_blob, blob); return INT2FIX(git_blob_rawsize(blob)); } /* * call-seq: * Blob.from_buffer(repository, buffer) -> oid * * Write a blob to +repository+ with the contents specified * in +buffer+, where +buffer+ is a +String+. * The encoding of +buffer+ is ignored and bytes are copied as-is. */ static VALUE rb_git_blob_from_buffer(VALUE self, VALUE rb_repo, VALUE rb_buffer) { int error; git_oid oid; git_repository *repo; Check_Type(rb_buffer, T_STRING); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_blob_create_frombuffer(&oid, repo, RSTRING_PTR(rb_buffer), RSTRING_LEN(rb_buffer)); rugged_exception_check(error); return rugged_create_oid(&oid); } /* * call-seq: * Blob.from_workdir(repository, file_path) -> oid * * Write the file specified in +file_path+ to a blob in +repository+. * +file_path+ must be relative to the repository's working folder. * The repository cannot be bare. * * Blob.from_workdir(repo, 'src/blob.h') #=> '9d09060c850defbc7711d08b57def0d14e742f4e' */ static VALUE rb_git_blob_from_workdir(VALUE self, VALUE rb_repo, VALUE rb_path) { int error; git_oid oid; git_repository *repo; Check_Type(rb_path, T_STRING); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_blob_create_fromworkdir(&oid, repo, StringValueCStr(rb_path)); rugged_exception_check(error); return rugged_create_oid(&oid); } /* * call-seq: * Blob.from_disk(repository, file_path) -> oid * * Write the file specified in +file_path+ to a blob in +repository+. * The repository can be bare or not. * * Example: * * Blob.from_disk(repo, '/var/repos/blob.h') #=> '5b5b025afb0b4c913b4c338a42934a3863bf3643' */ static VALUE rb_git_blob_from_disk(VALUE self, VALUE rb_repo, VALUE rb_path) { int error; git_oid oid; git_repository *repo; Check_Type(rb_path, T_STRING); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_blob_create_fromdisk(&oid, repo, StringValueCStr(rb_path)); rugged_exception_check(error); return rugged_create_oid(&oid); } static VALUE rb_read_check(VALUE pointer) { VALUE *args = (VALUE *)pointer; VALUE rb_buffer = rb_funcall(args[0], id_read, 1, args[1]); if (!NIL_P(rb_buffer)) Check_Type(rb_buffer, T_STRING); return rb_buffer; } /* * call-seq: * Blob.from_io(repository, io [, hint_path]) -> oid * * Write a loose blob to the +repository+ from an +IO+ provider * of data. * * The repository can be bare or not. * * The data provider +io+ should respond to a read(size) * method. Generally any instance of a class based on Ruby's +IO+ class * should work(ex. +File+). On each +read+ call it should * return a +String+ with maximum size of +size+. * * *NOTE:* If an exception is raised in the +io+ object's * +read+ method, no blob will be created. * * Provided the +hint_path+ parameter is given, its value * will help to determine what git filters should be applied * to the object before it can be placed to the object database. * * File.open('/path/to/file') do |file| * Blob.from_io(repo, file, 'hint/blob.h') #=> '42cab3c0cde61e2b5a2392e1eadbeffa20ffa171' * end */ static VALUE rb_git_blob_from_io(int argc, VALUE *argv, VALUE klass) { VALUE rb_repo, rb_io, rb_hint_path, rb_buffer, rb_read_args[2]; const char * hint_path = NULL; git_writestream *stream; int error = 0, exception = 0, max_length = 4096; git_oid oid; git_repository *repo; rb_scan_args(argc, argv, "21", &rb_repo, &rb_io, &rb_hint_path); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if (!NIL_P(rb_hint_path)) { Check_Type(rb_hint_path, T_STRING); hint_path = StringValueCStr(rb_hint_path); } error = git_blob_create_fromstream(&stream, repo, hint_path); if (error) goto cleanup; rb_read_args[0] = rb_io; rb_read_args[1] = INT2FIX(max_length); do { rb_buffer = rb_protect(rb_read_check, (VALUE)rb_read_args, &exception); if (exception) goto cleanup; if (NIL_P(rb_buffer)) break; error = stream->write(stream, RSTRING_PTR(rb_buffer), RSTRING_LEN(rb_buffer)); if (error) goto cleanup; } while (RSTRING_LEN(rb_buffer) == max_length); error = git_blob_create_fromstream_commit(&oid, stream); cleanup: if (exception) rb_jump_tag(exception); rugged_exception_check(error); return rugged_create_oid(&oid); } /* * call-seq: * blob.loc -> int * * Return the number of lines for this blob, * assuming the blob is plaintext (i.e. not binary) */ static VALUE rb_git_blob_loc(VALUE self) { git_blob *blob; const char *data, *data_end; size_t loc = 0; Data_Get_Struct(self, git_blob, blob); data = git_blob_rawcontent(blob); data_end = data + git_blob_rawsize(blob); if (data == data_end) return INT2FIX(0); for (; data < data_end; ++data) { if (data[0] == '\n') { loc++; } else if (data[0] == '\r') { if (data + 1 < data_end && data[1] == '\n') data++; loc++; } } if (data[-1] != '\n' && data[-1] != '\r') loc++; return INT2FIX(loc); } /* * call-seq: * blob.sloc -> int * * Return the number of non-empty code lines for the blob, * assuming the blob is plaintext (i.e. not binary) */ static VALUE rb_git_blob_sloc(VALUE self) { git_blob *blob; const char *data, *data_end; size_t sloc = 0; Data_Get_Struct(self, git_blob, blob); data = git_blob_rawcontent(blob); data_end = data + git_blob_rawsize(blob); if (data == data_end) return INT2FIX(0); /* go through the whole blob, counting lines * that are not empty */ while (data < data_end) { if (*data++ == '\n') { while (data < data_end && isspace(*data)) data++; sloc++; } } /* last line without trailing '\n'? */ if (data[-1] != '\n') sloc++; return INT2FIX(sloc); } /* * call-seq: * blob.binary? -> true or false * * Determine if the blob content is most certainly binary or not. * * The heuristic used to guess if a file is binary is taken from core git: * Searching for NUL bytes and looking for a reasonable ratio of printable * to non-printable characters among the first 4000 bytes. * */ static VALUE rb_git_blob_is_binary(VALUE self) { git_blob *blob; Data_Get_Struct(self, git_blob, blob); return git_blob_is_binary(blob) ? Qtrue : Qfalse; } /* * call-seq: * blob.diff(other, options = {}) -> patch * * Directly generate a Rugged::Patch from the difference between +blob+ and +other+. * * +other+ can either be another Rugged::Blob instance, a string, * or nil (treated as an empty blob). * * The following options can be passed in the +options+ Hash: * * :max_size :: * An integer specifying the maximum byte size of a blob before a it will * be treated as binary. The default value is 512MB. * * :context_lines :: * The number of unchanged lines that define the boundary of a hunk (and * to display before and after the actual changes). The default is 3. * * :interhunk_lines :: * The maximum number of unchanged lines between hunk boundaries before the hunks * will be merged into a one. The default is 0. * * :reverse :: * If true, the sides of the diff will be reversed. * * :force_text :: * If true, all files will be treated as text, disabling binary attributes & detection. * * :ignore_whitespace :: * If true, all whitespace will be ignored. * * :ignore_whitespace_change :: * If true, changes in amount of whitespace will be ignored. * * :ignore_whitespace_eol :: * If true, whitespace at end of line will be ignored. * * :patience :: * If true, the "patience diff" algorithm will be used (currently unimplemented). * * :skip_binary_check :: * If true, diff deltas will be generated without spending time on binary * detection. This is useful to improve performance in cases where the actual * file content difference is not needed. * * :old_path :: * An optional string to treat +blob+ as if it had this filename. * * :new_path :: * An optional string to treat +other+ as if it had this filename. */ static VALUE rb_git_blob_diff(int argc, VALUE *argv, VALUE self) { git_blob *blob; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_patch *patch; const char *old_path = NULL, *new_path = NULL; VALUE rb_other, rb_options; int error; rb_scan_args(argc, argv, "10:", &rb_other, &rb_options); if (!NIL_P(rb_options)) { VALUE rb_value; rb_value = rb_hash_aref(rb_options, CSTR2SYM("old_path")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); old_path = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("new_path")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); new_path = StringValueCStr(rb_value); } rugged_parse_diff_options(&opts, rb_options); } Data_Get_Struct(self, git_blob, blob); if (NIL_P(rb_other)) { error = git_patch_from_blobs(&patch, blob, old_path, NULL, new_path, &opts); } else if (rb_obj_is_kind_of(rb_other, rb_cRuggedBlob)) { git_blob *other_blob; Data_Get_Struct(rb_other, git_blob, other_blob); error = git_patch_from_blobs(&patch, blob, old_path, other_blob, new_path, &opts); } else if (TYPE(rb_other) == T_STRING) { const char * buffer = StringValueCStr(rb_other); error = git_patch_from_blob_and_buffer(&patch, blob, old_path, buffer, RSTRING_LEN(rb_other), new_path, &opts); } else { rb_raise(rb_eTypeError, "wrong argument type %s (expected Rugged::Blob, String, or nil)", rb_obj_classname(rb_other)); } rugged_exception_check(error); return rugged_patch_new(self, patch); } static VALUE rb_git_blob_to_buffer(int argc, VALUE *argv, VALUE self) { VALUE rb_repo, rb_sha1, rb_max_bytes; VALUE rb_ret; git_repository *repo = NULL; git_blob *blob = NULL; size_t size; const char *content; rb_scan_args(argc, argv, "21", &rb_repo, &rb_sha1, &rb_max_bytes); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); blob = (git_blob *)rugged_object_get(repo, rb_sha1, GIT_OBJ_BLOB); content = git_blob_rawcontent(blob); size = git_blob_rawsize(blob); if (!NIL_P(rb_max_bytes)) { int maxbytes; Check_Type(rb_max_bytes, T_FIXNUM); maxbytes = FIX2INT(rb_max_bytes); if (maxbytes >= 0 && (size_t)maxbytes < size) size = (size_t)maxbytes; } rb_ret = rb_ary_new(); rb_ary_push(rb_ret, rb_str_new(content, size)); rb_ary_push(rb_ret, INT2FIX(git_blob_rawsize(blob))); git_object_free((git_object*)blob); /* TODO: LOC */ return rb_ret; } #define RUGGED_MERGE_FILE_INPUT_INIT { GIT_MERGE_FILE_INPUT_INIT } typedef struct { git_merge_file_input parent; int has_id; git_oid id; } rugged_merge_file_input; static void rugged_parse_merge_file_input(rugged_merge_file_input *input, git_repository *repo, VALUE rb_input) { VALUE rb_value; Check_Type(rb_input, T_HASH); if (!NIL_P(rb_value = rb_hash_aref(rb_input, CSTR2SYM("content")))) { input->parent.ptr = RSTRING_PTR(rb_value); input->parent.size = RSTRING_LEN(rb_value); } else if (!NIL_P(rb_value = rb_hash_aref(rb_input, CSTR2SYM("oid")))) { if (!repo) rb_raise(rb_eArgError, "Rugged repository is required when file input is `:oid`."); rugged_exception_check(git_oid_fromstr(&input->id, RSTRING_PTR(rb_value))); input->has_id = 1; } else { rb_raise(rb_eArgError, "File input must have `:content` or `:oid`."); } rb_value = rb_hash_aref(rb_input, CSTR2SYM("filemode")); if (!NIL_P(rb_value)) input->parent.mode = FIX2UINT(rb_value); rb_value = rb_hash_aref(rb_input, CSTR2SYM("path")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); input->parent.path = RSTRING_PTR(rb_value); } } static int rugged_load_merge_file_input(git_blob **out, git_repository *repo, rugged_merge_file_input *input) { int error; if (!input->has_id) return 0; if ((error = git_blob_lookup(out, repo, &input->id)) < 0) return error; input->parent.ptr = git_blob_rawcontent(*out); input->parent.size = git_blob_rawsize(*out); return 0; } static VALUE rb_git_blob_merge_files(int argc, VALUE *argv, VALUE klass) { VALUE rb_repo, rb_ancestor, rb_ours, rb_theirs, rb_options, rb_result = Qnil; git_repository *repo = NULL; rugged_merge_file_input ancestor = RUGGED_MERGE_FILE_INPUT_INIT, ours = RUGGED_MERGE_FILE_INPUT_INIT, theirs = RUGGED_MERGE_FILE_INPUT_INIT; git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; git_merge_file_result result = {0}; int error; rb_scan_args(argc, argv, "41", &rb_repo, &rb_ancestor, &rb_ours, &rb_theirs, &rb_options); if (!NIL_P(rb_repo)) { rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); } if (!NIL_P(rb_options)) rugged_parse_merge_file_options(&opts, rb_options); if (!NIL_P(rb_ancestor)) rugged_parse_merge_file_input(&ancestor, repo, rb_ancestor); if (!NIL_P(rb_ours)) rugged_parse_merge_file_input(&ours, repo, rb_ours); if (!NIL_P(rb_theirs)) rugged_parse_merge_file_input(&theirs, repo, rb_theirs); if ((error = rugged_load_merge_file_input(&ancestor_blob, repo, &ancestor)) < 0 || (error = rugged_load_merge_file_input(&our_blob, repo, &ours)) < 0 || (error = rugged_load_merge_file_input(&their_blob, repo, &theirs)) < 0 || (error = git_merge_file(&result, &ancestor.parent, &ours.parent, &theirs.parent, &opts)) < 0) goto done; rb_result = rb_merge_file_result_fromC(&result); done: git_blob_free(ancestor_blob); git_blob_free(our_blob); git_blob_free(their_blob); git_merge_file_result_free(&result); rugged_exception_check(error); return rb_result; } static VALUE rb_git_blob_sig_new(int argc, VALUE *argv, VALUE klass) { int error, opts = 0; git_hashsig *sig; VALUE rb_blob, rb_options; if (rb_scan_args(argc, argv, "11", &rb_blob, &rb_options) == 2) { Check_Type(rb_options, T_FIXNUM); opts = FIX2INT(rb_options); } if (rb_obj_is_kind_of(rb_blob, rb_cRuggedBlob)) { git_blob *blob; Data_Get_Struct(rb_blob, git_blob, blob); error = git_hashsig_create(&sig, git_blob_rawcontent(blob), git_blob_rawsize(blob), opts); } else { Check_Type(rb_blob, T_STRING); error = git_hashsig_create(&sig, RSTRING_PTR(rb_blob), RSTRING_LEN(rb_blob), opts); } rugged_exception_check(error); return Data_Wrap_Struct(klass, NULL, &git_hashsig_free, sig); } static VALUE rb_git_blob_sig_compare(VALUE self, VALUE rb_sig_a, VALUE rb_sig_b) { git_hashsig *sig_a; git_hashsig *sig_b; int result; if (!rb_obj_is_kind_of(rb_sig_a, rb_cRuggedBlobSig) || !rb_obj_is_kind_of(rb_sig_b, rb_cRuggedBlobSig)) { rb_raise(rb_eTypeError, "Expected Rugged::Blob::HashSignature"); } Data_Get_Struct(rb_sig_a, git_hashsig, sig_a); Data_Get_Struct(rb_sig_b, git_hashsig, sig_b); result = git_hashsig_compare(sig_a, sig_b); if (result < 0) rugged_exception_check(result); return INT2FIX(result); } void Init_rugged_blob(void) { id_read = rb_intern("read"); rb_cRuggedBlob = rb_define_class_under(rb_mRugged, "Blob", rb_cRuggedObject); rb_define_method(rb_cRuggedBlob, "size", rb_git_blob_rawsize, 0); rb_define_method(rb_cRuggedBlob, "content", rb_git_blob_content_GET, -1); rb_define_method(rb_cRuggedBlob, "text", rb_git_blob_text_GET, -1); rb_define_method(rb_cRuggedBlob, "sloc", rb_git_blob_sloc, 0); rb_define_method(rb_cRuggedBlob, "loc", rb_git_blob_loc, 0); rb_define_method(rb_cRuggedBlob, "binary?", rb_git_blob_is_binary, 0); rb_define_method(rb_cRuggedBlob, "diff", rb_git_blob_diff, -1); rb_define_singleton_method(rb_cRuggedBlob, "from_buffer", rb_git_blob_from_buffer, 2); rb_define_singleton_method(rb_cRuggedBlob, "from_workdir", rb_git_blob_from_workdir, 2); rb_define_singleton_method(rb_cRuggedBlob, "from_disk", rb_git_blob_from_disk, 2); rb_define_singleton_method(rb_cRuggedBlob, "from_io", rb_git_blob_from_io, -1); rb_define_singleton_method(rb_cRuggedBlob, "to_buffer", rb_git_blob_to_buffer, -1); rb_define_singleton_method(rb_cRuggedBlob, "merge_files", rb_git_blob_merge_files, -1); rb_cRuggedBlobSig = rb_define_class_under(rb_cRuggedBlob, "HashSignature", rb_cObject); rb_define_singleton_method(rb_cRuggedBlobSig, "new", rb_git_blob_sig_new, -1); rb_define_singleton_method(rb_cRuggedBlobSig, "compare", rb_git_blob_sig_compare, 2); } rugged-0.26.0/ext/rugged/rugged_reference_collection.c0000644000175000017500000003241613147033070023135 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedReference; VALUE rb_cRuggedReferenceCollection; /* * call-seq: * ReferenceCollection.new(repo) -> refs * * Creates and returns a new collection of references for the given +repo+. */ static VALUE rb_git_reference_collection_initialize(VALUE self, VALUE repo) { rugged_set_owner(self, repo); return self; } /* * call-seq: * references.create(name, oid, options = {}) -> new_ref * references.create(name, target, options = {}) -> new_ref * * Create a symbolic or direct reference on the collection's +repository+ with the given +name+. * * If the second argument is a valid OID, the reference will be created as direct. * Otherwise, it will be assumed the target is the name of another reference. * * The following options can be passed in the +options+ Hash: * * :force :: * Overwrites the reference with the given +name+, if it already exists, * instead of raising an exception. * * If a reference with the given +name+ already exists and +:force+ is not +true+, * an exception will be raised. */ static VALUE rb_git_reference_collection_create(int argc, VALUE *argv, VALUE self) { VALUE rb_repo = rugged_owner(self), rb_name, rb_target, rb_options; git_repository *repo; git_reference *ref; git_oid oid; char *log_message = NULL; int error, force = 0; rb_scan_args(argc, argv, "20:", &rb_name, &rb_target, &rb_options); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); Check_Type(rb_target, T_STRING); if (!NIL_P(rb_options)) { VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message")); if (!NIL_P(rb_val)) log_message = StringValueCStr(rb_val); force = RTEST(rb_hash_aref(rb_options, CSTR2SYM("force"))); } if (git_oid_fromstr(&oid, StringValueCStr(rb_target)) == GIT_OK) { error = git_reference_create( &ref, repo, StringValueCStr(rb_name), &oid, force, log_message); } else { error = git_reference_symbolic_create( &ref, repo, StringValueCStr(rb_name), StringValueCStr(rb_target), force, log_message); } rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, rb_repo, ref); } /* * call-seq: * references[name] -> new_ref * * Lookup a reference in the collection with the given +name+. * * Returns a new Rugged::Reference object. */ static VALUE rb_git_reference_collection_aref(VALUE self, VALUE rb_name) { VALUE rb_repo = rugged_owner(self); git_repository *repo; git_reference *ref; int error; Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_lookup(&ref, repo, StringValueCStr(rb_name)); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, rb_repo, ref); } static VALUE rb_git_reference_collection__each(int argc, VALUE *argv, VALUE self, int only_names) { VALUE rb_glob, rb_repo = rugged_owner(self); git_repository *repo; git_reference_iterator *iter; int error, exception = 0; rb_scan_args(argc, argv, "01", &rb_glob); if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 2, only_names ? CSTR2SYM("each_name") : CSTR2SYM("each"), rb_glob); } rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if (!NIL_P(rb_glob)) { Check_Type(rb_glob, T_STRING); error = git_reference_iterator_glob_new(&iter, repo, StringValueCStr(rb_glob)); } else { error = git_reference_iterator_new(&iter, repo); } rugged_exception_check(error); if (only_names) { const char *ref_name; while (!exception && (error = git_reference_next_name(&ref_name, iter)) == GIT_OK) { rb_protect(rb_yield, rb_str_new_utf8(ref_name), &exception); } } else { git_reference *ref; while (!exception && (error = git_reference_next(&ref, iter)) == GIT_OK) { rb_protect(rb_yield, rugged_ref_new(rb_cRuggedReference, rb_repo, ref), &exception); } } git_reference_iterator_free(iter); if (exception) rb_jump_tag(exception); if (error != GIT_ITEROVER) rugged_exception_check(error); return Qnil; } /* * call-seq: * references.each(glob = nil) { |ref| block } -> nil * references.each(glob = nil) -> enumerator * * Iterate through all the references in the collection's +repository+. Iteration * can be optionally filtered to the ones matching the given * +glob+, a standard Unix filename glob. * * The given block will be called once with a Rugged::Reference * instance for each reference. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_reference_collection_each(int argc, VALUE *argv, VALUE self) { return rb_git_reference_collection__each(argc, argv, self, 0); } /* * call-seq: * references.each_name(glob = nil) { |ref_name| block } -> nil * references.each_name(glob = nil) -> enumerator * * Iterate through all the reference names in the collection's +repository+. Iteration * can be optionally filtered to the ones matching the given * +glob+, a standard Unix filename glob. * * The given block will be called once with the name of each reference. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_reference_collection_each_name(int argc, VALUE *argv, VALUE self) { return rb_git_reference_collection__each(argc, argv, self, 1); } /* * call-seq: * references.exist?(name) -> true or false * references.exists?(name) -> true or false * * Check if a given reference exists with the given +name+. */ static VALUE rb_git_reference_collection_exist_p(VALUE self, VALUE rb_name_or_ref) { VALUE rb_repo = rugged_owner(self); git_repository *repo; git_reference *ref; int error; if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference)) rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_ref) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_lookup(&ref, repo, StringValueCStr(rb_name_or_ref)); git_reference_free(ref); if (error == GIT_ENOTFOUND) return Qfalse; else rugged_exception_check(error); return Qtrue; } /* * call-seq: * references.rename(old_name, new_name, options = {}) -> new_ref * references.rename(ref, new_name, options = {}) -> new_ref * * Change the name of a reference. If +force+ is +true+, any previously * existing references will be overwritten when renaming. * * Return a new reference object with the new object * * reference.name #=> 'refs/heads/master' * new_ref = references.rename(ref, 'refs/heads/development') #=> * new_ref.name #=> 'refs/heads/development' * * The following options can be passed in the +options+ Hash: * * :force :: * Overwrites the reference with the given +name+, if it already exists, * instead of raising an exception. * * If a reference with the given +new_name+ already exists and +:force+ is not +true+, * an exception will be raised. */ static VALUE rb_git_reference_collection_rename(int argc, VALUE *argv, VALUE self) { VALUE rb_new_name, rb_name_or_ref, rb_options; VALUE rb_repo = rugged_owner(self); git_reference *ref, *out = NULL; git_repository *repo; char *log_message = NULL; int error, force = 0; rb_scan_args(argc, argv, "20:", &rb_name_or_ref, &rb_new_name, &rb_options); Check_Type(rb_new_name, T_STRING); if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference)) rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_ref) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if (!NIL_P(rb_options)) { VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message")); if (!NIL_P(rb_val)) log_message = StringValueCStr(rb_val); force = RTEST(rb_hash_aref(rb_options, CSTR2SYM("force"))); } if ((error = git_reference_lookup(&ref, repo, StringValueCStr(rb_name_or_ref))) == GIT_OK) error = git_reference_rename(&out, ref, StringValueCStr(rb_new_name), force, log_message); git_reference_free(ref); rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, rugged_owner(self), out); } /* * call-seq: * references.update(ref, oid) -> new_ref * references.update(name, oid) -> new_ref * references.update(ref, other_ref) -> new_ref * references.update(name, other_ref_name) -> new_ref * * Set the target of a reference. If +ref+ is a direct reference, * the new target must be a +String+ representing a SHA1 OID. * * If +reference+ is symbolic, the new target must be a +String+ with * the name of another reference. * * The original reference is unaltered; a new reference object is * returned with the new target, and the changes are persisted to * disk. * * r1.type #=> :symbolic * references.update(r1, "refs/heads/master") #=> * * r2.type #=> :direct * references.update(r2, "de5ba987198bcf2518885f0fc1350e5172cded78") #=> */ static VALUE rb_git_reference_collection_update(int argc, VALUE *argv, VALUE self) { VALUE rb_repo = rugged_owner(self), rb_name_or_ref, rb_target, rb_options; git_repository *repo = NULL; git_reference *ref = NULL, *out = NULL; char *log_message = NULL; int error; rb_scan_args(argc, argv, "20:", &rb_name_or_ref, &rb_target, &rb_options); if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference)) rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_ref) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); if (rb_obj_is_kind_of(rb_target, rb_cRuggedReference)) rb_target = rb_funcall(rb_target, rb_intern("canonical_name"), 0); if (TYPE(rb_target) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); if (!NIL_P(rb_options)) { VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message")); if (!NIL_P(rb_val)) log_message = StringValueCStr(rb_val); } rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_lookup(&ref, repo, StringValueCStr(rb_name_or_ref)); rugged_exception_check(error); if (git_reference_type(ref) == GIT_REF_OID) { git_oid target; error = git_oid_fromstr(&target, StringValueCStr(rb_target)); if (error) goto cleanup; error = git_reference_set_target(&out, ref, &target, log_message); } else { error = git_reference_symbolic_set_target(&out, ref, StringValueCStr(rb_target), log_message); } cleanup: git_reference_free(ref); rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, rb_repo, out); } /* * call-seq: * references.delete(ref) -> nil * references.delete(name) -> nil * * Delete specified reference. * * If a Rugged::Reference object was passed, the object will become * invalidated and won't be able to be used for any other operations. * * repo.references.delete("HEAD") * # Reference no longer exists on disk */ static VALUE rb_git_reference_collection_delete(VALUE self, VALUE rb_name_or_ref) { VALUE rb_repo = rugged_owner(self); git_reference *ref; git_repository *repo; int error; if (rb_obj_is_kind_of(rb_name_or_ref, rb_cRuggedReference)) rb_name_or_ref = rb_funcall(rb_name_or_ref, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_ref) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Reference instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_reference_lookup(&ref, repo, StringValueCStr(rb_name_or_ref)); rugged_exception_check(error); error = git_reference_delete(ref); git_reference_free(ref); rugged_exception_check(error); return Qnil; } void Init_rugged_reference_collection(void) { rb_cRuggedReferenceCollection = rb_define_class_under(rb_mRugged, "ReferenceCollection", rb_cObject); rb_include_module(rb_cRuggedReferenceCollection, rb_mEnumerable); rb_define_method(rb_cRuggedReferenceCollection, "initialize", rb_git_reference_collection_initialize, 1); rb_define_method(rb_cRuggedReferenceCollection, "create", rb_git_reference_collection_create, -1); rb_define_method(rb_cRuggedReferenceCollection, "[]", rb_git_reference_collection_aref, 1); rb_define_method(rb_cRuggedReferenceCollection, "each", rb_git_reference_collection_each, -1); rb_define_method(rb_cRuggedReferenceCollection, "each_name", rb_git_reference_collection_each_name, -1); rb_define_method(rb_cRuggedReferenceCollection, "exist?", rb_git_reference_collection_exist_p, 1); rb_define_method(rb_cRuggedReferenceCollection, "exists?", rb_git_reference_collection_exist_p, 1); rb_define_method(rb_cRuggedReferenceCollection, "move", rb_git_reference_collection_rename, -1); rb_define_method(rb_cRuggedReferenceCollection, "rename", rb_git_reference_collection_rename, -1); rb_define_method(rb_cRuggedReferenceCollection, "update", rb_git_reference_collection_update, -1); rb_define_method(rb_cRuggedReferenceCollection, "delete", rb_git_reference_collection_delete, 1); } rugged-0.26.0/ext/rugged/rugged_branch_collection.c0000644000175000017500000002735213147033070022437 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedBranch; VALUE rb_cRuggedBranchCollection; static inline VALUE rugged_branch_new(VALUE owner, git_reference *ref) { return rugged_ref_new(rb_cRuggedBranch, owner, ref); } // Helper method to normalize branch lookups. static inline int rugged_branch_lookup(git_reference **branch, git_repository *repo, VALUE rb_name_or_branch) { if (rb_obj_is_kind_of(rb_name_or_branch, rb_cRuggedBranch)) { rb_name_or_branch = rb_funcall(rb_name_or_branch, rb_intern("canonical_name"), 0); if (TYPE(rb_name_or_branch) != T_STRING) rb_raise(rb_eTypeError, "Expected #canonical_name to return a String"); return git_reference_lookup(branch, repo, StringValueCStr(rb_name_or_branch)); } else if (TYPE(rb_name_or_branch) == T_STRING) { char *branch_name = StringValueCStr(rb_name_or_branch), *ref_name; int error; if (strncmp(branch_name, "refs/heads/", strlen("refs/heads/")) == 0 || strncmp(branch_name, "refs/remotes/", strlen("refs/remotes/")) == 0) return git_reference_lookup(branch, repo, branch_name); if ((error = git_branch_lookup(branch, repo, branch_name, GIT_BRANCH_LOCAL)) == GIT_OK || error != GIT_ENOTFOUND) return error; if ((error = git_branch_lookup(branch, repo, branch_name, GIT_BRANCH_REMOTE)) == GIT_OK || error != GIT_ENOTFOUND) return error; ref_name = xmalloc((strlen(branch_name) + strlen("refs/") + 1) * sizeof(char)); strcpy(ref_name, "refs/"); strcat(ref_name, branch_name); error = git_reference_lookup(branch, repo, ref_name); xfree(ref_name); return error; } else { rb_raise(rb_eTypeError, "Expecting a String or Rugged::Branch instance"); } } /* * call-seq: * BranchCollection.new(repo) -> refs * * Creates and returns a new collection of branches for the given +repo+. */ static VALUE rb_git_branch_collection_initialize(VALUE self, VALUE repo) { rugged_set_owner(self, repo); return self; } static git_branch_t parse_branch_type(VALUE rb_filter) { ID id_filter; Check_Type(rb_filter, T_SYMBOL); id_filter = SYM2ID(rb_filter); if (id_filter == rb_intern("local")) { return GIT_BRANCH_LOCAL; } else if (id_filter == rb_intern("remote")) { return GIT_BRANCH_REMOTE; } else { rb_raise(rb_eTypeError, "Invalid branch filter. Expected `:remote`, `:local` or `nil`"); } } /* * call-seq: * branches.create(name, target, options = {}) -> branch * * Create a new branch with the given +name+, pointing to the +target+. * * +name+ needs to be a branch name, not an absolute reference path * (e.g. +development+ instead of +refs/heads/development+). * * +target+ needs to be an existing commit in the given repository. * * The following options can be passed in the +options+ Hash: * * :force :: * Overwrites the branch with the given +name+, if it already exists, * instead of raising an exception. * * If a branch with the given +name+ already exists and +:force+ is not +true+, * an exception will be raised. * * Returns a Rugged::Branch for the newly created branch. */ static VALUE rb_git_branch_collection_create(int argc, VALUE *argv, VALUE self) { VALUE rb_repo = rugged_owner(self), rb_name, rb_target, rb_options; git_repository *repo; git_reference *branch; git_commit *target; int error, force = 0; rb_scan_args(argc, argv, "20:", &rb_name, &rb_target, &rb_options); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); Check_Type(rb_target, T_STRING); if (!NIL_P(rb_options)) { force = RTEST(rb_hash_aref(rb_options, CSTR2SYM("force"))); } target = (git_commit *)rugged_object_get(repo, rb_target, GIT_OBJ_COMMIT); error = git_branch_create(&branch, repo, StringValueCStr(rb_name), target, force); git_commit_free(target); rugged_exception_check(error); return rugged_branch_new(rb_repo, branch); } /* * call-seq: * branches[name] -> branch * * Return the branch with the given +name+. * * Branches can be looked up by their relative (+development+) or absolute * (+refs/heads/development+) branch name. * * If a local branch and a remote branch both share the same short name * (e.g. +refs/heads/origin/master+ and +refs/remotes/origin/master+), * passing +origin/master+ as the +name+ will return the local branch. * You can explicitly request the local branch by passing * +heads/origin/master+, or the remote branch through +remotes/origin/master+. * * Returns the looked up branch, or +nil+ if the branch doesn't exist. */ static VALUE rb_git_branch_collection_aref(VALUE self, VALUE rb_name) { git_reference *branch; git_repository *repo; VALUE rb_repo = rugged_owner(self); int error; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); error = rugged_branch_lookup(&branch, repo, rb_name); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_branch_new(rb_repo, branch); } static VALUE each_branch(int argc, VALUE *argv, VALUE self, int branch_names_only) { VALUE rb_repo = rugged_owner(self), rb_filter; git_repository *repo; git_branch_iterator *iter; int error, exception = 0; git_branch_t filter = (GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE), branch_type; rb_scan_args(argc, argv, "01", &rb_filter); if (!rb_block_given_p()) { VALUE symbol = branch_names_only ? CSTR2SYM("each_name") : CSTR2SYM("each"); return rb_funcall(self, rb_intern("to_enum"), 2, symbol, rb_filter); } rugged_check_repo(rb_repo); if (!NIL_P(rb_filter)) filter = parse_branch_type(rb_filter); Data_Get_Struct(rb_repo, git_repository, repo); error = git_branch_iterator_new(&iter, repo, filter); rugged_exception_check(error); if (branch_names_only) { git_reference *branch; while (!exception && (error = git_branch_next(&branch, &branch_type, iter)) == GIT_OK) { rb_protect(rb_yield, rb_str_new_utf8(git_reference_shorthand(branch)), &exception); } } else { git_reference *branch; while (!exception && (error = git_branch_next(&branch, &branch_type, iter)) == GIT_OK) { rb_protect(rb_yield, rugged_branch_new(rb_repo, branch), &exception); } } git_branch_iterator_free(iter); if (exception) rb_jump_tag(exception); if (error != GIT_ITEROVER) rugged_exception_check(error); return Qnil; } /* * call-seq: * branches.each([filter]) { |branch| block } * branches.each([filter]) -> enumerator * * Iterate through the branches in the collection. Iteration can be * optionally filtered to yield only +:local+ or +:remote+ branches. * * The given block will be called once with a +Rugged::Branch+ object * for each branch in the repository. If no block is given, an enumerator * will be returned. */ static VALUE rb_git_branch_collection_each(int argc, VALUE *argv, VALUE self) { return each_branch(argc, argv, self, 0); } /* * call-seq: * branches.each_name([filter]) { |branch_name| block } * branches.each_name([filter]) -> enumerator * * Iterate through the names of the branches in the collection. Iteration can be * optionally filtered to yield only +:local+ or +:remote+ branches. * * The given block will be called once with the name of each branch as a +String+. * If no block is given, an enumerator will be returned. */ static VALUE rb_git_branch_collection_each_name(int argc, VALUE *argv, VALUE self) { return each_branch(argc, argv, self, 1); } /* * call-seq: * branches.delete(branch) -> nil * branches.delete(name) -> nil * * Delete the specified branch. * * If a Rugged::Branch object was passed, the object will become * invalidated and won't be able to be used for any other operations. */ static VALUE rb_git_branch_collection_delete(VALUE self, VALUE rb_name_or_branch) { git_reference *branch; git_repository *repo; VALUE rb_repo = rugged_owner(self); int error; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = rugged_branch_lookup(&branch, repo, rb_name_or_branch); rugged_exception_check(error); error = git_branch_delete(branch); git_reference_free(branch); rugged_exception_check(error); return Qnil; } /* * call-seq: * branch.move(old_name, new_name, options = {}) -> new_branch * branch.move(branch, new_name, options = {}) -> new_branch * branch.rename(old_name, new_name, options = {}) -> new_branch * branch.rename(branch, new_name, options = {}) -> new_branch * * Rename a branch to +new_name+. * * +new_name+ needs to be a branch name, not an absolute reference path * (e.g. +development+ instead of +refs/heads/development+). * * The following options can be passed in the +options+ Hash: * * :force :: * Overwrites the branch with the given +name+, if it already exists, * instead of raising an exception. * * If a branch with the given +new_name+ already exists and +:force+ is not +true+, * an exception will be raised. * * A new Rugged::Branch object for the renamed branch will be returned. * */ static VALUE rb_git_branch_collection_move(int argc, VALUE *argv, VALUE self) { VALUE rb_repo = rugged_owner(self), rb_name_or_branch, rb_new_branch_name, rb_options; git_reference *old_branch = NULL, *new_branch = NULL; git_repository *repo; int error, force = 0; rb_scan_args(argc, argv, "20:", &rb_name_or_branch, &rb_new_branch_name, &rb_options); Check_Type(rb_new_branch_name, T_STRING); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = rugged_branch_lookup(&old_branch, repo, rb_name_or_branch); rugged_exception_check(error); if (!NIL_P(rb_options)) { force = RTEST(rb_hash_aref(rb_options, CSTR2SYM("force"))); } error = git_branch_move(&new_branch, old_branch, StringValueCStr(rb_new_branch_name), force); git_reference_free(old_branch); rugged_exception_check(error); return rugged_branch_new(rugged_owner(self), new_branch); } /* * call-seq: * branches.exist?(name) -> true or false * branches.exists?(name) -> true or false * * Check if a branch exists with the given +name+. */ static VALUE rb_git_branch_collection_exist_p(VALUE self, VALUE rb_name) { VALUE rb_repo = rugged_owner(self); git_repository *repo; git_reference *branch; int error; Check_Type(rb_name, T_STRING); Data_Get_Struct(rb_repo, git_repository, repo); error = rugged_branch_lookup(&branch, repo, rb_name); git_reference_free(branch); if (error == GIT_ENOTFOUND) return Qfalse; else rugged_exception_check(error); return Qtrue; } void Init_rugged_branch_collection(void) { rb_cRuggedBranchCollection = rb_define_class_under(rb_mRugged, "BranchCollection", rb_cObject); rb_include_module(rb_cRuggedBranchCollection, rb_mEnumerable); rb_define_method(rb_cRuggedBranchCollection, "initialize", rb_git_branch_collection_initialize, 1); rb_define_method(rb_cRuggedBranchCollection, "[]", rb_git_branch_collection_aref, 1); rb_define_method(rb_cRuggedBranchCollection, "create", rb_git_branch_collection_create, -1); rb_define_method(rb_cRuggedBranchCollection, "each", rb_git_branch_collection_each, -1); rb_define_method(rb_cRuggedBranchCollection, "each_name", rb_git_branch_collection_each_name, -1); rb_define_method(rb_cRuggedBranchCollection, "exist?", rb_git_branch_collection_exist_p, 1); rb_define_method(rb_cRuggedBranchCollection, "exists?", rb_git_branch_collection_exist_p, 1); rb_define_method(rb_cRuggedBranchCollection, "move", rb_git_branch_collection_move, -1); rb_define_method(rb_cRuggedBranchCollection, "rename", rb_git_branch_collection_move, -1); rb_define_method(rb_cRuggedBranchCollection, "delete", rb_git_branch_collection_delete, 1); } rugged-0.26.0/ext/rugged/rugged_tag_collection.c0000644000175000017500000002156213147033070021752 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedTag; VALUE rb_cRuggedTagCollection; /* * call-seq: * TagCollection.new(repo) -> refs */ static VALUE rb_git_tag_collection_initialize(VALUE self, VALUE repo) { rugged_set_owner(self, repo); return self; } /* * call-seq: * tags[name] -> tag * * Lookup a tag in +repo+, with the given +name+. * * +name+ can be a short or canonical tag name * (e.g. +v0.1.0+ or +refs/tags/v0.1.0+). * * Returns the looked up tag, or +nil+ if the tag doesn't exist. */ static VALUE rb_git_tag_collection_aref(VALUE self, VALUE rb_name) { git_reference *tag; git_repository *repo; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); error = git_reference_lookup(&tag, repo, StringValueCStr(rb_name)); if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC) { char *canonical_ref = xmalloc((RSTRING_LEN(rb_name) + strlen("refs/tags/") + 1) * sizeof(char)); strcpy(canonical_ref, "refs/tags/"); strcat(canonical_ref, StringValueCStr(rb_name)); error = git_reference_lookup(&tag, repo, canonical_ref); xfree(canonical_ref); if (error == GIT_ENOTFOUND) return Qnil; } rugged_exception_check(error); return rugged_ref_new(rb_cRuggedTag, rb_repo, tag); } /* * call-seq: * tags.delete(name) -> nil * * Delete the tag reference identified by +name+. */ static VALUE rb_git_tag_collection_delete(VALUE self, VALUE rb_name) { VALUE rb_repo = rugged_owner(self); git_repository *repo; int error; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); error = git_tag_delete(repo, StringValueCStr(rb_name)); rugged_exception_check(error); return Qnil; } /* * call-seq: * tags.create(name, target[, force = false][, annotation = nil]) -> oid * * Create a new tag with the specified +name+ on +target+ in +repo+. * * If +annotation+ is not +nil+, it will cause the creation of an annotated tag object. * +annotation+ has to contain the following key value pairs: * * :tagger :: * An optional Hash containing a git signature. Defaults to the signature * from the configuration if only `:message` is given. Will cause the * creation of an annotated tag object if present. * * :message :: * An optional string containing the message for the new tag. * * Returns the OID of the newly created tag. */ static VALUE rb_git_tag_collection_create(int argc, VALUE *argv, VALUE self) { git_oid tag_oid; git_repository *repo = NULL; git_object *target = NULL; int error, force = 0; VALUE rb_repo = rugged_owner(self), rb_name, rb_target, rb_force, rb_annotation; rb_scan_args(argc, argv, "21:", &rb_name, &rb_target, &rb_force, &rb_annotation); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); if (!NIL_P(rb_force)) force = rugged_parse_bool(rb_force); target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY); if (NIL_P(rb_annotation)) { error = git_tag_create_lightweight( &tag_oid, repo, StringValueCStr(rb_name), target, force ); } else { git_signature *tagger = rugged_signature_get( rb_hash_aref(rb_annotation, CSTR2SYM("tagger")), repo ); VALUE rb_message = rb_hash_aref(rb_annotation, CSTR2SYM("message")); Check_Type(rb_message, T_STRING); error = git_tag_create( &tag_oid, repo, StringValueCStr(rb_name), target, tagger, StringValueCStr(rb_message), force ); git_signature_free(tagger); } git_object_free(target); rugged_exception_check(error); return rb_git_tag_collection_aref(self, rb_name); } /* * call-seq: * tags.create_annotation(name, target, annotation) -> annotation * * Create a new annotated tag object with the specified +name+ on +target+ in * +repo+. * * Unlike the +create+ method, +create_annotation+ simply creates a tag * object. It does not write a tag ref. * * +annotation+ must have the following keys: * * :tagger :: * An optional Hash containing a git signature. Defaults to the signature * from the configuration if only `:message` is given. Will cause the * creation of an annotated tag object if present. * * :message :: * An optional string containing the message for the new tag. * * Returns an instance of Rugged::Tag::Annotation representing the newly * created annotation. */ static VALUE rb_git_tag_collection_create_annotation(VALUE self, VALUE rb_name, VALUE rb_target, VALUE rb_annotation) { git_oid tag_oid; git_repository *repo = NULL; git_object *target = NULL, *tag = NULL; git_signature *tagger = NULL; VALUE rb_message; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY); rb_message = rb_hash_aref(rb_annotation, CSTR2SYM("message")); Check_Type(rb_message, T_STRING); tagger = rugged_signature_get( rb_hash_aref(rb_annotation, CSTR2SYM("tagger")), repo ); error = git_tag_annotation_create( &tag_oid, repo, StringValueCStr(rb_name), target, tagger, StringValueCStr(rb_message) ); git_object_free(target); git_signature_free(tagger); rugged_exception_check(error); error = git_object_lookup(&tag, repo, &tag_oid, GIT_OBJ_TAG); rugged_exception_check(error); return rugged_object_new(rb_repo, tag); } static VALUE each_tag(int argc, VALUE *argv, VALUE self, int tag_names_only) { git_repository *repo; git_strarray tags; size_t i; int error, exception = 0; VALUE rb_repo = rugged_owner(self), rb_pattern; const char *pattern = NULL; rb_scan_args(argc, argv, "01", &rb_pattern); if (!rb_block_given_p()) { VALUE symbol = tag_names_only ? CSTR2SYM("each_name") : CSTR2SYM("each"); return rb_funcall(self, rb_intern("to_enum"), 2, symbol, rb_pattern); } if (!NIL_P(rb_pattern)) { Check_Type(rb_pattern, T_STRING); pattern = StringValueCStr(rb_pattern); } rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_tag_list_match(&tags, pattern ? pattern : "", repo); rugged_exception_check(error); if (tag_names_only) { for (i = 0; !exception && i < tags.count; ++i) rb_protect(rb_yield, rb_str_new_utf8(tags.strings[i]), &exception); } else { for (i = 0; !exception && i < tags.count; ++i) { rb_protect(rb_yield, rb_git_tag_collection_aref(self, rb_str_new_utf8(tags.strings[i])), &exception); } } git_strarray_free(&tags); if (exception) rb_jump_tag(exception); return Qnil; } /* * call-seq: * tags.each_name([pattern]) { |name| block } -> nil * tags.each_name([pattern]) -> enumerator * * Iterate through all the tag names in +repo+. Iteration * can be optionally filtered to the ones matching the given * +pattern+, a standard Unix filename glob. * * If +pattern+ is empty or not given, all tag names will be returned. * * The given block will be called once with the name for each tag. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_tag_collection_each_name(int argc, VALUE *argv, VALUE self) { return each_tag(argc, argv, self, 1); } /* * call-seq: * tags.each([pattern]) { |name| block } -> nil * tags.each([pattern]) -> enumerator * * Iterate through all the tags in +repo+. Iteration * can be optionally filtered to the ones matching the given * +pattern+, a standard Unix filename glob. * * If +pattern+ is empty or not given, all tag names will be returned. * * The given block will be called once with the name for each tag. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_tag_collection_each(int argc, VALUE *argv, VALUE self) { return each_tag(argc, argv, self, 0); } void Init_rugged_tag_collection(void) { rb_cRuggedTagCollection = rb_define_class_under(rb_mRugged, "TagCollection", rb_cObject); rb_include_module(rb_cRuggedTagCollection, rb_mEnumerable); rb_define_method(rb_cRuggedTagCollection, "initialize", rb_git_tag_collection_initialize, 1); rb_define_method(rb_cRuggedTagCollection, "create", rb_git_tag_collection_create, -1); rb_define_method(rb_cRuggedTagCollection, "create_annotation", rb_git_tag_collection_create_annotation, 3); rb_define_method(rb_cRuggedTagCollection, "[]", rb_git_tag_collection_aref, 1); rb_define_method(rb_cRuggedTagCollection, "each", rb_git_tag_collection_each, -1); rb_define_method(rb_cRuggedTagCollection, "each_name", rb_git_tag_collection_each_name, -1); rb_define_method(rb_cRuggedTagCollection, "delete", rb_git_tag_collection_delete, 1); } rugged-0.26.0/ext/rugged/rugged_submodule.c0000644000175000017500000006031713147033070020764 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_cRuggedSubmodule; static VALUE id_in_head, id_in_index, id_in_config, id_in_workdir; static VALUE id_index_added, id_index_deleted, id_index_modified; static VALUE id_wd_uninitialized, id_wd_added, id_wd_deleted, id_wd_modified; static VALUE id_wd_index_modified, id_wd_wd_modified, id_wd_untracked; static ID id_ignore_none, id_ignore_untracked, id_ignore_dirty, id_ignore_all; static ID id_update_checkout, id_update_rebase, id_update_merge, id_update_none; void init_status_list(void) { id_in_head = CSTR2SYM("in_head"); id_in_index = CSTR2SYM("in_index"); id_in_config = CSTR2SYM("in_config"); id_in_workdir = CSTR2SYM("in_workdir"); id_index_added = CSTR2SYM("added_to_index"); id_index_deleted = CSTR2SYM("deleted_from_index"); id_index_modified = CSTR2SYM("modified_in_index"); id_wd_uninitialized = CSTR2SYM("uninitialized"); id_wd_added = CSTR2SYM("added_to_workdir"); id_wd_deleted = CSTR2SYM("deleted_from_workdir"); id_wd_modified = CSTR2SYM("modified_in_workdir"); id_wd_index_modified = CSTR2SYM("dirty_workdir_index"); id_wd_wd_modified = CSTR2SYM("modified_files_in_workdir"); id_wd_untracked = CSTR2SYM("untracked_files_in_workdir"); return; } static VALUE submodule_status_flags_to_rb(unsigned int flags) { VALUE rb_flags = rb_ary_new(); if (flags & GIT_SUBMODULE_STATUS_IN_HEAD) rb_ary_push(rb_flags, id_in_head); if (flags & GIT_SUBMODULE_STATUS_IN_INDEX) rb_ary_push(rb_flags, id_in_index); if (flags & GIT_SUBMODULE_STATUS_IN_CONFIG) rb_ary_push(rb_flags, id_in_config); if (flags & GIT_SUBMODULE_STATUS_IN_WD) rb_ary_push(rb_flags, id_in_workdir); if (flags & GIT_SUBMODULE_STATUS_INDEX_ADDED) rb_ary_push(rb_flags, id_index_added); if (flags & GIT_SUBMODULE_STATUS_INDEX_DELETED) rb_ary_push(rb_flags, id_index_deleted); if (flags & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) rb_ary_push(rb_flags, id_index_modified); if (flags & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) rb_ary_push(rb_flags, id_wd_uninitialized); if (flags & GIT_SUBMODULE_STATUS_WD_ADDED) rb_ary_push(rb_flags, id_wd_added); if (flags & GIT_SUBMODULE_STATUS_WD_DELETED) rb_ary_push(rb_flags, id_wd_deleted); if (flags & GIT_SUBMODULE_STATUS_WD_MODIFIED) rb_ary_push(rb_flags, id_wd_modified); if (flags & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) rb_ary_push(rb_flags, id_wd_index_modified); if (flags & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) rb_ary_push(rb_flags, id_wd_wd_modified); if (flags & GIT_SUBMODULE_STATUS_WD_UNTRACKED) rb_ary_push(rb_flags, id_wd_untracked); return rb_flags; } /* * call-seq: * submodule.status -> array * * Returns an +array+ with the status flags for a submodule. * * Depending on the #ignore_rule property of the submodule, some of * the flags may never be returned because they indicate changes that are * supposed to be ignored. * * Submodule info is contained in 4 places: the +HEAD+ tree, the index, * config files (both +.git/config+ and +.gitmodules+), and the working * directory. Any or all of those places might be missing information about * the submodule depending on what state the repository is in. We consider * all four places to build the combination of status flags. * * There are four values that are not really status, but give basic info * about what sources of submodule data are available. These will be * returned even if ignore is set to +:all+. * * :in_head :: * superproject +HEAD+ contains submodule * :in_index :: * superproject index contains submodule * :in_config :: * superproject +.gitmodules+ has submodule * :in_workdir :: * superproject workdir has submodule * * The following values will be returned as long as ignore is not +:all+. * * :added_to_index :: * submodule is in index, not in +HEAD+ * :deleted_from_index :: * submodule is in +HEAD+, not in index * :modified_in_index :: * submodule in index and +HEAD+ don't match * :uninitialized :: * submodule in workdir is not initialized * :added_to_workdir :: * submodule is in workdir, not index * :deleted_from_workdir :: * submodule is in index, not workdir * :modified_in_workdir :: * submodule in index and workdir +HEAD+ don't match * * The following can only be returned if ignore is +:none+ or +:untracked+. * * :dirty_workdir_index :: * submodule workdir index is dirty * :modified_files_in_workdir :: * submodule workdir has modified files * * Lastly, the following will only be returned for ignore +:none+. * * :untracked_files_in_workdir :: * submodule workdir contains untracked files */ static VALUE rb_git_submodule_status(VALUE self) { VALUE rb_repo = rugged_owner(self); git_submodule *submodule; git_repository *repo; unsigned int flags; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Data_Get_Struct(self, git_submodule, submodule); rugged_exception_check( git_submodule_status(&flags, repo, git_submodule_name(submodule), GIT_SUBMODULE_IGNORE_UNSPECIFIED) ); return submodule_status_flags_to_rb(flags); } #define RB_GIT_SUBMODULE_LOCATION_FLAG_CHECK(flag) \ git_submodule *submodule; \ unsigned int flags; \ Data_Get_Struct(self, git_submodule, submodule); \ rugged_exception_check( \ git_submodule_location(&flags, submodule) \ ); \ return (flags & flag) ? Qtrue : Qfalse; \ /* * call-seq: * submodule.in_head? -> true or false * * Returns +true+ if superproject +HEAD+ contains submodule. */ static VALUE rb_git_submodule_status_in_head(VALUE self) { RB_GIT_SUBMODULE_LOCATION_FLAG_CHECK(GIT_SUBMODULE_STATUS_IN_HEAD) } /* * call-seq: * submodule.in_index? -> true or false * * Returns +true+ if superproject index contains submodule. */ static VALUE rb_git_submodule_status_in_index(VALUE self) { RB_GIT_SUBMODULE_LOCATION_FLAG_CHECK(GIT_SUBMODULE_STATUS_IN_INDEX) } /* * call-seq: * submodule.in_config? -> true or false * * Returns +true+ if superproject +.gitmodules+ has submodule. */ static VALUE rb_git_submodule_status_in_config(VALUE self) { RB_GIT_SUBMODULE_LOCATION_FLAG_CHECK(GIT_SUBMODULE_STATUS_IN_CONFIG) } /* * call-seq: * submodule.in_workdir? -> true or false * * Returns +true+ if superproject workdir has submodule. */ static VALUE rb_git_submodule_status_in_workdir(VALUE self) { RB_GIT_SUBMODULE_LOCATION_FLAG_CHECK(GIT_SUBMODULE_STATUS_IN_WD) } #define RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(flag) \ VALUE rb_repo = rugged_owner(self); \ git_repository *repo; \ git_submodule *submodule; \ unsigned int flags; \ rugged_check_repo(rb_repo); \ Data_Get_Struct(rb_repo, git_repository, repo); \ Data_Get_Struct(self, git_submodule, submodule); \ rugged_exception_check( \ git_submodule_status(&flags, repo, git_submodule_name(submodule), \ GIT_SUBMODULE_IGNORE_UNSPECIFIED) \ ); \ return (flags & flag) ? Qtrue : Qfalse; \ /* * call-seq: * submodule.added_to_index? -> true or false * * Returns +true+ if submodule is in index, not in +HEAD+. */ static VALUE rb_git_submodule_status_added_to_index(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_INDEX_ADDED) } /* * call-seq: * submodule.deleted_from_index? -> true or false * * Returns +true+ if submodule is in +HEAD+, not in index */ static VALUE rb_git_submodule_status_deleted_from_index(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_INDEX_DELETED) } /* * call-seq: * submodule.modified_in_index? -> true or false * * Returns +true+ if submodule in index and +HEAD+ don't match. */ static VALUE rb_git_submodule_status_modified_in_index(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_INDEX_MODIFIED) } /* * call-seq: * submodule.uninitialized? -> true or false * * Returns +true+ if submodule in workdir is not initialized. */ static VALUE rb_git_submodule_status_uninitialized(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) } /* * call-seq: * submodule.added_to_workdir? -> true or false * * Returns +true+ if submodule is in workdir, not index. */ static VALUE rb_git_submodule_status_added_to_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_ADDED) } /* * call-seq: * submodule.deleted_from_workdir? -> true or false * * Returns +true+ if submodule is in index, not workdir. */ static VALUE rb_git_submodule_status_deleted_from_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_DELETED) } /* * call-seq: * submodule.modified_in_workdir? -> true or false * * Returns +true+ if submodule in index and workdir +HEAD+ don't match. */ static VALUE rb_git_submodule_status_modified_in_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_MODIFIED) } /* * call-seq: * submodule.dirty_workdir_index? -> true or false * * Returns +true+ if submodule workdir index is dirty. */ static VALUE rb_git_submodule_status_dirty_workdir_index(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) } /* * call-seq: * submodule.modified_files_in_workdir? -> true or false * * Returns +true+ if submodule workdir has modified files. */ static VALUE rb_git_submodule_status_modified_files_in_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) } /* * call-seq: * submodule.untracked_files_in_workdir? -> true or false * * Returns +true+ if submodule workdir contains untracked files. */ static VALUE rb_git_submodule_status_untracked_files_in_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_FLAG_CHECK(GIT_SUBMODULE_STATUS_WD_UNTRACKED) } #define RB_GIT_SUBMODULE_STATUS_CHECK(check) \ VALUE rb_repo = rugged_owner(self); \ git_repository *repo; \ git_submodule *submodule; \ unsigned int flags; \ rugged_check_repo(rb_repo); \ Data_Get_Struct(rb_repo, git_repository, repo); \ Data_Get_Struct(self, git_submodule, submodule); \ rugged_exception_check( \ git_submodule_status(&flags, repo, git_submodule_name(submodule), \ GIT_SUBMODULE_IGNORE_UNSPECIFIED) \ ); \ return check(flags) ? Qtrue : Qfalse; \ /* * call-seq: * submodule.unmodified? -> true or false * * Returns +true+ if the submodule is unmodified. */ static VALUE rb_git_submodule_status_unmodified(VALUE self) { RB_GIT_SUBMODULE_STATUS_CHECK(GIT_SUBMODULE_STATUS_IS_UNMODIFIED) } /* * call-seq: * submodule.dirty_workdir? -> true or false * * Returns +true+ if the submodule workdir is dirty. * * The workdir is considered dirty if the workdir index is modified, there * are modified files in the workdir or if there are untracked files in the * workdir. */ static VALUE rb_git_submodule_status_dirty_workdir(VALUE self) { RB_GIT_SUBMODULE_STATUS_CHECK(GIT_SUBMODULE_STATUS_IS_WD_DIRTY) } /* * call-seq: * submodule.add_to_index([options]) -> submodule * * Add current submodule +HEAD+ commit to the index of superproject. * * The following options can be passed in the +options+ Hash: * * :write_index :: * (default +true+) If this should immediately write the index file. * If passed as +false+, Rugged::Repository#index can be used to explicitly * call Rugged::Index#write to save the change. */ static VALUE rb_git_submodule_add_to_index(int argc, VALUE *argv, VALUE self) { git_submodule *submodule; VALUE rb_options; int write_index = 1; Data_Get_Struct(self, git_submodule, submodule); rb_scan_args(argc, argv, ":", &rb_options); if (!NIL_P(rb_options)) { VALUE rb_val; rb_val = rb_hash_aref(rb_options, CSTR2SYM("write_index")); write_index = (rb_val != Qfalse); } rugged_exception_check( git_submodule_add_to_index(submodule, write_index) ); return self; } /* * call-seq: * submodule.reload -> submodule * * Reread submodule info from config, index, and +HEAD+. * * Call this to reread cached submodule information for this submodule if * there is reason to believe that it has changed. */ static VALUE rb_git_submodule_reload(VALUE self) { git_submodule *submodule; Data_Get_Struct(self, git_submodule, submodule); rugged_exception_check( git_submodule_reload(submodule, 1) ); return self; } /* * call-seq: * submodule.sync -> submodule * * Copy submodule remote info into submodule repository. * * This copies the information about the submodules URL into the checked out * submodule config, acting like "git submodule sync". This is useful * if the URL for the submodule was altered (by a manual change, or a fetch of * upstream changes) and the local repository needs to be updated. */ static VALUE rb_git_submodule_sync(VALUE self) { git_submodule *submodule; Data_Get_Struct(self, git_submodule, submodule); rugged_exception_check( git_submodule_sync(submodule) ); return self; } /* * call-seq: * submodule.init([options]) -> submodule * * Copy submodule info into +.git/config+ file. * * Just like "git submodule init", this copies information about the * submodule into +.git/config+. * * The following options can be passed in the +options+ Hash: * * :overwrite :: * (defaults to +false+) - By default, existing entries * will not be overwritten, but setting this to +true+ forces them to be * updated. */ static VALUE rb_git_submodule_init(int argc, VALUE *argv, VALUE self) { git_submodule *submodule; VALUE rb_options; int overwrite = 0; Data_Get_Struct(self, git_submodule, submodule); rb_scan_args(argc, argv, ":", &rb_options); if (!NIL_P(rb_options)) { VALUE rb_val; rb_val = rb_hash_aref(rb_options, CSTR2SYM("overwrite")); overwrite = RTEST(rb_val); } rugged_exception_check( git_submodule_init(submodule, overwrite) ); return self; } /* * call-seq: * submodule.name -> string * * Returns the name of the submodule. */ static VALUE rb_git_submodule_name(VALUE self) { git_submodule *submodule; const char *name; Data_Get_Struct(self, git_submodule, submodule); name = git_submodule_name(submodule); return rb_str_new_utf8(name); } /* * call-seq: * submodule.url -> string or nil * * Returns the URL of the submodule. */ static VALUE rb_git_submodule_url(VALUE self) { git_submodule *submodule; const char *url; Data_Get_Struct(self, git_submodule, submodule); url = git_submodule_url(submodule); return url ? rb_str_new_utf8(url) : Qnil; } /* * call-seq: * submodule.path -> string * * Returns the path of the submodule. * * The +path+ is almost always the same as the #name, * but the two are actually not required to match. */ static VALUE rb_git_submodule_path(VALUE self) { git_submodule *submodule; const char *path; Data_Get_Struct(self, git_submodule, submodule); path = git_submodule_path(submodule); return rb_str_new_utf8(path); } #define RB_GIT_OID_GETTER(_klass, _attribute) \ git_##_klass *object; \ const git_oid * oid; \ Data_Get_Struct(self, git_##_klass, object); \ oid = git_##_klass##_##_attribute(object); \ return oid ? rugged_create_oid(oid) : Qnil; \ /* * call-seq: * submodule.head_oid -> string or nil * * Returns the OID for the submodule in the current +HEAD+ tree or +nil+ * if the submodule is not in the +HEAD+. */ static VALUE rb_git_submodule_head_id(VALUE self) { RB_GIT_OID_GETTER(submodule, head_id); } /* * call-seq: * submodule.index_oid -> string or nil * * Returns the OID for the submodule in the index or +nil+ if the submodule * is not in the index. */ static VALUE rb_git_submodule_index_id(VALUE self) { RB_GIT_OID_GETTER(submodule, index_id); } /* * call-seq: * submodule.workdir_oid -> string or nil * * Returns the OID for the submodule in the current working directory or * +nil+ of the submodule is not checked out. * * This returns the OID that corresponds to looking up +HEAD+ in the checked * out submodule. If there are pending changes in the index or anything * else, this won't notice that. #status can be called for a more complete * picture about the state of the working directory. */ static VALUE rb_git_submodule_wd_id(VALUE self) { RB_GIT_OID_GETTER(submodule, wd_id); } /* * call-seq: * submodule.fetch_recurse_submodules? -> true or false * * Returns the +fetchRecurseSubmodules+ rule for a submodule. * * This accesses the submodule..fetchRecurseSubmodules value * for the submodule that controls fetching behavior for the submodule. * * Note that at this time, +Rugged+ does not honor this setting and the fetch * functionality currently ignores submodules. * */ static VALUE rb_git_submodule_fetch_recurse_submodules(VALUE self) { git_submodule *submodule; Data_Get_Struct(self, git_submodule, submodule); return git_submodule_fetch_recurse_submodules(submodule) ? Qtrue : Qfalse; } static VALUE rb_git_subm_ignore_rule_fromC(git_submodule_ignore_t rule) { switch(rule) { case GIT_SUBMODULE_IGNORE_NONE: return ID2SYM(id_ignore_none); case GIT_SUBMODULE_IGNORE_UNTRACKED: return ID2SYM(id_ignore_untracked); case GIT_SUBMODULE_IGNORE_DIRTY: return ID2SYM(id_ignore_dirty); case GIT_SUBMODULE_IGNORE_ALL: return ID2SYM(id_ignore_all); default: return CSTR2SYM("unknown"); } } /* * call-seq: * submodule.ignore_rule -> symbol * * Returns the ignore rule for a submodule. * * There are four ignore values: * * :none (default):: * will consider any change to the contents of the submodule from * a clean checkout to be dirty, including the addition of untracked files. * :untracked :: * examines the contents of the working tree but untracked files will not * count as making the submodule dirty. * :dirty :: * means to only check if the +HEAD+ of the submodule has moved for status. * This is fast since it does not need to scan the working tree * of the submodule at all. * :all :: * means not to open the submodule repository. The working directory will be * considered clean so long as there is a checked out version present. * * See #status on how ignore rules reflect the returned status info * for a submodule. */ static VALUE rb_git_submodule_ignore_rule(VALUE self) { git_submodule *submodule; git_submodule_ignore_t ignore; Data_Get_Struct(self, git_submodule, submodule); ignore = git_submodule_ignore(submodule); return rb_git_subm_ignore_rule_fromC(ignore); } static VALUE rb_git_subm_update_rule_fromC(git_submodule_update_t rule) { switch(rule) { case GIT_SUBMODULE_UPDATE_CHECKOUT: return ID2SYM(id_update_checkout); case GIT_SUBMODULE_UPDATE_REBASE: return ID2SYM(id_update_rebase); case GIT_SUBMODULE_UPDATE_MERGE: return ID2SYM(id_update_merge); case GIT_SUBMODULE_UPDATE_NONE: return ID2SYM(id_update_none); default: return CSTR2SYM("unknown"); } } /* * call-seq: * submodule.update_rule -> symbol * * Returns the update rule for a submodule. * * There are four update_rule values: * * :checkout (default):: * the new commit specified in the superproject will be checked out in the * submodule on a detached +HEAD+. * :rebase :: * the current branch of the submodule will be rebased onto the commit * specified in the superproject. * :merge :: * the commit specified in the superproject will be merged into the current * branch of the submodule. * :none :: * the submodule will not be updated */ static VALUE rb_git_submodule_update_rule(VALUE self) { git_submodule *submodule; git_submodule_update_t update; Data_Get_Struct(self, git_submodule, submodule); update = git_submodule_update_strategy(submodule); return rb_git_subm_update_rule_fromC(update); } /* * call-seq: * submodule.repository -> repository * * Returns the +repository+ for the submodule. * * The returned +repository+ is a newly opened Rugged::Repository object. * This will only work if the submodule is checked out into the working * directory. */ static VALUE rb_git_submodule_repository(VALUE self) { git_submodule *submodule; git_repository *repo; Data_Get_Struct(self, git_submodule, submodule); rugged_exception_check( git_submodule_open(&repo, submodule) ); return rugged_repo_new(rb_cRuggedRepo, repo); } /* * call-seq: * submodule.finalize_add -> submodule * * Resolve the setup of a new submodule. * * This should be called on a submodule once * Rugged::SubmoduleCollection#setup_add is finished and the sumodule repo is * cloned. * * This adds the +.gitmodules+ file and the newly cloned submodule to the index * to be ready to be committed (but doesn't actually do the commit). */ static VALUE rb_git_submodule_finalize_add(VALUE self) { git_submodule *submodule; Data_Get_Struct(self, git_submodule, submodule); rugged_exception_check( git_submodule_add_finalize(submodule) ); return self; } void Init_rugged_submodule(void) { init_status_list(); id_ignore_none = rb_intern("none"); id_ignore_dirty = rb_intern("dirty"); id_ignore_untracked = rb_intern("untracked"); id_ignore_all = rb_intern("all"); id_update_checkout = rb_intern("checkout"); id_update_rebase = rb_intern("rebase"); id_update_merge = rb_intern("merge"); id_update_none = rb_intern("none"); rb_cRuggedSubmodule = rb_define_class_under(rb_mRugged, "Submodule", rb_cObject); rb_define_method(rb_cRuggedSubmodule, "finalize_add", rb_git_submodule_finalize_add, 0); rb_define_method(rb_cRuggedSubmodule, "name", rb_git_submodule_name, 0); rb_define_method(rb_cRuggedSubmodule, "url", rb_git_submodule_url, 0); rb_define_method(rb_cRuggedSubmodule, "path", rb_git_submodule_path, 0); rb_define_method(rb_cRuggedSubmodule, "fetch_recurse_submodules?", rb_git_submodule_fetch_recurse_submodules, 0); rb_define_method(rb_cRuggedSubmodule, "ignore_rule", rb_git_submodule_ignore_rule, 0); rb_define_method(rb_cRuggedSubmodule, "update_rule", rb_git_submodule_update_rule, 0); rb_define_method(rb_cRuggedSubmodule, "head_oid", rb_git_submodule_head_id, 0); rb_define_method(rb_cRuggedSubmodule, "index_oid", rb_git_submodule_index_id, 0); rb_define_method(rb_cRuggedSubmodule, "workdir_oid", rb_git_submodule_wd_id, 0); rb_define_method(rb_cRuggedSubmodule, "status", rb_git_submodule_status, 0); rb_define_method(rb_cRuggedSubmodule, "in_head?", rb_git_submodule_status_in_head, 0); rb_define_method(rb_cRuggedSubmodule, "in_index?", rb_git_submodule_status_in_index, 0); rb_define_method(rb_cRuggedSubmodule, "in_config?", rb_git_submodule_status_in_config, 0); rb_define_method(rb_cRuggedSubmodule, "in_workdir?", rb_git_submodule_status_in_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "added_to_index?", rb_git_submodule_status_added_to_index, 0); rb_define_method(rb_cRuggedSubmodule, "deleted_from_index?", rb_git_submodule_status_deleted_from_index, 0); rb_define_method(rb_cRuggedSubmodule, "modified_in_index?", rb_git_submodule_status_modified_in_index, 0); rb_define_method(rb_cRuggedSubmodule, "uninitialized?", rb_git_submodule_status_uninitialized, 0); rb_define_method(rb_cRuggedSubmodule, "added_to_workdir?", rb_git_submodule_status_added_to_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "deleted_from_workdir?", rb_git_submodule_status_deleted_from_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "modified_in_workdir?", rb_git_submodule_status_modified_in_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "dirty_workdir_index?", rb_git_submodule_status_dirty_workdir_index, 0); rb_define_method(rb_cRuggedSubmodule, "modified_files_in_workdir?", rb_git_submodule_status_modified_files_in_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "untracked_files_in_workdir?", rb_git_submodule_status_untracked_files_in_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "unmodified?", rb_git_submodule_status_unmodified, 0); rb_define_method(rb_cRuggedSubmodule, "dirty_workdir?", rb_git_submodule_status_dirty_workdir, 0); rb_define_method(rb_cRuggedSubmodule, "repository", rb_git_submodule_repository, 0); rb_define_method(rb_cRuggedSubmodule, "add_to_index", rb_git_submodule_add_to_index, -1); rb_define_method(rb_cRuggedSubmodule, "reload", rb_git_submodule_reload, 0); rb_define_method(rb_cRuggedSubmodule, "sync", rb_git_submodule_sync, 0); rb_define_method(rb_cRuggedSubmodule, "init", rb_git_submodule_init, -1); } rugged-0.26.0/ext/rugged/rugged_branch.c0000644000175000017500000001055013147033070020214 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedObject; extern VALUE rb_cRuggedReference; VALUE rb_cRuggedBranch; static inline VALUE rugged_branch_new(VALUE owner, git_reference *ref) { return rugged_ref_new(rb_cRuggedBranch, owner, ref); } /* * call-seq: * branch.head? -> true or false * * Returns +true+ if the branch is pointed at by +HEAD+, +false+ otherwise. */ static VALUE rb_git_branch_head_p(VALUE self) { git_reference *branch; Data_Get_Struct(self, git_reference, branch); return git_branch_is_head(branch) ? Qtrue : Qfalse; } /* * call-seq: * branch.name -> string * * Returns the name of +branch+. * * See Rugged::Reference#canonical_name if you need the fully qualified * name of the underlying reference. */ static VALUE rb_git_branch_name(VALUE self) { git_reference *branch; const char *branch_name; Data_Get_Struct(self, git_reference, branch); rugged_exception_check(git_branch_name(&branch_name, branch)); return rb_str_new_utf8(branch_name); } static VALUE rb_git_branch__remote_name(VALUE rb_repo, const char *canonical_name) { git_repository *repo; git_buf remote_name = { NULL }; int error; VALUE result = Qnil; Data_Get_Struct(rb_repo, git_repository, repo); if ((error = git_branch_remote_name(&remote_name, repo, canonical_name)) == GIT_OK) result = rb_enc_str_new(remote_name.ptr, remote_name.size, rb_utf8_encoding()); git_buf_free(&remote_name); rugged_exception_check(error); return result; } /* * call-seq: * branch.remote_name -> string * * Get the name of the remote the branch belongs to. * * If +branch+ is a remote branch, the name of the remote it belongs to is * returned. If +branch+ is a tracking branch, the name of the remote * of the tracked branch is returned. * * Otherwise, +nil+ is returned. */ static VALUE rb_git_branch_remote_name(VALUE self) { git_reference *branch, *remote_ref; int error = 0; Data_Get_Struct(self, git_reference, branch); if (git_reference_is_remote(branch)) { remote_ref = branch; } else { error = git_branch_upstream(&remote_ref, branch); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); } return rb_git_branch__remote_name( rugged_owner(self), git_reference_name(remote_ref)); } /* * call-seq: * branch.upstream -> branch * * Returns the remote tracking branch, or +nil+ if the branch is * remote or has no tracking branch. */ static VALUE rb_git_branch_upstream(VALUE self) { git_reference *branch, *upstream_branch; int error; Data_Get_Struct(self, git_reference, branch); if (git_reference_is_remote(branch)) return Qnil; error = git_branch_upstream(&upstream_branch, branch); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_branch_new(rugged_owner(self), upstream_branch); } /* * call-seq: * branch.upstream = branch * * Set the upstream configuration for a given local branch. * * Takes a local or remote Rugged::Branch instance or a Rugged::Reference * pointing to a branch. */ static VALUE rb_git_branch_set_upstream(VALUE self, VALUE rb_branch) { git_reference *branch, *target_branch; const char *target_branch_name; Data_Get_Struct(self, git_reference, branch); if (!NIL_P(rb_branch)) { if (!rb_obj_is_kind_of(rb_branch, rb_cRuggedReference)) rb_raise(rb_eTypeError, "Expecting a Rugged::Reference instance"); Data_Get_Struct(rb_branch, git_reference, target_branch); rugged_exception_check( git_branch_name(&target_branch_name, target_branch) ); } else { target_branch_name = NULL; } rugged_exception_check( git_branch_set_upstream(branch, target_branch_name) ); return rb_branch; } void Init_rugged_branch(void) { rb_cRuggedBranch = rb_define_class_under(rb_mRugged, "Branch", rb_cRuggedReference); rb_define_method(rb_cRuggedBranch, "head?", rb_git_branch_head_p, 0); rb_define_method(rb_cRuggedBranch, "name", rb_git_branch_name, 0); rb_define_method(rb_cRuggedBranch, "remote_name", rb_git_branch_remote_name, 0); rb_define_method(rb_cRuggedBranch, "upstream", rb_git_branch_upstream, 0); rb_define_method(rb_cRuggedBranch, "upstream=", rb_git_branch_set_upstream, 1); } rugged-0.26.0/ext/rugged/rugged_backend.c0000644000175000017500000000057213147033070020351 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_cRuggedBackend; void Init_rugged_backend(void) { rb_cRuggedBackend = rb_define_class_under(rb_mRugged, "Backend", rb_cObject); } rugged-0.26.0/ext/rugged/rugged_repo.c0000644000175000017500000022300513147033070017725 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" #include #include #include #include extern VALUE rb_mRugged; extern VALUE rb_eRuggedError; extern VALUE rb_cRuggedIndex; extern VALUE rb_cRuggedConfig; extern VALUE rb_cRuggedBackend; extern VALUE rb_cRuggedRemote; extern VALUE rb_cRuggedCommit; extern VALUE rb_cRuggedTag; extern VALUE rb_cRuggedTree; extern VALUE rb_cRuggedReference; extern VALUE rb_cRuggedBackend; extern VALUE rb_cRuggedCredPlaintext; extern VALUE rb_cRuggedCredSshKey; extern VALUE rb_cRuggedCredDefault; VALUE rb_cRuggedRepo; VALUE rb_cRuggedOdbObject; static ID id_call; /* * call-seq: * odb_obj.oid -> hex_oid * * Return the Object ID (a 40 character SHA1 hash) for this raw * object. * * odb_obj.oid #=> "d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f" */ static VALUE rb_git_odbobj_oid(VALUE self) { git_odb_object *obj; Data_Get_Struct(self, git_odb_object, obj); return rugged_create_oid(git_odb_object_id(obj)); } /* * call-seq: * odb_obj.data -> buffer * * Return an ASCII buffer with the raw bytes that form the Git object. * * odb_obj.data #=> "tree 87ebee8367f9cc5ac04858b3bd5610ca74f04df9\n" * #=> "parent 68d041ee999cb07c6496fbdd4f384095de6ca9e1\n" * #=> "author Vicent Martí 1326863045 -0800\n" * #=> ... */ static VALUE rb_git_odbobj_data(VALUE self) { git_odb_object *obj; Data_Get_Struct(self, git_odb_object, obj); return rb_str_new(git_odb_object_data(obj), git_odb_object_size(obj)); } /* * call-seq: * odb_obj.size -> size * * Return the size in bytes of the Git object after decompression. This is * also the size of the +obj.data+ buffer. * * odb_obj.size #=> 231 */ static VALUE rb_git_odbobj_size(VALUE self) { git_odb_object *obj; Data_Get_Struct(self, git_odb_object, obj); return INT2FIX(git_odb_object_size(obj)); } /* * call-seq: * odb_obj.type -> Symbol * * Return a Ruby symbol representing the basic Git type of this object. * Possible values are +:tree+, +:blob+, +:commit+ and +:tag+. * * odb_obj.type #=> :tag */ static VALUE rb_git_odbobj_type(VALUE self) { git_odb_object *obj; Data_Get_Struct(self, git_odb_object, obj); return rugged_otype_new(git_odb_object_type(obj)); } void rb_git__odbobj_free(void *obj) { git_odb_object_free((git_odb_object *)obj); } VALUE rugged_raw_read(git_repository *repo, const git_oid *oid) { git_odb *odb; git_odb_object *obj; int error; error = git_repository_odb(&odb, repo); rugged_exception_check(error); error = git_odb_read(&obj, odb, oid); git_odb_free(odb); rugged_exception_check(error); return Data_Wrap_Struct(rb_cRuggedOdbObject, NULL, rb_git__odbobj_free, obj); } void rb_git_repo__free(git_repository *repo) { git_repository_free(repo); } VALUE rugged_repo_new(VALUE klass, git_repository *repo) { VALUE rb_repo = Data_Wrap_Struct(klass, NULL, &rb_git_repo__free, repo); #ifdef HAVE_RUBY_ENCODING_H /* TODO: set this properly */ rb_iv_set(rb_repo, "@encoding", rb_enc_from_encoding(rb_filesystem_encoding())); #endif rb_iv_set(rb_repo, "@config", Qnil); rb_iv_set(rb_repo, "@index", Qnil); return rb_repo; } static void load_alternates(git_repository *repo, VALUE rb_alternates) { git_odb *odb = NULL; int i, error; if (NIL_P(rb_alternates)) return; Check_Type(rb_alternates, T_ARRAY); if (RARRAY_LEN(rb_alternates) == 0) return; for (i = 0; i < RARRAY_LEN(rb_alternates); ++i) Check_Type(rb_ary_entry(rb_alternates, i), T_STRING); error = git_repository_odb(&odb, repo); rugged_exception_check(error); for (i = 0; !error && i < RARRAY_LEN(rb_alternates); ++i) { VALUE alt = rb_ary_entry(rb_alternates, i); error = git_odb_add_disk_alternate(odb, StringValueCStr(alt)); } git_odb_free(odb); rugged_exception_check(error); } static void rugged_repo_new_with_backend(git_repository **repo, VALUE rb_path, VALUE rb_backend) { char *path; git_odb *odb = NULL; git_odb_backend *odb_backend = NULL; git_refdb *refdb = NULL; git_refdb_backend *refdb_backend = NULL; git_reference *head = NULL; rugged_backend *backend; int error = 0; Check_Type(rb_path, T_STRING); path = StringValueCStr(rb_path); if (rb_obj_is_kind_of(rb_backend, rb_cRuggedBackend) == Qfalse) { rb_raise(rb_eRuggedError, "Backend must be an instance of Rugged::Backend"); } Data_Get_Struct(rb_backend, rugged_backend, backend); error = git_odb_new(&odb); if (error) goto cleanup; error = backend->odb_backend(&odb_backend, backend, path); if (error) goto cleanup; error = git_odb_add_backend(odb, odb_backend, 1); if (error) { assert(odb_backend->free); odb_backend->free(odb_backend); goto cleanup; } error = git_repository_wrap_odb(repo, odb); if (error) goto cleanup; error = git_refdb_new(&refdb, *repo); if (error) goto cleanup; error = backend->refdb_backend(&refdb_backend, backend, path); if (error) { assert(refdb_backend->free); refdb_backend->free(refdb_backend); goto cleanup; } error = git_refdb_set_backend(refdb, refdb_backend); if (error) goto cleanup; git_repository_set_refdb(*repo, refdb); error = git_reference_lookup(&head, *repo, "HEAD"); if (error == GIT_ENOTFOUND) { giterr_clear(); error = git_reference_symbolic_create(&head, *repo, "HEAD", "refs/heads/master", 0, NULL); } if (!error) { git_reference_free(head); return; } cleanup: git_repository_free(*repo); git_odb_free(odb); git_refdb_free(refdb); rugged_exception_check(error); } /* * call-seq: * Repository.bare(path[, alternates]) -> repository OR * Repository.bare(path[, options]) -> repository * * Open a bare Git repository at +path+ and return a +Repository+ * object representing it. * * This is faster than Rugged::Repository.new, as it won't attempt to perform * any +.git+ directory discovery, won't try to load the config options to * determine whether the repository is bare and won't try to load the workdir. * * Optionally, you can pass a list of alternate object folders or an options Hash. * * Rugged::Repository.bare(path, ['./other/repo/.git/objects']) * Rugged::Repository.bare(path, opts) * * The following options can be passed in the +options+ Hash: * * :backend :: * A Rugged::Backend instance * :alternates :: * A list of alternate object folders. * Rugged::Repository.bare(path, :alternates => ['./other/repo/.git/objects']) */ static VALUE rb_git_repo_open_bare(int argc, VALUE *argv, VALUE klass) { git_repository *repo = NULL; int error = 0; VALUE rb_path, rb_options, rb_alternates = 0; rb_scan_args(argc, argv, "11", &rb_path, &rb_options); if (!NIL_P(rb_options) && TYPE(rb_options) == T_ARRAY) rb_alternates = rb_options; if (!NIL_P(rb_options) && TYPE(rb_options) == T_HASH) { /* Check for `:backend` */ VALUE rb_backend = rb_hash_aref(rb_options, CSTR2SYM("backend")); if (!NIL_P(rb_backend)) { rugged_repo_new_with_backend(&repo, rb_path, rb_backend); } /* Check for `:alternates` */ rb_alternates = rb_hash_aref(rb_options, CSTR2SYM("alternates")); } if (!repo) { Check_Type(rb_path, T_STRING); error = git_repository_open_bare(&repo, StringValueCStr(rb_path)); rugged_exception_check(error); } if (rb_alternates) { load_alternates(repo, rb_alternates); } return rugged_repo_new(klass, repo); } /* * call-seq: * Repository.new(path, options = {}) -> repository * * Open a Git repository in the given +path+ and return a +Repository+ object * representing it. An exception will be thrown if +path+ doesn't point to a * valid repository. If you need to create a repository from scratch, use * Rugged::Repository.init_at instead. * * The +path+ must point to either the actual folder (+.git+) of a Git repository, * or to the directorly that contains the +.git+ folder. * * See also Rugged::Repository.discover and Rugged::Repository.bare. * * The following options can be passed in the +options+ Hash: * * :alternates :: * A list of alternate object folders. * * Examples: * * Rugged::Repository.new('test/.git') #=> # * Rugged::Repository.new(path, :alternates => ['./other/repo/.git/objects']) */ static VALUE rb_git_repo_new(int argc, VALUE *argv, VALUE klass) { git_repository *repo; int error = 0; VALUE rb_path, rb_options; rb_scan_args(argc, argv, "10:", &rb_path, &rb_options); Check_Type(rb_path, T_STRING); error = git_repository_open(&repo, StringValueCStr(rb_path)); rugged_exception_check(error); if (!NIL_P(rb_options)) { /* Check for `:alternates` */ load_alternates(repo, rb_hash_aref(rb_options, CSTR2SYM("alternates"))); } return rugged_repo_new(klass, repo); } /* * call-seq: * Repository.init_at(path, is_bare = false, opts = {}) -> repository * * Initialize a Git repository in +path+. This implies creating all the * necessary files on the FS, or re-initializing an already existing * repository if the files have already been created. * * The +is_bare+ (optional, defaults to false) attribute specifies whether * the Repository should be created on disk as bare or not. * Bare repositories have no working directory and are created in the root * of +path+. Non-bare repositories are created in a +.git+ folder and * use +path+ as working directory. * * The following options can be passed in the +options+ Hash: * * :backend :: * A Rugged::Backend instance * * * Rugged::Repository.init_at('repository', :bare) #=> # */ static VALUE rb_git_repo_init_at(int argc, VALUE *argv, VALUE klass) { git_repository *repo = NULL; VALUE rb_path, rb_is_bare, rb_options; int error; rb_scan_args(argc, argv, "11:", &rb_path, &rb_is_bare, &rb_options); Check_Type(rb_path, T_STRING); if (!NIL_P(rb_options)) { /* Check for `:backend` */ VALUE rb_backend = rb_hash_aref(rb_options, CSTR2SYM("backend")); if (rb_backend && !NIL_P(rb_backend)) { rugged_repo_new_with_backend(&repo, rb_path, rb_backend); } } if(!repo) { error = git_repository_init(&repo, StringValueCStr(rb_path), RTEST(rb_is_bare)); rugged_exception_check(error); } return rugged_repo_new(klass, repo); } static void parse_clone_options(git_clone_options *ret, VALUE rb_options, struct rugged_remote_cb_payload *remote_payload) { VALUE val; if (NIL_P(rb_options)) return; val = rb_hash_aref(rb_options, CSTR2SYM("bare")); if (RTEST(val)) ret->bare = 1; val = rb_hash_aref(rb_options, CSTR2SYM("checkout_branch")); if (!NIL_P(val)) { Check_Type(val, T_STRING); ret->checkout_branch = StringValueCStr(val); } rugged_remote_init_callbacks_and_payload_from_options(rb_options, &ret->fetch_opts.callbacks, remote_payload); } /* * call-seq: * Repository.clone_at(url, local_path[, options]) -> repository * * Clone a repository from +url+ to +local_path+. * * The following options can be passed in the +options+ Hash: * * :bare :: * If +true+, the clone will be created as a bare repository. * Defaults to +false+. * * :checkout_branch :: * The name of a branch to checkout. Defaults to the remote's +HEAD+. * * :remote :: * The name to give to the "origin" remote. Defaults to "origin". * * :ignore_cert_errors :: * If set to +true+, errors while validating the remote's host certificate will be ignored. * * :credentials :: * The credentials to use for the clone operation. Can be either an instance of one * of the Rugged::Credentials types, or a proc returning one of the former. * The proc will be called with the +url+, the +username+ from the url (if applicable) and * a list of applicable credential types. * * :progress :: * A callback that will be executed with the textual progress received from the remote. * This is the text send over the progress side-band (ie. the "counting objects" output). * * :transfer_progress :: * A callback that will be executed to report clone progress information. It will be passed * the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+, * +total_deltas+, +indexed_deltas+, and +received_bytes+. * * :update_tips :: * A callback that will be executed each time a reference was updated locally. It will be * passed the +refname+, +old_oid+ and +new_oid+. * * Example: * * Repository.clone_at("https://github.com/libgit2/rugged.git", "./some/dir", { * transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes| * # ... * } * }) */ static VALUE rb_git_repo_clone_at(int argc, VALUE *argv, VALUE klass) { VALUE url, local_path, rb_options_hash; git_clone_options options = GIT_CLONE_OPTIONS_INIT; struct rugged_remote_cb_payload remote_payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 }; git_repository *repo; int error; rb_scan_args(argc, argv, "21", &url, &local_path, &rb_options_hash); Check_Type(url, T_STRING); Check_Type(local_path, T_STRING); parse_clone_options(&options, rb_options_hash, &remote_payload); error = git_clone(&repo, StringValueCStr(url), StringValueCStr(local_path), &options); if (RTEST(remote_payload.exception)) rb_jump_tag(remote_payload.exception); rugged_exception_check(error); return rugged_repo_new(klass, repo); } #define RB_GIT_REPO_OWNED_GET(_klass, _object) \ VALUE rb_data = rb_iv_get(self, "@" #_object); \ if (NIL_P(rb_data)) { \ git_repository *repo; \ git_##_object *data; \ int error; \ Data_Get_Struct(self, git_repository, repo); \ error = git_repository_##_object(&data, repo); \ rugged_exception_check(error); \ rb_data = rugged_##_object##_new(_klass, self, data); \ rb_iv_set(self, "@" #_object, rb_data); \ } \ return rb_data; \ #define RB_GIT_REPO_OWNED_SET(_klass, _object) \ VALUE rb_old_data; \ git_repository *repo; \ git_##_object *data; \ if (!rb_obj_is_kind_of(rb_data, _klass))\ rb_raise(rb_eTypeError, \ "The given object is not a Rugged::" #_object); \ if (!NIL_P(rugged_owner(rb_data))) \ rb_raise(rb_eRuntimeError, \ "The given object is already owned by another repository"); \ Data_Get_Struct(self, git_repository, repo); \ Data_Get_Struct(rb_data, git_##_object, data); \ git_repository_set_##_object(repo, data); \ rb_old_data = rb_iv_get(self, "@" #_object); \ if (!NIL_P(rb_old_data)) rugged_set_owner(rb_old_data, Qnil); \ rugged_set_owner(rb_data, self); \ rb_iv_set(self, "@" #_object, rb_data); \ return Qnil; \ /* * call-seq: * repo.index = idx * * Set the index for this +Repository+. +idx+ must be a instance of * Rugged::Index. This index will be used internally by all * operations that use the Git index on +repo+. * * Note that it's not necessary to set the +index+ for any repository; * by default repositories are loaded with the index file that can be * located on the +.git+ folder in the filesystem. */ static VALUE rb_git_repo_set_index(VALUE self, VALUE rb_data) { RB_GIT_REPO_OWNED_SET(rb_cRuggedIndex, index); } /* * call-seq: * repo.index -> idx * * Return the default index for this repository. */ static VALUE rb_git_repo_get_index(VALUE self) { RB_GIT_REPO_OWNED_GET(rb_cRuggedIndex, index); } /* * call-seq: * repo.config = cfg * * Set the configuration file for this +Repository+. +cfg+ must be a instance of * Rugged::Config. This config file will be used internally by all * operations that need to lookup configuration settings on +repo+. * * Note that it's not necessary to set the +config+ for any repository; * by default repositories are loaded with their relevant config files * on the filesystem, and the corresponding global and system files if * they can be found. */ static VALUE rb_git_repo_set_config(VALUE self, VALUE rb_data) { RB_GIT_REPO_OWNED_SET(rb_cRuggedConfig, config); } /* * call-seq: * repo.config -> cfg * * Return a Rugged::Config object representing this repository's config. */ static VALUE rb_git_repo_get_config(VALUE self) { RB_GIT_REPO_OWNED_GET(rb_cRuggedConfig, config); } /* * call-seq: * repo.ident = ident * * Set the identity to be used for writing reflogs. * * +ident+ can be either +nil+ or a Hash containing +name+ and/or +email+ entries. */ static VALUE rb_git_repo_set_ident(VALUE self, VALUE rb_ident) { VALUE rb_val; git_repository *repo; const char *name = NULL, *email = NULL; Data_Get_Struct(self, git_repository, repo); if (!NIL_P(rb_ident)) { Check_Type(rb_ident, T_HASH); if (!NIL_P(rb_val = rb_hash_aref(rb_ident, CSTR2SYM("name")))) { Check_Type(rb_val, T_STRING); name = StringValueCStr(rb_val); } if (!NIL_P(rb_val = rb_hash_aref(rb_ident, CSTR2SYM("email")))) { Check_Type(rb_val, T_STRING); email = StringValueCStr(rb_val); } } rugged_exception_check( git_repository_set_ident(repo, name, email) ); return Qnil; } /* * call-seq: * repo.ident -> ident * * Return a Hash containing the identity that is used to write reflogs. * * +ident+ is a Hash containing +name+ and/or +email+ entries, or `nil`. */ static VALUE rb_git_repo_get_ident(VALUE self) { VALUE rb_ident = rb_hash_new(); git_repository *repo; const char *name = NULL, *email = NULL; Data_Get_Struct(self, git_repository, repo); rugged_exception_check( git_repository_ident(&name, &email, repo) ); if (name) { rb_hash_aset(rb_ident, CSTR2SYM("name"), rb_str_new_utf8(name)); } if (email) { rb_hash_aset(rb_ident, CSTR2SYM("email"), rb_str_new_utf8(email)); } return rb_ident; } /* * call-seq: * repo.merge_base(oid1, oid2, ...) * repo.merge_base(ref1, ref2, ...) * repo.merge_base(commit1, commit2, ...) * * Find a merge base, given two or more commits or oids. * Returns nil if a merge base is not found. */ static VALUE rb_git_repo_merge_base(VALUE self, VALUE rb_args) { int error = GIT_OK, i; git_repository *repo; git_oid base, *input_array = xmalloc(sizeof(git_oid) * RARRAY_LEN(rb_args)); int len = (int)RARRAY_LEN(rb_args); if (len < 2) rb_raise(rb_eArgError, "wrong number of arguments (%d for 2+)", len); Data_Get_Struct(self, git_repository, repo); for (i = 0; !error && i < len; ++i) { error = rugged_oid_get(&input_array[i], repo, rb_ary_entry(rb_args, i)); } if (error) { xfree(input_array); rugged_exception_check(error); } error = git_merge_base_many(&base, repo, len, input_array); xfree(input_array); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_create_oid(&base); } /* * call-seq: * repo.merge_bases(oid1, oid2, ...) -> Array * repo.merge_bases(ref1, ref2, ...) -> Array * repo.merge_bases(commit1, commit2, ...) -> Array * * Find all merge bases, given two or more commits or oids. * Returns an empty array if no merge bases are found. */ static VALUE rb_git_repo_merge_bases(VALUE self, VALUE rb_args) { int error = GIT_OK; size_t i, len = (size_t)RARRAY_LEN(rb_args); git_repository *repo; git_oidarray bases = {NULL, 0}; git_oid *input_array; VALUE rb_bases; if (len < 2) rb_raise(rb_eArgError, "wrong number of arguments (%ld for 2+)", RARRAY_LEN(rb_args)); Data_Get_Struct(self, git_repository, repo); input_array = xmalloc(sizeof(git_oid) * len); for (i = 0; !error && i < len; ++i) { error = rugged_oid_get(&input_array[i], repo, rb_ary_entry(rb_args, i)); } if (error) { xfree(input_array); rugged_exception_check(error); } error = git_merge_bases_many(&bases, repo, len, input_array); xfree(input_array); if (error != GIT_ENOTFOUND) rugged_exception_check(error); rb_bases = rb_ary_new2(bases.count); for (i = 0; i < bases.count; ++i) { rb_ary_push(rb_bases, rugged_create_oid(&bases.ids[i])); } git_oidarray_free(&bases); return rb_bases; } /* * call-seq: * repo.merge_analysis(their_commit) -> Array * * Analyzes the given commit and determines the opportunities for merging * it into the repository's HEAD. Returns an Array containing a combination * of the following symbols: * * :normal :: * A "normal" merge is possible, both HEAD and the given commit have * diverged from their common ancestor. The divergent commits must be * merged. * * :up_to_date :: * The given commit is reachable from HEAD, meaning HEAD is up-to-date * and no merge needs to be performed. * * :fastforward :: * The given commit is a fast-forward from HEAD and no merge needs to be * performed. HEAD can simply be set to the given commit. * * :unborn :: * The HEAD of the current repository is "unborn" and does not point to * a valid commit. No merge can be performed, but the caller may wish * to simply set HEAD to the given commit. */ static VALUE rb_git_repo_merge_analysis(int argc, VALUE *argv, VALUE self) { int error; git_repository *repo; git_commit *their_commit; git_annotated_commit *annotated_commit; git_merge_analysis_t analysis; git_merge_preference_t preference; VALUE rb_their_commit, result; rb_scan_args(argc, argv, "10", &rb_their_commit); Data_Get_Struct(self, git_repository, repo); if (TYPE(rb_their_commit) == T_STRING) { rb_their_commit = rugged_object_rev_parse(self, rb_their_commit, 1); } if (!rb_obj_is_kind_of(rb_their_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } Data_Get_Struct(rb_their_commit, git_commit, their_commit); error = git_annotated_commit_lookup(&annotated_commit, repo, git_commit_id(their_commit)); rugged_exception_check(error); error = git_merge_analysis(&analysis, &preference, repo, /* hack as we currently only do one commit */ (const git_annotated_commit **) &annotated_commit, 1); git_annotated_commit_free(annotated_commit); rugged_exception_check(error); result = rb_ary_new(); if (analysis & GIT_MERGE_ANALYSIS_NORMAL) rb_ary_push(result, CSTR2SYM("normal")); if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) rb_ary_push(result, CSTR2SYM("up_to_date")); if (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD) rb_ary_push(result, CSTR2SYM("fastforward")); if (analysis & GIT_MERGE_ANALYSIS_UNBORN) rb_ary_push(result, CSTR2SYM("unborn")); return result; } /* * call-seq: * repo.revert_commit(revert_commit, our_commit, options = {}) -> index * * Reverts the given commit against the given "our" commit, producing an * index that reflects the result of the revert. */ static VALUE rb_git_repo_revert_commit(int argc, VALUE *argv, VALUE self) { VALUE rb_revert_commit, rb_our_commit, rb_options; git_commit *revert_commit, *our_commit; git_index *index; git_repository *repo; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; unsigned int mainline = 0; int error; rb_scan_args(argc, argv, "20:", &rb_revert_commit, &rb_our_commit, &rb_options); if (TYPE(rb_revert_commit) == T_STRING) rb_revert_commit = rugged_object_rev_parse(self, rb_revert_commit, 1); if (TYPE(rb_our_commit) == T_STRING) rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1); if (!rb_obj_is_kind_of(rb_revert_commit, rb_cRuggedCommit) || !rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } if (!NIL_P(rb_options)) { VALUE rb_mainline; Check_Type(rb_options, T_HASH); rugged_parse_merge_options(&opts, rb_options); rb_mainline = rb_hash_aref(rb_options, CSTR2SYM("mainline")); if (!NIL_P(rb_mainline)) { Check_Type(rb_mainline, T_FIXNUM); mainline = FIX2UINT(rb_mainline); } } Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_revert_commit, git_commit, revert_commit); Data_Get_Struct(rb_our_commit, git_commit, our_commit); error = git_revert_commit(&index, repo, revert_commit, our_commit, mainline, &opts); if (error == GIT_EMERGECONFLICT) return Qnil; rugged_exception_check(error); return rugged_index_new(rb_cRuggedIndex, self, index); } /* * call-seq: * repo.merge_commits(our_commit, their_commit, options = {}) -> index * * Merges the two given commits, returning a Rugged::Index that reflects * the result of the merge. * * +our_commit+ and +their_commit+ can either be Rugged::Commit objects, * or OIDs resolving to the former. */ static VALUE rb_git_repo_merge_commits(int argc, VALUE *argv, VALUE self) { VALUE rb_our_commit, rb_their_commit, rb_options; git_commit *our_commit, *their_commit; git_index *index; git_repository *repo; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; int error; rb_scan_args(argc, argv, "20:", &rb_our_commit, &rb_their_commit, &rb_options); if (TYPE(rb_our_commit) == T_STRING) { rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1); } if (!rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } if (TYPE(rb_their_commit) == T_STRING) { rb_their_commit = rugged_object_rev_parse(self, rb_their_commit, 1); } if (!rb_obj_is_kind_of(rb_their_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } if (!NIL_P(rb_options)) { Check_Type(rb_options, T_HASH); rugged_parse_merge_options(&opts, rb_options); } Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_our_commit, git_commit, our_commit); Data_Get_Struct(rb_their_commit, git_commit, their_commit); error = git_merge_commits(&index, repo, our_commit, their_commit, &opts); if (error == GIT_EMERGECONFLICT) return Qnil; rugged_exception_check(error); return rugged_index_new(rb_cRuggedIndex, self, index); } /* * call-seq: * repo.include?(oid) -> true or false * repo.exists?(oid) -> true or false * * Return whether an object with the given SHA1 OID (represented as * a hex string of at least 7 characters) exists in the repository. * * repo.include?("d8786bfc97485e8d7b19b21fb88c8ef1f199fc3f") #=> true * repo.include?("d8786bfc") #=> true */ static VALUE rb_git_repo_exists(VALUE self, VALUE hex) { git_repository *repo; git_odb *odb; git_oid oid; int error; Data_Get_Struct(self, git_repository, repo); Check_Type(hex, T_STRING); error = git_oid_fromstrn(&oid, RSTRING_PTR(hex), RSTRING_LEN(hex)); rugged_exception_check(error); error = git_repository_odb(&odb, repo); rugged_exception_check(error); error = git_odb_exists_prefix(NULL, odb, &oid, RSTRING_LEN(hex)); git_odb_free(odb); if (error == 0 || error == GIT_EAMBIGUOUS) return Qtrue; return Qfalse; } /* * call-seq: * repo.read(oid) -> str * * Read and return the raw data of the object identified by the given +oid+. */ static VALUE rb_git_repo_read(VALUE self, VALUE hex) { git_repository *repo; git_oid oid; int error; Data_Get_Struct(self, git_repository, repo); Check_Type(hex, T_STRING); error = git_oid_fromstr(&oid, StringValueCStr(hex)); rugged_exception_check(error); return rugged_raw_read(repo, &oid); } /* * call-seq: * repo.read_header(oid) -> hash * * Read and return the header information in +repo+'s ODB * for the object identified by the given +oid+. * * Returns a Hash object with the following key/value pairs: * * :type :: * A Symbol denoting the object's type. Possible values are: * +:tree+, +:blob+, +:commit+ or +:tag+. * :len :: * A Number representing the object's length, in bytes. */ static VALUE rb_git_repo_read_header(VALUE self, VALUE hex) { git_repository *repo; git_oid oid; git_odb *odb; git_otype type; size_t len; VALUE rb_hash; int error; Data_Get_Struct(self, git_repository, repo); Check_Type(hex, T_STRING); error = git_oid_fromstr(&oid, StringValueCStr(hex)); rugged_exception_check(error); error = git_repository_odb(&odb, repo); rugged_exception_check(error); error = git_odb_read_header(&len, &type, odb, &oid); git_odb_free(odb); rugged_exception_check(error); rb_hash = rb_hash_new(); rb_hash_aset(rb_hash, CSTR2SYM("type"), CSTR2SYM(git_object_type2string(type))); rb_hash_aset(rb_hash, CSTR2SYM("len"), INT2FIX(len)); return rb_hash; } /** * call-seq: * repo.expand_oids([oid..], object_type = :any) -> hash * repo.expand_oids([oid..], object_type = [type..]) -> hash * * Expand a list of short oids to their full value, assuming they exist * in the repository. If `object_type` is passed and is an array, it must * be the same length as the OIDs array. If it's a single type name, all * OIDs will be expected to resolve to that object type. OIDs that don't * match the expected object types will not be expanded. * * Returns a hash of `{ short_oid => full_oid }` for the short OIDs which * exist in the repository and match the expected object type. Missing OIDs * will not appear in the resulting hash. */ static VALUE rb_git_repo_expand_oids(int argc, VALUE *argv, VALUE self) { VALUE rb_result, rb_oids, rb_expected_type; git_repository *repo; git_odb *odb; git_odb_expand_id *expand; long i, expand_count; int error; Data_Get_Struct(self, git_repository, repo); rb_scan_args(argc, argv, "11", &rb_oids, &rb_expected_type); Check_Type(rb_oids, T_ARRAY); expand_count = RARRAY_LEN(rb_oids); expand = alloca(expand_count * sizeof(git_odb_expand_id)); for (i = 0; i < expand_count; ++i) { VALUE rb_hex = rb_ary_entry(rb_oids, i); Check_Type(rb_hex, T_STRING); rugged_exception_check( git_oid_fromstrn(&expand[i].id, RSTRING_PTR(rb_hex), RSTRING_LEN(rb_hex)) ); expand[i].length = RSTRING_LEN(rb_hex); } if (TYPE(rb_expected_type) == T_ARRAY) { if (RARRAY_LEN(rb_expected_type) != expand_count) rb_raise(rb_eRuntimeError, "the `object_type` array must be the same length as the `oids` array"); for (i = 0; i < expand_count; ++i) { VALUE rb_type = rb_ary_entry(rb_expected_type, i); expand[i].type = rugged_otype_get(rb_type); } } else { git_otype expected_type = GIT_OBJ_ANY; if (!NIL_P(rb_expected_type)) expected_type = rugged_otype_get(rb_expected_type); for (i = 0; i < expand_count; ++i) expand[i].type = expected_type; } error = git_repository_odb(&odb, repo); rugged_exception_check(error); error = git_odb_expand_ids(odb, expand, (size_t)expand_count); git_odb_free(odb); rugged_exception_check(error); rb_result = rb_hash_new(); for (i = 0; i < expand_count; ++i) { if (expand[i].length) { rb_hash_aset(rb_result, rb_ary_entry(rb_oids, i), rugged_create_oid(&expand[i].id)); } } return rb_result; } /* * call-seq: * repo.descendant_of?(commit, ancestor) -> true or false * * +commit+ and +ancestor+ must be String commit OIDs or instances of Rugged::Commit. * * Returns true if +commit+ is a descendant of +ancestor+, or false if not. */ static VALUE rb_git_repo_descendant_of(VALUE self, VALUE rb_commit, VALUE rb_ancestor) { int result; int error; git_repository *repo; git_oid commit, ancestor; Data_Get_Struct(self, git_repository, repo); error = rugged_oid_get(&commit, repo, rb_commit); rugged_exception_check(error); error = rugged_oid_get(&ancestor, repo, rb_ancestor); rugged_exception_check(error); result = git_graph_descendant_of(repo, &commit, &ancestor); rugged_exception_check(result); return result ? Qtrue : Qfalse; } /* * call-seq: * Repository.hash_data(str, type) -> oid * * Hash the contents of +str+ as raw bytes (ignoring any encoding * information) and adding the relevant header corresponding to +type+, * and return a hex string representing the result from the hash. * * Repository.hash_data('hello world', :commit) #=> "de5ba987198bcf2518885f0fc1350e5172cded78" * * Repository.hash_data('hello_world', :tag) #=> "9d09060c850defbc7711d08b57def0d14e742f4e" */ static VALUE rb_git_repo_hash(VALUE self, VALUE rb_buffer, VALUE rb_type) { int error; git_oid oid; Check_Type(rb_buffer, T_STRING); error = git_odb_hash(&oid, RSTRING_PTR(rb_buffer), RSTRING_LEN(rb_buffer), rugged_otype_get(rb_type) ); rugged_exception_check(error); return rugged_create_oid(&oid); } /* * call-seq: * Repository.hash_file(path, type) -> oid * * Hash the contents of the file pointed at by +path+, assuming * that it'd be stored in the ODB with the given +type+, and return * a hex string representing the SHA1 OID resulting from the hash. * * Repository.hash_file('foo.txt', :commit) #=> "de5ba987198bcf2518885f0fc1350e5172cded78" * * Repository.hash_file('foo.txt', :tag) #=> "9d09060c850defbc7711d08b57def0d14e742f4e" */ static VALUE rb_git_repo_hashfile(VALUE self, VALUE rb_path, VALUE rb_type) { int error; git_oid oid; Check_Type(rb_path, T_STRING); error = git_odb_hashfile(&oid, StringValueCStr(rb_path), rugged_otype_get(rb_type) ); rugged_exception_check(error); return rugged_create_oid(&oid); } /* * call-seq: * repo.write(buffer, type) -> oid * * Write the data contained in the +buffer+ string as a raw object of the * given +type+ into the repository's object database. * * +type+ can be either +:tag+, +:commit+, +:tree+ or +:blob+. * * Returns the newly created object's oid. */ static VALUE rb_git_repo_write(VALUE self, VALUE rb_buffer, VALUE rub_type) { git_repository *repo; git_odb_stream *stream; git_odb *odb; git_oid oid; int error; git_otype type; Data_Get_Struct(self, git_repository, repo); Check_Type(rb_buffer, T_STRING); error = git_repository_odb(&odb, repo); rugged_exception_check(error); type = rugged_otype_get(rub_type); error = git_odb_open_wstream(&stream, odb, RSTRING_LEN(rb_buffer), type); git_odb_free(odb); rugged_exception_check(error); error = git_odb_stream_write(stream, RSTRING_PTR(rb_buffer), RSTRING_LEN(rb_buffer)); if (!error) error = git_odb_stream_finalize_write(&oid, stream); git_odb_stream_free(stream); rugged_exception_check(error); return rugged_create_oid(&oid); } #define RB_GIT_REPO_GETTER(method) \ git_repository *repo; \ int error; \ Data_Get_Struct(self, git_repository, repo); \ error = git_repository_##method(repo); \ rugged_exception_check(error); \ return error ? Qtrue : Qfalse; \ /* * call-seq: * repo.bare? -> true or false * * Return whether a repository is bare or not. A bare repository has no * working directory. */ static VALUE rb_git_repo_is_bare(VALUE self) { RB_GIT_REPO_GETTER(is_bare); } /* * call-seq: * repo.shallow? -> true or false * * Return whether a repository is a shallow clone or not. A shallow clone has * a truncated history and can not be cloned or fetched from, nor can be * pushed from nor into it. */ static VALUE rb_git_repo_is_shallow(VALUE self) { RB_GIT_REPO_GETTER(is_shallow); } /* * call-seq: * repo.empty? -> true or false * * Return whether a repository is empty or not. An empty repository has just * been initialized and has no commits yet. */ static VALUE rb_git_repo_is_empty(VALUE self) { RB_GIT_REPO_GETTER(is_empty); } /* * call-seq: * repo.head_detached? -> true or false * * Return whether the +HEAD+ of a repository is detached or not. */ static VALUE rb_git_repo_head_detached(VALUE self) { RB_GIT_REPO_GETTER(head_detached); } /* * call-seq: * repo.head_unborn? -> true or false * * Return whether the current branch is unborn (+HEAD+ points to a * non-existent branch). */ static VALUE rb_git_repo_head_unborn(VALUE self) { RB_GIT_REPO_GETTER(head_unborn); } /* * call-seq: * repo.head = str * * Make the repository's +HEAD+ point to the specified reference. */ static VALUE rb_git_repo_set_head(VALUE self, VALUE rb_head) { git_repository *repo; int error; Data_Get_Struct(self, git_repository, repo); Check_Type(rb_head, T_STRING); error = git_repository_set_head(repo, StringValueCStr(rb_head)); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.head -> ref * * Retrieve and resolve the reference pointed at by the repository's +HEAD+. * * Returns +nil+ if +HEAD+ is missing. */ static VALUE rb_git_repo_get_head(VALUE self) { git_repository *repo; git_reference *head; int error; Data_Get_Struct(self, git_repository, repo); error = git_repository_head(&head, repo); if (error == GIT_ENOTFOUND) return Qnil; else rugged_exception_check(error); return rugged_ref_new(rb_cRuggedReference, self, head); } /* * call-seq: * repo.path -> path * * Return the full, normalized path to this repository. For non-bare repositories, * this is the path of the actual +.git+ folder, not the working directory. * * repo.path #=> "/home/foo/workthing/.git" */ static VALUE rb_git_repo_path(VALUE self) { git_repository *repo; const char *path; Data_Get_Struct(self, git_repository, repo); path = git_repository_path(repo); return path ? rb_str_new_utf8(path) : Qnil; } /* * call-seq: * repo.workdir -> path or nil * * Return the working directory for this repository, or +nil+ if * the repository is bare. * * repo1.bare? #=> false * repo1.workdir #=> "/home/foo/workthing/" * * repo2.bare? #=> true * repo2.workdir #=> nil */ static VALUE rb_git_repo_workdir(VALUE self) { git_repository *repo; const char *workdir; Data_Get_Struct(self, git_repository, repo); workdir = git_repository_workdir(repo); return workdir ? rb_str_new_utf8(workdir) : Qnil; } /* * call-seq: * repo.workdir = path * * Sets the working directory of +repo+ to +path+. All internal * operations on +repo+ that affect the working directory will * instead use +path+. * * The +workdir+ can be set on bare repositories to temporarily * turn them into normal repositories. * * repo.bare? #=> true * repo.workdir = "/tmp/workdir" * repo.bare? #=> false * repo.checkout */ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) { git_repository *repo; Data_Get_Struct(self, git_repository, repo); Check_Type(rb_workdir, T_STRING); rugged_exception_check( git_repository_set_workdir(repo, StringValueCStr(rb_workdir), 0) ); return Qnil; } /* * call-seq: * Repository.discover(path = nil, across_fs = true) -> repository * * Traverse +path+ upwards until a Git working directory with a +.git+ * folder has been found, open it and return it as a +Repository+ * object. * * If +path+ is +nil+, the current working directory will be used as * a starting point. * * If +across_fs+ is +true+, the traversal won't stop when reaching * a different device than the one that contained +path+ (only applies * to UNIX-based OSses). */ static VALUE rb_git_repo_discover(int argc, VALUE *argv, VALUE klass) { git_repository *repo; VALUE rb_path, rb_across_fs; git_buf repository_path = { NULL }; int error, across_fs = 0; rb_scan_args(argc, argv, "02", &rb_path, &rb_across_fs); if (NIL_P(rb_path)) { VALUE rb_dir = rb_const_get(rb_cObject, rb_intern("Dir")); rb_path = rb_funcall(rb_dir, rb_intern("pwd"), 0); } if (!NIL_P(rb_across_fs)) { across_fs = rugged_parse_bool(rb_across_fs); } Check_Type(rb_path, T_STRING); error = git_repository_discover( &repository_path, StringValueCStr(rb_path), across_fs, NULL ); rugged_exception_check(error); error = git_repository_open(&repo, repository_path.ptr); git_buf_free(&repository_path); rugged_exception_check(error); return rugged_repo_new(klass, repo); } static VALUE flags_to_rb(unsigned int flags) { VALUE rb_flags = rb_ary_new(); if (flags & GIT_STATUS_INDEX_NEW) rb_ary_push(rb_flags, CSTR2SYM("index_new")); if (flags & GIT_STATUS_INDEX_MODIFIED) rb_ary_push(rb_flags, CSTR2SYM("index_modified")); if (flags & GIT_STATUS_INDEX_DELETED) rb_ary_push(rb_flags, CSTR2SYM("index_deleted")); if (flags & GIT_STATUS_WT_NEW) rb_ary_push(rb_flags, CSTR2SYM("worktree_new")); if (flags & GIT_STATUS_WT_MODIFIED) rb_ary_push(rb_flags, CSTR2SYM("worktree_modified")); if (flags & GIT_STATUS_WT_DELETED) rb_ary_push(rb_flags, CSTR2SYM("worktree_deleted")); if (flags & GIT_STATUS_IGNORED) rb_ary_push(rb_flags, CSTR2SYM("ignored")); return rb_flags; } static int rugged__status_cb(const char *path, unsigned int flags, void *payload) { rb_funcall((VALUE)payload, rb_intern("call"), 2, rb_str_new_utf8(path), flags_to_rb(flags) ); return GIT_OK; } /* * call-seq: * repo.status { |file, status_data| block } * repo.status(path) -> status_data * * Returns the status for one or more files in the working directory * of the repository. This is equivalent to the +git status+ command. * * The returned +status_data+ is always an array containing one or more * status flags as Ruby symbols. Possible flags are: * * - +:index_new+: the file is new in the index * - +:index_modified+: the file has been modified in the index * - +:index_deleted+: the file has been deleted from the index * - +:worktree_new+: the file is new in the working directory * - +:worktree_modified+: the file has been modified in the working directory * - +:worktree_deleted+: the file has been deleted from the working directory * * If a +block+ is given, status information will be gathered for every * single file on the working dir. The +block+ will be called with the * status data for each file. * * repo.status { |file, status_data| puts "#{file} has status: #{status_data.inspect}" } * * results in, for example: * * src/diff.c has status: [:index_new, :worktree_new] * README has status: [:worktree_modified] * * If a +path+ is given instead, the function will return the +status_data+ for * the file pointed to by path, or raise an exception if the path doesn't exist. * * +path+ must be relative to the repository's working directory. * * repo.status('src/diff.c') #=> [:index_new, :worktree_new] */ static VALUE rb_git_repo_status(int argc, VALUE *argv, VALUE self) { int error; VALUE rb_path; git_repository *repo; Data_Get_Struct(self, git_repository, repo); if (rb_scan_args(argc, argv, "01", &rb_path) == 1) { unsigned int flags; Check_Type(rb_path, T_STRING); error = git_status_file(&flags, repo, StringValueCStr(rb_path)); rugged_exception_check(error); return flags_to_rb(flags); } if (!rb_block_given_p()) rb_raise(rb_eRuntimeError, "A block was expected for iterating through " "the repository contents."); error = git_status_foreach( repo, &rugged__status_cb, (void *)rb_block_proc() ); rugged_exception_check(error); return Qnil; } static int rugged__each_id_cb(const git_oid *id, void *payload) { int *exception = (int *)payload; rb_protect(rb_yield, rugged_create_oid(id), exception); return *exception ? GIT_ERROR : GIT_OK; } /* * call-seq: * repo.each_id { |id| block } * repo.each_id -> Enumerator * * Call the given +block+ once with every object ID found in +repo+ * and all its alternates. Object IDs are passed as 40-character * strings. */ static VALUE rb_git_repo_each_id(VALUE self) { git_repository *repo; git_odb *odb; int error, exception = 0; if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_id")); Data_Get_Struct(self, git_repository, repo); error = git_repository_odb(&odb, repo); rugged_exception_check(error); error = git_odb_foreach(odb, &rugged__each_id_cb, &exception); git_odb_free(odb); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } static int parse_reset_type(VALUE rb_reset_type) { ID id_reset_type; Check_Type(rb_reset_type, T_SYMBOL); id_reset_type = SYM2ID(rb_reset_type); if (id_reset_type == rb_intern("soft")) { return GIT_RESET_SOFT; } else if (id_reset_type == rb_intern("mixed")) { return GIT_RESET_MIXED; } else if (id_reset_type == rb_intern("hard")) { return GIT_RESET_HARD; } else { rb_raise(rb_eArgError, "Invalid reset type. Expected `:soft`, `:mixed` or `:hard`"); } } /* * call-seq: * repo.reset(target, reset_type) -> nil * * Sets the current head to the specified commit oid and optionally * resets the index and working tree to match. * - +target+: Rugged::Commit, Rugged::Tag or rev that resolves to a commit or tag object * - +reset_type+: +:soft+, +:mixed+ or +:hard+ * * [:soft] * the head will be moved to the commit. * [:mixed] * will trigger a +:soft+ reset, plus the index will be replaced * with the content of the commit tree. * [:hard] * will trigger a +:mixed+ reset and the working directory will be * replaced with the content of the index. (Untracked and ignored files * will be left alone) * * Examples: * * repo.reset('origin/master', :hard) #=> nil */ static VALUE rb_git_repo_reset(VALUE self, VALUE rb_target, VALUE rb_reset_type) { git_repository *repo; int reset_type; git_object *target = NULL; int error; Data_Get_Struct(self, git_repository, repo); reset_type = parse_reset_type(rb_reset_type); target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY); error = git_reset(repo, target, reset_type, NULL); git_object_free(target); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.reset_path(pathspecs, target=nil) -> nil * * Updates entries in the index from the +target+ commit tree, matching * the given +pathspecs+. * * Passing a nil +target+ will result in removing * entries in the index matching the provided pathspecs. * * - +pathspecs+: list of pathspecs to operate on (+String+ or +Array+ of +String+ objects) * - +target+(optional): Rugged::Commit, Rugged::Tag or rev that resolves to a commit or tag object. * * Examples: * reset_path(File.join('subdir','file.txt'), '441034f860c1d5d90e4188d11ae0d325176869a8') #=> nil */ static VALUE rb_git_repo_reset_path(int argc, VALUE *argv, VALUE self) { git_repository *repo; git_object *target = NULL; git_strarray pathspecs; VALUE rb_target, rb_paths; int error = 0; pathspecs.strings = NULL; pathspecs.count = 0; Data_Get_Struct(self, git_repository, repo); rb_scan_args(argc, argv, "11", &rb_paths, &rb_target); rugged_rb_ary_to_strarray(rb_paths, &pathspecs); if (!NIL_P(rb_target)) target = rugged_object_get(repo, rb_target, GIT_OBJ_ANY); error = git_reset_default(repo, target, &pathspecs); xfree(pathspecs.strings); git_object_free(target); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.close -> nil * * Frees all the resources used by this repository immediately. The repository can * still be used after this call. Resources will be opened as necessary. * * It is not required to call this method explicitly. Repositories are closed * automatically before garbage collection */ static VALUE rb_git_repo_close(VALUE self) { git_repository *repo; Data_Get_Struct(self, git_repository, repo); git_repository__cleanup(repo); return Qnil; } /* * call-seq: * repo.namespace = new_namespace * * Sets the active namespace for the repository. * If set to nil, no namespace will be active. */ static VALUE rb_git_repo_set_namespace(VALUE self, VALUE rb_namespace) { git_repository *repo; int error; Data_Get_Struct(self, git_repository, repo); if (!NIL_P(rb_namespace)) { Check_Type(rb_namespace, T_STRING); error = git_repository_set_namespace(repo, StringValueCStr(rb_namespace)); } else { error = git_repository_set_namespace(repo, NULL); } rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.namespace -> str * * Returns the active namespace for the repository. */ static VALUE rb_git_repo_get_namespace(VALUE self) { git_repository *repo; const char *namespace; Data_Get_Struct(self, git_repository, repo); namespace = git_repository_get_namespace(repo); return namespace ? rb_str_new_utf8(namespace) : Qnil; } /* * call-seq: * repo.ahead_behind(local, upstream) -> Array * * Returns a 2 element Array containing the number of commits * that the upstream object is ahead and behind the local object. * * +local+ and +upstream+ can either be strings containing SHA1 OIDs or * Rugged::Object instances. */ static VALUE rb_git_repo_ahead_behind(VALUE self, VALUE rb_local, VALUE rb_upstream) { git_repository *repo; int error; git_oid local, upstream; size_t ahead, behind; VALUE rb_result; Data_Get_Struct(self, git_repository, repo); error = rugged_oid_get(&local, repo, rb_local); rugged_exception_check(error); error = rugged_oid_get(&upstream, repo, rb_upstream); rugged_exception_check(error); error = git_graph_ahead_behind(&ahead, &behind, repo, &local, &upstream); rugged_exception_check(error); rb_result = rb_ary_new2(2); rb_ary_push(rb_result, INT2FIX((int) ahead)); rb_ary_push(rb_result, INT2FIX((int) behind)); return rb_result; } /* * call-seq: * repo.default_signature -> signature or nil * * Returns a +Hash+ with the default user +signature+ or +nil+. * * Looks up the +user.name+ and +user.email+ from the configuration and * uses the current time as the timestamp, and creates a new signature * based on that information. It will return +nil+ if either the * +user.name+ or +user.email+ are not set. * * Returns a +Hash+: * - +:name+: the +user.name+ config value * - +:email+: the +user.email+ config value * - +:time+: the current time as a +Time+ instance */ static VALUE rb_git_repo_default_signature(VALUE self) { int error; git_repository *repo; git_signature *signature; VALUE rb_signature; Data_Get_Struct(self, git_repository, repo); error = git_signature_default(&signature, repo); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); rb_signature = rugged_signature_new(signature, NULL); git_signature_free(signature); return rb_signature; } void rugged__checkout_progress_cb( const char *path, size_t completed_steps, size_t total_steps, void *data ) { struct rugged_cb_payload *payload = data; VALUE args = rb_ary_new2(4); rb_ary_push(args, payload->rb_data); rb_ary_push(args, path == NULL ? Qnil : rb_str_new2(path)); rb_ary_push(args, INT2FIX(completed_steps)); rb_ary_push(args, INT2FIX(total_steps)); rb_protect(rugged__block_yield_splat, args, &payload->exception); } static int rugged__checkout_notify_cb( git_checkout_notify_t why, const char *path, const git_diff_file *baseline, const git_diff_file *target, const git_diff_file *workdir, void *data ) { struct rugged_cb_payload *payload = data; VALUE args = rb_ary_new2(5); rb_ary_push(args, payload->rb_data); switch (why) { case GIT_CHECKOUT_NOTIFY_CONFLICT: rb_ary_push(args, CSTR2SYM("conflict")); break; case GIT_CHECKOUT_NOTIFY_DIRTY: rb_ary_push(args, CSTR2SYM("dirty")); break; case GIT_CHECKOUT_NOTIFY_UPDATED: rb_ary_push(args, CSTR2SYM("updated")); break; case GIT_CHECKOUT_NOTIFY_UNTRACKED: rb_ary_push(args, CSTR2SYM("untracked")); break; case GIT_CHECKOUT_NOTIFY_IGNORED: rb_ary_push(args, CSTR2SYM("ignored")); break; default: rb_ary_push(args, CSTR2SYM("unknown")); } rb_ary_push(args, rb_git_delta_file_fromC(baseline)); rb_ary_push(args, rb_git_delta_file_fromC(target)); rb_ary_push(args, rb_git_delta_file_fromC(workdir)); rb_protect(rugged__block_yield_splat, args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } /** * The caller has to free the returned git_checkout_options paths strings array. */ void rugged_parse_checkout_options(git_checkout_options *opts, VALUE rb_options) { VALUE rb_value; if (NIL_P(rb_options)) return; Check_Type(rb_options, T_HASH); rb_value = rb_hash_aref(rb_options, CSTR2SYM("progress")); if (!NIL_P(rb_value)) { struct rugged_cb_payload *payload = xmalloc(sizeof(struct rugged_cb_payload)); payload->rb_data = rb_value; payload->exception = 0; opts->progress_payload = payload; opts->progress_cb = &rugged__checkout_progress_cb; } rb_value = rb_hash_aref(rb_options, CSTR2SYM("notify")); if (!NIL_P(rb_value)) { struct rugged_cb_payload *payload = xmalloc(sizeof(struct rugged_cb_payload)); payload->rb_data = rb_value; payload->exception = 0; opts->notify_payload = payload; opts->notify_cb = &rugged__checkout_notify_cb; } if (!NIL_P(rb_value = rb_hash_aref(rb_options, CSTR2SYM("strategy")))) { int i; rb_value = rb_ary_to_ary(rb_value); for (i = 0; i < RARRAY_LEN(rb_value); ++i) { VALUE rb_strategy = rb_ary_entry(rb_value, i); if (rb_strategy == CSTR2SYM("safe")) { opts->checkout_strategy |= GIT_CHECKOUT_SAFE; } else if (rb_strategy == CSTR2SYM("force")) { opts->checkout_strategy |= GIT_CHECKOUT_FORCE; } else if (rb_strategy == CSTR2SYM("recreate_missing")) { opts->checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING; } else if (rb_strategy == CSTR2SYM("allow_conflicts")) { opts->checkout_strategy |= GIT_CHECKOUT_ALLOW_CONFLICTS; } else if (rb_strategy == CSTR2SYM("remove_untracked")) { opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; } else if (rb_strategy == CSTR2SYM("remove_ignored")) { opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED; } else if (rb_strategy == CSTR2SYM("update_only")) { opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY; } else if (rb_strategy == CSTR2SYM("dont_update_index")) { opts->checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX; } else if (rb_strategy == CSTR2SYM("no_refresh")) { opts->checkout_strategy |= GIT_CHECKOUT_NO_REFRESH; } else if (rb_strategy == CSTR2SYM("disable_pathspec_match")) { opts->checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH; } else if (rb_strategy == CSTR2SYM("skip_locked_directories")) { opts->checkout_strategy |= GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES; } else if (rb_strategy == CSTR2SYM("skip_unmerged")) { opts->checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED; } else if (rb_strategy == CSTR2SYM("use_ours")) { opts->checkout_strategy |= GIT_CHECKOUT_USE_OURS; } else if (rb_strategy == CSTR2SYM("use_theirs")) { opts->checkout_strategy |= GIT_CHECKOUT_USE_THEIRS; } else if (rb_strategy == CSTR2SYM("update_submodules")) { opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES; } else if (rb_strategy == CSTR2SYM("update_submodules_if_changed")) { opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED; } else if (rb_strategy != CSTR2SYM("none")) { rb_raise(rb_eArgError, "Unknown checkout strategy"); } } } if (!NIL_P(rb_value = rb_hash_aref(rb_options, CSTR2SYM("notify_flags")))) { int i; rb_value = rb_ary_to_ary(rb_value); for (i = 0; i < RARRAY_LEN(rb_value); ++i) { VALUE rb_notify_flag = rb_ary_entry(rb_value, i); if (rb_notify_flag == CSTR2SYM("conflict")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_CONFLICT; } else if (rb_notify_flag == CSTR2SYM("dirty")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_DIRTY; } else if (rb_notify_flag == CSTR2SYM("updated")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_UPDATED; } else if (rb_notify_flag == CSTR2SYM("untracked")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_UNTRACKED; } else if (rb_notify_flag == CSTR2SYM("ignored")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_IGNORED; } else if (rb_notify_flag == CSTR2SYM("all")) { opts->notify_flags |= GIT_CHECKOUT_NOTIFY_ALL; } else if (rb_notify_flag != CSTR2SYM("none")) { rb_raise(rb_eArgError, "Unknown checkout notify flag"); } } } opts->disable_filters = RTEST(rb_hash_aref(rb_options, CSTR2SYM("disable_filters"))); rb_value = rb_hash_aref(rb_options, CSTR2SYM("dir_mode")); if (!NIL_P(rb_value)) { opts->dir_mode = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("file_mode")); if (!NIL_P(rb_value)) { opts->file_mode = FIX2UINT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("file_open_flags")); if (!NIL_P(rb_value)) { opts->file_mode = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("target_directory")); if (!NIL_P(rb_value)) { opts->target_directory = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("baseline")); if (!NIL_P(rb_value)) { if (rb_obj_is_kind_of(rb_value, rb_cRuggedTree)) { Data_Get_Struct(rb_value, git_tree, opts->baseline); } else { rb_raise(rb_eTypeError, "Expected a Rugged::Tree."); } } rb_value = rb_hash_aref(rb_options, CSTR2SYM("paths")); rugged_rb_ary_to_strarray(rb_value, &opts->paths); } /** * call-seq: * repo.checkout_tree(treeish[, options]) * * Updates files in the index and working tree to match the content of the * tree pointed at by the +treeish+. * * The following options can be passed in the +options+ Hash: * * :progress :: * A callback that will be executed for checkout progress notifications. * Up to 3 parameters are passed on each execution: * * - The path to the last updated file (or +nil+ on the very first invocation). * - The number of completed checkout steps. * - The number of total checkout steps to be performed. * * :notify :: * A callback that will be executed for each checkout notification types specified * with +:notify_flags+. Up to 5 parameters are passed on each execution: * * - An array containing the +:notify_flags+ that caused the callback execution. * - The path of the current file. * - A hash describing the baseline blob (or +nil+ if it does not exist). * - A hash describing the target blob (or +nil+ if it does not exist). * - A hash describing the workdir blob (or +nil+ if it does not exist). * * :strategy :: * A single symbol or an array of symbols representing the strategies to use when * performing the checkout. Possible values are: * * :none :: * Perform a dry run (default). * * :safe :: * Allow safe updates that cannot overwrite uncommitted data. * * :recreate_missing :: * Allow checkout to recreate missing files. * * :force :: * Allow all updates to force working directory to look like index. * * :allow_conflicts :: * Allow checkout to make safe updates even if conflicts are found. * * :remove_untracked :: * Remove untracked files not in index (that are not ignored). * * :remove_ignored :: * Remove ignored files not in index. * * :update_only :: * Only update existing files, don't create new ones. * * :dont_update_index :: * Normally checkout updates index entries as it goes; this stops that. * * :no_refresh :: * Don't refresh index/config/etc before doing checkout. * * :disable_pathspec_match :: * Treat pathspec as simple list of exact match file paths. * * :skip_locked_directories :: * Ignore directories in use, they will be left empty. * * :skip_unmerged :: * Allow checkout to skip unmerged files (NOT IMPLEMENTED). * * :use_ours :: * For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED). * * :use_theirs :: * For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED). * * :update_submodules :: * Recursively checkout submodules with same options (NOT IMPLEMENTED). * * :update_submodules_if_changed :: * Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED). * * :disable_filters :: * If +true+, filters like CRLF line conversion will be disabled. * * :dir_mode :: * Mode for newly created directories. Default: +0755+. * * :file_mode :: * Mode for newly created files. Default: +0755+ or +0644+. * * :file_open_flags :: * Mode for opening files. Default: IO::CREAT | IO::TRUNC | IO::WRONLY. * * :notify_flags :: * A single symbol or an array of symbols representing the cases in which the +:notify+ * callback should be invoked. Possible values are: * * :none :: * Do not invoke the +:notify+ callback (default). * * :conflict :: * Invoke the callback for conflicting paths. * * :dirty :: * Invoke the callback for "dirty" files, i.e. those that do not need an update but * no longer match the baseline. * * :updated :: * Invoke the callback for any file that was changed. * * :untracked :: * Invoke the callback for untracked files. * * :ignored :: * Invoke the callback for ignored files. * * :all :: * Invoke the callback for all these cases. * * :paths :: * A glob string or an array of glob strings specifying which paths should be taken * into account for the checkout operation. +nil+ will match all files. * Default: +nil+. * * :baseline :: * A Rugged::Tree that represents the current, expected contents of the workdir. * Default: +HEAD+. * * :target_directory :: * A path to an alternative workdir directory in which the checkout should be performed. */ static VALUE rb_git_checkout_tree(int argc, VALUE *argv, VALUE self) { VALUE rb_treeish, rb_options; git_repository *repo; git_object *treeish; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct rugged_cb_payload *payload; int error, exception = 0; rb_scan_args(argc, argv, "10:", &rb_treeish, &rb_options); if (TYPE(rb_treeish) == T_STRING) { rb_treeish = rugged_object_rev_parse(self, rb_treeish, 1); } if (!rb_obj_is_kind_of(rb_treeish, rb_cRuggedCommit) && !rb_obj_is_kind_of(rb_treeish, rb_cRuggedTag) && !rb_obj_is_kind_of(rb_treeish, rb_cRuggedTree)) { rb_raise(rb_eTypeError, "Expected Rugged::Commit, Rugged::Tag or Rugged::Tree"); } Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_treeish, git_object, treeish); rugged_parse_checkout_options(&opts, rb_options); error = git_checkout_tree(repo, treeish, &opts); xfree(opts.paths.strings); if ((payload = opts.notify_payload) != NULL) { exception = payload->exception; xfree(opts.notify_payload); } if ((payload = opts.progress_payload) != NULL) { exception = payload->exception; xfree(opts.progress_payload); } if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /** * call-seq: repo.checkout_index(index[,options]) -> nil * * Updates files in the index and the working tree to match the content of the * commit pointed at by +index+. * * See Repository#checkout_tree for a list of supported +options+. */ static VALUE rb_git_checkout_index(int argc, VALUE *argv, VALUE self) { VALUE rb_index, rb_options; git_repository *repo; git_index *index; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct rugged_cb_payload *payload; int error, exception = 0; rb_scan_args(argc, argv, "10:", &rb_index, &rb_options); if (!rb_obj_is_kind_of(rb_index, rb_cRuggedIndex)) rb_raise(rb_eTypeError, "Expected Rugged::Index"); Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_index, git_index, index); rugged_parse_checkout_options(&opts, rb_options); error = git_checkout_index(repo, index, &opts); xfree(opts.paths.strings); if ((payload = opts.notify_payload) != NULL) { exception = payload->exception; xfree(opts.notify_payload); } if ((payload = opts.progress_payload) != NULL) { exception = payload->exception; xfree(opts.progress_payload); } if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /** * call-seq: repo.checkout_head([options]) -> nil * * Updates files in the index and the working tree to match the content of the * commit pointed at by +HEAD+. * * See Repository#checkout_tree for a list of supported +options+. */ static VALUE rb_git_checkout_head(int argc, VALUE *argv, VALUE self) { VALUE rb_options; git_repository *repo; git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct rugged_cb_payload *payload; int error, exception = 0; rb_scan_args(argc, argv, "00:", &rb_options); Data_Get_Struct(self, git_repository, repo); rugged_parse_checkout_options(&opts, rb_options); error = git_checkout_head(repo, &opts); xfree(opts.paths.strings); if ((payload = opts.notify_payload) != NULL) { exception = payload->exception; xfree(opts.notify_payload); } if ((payload = opts.progress_payload) != NULL) { exception = payload->exception; xfree(opts.progress_payload); } if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.path_ignored?(path) -> true or false * * Return whether a path is ignored or not. */ static VALUE rb_git_repo_is_path_ignored(VALUE self, VALUE rb_path) { git_repository *repo; const char *path; int error; int ignored; Data_Get_Struct(self, git_repository, repo); path = StringValueCStr(rb_path); error = git_ignore_path_is_ignored(&ignored, repo, path); rugged_exception_check(error); return ignored ? Qtrue : Qfalse; } static void rugged_parse_cherrypick_options(git_cherrypick_options *opts, VALUE rb_options) { VALUE rb_value; if (NIL_P(rb_options)) return; Check_Type(rb_options, T_HASH); rb_value = rb_hash_aref(rb_options, CSTR2SYM("mainline")); if (!NIL_P(rb_value)) { opts->mainline = FIX2UINT(rb_value); } } static VALUE rugged_create_attr(const char *attr) { switch (git_attr_value(attr)) { case GIT_ATTR_TRUE_T: return Qtrue; case GIT_ATTR_FALSE_T: return Qfalse; case GIT_ATTR_VALUE_T: return rb_str_new2(attr); case GIT_ATTR_UNSPECIFIED_T: default: return Qnil; } } static int foreach_attr_hash(const char *name, const char *value, void *payload) { VALUE rb_hash = (VALUE)payload; rb_hash_aset(rb_hash, rb_str_new2(name), rugged_create_attr(value)); return 0; } static VALUE rb_git_repo_attributes(int argc, VALUE *argv, VALUE self) { VALUE rb_path, rb_names, rb_options; git_repository *repo; int error, options = 0; rb_scan_args(argc, argv, "12", &rb_path, &rb_names, &rb_options); Data_Get_Struct(self, git_repository, repo); Check_Type(rb_path, T_STRING); if (!NIL_P(rb_options)) { Check_Type(rb_options, T_FIXNUM); options = FIX2INT(rb_options); } switch (TYPE(rb_names)) { case T_ARRAY: { VALUE rb_result; const char **values; const char **names; long i, num_attr = RARRAY_LEN(rb_names); if (num_attr > 32) rb_raise(rb_eRuntimeError, "Too many attributes requested"); values = alloca(num_attr * sizeof(const char *)); names = alloca(num_attr * sizeof(const char *)); for (i = 0; i < num_attr; ++i) { VALUE attr = rb_ary_entry(rb_names, i); Check_Type(attr, T_STRING); names[i] = StringValueCStr(attr); } error = git_attr_get_many( values, repo, options, StringValueCStr(rb_path), (size_t)num_attr, names); rugged_exception_check(error); rb_result = rb_hash_new(); for (i = 0; i < num_attr; ++i) { VALUE attr = rb_ary_entry(rb_names, i); rb_hash_aset(rb_result, attr, rugged_create_attr(values[i])); } return rb_result; } case T_STRING: { const char *value; error = git_attr_get( &value, repo, options, StringValueCStr(rb_path), StringValueCStr(rb_names)); rugged_exception_check(error); return rugged_create_attr(value); } case T_NIL: { VALUE rb_result = rb_hash_new(); error = git_attr_foreach( repo, options, StringValueCStr(rb_path), &foreach_attr_hash, (void *)rb_result); rugged_exception_check(error); return rb_result; } default: rb_raise(rb_eTypeError, "Invalid attribute name (expected String or Array)"); } } /* * call-seq: * repo.cherrypick(commit[, options]) -> nil * * Cherry-pick the given commit and update the index and working * directory accordingly. * * `commit` can be either a string containing a commit id or a * `Rugged::Commit` object. * * The following options can be passed in the +options+ Hash: * * :mainline :: * When cherry-picking a merge, you need to specify the parent number * (starting from 1) which should be considered the mainline. */ static VALUE rb_git_repo_cherrypick(int argc, VALUE *argv, VALUE self) { VALUE rb_options, rb_commit; git_repository *repo; git_commit *commit; git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; int error; rb_scan_args(argc, argv, "10:", &rb_commit, &rb_options); if (TYPE(rb_commit) == T_STRING) { rb_commit = rugged_object_rev_parse(self, rb_commit, 1); } if (!rb_obj_is_kind_of(rb_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_commit, git_commit, commit); rugged_parse_cherrypick_options(&opts, rb_options); error = git_cherrypick(repo, commit, &opts); rugged_exception_check(error); return Qnil; } /* * call-seq: * repo.cherrypick_commit(commit, our_commit, [mainline, options]) -> nil * * Cherry-pick the given commit on the given base in-memory and * return an index with the result. * * `commit` can be either a string containing a commit id or a * `Rugged::Commit` object. * * `our_commit` is the base commit, can be either a string containing * a commit id or a `Rugged::Commit` object. * * `mainline` when cherry-picking a merge, this is the parent number * (starting from 1) which should be considered the mainline. */ static VALUE rb_git_repo_cherrypick_commit(int argc, VALUE *argv, VALUE self) { VALUE rb_options, rb_commit, rb_our_commit, rb_mainline; git_repository *repo; git_commit *commit, *our_commit; git_merge_options opts = GIT_MERGE_OPTIONS_INIT; git_index *index; int error, mainline; rb_scan_args(argc, argv, "21:", &rb_commit, &rb_our_commit, &rb_mainline, &rb_options); if (TYPE(rb_commit) == T_STRING) { rb_commit = rugged_object_rev_parse(self, rb_commit, 1); } if (TYPE(rb_our_commit) == T_STRING) { rb_our_commit = rugged_object_rev_parse(self, rb_our_commit, 1); } if (!rb_obj_is_kind_of(rb_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } if (!rb_obj_is_kind_of(rb_our_commit, rb_cRuggedCommit)) { rb_raise(rb_eArgError, "Expected a Rugged::Commit."); } Data_Get_Struct(self, git_repository, repo); Data_Get_Struct(rb_commit, git_commit, commit); Data_Get_Struct(rb_our_commit, git_commit, our_commit); rugged_parse_merge_options(&opts, rb_options); mainline = NIL_P(rb_mainline) ? 0 : FIX2UINT(rb_mainline); error = git_cherrypick_commit(&index, repo, commit, our_commit, mainline, &opts); rugged_exception_check(error); return rugged_index_new(rb_cRuggedIndex, self, index); } void Init_rugged_repo(void) { id_call = rb_intern("call"); rb_cRuggedRepo = rb_define_class_under(rb_mRugged, "Repository", rb_cObject); rb_define_singleton_method(rb_cRuggedRepo, "new", rb_git_repo_new, -1); rb_define_singleton_method(rb_cRuggedRepo, "bare", rb_git_repo_open_bare, -1); rb_define_singleton_method(rb_cRuggedRepo, "hash_data", rb_git_repo_hash, 2); rb_define_singleton_method(rb_cRuggedRepo, "hash_file", rb_git_repo_hashfile, 2); rb_define_singleton_method(rb_cRuggedRepo, "init_at", rb_git_repo_init_at, -1); rb_define_singleton_method(rb_cRuggedRepo, "discover", rb_git_repo_discover, -1); rb_define_singleton_method(rb_cRuggedRepo, "clone_at", rb_git_repo_clone_at, -1); rb_define_method(rb_cRuggedRepo, "close", rb_git_repo_close, 0); rb_define_method(rb_cRuggedRepo, "exists?", rb_git_repo_exists, 1); rb_define_method(rb_cRuggedRepo, "include?", rb_git_repo_exists, 1); rb_define_method(rb_cRuggedRepo, "expand_oids", rb_git_repo_expand_oids, -1); rb_define_method(rb_cRuggedRepo, "descendant_of?", rb_git_repo_descendant_of, 2); rb_define_method(rb_cRuggedRepo, "read", rb_git_repo_read, 1); rb_define_method(rb_cRuggedRepo, "read_header", rb_git_repo_read_header, 1); rb_define_method(rb_cRuggedRepo, "write", rb_git_repo_write, 2); rb_define_method(rb_cRuggedRepo, "each_id", rb_git_repo_each_id, 0); rb_define_method(rb_cRuggedRepo, "path", rb_git_repo_path, 0); rb_define_method(rb_cRuggedRepo, "workdir", rb_git_repo_workdir, 0); rb_define_method(rb_cRuggedRepo, "workdir=", rb_git_repo_set_workdir, 1); rb_define_method(rb_cRuggedRepo, "status", rb_git_repo_status, -1); rb_define_method(rb_cRuggedRepo, "index", rb_git_repo_get_index, 0); rb_define_method(rb_cRuggedRepo, "index=", rb_git_repo_set_index, 1); rb_define_method(rb_cRuggedRepo, "config", rb_git_repo_get_config, 0); rb_define_method(rb_cRuggedRepo, "config=", rb_git_repo_set_config, 1); rb_define_method(rb_cRuggedRepo, "ident", rb_git_repo_get_ident, 0); rb_define_method(rb_cRuggedRepo, "ident=", rb_git_repo_set_ident, 1); rb_define_method(rb_cRuggedRepo, "bare?", rb_git_repo_is_bare, 0); rb_define_method(rb_cRuggedRepo, "shallow?", rb_git_repo_is_shallow, 0); rb_define_method(rb_cRuggedRepo, "empty?", rb_git_repo_is_empty, 0); rb_define_method(rb_cRuggedRepo, "head_detached?", rb_git_repo_head_detached, 0); rb_define_method(rb_cRuggedRepo, "head_unborn?", rb_git_repo_head_unborn, 0); rb_define_method(rb_cRuggedRepo, "head=", rb_git_repo_set_head, 1); rb_define_method(rb_cRuggedRepo, "head", rb_git_repo_get_head, 0); rb_define_method(rb_cRuggedRepo, "merge_base", rb_git_repo_merge_base, -2); rb_define_method(rb_cRuggedRepo, "merge_bases", rb_git_repo_merge_bases, -2); rb_define_method(rb_cRuggedRepo, "merge_analysis", rb_git_repo_merge_analysis, -1); rb_define_method(rb_cRuggedRepo, "merge_commits", rb_git_repo_merge_commits, -1); rb_define_method(rb_cRuggedRepo, "revert_commit", rb_git_repo_revert_commit, -1); rb_define_method(rb_cRuggedRepo, "path_ignored?", rb_git_repo_is_path_ignored, 1); rb_define_method(rb_cRuggedRepo, "reset", rb_git_repo_reset, 2); rb_define_method(rb_cRuggedRepo, "reset_path", rb_git_repo_reset_path, -1); rb_define_method(rb_cRuggedRepo, "namespace=", rb_git_repo_set_namespace, 1); rb_define_method(rb_cRuggedRepo, "namespace", rb_git_repo_get_namespace, 0); rb_define_method(rb_cRuggedRepo, "ahead_behind", rb_git_repo_ahead_behind, 2); rb_define_method(rb_cRuggedRepo, "default_signature", rb_git_repo_default_signature, 0); rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_checkout_tree, -1); rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_checkout_index, -1); rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_checkout_head, -1); rb_define_method(rb_cRuggedRepo, "cherrypick", rb_git_repo_cherrypick, -1); rb_define_method(rb_cRuggedRepo, "cherrypick_commit", rb_git_repo_cherrypick_commit, -1); rb_define_method(rb_cRuggedRepo, "fetch_attributes", rb_git_repo_attributes, -1); rb_cRuggedOdbObject = rb_define_class_under(rb_mRugged, "OdbObject", rb_cObject); rb_define_method(rb_cRuggedOdbObject, "data", rb_git_odbobj_data, 0); rb_define_method(rb_cRuggedOdbObject, "len", rb_git_odbobj_size, 0); rb_define_method(rb_cRuggedOdbObject, "type", rb_git_odbobj_type, 0); rb_define_method(rb_cRuggedOdbObject, "oid", rb_git_odbobj_oid, 0); } rugged-0.26.0/ext/rugged/rugged_diff_delta.c0000644000175000017500000000464413147033070021047 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_cRuggedDiff; VALUE rb_cRuggedDiffDelta; VALUE rb_git_delta_file_fromC(const git_diff_file *file) { VALUE rb_file; if (!file) return Qnil; rb_file = rb_hash_new(); rb_hash_aset(rb_file, CSTR2SYM("oid"), rugged_create_oid(&file->id)); rb_hash_aset(rb_file, CSTR2SYM("path"), file->path ? rb_str_new2(file->path) : Qnil); rb_hash_aset(rb_file, CSTR2SYM("size"), INT2FIX(file->size)); rb_hash_aset(rb_file, CSTR2SYM("flags"), UINT2NUM(file->flags)); rb_hash_aset(rb_file, CSTR2SYM("mode"), UINT2NUM(file->mode)); return rb_file; } static VALUE rb_git_delta_status_fromC(git_delta_t status) { switch(status) { case GIT_DELTA_UNMODIFIED: return CSTR2SYM("unmodified"); case GIT_DELTA_ADDED: return CSTR2SYM("added"); case GIT_DELTA_DELETED: return CSTR2SYM("deleted"); case GIT_DELTA_MODIFIED: return CSTR2SYM("modified"); case GIT_DELTA_RENAMED: return CSTR2SYM("renamed"); case GIT_DELTA_COPIED: return CSTR2SYM("copied"); case GIT_DELTA_IGNORED: return CSTR2SYM("ignored"); case GIT_DELTA_UNTRACKED: return CSTR2SYM("untracked"); case GIT_DELTA_TYPECHANGE: return CSTR2SYM("typechange"); default: return CSTR2SYM("unknown"); } } static VALUE rb_git_delta_status_char_fromC(git_delta_t status) { char status_char[2]; status_char[0] = git_diff_status_char(status); status_char[1] = '\0'; return CSTR2SYM(status_char); } VALUE rugged_diff_delta_new(VALUE owner, const git_diff_delta *delta) { VALUE rb_delta = rb_class_new_instance(0, NULL, rb_cRuggedDiffDelta); rugged_set_owner(rb_delta, owner); rb_iv_set(rb_delta, "@old_file", rb_git_delta_file_fromC(&delta->old_file)); rb_iv_set(rb_delta, "@new_file", rb_git_delta_file_fromC(&delta->new_file)); rb_iv_set(rb_delta, "@similarity", INT2FIX(delta->similarity)); rb_iv_set(rb_delta, "@status", rb_git_delta_status_fromC(delta->status)); rb_iv_set(rb_delta, "@status_char", rb_git_delta_status_char_fromC(delta->status)); rb_iv_set(rb_delta, "@binary", (!(delta->flags & GIT_DIFF_FLAG_NOT_BINARY) && (delta->flags & GIT_DIFF_FLAG_BINARY)) ? Qtrue : Qfalse ); return rb_delta; } void Init_rugged_diff_delta(void) { rb_cRuggedDiffDelta = rb_define_class_under(rb_cRuggedDiff, "Delta", rb_cObject); } rugged-0.26.0/ext/rugged/rugged_config.c0000644000175000017500000002302613147033070020226 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_cRuggedConfig; void rb_git_config__free(git_config *config) { git_config_free(config); } VALUE rugged_config_new(VALUE klass, VALUE owner, git_config *cfg) { VALUE rb_config = Data_Wrap_Struct(klass, NULL, &rb_git_config__free, cfg); rugged_set_owner(rb_config, owner); return rb_config; } /* * call-seq: * Config.new(path) -> new_config * * Open the file specified in +path+ as a +Rugged::Config+ file. * If +path+ cannot be found, or the file is an invalid Git config, * an exception will be raised. */ static VALUE rb_git_config_new(VALUE klass, VALUE rb_path) { git_config *config = NULL; if (TYPE(rb_path) == T_ARRAY) { int error, i; error = git_config_new(&config); rugged_exception_check(error); for (i = 0; i < RARRAY_LEN(rb_path) && !error; ++i) { VALUE f = rb_ary_entry(rb_path, i); Check_Type(f, T_STRING); error = git_config_add_file_ondisk(config, StringValueCStr(f), i + 1, 1); } if (error) { git_config_free(config); rugged_exception_check(error); } } else if (TYPE(rb_path) == T_STRING) { rugged_exception_check( git_config_open_ondisk(&config, StringValueCStr(rb_path)) ); } else { rb_raise(rb_eTypeError, "Expecting a filename or an array of filenames"); } return rugged_config_new(klass, Qnil, config); } /* * call-seq: * cfg.get(key) -> value * cfg[key] -> value * * Get the value for the given config +key+. Values are always * returned as +String+, or +nil+ if the given key doesn't exist * in the Config file. * * cfg['apply.whitespace'] #=> 'fix' * cfg['diff.renames'] #=> 'true' */ static VALUE rb_git_config_get(VALUE self, VALUE rb_key) { git_config *config; git_buf buf = { NULL }; int error; VALUE rb_result; Data_Get_Struct(self, git_config, config); Check_Type(rb_key, T_STRING); error = git_config_get_string_buf(&buf, config, StringValueCStr(rb_key)); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); rb_result = rb_str_new_utf8(buf.ptr); git_buf_free(&buf); return rb_result; } /* * call-seq: * cfg.store(key, value) * cfg[key] = value * * Store the given +value+ in the Config file, under the section * and name specified by +key+. Value can be any of the following * Ruby types: +String+, +true+, +false+ and +Fixnum+. * * The config file will be automatically stored to disk. * * cfg['apply.whitespace'] = 'fix' * cfg['diff.renames'] = true * cfg['gc.reflogexpre'] = 90 */ static VALUE rb_git_config_store(VALUE self, VALUE rb_key, VALUE rb_val) { git_config *config; const char *key; int error; Data_Get_Struct(self, git_config, config); Check_Type(rb_key, T_STRING); key = StringValueCStr(rb_key); switch (TYPE(rb_val)) { case T_STRING: error = git_config_set_string(config, key, StringValueCStr(rb_val)); break; case T_TRUE: case T_FALSE: error = git_config_set_bool(config, key, (rb_val == Qtrue)); break; case T_FIXNUM: error = git_config_set_int32(config, key, FIX2INT(rb_val)); break; default: rb_raise(rb_eTypeError, "Invalid value; config files can only store string, bool or int keys"); } rugged_exception_check(error); return Qnil; } /* * call-seq: * cfg.delete(key) -> true or false * * Delete the given +key+ from the config file. Return +true+ if * the deletion was successful, or +false+ if the key was not * found in the Config file. * * The config file is immediately updated on disk. */ static VALUE rb_git_config_delete(VALUE self, VALUE rb_key) { git_config *config; int error; Data_Get_Struct(self, git_config, config); Check_Type(rb_key, T_STRING); error = git_config_delete_entry(config, StringValueCStr(rb_key)); if (error == GIT_ENOTFOUND) return Qfalse; rugged_exception_check(error); return Qtrue; } static int cb_config__each_key(const git_config_entry *entry, void *opaque) { rb_funcall((VALUE)opaque, rb_intern("call"), 1, rb_str_new_utf8(entry->name)); return GIT_OK; } static int cb_config__each_pair(const git_config_entry *entry, void *opaque) { rb_funcall((VALUE)opaque, rb_intern("call"), 2, rb_str_new_utf8(entry->name), rb_str_new_utf8(entry->value) ); return GIT_OK; } static int cb_config__to_hash(const git_config_entry *entry, void *opaque) { rb_hash_aset((VALUE)opaque, rb_str_new_utf8(entry->name), rb_str_new_utf8(entry->value) ); return GIT_OK; } /* * call-seq: * cfg.each_key { |key| block } * cfg.each_key -> enumarator * * Call the given block once for each key in the config file. If no block * is given, an enumerator is returned. * * cfg.each_key do |key| * puts key * end */ static VALUE rb_git_config_each_key(VALUE self) { git_config *config; int error; Data_Get_Struct(self, git_config, config); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_key")); error = git_config_foreach(config, &cb_config__each_key, (void *)rb_block_proc()); rugged_exception_check(error); return Qnil; } /* * call-seq: * cfg.each_pair { |key, value| block } * cfg.each_pair -> enumerator * cfg.each { |key, value| block } * cfg.each -> enumerator * * Call the given block once for each key/value pair in the config file. * If no block is given, an enumerator is returned. * * cfg.each do |key, value| * puts "#{key} => #{value}" * end */ static VALUE rb_git_config_each_pair(VALUE self) { git_config *config; int error; Data_Get_Struct(self, git_config, config); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_pair")); error = git_config_foreach(config, &cb_config__each_pair, (void *)rb_block_proc()); rugged_exception_check(error); return Qnil; } /* * call-seq: * cfg.to_hash -> hash * * Returns the config file represented as a Ruby hash, where * each configuration entry appears as a key with its * corresponding value. * * cfg.to_hash #=> {"core.autolf" => "true", "core.bare" => "true"} */ static VALUE rb_git_config_to_hash(VALUE self) { git_config *config; int error; VALUE hash; Data_Get_Struct(self, git_config, config); hash = rb_hash_new(); error = git_config_foreach(config, &cb_config__to_hash, (void *)hash); rugged_exception_check(error); return hash; } /* * call-seq: * Config.global() -> new_config * Config.open_global() -> new_config * * Open the default global config file as a new +Rugged::Config+ object. * An exception will be raised if the global config file doesn't * exist. */ static VALUE rb_git_config_open_default(VALUE klass) { git_config *cfg; int error; error = git_config_open_default(&cfg); rugged_exception_check(error); return rugged_config_new(klass, Qnil, cfg); } /* * call-seq: * config.snapshot -> snapshot * * Create a snapshot of the configuration. * * Provides a consistent, read-only view of the configuration for * looking up complex values from a configuration. */ static VALUE rb_git_config_snapshot(VALUE self) { git_config *config, *snapshot; Data_Get_Struct(self, git_config, config); rugged_exception_check( git_config_snapshot(&snapshot, config) ); return rugged_config_new(rb_obj_class(self), Qnil, snapshot); } /* * call-seq: * config.transaction { |config| } * * Perform configuration changes in a transaction. * * Locks the configuration, executes the given block and stores * any changes that were made to the configuration. If the block * throws an exception, all changes are rolled back automatically. * * During the execution of the block, configuration changes don't * get stored to disk immediately, so reading from the configuration * will continue to return the values that were stored in the configuration * when the transaction was started. */ static VALUE rb_git_config_transaction(VALUE self) { git_config *config; git_transaction *tx; VALUE rb_result; int error = 0, exception = 0; Data_Get_Struct(self, git_config, config); git_config_lock(&tx, config); rb_result = rb_protect(rb_yield, self, &exception); if (!exception) error = git_transaction_commit(tx); git_transaction_free(tx); if (exception) rb_jump_tag(exception); else if (error) rugged_exception_check(error); return rb_result; } void Init_rugged_config(void) { /* * Config */ rb_cRuggedConfig = rb_define_class_under(rb_mRugged, "Config", rb_cObject); rb_define_singleton_method(rb_cRuggedConfig, "new", rb_git_config_new, 1); rb_define_singleton_method(rb_cRuggedConfig, "global", rb_git_config_open_default, 0); rb_define_singleton_method(rb_cRuggedConfig, "open_global", rb_git_config_open_default, 0); rb_define_method(rb_cRuggedConfig, "delete", rb_git_config_delete, 1); rb_define_method(rb_cRuggedConfig, "store", rb_git_config_store, 2); rb_define_method(rb_cRuggedConfig, "[]=", rb_git_config_store, 2); rb_define_method(rb_cRuggedConfig, "get", rb_git_config_get, 1); rb_define_method(rb_cRuggedConfig, "[]", rb_git_config_get, 1); rb_define_method(rb_cRuggedConfig, "each_key", rb_git_config_each_key, 0); rb_define_method(rb_cRuggedConfig, "each_pair", rb_git_config_each_pair, 0); rb_define_method(rb_cRuggedConfig, "each", rb_git_config_each_pair, 0); rb_define_method(rb_cRuggedConfig, "to_hash", rb_git_config_to_hash, 0); rb_define_method(rb_cRuggedConfig, "snapshot", rb_git_config_snapshot, 0); rb_define_method(rb_cRuggedConfig, "transaction", rb_git_config_transaction, 0); } rugged-0.26.0/ext/rugged/rugged_commit.c0000644000175000017500000005641613147033070020262 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" #include "git2/commit.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedObject; extern VALUE rb_cRuggedRepo; extern VALUE rb_cRuggedSignature; VALUE rb_cRuggedCommit; /* * call-seq: * commit.message -> msg * * Return the message of this commit. This includes the full body of the * message, with the short description, detailed description, and any * optional footers or signatures after it. * * In Ruby 1.9+, the returned string will be encoded with the encoding * specified in the +Encoding+ header of the commit, if available. * * commit.message #=> "add a lot of RDoc docs\n\nthis includes docs for commit and blob" */ static VALUE rb_git_commit_message_GET(VALUE self) { git_commit *commit; rb_encoding *encoding = rb_utf8_encoding(); const char *encoding_name; const char *message; Data_Get_Struct(self, git_commit, commit); message = git_commit_message(commit); encoding_name = git_commit_message_encoding(commit); if (encoding_name != NULL) encoding = rb_enc_find(encoding_name); return rb_enc_str_new(message, strlen(message), encoding); } /* * call-seq: * commit.summary -> summary * * Return the short summary message of this commit. * * In Ruby 1.9+, the returned string will be encoded with the encoding * specified in the +Encoding+ header of the commit, if available. * * commit.message #=> "add a lot of RDoc docs\n\nthis includes docs for commit and blob" * commit.summary #=> "add a lot of RDoc docs" */ static VALUE rb_git_commit_summary_GET(VALUE self) { git_commit *commit; rb_encoding *encoding = rb_utf8_encoding(); const char *encoding_name; const char *summary; Data_Get_Struct(self, git_commit, commit); summary = git_commit_summary(commit); encoding_name = git_commit_message_encoding(commit); if (encoding_name != NULL) encoding = rb_enc_find(encoding_name); return rb_enc_str_new(summary, strlen(summary), encoding); } /* * call-seq: * commit.committer -> signature * * Return the signature for the committer of this +commit+. The signature * is returned as a +Hash+ containing +:name+, +:email+ of the author * and +:time+ of the change. * * The committer of a commit is the person who actually applied the changes * of the commit; in most cases it's the same as the author. * * In Ruby 1.9+, the returned string will be encoded with the encoding * specified in the +Encoding+ header of the commit, if available. * * commit.committer #=> {:email=>"tanoku@gmail.com", :time=>Tue Jan 24 05:42:45 UTC 2012, :name=>"Vicent Mart\303\255"} */ static VALUE rb_git_commit_committer_GET(VALUE self) { git_commit *commit; Data_Get_Struct(self, git_commit, commit); return rugged_signature_new( git_commit_committer(commit), git_commit_message_encoding(commit)); } /* * call-seq: * commit.author -> signature * * Return the signature for the author of this +commit+. The signature * is returned as a +Hash+ containing +:name+, +:email+ of the author * and +:time+ of the change. * * The author of the commit is the person who intially created the changes. * * In Ruby 1.9+, the returned string will be encoded with the encoding * specified in the +Encoding+ header of the commit, if available. * * commit.author #=> {:email=>"tanoku@gmail.com", :time=>Tue Jan 24 05:42:45 UTC 2012, :name=>"Vicent Mart\303\255"} */ static VALUE rb_git_commit_author_GET(VALUE self) { git_commit *commit; Data_Get_Struct(self, git_commit, commit); return rugged_signature_new( git_commit_author(commit), git_commit_message_encoding(commit)); } /* * call-seq: * commit.epoch_time -> int * * Return the time when this commit was made effective. This is the same value * as the +:time+ attribute for +commit.committer+, but represented as an +Integer+ * value in seconds since the Epoch. * * commit.time #=> 1327383765 */ static VALUE rb_git_commit_epoch_time_GET(VALUE self) { git_commit *commit; Data_Get_Struct(self, git_commit, commit); return ULONG2NUM(git_commit_time(commit)); } /* * call-seq: * commit.tree -> tree * * Return the tree pointed at by this +commit+. The tree is * returned as a +Rugged::Tree+ object. * * commit.tree #=> # */ static VALUE rb_git_commit_tree_GET(VALUE self) { git_commit *commit; git_tree *tree; VALUE owner; int error; Data_Get_Struct(self, git_commit, commit); owner = rugged_owner(self); error = git_commit_tree(&tree, commit); rugged_exception_check(error); return rugged_object_new(owner, (git_object *)tree); } /* * call-seq: * commit.tree_id -> oid * * Return the tree oid pointed at by this +commit+. The tree is * returned as a String object. * * commit.tree_id #=> "f148106ca58764adc93ad4e2d6b1d168422b9796" */ static VALUE rb_git_commit_tree_id_GET(VALUE self) { git_commit *commit; const git_oid *tree_id; Data_Get_Struct(self, git_commit, commit); tree_id = git_commit_tree_id(commit); return rugged_create_oid(tree_id); } /* * call-seq: * commit.parents -> [commit, ...] * * Return the parent(s) of this commit as an array of +Rugged::Commit+ * objects. An array is always returned even when the commit has only * one or zero parents. * * commit.parents #=> => [#] * root.parents #=> [] */ static VALUE rb_git_commit_parents_GET(VALUE self) { git_commit *commit; git_commit *parent; unsigned int n, parent_count; VALUE ret_arr, owner; int error; Data_Get_Struct(self, git_commit, commit); owner = rugged_owner(self); parent_count = git_commit_parentcount(commit); ret_arr = rb_ary_new2((long)parent_count); for (n = 0; n < parent_count; n++) { error = git_commit_parent(&parent, commit, n); rugged_exception_check(error); rb_ary_push(ret_arr, rugged_object_new(owner, (git_object *)parent)); } return ret_arr; } /* * call-seq: * commit.parent_ids -> [oid, ...] * * Return the parent oid(s) of this commit as an array of oid String * objects. An array is always returned even when the commit has only * one or zero parents. * * commit.parent_ids #=> => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1", ...] * root.parent_ids #=> [] */ static VALUE rb_git_commit_parent_ids_GET(VALUE self) { git_commit *commit; const git_oid *parent_id; unsigned int n, parent_count; VALUE ret_arr; Data_Get_Struct(self, git_commit, commit); parent_count = git_commit_parentcount(commit); ret_arr = rb_ary_new2((long)parent_count); for (n = 0; n < parent_count; n++) { parent_id = git_commit_parent_id(commit, n); if (parent_id) { rb_ary_push(ret_arr, rugged_create_oid(parent_id)); } } return ret_arr; } /* * call-seq: * commit.amend(data = {}) -> oid * * Amend a commit object, with the given +data+ * arguments, passed as a +Hash+: * * - +:message+: a string with the full text for the commit's message * - +:committer+ (optional): a hash with the signature for the committer, * defaults to the signature from the configuration * - +:author+ (optional): a hash with the signature for the author, * defaults to the signature from the configuration * - +:tree+: the tree for this amended commit, represented as a Rugged::Tree * instance or an OID +String+. * - +:update_ref+ (optional): a +String+ with the name of a reference in the * repository which should be updated to point to this amended commit (e.g. "HEAD") * * When the amended commit is successfully written to disk, its +oid+ will be * returned as a hex +String+. * * author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} * * commit.amend( * :author => author, * :message => "Updated Hello world\n\n", * :committer => author, * :tree => some_tree) #=> "f148106ca58764adc93ad4e2d6b1d168422b9796" */ static VALUE rb_git_commit_amend(VALUE self, VALUE rb_data) { VALUE rb_message, rb_tree, rb_ref, owner; int error = 0; git_commit *commit_to_amend; char *message = NULL; git_tree *tree = NULL; git_signature *author = NULL, *committer = NULL; git_oid commit_oid; git_repository *repo; const char *update_ref = NULL; Check_Type(rb_data, T_HASH); Data_Get_Struct(self, git_commit, commit_to_amend); owner = rugged_owner(self); Data_Get_Struct(owner, git_repository, repo); rb_ref = rb_hash_aref(rb_data, CSTR2SYM("update_ref")); if (!NIL_P(rb_ref)) { Check_Type(rb_ref, T_STRING); update_ref = StringValueCStr(rb_ref); } rb_message = rb_hash_aref(rb_data, CSTR2SYM("message")); if (!NIL_P(rb_message)) { Check_Type(rb_message, T_STRING); message = StringValueCStr(rb_message); } rb_tree = rb_hash_aref(rb_data, CSTR2SYM("tree")); if (!NIL_P(rb_tree)) tree = (git_tree *)rugged_object_get(repo, rb_tree, GIT_OBJ_TREE); if (!NIL_P(rb_hash_aref(rb_data, CSTR2SYM("committer")))) { committer = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("committer")), repo ); } if (!NIL_P(rb_hash_aref(rb_data, CSTR2SYM("author")))) { author = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("author")), repo ); } error = git_commit_amend( &commit_oid, commit_to_amend, update_ref, author, committer, NULL, message, tree); git_signature_free(author); git_signature_free(committer); git_object_free((git_object *)tree); rugged_exception_check(error); return rugged_create_oid(&commit_oid); } struct commit_data { VALUE rb_err_obj; const char *update_ref; const char *message; git_tree *tree; git_signature *author; git_signature *committer; int parent_count; const git_commit **parents; }; /** * Parse the commit options into something we can re-use * * Note that parents may be set even when the function errors, so make * sure to free this data. */ static VALUE parse_commit_options(struct commit_data *out, git_repository *repo, VALUE rb_data) { VALUE rb_message, rb_tree, rb_parents, rb_ref; int error = 0, parent_count, i; rb_ref = rb_hash_aref(rb_data, CSTR2SYM("update_ref")); if (!NIL_P(rb_ref)) { Check_Type(rb_ref, T_STRING); out->update_ref = StringValueCStr(rb_ref); } rb_message = rb_hash_aref(rb_data, CSTR2SYM("message")); Check_Type(rb_message, T_STRING); out->message = StringValueCStr(rb_message); out->committer = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("committer")), repo ); out->author = rugged_signature_get( rb_hash_aref(rb_data, CSTR2SYM("author")), repo ); rb_parents = rb_hash_aref(rb_data, CSTR2SYM("parents")); Check_Type(rb_parents, T_ARRAY); rb_tree = rb_hash_aref(rb_data, CSTR2SYM("tree")); out->tree = (git_tree *)rugged_object_get(repo, rb_tree, GIT_OBJ_TREE); out->parents = xcalloc(RARRAY_LEN(rb_parents), sizeof(void *)); parent_count = 0; for (i = 0; i < (int)RARRAY_LEN(rb_parents); ++i) { VALUE p = rb_ary_entry(rb_parents, i); git_commit *parent = NULL; git_commit *tmp = NULL; if (NIL_P(p)) continue; if (TYPE(p) == T_STRING) { git_oid oid; error = git_oid_fromstr(&oid, StringValueCStr(p)); if (error < GIT_OK) goto out; error = git_commit_lookup(&parent, repo, &oid); if (error < GIT_OK) goto out; } else if (rb_obj_is_kind_of(p, rb_cRuggedCommit)) { Data_Get_Struct(p, git_commit, tmp); if ((error = git_object_dup((git_object **) &parent, (git_object *) tmp)) < 0) goto out; } else { out->rb_err_obj = rb_exc_new2(rb_eTypeError, "Invalid type for parent object"); error = -1; goto out; } out->parents[parent_count] = parent; parent_count++; } out: out->parent_count = parent_count; return error; } static void free_commit_options(struct commit_data *commit_data) { int i; git_signature_free(commit_data->author); git_signature_free(commit_data->committer); git_object_free((git_object *)commit_data->tree); for (i = 0; i < commit_data->parent_count; ++i) git_object_free((git_object *) commit_data->parents[i]); xfree(commit_data->parents); } /* * call-seq: * Commit.create(repository, data = {}) -> oid * * Write a new +Commit+ object to +repository+, with the given +data+ * arguments, passed as a +Hash+: * * - +:message+: a string with the full text for the commit's message * - +:committer+ (optional): a hash with the signature for the committer, * defaults to the signature from the configuration * - +:author+ (optional): a hash with the signature for the author, * defaults to the signature from the configuration * - +:parents+: an +Array+ with zero or more parents for this commit, * represented as Rugged::Commit instances, or OID +String+. * - +:tree+: the tree for this commit, represented as a Rugged::Tree * instance or an OID +String+. * - +:update_ref+ (optional): a +String+ with the name of a reference in the * repository which should be updated to point to this commit (e.g. "HEAD") * * When the commit is successfully written to disk, its +oid+ will be * returned as a hex +String+. * * author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} * * Rugged::Commit.create(r, * :author => author, * :message => "Hello world\n\n", * :committer => author, * :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"], * :tree => some_tree) #=> "f148106ca58764adc93ad4e2d6b1d168422b9796" */ static VALUE rb_git_commit_create(VALUE self, VALUE rb_repo, VALUE rb_data) { int error = 0; struct commit_data commit_data = { Qnil }; git_oid commit_oid; git_repository *repo; Check_Type(rb_data, T_HASH); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if ((error = parse_commit_options(&commit_data, repo, rb_data)) < 0) goto cleanup; error = git_commit_create( &commit_oid, repo, commit_data.update_ref, commit_data.author, commit_data.committer, NULL, commit_data.message, commit_data.tree, commit_data.parent_count, commit_data.parents); cleanup: free_commit_options(&commit_data); if (!NIL_P(commit_data.rb_err_obj)) rb_exc_raise(commit_data.rb_err_obj); rugged_exception_check(error); return rugged_create_oid(&commit_oid); } /* * call-seq: * commit.to_mbox(options = {}) -> str * * Returns +commit+'s contents formatted to resemble UNIX mailbox format. * * Does not (yet) support merge commits. * * The following options can be passed in the +options+ Hash: * * :patch_no :: * Number for this patch in the series. Defaults to +1+. * * :total_patches :: * Total number of patches in the series. Defaults to +1+. * * :exclude_subject_patch_marker :: * If set to true, no "[PATCH]" marker will be * added to the beginning of the subject line. * * Additionally, you can also pass the same options as for Rugged::Tree#diff. */ static VALUE rb_git_commit_to_mbox(int argc, VALUE *argv, VALUE self) { git_buf email_patch = { NULL }; git_repository *repo; git_commit *commit; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff_format_email_flags_t flags = GIT_DIFF_FORMAT_EMAIL_NONE; VALUE rb_repo = rugged_owner(self), rb_email_patch = Qnil, rb_val, rb_options; int error; size_t patch_no = 1, total_patches = 1; rb_scan_args(argc, argv, ":", &rb_options); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Data_Get_Struct(self, git_commit, commit); if (!NIL_P(rb_options)) { Check_Type(rb_options, T_HASH); rb_val = rb_hash_aref(rb_options, CSTR2SYM("patch_no")); if (!NIL_P(rb_val)) patch_no = NUM2INT(rb_val); rb_val = rb_hash_aref(rb_options, CSTR2SYM("total_patches")); if (!NIL_P(rb_val)) total_patches = NUM2INT(rb_val); if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("exclude_subject_patch_marker")))) flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER; rugged_parse_diff_options(&opts, rb_options); } error = git_diff_commit_as_email( &email_patch, repo, commit, patch_no, total_patches, flags, &opts); if (error) goto cleanup; rb_email_patch = rb_enc_str_new(email_patch.ptr, email_patch.size, rb_utf8_encoding()); cleanup: xfree(opts.pathspec.strings); git_buf_free(&email_patch); rugged_exception_check(error); return rb_email_patch; } /* * call-seq: * commit.header_field(field_name) -> str * * Returns +commit+'s header field value. */ static VALUE rb_git_commit_header_field(VALUE self, VALUE rb_field) { git_buf header_field = { 0 }; git_commit *commit = NULL; const char *encoding_name; rb_encoding *encoding = rb_utf8_encoding(); VALUE rb_result; int error; Check_Type(rb_field, T_STRING); Data_Get_Struct(self, git_commit, commit); error = git_commit_header_field(&header_field, commit, StringValueCStr(rb_field)); if (error < 0) { git_buf_free(&header_field); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); } encoding_name = git_commit_message_encoding(commit); if (encoding_name != NULL) encoding = rb_enc_find(encoding_name); rb_result = rb_enc_str_new(header_field.ptr, header_field.size, encoding); git_buf_free(&header_field); return rb_result; } /* * call-seq: * commit.header -> str * * Returns +commit+'s entire raw header. */ static VALUE rb_git_commit_header(VALUE self) { git_commit *commit; const char *raw_header; Data_Get_Struct(self, git_commit, commit); raw_header = git_commit_raw_header(commit); return rb_str_new_utf8(raw_header); } /* * call-seq: * Commit.extract_signature(repo, commit, field_name) -> [str, str] * * Returns +commit+'s signature in 'field' and the signed data * * The signature is done over the contents of the commit without the * signature block in the header, which is the data in the second * element in the return array. */ static VALUE rb_git_commit_extract_signature(int argc, VALUE *argv, VALUE self) { int error; VALUE ret; git_oid commit_id; const char *field; git_repository *repo; git_buf signature = {0}, signed_data = {0}; VALUE rb_repo, rb_commit, rb_field = Qnil; rb_scan_args(argc, argv, "21", &rb_repo, &rb_commit, &rb_field); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_oid_fromstr(&commit_id, StringValueCStr(rb_commit)); rugged_exception_check(error); field = NIL_P(rb_field) ? NULL : StringValueCStr(rb_field); error = git_commit_extract_signature(&signature, &signed_data, repo, &commit_id, field); if (error < 0) { git_buf_free(&signature); git_buf_free(&signed_data); } if (error == GIT_ENOTFOUND && giterr_last()->klass == GITERR_OBJECT ) { ret = Qnil; } else { rugged_exception_check(error); ret = rb_ary_new3(2, rb_str_new(signature.ptr, signature.size), rb_str_new(signed_data.ptr, signed_data.size)); } git_buf_free(&signature); git_buf_free(&signed_data); return ret; } /* * call-seq: * Commit.create_to_s(repository, data = {}) -> str * * Create a string with the contents of the commit, created with the * given +data+ arguments, passed as a +Hash+: * * - +:message+: a string with the full text for the commit's message * - +:committer+ (optional): a hash with the signature for the committer, * defaults to the signature from the configuration * - +:author+ (optional): a hash with the signature for the author, * defaults to the signature from the configuration * - +:parents+: an +Array+ with zero or more parents for this commit, * represented as Rugged::Commit instances, or OID +String+. * - +:tree+: the tree for this commit, represented as a Rugged::Tree * instance or an OID +String+. * * author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} * * Rugged::Commit.create(r, * :author => author, * :message => "Hello world\n\n", * :committer => author, * :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"], * :tree => some_tree) #=> "tree some_tree\nparent 2cb831...." */ static VALUE rb_git_commit_create_to_s(VALUE self, VALUE rb_repo, VALUE rb_data) { int error = 0; struct commit_data commit_data = { Qnil }; git_repository *repo; git_buf buf = { 0 }; VALUE ret; Check_Type(rb_data, T_HASH); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); if ((error = parse_commit_options(&commit_data, repo, rb_data)) < 0) goto cleanup; error = git_commit_create_buffer( &buf, repo, commit_data.author, commit_data.committer, NULL, commit_data.message, commit_data.tree, commit_data.parent_count, commit_data.parents); cleanup: free_commit_options(&commit_data); if (!NIL_P(commit_data.rb_err_obj)) rb_exc_raise(commit_data.rb_err_obj); rugged_exception_check(error); ret = rb_str_new_utf8(buf.ptr); git_buf_free(&buf); return ret; } /* * call-seq: * Commit.create_with_signature(repo, content, signature, field_name = "gpgsig") -> oid * * Create a commit from the +content+ string and the +signature+, * adding this data to the +field_name+ header field in the resulting * commit. * * Returns the new commit's object id. * */ static VALUE rb_git_commit_create_with_signature(int argc, VALUE *argv, VALUE self) { int error; git_oid id; const char *field = NULL; git_repository *repo; VALUE rb_repo, rb_content, rb_signature, rb_field = Qnil; rb_scan_args(argc, argv, "31", &rb_repo, &rb_content, &rb_signature, &rb_field); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_content, T_STRING); Check_Type(rb_signature, T_STRING); if (!NIL_P(rb_field)) { Check_Type(rb_field, T_STRING); field = StringValueCStr(rb_field); } error = git_commit_create_with_signature(&id, repo, StringValueCStr(rb_content), StringValueCStr(rb_signature), field); rugged_exception_check(error); return rugged_create_oid(&id); } void Init_rugged_commit(void) { rb_cRuggedCommit = rb_define_class_under(rb_mRugged, "Commit", rb_cRuggedObject); rb_define_singleton_method(rb_cRuggedCommit, "create", rb_git_commit_create, 2); rb_define_singleton_method(rb_cRuggedCommit, "create_to_s", rb_git_commit_create_to_s, 2); rb_define_singleton_method(rb_cRuggedCommit, "create_with_signature", rb_git_commit_create_with_signature, -1); rb_define_singleton_method(rb_cRuggedCommit, "extract_signature", rb_git_commit_extract_signature, -1); rb_define_method(rb_cRuggedCommit, "message", rb_git_commit_message_GET, 0); rb_define_method(rb_cRuggedCommit, "summary", rb_git_commit_summary_GET, 0); rb_define_method(rb_cRuggedCommit, "epoch_time", rb_git_commit_epoch_time_GET, 0); rb_define_method(rb_cRuggedCommit, "committer", rb_git_commit_committer_GET, 0); rb_define_method(rb_cRuggedCommit, "author", rb_git_commit_author_GET, 0); rb_define_method(rb_cRuggedCommit, "tree", rb_git_commit_tree_GET, 0); rb_define_method(rb_cRuggedCommit, "tree_id", rb_git_commit_tree_id_GET, 0); rb_define_method(rb_cRuggedCommit, "tree_oid", rb_git_commit_tree_id_GET, 0); rb_define_method(rb_cRuggedCommit, "parents", rb_git_commit_parents_GET, 0); rb_define_method(rb_cRuggedCommit, "parent_ids", rb_git_commit_parent_ids_GET, 0); rb_define_method(rb_cRuggedCommit, "parent_oids", rb_git_commit_parent_ids_GET, 0); rb_define_method(rb_cRuggedCommit, "amend", rb_git_commit_amend, 1); rb_define_method(rb_cRuggedCommit, "to_mbox", rb_git_commit_to_mbox, -1); rb_define_method(rb_cRuggedCommit, "header_field", rb_git_commit_header_field, 1); rb_define_method(rb_cRuggedCommit, "header", rb_git_commit_header, 0); } rugged-0.26.0/ext/rugged/rugged_object.c0000644000175000017500000002152213147033070020226 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #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); } rugged-0.26.0/ext/rugged/rugged_cred.c0000644000175000017500000001013113147033070017667 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_mRuggedCred; VALUE rb_cRuggedCredUserPassword; VALUE rb_cRuggedCredSshKey; VALUE rb_cRuggedCredSshKeyFromAgent; VALUE rb_cRuggedCredDefault; static void rugged_cred_extract_userpass(git_cred **cred, VALUE rb_credential) { VALUE rb_username = rb_iv_get(rb_credential, "@username"); VALUE rb_password = rb_iv_get(rb_credential, "@password"); Check_Type(rb_username, T_STRING); Check_Type(rb_password, T_STRING); rugged_exception_check( git_cred_userpass_plaintext_new(cred, StringValueCStr(rb_username), StringValueCStr(rb_password) ) ); } static void rugged_cred_extract_ssh_key(git_cred **cred, VALUE rb_credential) { VALUE rb_username = rb_iv_get(rb_credential, "@username"); VALUE rb_publickey = rb_iv_get(rb_credential, "@publickey"); VALUE rb_privatekey = rb_iv_get(rb_credential, "@privatekey"); VALUE rb_passphrase = rb_iv_get(rb_credential, "@passphrase"); Check_Type(rb_username, T_STRING); Check_Type(rb_privatekey, T_STRING); if (!NIL_P(rb_publickey)) Check_Type(rb_publickey, T_STRING); if (!NIL_P(rb_passphrase)) Check_Type(rb_passphrase, T_STRING); rugged_exception_check( git_cred_ssh_key_new(cred, StringValueCStr(rb_username), NIL_P(rb_publickey) ? NULL : StringValueCStr(rb_publickey), StringValueCStr(rb_privatekey), NIL_P(rb_passphrase) ? NULL : StringValueCStr(rb_passphrase) ) ); } static void rugged_credential_extract_ssh_key_from_agent(git_cred **cred, VALUE rb_credential) { VALUE rb_username = rb_iv_get(rb_credential, "@username"); Check_Type(rb_username, T_STRING); rugged_exception_check( git_cred_ssh_key_from_agent(cred, StringValueCStr(rb_username)) ); } static void rugged_cred_extract_default(git_cred **cred, VALUE rb_credential) { rugged_exception_check(git_cred_default_new(cred)); } static void rugged_cred_extract_username(git_cred **cred, VALUE rb_credential) { VALUE rb_username = rb_iv_get(rb_credential, "@username"); Check_Type(rb_username, T_STRING); rugged_exception_check(git_cred_username_new(cred, StringValueCStr(rb_username))); } void rugged_cred_extract(git_cred **cred, int allowed_types, VALUE rb_credential) { if (rb_obj_is_kind_of(rb_credential, rb_cRuggedCredUserPassword)) { if (allowed_types & GIT_CREDTYPE_USERNAME) { rugged_cred_extract_username(cred, rb_credential); return; } if (!(allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)) rb_raise(rb_eArgError, "Invalid credential type"); rugged_cred_extract_userpass(cred, rb_credential); } else if (rb_obj_is_kind_of(rb_credential, rb_cRuggedCredSshKey)) { if (allowed_types & GIT_CREDTYPE_USERNAME) { rugged_cred_extract_username(cred, rb_credential); return; } if (!(allowed_types & GIT_CREDTYPE_SSH_KEY)) rb_raise(rb_eArgError, "Invalid credential type"); rugged_cred_extract_ssh_key(cred, rb_credential); } else if (rb_obj_is_kind_of(rb_credential, rb_cRuggedCredSshKeyFromAgent)) { if (allowed_types & GIT_CREDTYPE_USERNAME) { rugged_cred_extract_username(cred, rb_credential); return; } if (!(allowed_types & GIT_CREDTYPE_SSH_KEY)) rb_raise(rb_eArgError, "Invalid credential type"); rugged_credential_extract_ssh_key_from_agent(cred, rb_credential); } else if (rb_obj_is_kind_of(rb_credential, rb_cRuggedCredDefault)) { if (!(allowed_types & GIT_CREDTYPE_DEFAULT)) rb_raise(rb_eArgError, "Invalid credential type"); rugged_cred_extract_default(cred, rb_credential); } } void Init_rugged_cred(void) { rb_mRuggedCred = rb_define_module_under(rb_mRugged, "Credentials"); rb_cRuggedCredUserPassword = rb_define_class_under(rb_mRuggedCred, "UserPassword", rb_cObject); rb_cRuggedCredSshKey = rb_define_class_under(rb_mRuggedCred, "SshKey", rb_cObject); rb_cRuggedCredSshKeyFromAgent = rb_define_class_under(rb_mRuggedCred, "SshKeyFromAgent", rb_cObject); rb_cRuggedCredDefault = rb_define_class_under(rb_mRuggedCred, "Default", rb_cObject); } rugged-0.26.0/ext/rugged/rugged_revwalk.c0000644000175000017500000003144713147033070020442 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedObject; VALUE rb_cRuggedWalker; static void rb_git_walk__free(git_revwalk *walk) { git_revwalk_free(walk); } VALUE rugged_walker_new(VALUE klass, VALUE owner, git_revwalk *walk) { VALUE rb_walk = Data_Wrap_Struct(klass, NULL, &rb_git_walk__free, walk); rugged_set_owner(rb_walk, owner); return rb_walk; } static void push_commit_oid(git_revwalk *walk, const git_oid *oid, int hide) { int error; if (hide) error = git_revwalk_hide(walk, oid); else error = git_revwalk_push(walk, oid); rugged_exception_check(error); } static void push_commit_ref(git_revwalk *walk, const char *ref, int hide) { int error; if (hide) error = git_revwalk_hide_ref(walk, ref); else error = git_revwalk_push_ref(walk, ref); rugged_exception_check(error); } static void push_commit_1(git_revwalk *walk, VALUE rb_commit, int hide) { if (rb_obj_is_kind_of(rb_commit, rb_cRuggedObject)) { git_object *object; Data_Get_Struct(rb_commit, git_object, object); push_commit_oid(walk, git_object_id(object), hide); return; } Check_Type(rb_commit, T_STRING); if (RSTRING_LEN(rb_commit) == 40) { git_oid commit_oid; if (git_oid_fromstr(&commit_oid, RSTRING_PTR(rb_commit)) == 0) { push_commit_oid(walk, &commit_oid, hide); return; } } push_commit_ref(walk, StringValueCStr(rb_commit), hide); } static void push_commit(git_revwalk *walk, VALUE rb_commit, int hide) { if (rb_type(rb_commit) == T_ARRAY) { long i; for (i = 0; i < RARRAY_LEN(rb_commit); ++i) push_commit_1(walk, rb_ary_entry(rb_commit, i), hide); return; } push_commit_1(walk, rb_commit, hide); } /* * call-seq: * Walker.new(repository) -> walker * * Create a new +Walker+ instance able to walk commits found * in +repository+, which is a Rugged::Repository instance. */ static VALUE rb_git_walker_new(VALUE klass, VALUE rb_repo) { git_repository *repo; git_revwalk *walk; int error; Data_Get_Struct(rb_repo, git_repository, repo); error = git_revwalk_new(&walk, repo); rugged_exception_check(error); return rugged_walker_new(klass, rb_repo, walk);; } /* * call-seq: * walker.push(commit) -> nil * * Push one new +commit+ to start the walk from. +commit+ must be a * +String+ with the OID of a commit in the repository, or a Rugged::Commit * instance. * * More than one commit may be pushed to the walker (to walk several * branches simulataneously). * * Duplicate pushed commits will be ignored; at least one commit must have been * pushed as a starting point before the walk can begin. * * walker.push("92b22bbcb37caf4f6f53d30292169e84f5e4283b") */ static VALUE rb_git_walker_push(VALUE self, VALUE rb_commit) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); push_commit(walk, rb_commit, 0); return Qnil; } static VALUE rb_git_walker_push_range(VALUE self, VALUE range) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); rugged_exception_check(git_revwalk_push_range(walk, StringValueCStr(range))); return Qnil; } /* * call-seq: * walker.hide(commit) -> nil * * Hide the given +commit+ (and all its parents) from the * output in the revision walk. */ static VALUE rb_git_walker_hide(VALUE self, VALUE rb_commit) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); push_commit(walk, rb_commit, 1); return Qnil; } /* * call-seq: * walker.sorting(sort_mode) -> nil * * Change the sorting mode for the revision walk. * * This will cause +walker+ to be reset. */ static VALUE rb_git_walker_sorting(VALUE self, VALUE ruby_sort_mode) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); git_revwalk_sorting(walk, FIX2INT(ruby_sort_mode)); return Qnil; } /* * call-seq: * walker.simplify_first_parent() -> nil * * Simplify the walk to the first parent of each commit. */ static VALUE rb_git_walker_simplify_first_parent(VALUE self) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); git_revwalk_simplify_first_parent(walk); return Qnil; } /* * call-seq: * walker.count -> Fixnum * * Returns the amount of objects a walker iterated over. If an argument or * block is given this method delegates to +Enumerable#count+. */ static VALUE rb_git_walker_count(int argc, VALUE *argv, VALUE self) { git_revwalk *walk; git_oid commit_oid; int error = 0; uint64_t count = 0; if (argc > 0 || rb_block_given_p()) return rb_call_super(argc, argv); Data_Get_Struct(self, git_revwalk, walk); while (((error = git_revwalk_next(&commit_oid, walk)) == 0) && ++count != UINT64_MAX); if (error != GIT_ITEROVER) rugged_exception_check(error); return ULONG2NUM(count); } /* * call-seq: * walker.reset -> nil * * Remove all pushed and hidden commits and reset the +walker+ * back into a blank state. */ static VALUE rb_git_walker_reset(VALUE self) { git_revwalk *walk; Data_Get_Struct(self, git_revwalk, walk); git_revwalk_reset(walk); return Qnil; } struct walk_options { VALUE rb_owner; VALUE rb_options; git_repository *repo; git_revwalk *walk; int oid_only; uint64_t offset, limit; }; static void load_walk_limits(struct walk_options *w, VALUE rb_options) { VALUE rb_value = rb_hash_lookup(rb_options, CSTR2SYM("offset")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); w->offset = FIX2ULONG(rb_value); } rb_value = rb_hash_lookup(rb_options, CSTR2SYM("limit")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); w->limit = FIX2ULONG(rb_value); } } static VALUE load_all_options(VALUE _payload) { struct walk_options *w = (struct walk_options *)_payload; VALUE rb_options = w->rb_options; VALUE rb_show, rb_hide, rb_sort, rb_simp, rb_oid_only; load_walk_limits(w, rb_options); rb_sort = rb_hash_lookup(rb_options, CSTR2SYM("sort")); if (!NIL_P(rb_sort)) { Check_Type(rb_sort, T_FIXNUM); git_revwalk_sorting(w->walk, FIX2INT(rb_sort)); } rb_show = rb_hash_lookup(rb_options, CSTR2SYM("show")); if (!NIL_P(rb_show)) { push_commit(w->walk, rb_show, 0); } rb_hide = rb_hash_lookup(rb_options, CSTR2SYM("hide")); if (!NIL_P(rb_hide)) { push_commit(w->walk, rb_hide, 1); } rb_simp = rb_hash_lookup(rb_options, CSTR2SYM("simplify")); if (RTEST(rb_simp)) { git_revwalk_simplify_first_parent(w->walk); } rb_oid_only = rb_hash_lookup(rb_options, CSTR2SYM("oid_only")); if (RTEST(rb_oid_only)) { w->oid_only = 1; } return Qnil; } static VALUE do_walk(VALUE _payload) { struct walk_options *w = (struct walk_options *)_payload; int error; git_oid commit_oid; while ((error = git_revwalk_next(&commit_oid, w->walk)) == 0) { if (w->offset > 0) { w->offset--; continue; } if (w->oid_only) { rb_yield(rugged_create_oid(&commit_oid)); } else { git_commit *commit; error = git_commit_lookup(&commit, w->repo, &commit_oid); rugged_exception_check(error); rb_yield( rugged_object_new(w->rb_owner, (git_object *)commit) ); } if (--w->limit == 0) break; } if (error != GIT_ITEROVER) rugged_exception_check(error); return Qnil; } /* * call-seq: * Rugged::Walker.walk(repo, options={}) { |commit| block } * * Create a Walker object, initialize it with the given options * and perform a walk on the repository; the lifetime of the * walker is bound to the call and it is immediately cleaned * up after the walk is over. * * The following options are available: * * - +sort+: Sorting mode for the walk (or an OR combination * of several sorting modes). * * - +show+: Tips of the repository that will be walked; * this is necessary for the walk to yield any results. * A tip can be a 40-char object ID, an existing +Rugged::Commit+ * object, a reference, or an +Array+ of several of these * (if you'd like to walk several tips in parallel). * * - +hide+: Same as +show+, but hides the given tips instead * so they don't appear on the walk. * * - +oid_only+: if +true+, the walker will yield 40-char OIDs * for each commit, instead of real +Rugged::Commit+ objects. * Defaults to +false+. * * - +simplify+: if +true+, the walk will be simplified * to the first parent of each commit. * * Example: * * Rugged::Walker.walk(repo, * show: "92b22bbcb37caf4f6f53d30292169e84f5e4283b", * sort: Rugged::SORT_DATE|Rugged::SORT_TOPO, * oid_only: true) do |commit_oid| * puts commit_oid * end * * generates: * * 92b22bbcb37caf4f6f53d30292169e84f5e4283b * 6b750d5800439b502de669465b385e5f469c78b6 * ef9207141549f4ffcd3c4597e270d32e10d0a6bc * cb75e05f0f8ac3407fb3bd0ebd5ff07573b16c9f * ... */ static VALUE rb_git_walk(int argc, VALUE *argv, VALUE self) { VALUE rb_repo, rb_options; struct walk_options w; int exception = 0; rb_scan_args(argc, argv, "10:", &rb_repo, &rb_options); if (!rb_block_given_p()) { ID iter_method = ID2SYM(rb_intern("walk")); return rb_funcall(self, rb_intern("to_enum"), 3, iter_method, rb_repo, rb_options); } Data_Get_Struct(rb_repo, git_repository, w.repo); rugged_exception_check(git_revwalk_new(&w.walk, w.repo)); w.rb_owner = rb_repo; w.rb_options = rb_options; w.oid_only = 0; w.offset = 0; w.limit = UINT64_MAX; if (!NIL_P(w.rb_options)) rb_protect(load_all_options, (VALUE)&w, &exception); if (!exception) rb_protect(do_walk, (VALUE)&w, &exception); git_revwalk_free(w.walk); if (exception) rb_jump_tag(exception); return Qnil; } static VALUE rb_git_walk_with_opts(int argc, VALUE *argv, VALUE self, int oid_only) { VALUE rb_options; struct walk_options w; rb_scan_args(argc, argv, "01", &rb_options); if (!rb_block_given_p()) { ID iter_method = ID2SYM(rb_intern(oid_only ? "each_oid" : "each")); return rb_funcall(self, rb_intern("to_enum"), 2, iter_method, rb_options); } Data_Get_Struct(self, git_revwalk, w.walk); w.repo = git_revwalk_repository(w.walk); w.rb_owner = rugged_owner(self); w.rb_options = Qnil; w.oid_only = oid_only; w.offset = 0; w.limit = UINT64_MAX; if (!NIL_P(rb_options)) load_walk_limits(&w, rb_options); return do_walk((VALUE)&w); } /* * call-seq: * walker.each { |commit| block } * walker.each -> Enumerator * * Perform the walk through the repository, yielding each * one of the commits found as a Rugged::Commit instance * to +block+. * * If no +block+ is given, an +Enumerator+ will be returned. * * The walker must have been previously set-up before a walk can be performed * (i.e. at least one commit must have been pushed). * * walker.push("92b22bbcb37caf4f6f53d30292169e84f5e4283b") * walker.each { |commit| puts commit.oid } * * generates: * * 92b22bbcb37caf4f6f53d30292169e84f5e4283b * 6b750d5800439b502de669465b385e5f469c78b6 * ef9207141549f4ffcd3c4597e270d32e10d0a6bc * cb75e05f0f8ac3407fb3bd0ebd5ff07573b16c9f * ... */ static VALUE rb_git_walker_each(int argc, VALUE *argv, VALUE self) { return rb_git_walk_with_opts(argc, argv, self, 0); } /* * call-seq: * walker.each_oid { |commit| block } * walker.each_oid -> Enumerator * * Perform the walk through the repository, yielding each * one of the commit oids found as a String * to +block+. * * If no +block+ is given, an +Enumerator+ will be returned. * * The walker must have been previously set-up before a walk can be performed * (i.e. at least one commit must have been pushed). * * walker.push("92b22bbcb37caf4f6f53d30292169e84f5e4283b") * walker.each { |commit_oid| puts commit_oid } * * generates: * * 92b22bbcb37caf4f6f53d30292169e84f5e4283b * 6b750d5800439b502de669465b385e5f469c78b6 * ef9207141549f4ffcd3c4597e270d32e10d0a6bc * cb75e05f0f8ac3407fb3bd0ebd5ff07573b16c9f * ... */ static VALUE rb_git_walker_each_oid(int argc, VALUE *argv, VALUE self) { return rb_git_walk_with_opts(argc, argv, self, 1); } void Init_rugged_revwalk(void) { rb_cRuggedWalker = rb_define_class_under(rb_mRugged, "Walker", rb_cObject); rb_define_singleton_method(rb_cRuggedWalker, "new", rb_git_walker_new, 1); rb_define_singleton_method(rb_cRuggedWalker, "walk", rb_git_walk, -1); rb_define_method(rb_cRuggedWalker, "push", rb_git_walker_push, 1); rb_define_method(rb_cRuggedWalker, "push_range", rb_git_walker_push_range, 1); rb_define_method(rb_cRuggedWalker, "each", rb_git_walker_each, -1); rb_define_method(rb_cRuggedWalker, "each_oid", rb_git_walker_each_oid, -1); rb_define_method(rb_cRuggedWalker, "walk", rb_git_walker_each, -1); rb_define_method(rb_cRuggedWalker, "hide", rb_git_walker_hide, 1); rb_define_method(rb_cRuggedWalker, "reset", rb_git_walker_reset, 0); rb_define_method(rb_cRuggedWalker, "sorting", rb_git_walker_sorting, 1); rb_define_method(rb_cRuggedWalker, "simplify_first_parent", rb_git_walker_simplify_first_parent, 0); rb_define_method(rb_cRuggedWalker, "count", rb_git_walker_count, -1); } rugged-0.26.0/ext/rugged/rugged_remote.c0000644000175000017500000005342713147033070020264 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_eRuggedError; VALUE rb_cRuggedRemote; static int progress_cb(const char *str, int len, void *data) { struct rugged_remote_cb_payload *payload = data; VALUE args = rb_ary_new2(2); if (NIL_P(payload->progress)) return 0; rb_ary_push(args, payload->progress); rb_ary_push(args, rb_str_new(str, len)); rb_protect(rugged__block_yield_splat, args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } static int transfer_progress_cb(const git_transfer_progress *stats, void *data) { struct rugged_remote_cb_payload *payload = data; VALUE args = rb_ary_new2(5); if (NIL_P(payload->transfer_progress)) return 0; rb_ary_push(args, payload->transfer_progress); rb_ary_push(args, UINT2NUM(stats->total_objects)); rb_ary_push(args, UINT2NUM(stats->indexed_objects)); rb_ary_push(args, UINT2NUM(stats->received_objects)); rb_ary_push(args, UINT2NUM(stats->local_objects)); rb_ary_push(args, UINT2NUM(stats->total_deltas)); rb_ary_push(args, UINT2NUM(stats->indexed_deltas)); rb_ary_push(args, INT2FIX(stats->received_bytes)); rb_protect(rugged__block_yield_splat, args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } static int push_update_reference_cb(const char *refname, const char *status, void *data) { struct rugged_remote_cb_payload *payload = data; if (status != NULL) rb_hash_aset(payload->result, rb_str_new_utf8(refname), rb_str_new_utf8(status)); return GIT_OK; } static int update_tips_cb(const char *refname, const git_oid *src, const git_oid *dest, void *data) { struct rugged_remote_cb_payload *payload = data; VALUE args = rb_ary_new2(4); if (NIL_P(payload->update_tips)) return 0; rb_ary_push(args, payload->update_tips); rb_ary_push(args, rb_str_new_utf8(refname)); rb_ary_push(args, git_oid_iszero(src) ? Qnil : rugged_create_oid(src)); rb_ary_push(args, git_oid_iszero(dest) ? Qnil : rugged_create_oid(dest)); rb_protect(rugged__block_yield_splat, args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } static int certificate_check_cb(git_cert *cert, int valid, const char *host, void *data) { struct rugged_remote_cb_payload *payload = data; VALUE args = rb_ary_new2(3); VALUE ret; if (NIL_P(payload->certificate_check)) return valid ? 0 : GIT_ECERTIFICATE; rb_ary_push(args, payload->certificate_check); rb_ary_push(args, valid ? Qtrue : Qfalse); rb_ary_push(args, rb_str_new_utf8(host)); ret = rb_protect(rugged__block_yield_splat, args, &payload->exception); if (payload->exception) return GIT_ERROR; return rugged_parse_bool(ret) ? GIT_OK : GIT_ECERTIFICATE; } struct extract_cred_args { VALUE rb_callback; git_cred **cred; const char *url; const char *username_from_url; unsigned int allowed_types; }; static VALUE allowed_types_to_rb_ary(int allowed_types) { VALUE rb_allowed_types = rb_ary_new(); if (allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT) rb_ary_push(rb_allowed_types, CSTR2SYM("plaintext")); if (allowed_types & GIT_CREDTYPE_SSH_KEY) rb_ary_push(rb_allowed_types, CSTR2SYM("ssh_key")); if (allowed_types & GIT_CREDTYPE_DEFAULT) rb_ary_push(rb_allowed_types, CSTR2SYM("default")); return rb_allowed_types; } static VALUE extract_cred(VALUE data) { struct extract_cred_args *args = (struct extract_cred_args*)data; VALUE rb_url, rb_username_from_url, rb_cred; rb_url = args->url ? rb_str_new2(args->url) : Qnil; rb_username_from_url = args->username_from_url ? rb_str_new2(args->username_from_url) : Qnil; rb_cred = rb_funcall(args->rb_callback, rb_intern("call"), 3, rb_url, rb_username_from_url, allowed_types_to_rb_ary(args->allowed_types)); rugged_cred_extract(args->cred, args->allowed_types, rb_cred); return Qnil; } static int credentials_cb( git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data) { struct rugged_remote_cb_payload *payload = data; struct extract_cred_args args = { payload->credentials, cred, url, username_from_url, allowed_types }; if (NIL_P(payload->credentials)) return GIT_PASSTHROUGH; rb_protect(extract_cred, (VALUE)&args, &payload->exception); return payload->exception ? GIT_ERROR : GIT_OK; } #define CALLABLE_OR_RAISE(ret, name) \ do { \ if (!rb_respond_to(ret, rb_intern("call"))) \ rb_raise(rb_eArgError, "Expected a Proc or an object that responds to #call (:" name " )."); \ } while (0); void rugged_remote_init_callbacks_and_payload_from_options( VALUE rb_options, git_remote_callbacks *callbacks, struct rugged_remote_cb_payload *payload) { callbacks->payload = payload; callbacks->push_update_reference = push_update_reference_cb; if (!NIL_P(rb_options)) { payload->progress = rb_hash_aref(rb_options, CSTR2SYM("progress")); if (!NIL_P(payload->progress)) { CALLABLE_OR_RAISE(payload->progress, "progress"); callbacks->sideband_progress = progress_cb; } payload->credentials = rb_hash_aref(rb_options, CSTR2SYM("credentials")); if (!NIL_P(payload->credentials)) { CALLABLE_OR_RAISE(payload->credentials, "credentials"); callbacks->credentials = credentials_cb; } payload->certificate_check = rb_hash_aref(rb_options, CSTR2SYM("certificate_check")); if (!NIL_P(payload->certificate_check)) { CALLABLE_OR_RAISE(payload->certificate_check, "certificate_check"); callbacks->certificate_check = certificate_check_cb; } payload->transfer_progress = rb_hash_aref(rb_options, CSTR2SYM("transfer_progress")); if (!NIL_P(payload->transfer_progress)) { CALLABLE_OR_RAISE(payload->transfer_progress, "transfer_progress"); callbacks->transfer_progress = transfer_progress_cb; } payload->update_tips = rb_hash_aref(rb_options, CSTR2SYM("update_tips")); if (!NIL_P(payload->update_tips)) { CALLABLE_OR_RAISE(payload->update_tips, "update_tips"); callbacks->update_tips = update_tips_cb; } } } static void init_custom_headers(VALUE rb_options, git_strarray *custom_headers) { if (!NIL_P(rb_options)) { VALUE rb_headers = rb_hash_aref(rb_options, CSTR2SYM("headers")); rugged_rb_ary_to_strarray(rb_headers, custom_headers); } } static int parse_prune_type(VALUE rb_prune_type) { if (rb_prune_type == Qtrue) { return GIT_FETCH_PRUNE; } else if (rb_prune_type == Qfalse) { return GIT_FETCH_NO_PRUNE; } else if (rb_prune_type == Qnil) { return GIT_FETCH_PRUNE_UNSPECIFIED; } else { rb_raise(rb_eTypeError, "wrong argument type for :prune (expected true, false or nil)"); } } static void rb_git_remote__free(git_remote *remote) { git_remote_free(remote); } VALUE rugged_remote_new(VALUE owner, git_remote *remote) { VALUE rb_remote; rb_remote = Data_Wrap_Struct(rb_cRuggedRemote, NULL, &rb_git_remote__free, remote); rugged_set_owner(rb_remote, owner); return rb_remote; } static VALUE rugged_rhead_new(const git_remote_head *head) { VALUE rb_head = rb_hash_new(); rb_hash_aset(rb_head, CSTR2SYM("local?"), head->local ? Qtrue : Qfalse); rb_hash_aset(rb_head, CSTR2SYM("oid"), rugged_create_oid(&head->oid)); rb_hash_aset(rb_head, CSTR2SYM("loid"), git_oid_iszero(&head->loid) ? Qnil : rugged_create_oid(&head->loid)); rb_hash_aset(rb_head, CSTR2SYM("name"), rb_str_new_utf8(head->name)); return rb_head; } /* * call-seq: * remote.ls(options = {}) -> an_enumerator * remote.ls(options = {}) { |remote_head_hash| block } * * Connects +remote+ to list all references available along with their * associated commit ids. * * The given block is called once for each remote head with a Hash containing the * following keys: * * :local? :: * +true+ if the remote head is available locally, +false+ otherwise. * * :oid :: * The id of the object the remote head is currently pointing to. * * :loid :: * The id of the object the local copy of the remote head is currently * pointing to. Set to +nil+ if there is no local copy of the remote head. * * :name :: * The fully qualified reference name of the remote head. * * If no block is given, an enumerator will be returned. * * The following options can be passed in the +options+ Hash: * * :credentials :: * The credentials to use for the ls operation. Can be either an instance of one * of the Rugged::Credentials types, or a proc returning one of the former. * The proc will be called with the +url+, the +username+ from the url (if applicable) and * a list of applicable credential types. * * :headers :: * Extra HTTP headers to include with the request (only applies to http:// or https:// remotes) */ static VALUE rb_git_remote_ls(int argc, VALUE *argv, VALUE self) { git_remote *remote; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; git_strarray custom_headers = {0}; const git_remote_head **heads; struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 }; VALUE rb_options; int error; size_t heads_len, i; Data_Get_Struct(self, git_remote, remote); rb_scan_args(argc, argv, ":", &rb_options); if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 2, CSTR2SYM("ls"), rb_options); rugged_remote_init_callbacks_and_payload_from_options(rb_options, &callbacks, &payload); init_custom_headers(rb_options, &custom_headers); if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, &custom_headers)) || (error = git_remote_ls(&heads, &heads_len, remote))) goto cleanup; for (i = 0; i < heads_len && !payload.exception; i++) rb_protect(rb_yield, rugged_rhead_new(heads[i]), &payload.exception); cleanup: git_remote_disconnect(remote); git_strarray_free(&custom_headers); if (payload.exception) rb_jump_tag(payload.exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * remote.name() -> string * * Returns the remote's name. * * remote.name #=> "origin" */ static VALUE rb_git_remote_name(VALUE self) { git_remote *remote; const char * name; Data_Get_Struct(self, git_remote, remote); name = git_remote_name(remote); return name ? rb_str_new_utf8(name) : Qnil; } /* * call-seq: * remote.url() -> string * * Returns the remote's url * * remote.url #=> "git://github.com/libgit2/rugged.git" */ static VALUE rb_git_remote_url(VALUE self) { git_remote *remote; Data_Get_Struct(self, git_remote, remote); return rb_str_new_utf8(git_remote_url(remote)); } /* * call-seq: * remote.push_url() -> string or nil * * Returns the remote's url for pushing or nil if no special url for * pushing is set. * * remote.push_url #=> "git://github.com/libgit2/rugged.git" */ static VALUE rb_git_remote_push_url(VALUE self) { git_remote *remote; const char * push_url; Data_Get_Struct(self, git_remote, remote); push_url = git_remote_pushurl(remote); return push_url ? rb_str_new_utf8(push_url) : Qnil; } /* * call-seq: * remote.push_url = url -> url * * Sets the remote's url for pushing without persisting it in the config. * Existing connections will not be updated. * * remote.push_url = 'git@github.com/libgit2/rugged.git' #=> "git@github.com/libgit2/rugged.git" */ static VALUE rb_git_remote_set_push_url(VALUE self, VALUE rb_url) { VALUE rb_repo = rugged_owner(self); git_remote *remote; git_repository *repo; rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_url, T_STRING); Data_Get_Struct(self, git_remote, remote); rugged_exception_check( git_remote_set_pushurl(repo, git_remote_name(remote), StringValueCStr(rb_url)) ); return rb_url; } static VALUE rb_git_remote_refspecs(VALUE self, git_direction direction) { git_remote *remote; int error = 0; git_strarray refspecs; VALUE rb_refspec_array; Data_Get_Struct(self, git_remote, remote); if (direction == GIT_DIRECTION_FETCH) error = git_remote_get_fetch_refspecs(&refspecs, remote); else error = git_remote_get_push_refspecs(&refspecs, remote); rugged_exception_check(error); rb_refspec_array = rugged_strarray_to_rb_ary(&refspecs); git_strarray_free(&refspecs); return rb_refspec_array; } /* * call-seq: * remote.fetch_refspecs -> array * * Get the remote's list of fetch refspecs as +array+. */ static VALUE rb_git_remote_fetch_refspecs(VALUE self) { return rb_git_remote_refspecs(self, GIT_DIRECTION_FETCH); } /* * call-seq: * remote.push_refspecs -> array * * Get the remote's list of push refspecs as +array+. */ static VALUE rb_git_remote_push_refspecs(VALUE self) { return rb_git_remote_refspecs(self, GIT_DIRECTION_PUSH); } /* * call-seq: * remote.check_connection(direction, options = {}) -> boolean * * Try to connect to the +remote+. Useful to simulate * git fetch --dry-run and git push --dry-run. * * Returns +true+ if connection is successful, +false+ otherwise. * * +direction+ must be either +:fetch+ or +:push+. * * The following options can be passed in the +options+ Hash: * * +credentials+ :: * The credentials to use for the connection. Can be either an instance of * one of the Rugged::Credentials types, or a proc returning one of the * former. * The proc will be called with the +url+, the +username+ from the url (if * applicable) and a list of applicable credential types. * * :headers :: * Extra HTTP headers to include with the request (only applies to http:// or https:// remotes) * * Example: * * remote = repo.remotes["origin"] * success = remote.check_connection(:fetch) * raise Error("Unable to pull without credentials") unless success */ static VALUE rb_git_remote_check_connection(int argc, VALUE *argv, VALUE self) { git_remote *remote; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; git_strarray custom_headers = {0}; struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 }; VALUE rb_direction, rb_options; ID id_direction; int error, direction; Data_Get_Struct(self, git_remote, remote); rb_scan_args(argc, argv, "01:", &rb_direction, &rb_options); Check_Type(rb_direction, T_SYMBOL); id_direction = SYM2ID(rb_direction); if (id_direction == rb_intern("fetch")) direction = GIT_DIRECTION_FETCH; else if (id_direction == rb_intern("push")) direction = GIT_DIRECTION_PUSH; else rb_raise(rb_eTypeError, "Invalid direction. Expected :fetch or :push"); rugged_remote_init_callbacks_and_payload_from_options(rb_options, &callbacks, &payload); init_custom_headers(rb_options, &custom_headers); error = git_remote_connect(remote, direction, &callbacks, NULL, &custom_headers); git_remote_disconnect(remote); git_strarray_free(&custom_headers); if (payload.exception) rb_jump_tag(payload.exception); return error ? Qfalse : Qtrue; } /* * call-seq: * remote.fetch(refspecs = nil, options = {}) -> hash * * Downloads new data from the remote for the given +refspecs+ and updates tips. * * You can optionally pass in a single or multiple alternative +refspecs+ to use instead of the fetch * refspecs already configured for +remote+. * * Returns a hash containing statistics for the fetch operation. * * The following options can be passed in the +options+ Hash: * * :credentials :: * The credentials to use for the fetch operation. Can be either an instance of one * of the Rugged::Credentials types, or a proc returning one of the former. * The proc will be called with the +url+, the +username+ from the url (if applicable) and * a list of applicable credential types. * * :headers :: * Extra HTTP headers to include with the request (only applies to http:// or https:// remotes) * * :progress :: * A callback that will be executed with the textual progress received from the remote. * This is the text send over the progress side-band (ie. the "counting objects" output). * * :transfer_progress :: * A callback that will be executed to report clone progress information. It will be passed * the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+, * +total_deltas+, +indexed_deltas+ and +received_bytes+. * * :update_tips :: * A callback that will be executed each time a reference is updated locally. It will be * passed the +refname+, +old_oid+ and +new_oid+. * * :certificate_check :: * A callback that will be executed each time we validate a certificate using https. It * will be passed the +valid+, +host_name+ and the callback should return a true/false to * indicate if the certificate has been validated. * * :message :: * The message to insert into the reflogs. Defaults to "fetch". * * :prune :: * Specifies the prune mode for the fetch. +true+ remove any remote-tracking references that * no longer exist, +false+ do not prune, +nil+ use configured settings Defaults to "nil". * * Example: * * remote = Rugged::Remote.lookup(@repo, 'origin') * remote.fetch({ * transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes| * # ... * } * }) */ static VALUE rb_git_remote_fetch(int argc, VALUE *argv, VALUE self) { git_remote *remote; git_strarray refspecs; git_fetch_options opts = GIT_FETCH_OPTIONS_INIT; const git_transfer_progress *stats; struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 }; char *log_message = NULL; int error; VALUE rb_options, rb_refspecs, rb_result = Qnil; rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options); rugged_rb_ary_to_strarray(rb_refspecs, &refspecs); Data_Get_Struct(self, git_remote, remote); rugged_remote_init_callbacks_and_payload_from_options(rb_options, &opts.callbacks, &payload); init_custom_headers(rb_options, &opts.custom_headers); if (!NIL_P(rb_options)) { VALUE rb_prune_type; VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message")); if (!NIL_P(rb_val)) log_message = StringValueCStr(rb_val); rb_prune_type = rb_hash_aref(rb_options, CSTR2SYM("prune")); opts.prune = parse_prune_type(rb_prune_type); } error = git_remote_fetch(remote, &refspecs, &opts, log_message); xfree(refspecs.strings); git_strarray_free(&opts.custom_headers); if (payload.exception) rb_jump_tag(payload.exception); rugged_exception_check(error); stats = git_remote_stats(remote); rb_result = rb_hash_new(); rb_hash_aset(rb_result, CSTR2SYM("total_objects"), UINT2NUM(stats->total_objects)); rb_hash_aset(rb_result, CSTR2SYM("indexed_objects"), UINT2NUM(stats->indexed_objects)); rb_hash_aset(rb_result, CSTR2SYM("received_objects"), UINT2NUM(stats->received_objects)); rb_hash_aset(rb_result, CSTR2SYM("local_objects"), UINT2NUM(stats->local_objects)); rb_hash_aset(rb_result, CSTR2SYM("total_deltas"), UINT2NUM(stats->total_deltas)); rb_hash_aset(rb_result, CSTR2SYM("indexed_deltas"), UINT2NUM(stats->indexed_deltas)); rb_hash_aset(rb_result, CSTR2SYM("received_bytes"), INT2FIX(stats->received_bytes)); return rb_result; } /* * call-seq: * remote.push(refspecs = nil, options = {}) -> hash * * Pushes the given +refspecs+ to the given +remote+. Returns a hash that contains * key-value pairs that reflect pushed refs and error messages, if applicable. * * You can optionally pass in an alternative list of +refspecs+ to use instead of the push * refspecs already configured for +remote+. * * The following options can be passed in the +options+ Hash: * * :credentials :: * The credentials to use for the push operation. Can be either an instance of one * of the Rugged::Credentials types, or a proc returning one of the former. * The proc will be called with the +url+, the +username+ from the url (if applicable) and * a list of applicable credential types. * * :update_tips :: * A callback that will be executed each time a reference is updated remotely. It will be * passed the +refname+, +old_oid+ and +new_oid+. * * :headers :: * Extra HTTP headers to include with the push (only applies to http:// or https:// remotes) * * Example: * * remote = Rugged::Remote.lookup(@repo, 'origin') * remote.push(["refs/heads/master", ":refs/heads/to_be_deleted"]) */ static VALUE rb_git_remote_push(int argc, VALUE *argv, VALUE self) { VALUE rb_refspecs, rb_options; git_remote *remote; git_strarray refspecs; git_push_options opts = GIT_PUSH_OPTIONS_INIT; int error = 0; struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, rb_hash_new(), 0 }; rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options); rugged_rb_ary_to_strarray(rb_refspecs, &refspecs); Data_Get_Struct(self, git_remote, remote); rugged_remote_init_callbacks_and_payload_from_options(rb_options, &opts.callbacks, &payload); init_custom_headers(rb_options, &opts.custom_headers); error = git_remote_push(remote, &refspecs, &opts); xfree(refspecs.strings); git_strarray_free(&opts.custom_headers); if (payload.exception) rb_jump_tag(payload.exception); rugged_exception_check(error); return payload.result; } void Init_rugged_remote(void) { rb_cRuggedRemote = rb_define_class_under(rb_mRugged, "Remote", rb_cObject); rb_define_method(rb_cRuggedRemote, "name", rb_git_remote_name, 0); rb_define_method(rb_cRuggedRemote, "url", rb_git_remote_url, 0); rb_define_method(rb_cRuggedRemote, "push_url", rb_git_remote_push_url, 0); rb_define_method(rb_cRuggedRemote, "push_url=", rb_git_remote_set_push_url, 1); rb_define_method(rb_cRuggedRemote, "fetch_refspecs", rb_git_remote_fetch_refspecs, 0); rb_define_method(rb_cRuggedRemote, "push_refspecs", rb_git_remote_push_refspecs, 0); rb_define_method(rb_cRuggedRemote, "ls", rb_git_remote_ls, -1); rb_define_method(rb_cRuggedRemote, "check_connection", rb_git_remote_check_connection, -1); rb_define_method(rb_cRuggedRemote, "fetch", rb_git_remote_fetch, -1); rb_define_method(rb_cRuggedRemote, "push", rb_git_remote_push, -1); } rugged-0.26.0/ext/rugged/rugged_remote_collection.c0000644000175000017500000003054613147033070022474 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; extern VALUE rb_cRuggedRepo; extern VALUE rb_eRuggedError; extern VALUE rb_cRuggedRemote; VALUE rb_cRuggedRemoteCollection; /* * call-seq: * RemoteCollection.new(repo) -> remotes * * Creates and returns a new collection of remotes for the given +repo+. */ static VALUE rb_git_remote_collection_initialize(VALUE self, VALUE repo) { rugged_set_owner(self, repo); return self; } /* * call-seq: * remotes.create_anonymous(url) -> remote * * Return a new remote with +url+ in +repository+ , the remote is not persisted: * - +url+: a valid remote url * * Returns a new Rugged::Remote object. * * @repo.remotes.create_anonymous('git://github.com/libgit2/libgit2.git') #=> # */ static VALUE rb_git_remote_collection_create_anonymous(VALUE self, VALUE rb_url) { git_remote *remote; git_repository *repo; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_url, T_STRING); error = git_remote_create_anonymous( &remote, repo, StringValueCStr(rb_url)); rugged_exception_check(error); return rugged_remote_new(rb_repo, remote); } /* * call-seq: * remotes.create(name, url) -> remote * * Add a new remote with +name+ and +url+ to +repository+ * - +url+: a valid remote url * - +name+: a valid remote name * * Returns a new Rugged::Remote object. * * @repo.remotes.create('origin', 'git://github.com/libgit2/rugged.git') #=> # */ static VALUE rb_git_remote_collection_create(VALUE self, VALUE rb_name, VALUE rb_url) { git_remote *remote; git_repository *repo; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); Check_Type(rb_url, T_STRING); error = git_remote_create( &remote, repo, StringValueCStr(rb_name), StringValueCStr(rb_url)); rugged_exception_check(error); return rugged_remote_new(rb_repo, remote); } /* * call-seq: * remotes[name] -> remote or nil * * Lookup a remote in the collection with the given +name+. * * Returns a new Rugged::Remote object or +nil+ if the * remote doesn't exist. * * @repo.remotes["origin"] #=> # */ static VALUE rb_git_remote_collection_aref(VALUE self, VALUE rb_name) { git_remote *remote; git_repository *repo; int error; VALUE rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_name, T_STRING); error = git_remote_lookup(&remote, repo, StringValueCStr(rb_name)); if (error == GIT_ENOTFOUND) return Qnil; rugged_exception_check(error); return rugged_remote_new(rb_repo, remote); } static VALUE rb_git_remote_collection__each(VALUE self, int only_names) { git_repository *repo; git_strarray remotes; size_t i; int error = 0; int exception = 0; VALUE rb_repo; if (!rb_block_given_p()) { if (only_names) return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_name")); else return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each")); } rb_repo = rugged_owner(self); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_remote_list(&remotes, repo); rugged_exception_check(error); if (only_names) { for (i = 0; !exception && i < remotes.count; ++i) { rb_protect(rb_yield, rb_str_new_utf8(remotes.strings[i]), &exception); } } else { for (i = 0; !exception && !error && i < remotes.count; ++i) { git_remote *remote; if (!(error = git_remote_lookup(&remote, repo, remotes.strings[i]))) rb_protect(rb_yield, rugged_remote_new(rb_repo, remote), &exception); } } git_strarray_free(&remotes); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return Qnil; } /* * call-seq: * remotes.each { |remote| } -> nil * remotes.each -> enumerator * * Iterate through all the remotes in the collection's +repository+. * * The given block will be called once with a Rugged::Remote * instance for each remote. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_remote_collection_each(VALUE self) { return rb_git_remote_collection__each(self, 0); } /* * call-seq: * remotes.each_name { |str| } -> nil * remotes.each_name -> enumerator * * Iterate through all the remote names in the collection's +repository+. * * The given block will be called once with the name of each remote. * * If no block is given, an enumerator will be returned. */ static VALUE rb_git_remote_collection_each_name(VALUE self) { return rb_git_remote_collection__each(self, 1); } /* * call-seq: * remotes.rename(remote, new_name) { |str| } -> remote * remotes.rename(name, new_name) { |str| } -> remote * * Renames a remote. * * All remote-tracking branches and configuration settings * for the remote are updated. * * Non-default refspecs cannot be renamed automatically and will be * yielded to the given block. * * Anonymous, in-memory remotes created through * +ReferenceCollection#create_anonymous+ can not be given a name through * this method. * * Returns a new Rugged::Remote object with the new name. */ static VALUE rb_git_remote_collection_rename(VALUE self, VALUE rb_name_or_remote, VALUE rb_new_name) { VALUE rb_repo = rugged_owner(self); git_repository *repo; size_t i; int error, exception; git_strarray problems; if (!rb_block_given_p()) rb_raise(rb_eArgError, "Rugged::RemoteCollection#rename must be called with a block"); Check_Type(rb_new_name, T_STRING); if (rb_obj_is_kind_of(rb_name_or_remote, rb_cRuggedRemote)) rb_name_or_remote = rb_funcall(rb_name_or_remote, rb_intern("name"), 0); if (TYPE(rb_name_or_remote) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Remote instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); error = git_remote_rename(&problems, repo, StringValueCStr(rb_name_or_remote), StringValueCStr(rb_new_name)); rugged_exception_check(error); for (i = exception = 0; !exception && i < problems.count; ++i) { rb_protect(rb_yield, rb_str_new_utf8(problems.strings[i]), &exception); } git_strarray_free(&problems); if (exception) rb_jump_tag(exception); return rb_git_remote_collection_aref(self, rb_new_name); } /* * call-seq: * remotes.delete(remote) -> nil * remotes.delete(name) -> nil * * Delete the specified remote. * * repo.remotes.delete("origin") * # Remote no longer exists in the configuration. */ static VALUE rb_git_remote_collection_delete(VALUE self, VALUE rb_name_or_remote) { VALUE rb_repo = rugged_owner(self); git_repository *repo; if (rb_obj_is_kind_of(rb_name_or_remote, rb_cRuggedRemote)) rb_name_or_remote = rb_funcall(rb_name_or_remote, rb_intern("name"), 0); if (TYPE(rb_name_or_remote) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Remote instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); rugged_exception_check( git_remote_delete(repo, StringValueCStr(rb_name_or_remote)) ); return Qnil; } /* * call-seq: * remotes.set_url(remote, url) -> nil * remotes.set_url(name, url) -> nil * * Sets the remote's url in the configuration. * Rugged::Remote objects already in memory will not be affected. * * repo.remotes.set_url("origin", 'git://github.com/libgit2/rugged.git') */ static VALUE rb_git_remote_collection_set_url(VALUE self, VALUE rb_name_or_remote, VALUE rb_url) { VALUE rb_repo = rugged_owner(self); git_repository *repo; if (rb_obj_is_kind_of(rb_name_or_remote, rb_cRuggedRemote)) rb_name_or_remote = rb_funcall(rb_name_or_remote, rb_intern("name"), 0); if (TYPE(rb_name_or_remote) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Remote instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_url, T_STRING); rugged_exception_check( git_remote_set_url(repo, StringValueCStr(rb_name_or_remote), StringValueCStr(rb_url)) ); return Qnil; } /* * call-seq: * remotes.set_push_url(remote, url) -> nil * remotes.set_push_url(name, url) -> nil * * Sets the remote's url for pushing in the configuration. * Rugged::Remote objects already in memory will not be affected. * * repo.remotes.set_push_url("origin", 'git://github.com/libgit2/rugged.git') */ static VALUE rb_git_remote_collection_set_push_url(VALUE self, VALUE rb_name_or_remote, VALUE rb_url) { VALUE rb_repo = rugged_owner(self); git_repository *repo; if (rb_obj_is_kind_of(rb_name_or_remote, rb_cRuggedRemote)) rb_name_or_remote = rb_funcall(rb_name_or_remote, rb_intern("name"), 0); if (TYPE(rb_name_or_remote) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Remote instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_url, T_STRING); rugged_exception_check( git_remote_set_pushurl(repo, StringValueCStr(rb_name_or_remote), StringValueCStr(rb_url)) ); return Qnil; } static VALUE rb_git_remote_collection_add_refspec(VALUE self, VALUE rb_name_or_remote, VALUE rb_refspec, git_direction direction) { VALUE rb_repo = rugged_owner(self); git_repository *repo; int error = 0; if (rb_obj_is_kind_of(rb_name_or_remote, rb_cRuggedRemote)) rb_name_or_remote = rb_funcall(rb_name_or_remote, rb_intern("name"), 0); if (TYPE(rb_name_or_remote) != T_STRING) rb_raise(rb_eTypeError, "Expecting a String or Rugged::Remote instance"); rugged_check_repo(rb_repo); Data_Get_Struct(rb_repo, git_repository, repo); Check_Type(rb_refspec, T_STRING); if (direction == GIT_DIRECTION_FETCH) error = git_remote_add_fetch(repo, StringValueCStr(rb_name_or_remote), StringValueCStr(rb_refspec)); else error = git_remote_add_push(repo, StringValueCStr(rb_name_or_remote), StringValueCStr(rb_refspec)); rugged_exception_check(error); return Qnil; } /* * call-seq: * remotes.add_fetch_refspec(remote, refspec) -> nil * remotes.add_fetch_refspec(name, refspec) -> nil * * Add a fetch refspec to the remote. */ static VALUE rb_git_remote_collection_add_fetch_refspec(VALUE self, VALUE rb_name_or_remote, VALUE rb_refspec) { return rb_git_remote_collection_add_refspec(self, rb_name_or_remote, rb_refspec, GIT_DIRECTION_FETCH); } /* * call-seq: * remotes.add_push_refspec(remote, refspec) -> nil * remotes.add_push_refspec(name, refspec) -> nil * * Add a push refspec to the remote. */ static VALUE rb_git_remote_collection_add_push_refspec(VALUE self, VALUE rb_name_or_remote, VALUE rb_refspec) { return rb_git_remote_collection_add_refspec(self, rb_name_or_remote, rb_refspec, GIT_DIRECTION_PUSH); } void Init_rugged_remote_collection(void) { rb_cRuggedRemoteCollection = rb_define_class_under(rb_mRugged, "RemoteCollection", rb_cObject); rb_include_module(rb_cRuggedRemoteCollection, rb_mEnumerable); rb_define_method(rb_cRuggedRemoteCollection, "initialize", rb_git_remote_collection_initialize, 1); rb_define_method(rb_cRuggedRemoteCollection, "[]", rb_git_remote_collection_aref, 1); rb_define_method(rb_cRuggedRemoteCollection, "create", rb_git_remote_collection_create, 2); rb_define_method(rb_cRuggedRemoteCollection, "create_anonymous", rb_git_remote_collection_create_anonymous, 1); rb_define_method(rb_cRuggedRemoteCollection, "each", rb_git_remote_collection_each, 0); rb_define_method(rb_cRuggedRemoteCollection, "each_name", rb_git_remote_collection_each_name, 0); rb_define_method(rb_cRuggedRemoteCollection, "set_url", rb_git_remote_collection_set_url, 2); rb_define_method(rb_cRuggedRemoteCollection, "set_push_url", rb_git_remote_collection_set_push_url, 2); rb_define_method(rb_cRuggedRemoteCollection, "add_push_refspec", rb_git_remote_collection_add_push_refspec, 2); rb_define_method(rb_cRuggedRemoteCollection, "add_fetch_refspec", rb_git_remote_collection_add_fetch_refspec, 2); rb_define_method(rb_cRuggedRemoteCollection, "rename", rb_git_remote_collection_rename, 2); rb_define_method(rb_cRuggedRemoteCollection, "delete", rb_git_remote_collection_delete, 1); } rugged-0.26.0/ext/rugged/extconf.rb0000644000175000017500000000727213147033070017260 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. require 'mkmf' RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC'] $CFLAGS << " #{ENV["CFLAGS"]}" $CFLAGS << " -g" $CFLAGS << " -O3" unless $CFLAGS[/-O\d/] $CFLAGS << " -Wall -Wno-comment" cmake_flags = [ ENV["CMAKE_FLAGS"] ] cmake_flags << "-DUSE_SHA1DC=ON" if with_config("sha1dc") def sys(cmd) puts " -- #{cmd}" unless ret = xsystem(cmd) raise "ERROR: '#{cmd}' failed" end ret end MAKE = if Gem.win_platform? # On Windows, Ruby-DevKit only has 'make'. find_executable('make') else find_executable('gmake') || find_executable('make') end if !MAKE abort "ERROR: GNU make is required to build Rugged." end CWD = File.expand_path(File.dirname(__FILE__)) LIBGIT2_DIR = File.join(CWD, '..', '..', 'vendor', 'libgit2') if arg_config("--use-system-libraries", !!ENV['RUGGED_USE_SYSTEM_LIBRARIES']) puts "Building Rugged using system libraries.\n" dir_config('git2').any? or pkg_config('libgit2') major = minor = nil File.readlines(File.join(LIBGIT2_DIR, "include", "git2", "version.h")).each do |line| if !major && (matches = line.match(/^#define LIBGIT2_VER_MAJOR ([0-9]+)$/)) major = matches[1] next end if !minor && (matches = line.match(/^#define LIBGIT2_VER_MINOR ([0-9]+)$/)) minor = matches[1] next end break if major && minor end try_compile(<<-SRC) or abort "libgit2 version is not compatible, expected ~> #{major}.#{minor}.0" #include #if LIBGIT2_VER_MAJOR != #{major} || LIBGIT2_VER_MINOR != #{minor} #error libgit2 version is not compatible #endif SRC else if !find_executable('cmake') abort "ERROR: CMake is required to build Rugged." end if !Gem.win_platform? && !find_executable('pkg-config') abort "ERROR: pkg-config is required to build Rugged." end Dir.chdir(LIBGIT2_DIR) do Dir.mkdir("build") if !Dir.exists?("build") Dir.chdir("build") do # On Windows, Ruby-DevKit is MSYS-based, so ensure to use MSYS Makefiles. generator = "-G \"MSYS Makefiles\"" if Gem.win_platform? sys("cmake .. -DBUILD_CLAR=OFF -DTHREADSAFE=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS=-fPIC -DCMAKE_BUILD_TYPE=RelWithDebInfo #{cmake_flags.join(' ')} #{generator}") sys(MAKE) # "normal" libraries (and libgit2 builds) get all these when they build but we're doing it # statically so we put the libraries in by hand. It's important that we put the libraries themselves # in $LIBS or the final linking stage won't pick them up if Gem.win_platform? $LDFLAGS << " " + "-L#{Dir.pwd}/deps/winhttp" $LIBS << " -lwinhttp -lcrypt32 -lrpcrt4 -lole32 -lz" else pcfile = File.join(LIBGIT2_DIR, "build", "libgit2.pc") $LDFLAGS << " " + `pkg-config --libs --static #{pcfile}`.strip end end end # Prepend the vendored libgit2 build dir to the $DEFLIBPATH. # # By default, $DEFLIBPATH includes $(libpath), which usually points # to something like /usr/lib for system ruby versions (not those # installed through rbenv or rvm). # # This was causing system-wide libgit2 installations to be preferred # over of our vendored libgit2 version when building rugged. # # By putting the path to the vendored libgit2 library at the front of # $DEFLIBPATH, we can ensure that our bundled version is always used. $DEFLIBPATH.unshift("#{LIBGIT2_DIR}/build") dir_config('git2', "#{LIBGIT2_DIR}/include", "#{LIBGIT2_DIR}/build") end unless have_library 'git2' and have_header 'git2.h' abort "ERROR: Failed to build libgit2" end create_makefile("rugged/rugged") rugged-0.26.0/ext/rugged/rugged_diff.c0000644000175000017500000004341413147033070017674 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #include "rugged.h" extern VALUE rb_mRugged; VALUE rb_cRuggedDiff; VALUE rugged_diff_new(VALUE klass, VALUE owner, git_diff *diff) { VALUE rb_diff = Data_Wrap_Struct(klass, NULL, git_diff_free, diff); rugged_set_owner(rb_diff, owner); return rb_diff; } /** * The caller has to free the returned git_diff_options pathspec strings array. */ void rugged_parse_diff_options(git_diff_options *opts, VALUE rb_options) { if (!NIL_P(rb_options)) { VALUE rb_value; Check_Type(rb_options, T_HASH); if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("reverse")))) { opts->flags |= GIT_DIFF_REVERSE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("include_ignored")))) { opts->flags |= GIT_DIFF_INCLUDE_IGNORED; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("recurse_ignored_dirs")))) { opts->flags |= GIT_DIFF_RECURSE_IGNORED_DIRS; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("include_untracked")))) { opts->flags |= GIT_DIFF_INCLUDE_UNTRACKED; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("recurse_untracked_dirs")))) { opts->flags |= GIT_DIFF_RECURSE_UNTRACKED_DIRS; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("include_unmodified")))) { opts->flags |= GIT_DIFF_INCLUDE_UNMODIFIED; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("include_typechange")))) { opts->flags |= GIT_DIFF_INCLUDE_TYPECHANGE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("include_typechange_trees")))) { opts->flags |= GIT_DIFF_INCLUDE_TYPECHANGE_TREES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_filemode")))) { opts->flags |= GIT_DIFF_IGNORE_FILEMODE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_submodules")))) { opts->flags |= GIT_DIFF_IGNORE_SUBMODULES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_case")))) { opts->flags |= GIT_DIFF_IGNORE_CASE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("disable_pathspec_match")))) { opts->flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("skip_binary_check")))) { opts->flags |= GIT_DIFF_SKIP_BINARY_CHECK; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("fast_untracked_dirs")))) { opts->flags |= GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("force_text")))) { opts->flags |= GIT_DIFF_FORCE_TEXT; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("force_binary")))) { opts->flags |= GIT_DIFF_FORCE_BINARY; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_whitespace")))) { opts->flags |= GIT_DIFF_IGNORE_WHITESPACE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_whitespace_change")))) { opts->flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_whitespace_eol")))) { opts->flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("show_untracked_content")))) { opts->flags |= GIT_DIFF_SHOW_UNTRACKED_CONTENT; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("show_unmodified")))) { opts->flags |= GIT_DIFF_SHOW_UNMODIFIED; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("show_binary")))) { opts->flags |= GIT_DIFF_SHOW_BINARY; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("patience")))) { opts->flags |= GIT_DIFF_PATIENCE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("minimal")))) { opts->flags |= GIT_DIFF_MINIMAL; } rb_value = rb_hash_aref(rb_options, CSTR2SYM("paths")); if (!NIL_P(rb_value)) { int i; Check_Type(rb_value, T_ARRAY); for (i = 0; i < RARRAY_LEN(rb_value); ++i) Check_Type(rb_ary_entry(rb_value, i), T_STRING); opts->pathspec.count = RARRAY_LEN(rb_value); opts->pathspec.strings = xmalloc(opts->pathspec.count * sizeof(char *)); for (i = 0; i < RARRAY_LEN(rb_value); ++i) { VALUE rb_path = rb_ary_entry(rb_value, i); opts->pathspec.strings[i] = StringValueCStr(rb_path); } } rb_value = rb_hash_aref(rb_options, CSTR2SYM("context_lines")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->context_lines = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("interhunk_lines")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->interhunk_lines = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("id_abbrev")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->id_abbrev = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("max_size")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts->max_size = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("old_prefix")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); opts->old_prefix = StringValueCStr(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("new_prefix")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_STRING); opts->new_prefix = StringValueCStr(rb_value); } } } static int diff_print_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { VALUE rb_str = (VALUE)payload; switch (line->origin) { case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_ADDITION: case GIT_DIFF_LINE_DELETION: rb_str_cat(rb_str, &line->origin, 1); } rb_str_cat(rb_str, line->content, line->content_len); return GIT_OK; } /* * call-seq: * diff.patch -> patch * diff.patch(:compact => true) -> compact_patch * * Return a string containing the diff in patch form. */ static VALUE rb_git_diff_patch(int argc, VALUE *argv, VALUE self) { git_diff *diff; VALUE rb_str = rb_str_new(NULL, 0); VALUE rb_opts; rb_scan_args(argc, argv, "00:", &rb_opts); Data_Get_Struct(self, git_diff, diff); if (!NIL_P(rb_opts)) { if (rb_hash_aref(rb_opts, CSTR2SYM("compact")) == Qtrue) git_diff_print(diff, GIT_DIFF_FORMAT_NAME_STATUS, diff_print_cb, (void*)rb_str); else git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, (void*)rb_str); } else { git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, (void*)rb_str); } return rb_str; } static int diff_write_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { VALUE rb_io = (VALUE)payload, str = rb_str_new(line->content, line->content_len); rb_io_write(rb_io, str); return GIT_OK; } /* * call-seq: * diff.write_patch(io) -> nil * diff.write_patch(io, :compact => true) -> nil * * Write a patch directly to an object which responds to "write". */ static VALUE rb_git_diff_write_patch(int argc, VALUE *argv, VALUE self) { git_diff *diff; VALUE rb_io, rb_opts; rb_scan_args(argc, argv, "10:", &rb_io, &rb_opts); if (!rb_respond_to(rb_io, rb_intern("write"))) rb_raise(rb_eArgError, "Expected io to respond to \"write\""); Data_Get_Struct(self, git_diff, diff); if (!NIL_P(rb_opts)) { if (rb_hash_aref(rb_opts, CSTR2SYM("compact")) == Qtrue) git_diff_print(diff, GIT_DIFF_FORMAT_NAME_STATUS, diff_write_cb, (void*)rb_io); else git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_write_cb, (void*)rb_io); } else { git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_write_cb, (void*)rb_io); } return Qnil; } /* * call-seq: * diff.merge!(other_diff) -> self * * Merges all diff information from +other_diff+. */ static VALUE rb_git_diff_merge(VALUE self, VALUE rb_other) { git_diff *diff; git_diff *other; int error; if (!rb_obj_is_kind_of(rb_other, rb_cRuggedDiff)) rb_raise(rb_eTypeError, "A Rugged::Diff instance is required"); Data_Get_Struct(self, git_diff, diff); Data_Get_Struct(rb_other, git_diff, other); error = git_diff_merge(diff, other); rugged_exception_check(error); return self; } /* * call-seq: * diff.find_similar!([options]) -> self * * Detects entries in the diff that look like renames or copies (based on the * given options) and replaces them with actual rename or copy entries. * * Additionally, modified files can be broken into add/delete pairs if the * amount of changes are above a specific threshold (see +:break_rewrite_threshold+). * * By default, similarity will be measured without leading whitespace. You * you can use the +:dont_ignore_whitespace+ to disable this. * * The following options can be passed in the +options+ Hash: * * :rename_threshold :: * An integer specifying the similarity to consider a file renamed (default 50). * * :rename_from_rewrite_threshold :: * An integer specifying the similarity of modified to be eligible * rename source (default 50). * * :copy_threshold :: * An integer specifying the similarity to consider a file a copy (default 50). * * :break_rewrite_threshold :: * An integer specifying the similarity to split modify into delete/add pair (default 60). * * :rename_limit :: * An integer specifying the maximum amount of similarity sources to examine * (a la diff's +-l+ option or the +diff.renameLimit+ config) (default 200). * * :renames :: * If true, looking for renames will be enabled (+--find-renames+). * * :renames_from_rewrites :: * If true, the "old side" of modified files will be considered for renames (+--break-rewrites=N+). * * :copies :: * If true, looking for copies will be enabled (+--find-copies+). * * :copies_from_unmodified :: * If true, unmodified files will be considered as copy sources (+--find-copies-harder+). * * :break_rewrites :: * If true, larger rewrites will be split into delete/add pairs (+--break-rewrites=/M+). * * :all :: * If true, enables all finding features. * * :ignore_whitespace :: * If true, similarity will be measured with all whitespace ignored. * * :dont_ignore_whitespace :: * If true, similarity will be measured without ignoring any whitespace. * */ static VALUE rb_git_diff_find_similar(int argc, VALUE *argv, VALUE self) { git_diff *diff; git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; VALUE rb_options; int error; Data_Get_Struct(self, git_diff, diff); rb_scan_args(argc, argv, "00:", &rb_options); if (!NIL_P(rb_options)) { VALUE rb_value = rb_hash_aref(rb_options, CSTR2SYM("rename_threshold")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts.rename_threshold = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("rename_from_rewrite_threshold")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts.rename_from_rewrite_threshold = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("copy_threshold")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts.copy_threshold = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("break_rewrite_threshold")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts.break_rewrite_threshold = FIX2INT(rb_value); } rb_value = rb_hash_aref(rb_options, CSTR2SYM("rename_limit")); if (!NIL_P(rb_value)) { Check_Type(rb_value, T_FIXNUM); opts.rename_limit = FIX2INT(rb_value); } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("renames")))) { opts.flags |= GIT_DIFF_FIND_RENAMES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("renames_from_rewrites")))) { opts.flags |= GIT_DIFF_FIND_RENAMES_FROM_REWRITES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("copies")))) { opts.flags |= GIT_DIFF_FIND_COPIES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("copies_from_unmodified")))) { opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("break_rewrites")))) { opts.flags |= GIT_DIFF_FIND_AND_BREAK_REWRITES; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("all")))) { opts.flags |= GIT_DIFF_FIND_ALL; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("ignore_whitespace")))) { opts.flags |= GIT_DIFF_FIND_IGNORE_WHITESPACE; } if (RTEST(rb_hash_aref(rb_options, CSTR2SYM("dont_ignore_whitespace")))) { opts.flags |= GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE; } } error = git_diff_find_similar(diff, &opts); rugged_exception_check(error); return self; } /* * call-seq: * diff.each_patch { |patch| } -> self * diff.each_patch -> enumerator * * If given a block, yields each patch that is part of the diff. * If no block is given, an enumerator will be returned. */ static VALUE rb_git_diff_each_patch(VALUE self) { git_diff *diff; git_patch *patch; int error = 0; size_t d, delta_count; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_patch"), self); } Data_Get_Struct(self, git_diff, diff); delta_count = git_diff_num_deltas(diff); for (d = 0; d < delta_count; ++d) { error = git_patch_from_diff(&patch, diff, d); if (error) break; rb_yield(rugged_patch_new(self, patch)); } rugged_exception_check(error); return self; } /* * call-seq: * diff.each_delta { |delta| } -> self * diff.each_delta -> enumerator * * If given a block, yields each delta that is part of the diff. * If no block is given, an enumerator will be returned. * * This method should be preferred over #each_patch if you're not interested * in the actual line-by-line changes of the diff. */ static VALUE rb_git_diff_each_delta(VALUE self) { git_diff *diff; const git_diff_delta *delta; int error = 0; size_t d, delta_count; if (!rb_block_given_p()) { return rb_funcall(self, rb_intern("to_enum"), 1, CSTR2SYM("each_delta"), self); } Data_Get_Struct(self, git_diff, diff); delta_count = git_diff_num_deltas(diff); for (d = 0; d < delta_count; ++d) { delta = git_diff_get_delta(diff, d); rb_yield(rugged_diff_delta_new(self, delta)); } rugged_exception_check(error); return self; } static int each_line_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { int *exception = (int *)payload; rb_protect(rb_yield, rugged_diff_line_new(line), exception); return *exception ? GIT_ERROR : GIT_OK; } /* * call-seq: * diff.each_line([format = :patch]) { |line| } -> self * diff.each_line([format = :patch]) -> enumerator */ static VALUE rb_git_diff_each_line(int argc, VALUE *argv, VALUE self) { VALUE rb_format; git_diff *diff; git_diff_format_t format; int exception = 0, error; Data_Get_Struct(self, git_diff, diff); if (rb_scan_args(argc, argv, "01", &rb_format) == 1) { Check_Type(rb_format, T_SYMBOL); } else { rb_format = CSTR2SYM("patch"); } if (!rb_block_given_p()) return rb_funcall(self, rb_intern("to_enum"), 2, CSTR2SYM("each_line"), rb_format); if (SYM2ID(rb_format) == rb_intern("patch")) { format = GIT_DIFF_FORMAT_PATCH; } else if (SYM2ID(rb_format) == rb_intern("patch_header")) { format = GIT_DIFF_FORMAT_PATCH_HEADER; } else if (SYM2ID(rb_format) == rb_intern("raw")) { format = GIT_DIFF_FORMAT_RAW; } else if (SYM2ID(rb_format) == rb_intern("name_only")) { format = GIT_DIFF_FORMAT_NAME_ONLY; } else if (SYM2ID(rb_format) == rb_intern("name_status")) { format = GIT_DIFF_FORMAT_NAME_STATUS; } else { rb_raise(rb_eArgError, "unknown :format"); } error = git_diff_print(diff, format, each_line_cb, &exception); if (exception) rb_jump_tag(exception); rugged_exception_check(error); return self; } /* * call-seq: diff.size -> int * * Returns the number of deltas/patches in this diff. */ static VALUE rb_git_diff_size(VALUE self) { git_diff *diff; Data_Get_Struct(self, git_diff, diff); return INT2FIX(git_diff_num_deltas(diff)); } struct diff_stats { size_t files, adds, dels; }; static int diff_file_stats_cb( const git_diff_delta *delta, float progress, void *payload) { struct diff_stats *stats = payload; switch (delta->status) { case GIT_DELTA_ADDED: case GIT_DELTA_DELETED: case GIT_DELTA_MODIFIED: case GIT_DELTA_RENAMED: case GIT_DELTA_COPIED: case GIT_DELTA_TYPECHANGE: stats->files++; break; default: /* unmodified, ignored, and untracked files don't count */ break; } return GIT_OK; } static int diff_line_stats_cb( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, void *payload) { struct diff_stats *stats = payload; switch (line->origin) { case GIT_DIFF_LINE_ADDITION: stats->adds++; break; case GIT_DIFF_LINE_DELETION: stats->dels++; break; default: break; } return GIT_OK; } /* * call-seq: diff.stat -> int, int, int * * Returns the number of files/additions/deletions in this diff. */ static VALUE rb_git_diff_stat(VALUE self) { git_diff *diff; struct diff_stats stats = { 0, 0, 0 }; Data_Get_Struct(self, git_diff, diff); git_diff_foreach( diff, diff_file_stats_cb, NULL, NULL, diff_line_stats_cb, &stats); return rb_ary_new3( 3, INT2FIX(stats.files), INT2FIX(stats.adds), INT2FIX(stats.dels)); } /* * call-seq: diff.sorted_icase? * * Returns true when deltas are sorted case insensitively. */ static VALUE rb_git_diff_sorted_icase_p(VALUE self) { git_diff *diff; Data_Get_Struct(self, git_diff, diff); return git_diff_is_sorted_icase(diff) ? Qtrue : Qfalse; } void Init_rugged_diff(void) { rb_cRuggedDiff = rb_define_class_under(rb_mRugged, "Diff", rb_cObject); rb_define_method(rb_cRuggedDiff, "patch", rb_git_diff_patch, -1); rb_define_method(rb_cRuggedDiff, "write_patch", rb_git_diff_write_patch, -1); rb_define_method(rb_cRuggedDiff, "find_similar!", rb_git_diff_find_similar, -1); rb_define_method(rb_cRuggedDiff, "merge!", rb_git_diff_merge, 1); rb_define_method(rb_cRuggedDiff, "size", rb_git_diff_size, 0); rb_define_method(rb_cRuggedDiff, "stat", rb_git_diff_stat, 0); rb_define_method(rb_cRuggedDiff, "sorted_icase?", rb_git_diff_sorted_icase_p, 0); rb_define_method(rb_cRuggedDiff, "each_patch", rb_git_diff_each_patch, 0); rb_define_method(rb_cRuggedDiff, "each_delta", rb_git_diff_each_delta, 0); rb_define_method(rb_cRuggedDiff, "each_line", rb_git_diff_each_line, -1); } rugged-0.26.0/ext/rugged/rugged_submodule_collection.c0000644000175000017500000002416213147033070023175 0ustar abhijithabhijith/* * Copyright (C) the Rugged contributors. All rights reserved. * * This file is part of Rugged, distributed under the MIT license. * For full terms see the included LICENSE file. */ #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); } rugged-0.26.0/LICENSE0000644000175000017500000000206013147033070014203 0ustar abhijithabhijithThe MIT License Copyright (c) 2016 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. rugged-0.26.0/README.md0000644000175000017500000004346313147033070014471 0ustar abhijithabhijith# Rugged **libgit2 bindings in Ruby** Rugged is a library for accessing [libgit2](https://github.com/libgit2/libgit2) in Ruby. It gives you the speed and portability of libgit2 with the beauty of the Ruby language. ### libgit2 libgit2 is a pure C implementation of the Git core methods. It's designed to be fast and portable. For more information about libgit2, [check out libgit2's website](http://libgit2.github.com) or browse the [libgit2 organization](https://github.com/libgit2) on GitHub. ## Install Rugged is a self-contained gem. You can install it by running: $ gem install rugged You need to have CMake and `pkg-config` installed on your system to be able to build the included version of `libgit2`. On OS X, after installing [Homebrew](http://brew.sh/), you can get CMake with: ```bash $ brew install cmake ``` Please follow the above in case installation of the gem fails with `ERROR: CMake is required to build Rugged.`. If you want to build Rugged with HTTPS and SSH support, check out the list of optional [libgit2 dependencies](https://github.com/libgit2/libgit2#optional-dependencies). If you're using bundler and want to bundle `libgit2` with Rugged, you can use the `:submodules` option: ```ruby gem 'rugged', git: 'git://github.com/libgit2/rugged.git', submodules: true ``` To load Rugged, you'll usually want to add something like this: ```ruby require 'rugged' ``` ### Use the system provided libgit2 By default, Rugged builds and uses a bundled version of libgit2. If you want to use the system library instead, you can install rugged as follows: ``` gem install rugged -- --use-system-libraries ``` Or if you are using bundler: ``` bundle config build.rugged --use-system-libraries bundle install ``` However, note that Rugged does only support specific versions of libgit2. ## Usage Rugged gives you access to the many parts of a Git repository. You can read and write objects, walk a tree, access the staging area, and lots more. Let's look at each area individually. ### Repositories #### Instantiation The repository is naturally central to Git. Rugged has a `Repository` class that you can instantiate with a path to open an existing repository : ```ruby repo = Rugged::Repository.new('path/to/my/repository') # => # ``` You can create a new repository with `init_at`. Add a second parameter `:bare` to make a bare repository: ```ruby Rugged::Repository.init_at('.', :bare) ``` You can also let Rugged discover the path to the .git directory if you give it a subdirectory. ```ruby Rugged::Repository.discover("/Users/me/projects/repo/lib/subdir/") # => "/Users/me/projects/repo/.git/" ``` Once your Repository instantiated (in the following examples, as `repo`), you can access or modify it. #### Accessing a Repository ```ruby # Does the given SHA1 exist in this repository? repo.exists?('07b44cbda23b726e5d54e2ef383495922c024202') # => true # Boolean repository state values: repo.bare? # => false repo.empty? # => true repo.head_unborn? # => false repo.head_detached? # => false # Path accessors repo.path # => "path/to/my/repository/.git/" repo.workdir # => "path/to/my/repository/" # The HEAD of the repository. ref = repo.head # => #}> # From the returned ref, you can also access the `name`, `target`, and target SHA: ref.name # => "refs/heads/master" ref.target # => #}> ref.target_id # => "2bc6a70483369f33f641ca44873497f13a15cde5" # Reading an object object = repo.read('a0ae5566e3c8a3bddffab21022056f0b5e03ef07') # => # object.len # => 237 object.data # => "tree 76f23f186076fc291742816721ea8c3e95567241\nparent 8e3c5c52b8f29da0adc7e8be8a037cbeaea6de6b\nauthor Vicent Mart\303\255 1333859005 +0200\ncommitter Vicent Mart\303\255 1333859005 +0200\n\nAdd `Repository#blob_at`\n" object.type # => :commit ``` #### Writing to a Repository There's a few ways to write to a repository. To write directly from your instantiated repository object: ```ruby sha = repo.write(content, type) ``` You can also use the `Commit` object directly to craft a commit; this is a bit more high-level, so it may be preferable: ```ruby oid = repo.write("This is a blob.", :blob) index = repo.index index.read_tree(repo.head.target.tree) index.add(:path => "README.md", :oid => oid, :mode => 0100644) options = {} options[:tree] = index.write_tree(repo) options[:author] = { :email => "testuser@github.com", :name => 'Test Author', :time => Time.now } options[:committer] = { :email => "testuser@github.com", :name => 'Test Author', :time => Time.now } options[:message] ||= "Making a commit via Rugged!" options[:parents] = repo.empty? ? [] : [ repo.head.target ].compact options[:update_ref] = 'HEAD' Rugged::Commit.create(repo, options) ``` --- ### Objects `Object` is the main object class - it shouldn't be created directly, but all of these methods should be useful in their derived classes. ```ruby obj = repo.lookup(sha) obj.oid # object sha obj.type # One of :commit, :tree, :blob or :tag robj = obj.read_raw str = robj.data int = robj.len ``` There are four base object types in Git: **blobs**, **commits**, **tags**, and **trees**. Each of these object types have a corresponding class within Rugged. ### Commit Objects ```ruby commit = repo.lookup('a0ae5566e3c8a3bddffab21022056f0b5e03ef07') # => # commit.message # => "Add `Repository#blob_at`\n" commit.time # => Sat Apr 07 21:23:25 -0700 2012 commit.author # => {:email=>"tanoku@gmail.com", :name=>"Vicent Mart\303\255", :time=>Sun Apr 08 04:23:25 UTC 2012} commit.tree # => # commit.parents # => [#] ``` You can also write new objects to the database this way: ```ruby author = {:email=>"tanoku@gmail.com", :time=>Time.now, :name=>"Vicent Mart\303\255"} Rugged::Commit.create(r, :author => author, :message => "Hello world\n\n", :committer => author, :parents => ["2cb831a8aea28b2c1b9c63385585b864e4d3bad1"], :tree => some_tree, :update_ref => "HEAD") #=> "f148106ca58764adc93ad4e2d6b1d168422b9796" ``` ### Tag Objects ```ruby tag = repo.lookup(tag_sha) object = tag.target sha = tag.target.oid str = tag.target_type # :commit, :tag, :blob str = tag.name # "v1.0" str = tag.message person = tag.tagger ``` ### Tree Objects ```ruby tree = repo.lookup('779fbb1e17e666832773a9825875300ea736c2da') # => # # number of tree entries tree.count tree[0] # or... tree.first # or... tree.get_entry(0) # => {:type=>:blob, :oid=>"99e7edb53db9355f10c6f2dfaa5a183f205d93bf", :filemode=>33188, :name=>".gitignore"} ``` The tree object is an Enumerable, so you can also do stuff like this: ```ruby tree.each { |e| puts e[:oid] } tree.sort { |a, b| a[:oid] <=> b[:oid] }.map { |e| e[:name] }.join(':') ``` And there are some Rugged-specific methods, too: ```ruby tree.each_tree { |entry| puts entry[:name] } # list subdirs tree.each_blob { |entry| puts entry[:name] } # list only files ``` You can also write trees with the `TreeBuilder`: ```ruby oid = repo.write("This is a blob.", :blob) builder = Rugged::Tree::Builder.new(repo) builder << { :type => :blob, :name => "README.md", :oid => oid, :filemode => 0100644 } options = {} options[:tree] = builder.write options[:author] = { :email => "testuser@github.com", :name => 'Test Author', :time => Time.now } options[:committer] = { :email => "testuser@github.com", :name => 'Test Author', :time => Time.now } options[:message] ||= "Making a commit via Rugged!" options[:parents] = repo.empty? ? [] : [ repo.head.target ].compact options[:update_ref] = 'HEAD' Rugged::Commit.create(repo, options) ``` ### Blob Objects Blob objects represent the data in the files of a Tree Object. ```ruby blob = repo.lookup('e1253910439ea902cf49be8a9f02f3c08d89ac73') blob.content # => Gives you the content of the blob. ``` #### Streaming Blob Objects There is currently no way to stream data from a blob, because `libgit2` itself does not (yet) support streaming blobs out of the git object database. While there are hooks and interfaces for supporting it, the default file system backend always loads the entire blob contents into memory. If you need to access a Blob object through an IO-like API, you can wrap it with the `StringIO` class. Note that the only advantage here is a stream-compatible interface, the complete blob object will still be loaded into memory. Below is an example for streaming a Blob using the Sinatra framework: ```ruby # Sinatra endpoint get "/blobs/:sha" do repo = Rugged::Repository.new(my_repo_path) blob = repo.lookup params[:sha] headers({ "Vary" => "Accept", "Connection" => "keep-alive", "Transfer-Encoding" => "chunked", "Content-Type" => "application/octet-stream", }) stream do |out| StringIO.new(blob.content).each(8000) do |chunk| out << chunk end end end ``` --- ### Commit Walker `Rugged::Walker` is a class designed to help you traverse a set of commits over a repository. You first push head SHAs onto the walker, and then call next to get a list of the reachable commit objects one at a time. You can also `hide()` commits if you are not interested in anything beneath them (useful in situations like when you're running something like `git log master ^origin/master`). ```ruby walker = Rugged::Walker.new(repo) walker.sorting(Rugged::SORT_TOPO | Rugged::SORT_REVERSE) # optional walker.push(hex_sha_interesting) walker.hide(hex_sha_uninteresting) walker.each { |c| puts c.inspect } walker.reset ``` --- ### Index ("staging") area We can inspect and manipulate the Git Index as well. To work with the index inside an existing repository, instantiate it by using the `Repository.index` method instead of manually opening the Index by its path. ```ruby index = Rugged::Index.new(path) # Re-read the index file from disk. index.reload # Count up index entries. count = index.count # The collection of index entries. index.entries # Iterating over index entries. index.each { |i| puts i.inspect } # Get a particular entry in the index. index[path] # Unstage. index.remove(path) # Stage. Also updates existing entry if there is one. index.add(ientry) # Stage. Create ientry from file in path, updates the index. index.add(path) ``` --- ### Refs You can access references through the `Rugged::ReferenceCollection` object returned by `Repository#references`. ```ruby ref = repo.references["refs/heads/master"] sha = ref.target_id str = ref.type # :direct str = ref.name # "refs/heads/master" ``` You can also easily iterate over all references: ```ruby repo.references.each do |ref| puts ref.name end ``` Or only over references that match the given pattern (glob): ```ruby repo.references.each("refs/tags/*") do |ref| puts ref.name end ``` It is also easy to create, update, rename or delete a reference: ```ruby ref = repo.references.create("refs/heads/unit_test", some_commit_sha) repo.references.update(ref, new_sha) # or... repo.references.update("refs/heads/unit_test", new_sha) repo.references.rename(ref, "refs/heads/blead") # or... repo.references.rename("refs/heads/unit_test", "refs/heads/blead") repo.references.delete(ref) # or... repo.references.delete("refs/heads/unit_test") # or... ``` Finally, you can access the reflog for any branch: ```ruby ref = repo.references["refs/heads/master"] entry = ref.log.first sha = entry[:id_old] sha = entry[:id_new] str = entry[:message] prsn = entry[:committer] ``` --- ### Branches The `Rugged::BranchCollection` object returned by `Repository#branches` will help you with all of your branch-related needs. Iterate over all branches: ```ruby repo.branches.each_name().sort # => ["master", "origin/HEAD", "origin/master", "origin/packed"] repo.branches.each_name(:local).sort # => ["master"] repo.branches.each_name(:remote).sort # => ["origin/HEAD", "origin/master", "origin/packed"] ``` Look up branches and get attributes: ```ruby branch = repo.branches["master"] branch.name # => 'master' branch.canonical_name # => 'refs/heads/master' ``` Look up the id for the target of a branch: ```ruby repo.branches["master"].target_id # => "36060c58702ed4c2a40832c51758d5344201d89a" ``` Creation and deletion: ```ruby branch = repo.branches.create("test_branch", "HEAD") repo.branches.rename("test_branch", "new_branch") # or... repo.branches.rename("refs/heads/test_branch", "new_branch") # or... repo.branches.rename(ref, "new_branch") # or... repo.branches.delete("test_branch") # or... repo.branches.delete("refs/heads/test_branch") # or... repo.branches.delete(ref) # or... ``` --- ### Diffs There are various ways to get hands on diffs: ```ruby # Diff between two subsequent commits diff_commits = commit_object.parents[0].diff(commit_object) # Diff between two tree objects diff_trees = tree_object_a.diff(tree_object_b) # Diff between index/staging and current working directory diff_index = repository.index.diff # Diff between index/staging and another diffable (commit/tree/index) diff_index_diffable = repository.index.diff(some_diffable) ``` When you already have a diff object, you can examine it: ```ruby # Get patch diff.patch => "diff --git a/foo1 b/foo1\nnew file mode 100644\nindex 0000000..81b68f0\n--- /dev/null\n+++ b/foo1\n@@ -0,0 +1,2 @@\n+abc\n+add line1\ndiff --git a/txt1 b/txt1\ndeleted file mode 100644\nindex 81b68f0..0000000\n--- a/txt1\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-abc\n-add line1\ndiff --git a/txt2 b/txt2\nindex a7bb42f..a357de7 100644\n--- a/txt2\n+++ b/txt2\n@@ -1,2 +1,3 @@\n abc2\n add line2-1\n+add line2-2\n" # Get delta (faster, if you only need information on what files changed) diff.each_delta{ |d| puts d.inspect } #"0000000000000000000000000000000000000000", :path=>"foo1", :size=>0, :flags=>6, :mode=>0}, new_file: {:oid=>"81b68f040b120c9627518213f7fc317d1ed18e1c", :path=>"foo1", :size=>14, :flags=>6, :mode=>33188}, similarity: 0, status: :added> #"81b68f040b120c9627518213f7fc317d1ed18e1c", :path=>"txt1", :size=>14, :flags=>6, :mode=>33188}, new_file: {:oid=>"0000000000000000000000000000000000000000", :path=>"txt1", :size=>0, :flags=>6, :mode=>0}, similarity: 0, status: :deleted> #"a7bb42f71183c162efea5e4c80597437d716c62b", :path=>"txt2", :size=>17, :flags=>6, :mode=>33188}, new_file: {:oid=>"a357de7d870823acc3953f1b2471f9c18d0d56ea", :path=>"txt2", :size=>29, :flags=>6, :mode=>33188}, similarity: 0, status: :modified> # Detect renamed files # Note that the status field changed from :added/:deleted to :renamed diff.find_similar! diff.each_delta{ |d| puts d.inspect } #"81b68f040b120c9627518213f7fc317d1ed18e1c", :path=>"txt1", :size=>14, :flags=>6, :mode=>33188}, new_file: {:oid=>"81b68f040b120c9627518213f7fc317d1ed18e1c", :path=>"foo1", :size=>14, :flags=>6, :mode=>33188}, similarity: 100, status: :renamed> #"a7bb42f71183c162efea5e4c80597437d716c62b", :path=>"txt2", :size=>17, :flags=>6, :mode=>33188}, new_file: {:oid=>"a357de7d870823acc3953f1b2471f9c18d0d56ea", :path=>"txt2", :size=>29, :flags=>6, :mode=>33188}, similarity: 0, status: :modified> # Merge one diff into another (mutating the first one) diff1.merge!(diff2) # Write a patch into a file (or any other object responding to write) # Note that the patch as in diff.patch will be written, it won't be applied file = File.open('/some/file', 'w') diff.write_patch(file) file.close ``` --- ### Config files It's also easy to read and manipulate the Git config file data with Rugged. ```ruby # Read values repo.config['core.bare'] # Set values repo.config['user.name'] = true # Delete values repo.config.delete('user.name') ``` --- ### General methods Rugged also includes a general library for handling basic Git operations. One of these is converting a raw sha (20 bytes) into a readable hex sha (40 characters). ```ruby Rugged.hex_to_raw('bfde59cdd0dfac1d892814f66a95641abd8a1faf') # => "\277\336Y\315\320\337\254\035\211(\024\366j\225d\032\275\212\037\257" Rugged.raw_to_hex("\277\336Y\315\320\337\254\035\211(\024\366j\225d\032\275\212\037\257") => "bfde59cdd0dfac1d892814f66a95641abd8a1faf" ``` --- ### Alternative backends You can store bare repositories in alternative backends instead of storing on disk. (see `redbadger/rugged-redis` for an example of how a rugged backend works). ```ruby a_backend = Rugged::InMemory::Backend.new(opt1: 'setting', opt2: 'setting') repo = Rugged::Repository.init_at('repo_name', :bare, backend: a_backend) # or repo = Rugged::Repository.bare('repo_name', backend: a_backend) ``` --- ## Contributing Fork libgit2/rugged on GitHub, make it awesomer (preferably in a branch named for the topic), send a pull request. ## Development Simply clone and install: $ git clone https://github.com/libgit2/rugged.git $ cd rugged $ bundle install $ rake compile $ rake test ## Support We encourage you to use StackOverflow for any questions or concerns regarding Rugged. Please tag your questions with the [rugged](http://stackoverflow.com/questions/tagged/rugged) keyword. For bug reports, please open a ticket on the GitHub [issue tracker](https://github.com/libgit2/rugged/issues). ## Authors * Vicent Marti * Scott Chacon * Arthur Schreiber ## License MIT. See LICENSE file. rugged-0.26.0/lib/0000755000175000017500000000000013147033070013746 5ustar abhijithabhijithrugged-0.26.0/lib/rugged.rb0000644000175000017500000000132313147033070015547 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. begin RUBY_VERSION =~ /(\d+.\d+)/ require "rugged/#{$1}/rugged" rescue LoadError require "rugged/rugged" end require 'rugged/index' require 'rugged/object' require 'rugged/commit' require 'rugged/version' require 'rugged/repository' require 'rugged/reference' require 'rugged/walker' require 'rugged/tree' require 'rugged/tag' require 'rugged/branch' require 'rugged/diff' require 'rugged/patch' require 'rugged/remote' require 'rugged/credentials' require 'rugged/attributes' require 'rugged/blob' require 'rugged/submodule_collection' rugged-0.26.0/lib/rugged/0000755000175000017500000000000013147033070015223 5ustar abhijithabhijithrugged-0.26.0/lib/rugged/version.rb0000644000175000017500000000034513147033070017237 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged Version = VERSION = '0.26.0' end rugged-0.26.0/lib/rugged/submodule_collection.rb0000644000175000017500000000343713147033070021771 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class SubmoduleCollection # call-seq: # submodules.setup_add(url, path[, options]) -> submodule # # Add a new +submodule+. # # This does "git submodule add" incuding fetch and checkout of the # submodule contents. # # - +url+: URL for the submodule's remote # - +path+: path at which the submodule should be created # # The +options+ hash accepts all options supported by Rugged::Remote#fetch # and the following: # :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+ def add(url, path, options = {}) submodule = setup_add(url, path, options) clone_submodule(submodule.repository, options) submodule.finalize_add end private # currently libgit2's `git_submodule_add_setup` initializes a repo # with a workdir for the submodule. libgit2's `git_clone` however # requires the target for the clone to be an empty dir. # # This provides a ghetto clone implementation that: # 1. fetches the remote # 2. sets up a master branch to be tracking origin/master # 3. checkouts the submodule def clone_submodule(repo, fetch_options) # the remote was just added by setup_add, no need to check presence repo.remotes['origin'].fetch(fetch_options) repo.branches.create('master','origin/master') repo.branches['master'].upstream = repo.branches['origin/master'] repo.checkout_head(strategy: :force) end end end rugged-0.26.0/lib/rugged/repository.rb0000644000175000017500000001515413147033070017775 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged # Repository is an interface into a Git repository on-disk. It's the primary # interface between your app and the main Git objects Rugged makes available # to you. class Repository # Pretty formatting of a Repository. # # Returns a very pretty String. def inspect "#" end # Get the most recent commit from this repo. # # Returns a Rugged::Commit object. def last_commit self.head.target end # Checkout the specified branch, reference or commit. # # target - A revparse spec for the branch, reference or commit to check out. # options - Options passed to #checkout_tree. def checkout(target, options = {}) options[:strategy] ||= :safe options.delete(:paths) return checkout_head(options) if target == "HEAD" if target.kind_of?(Rugged::Branch) branch = target else branch = branches[target] end if branch self.checkout_tree(branch.target, options) if branch.remote? references.create("HEAD", branch.target_id, force: true) else references.create("HEAD", branch.canonical_name, force: true) end else commit = Commit.lookup(self, self.rev_parse_oid(target)) references.create("HEAD", commit.oid, force: true) self.checkout_tree(commit, options) end end def diff(left, right, opts = {}) left = rev_parse(left) if left.kind_of?(String) right = rev_parse(right) if right.kind_of?(String) if !left.is_a?(Rugged::Tree) && !left.is_a?(Rugged::Commit) && !left.nil? raise TypeError, "Expected a Rugged::Tree or Rugged::Commit instance" end if !right.is_a?(Rugged::Tree) && !right.is_a?(Rugged::Commit) && !right.nil? raise TypeError, "Expected a Rugged::Tree or Rugged::Commit instance" end if left left.diff(right, opts) elsif right right.diff(left, opts.merge(:reverse => !opts[:reverse])) end end def diff_workdir(left, opts = {}) left = rev_parse(left) if left.kind_of?(String) if !left.is_a?(Rugged::Tree) && !left.is_a?(Rugged::Commit) raise TypeError, "Expected a Rugged::Tree or Rugged::Commit instance" end left.diff_workdir(opts) end # Walks over a set of commits using Rugged::Walker. # # from - The String SHA1 to push onto Walker to begin our walk. # sorting - The sorting order of the commits, as defined in the README. # block - A block that we pass into walker#each. # # Returns nothing if called with a block, otherwise returns an instance of # Enumerable::Enumerator containing Rugged::Commit objects. def walk(from, sorting=Rugged::SORT_DATE, &block) walker = Rugged::Walker.new(self) walker.sorting(sorting) walker.push(from) walker.each(&block) end # Look up a SHA1. # # Returns one of the four classes that inherit from Rugged::Object. def lookup(oid) Rugged::Object.lookup(self, oid) end # Look up an object by a revision string. # # Returns one of the four classes that inherit from Rugged::Object. def rev_parse(spec) Rugged::Object.rev_parse(self, spec) end # Look up an object by a revision string. # # Returns the oid of the matched object as a String def rev_parse_oid(spec) Rugged::Object.rev_parse_oid(self, spec) end # Look up a single reference by name. # # Example: # # repo.ref 'refs/heads/master' # # => # # # Returns a Rugged::Reference. def ref(ref_name) references[ref_name] end def refs(glob = nil) references.each(glob) end def references @references ||= ReferenceCollection.new(self) end def ref_names(glob = nil) references.each_name(glob) end # All the tags in the repository. # # Returns a TagCollection containing all the tags. def tags @tags ||= TagCollection.new(self) end # All the remotes in the repository. # # Returns a Rugged::RemoteCollection containing all the Rugged::Remote objects # in the repository. def remotes @remotes ||= RemoteCollection.new(self) end # All the branches in the repository # # Returns a BranchCollection containing Rugged::Branch objects def branches @branches ||= BranchCollection.new(self) end # All the submodules in the repository # # Returns a SubmoduleCollection containing Rugged::Submodule objects def submodules @submodules ||= SubmoduleCollection.new(self) end # Create a new branch in the repository # # name - The name of the branch (without a full reference path) # sha_or_ref - The target of the branch; either a String representing # an OID or a reference name, or a Rugged::Object instance. # # Returns a Rugged::Branch object def create_branch(name, sha_or_ref = "HEAD") case sha_or_ref when Rugged::Object target = sha_or_ref.oid else target = rev_parse_oid(sha_or_ref) end branches.create(name, target) end # Get the blob at a path for a specific revision. # # revision - The String SHA1. # path - The String file path. # # Returns a Rugged::Blob object def blob_at(revision, path) tree = Rugged::Commit.lookup(self, revision).tree begin blob_data = tree.path(path) rescue Rugged::TreeError return nil end blob = Rugged::Blob.lookup(self, blob_data[:oid]) (blob.type == :blob) ? blob : nil end def fetch(remote_or_url, *args) unless remote_or_url.kind_of? Remote remote_or_url = remotes[remote_or_url] || remotes.create_anonymous(remote_or_url) end remote_or_url.fetch(*args) end # Push a list of refspecs to the given remote. # # refspecs - A list of refspecs that should be pushed to the remote. # # Returns a hash containing the pushed refspecs as keys and # any error messages or +nil+ as values. def push(remote_or_url, *args) unless remote_or_url.kind_of? Remote remote_or_url = remotes[remote_or_url] || remotes.create_anonymous(remote_or_url) end remote_or_url.push(*args) end end end rugged-0.26.0/lib/rugged/tag.rb0000644000175000017500000000256513147033070016333 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Tag < Rugged::Reference GPG_SIGNATURE_PREFIX = "-----BEGIN PGP SIGNATURE-----".freeze def self.extract_signature(repo, oid, prefix=GPG_SIGNATURE_PREFIX) object = repo.read(oid) unless object.type == :tag raise GitRPC::InvalidObject, "Invalid object type #{object.type}, expected tag" end if index = object.data.index(prefix) [ object.data.byteslice(index..-1), object.data.byteslice(0...index) ] else nil end end def name canonical_name.sub(%r{^refs/tags/}, "") end class Annotation def self.prettify_message(msg, strip_comments = true) Rugged::prettify_message(msg, strip_comments) end def inspect "#" end def to_hash { :message => message, :name => name, :target => target, :tagger => tagger, } end def modify(new_args, force=True) args = self.to_hash.merge(new_args) Tag.create(args, force) end end end end rugged-0.26.0/lib/rugged/diff.rb0000644000175000017500000000072213147033070016461 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. require 'rugged/diff/hunk' require 'rugged/diff/line' require 'rugged/diff/delta' module Rugged class Diff include Enumerable alias each each_patch attr_reader :owner def patches each_patch.to_a end def deltas each_delta.to_a end end end rugged-0.26.0/lib/rugged/patch.rb0000644000175000017500000000150013147033070016643 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Patch include Enumerable alias each each_hunk alias size hunk_count alias count hunk_count attr_accessor :owner alias diff owner def inspect "#<#{self.class.name}:#{object_id}>" end # Returns the number of additions in the patch. def additions stat[0] end # Returns the number of deletions in the patch. def deletions stat[1] end # Returns the number of total changes in the patch. def changes additions + deletions end # Returns an Array containing all hunks of the patch. def hunks each_hunk.to_a end end end rugged-0.26.0/lib/rugged/blob.rb0000644000175000017500000000142213147033070016465 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Blob class HashSignature WHITESPACE_DEFAULT = 0 WHITESPACE_IGNORE = 1 WHITESPACE_SMART = 2 end def hashsig(options = 0) @hashsig ||= HashSignature.new(self, options) end def similarity(other) other_sig = case other when HashSignature other when String HashSignature.new(other) when Blob other.hashsig else raise TypeError, "Expected a Rugged::Blob, String or Rugged::Blob::HashSignature" end HashSignature.compare(self.hashsig, other_sig) end end end rugged-0.26.0/lib/rugged/branch.rb0000644000175000017500000000131113147033070017001 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Branch < Rugged::Reference def ==(other) other.instance_of?(Rugged::Branch) && other.canonical_name == self.canonical_name end # Get the remote the branch belongs to. # # If the branch is remote returns the remote it belongs to. # In case of local branch, it returns the remote of the branch # it tracks or nil if there is no tracking branch. # def remote remote_name = self.remote_name @owner.remotes[remote_name] if remote_name end end end rugged-0.26.0/lib/rugged/tree.rb0000644000175000017500000001527513147033070016521 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Tree ## # call-seq: # Tree.diff(repo, tree, diffable[, options]) -> diff # # Returns a diff between the `tree` and the diffable object that was given. # +diffable+ can either be a +Rugged::Commit+, a +Rugged::Tree+, a +Rugged::Index+, # or +nil+. # # The +tree+ object will be used as the "old file" side of the diff, while the # parent tree or the +diffable+ object will be used for the "new file" side. # # If +tree+ or +diffable+ are nil, they will be treated as an empty tree. Passing # both as `nil` will raise an exception. # # The following options can be passed in the +options+ Hash: # # :paths :: # An array of paths / fnmatch patterns to constrain the diff to a specific # set of files. Also see +:disable_pathspec_match+. # # :max_size :: # An integer specifying the maximum byte size of a file before a it will # be treated as binary. The default value is 512MB. # # :context_lines :: # The number of unchanged lines that define the boundary of a hunk (and # to display before and after the actual changes). The default is 3. # # :interhunk_lines :: # The maximum number of unchanged lines between hunk boundaries before the hunks # will be merged into a one. The default is 0. # # :old_prefix :: # The virtual "directory" to prefix to old filenames in hunk headers. # The default is "a". # # :new_prefix :: # The virtual "directory" to prefix to new filenames in hunk headers. # The default is "b". # # :reverse :: # If true, the sides of the diff will be reversed. # # :force_text :: # If true, all files will be treated as text, disabling binary attributes & detection. # # :ignore_whitespace :: # If true, all whitespace will be ignored. # # :ignore_whitespace_change :: # If true, changes in amount of whitespace will be ignored. # # :ignore_whitespace_eol :: # If true, whitespace at end of line will be ignored. # # :ignore_submodules :: # if true, submodules will be excluded from the diff completely. # # :patience :: # If true, the "patience diff" algorithm will be used (currenlty unimplemented). # # :include_ignored :: # If true, ignored files will be included in the diff. # # :include_untracked :: # If true, untracked files will be included in the diff. # # :include_unmodified :: # If true, unmodified files will be included in the diff. # # :recurse_untracked_dirs :: # Even if +:include_untracked+ is true, untracked directories will only be # marked with a single entry in the diff. If this flag is set to true, # all files under ignored directories will be included in the diff, too. # # :disable_pathspec_match :: # If true, the given +:paths+ will be applied as exact matches, instead of # as fnmatch patterns. # # :deltas_are_icase :: # If true, filename comparisons will be made with case-insensitivity. # # :include_untracked_content :: # if true, untracked content will be contained in the the diff patch text. # # :skip_binary_check :: # If true, diff deltas will be generated without spending time on binary # detection. This is useful to improve performance in cases where the actual # file content difference is not needed. # # :include_typechange :: # If true, type changes for files will not be interpreted as deletion of # the "old file" and addition of the "new file", but will generate # typechange records. # # :include_typechange_trees :: # Even if +:include_typechange+ is true, blob -> tree changes will still # usually be handled as a deletion of the blob. If this flag is set to true, # blob -> tree changes will be marked as typechanges. # # :ignore_filemode :: # If true, file mode changes will be ignored. # # :recurse_ignored_dirs :: # Even if +:include_ignored+ is true, ignored directories will only be # marked with a single entry in the diff. If this flag is set to true, # all files under ignored directories will be included in the diff, too. # # Examples: # # # Emulating `git diff ` # tree = Rugged::Tree.lookup(repo, "d70d245ed97ed2aa596dd1af6536e4bfdb047b69") # diff = tree.diff(repo.index) # diff.merge!(tree.diff) # # # Tree-to-Tree Diff # tree = Rugged::Tree.lookup(repo, "d70d245ed97ed2aa596dd1af6536e4bfdb047b69") # other_tree = Rugged::Tree.lookup(repo, "7a9e0b02e63179929fed24f0a3e0f19168114d10") # diff = tree.diff(other_tree) # def self.diff(repo, tree, other_tree = nil, options = {}) if tree && !tree.is_a?(Rugged::Tree) raise TypeError, "At least a Rugged::Tree object is required for diffing" end if other_tree.nil? if tree.nil? raise TypeError, "Need 'old' or 'new' for diffing" else diff_tree_to_tree repo, tree, nil, options end else if other_tree.is_a?(::String) other_tree = Rugged::Object.rev_parse repo, other_tree end case other_tree when Rugged::Commit diff_tree_to_tree repo, tree, other_tree.tree, options when Rugged::Tree diff_tree_to_tree repo, tree, other_tree, options when Rugged::Index diff_tree_to_index repo, tree, other_tree, options else raise TypeError, "A Rugged::Commit, Rugged::Tree or Rugged::Index instance is required" end end end include Enumerable attr_reader :owner alias repo owner def diff(other = nil, options = nil) Tree.diff(repo, self, other, options) end def inspect data = "#\n" self.each { |e| data << " <\"#{e[:name]}\" #{e[:oid]}>\n" } data end # Walks the tree but only yields blobs def walk_blobs(mode=:postorder) self.walk(mode) { |root, e| yield root, e if e[:type] == :blob } end # Walks the tree but only yields subtrees def walk_trees(mode=:postorder) self.walk(mode) { |root, e| yield root, e if e[:type] == :tree } end # Iterate over the blobs in this tree def each_blob self.each { |e| yield e if e[:type] == :blob } end # Iterate over the subtrees in this tree def each_tree self.each { |e| yield e if e[:type] == :tree } end end end rugged-0.26.0/lib/rugged/remote.rb0000644000175000017500000000033313147033070017042 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Remote end end rugged-0.26.0/lib/rugged/commit.rb0000644000175000017500000000302713147033070017042 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Commit def self.prettify_message(msg, strip_comments = true) Rugged::prettify_message(msg, strip_comments) end def inspect "#" end def header_field?(field) !!header_field(field) end # Return a diff between this commit and its first parent or another commit or tree. # # See Rugged::Tree#diff for more details. def diff(*args) args.unshift(parents.first) if args.size == 1 && args.first.is_a?(Hash) self.tree.diff(*args) end # Return a diff between this commit and the workdir. # # See Rugged::Tree#diff_workdir for more details. def diff_workdir(options = {}) self.tree.diff_workdir(options) end # The time when this commit was made effective. This is the same value # as the +:time+ attribute for +commit.committer+. # # Returns a Time object def time @time ||= Time.at(self.epoch_time) end def to_hash { :message => message, :committer => committer, :author => author, :tree => tree, :parents => parents, } end def modify(new_args, update_ref=nil) args = self.to_hash.merge(new_args) Commit.create(args, update_ref) end end end rugged-0.26.0/lib/rugged/index.rb0000644000175000017500000001133513147033070016662 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Index include Enumerable # call-seq: # index.diff([options]) -> diff # index.diff(diffable[, options]) -> diff # # The first form returns a diff between the index and the current working # directory. # # The second form returns a diff between the index and the given diffable object. # +diffable+ can either be a +Rugged::Commit+ or a +Rugged::Tree+. # # The index will be used as the "old file" side of the diff, while the working # directory or the +diffable+ will be used for the "new file" side. # # The following options can be passed in the +options+ Hash: # # :paths :: # An array of paths / fnmatch patterns to constrain the diff to a specific # set of files. Also see +:disable_pathspec_match+. # # :max_size :: # An integer specifying the maximum byte size of a file before a it will # be treated as binary. The default value is 512MB. # # :context_lines :: # The number of unchanged lines that define the boundary of a hunk (and # to display before and after the actual changes). The default is 3. # # :interhunk_lines :: # The maximum number of unchanged lines between hunk boundaries before the hunks # will be merged into a one. The default is 0. # # :reverse :: # If true, the sides of the diff will be reversed. # # :force_text :: # If true, all files will be treated as text, disabling binary attributes & detection. # # :ignore_whitespace :: # If true, all whitespace will be ignored. # # :ignore_whitespace_change :: # If true, changes in amount of whitespace will be ignored. # # :ignore_whitespace_eol :: # If true, whitespace at end of line will be ignored. # # :ignore_submodules :: # if true, submodules will be excluded from the diff completely. # # :patience :: # If true, the "patience diff" algorithm will be used (currenlty unimplemented). # # :include_ignored :: # If true, ignored files will be included in the diff. # # :include_untracked :: # If true, untracked files will be included in the diff. # # :include_unmodified :: # If true, unmodified files will be included in the diff. # # :recurse_untracked_dirs :: # Even if +:include_untracked+ is true, untracked directories will only be # marked with a single entry in the diff. If this flag is set to true, # all files under ignored directories will be included in the diff, too. # # :disable_pathspec_match :: # If true, the given +:paths+ will be applied as exact matches, instead of # as fnmatch patterns. # # :deltas_are_icase :: # If true, filename comparisons will be made with case-insensitivity. # # :include_untracked_content :: # if true, untracked content will be contained in the the diff patch text. # # :skip_binary_check :: # If true, diff deltas will be generated without spending time on binary # detection. This is useful to improve performance in cases where the actual # file content difference is not needed. # # :include_typechange :: # If true, type changes for files will not be interpreted as deletion of # the "old file" and addition of the "new file", but will generate # typechange records. # # :include_typechange_trees :: # Even if +:include_typechange+ is true, blob -> tree changes will still # usually be handled as a deletion of the blob. If this flag is set to true, # blob -> tree changes will be marked as typechanges. # # :ignore_filemode :: # If true, file mode changes will be ignored. # # :recurse_ignored_dirs :: # Even if +:include_ignored+ is true, ignored directories will only be # marked with a single entry in the diff. If this flag is set to true, # all files under ignored directories will be included in the diff, too. def diff(*args) options = args.last.is_a?(Hash) ? args.pop : {} other = args.shift case other when nil diff_index_to_workdir options when ::Rugged::Commit diff_tree_to_index other.tree, options when ::Rugged::Tree diff_tree_to_index other, options else raise TypeError, "A Rugged::Commit or Rugged::Tree instance is required" end end def to_s s = "#' end end end rugged-0.26.0/lib/rugged/object.rb0000644000175000017500000000042313147033070017015 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Object def <=>(other) self.oid <=> other.oid end end end rugged-0.26.0/lib/rugged/walker.rb0000644000175000017500000000036213147033070017036 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Walker include Enumerable end end rugged-0.26.0/lib/rugged/credentials.rb0000644000175000017500000000236413147033070020052 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged module Credentials # A plain-text username and password credential object. class UserPassword def initialize(options) @username, @password = options[:username], options[:password] end def call(url, username_from_url, allowed_types) self end end # A ssh key credential object that can optionally be passphrase-protected class SshKey def initialize(options) @username, @publickey, @privatekey, @passphrase = options[:username], options[:publickey], options[:privatekey], options[:passphrase] end def call(url, username_from_url, allowed_types) self end end class SshKeyFromAgent def initialize(options) @username = options[:username] end def call(url, username_from_url, allowed_types) self end end # A "default" credential usable for Negotiate mechanisms like NTLM or # Kerberos authentication class Default def call(url, username_from_url, allowed_types) self end end end end rugged-0.26.0/lib/rugged/console.rb0000644000175000017500000000061013147033070017207 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. # Loaded by script/console. Land helpers here. def repo Rugged::Repository.new(File.expand_path('../../../', __FILE__)) end Pry.config.prompt = lambda do |context, nesting, pry| "[rugged] #{context}> " end rugged-0.26.0/lib/rugged/attributes.rb0000644000175000017500000000205213147033070017735 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Repository def attributes(path, options = {}) Attributes.new(self, path, options) end class Attributes include Enumerable LOAD_PRIORITIES = { [:file, :index] => 0, [:index, :file] => 1, [:index] => 2, } def self.parse_opts(opt) flags = LOAD_PRIORITIES[opt[:priority]] || 0 flags |= 4 if opt[:skip_system] flags end def initialize(repository, path, options = {}) @repository = repository @path = path @load_flags = Attributes.parse_opts(options) end def [](attribute) @repository.fetch_attributes(@path, attribute, @load_flags) end def to_h @hash ||= @repository.fetch_attributes(@path, nil, @load_flags) end def each(&block) to_h.each(&block) end end end end rugged-0.26.0/lib/rugged/reference.rb0000644000175000017500000000051713147033070017511 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Reference def inspect "#<#{self.class}:#{object_id} {name: #{name.inspect}, target: #{target.inspect}}>" end end end rugged-0.26.0/lib/rugged/diff/0000755000175000017500000000000013147033070016133 5ustar abhijithabhijithrugged-0.26.0/lib/rugged/diff/line.rb0000644000175000017500000000212313147033070017405 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Diff class Line attr_reader :line_origin, :content, :old_lineno, :new_lineno, :content_offset def context? @line_origin == :context end def addition? @line_origin == :addition end def deletion? @line_origin == :deletion end def eof_no_newline? @line_origin == :eof_no_newline end def eof_newline_added? @line_origin == :eof_newline_added end def eof_newline_removed? @line_origin == :eof_newline_removed end def file_header? @line_origin == :file_header end def hunk_header? @line_origin == :hunk_header end def binary? @line_origin == :binary end def inspect "#<#{self.class.name}:#{object_id} {line_origin: #{line_origin.inspect}, content: #{content.inspect}>" end end end end rugged-0.26.0/lib/rugged/diff/delta.rb0000644000175000017500000000217313147033070017554 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Diff class Delta attr_reader :owner alias diff owner attr_reader :old_file attr_reader :new_file attr_reader :similarity attr_reader :status attr_reader :status_char attr_reader :binary alias binary? binary def added? status == :added end def deleted? status == :deleted end def modified? status == :modified end def renamed? status == :renamed end def copied? status == :copied end def ignored? status == :ignored end def untracked? status == :untracked end def typechange? status == :typechange end def inspect "#<#{self.class.name}:#{object_id} {old_file: #{old_file.inspect}, new_file: #{new_file.inspect}, similarity: #{similarity.inspect}, status: #{status.inspect}>" end end end end rugged-0.26.0/lib/rugged/diff/hunk.rb0000644000175000017500000000100513147033070017421 0ustar abhijithabhijith# Copyright (C) the Rugged contributors. All rights reserved. # # This file is part of Rugged, distributed under the MIT license. # For full terms see the included LICENSE file. module Rugged class Diff class Hunk def delta @owner end def inspect "#<#{self.class.name}:#{object_id} {header: #{header.inspect}, count: #{count.inspect}}>" end # Returns an Array containing all lines of the hunk. def lines each_line.to_a end end end end rugged-0.26.0/rugged.gemspec0000644000175000017500000005333013147033070016026 0ustar abhijithabhijith######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: rugged 0.26.0 ruby lib # stub: ext/rugged/extconf.rb Gem::Specification.new do |s| s.name = "rugged".freeze s.version = "0.26.0" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Scott Chacon".freeze, "Vicent Marti".freeze] s.date = "2017-07-07" s.description = "Rugged is a Ruby bindings to the libgit2 linkable C Git library. This is\nfor testing and using the libgit2 library in a language that is awesome.\n".freeze s.email = "schacon@gmail.com".freeze s.extensions = ["ext/rugged/extconf.rb".freeze] s.files = ["LICENSE".freeze, "README.md".freeze, "ext/rugged/extconf.rb".freeze, "ext/rugged/rugged.c".freeze, "ext/rugged/rugged.h".freeze, "ext/rugged/rugged_backend.c".freeze, "ext/rugged/rugged_blame.c".freeze, "ext/rugged/rugged_blob.c".freeze, "ext/rugged/rugged_branch.c".freeze, "ext/rugged/rugged_branch_collection.c".freeze, "ext/rugged/rugged_commit.c".freeze, "ext/rugged/rugged_config.c".freeze, "ext/rugged/rugged_cred.c".freeze, "ext/rugged/rugged_diff.c".freeze, "ext/rugged/rugged_diff_delta.c".freeze, "ext/rugged/rugged_diff_hunk.c".freeze, "ext/rugged/rugged_diff_line.c".freeze, "ext/rugged/rugged_index.c".freeze, "ext/rugged/rugged_note.c".freeze, "ext/rugged/rugged_object.c".freeze, "ext/rugged/rugged_patch.c".freeze, "ext/rugged/rugged_rebase.c".freeze, "ext/rugged/rugged_reference.c".freeze, "ext/rugged/rugged_reference_collection.c".freeze, "ext/rugged/rugged_remote.c".freeze, "ext/rugged/rugged_remote_collection.c".freeze, "ext/rugged/rugged_repo.c".freeze, "ext/rugged/rugged_revwalk.c".freeze, "ext/rugged/rugged_settings.c".freeze, "ext/rugged/rugged_signature.c".freeze, "ext/rugged/rugged_submodule.c".freeze, "ext/rugged/rugged_submodule_collection.c".freeze, "ext/rugged/rugged_tag.c".freeze, "ext/rugged/rugged_tag_collection.c".freeze, "ext/rugged/rugged_tree.c".freeze, "lib/rugged.rb".freeze, "lib/rugged/attributes.rb".freeze, "lib/rugged/blob.rb".freeze, "lib/rugged/branch.rb".freeze, "lib/rugged/commit.rb".freeze, "lib/rugged/console.rb".freeze, "lib/rugged/credentials.rb".freeze, "lib/rugged/diff.rb".freeze, "lib/rugged/diff/delta.rb".freeze, "lib/rugged/diff/hunk.rb".freeze, "lib/rugged/diff/line.rb".freeze, "lib/rugged/index.rb".freeze, "lib/rugged/object.rb".freeze, "lib/rugged/patch.rb".freeze, "lib/rugged/reference.rb".freeze, "lib/rugged/remote.rb".freeze, "lib/rugged/repository.rb".freeze, "lib/rugged/submodule_collection.rb".freeze, "lib/rugged/tag.rb".freeze, "lib/rugged/tree.rb".freeze, "lib/rugged/version.rb".freeze, "lib/rugged/walker.rb".freeze, "vendor/libgit2/AUTHORS".freeze, "vendor/libgit2/CMakeLists.txt".freeze, "vendor/libgit2/COPYING".freeze, "vendor/libgit2/cmake/Modules/AddCFlagIfSupported.cmake".freeze, "vendor/libgit2/cmake/Modules/FindCoreFoundation.cmake".freeze, "vendor/libgit2/cmake/Modules/FindGSSAPI.cmake".freeze, "vendor/libgit2/cmake/Modules/FindHTTP_Parser.cmake".freeze, "vendor/libgit2/cmake/Modules/FindIconv.cmake".freeze, "vendor/libgit2/cmake/Modules/FindSecurity.cmake".freeze, "vendor/libgit2/deps/http-parser/LICENSE-MIT".freeze, "vendor/libgit2/deps/http-parser/http_parser.c".freeze, "vendor/libgit2/deps/http-parser/http_parser.h".freeze, "vendor/libgit2/deps/regex/config.h".freeze, "vendor/libgit2/deps/regex/regcomp.c".freeze, "vendor/libgit2/deps/regex/regex.c".freeze, "vendor/libgit2/deps/regex/regex.h".freeze, "vendor/libgit2/deps/regex/regex_internal.c".freeze, "vendor/libgit2/deps/regex/regex_internal.h".freeze, "vendor/libgit2/deps/regex/regexec.c".freeze, "vendor/libgit2/deps/winhttp/urlmon.h".freeze, "vendor/libgit2/deps/winhttp/winhttp.def".freeze, "vendor/libgit2/deps/winhttp/winhttp.h".freeze, "vendor/libgit2/deps/winhttp/winhttp64.def".freeze, "vendor/libgit2/deps/zlib/adler32.c".freeze, "vendor/libgit2/deps/zlib/crc32.c".freeze, "vendor/libgit2/deps/zlib/crc32.h".freeze, "vendor/libgit2/deps/zlib/deflate.c".freeze, "vendor/libgit2/deps/zlib/deflate.h".freeze, "vendor/libgit2/deps/zlib/infback.c".freeze, "vendor/libgit2/deps/zlib/inffast.c".freeze, "vendor/libgit2/deps/zlib/inffast.h".freeze, "vendor/libgit2/deps/zlib/inffixed.h".freeze, "vendor/libgit2/deps/zlib/inflate.c".freeze, "vendor/libgit2/deps/zlib/inflate.h".freeze, "vendor/libgit2/deps/zlib/inftrees.c".freeze, "vendor/libgit2/deps/zlib/inftrees.h".freeze, "vendor/libgit2/deps/zlib/trees.c".freeze, "vendor/libgit2/deps/zlib/trees.h".freeze, "vendor/libgit2/deps/zlib/zconf.h".freeze, "vendor/libgit2/deps/zlib/zlib.h".freeze, "vendor/libgit2/deps/zlib/zutil.c".freeze, "vendor/libgit2/deps/zlib/zutil.h".freeze, "vendor/libgit2/include/git2.h".freeze, "vendor/libgit2/include/git2/annotated_commit.h".freeze, "vendor/libgit2/include/git2/attr.h".freeze, "vendor/libgit2/include/git2/blame.h".freeze, "vendor/libgit2/include/git2/blob.h".freeze, "vendor/libgit2/include/git2/branch.h".freeze, "vendor/libgit2/include/git2/buffer.h".freeze, "vendor/libgit2/include/git2/checkout.h".freeze, "vendor/libgit2/include/git2/cherrypick.h".freeze, "vendor/libgit2/include/git2/clone.h".freeze, "vendor/libgit2/include/git2/commit.h".freeze, "vendor/libgit2/include/git2/common.h".freeze, "vendor/libgit2/include/git2/config.h".freeze, "vendor/libgit2/include/git2/cred_helpers.h".freeze, "vendor/libgit2/include/git2/describe.h".freeze, "vendor/libgit2/include/git2/diff.h".freeze, "vendor/libgit2/include/git2/errors.h".freeze, "vendor/libgit2/include/git2/filter.h".freeze, "vendor/libgit2/include/git2/global.h".freeze, "vendor/libgit2/include/git2/graph.h".freeze, "vendor/libgit2/include/git2/ignore.h".freeze, "vendor/libgit2/include/git2/index.h".freeze, "vendor/libgit2/include/git2/indexer.h".freeze, "vendor/libgit2/include/git2/inttypes.h".freeze, "vendor/libgit2/include/git2/merge.h".freeze, "vendor/libgit2/include/git2/message.h".freeze, "vendor/libgit2/include/git2/net.h".freeze, "vendor/libgit2/include/git2/notes.h".freeze, "vendor/libgit2/include/git2/object.h".freeze, "vendor/libgit2/include/git2/odb.h".freeze, "vendor/libgit2/include/git2/odb_backend.h".freeze, "vendor/libgit2/include/git2/oid.h".freeze, "vendor/libgit2/include/git2/oidarray.h".freeze, "vendor/libgit2/include/git2/pack.h".freeze, "vendor/libgit2/include/git2/patch.h".freeze, "vendor/libgit2/include/git2/pathspec.h".freeze, "vendor/libgit2/include/git2/proxy.h".freeze, "vendor/libgit2/include/git2/rebase.h".freeze, "vendor/libgit2/include/git2/refdb.h".freeze, "vendor/libgit2/include/git2/reflog.h".freeze, "vendor/libgit2/include/git2/refs.h".freeze, "vendor/libgit2/include/git2/refspec.h".freeze, "vendor/libgit2/include/git2/remote.h".freeze, "vendor/libgit2/include/git2/repository.h".freeze, "vendor/libgit2/include/git2/reset.h".freeze, "vendor/libgit2/include/git2/revert.h".freeze, "vendor/libgit2/include/git2/revparse.h".freeze, "vendor/libgit2/include/git2/revwalk.h".freeze, "vendor/libgit2/include/git2/signature.h".freeze, "vendor/libgit2/include/git2/stash.h".freeze, "vendor/libgit2/include/git2/status.h".freeze, "vendor/libgit2/include/git2/stdint.h".freeze, "vendor/libgit2/include/git2/strarray.h".freeze, "vendor/libgit2/include/git2/submodule.h".freeze, "vendor/libgit2/include/git2/sys/commit.h".freeze, "vendor/libgit2/include/git2/sys/config.h".freeze, "vendor/libgit2/include/git2/sys/diff.h".freeze, "vendor/libgit2/include/git2/sys/filter.h".freeze, "vendor/libgit2/include/git2/sys/hashsig.h".freeze, "vendor/libgit2/include/git2/sys/index.h".freeze, "vendor/libgit2/include/git2/sys/mempack.h".freeze, "vendor/libgit2/include/git2/sys/merge.h".freeze, "vendor/libgit2/include/git2/sys/odb_backend.h".freeze, "vendor/libgit2/include/git2/sys/openssl.h".freeze, "vendor/libgit2/include/git2/sys/refdb_backend.h".freeze, "vendor/libgit2/include/git2/sys/reflog.h".freeze, "vendor/libgit2/include/git2/sys/refs.h".freeze, "vendor/libgit2/include/git2/sys/remote.h".freeze, "vendor/libgit2/include/git2/sys/repository.h".freeze, "vendor/libgit2/include/git2/sys/stream.h".freeze, "vendor/libgit2/include/git2/sys/time.h".freeze, "vendor/libgit2/include/git2/sys/transport.h".freeze, "vendor/libgit2/include/git2/tag.h".freeze, "vendor/libgit2/include/git2/trace.h".freeze, "vendor/libgit2/include/git2/transaction.h".freeze, "vendor/libgit2/include/git2/transport.h".freeze, "vendor/libgit2/include/git2/tree.h".freeze, "vendor/libgit2/include/git2/types.h".freeze, "vendor/libgit2/include/git2/version.h".freeze, "vendor/libgit2/include/git2/worktree.h".freeze, "vendor/libgit2/libgit2.pc.in".freeze, "vendor/libgit2/src/annotated_commit.c".freeze, "vendor/libgit2/src/annotated_commit.h".freeze, "vendor/libgit2/src/apply.c".freeze, "vendor/libgit2/src/apply.h".freeze, "vendor/libgit2/src/array.h".freeze, "vendor/libgit2/src/attr.c".freeze, "vendor/libgit2/src/attr.h".freeze, "vendor/libgit2/src/attr_file.c".freeze, "vendor/libgit2/src/attr_file.h".freeze, "vendor/libgit2/src/attrcache.c".freeze, "vendor/libgit2/src/attrcache.h".freeze, "vendor/libgit2/src/bitvec.h".freeze, "vendor/libgit2/src/blame.c".freeze, "vendor/libgit2/src/blame.h".freeze, "vendor/libgit2/src/blame_git.c".freeze, "vendor/libgit2/src/blame_git.h".freeze, "vendor/libgit2/src/blob.c".freeze, "vendor/libgit2/src/blob.h".freeze, "vendor/libgit2/src/branch.c".freeze, "vendor/libgit2/src/branch.h".freeze, "vendor/libgit2/src/buf_text.c".freeze, "vendor/libgit2/src/buf_text.h".freeze, "vendor/libgit2/src/buffer.c".freeze, "vendor/libgit2/src/buffer.h".freeze, "vendor/libgit2/src/cache.c".freeze, "vendor/libgit2/src/cache.h".freeze, "vendor/libgit2/src/cc-compat.h".freeze, "vendor/libgit2/src/checkout.c".freeze, "vendor/libgit2/src/checkout.h".freeze, "vendor/libgit2/src/cherrypick.c".freeze, "vendor/libgit2/src/clone.c".freeze, "vendor/libgit2/src/clone.h".freeze, "vendor/libgit2/src/commit.c".freeze, "vendor/libgit2/src/commit.h".freeze, "vendor/libgit2/src/commit_list.c".freeze, "vendor/libgit2/src/commit_list.h".freeze, "vendor/libgit2/src/common.h".freeze, "vendor/libgit2/src/config.c".freeze, "vendor/libgit2/src/config.h".freeze, "vendor/libgit2/src/config_cache.c".freeze, "vendor/libgit2/src/config_file.c".freeze, "vendor/libgit2/src/config_file.h".freeze, "vendor/libgit2/src/crlf.c".freeze, "vendor/libgit2/src/curl_stream.c".freeze, "vendor/libgit2/src/curl_stream.h".freeze, "vendor/libgit2/src/date.c".freeze, "vendor/libgit2/src/delta.c".freeze, "vendor/libgit2/src/delta.h".freeze, "vendor/libgit2/src/describe.c".freeze, "vendor/libgit2/src/diff.c".freeze, "vendor/libgit2/src/diff.h".freeze, "vendor/libgit2/src/diff_driver.c".freeze, "vendor/libgit2/src/diff_driver.h".freeze, "vendor/libgit2/src/diff_file.c".freeze, "vendor/libgit2/src/diff_file.h".freeze, "vendor/libgit2/src/diff_generate.c".freeze, "vendor/libgit2/src/diff_generate.h".freeze, "vendor/libgit2/src/diff_parse.c".freeze, "vendor/libgit2/src/diff_parse.h".freeze, "vendor/libgit2/src/diff_print.c".freeze, "vendor/libgit2/src/diff_stats.c".freeze, "vendor/libgit2/src/diff_tform.c".freeze, "vendor/libgit2/src/diff_tform.h".freeze, "vendor/libgit2/src/diff_xdiff.c".freeze, "vendor/libgit2/src/diff_xdiff.h".freeze, "vendor/libgit2/src/errors.c".freeze, "vendor/libgit2/src/fetch.c".freeze, "vendor/libgit2/src/fetch.h".freeze, "vendor/libgit2/src/fetchhead.c".freeze, "vendor/libgit2/src/fetchhead.h".freeze, "vendor/libgit2/src/filebuf.c".freeze, "vendor/libgit2/src/filebuf.h".freeze, "vendor/libgit2/src/fileops.c".freeze, "vendor/libgit2/src/fileops.h".freeze, "vendor/libgit2/src/filter.c".freeze, "vendor/libgit2/src/filter.h".freeze, "vendor/libgit2/src/fnmatch.c".freeze, "vendor/libgit2/src/fnmatch.h".freeze, "vendor/libgit2/src/global.c".freeze, "vendor/libgit2/src/global.h".freeze, "vendor/libgit2/src/graph.c".freeze, "vendor/libgit2/src/hash.c".freeze, "vendor/libgit2/src/hash.h".freeze, "vendor/libgit2/src/hash/hash_collisiondetect.h".freeze, "vendor/libgit2/src/hash/hash_common_crypto.h".freeze, "vendor/libgit2/src/hash/hash_generic.c".freeze, "vendor/libgit2/src/hash/hash_generic.h".freeze, "vendor/libgit2/src/hash/hash_openssl.h".freeze, "vendor/libgit2/src/hash/hash_win32.c".freeze, "vendor/libgit2/src/hash/hash_win32.h".freeze, "vendor/libgit2/src/hash/sha1dc/sha1.c".freeze, "vendor/libgit2/src/hash/sha1dc/sha1.h".freeze, "vendor/libgit2/src/hash/sha1dc/ubc_check.c".freeze, "vendor/libgit2/src/hash/sha1dc/ubc_check.h".freeze, "vendor/libgit2/src/hashsig.c".freeze, "vendor/libgit2/src/ident.c".freeze, "vendor/libgit2/src/idxmap.c".freeze, "vendor/libgit2/src/idxmap.h".freeze, "vendor/libgit2/src/ignore.c".freeze, "vendor/libgit2/src/ignore.h".freeze, "vendor/libgit2/src/index.c".freeze, "vendor/libgit2/src/index.h".freeze, "vendor/libgit2/src/indexer.c".freeze, "vendor/libgit2/src/indexer.h".freeze, "vendor/libgit2/src/integer.h".freeze, "vendor/libgit2/src/iterator.c".freeze, "vendor/libgit2/src/iterator.h".freeze, "vendor/libgit2/src/khash.h".freeze, "vendor/libgit2/src/map.h".freeze, "vendor/libgit2/src/merge.c".freeze, "vendor/libgit2/src/merge.h".freeze, "vendor/libgit2/src/merge_driver.c".freeze, "vendor/libgit2/src/merge_driver.h".freeze, "vendor/libgit2/src/merge_file.c".freeze, "vendor/libgit2/src/message.c".freeze, "vendor/libgit2/src/message.h".freeze, "vendor/libgit2/src/mwindow.c".freeze, "vendor/libgit2/src/mwindow.h".freeze, "vendor/libgit2/src/netops.c".freeze, "vendor/libgit2/src/netops.h".freeze, "vendor/libgit2/src/notes.c".freeze, "vendor/libgit2/src/notes.h".freeze, "vendor/libgit2/src/object.c".freeze, "vendor/libgit2/src/object.h".freeze, "vendor/libgit2/src/object_api.c".freeze, "vendor/libgit2/src/odb.c".freeze, "vendor/libgit2/src/odb.h".freeze, "vendor/libgit2/src/odb_loose.c".freeze, "vendor/libgit2/src/odb_mempack.c".freeze, "vendor/libgit2/src/odb_pack.c".freeze, "vendor/libgit2/src/offmap.c".freeze, "vendor/libgit2/src/offmap.h".freeze, "vendor/libgit2/src/oid.c".freeze, "vendor/libgit2/src/oid.h".freeze, "vendor/libgit2/src/oidarray.c".freeze, "vendor/libgit2/src/oidarray.h".freeze, "vendor/libgit2/src/oidmap.c".freeze, "vendor/libgit2/src/oidmap.h".freeze, "vendor/libgit2/src/openssl_stream.c".freeze, "vendor/libgit2/src/openssl_stream.h".freeze, "vendor/libgit2/src/pack-objects.c".freeze, "vendor/libgit2/src/pack-objects.h".freeze, "vendor/libgit2/src/pack.c".freeze, "vendor/libgit2/src/pack.h".freeze, "vendor/libgit2/src/patch.c".freeze, "vendor/libgit2/src/patch.h".freeze, "vendor/libgit2/src/patch_generate.c".freeze, "vendor/libgit2/src/patch_generate.h".freeze, "vendor/libgit2/src/patch_parse.c".freeze, "vendor/libgit2/src/patch_parse.h".freeze, "vendor/libgit2/src/path.c".freeze, "vendor/libgit2/src/path.h".freeze, "vendor/libgit2/src/pathspec.c".freeze, "vendor/libgit2/src/pathspec.h".freeze, "vendor/libgit2/src/pool.c".freeze, "vendor/libgit2/src/pool.h".freeze, "vendor/libgit2/src/posix.c".freeze, "vendor/libgit2/src/posix.h".freeze, "vendor/libgit2/src/pqueue.c".freeze, "vendor/libgit2/src/pqueue.h".freeze, "vendor/libgit2/src/proxy.c".freeze, "vendor/libgit2/src/proxy.h".freeze, "vendor/libgit2/src/push.c".freeze, "vendor/libgit2/src/push.h".freeze, "vendor/libgit2/src/rebase.c".freeze, "vendor/libgit2/src/refdb.c".freeze, "vendor/libgit2/src/refdb.h".freeze, "vendor/libgit2/src/refdb_fs.c".freeze, "vendor/libgit2/src/refdb_fs.h".freeze, "vendor/libgit2/src/reflog.c".freeze, "vendor/libgit2/src/reflog.h".freeze, "vendor/libgit2/src/refs.c".freeze, "vendor/libgit2/src/refs.h".freeze, "vendor/libgit2/src/refspec.c".freeze, "vendor/libgit2/src/refspec.h".freeze, "vendor/libgit2/src/remote.c".freeze, "vendor/libgit2/src/remote.h".freeze, "vendor/libgit2/src/repo_template.h".freeze, "vendor/libgit2/src/repository.c".freeze, "vendor/libgit2/src/repository.h".freeze, "vendor/libgit2/src/reset.c".freeze, "vendor/libgit2/src/revert.c".freeze, "vendor/libgit2/src/revparse.c".freeze, "vendor/libgit2/src/revwalk.c".freeze, "vendor/libgit2/src/revwalk.h".freeze, "vendor/libgit2/src/settings.c".freeze, "vendor/libgit2/src/sha1_lookup.c".freeze, "vendor/libgit2/src/sha1_lookup.h".freeze, "vendor/libgit2/src/signature.c".freeze, "vendor/libgit2/src/signature.h".freeze, "vendor/libgit2/src/socket_stream.c".freeze, "vendor/libgit2/src/socket_stream.h".freeze, "vendor/libgit2/src/sortedcache.c".freeze, "vendor/libgit2/src/sortedcache.h".freeze, "vendor/libgit2/src/stash.c".freeze, "vendor/libgit2/src/status.c".freeze, "vendor/libgit2/src/status.h".freeze, "vendor/libgit2/src/stransport_stream.c".freeze, "vendor/libgit2/src/stransport_stream.h".freeze, "vendor/libgit2/src/stream.h".freeze, "vendor/libgit2/src/strmap.c".freeze, "vendor/libgit2/src/strmap.h".freeze, "vendor/libgit2/src/strnlen.h".freeze, "vendor/libgit2/src/submodule.c".freeze, "vendor/libgit2/src/submodule.h".freeze, "vendor/libgit2/src/sysdir.c".freeze, "vendor/libgit2/src/sysdir.h".freeze, "vendor/libgit2/src/tag.c".freeze, "vendor/libgit2/src/tag.h".freeze, "vendor/libgit2/src/thread-utils.c".freeze, "vendor/libgit2/src/thread-utils.h".freeze, "vendor/libgit2/src/tls_stream.c".freeze, "vendor/libgit2/src/tls_stream.h".freeze, "vendor/libgit2/src/trace.c".freeze, "vendor/libgit2/src/trace.h".freeze, "vendor/libgit2/src/transaction.c".freeze, "vendor/libgit2/src/transaction.h".freeze, "vendor/libgit2/src/transport.c".freeze, "vendor/libgit2/src/transports/auth.c".freeze, "vendor/libgit2/src/transports/auth.h".freeze, "vendor/libgit2/src/transports/auth_negotiate.c".freeze, "vendor/libgit2/src/transports/auth_negotiate.h".freeze, "vendor/libgit2/src/transports/cred.c".freeze, "vendor/libgit2/src/transports/cred.h".freeze, "vendor/libgit2/src/transports/cred_helpers.c".freeze, "vendor/libgit2/src/transports/git.c".freeze, "vendor/libgit2/src/transports/http.c".freeze, "vendor/libgit2/src/transports/local.c".freeze, "vendor/libgit2/src/transports/smart.c".freeze, "vendor/libgit2/src/transports/smart.h".freeze, "vendor/libgit2/src/transports/smart_pkt.c".freeze, "vendor/libgit2/src/transports/smart_protocol.c".freeze, "vendor/libgit2/src/transports/ssh.c".freeze, "vendor/libgit2/src/transports/ssh.h".freeze, "vendor/libgit2/src/transports/winhttp.c".freeze, "vendor/libgit2/src/tree-cache.c".freeze, "vendor/libgit2/src/tree-cache.h".freeze, "vendor/libgit2/src/tree.c".freeze, "vendor/libgit2/src/tree.h".freeze, "vendor/libgit2/src/tsort.c".freeze, "vendor/libgit2/src/unix/map.c".freeze, "vendor/libgit2/src/unix/posix.h".freeze, "vendor/libgit2/src/unix/pthread.h".freeze, "vendor/libgit2/src/unix/realpath.c".freeze, "vendor/libgit2/src/userdiff.h".freeze, "vendor/libgit2/src/util.c".freeze, "vendor/libgit2/src/util.h".freeze, "vendor/libgit2/src/varint.c".freeze, "vendor/libgit2/src/varint.h".freeze, "vendor/libgit2/src/vector.c".freeze, "vendor/libgit2/src/vector.h".freeze, "vendor/libgit2/src/win32/dir.c".freeze, "vendor/libgit2/src/win32/dir.h".freeze, "vendor/libgit2/src/win32/error.c".freeze, "vendor/libgit2/src/win32/error.h".freeze, "vendor/libgit2/src/win32/findfile.c".freeze, "vendor/libgit2/src/win32/findfile.h".freeze, "vendor/libgit2/src/win32/git2.rc".freeze, "vendor/libgit2/src/win32/map.c".freeze, "vendor/libgit2/src/win32/mingw-compat.h".freeze, "vendor/libgit2/src/win32/msvc-compat.h".freeze, "vendor/libgit2/src/win32/path_w32.c".freeze, "vendor/libgit2/src/win32/path_w32.h".freeze, "vendor/libgit2/src/win32/posix.h".freeze, "vendor/libgit2/src/win32/posix_w32.c".freeze, "vendor/libgit2/src/win32/precompiled.c".freeze, "vendor/libgit2/src/win32/precompiled.h".freeze, "vendor/libgit2/src/win32/reparse.h".freeze, "vendor/libgit2/src/win32/thread.c".freeze, "vendor/libgit2/src/win32/thread.h".freeze, "vendor/libgit2/src/win32/utf-conv.c".freeze, "vendor/libgit2/src/win32/utf-conv.h".freeze, "vendor/libgit2/src/win32/version.h".freeze, "vendor/libgit2/src/win32/w32_buffer.c".freeze, "vendor/libgit2/src/win32/w32_buffer.h".freeze, "vendor/libgit2/src/win32/w32_crtdbg_stacktrace.c".freeze, "vendor/libgit2/src/win32/w32_crtdbg_stacktrace.h".freeze, "vendor/libgit2/src/win32/w32_stack.c".freeze, "vendor/libgit2/src/win32/w32_stack.h".freeze, "vendor/libgit2/src/win32/w32_util.c".freeze, "vendor/libgit2/src/win32/w32_util.h".freeze, "vendor/libgit2/src/win32/win32-compat.h".freeze, "vendor/libgit2/src/worktree.c".freeze, "vendor/libgit2/src/worktree.h".freeze, "vendor/libgit2/src/xdiff/xdiff.h".freeze, "vendor/libgit2/src/xdiff/xdiffi.c".freeze, "vendor/libgit2/src/xdiff/xdiffi.h".freeze, "vendor/libgit2/src/xdiff/xemit.c".freeze, "vendor/libgit2/src/xdiff/xemit.h".freeze, "vendor/libgit2/src/xdiff/xhistogram.c".freeze, "vendor/libgit2/src/xdiff/xinclude.h".freeze, "vendor/libgit2/src/xdiff/xmacros.h".freeze, "vendor/libgit2/src/xdiff/xmerge.c".freeze, "vendor/libgit2/src/xdiff/xpatience.c".freeze, "vendor/libgit2/src/xdiff/xprepare.c".freeze, "vendor/libgit2/src/xdiff/xprepare.h".freeze, "vendor/libgit2/src/xdiff/xtypes.h".freeze, "vendor/libgit2/src/xdiff/xutils.c".freeze, "vendor/libgit2/src/xdiff/xutils.h".freeze, "vendor/libgit2/src/zstream.c".freeze, "vendor/libgit2/src/zstream.h".freeze] s.homepage = "https://github.com/libgit2/rugged".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 1.9.3".freeze) s.rubygems_version = "2.5.2".freeze s.summary = "Rugged is a Ruby binding to the libgit2 linkable library".freeze if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, ["~> 5.0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0.9.0"]) else s.add_dependency(%q.freeze, ["~> 5.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0.9.0"]) end else s.add_dependency(%q.freeze, ["~> 5.0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0.9.0"]) end end