Ejemplo n.º 1
0
/*
 *  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);
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
/*
 *  call-seq:
 *    walker.each { |commit| block }
 *    walker.each -> Iterator
 *
 *  Perform the walk through the repository, yielding each
 *  one of the commits found as a <tt>Rugged::Commit</tt> instance
 *  to +block+.
 *
 *  If no +block+ is given, an +Iterator+ 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(VALUE self)
{
	git_revwalk *walk;
	git_commit *commit;
	git_repository *repo;
	git_oid commit_oid;
	int error;

	Data_Get_Struct(self, git_revwalk, walk);
	repo = git_revwalk_repository(walk);

	if (!rb_block_given_p())
		return rb_funcall(self, rb_intern("to_enum"), 0);

	while ((error = git_revwalk_next(&commit_oid, walk)) == 0) {
		error = git_commit_lookup(&commit, repo, &commit_oid);
		rugged_exception_check(error);

		rb_yield(rugged_object_new(rugged_owner(self), (git_object *)commit));
	}

	if (error != GIT_ITEROVER)
		rugged_exception_check(error);

	return Qnil;
}
Ejemplo n.º 4
0
/*
 *  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);
}
Ejemplo n.º 5
0
/*
 *  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));
}
Ejemplo n.º 6
0
/*
 *  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;
}
Ejemplo n.º 7
0
/*
 *  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);
	}
}
/*
 *  call-seq:
 *    submodules.setup_add(url, path[, options]) -> submodule
 *
 *  Setup a new +submodule+ for checkout in +repository+.
 *
 *  This does <tt>"git submodule add"</tt> 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 <tt>"git submodule add"</tt> 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);
}
Ejemplo n.º 9
0
/*
 *  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);
}
Ejemplo n.º 10
0
/*
 *  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;
}
Ejemplo n.º 11
0
/*
 *  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.
 *
 *  :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+.
 *
 *  :message ::
 *    The message to insert into the reflogs. Defaults to "fetch".
 *
 *  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_repository *repo;
	git_strarray refspecs;
	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
	struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };

	char *log_message = NULL;
	int error;

	VALUE rb_options, rb_refspecs, rb_result = Qnil, rb_repo = rugged_owner(self);

	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_check_repo(rb_repo);
	Data_Get_Struct(rb_repo, git_repository, repo);

	rugged_remote_init_callbacks_and_payload_from_options(rb_options, &callbacks, &payload);

	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);
	}

	if ((error = git_remote_set_callbacks(remote, &callbacks)))
		goto cleanup;

	if ((error = git_remote_fetch(remote, &refspecs, log_message)) == GIT_OK) {
		const git_transfer_progress *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));
	}

	cleanup:

	xfree(refspecs.strings);

	if (payload.exception)
		rb_jump_tag(payload.exception);

	rugged_exception_check(error);

	return rb_result;
}
Ejemplo n.º 12
0
/*
 *  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);
}
Ejemplo n.º 13
0
/*
 *  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=>"*****@*****.**", :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;
}
Ejemplo n.º 14
0
/*
 *  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 #=> #<Rugged::Reference:0x401b3948>
 *    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);
}
Ejemplo n.º 15
0
static VALUE rb_git_merge_file(int argc, VALUE *argv, VALUE self)
{
	VALUE rb_path, rb_options;
	VALUE rb_result = rb_hash_new();
	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)) {
		Check_Type(rb_options, T_HASH);
		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_hash_aset(rb_result, CSTR2SYM("automergeable"), merge_file_result.automergeable ? Qtrue : Qfalse);
	rb_hash_aset(rb_result, CSTR2SYM("path"),         rb_path);
	rb_hash_aset(rb_result, CSTR2SYM("filemode"),     INT2FIX(merge_file_result.mode));
	rb_hash_aset(rb_result, CSTR2SYM("data"),         rb_str_new(merge_file_result.ptr, merge_file_result.len));

	git_merge_file_result_free(&merge_file_result);

	return rb_result;
}
Ejemplo n.º 16
0
/*
 *  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 #=> #<Rugged::Reference ...>
 *
 *    ref2.type #=> :direct
 *    ref2.target #=> #<Rugged::Commit ...>
 */
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);
	}
}
Ejemplo n.º 17
0
/*
 *  call-seq:
 *    annotation.target -> object
 *
 *  Return the +object+ pointed at by this tag annotation, as a <tt>Rugged::Object</tt>
 *  instance.
 *
 *    annotation.target #=> #<Rugged::Commit:0x108828918>
 */
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);
}
Ejemplo n.º 18
0
/*
 *  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;
}
Ejemplo n.º 19
0
/*
 *  call-seq:
 *    commit.tree -> tree
 *
 *  Return the tree pointed at by this +commit+. The tree is
 *  returned as a +Rugged::Tree+ object.
 *
 *    commit.tree #=> #<Rugged::Tree:0x10882c680>
 */
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);
}
Ejemplo n.º 20
0
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;
}
Ejemplo n.º 21
0
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;
}
Ejemplo n.º 22
0
/*
 *  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.
 */
static VALUE rb_git_index_diff(int argc, VALUE *argv, VALUE self)
{
	git_index *index;
	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
	git_repository *repo;
	git_diff *diff = NULL;
	VALUE owner, rb_other, rb_options;
	int error;

	rb_scan_args(argc, argv, "01:", &rb_other, &rb_options);
	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);

	if (NIL_P(rb_other)) {
		error = git_diff_index_to_workdir(&diff, repo, index, &opts);
	} else {
		// 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;

		if (rb_obj_is_kind_of(rb_other, rb_cRuggedCommit)) {
			git_tree *other_tree;
			git_commit *commit;
			Data_Get_Struct(rb_other, git_commit, commit);
			error = git_commit_tree(&other_tree, commit);

			if (!error)
				error = git_diff_tree_to_index(&diff, repo, other_tree, index, &opts);
		} else if (rb_obj_is_kind_of(rb_other, rb_cRuggedTree)) {
			git_tree *other_tree;
			Data_Get_Struct(rb_other, git_tree, other_tree);
			error = git_diff_tree_to_index(&diff, repo, other_tree, index, &opts);
		} else {
			xfree(opts.pathspec.strings);
			rb_raise(rb_eTypeError, "A Rugged::Commit or Rugged::Tree instance is required");
		}
	}

	xfree(opts.pathspec.strings);
	rugged_exception_check(error);

	return rugged_diff_new(rb_cRuggedDiff, owner, diff);
}
Ejemplo n.º 23
0
/*
 *  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`).
 *
 *  :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);
}
Ejemplo n.º 24
0
/*
 *  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 = '[email protected]/libgit2/rugged.git' #=> "[email protected]/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;
}
Ejemplo n.º 25
0
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;
}
Ejemplo n.º 26
0
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;
}
Ejemplo n.º 27
0
/*
 *  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);
}
Ejemplo n.º 28
0
/*
 *  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);
}
Ejemplo n.º 29
0
/*
 *  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);
}
Ejemplo n.º 30
0
/*
 *  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;
	int error;

	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_delete(repo, StringValueCStr(rb_name_or_remote));
	rugged_exception_check(error);

	return Qnil;
}