From 962aeb3042277d4862260f5c8f86953d785ef2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 5 Oct 2012 15:26:04 +0200 Subject: [PATCH 01/34] Bump libgit2 --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index 62eafd062..f9cf12b4f 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit 62eafd0620eff3d7ca3659a3f4a4808488f0b2c3 +Subproject commit f9cf12b4f58588309c0329d0525f1505f579c32d From b613a8b0e09b8ca82f90ceb1b7e2815004d0a0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 5 Oct 2012 18:40:47 +0200 Subject: [PATCH 02/34] Fix GIT_REVWALKOVER -> GIT_ITEROVER (renamed in libgit2) --- ext/rugged/rugged_revwalk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/rugged/rugged_revwalk.c b/ext/rugged/rugged_revwalk.c index 02b384e60..2ae1efe7f 100644 --- a/ext/rugged/rugged_revwalk.c +++ b/ext/rugged/rugged_revwalk.c @@ -106,7 +106,7 @@ static VALUE rb_git_walker_each(VALUE self) rb_yield(rugged_object_new(rugged_owner(self), (git_object *)commit)); } - if (error != GIT_REVWALKOVER) + if (error != GIT_ITEROVER) rugged_exception_check(error); return Qnil; From e28f102e7d81fcf78ee768f3a6f7140e5f958fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 15:39:05 +0200 Subject: [PATCH 03/34] Fix Repository#create_branch crashing when a direct reference was passed. The behaviour of git_reference_lookup has slightly changed upstream; instead of returning GIT_ENOTFOUND it now returns GIT_ERROR on invalid reference names. However, the function git_reference_is_valid_name() can be used to test whether a given reference name is well-formed. This function now is available from Ruby as Reference.valid? and is used inside Repository#create_branch. --- ext/rugged/rugged_reference.c | 17 +++++++++++++++++ lib/rugged/repository.rb | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ext/rugged/rugged_reference.c b/ext/rugged/rugged_reference.c index 974b40259..008a6afb7 100644 --- a/ext/rugged/rugged_reference.c +++ b/ext/rugged/rugged_reference.c @@ -168,6 +168,22 @@ static VALUE rb_git_ref_exist(VALUE klass, VALUE rb_repo, VALUE rb_name) return Qtrue; } +/* + * call-seq: + * Reference.valid?(name) -> true or false + * + * Checks whether the given string is a well-formed reference + * name. Note this just checks the syntax, no semantic checks + * are performed. + */ +static VALUE rb_git_ref_valid(VALUE self, VALUE rb_str) +{ + if (git_reference_is_valid_name(StringValueCStr(rb_str))) + return Qtrue; + else + return Qfalse; +} + /* * call-seq: * Reference.create(repository, name, oid, force = false) -> new_ref @@ -561,6 +577,7 @@ void Init_rugged_reference() rb_define_singleton_method(rb_cRuggedReference, "lookup", rb_git_ref_lookup, 2); rb_define_singleton_method(rb_cRuggedReference, "exist?", rb_git_ref_exist, 2); rb_define_singleton_method(rb_cRuggedReference, "exists?", rb_git_ref_exist, 2); + rb_define_singleton_method(rb_cRuggedReference, "valid?", rb_git_ref_valid, 1); rb_define_singleton_method(rb_cRuggedReference, "create", rb_git_ref_create, -1); rb_define_singleton_method(rb_cRuggedReference, "pack_all", rb_git_ref_packall, 1); rb_define_singleton_method(rb_cRuggedReference, "each", rb_git_ref_each, -1); diff --git a/lib/rugged/repository.rb b/lib/rugged/repository.rb index cde53cb5f..4d94fd842 100644 --- a/lib/rugged/repository.rb +++ b/lib/rugged/repository.rb @@ -140,7 +140,8 @@ def create_branch(name, sha_or_ref = "HEAD") when Rugged::Object target = sha_or_ref.oid else - if (ref = Rugged::Reference.lookup(self, sha_or_ref)) + if Rugged::Reference.valid?(sha_or_ref) + ref = Rugged::Reference.lookup(self, sha_or_ref) target = ref.resolve.target else target = Rugged::Commit.lookup(self, sha_or_ref) From e068d35433460854806067f25b2199f942b01d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 15:56:54 +0200 Subject: [PATCH 04/34] Add tests for Reference.valid? --- test/reference_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/reference_test.rb b/test/reference_test.rb index 3a665a734..860d21adf 100644 --- a/test/reference_test.rb +++ b/test/reference_test.rb @@ -65,6 +65,13 @@ assert !exists end + test "recognises valid/invalid reference names" do + assert Rugged::Reference.valid?("refs/heads/master") + assert Rugged::Reference.valid?("HEAD") + assert Rugged::Reference.valid?("refs/heads/n0n3x15t4nt") # Only syntax check + assert !Rugged::Reference.valid?("5b5b025afb0b4c913b4c338a42934a3863bf3644") # Commits are no valid reference *names* + end + test "can open packed reference" do ref = Rugged::Reference.lookup(@repo, "refs/heads/packed") assert_equal "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9", ref.target From 67ba33b544138d88a713ace0a0f1bbb5479e3412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 5 Oct 2012 12:00:10 +0200 Subject: [PATCH 05/34] Add Rugged::Repository.clone. However, the underlying libgit2 doesn't support the file:// protocol yet. --- ext/rugged/rugged_repo.c | 30 ++++++++++++++++++++++++++++++ test/repo_test.rb | 17 ++++++++++++++++- test/test_helper.rb | 7 +++++-- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 3212e51f3..b7c2a74a1 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -199,6 +199,35 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) return rugged_repo_new(klass, repo); } +/* + * call-seq: + * Rugged::Repository.clone_repo(url, target_path) -> repository + * + * Clone a Git repository from the remote at +url+ into the given + * local +target_path+ and return a Repository object pointing to + * the freshly cloned repository's .git directory. + * + * Rugged::Repository.clone("git://github.com/libgit2/libgit2.git", "~/libgit2") + */ +static VALUE rb_git_repo_clone(VALUE klass, VALUE url, VALUE target_path) +{ /* TODO: Bare repos | Progress block | Exception docs */ + git_repository *repo; + int error; + + Check_Type(url, T_STRING); + Check_Type(target_path, T_STRING); + + error = git_clone(&repo, + StringValueCStr(url), + StringValueCStr(target_path), + NULL, /* TODO: Fetch stats for Ruby block */ + NULL, /* Not documented */ + NULL /* Not documented */); + 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)) { \ @@ -696,6 +725,7 @@ void Init_rugged_repo() rb_define_singleton_method(rb_cRuggedRepo, "hash", 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, 2); + rb_define_singleton_method(rb_cRuggedRepo, "clone_repo", rb_git_repo_clone, 2); rb_define_singleton_method(rb_cRuggedRepo, "discover", rb_git_repo_discover, -1); rb_define_method(rb_cRuggedRepo, "exists?", rb_git_repo_exists, 1); diff --git a/test/repo_test.rb b/test/repo_test.rb index 29ccaf1ad..b39e84f8a 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -3,7 +3,7 @@ context "Rugged::Repository stuff" do setup do - @path = File.dirname(__FILE__) + '/fixtures/testrepo.git/' + @path = test_repo_path("testrepo.git") @repo = Rugged::Repository.new(@path) @test_content = "my test data\n" @@ -139,4 +139,19 @@ ObjectSpace.garbage_collect end + test "can successfully clone a repository" do + Dir.mktmpdir("clone-target-dir") do |tmpdir| + repo = Rugged::Repository.clone_repo("file://#@path", tmpdir) + assert "36060c58702ed4c2a40832c51758d5344201d89a", repo.last_commit.oid + assert repo.lookup("8496071c1b46c854b31185ea97743be6a8774479").kind_of?(Rugged::Commit) + + ref = repo.ref('refs/heads/master') + assert ref.kind_of?(Rugged::Reference) + assert_equal 'refs/heads/master', ref.name + + refs = repo.refs + assert_equal 4, refs.length + end + end + end diff --git a/test/test_helper.rb b/test/test_helper.rb index 60e64a19d..2c8907c96 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -25,10 +25,13 @@ class << self private + def test_repo_path(repo) + File.join(TEST_DIR, (File.join('fixtures', repo, '.'))) + end + def temp_repo(repo) dir = Dir.mktmpdir 'dir' - repo_dir = File.join(TEST_DIR, (File.join('fixtures', repo, '.'))) - `git clone #{repo_dir} #{dir}` + `git clone #{test_repo_path(repo)} #{dir}` dir end From a1222be2ff4144e5c3c24753cefd28abd2de05eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 5 Oct 2012 18:41:07 +0200 Subject: [PATCH 06/34] Add wrappers for git_checkout_tree et al --- ext/rugged/rugged_repo.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index b7c2a74a1..6a31792e7 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -28,6 +28,7 @@ extern VALUE rb_mRugged; extern VALUE rb_cRuggedIndex; extern VALUE rb_cRuggedConfig; extern VALUE rb_cRuggedBackend; +extern VALUE rb_cRuggedObject; VALUE rb_cRuggedRepo; VALUE rb_cRuggedOdbObject; @@ -569,6 +570,50 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) return Qnil; } +static VALUE rb_git_repo_checkout_head(VALUE self) +{ + git_repository *repo; + int error; + + error = git_checkout_head(repo, NULL, NULL); /* TODO: Progress access for block */ + rugged_exception_check(error); + + return Qnil; +} + +static VALUE rb_git_repo_checkout_index(VALUE self) +{ + git_repository *repo; + int error; + + error = git_checkout_index(repo, NULL, NULL); /* TODO: Progress access for block */ + rugged_exception_check(error); + + return Qnil; +} + +static VALUE rb_git_repo_checkout_tree(VALUE self, VALUE target) +{ + git_repository *repo; + int error; + git_object *treeish; + git_index *index; + + Data_Get_Struct(self, git_repository, repo); + + if (!rb_obj_is_kind_of(target, rb_cRuggedObject)) + rb_raise(rb_eTypeError, "Expected argument #1 to be a Rugged::Object (a commit, a tag or a tree)!"); + Data_Get_Struct(target, git_object, treeish); + + /* Update the index */ + error = git_checkout_tree(repo, treeish, NULL, NULL); /* TODO: Progress access for block */ + rugged_exception_check(error); + + /* TODO: Update the HEAD pointer -- detached HEAD? */ + + return Qnil; +} + /* * call-seq: * Repository.discover(path = nil, across_fs = true) -> repository @@ -749,6 +794,10 @@ void Init_rugged_repo() rb_define_method(rb_cRuggedRepo, "head_detached?", rb_git_repo_head_detached, 0); rb_define_method(rb_cRuggedRepo, "head_orphan?", rb_git_repo_head_orphan, 0); + rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_repo_checkout_tree, 1); + rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_repo_checkout_head, 0); + rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_repo_checkout_index, 0); + 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); From 99f96dc498813d8dc73a5c3accb2b7f98472cf6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 09:58:07 +0200 Subject: [PATCH 07/34] First working implementation of Repository#checkout_tree . --- ext/rugged/rugged_repo.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 6a31792e7..b419b98ad 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include #include "rugged.h" extern VALUE rb_mRugged; @@ -593,11 +594,13 @@ static VALUE rb_git_repo_checkout_index(VALUE self) } static VALUE rb_git_repo_checkout_tree(VALUE self, VALUE target) -{ +{ /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ git_repository *repo; int error; git_object *treeish; git_index *index; + git_checkout_opts opts; + git_strarray ary; Data_Get_Struct(self, git_repository, repo); @@ -605,8 +608,19 @@ static VALUE rb_git_repo_checkout_tree(VALUE self, VALUE target) rb_raise(rb_eTypeError, "Expected argument #1 to be a Rugged::Object (a commit, a tag or a tree)!"); Data_Get_Struct(target, git_object, treeish); + /* Set the checkout options */ + /* TODO: Hand these options to the user's control */ + ary.count = 0; /* 0 means checking out ALL files */ + opts.checkout_strategy = GIT_CHECKOUT_OVERWRITE_MODIFIED | GIT_CHECKOUT_CREATE_MISSING | GIT_CHECKOUT_REMOVE_UNTRACKED; + opts.disable_filters = 0; /* default value */ + opts.dir_mode = 0755; /* default value */ + opts.file_mode = 0644; /* default value */ + opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY; /* default value */ + opts.notify_payload = NULL; /* default value */ + opts.paths = ary; /* tricky, the default value segfaults; see `ary.count=0' above. */ + /* Update the index */ - error = git_checkout_tree(repo, treeish, NULL, NULL); /* TODO: Progress access for block */ + error = git_checkout_tree(repo, treeish, &opts, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); /* TODO: Update the HEAD pointer -- detached HEAD? */ From 678903062303419c95d395115de030cdaf11f719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 12:16:31 +0200 Subject: [PATCH 08/34] Further complete Repository#checkout, add docs. --- ext/rugged/rugged_repo.c | 91 ++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index b419b98ad..453c80e39 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -593,8 +593,51 @@ static VALUE rb_git_repo_checkout_index(VALUE self) return Qnil; } -static VALUE rb_git_repo_checkout_tree(VALUE self, VALUE target) +/* + * call-seq: + * repo.checkout_tree(treeish [, opts = {}]) + * + * Reads the tree pointed to by +treeish+ into the index and if requested, + * writes the index out to the working tree afterwards. Note this method + * does *not* update the HEAD pointer, so you probably want to do this + * afterwards: + * + * # Checkout the branch "yourbranch" + * ref = Rugged::Reference.new(repo, "refs/heads/yourbranch") + * commit = Rugged::Commit.lookup(repo, ref.target) + * repo.checkout_tree(commit, strategy: [:overwrite_modified, :create_missing, :remove_untracked]) + * + * # Update the HEAD pointer to point to "yourbranch" + * head = Rugged::Reference.lookup(repo, "HEAD") + * head.target = ref.name + * + * +opts+ is a hash which can take the following values: + * [:strategy ([:default])] + * Defines how to exactly check out the given +treeish+. This is an + * array containing one or more of the following symbols: + * [:default] Read +treeish+ into the index. You should always include this. + * [:overwrite_modified] Write files out to the working tree, overwriting files. + * [:create_missing] Write files out to the working tree, creating files that are not there yet. + * [:remove_untracked] Write files out to the working tree and remove all files not checked into Git. + * [:disable_filters (0)] + * Not documented by libgit2. + * [:dir_mode (0755)] + * Not documented by libgit2. + * [:file_mode (0664)] + * Not documented by libgit2. + * [:file_open_flags (IO::CREAT | IO::TRUNC | IO::WRONLY)] + * Not documented by libgit2. + */ +static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) { /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ + VALUE target; + VALUE ruby_opts; + VALUE opts_strategy; + VALUE opts_disable_filters; + VALUE opts_dir_mode; + VALUE opts_file_mode; + VALUE opts_file_open_flags; + git_repository *repo; int error; git_object *treeish; @@ -604,27 +647,51 @@ static VALUE rb_git_repo_checkout_tree(VALUE self, VALUE target) Data_Get_Struct(self, git_repository, repo); + /* Grab the Ruby hash values */ + rb_scan_args(argc, argv, "1:", &target, &ruby_opts); + if (TYPE(ruby_opts) != T_HASH) + ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ + + opts_strategy = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("strategy"))); + opts_disable_filters = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("disable_filters"))); + opts_dir_mode = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("dir_mode"))); + opts_file_mode = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("file_mode"))); + opts_file_open_flags = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + if (!rb_obj_is_kind_of(target, rb_cRuggedObject)) rb_raise(rb_eTypeError, "Expected argument #1 to be a Rugged::Object (a commit, a tag or a tree)!"); Data_Get_Struct(target, git_object, treeish); - /* Set the checkout options */ - /* TODO: Hand these options to the user's control */ + /* Convert the Ruby hash values to C stuff */ + opts.disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ + opts.dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2NUM(opts_dir_mode) : 0755 /* libgit2 default */; + opts.file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2NUM(opts_file_mode) : 0644 /* libgit2 default */; + opts.file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2NUM(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; + + if (TYPE(opts_strategy) == T_ARRAY){ + opts.checkout_strategy = 0; /* Ensure we start with a clean slate */ + + /* Now OR-in all requested flags */ + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) + opts.checkout_strategy |= GIT_CHECKOUT_DEFAULT; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) + opts.checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) + opts.checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) + opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + } + else + opts.checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ + + /* Set other options (segfaults without) */ ary.count = 0; /* 0 means checking out ALL files */ - opts.checkout_strategy = GIT_CHECKOUT_OVERWRITE_MODIFIED | GIT_CHECKOUT_CREATE_MISSING | GIT_CHECKOUT_REMOVE_UNTRACKED; - opts.disable_filters = 0; /* default value */ - opts.dir_mode = 0755; /* default value */ - opts.file_mode = 0644; /* default value */ - opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY; /* default value */ opts.notify_payload = NULL; /* default value */ opts.paths = ary; /* tricky, the default value segfaults; see `ary.count=0' above. */ /* Update the index */ error = git_checkout_tree(repo, treeish, &opts, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); - - /* TODO: Update the HEAD pointer -- detached HEAD? */ - return Qnil; } @@ -808,7 +875,7 @@ void Init_rugged_repo() rb_define_method(rb_cRuggedRepo, "head_detached?", rb_git_repo_head_detached, 0); rb_define_method(rb_cRuggedRepo, "head_orphan?", rb_git_repo_head_orphan, 0); - rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_repo_checkout_tree, 1); + rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_repo_checkout_tree, -1); rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_repo_checkout_head, 0); rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_repo_checkout_index, 0); From ba34a71d412e32489f67a33785e7897752700083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 13:09:18 +0200 Subject: [PATCH 09/34] Extract the checkout argument parsing into an extra function, add Repository#checkout_index. --- ext/rugged/rugged_repo.c | 152 +++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 61 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 453c80e39..174afbee1 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -571,23 +571,106 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) return Qnil; } -static VALUE rb_git_repo_checkout_head(VALUE self) +/* Helper function used by the checkout_* methods to parse the argument hash. + * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results + * of the parsing process. */ +static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) { + VALUE opts_strategy; + VALUE opts_disable_filters; + VALUE opts_dir_mode; + VALUE opts_file_mode; + VALUE opts_file_open_flags; + git_strarray paths; + + opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); + opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); + opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); + opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); + opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + + /* Convert the Ruby hash values to C stuff */ + p_opts->disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ + p_opts->dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2INT(opts_dir_mode) : 0755 /* libgit2 default */; + p_opts->file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2INT(opts_file_mode) : 0644 /* libgit2 default */; + p_opts->file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2INT(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; + + if (TYPE(opts_strategy) == T_ARRAY){ + p_opts->checkout_strategy = 0; /* Ensure we start with a clean slate */ + + /* Now OR-in all requested flags */ + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + } + else + p_opts->checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ + + /* Set other options (segfaults without) */ + paths.count = 0; /* 0 means checking out ALL files */ + p_opts->paths = paths; /* tricky, the default value segfaults; see `paths.count=0' above. */ + p_opts->notify_payload = NULL; /* default value */ +} + +/* + * call-seq: + * repo.checkout_index( [ opts = {} ] ) + * + * Writes the files in the index ("staging area") out to the working + * tree. Does not update the HEAD pointer. + * + * +opts+ is a hash which can take the following values: + * [:strategy ([:default])] + * Defines how to exactly check out the given +treeish+. This is an + * array containing one or more of the following symbols: + * [:default] Don't overwrite anything. + * [:overwrite_modified] Overwrite files already there. + * [:create_missing] If a file is in the index, but not in the worktree, copy it nevertheless to the work tree. + * [:remove_untracked] Delete files from the working tree not checked into Git. + * [:disable_filters (0)] + * Not documented by libgit2. + * [:dir_mode (0755)] + * Not documented by libgit2. + * [:file_mode (0664)] + * Not documented by libgit2. + * [:file_open_flags (IO::CREAT | IO::TRUNC | IO::WRONLY)] + * Not documented by libgit2. + */ +static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) +{ + VALUE target; + VALUE ruby_opts; git_repository *repo; int error; + git_checkout_opts opts; - error = git_checkout_head(repo, NULL, NULL); /* TODO: Progress access for block */ + Data_Get_Struct(self, git_repository, repo); + + /* Grab the options hash */ + rb_scan_args(argc, argv, "1:", &target, &ruby_opts); + if (TYPE(ruby_opts) != T_HASH) + ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ + + /* Parse the wealth of options and collect the results in `opts' */ + rb_git__parse_checkout_options(&ruby_opts, &opts); + + error = git_checkout_index(repo, &opts, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); return Qnil; } -static VALUE rb_git_repo_checkout_index(VALUE self) +static VALUE rb_git_repo_checkout_head(VALUE self) { git_repository *repo; int error; - error = git_checkout_index(repo, NULL, NULL); /* TODO: Progress access for block */ + error = git_checkout_head(repo, NULL, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); return Qnil; @@ -611,83 +694,30 @@ static VALUE rb_git_repo_checkout_index(VALUE self) * head = Rugged::Reference.lookup(repo, "HEAD") * head.target = ref.name * - * +opts+ is a hash which can take the following values: - * [:strategy ([:default])] - * Defines how to exactly check out the given +treeish+. This is an - * array containing one or more of the following symbols: - * [:default] Read +treeish+ into the index. You should always include this. - * [:overwrite_modified] Write files out to the working tree, overwriting files. - * [:create_missing] Write files out to the working tree, creating files that are not there yet. - * [:remove_untracked] Write files out to the working tree and remove all files not checked into Git. - * [:disable_filters (0)] - * Not documented by libgit2. - * [:dir_mode (0755)] - * Not documented by libgit2. - * [:file_mode (0664)] - * Not documented by libgit2. - * [:file_open_flags (IO::CREAT | IO::TRUNC | IO::WRONLY)] - * Not documented by libgit2. + * See #checkout_tree for the values you can pass via +opts+. */ static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) { /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ VALUE target; VALUE ruby_opts; - VALUE opts_strategy; - VALUE opts_disable_filters; - VALUE opts_dir_mode; - VALUE opts_file_mode; - VALUE opts_file_open_flags; - git_repository *repo; int error; git_object *treeish; - git_index *index; git_checkout_opts opts; - git_strarray ary; Data_Get_Struct(self, git_repository, repo); - /* Grab the Ruby hash values */ + /* Grab the options hash */ rb_scan_args(argc, argv, "1:", &target, &ruby_opts); if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ - opts_strategy = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("strategy"))); - opts_disable_filters = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("disable_filters"))); - opts_dir_mode = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("dir_mode"))); - opts_file_mode = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("file_mode"))); - opts_file_open_flags = rb_hash_aref(ruby_opts, ID2SYM(rb_intern("file_open_flags"))); - if (!rb_obj_is_kind_of(target, rb_cRuggedObject)) rb_raise(rb_eTypeError, "Expected argument #1 to be a Rugged::Object (a commit, a tag or a tree)!"); Data_Get_Struct(target, git_object, treeish); - /* Convert the Ruby hash values to C stuff */ - opts.disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ - opts.dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2NUM(opts_dir_mode) : 0755 /* libgit2 default */; - opts.file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2NUM(opts_file_mode) : 0644 /* libgit2 default */; - opts.file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2NUM(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; - - if (TYPE(opts_strategy) == T_ARRAY){ - opts.checkout_strategy = 0; /* Ensure we start with a clean slate */ - - /* Now OR-in all requested flags */ - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) - opts.checkout_strategy |= GIT_CHECKOUT_DEFAULT; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) - opts.checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) - opts.checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) - opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; - } - else - opts.checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ - - /* Set other options (segfaults without) */ - ary.count = 0; /* 0 means checking out ALL files */ - opts.notify_payload = NULL; /* default value */ - opts.paths = ary; /* tricky, the default value segfaults; see `ary.count=0' above. */ + /* Parse the wealth of options and collect the results in `opts' */ + rb_git__parse_checkout_options(&ruby_opts, &opts); /* Update the index */ error = git_checkout_tree(repo, treeish, &opts, NULL); /* TODO: Progress access for block */ From a42681f2aab197020efe1209d6fd34962cdc6095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 14:18:30 +0200 Subject: [PATCH 10/34] Add Repository#checkout_head. --- ext/rugged/rugged_repo.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 174afbee1..8b5c3b21b 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -643,7 +643,6 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ */ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) { - VALUE target; VALUE ruby_opts; git_repository *repo; int error; @@ -652,25 +651,47 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) Data_Get_Struct(self, git_repository, repo); /* Grab the options hash */ - rb_scan_args(argc, argv, "1:", &target, &ruby_opts); + rb_scan_args(argc, argv, "01", &ruby_opts); if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ /* Parse the wealth of options and collect the results in `opts' */ rb_git__parse_checkout_options(&ruby_opts, &opts); + /* Checkout operation */ error = git_checkout_index(repo, &opts, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); return Qnil; } -static VALUE rb_git_repo_checkout_head(VALUE self) +/* + * call-seq: + * repo.checkout_head( [ opts = {} ] ) + * + * Updates files in the index and the working tree to match the commit pointed + * at by HEAD. + * + * See #checkout_index for a description of the values you can assign + * to +opts+. + */ +static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) { + VALUE ruby_opts; git_repository *repo; int error; + git_checkout_opts opts; + + /* Grab the options hash */ + rb_scan_args(argc, argv, "01", &ruby_opts); + if (TYPE(ruby_opts) != T_HASH) + ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ + + /* Parse the wealth of options and collect the results in `opts' */ + rb_git__parse_checkout_options(&ruby_opts, &opts); - error = git_checkout_head(repo, NULL, NULL); /* TODO: Progress access for block */ + /* Checkout operation */ + error = git_checkout_head(repo, &opts, NULL); /* TODO: Progress access for block */ rugged_exception_check(error); return Qnil; @@ -694,7 +715,8 @@ static VALUE rb_git_repo_checkout_head(VALUE self) * head = Rugged::Reference.lookup(repo, "HEAD") * head.target = ref.name * - * See #checkout_tree for the values you can pass via +opts+. + * See #checkout_index for the values you can pass via +opts+. +treeish+ may be + * a real Tree instance, a Commit or a Tag object. */ static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) { /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ @@ -708,7 +730,7 @@ static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) Data_Get_Struct(self, git_repository, repo); /* Grab the options hash */ - rb_scan_args(argc, argv, "1:", &target, &ruby_opts); + rb_scan_args(argc, argv, "11", &target, &ruby_opts); if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ @@ -905,9 +927,9 @@ void Init_rugged_repo() rb_define_method(rb_cRuggedRepo, "head_detached?", rb_git_repo_head_detached, 0); rb_define_method(rb_cRuggedRepo, "head_orphan?", rb_git_repo_head_orphan, 0); + rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_repo_checkout_index, -1); + rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_repo_checkout_head, -1); rb_define_method(rb_cRuggedRepo, "checkout_tree", rb_git_repo_checkout_tree, -1); - rb_define_method(rb_cRuggedRepo, "checkout_head", rb_git_repo_checkout_head, 0); - rb_define_method(rb_cRuggedRepo, "checkout_index", rb_git_repo_checkout_index, 0); rb_cRuggedOdbObject = rb_define_class_under(rb_mRugged, "OdbObject", rb_cObject); rb_define_method(rb_cRuggedOdbObject, "data", rb_git_odbobj_data, 0); From f7a1b905f84792f30b47bfe1f30f29afd78da8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 17:05:52 +0200 Subject: [PATCH 11/34] Add tests for Repository::clone. --- test/repo_test.rb | 24 +++++++++++++++++------- test/test_helper.rb | 1 + 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/test/repo_test.rb b/test/repo_test.rb index b39e84f8a..55e5084e6 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -140,17 +140,27 @@ end test "can successfully clone a repository" do - Dir.mktmpdir("clone-target-dir") do |tmpdir| - repo = Rugged::Repository.clone_repo("file://#@path", tmpdir) - assert "36060c58702ed4c2a40832c51758d5344201d89a", repo.last_commit.oid - assert repo.lookup("8496071c1b46c854b31185ea97743be6a8774479").kind_of?(Rugged::Commit) + # Remote clone + tmpdir = Dir.mktmpdir("clone-target-dir") + begin + repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir) + assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") + ensure + FileUtils.rm_rf(tmpdir) + end - ref = repo.ref('refs/heads/master') - assert ref.kind_of?(Rugged::Reference) - assert_equal 'refs/heads/master', ref.name + # Local clone + skip "libgit2 currently doesn't support the file:// protocol." # Remove this when libgit2 adds file://. + tmpdir = Dir.mktmpdir("clone-target-dir") + begin + repo = Rugged::Repository.clone_repo("file://#@path", tmpdir) + assert_equal "36060c58702ed4c2a40832c51758d5344201d89a", repo.last_commit.oid + assert_kind_of Rugged::Commit repo.lookup("8496071c1b46c854b31185ea97743be6a8774479") refs = repo.refs assert_equal 4, refs.length + ensure + FileUtils.rm_rf(tmpdir) end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2c8907c96..626bd3af7 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,5 +1,6 @@ require 'tempfile' require 'tmpdir' +require "fileutils" require 'rubygems' require 'minitest/autorun' require 'rugged' From 92b7859d1ece7a2108247a628b62da7fef31d224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Mon, 8 Oct 2012 17:42:30 +0200 Subject: [PATCH 12/34] Add :bare option to Repository::clone and allow passing checkout options to it. --- ext/rugged/rugged_repo.c | 147 +++++++++++++++++++++++---------------- test/repo_test.rb | 9 +++ 2 files changed, 97 insertions(+), 59 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 8b5c3b21b..d9fe8ca1c 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -128,6 +128,52 @@ void rb_git_repo__free(git_repository *repo) git_repository_free(repo); } +/* Helper function used by the checkout_* methods to parse the argument hash. + * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results + * of the parsing process. */ +static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) +{ + VALUE opts_strategy; + VALUE opts_disable_filters; + VALUE opts_dir_mode; + VALUE opts_file_mode; + VALUE opts_file_open_flags; + git_strarray paths; + + opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); + opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); + opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); + opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); + opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + + /* Convert the Ruby hash values to C stuff */ + p_opts->disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ + p_opts->dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2INT(opts_dir_mode) : 0755 /* libgit2 default */; + p_opts->file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2INT(opts_file_mode) : 0644 /* libgit2 default */; + p_opts->file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2INT(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; + + if (TYPE(opts_strategy) == T_ARRAY){ + p_opts->checkout_strategy = 0; /* Ensure we start with a clean slate */ + + /* Now OR-in all requested flags */ + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + } + else + p_opts->checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ + + /* Set other options (segfaults without) */ + paths.count = 0; /* 0 means checking out ALL files */ + p_opts->paths = paths; /* tricky, the default value segfaults; see `paths.count=0' above. */ + p_opts->notify_payload = NULL; /* default value */ +} + static VALUE rugged_repo_new(VALUE klass, git_repository *repo) { VALUE rb_repo = Data_Wrap_Struct(klass, NULL, &rb_git_repo__free, repo); @@ -203,31 +249,60 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) /* * call-seq: - * Rugged::Repository.clone_repo(url, target_path) -> repository + * Rugged::Repository.clone_repo(url, target_path [, opts = {} ]) -> repository * * Clone a Git repository from the remote at +url+ into the given * local +target_path+ and return a Repository object pointing to * the freshly cloned repository's .git directory. * + * After the cloning has completed, Rugged immediately checks out the branch + * pointed to by the remote HEAD. To suppress this, you can add the option + * :bare to the +opts+ hash, which (as the name indicates) + * will cause a bare repository to be created. Other than that, + * +opts+ will be passed on to the checkout operation, so see + * #checkout_index for possible values you can assign to +opts+. + * * Rugged::Repository.clone("git://github.com/libgit2/libgit2.git", "~/libgit2") */ -static VALUE rb_git_repo_clone(VALUE klass, VALUE url, VALUE target_path) -{ /* TODO: Bare repos | Progress block | Exception docs */ +static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) +{ + VALUE url; + VALUE target_path; + VALUE ruby_opts; git_repository *repo; int error; - Check_Type(url, T_STRING); - Check_Type(target_path, T_STRING); + rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); + if (TYPE(ruby_opts) != T_HASH) + ruby_opts = rb_hash_new(); /* Assume empty hash of none given */ + + if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { + error = git_clone_bare(&repo, + StringValueCStr(url), + StringValueCStr(target_path), + NULL); /* TODO: Stats for block */ + } + else { + /* Clone the options hash, so we can safely remove :bare from + * it in order to pass it on to the checkout operation. */ + VALUE checkout_opts = rb_obj_dup(ruby_opts); + rb_hash_delete(checkout_opts, ID2SYM(rb_intern("bare"))); + + /* Parse the checkout options */ + git_checkout_opts opts; + rb_git__parse_checkout_options(&ruby_opts, &opts); + + error = git_clone(&repo, + StringValueCStr(url), + StringValueCStr(target_path), + NULL, /* TODO: Fetch stats for Ruby block */ + NULL, /* TODO: Checkout stats for Ruby block */ + &opts); + } - error = git_clone(&repo, - StringValueCStr(url), - StringValueCStr(target_path), - NULL, /* TODO: Fetch stats for Ruby block */ - NULL, /* Not documented */ - NULL /* Not documented */); rugged_exception_check(error); - return rugged_repo_new(klass, repo); + return rugged_repo_new(self, repo); } #define RB_GIT_REPO_OWNED_GET(_klass, _object) \ @@ -571,52 +646,6 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) return Qnil; } -/* Helper function used by the checkout_* methods to parse the argument hash. - * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results - * of the parsing process. */ -static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) -{ - VALUE opts_strategy; - VALUE opts_disable_filters; - VALUE opts_dir_mode; - VALUE opts_file_mode; - VALUE opts_file_open_flags; - git_strarray paths; - - opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); - opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); - opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); - opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); - opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); - - /* Convert the Ruby hash values to C stuff */ - p_opts->disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ - p_opts->dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2INT(opts_dir_mode) : 0755 /* libgit2 default */; - p_opts->file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2INT(opts_file_mode) : 0644 /* libgit2 default */; - p_opts->file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2INT(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; - - if (TYPE(opts_strategy) == T_ARRAY){ - p_opts->checkout_strategy = 0; /* Ensure we start with a clean slate */ - - /* Now OR-in all requested flags */ - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; - } - else - p_opts->checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ - - /* Set other options (segfaults without) */ - paths.count = 0; /* 0 means checking out ALL files */ - p_opts->paths = paths; /* tricky, the default value segfaults; see `paths.count=0' above. */ - p_opts->notify_payload = NULL; /* default value */ -} - /* * call-seq: * repo.checkout_index( [ opts = {} ] ) @@ -903,7 +932,7 @@ void Init_rugged_repo() rb_define_singleton_method(rb_cRuggedRepo, "hash", 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, 2); - rb_define_singleton_method(rb_cRuggedRepo, "clone_repo", rb_git_repo_clone, 2); + rb_define_singleton_method(rb_cRuggedRepo, "clone_repo", rb_git_repo_clone, -1); /* Cannot name it ::clone b/c Ruby already has a method with this name */ rb_define_singleton_method(rb_cRuggedRepo, "discover", rb_git_repo_discover, -1); rb_define_method(rb_cRuggedRepo, "exists?", rb_git_repo_exists, 1); diff --git a/test/repo_test.rb b/test/repo_test.rb index 55e5084e6..2f21b0a37 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -162,6 +162,15 @@ ensure FileUtils.rm_rf(tmpdir) end + + # Bare repository + tmpdir = Dir.mktmpdir("clone-target-dir") + begin + repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, bare: true) + assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") + ensure + FileUtils.rm_rf(tmpdir) + end end end From e03ae59940456948e39db51d77b7de30217a78db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Tue, 9 Oct 2012 12:26:14 +0200 Subject: [PATCH 13/34] Make Repository::clone release the GVL. --- ext/rugged/rugged_repo.c | 102 +++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index d9fe8ca1c..dc8b62300 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -247,6 +247,70 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) return rugged_repo_new(klass, repo); } +/* Helper struct used to pass information to + * the nonblocking clone functions. Thank MRI for + * requiring this. The parameters are those required + * for the libgit2 clone functions; p_opts is not used for + * bare cloning. */ +struct _clone_params { + git_repository** pp_repo; + git_checkout_opts* p_opts; + char* url; + char* target_path; + + /* This value will automatically be set by the nonblocking + * cloning functions. Setting it is meaningless, but after + * rb_thread_blocking_region returns this is set to the + * return value of the git_clone* functions. */ + int git_error; +}; + +/* Runs the git-clone operation without the GVL. `params' is a pointer + * to a _clone_params struct. Called by rb_git_repo_clone(). */ +static VALUE rb_git_repo__nonblocking_clone(void* p_params) +{ + struct _clone_params* p_clone_params = (struct _clone_params*) p_params; + int error; + + error = git_clone(p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL, /* Can't call back to Ruby for fetch stats without dirty hacks */ + NULL, /* Can't call back to Ruby for checkout stats without dirty hacks */ + p_clone_params->p_opts); + p_clone_params->git_error = error; + + return Qnil; +} + +/* Runs the git-clone --bare operation without the GVL. `params' is a pointer + * to a _clone_params struct. Called by rb_git_repo_clone(). */ +static VALUE rb_git_repo__nonblocking_clone_bare(void* p_params) +{ + struct _clone_params* p_clone_params = (struct _clone_params*) p_params; + int error; + + error = git_clone_bare(p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL); + p_clone_params->git_error = error; + + return Qnil; +} + +/* Currently libgit2 has no way to abort a running clone + * operation. If one is added, this function should be + * adjusted apropriately. `p_param' is a pointer to + * a _clone_params struct (but beware; depending on the + * abortion time, the pp_repo parameter may not be fully + * initialised). Called by rb_git_repo_clone(). */ +static void rb_git_repo__abort_clone(void* p_param) +{ + /* struct _clone_params* p_repo = (struct _clone_params*) p_param; */ + return; /* Yes, this currently DOES NOTHING. See comments above. */ +} + /* * call-seq: * Rugged::Repository.clone_repo(url, target_path [, opts = {} ]) -> repository @@ -262,25 +326,35 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) * +opts+ will be passed on to the checkout operation, so see * #checkout_index for possible values you can assign to +opts+. * - * Rugged::Repository.clone("git://github.com/libgit2/libgit2.git", "~/libgit2") + * This method releases the GVL during the clone operation. + * + * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2") + * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2.git", bare: true) */ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) { VALUE url; VALUE target_path; VALUE ruby_opts; - git_repository *repo; + git_repository *p_repo; int error; + struct _clone_params clone_params; rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* Assume empty hash of none given */ + clone_params.pp_repo = &p_repo; + clone_params.url = StringValueCStr(url); + clone_params.target_path = StringValueCStr(target_path); + if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { - error = git_clone_bare(&repo, - StringValueCStr(url), - StringValueCStr(target_path), - NULL); /* TODO: Stats for block */ + clone_params.p_opts = NULL; /* Not required for bare clone */ + + rb_thread_blocking_region(rb_git_repo__nonblocking_clone_bare, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); } else { /* Clone the options hash, so we can safely remove :bare from @@ -291,18 +365,18 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) /* Parse the checkout options */ git_checkout_opts opts; rb_git__parse_checkout_options(&ruby_opts, &opts); + clone_params.p_opts = &opts; + + rb_thread_blocking_region(rb_git_repo__nonblocking_clone, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); - error = git_clone(&repo, - StringValueCStr(url), - StringValueCStr(target_path), - NULL, /* TODO: Fetch stats for Ruby block */ - NULL, /* TODO: Checkout stats for Ruby block */ - &opts); } - rugged_exception_check(error); + rugged_exception_check(clone_params.git_error); - return rugged_repo_new(self, repo); + return rugged_repo_new(self, p_repo); } #define RB_GIT_REPO_OWNED_GET(_klass, _object) \ From 6f337a25f310123687fd89d238a1f2cad6797859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Tue, 9 Oct 2012 14:59:52 +0200 Subject: [PATCH 14/34] Move test for bare repos before local test. The local test is currently skipped (b/c libgit2 doesn't support local cloning yet), so the bare test would also be skipped if placed behind it. --- test/repo_test.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/repo_test.rb b/test/repo_test.rb index 2f21b0a37..0e5deb059 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -149,6 +149,15 @@ FileUtils.rm_rf(tmpdir) end + # Bare repository + tmpdir = Dir.mktmpdir("clone-target-dir") + begin + repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, bare: true) + assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") + ensure + FileUtils.rm_rf(tmpdir) + end + # Local clone skip "libgit2 currently doesn't support the file:// protocol." # Remove this when libgit2 adds file://. tmpdir = Dir.mktmpdir("clone-target-dir") @@ -162,15 +171,6 @@ ensure FileUtils.rm_rf(tmpdir) end - - # Bare repository - tmpdir = Dir.mktmpdir("clone-target-dir") - begin - repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, bare: true) - assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") - ensure - FileUtils.rm_rf(tmpdir) - end end end From 772acebaab52f55c87fa12c17ed416d569525a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Tue, 9 Oct 2012 16:40:20 +0200 Subject: [PATCH 15/34] Add tests for Repository::checkout. --- test/branch_test.rb | 2 + .../testrepo.git/logs/refs/heads/new-file | 1 + .../77/d8517569c4f7bdf90fb77cafc8c93c7353e0db | Bin 0 -> 35 bytes .../7f/043268ea43ce18e3540acaabf9e090c91965b0 | Bin 0 -> 55 bytes .../8d/f53dc31f9ca3daa002d25eff928dbaa6b150b8 | Bin 0 -> 153 bytes .../ea/d02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 | 2 + .../fixtures/testrepo.git/refs/heads/new-file | 1 + test/reference_test.rb | 4 +- test/repo_test.rb | 42 ++++++++++++++++-- test/test_helper.rb | 1 + 10 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/testrepo.git/logs/refs/heads/new-file create mode 100644 test/fixtures/testrepo.git/objects/77/d8517569c4f7bdf90fb77cafc8c93c7353e0db create mode 100644 test/fixtures/testrepo.git/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 create mode 100644 test/fixtures/testrepo.git/objects/8d/f53dc31f9ca3daa002d25eff928dbaa6b150b8 create mode 100644 test/fixtures/testrepo.git/objects/ea/d02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 create mode 100644 test/fixtures/testrepo.git/refs/heads/new-file diff --git a/test/branch_test.rb b/test/branch_test.rb index 4323da641..483e59f24 100644 --- a/test/branch_test.rb +++ b/test/branch_test.rb @@ -12,6 +12,7 @@ "master", "origin/HEAD", "origin/master", + "origin/new-file", "origin/packed", ], Rugged::Branch.each_name(@repo).sort end @@ -24,6 +25,7 @@ assert_equal [ "origin/HEAD", "origin/master", + "origin/new-file", "origin/packed", ], Rugged::Branch.each_name(@repo, :remote).sort end diff --git a/test/fixtures/testrepo.git/logs/refs/heads/new-file b/test/fixtures/testrepo.git/logs/refs/heads/new-file new file mode 100644 index 000000000..3fcde72e8 --- /dev/null +++ b/test/fixtures/testrepo.git/logs/refs/heads/new-file @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 ead02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 Marvin Gülker 1349787877 +0200 push diff --git a/test/fixtures/testrepo.git/objects/77/d8517569c4f7bdf90fb77cafc8c93c7353e0db b/test/fixtures/testrepo.git/objects/77/d8517569c4f7bdf90fb77cafc8c93c7353e0db new file mode 100644 index 0000000000000000000000000000000000000000..909ab0f0cfda575faa9a131e35f173d457eeb956 GIT binary patch literal 35 tcmV+;0Nnq00ZYosPf{>4WC+Q~ELH%b#Jv2HjMO59w9K4TE&z7F=H?^FfcPQQ3!H%bn$g%5N`dHvVMD1*wTH+ot*d0HmhE8 ziUX=5F)zO)Bef_#Ei)%oucV@cq5MW*Y37mddw=q8uUUWMq)l<~gWFKGd8y@KCBHm3 zluuWlxHf{hAmG#T_>#xza+6F9fIy+RG$|#sh#_(QOw*}#j69BWc3uoqoS1w64EHnu H9s@p9n50J! literal 0 HcmV?d00001 diff --git a/test/fixtures/testrepo.git/objects/ea/d02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 b/test/fixtures/testrepo.git/objects/ea/d02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 new file mode 100644 index 000000000..199018a87 --- /dev/null +++ b/test/fixtures/testrepo.git/objects/ea/d02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 @@ -0,0 +1,2 @@ +x¥Ž;NÄ0@©}Šé‘¢ñ/žH!QPqˆÉx¼‰–8àuàrt\Œe¯@ùŠ÷ôdß¶µƒKá®7U \¢Ïâm™„}fFtÙE-er”gæq¶g2ïÜ´vð#Ž(‘:ÍA$ï$Ú)G‚C›ibÃG_ö¯Ü>× +/?ßogmð°Ýx8úÇO|ù8´ñ�õ¬S¢DÁÂ=:D#·×~ÕþS1ÏM¹+0Tý‚¹q•ÊuM•ó~tèzék= æbšZ� \ No newline at end of file diff --git a/test/fixtures/testrepo.git/refs/heads/new-file b/test/fixtures/testrepo.git/refs/heads/new-file new file mode 100644 index 000000000..b4a460ed6 --- /dev/null +++ b/test/fixtures/testrepo.git/refs/heads/new-file @@ -0,0 +1 @@ +ead02fb51ce5f0fa7fd451bdc0a02defe09fc7a0 diff --git a/test/reference_test.rb b/test/reference_test.rb index 860d21adf..15801d146 100644 --- a/test/reference_test.rb +++ b/test/reference_test.rb @@ -16,13 +16,13 @@ test "can list references" do refs = @repo.refs.map { |r| r.name.gsub("refs/", '') }.sort.join(':') - assert_equal "heads/master:heads/packed:tags/v0.9:tags/v1.0", refs + assert_equal "heads/master:heads/new-file:heads/packed:tags/v0.9:tags/v1.0", refs end test "can list references with non 7-bit ASCII characters" do Rugged::Reference.create(@repo, "refs/heads/#{UNICODE_REF_NAME}", "refs/heads/master") refs = @repo.refs.map { |r| r.name.gsub("refs/", '') }.sort.join(':') - assert_equal "heads/#{UNICODE_REF_NAME}:heads/master:heads/packed:tags/v0.9:tags/v1.0", refs + assert_equal "heads/#{UNICODE_REF_NAME}:heads/master:heads/new-file:heads/packed:tags/v0.9:tags/v1.0", refs end test "can list filtered references from regex" do diff --git a/test/repo_test.rb b/test/repo_test.rb index 0e5deb059..e7bc16676 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -88,20 +88,20 @@ test "can return all refs" do refs = @repo.refs - assert_equal 4, refs.length + assert_equal 5, refs.length end test "can return all refs that match" do refs = @repo.refs 'refs/heads' - assert_equal 2, refs.length + assert_equal 3, refs.length end test "can return the names of all refs" do refs = @repo.ref_names refs.each {|name| assert name.kind_of?(String)} - assert_equal 4, refs.count + assert_equal 5, refs.count end test "can return all tags" do @@ -173,4 +173,40 @@ end end + test "can checkout back and fourth" do + # Clone the test repo and set up the remote-tracking branch new-file + # (checkout doesn't do tracking) + repo_path = temp_repo("testrepo.git") + `cd '#{repo_path}' && git branch new-file refs/remotes/origin/new-file` + + repo = Rugged::Repository.new(repo_path) + path_to_file = File.join(repo_path, "another_file.txt") # This file is in the new-file branch + path_to_file2 = File.join(repo_path, "stuf") + new_file = Rugged::Reference.lookup(repo, "refs/heads/new-file") + master = Rugged::Reference.lookup(repo, "refs/heads/master") + + last_new_file_commit = Rugged::Commit.lookup(repo, new_file.target) + last_master_commit = Rugged::Commit.lookup(repo, master.target) + + File.open(path_to_file2, "w"){|f| f.write("more stuff")} + assert !File.file?(path_to_file) + repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert File.file?(path_to_file) + assert !File.file?(path_to_file2) # :remove_untracked + repo.checkout_tree(last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert !File.file?(path_to_file) + repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified, :remove_untracked]) + assert !File.file?(path_to_file) # :create_missing ommited + repo.checkout_tree(last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + File.open(path_to_file2, "w"){|f| f.write("more stuff")} + repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified]) + assert File.file?(path_to_file2) # :remove_untracked ommited + File.open(path_to_file, "w"){|f| f.write("Modified this file.")} + repo.checkout_tree(last_master_commit, strategy: [:default, :create_missing]) + assert_equal("Modified this file.", File.read(path_to_file)) # :overwrite_modified ommited + + skip "libgit2 currently segfaults on git_checkout_tree() without a strategy (i.e. only :default)" + assert_nil repo.checkout_tree(last_new_file_commit) + end + end diff --git a/test/test_helper.rb b/test/test_helper.rb index 626bd3af7..bb6b3e09a 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,6 +1,7 @@ require 'tempfile' require 'tmpdir' require "fileutils" +require "pathname" require 'rubygems' require 'minitest/autorun' require 'rugged' From 16f7722568eb3b3163e913720180003311a853b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 10:57:48 +0200 Subject: [PATCH 16/34] Use memset() to zero out the git_checkout_options struct before filling it. --- ext/rugged/rugged_repo.c | 25 ++++++++++++------------- test/repo_test.rb | 1 - 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index dc8b62300..c49e05513 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -138,7 +138,9 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ VALUE opts_dir_mode; VALUE opts_file_mode; VALUE opts_file_open_flags; - git_strarray paths; + + /* Ensure we have a clean slate to work from */ + memset(p_opts, 0, sizeof(*p_opts)); opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); @@ -147,13 +149,17 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); /* Convert the Ruby hash values to C stuff */ - p_opts->disable_filters = RTEST(opts_disable_filters); /* not set = nil = 0 = libgit2 default */ - p_opts->dir_mode = TYPE(opts_dir_mode) == T_FIXNUM ? FIX2INT(opts_dir_mode) : 0755 /* libgit2 default */; - p_opts->file_mode = TYPE(opts_file_mode) == T_FIXNUM ? FIX2INT(opts_file_mode) : 0644 /* libgit2 default */; - p_opts->file_open_flags = TYPE(opts_file_open_flags) == T_FIXNUM ? FIX2INT(opts_file_open_flags) : O_CREAT | O_TRUNC | O_WRONLY /* libgit2 default */; + if (RTEST(opts_disable_filters)) + p_opts->disable_filters = true; + if (TYPE(opts_dir_mode) == T_FIXNUM) + p_opts->dir_mode = FIX2INT(opts_dir_mode); + if (TYPE(opts_file_mode) == T_FIXNUM) + p_opts->file_mode = FIX2INT(opts_file_mode); + if (TYPE(opts_file_open_flags) == T_FIXNUM) + p_opts->file_open_flags = FIX2INT(opts_file_open_flags); if (TYPE(opts_strategy) == T_ARRAY){ - p_opts->checkout_strategy = 0; /* Ensure we start with a clean slate */ + p_opts->checkout_strategy = 0; /* Now OR-in all requested flags */ if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) @@ -165,13 +171,6 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; } - else - p_opts->checkout_strategy = GIT_CHECKOUT_DEFAULT; /* libgit2 default (for some unknown reason libgit2 segfaults with its own default value) */ - - /* Set other options (segfaults without) */ - paths.count = 0; /* 0 means checking out ALL files */ - p_opts->paths = paths; /* tricky, the default value segfaults; see `paths.count=0' above. */ - p_opts->notify_payload = NULL; /* default value */ } static VALUE rugged_repo_new(VALUE klass, git_repository *repo) diff --git a/test/repo_test.rb b/test/repo_test.rb index e7bc16676..dba684053 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -205,7 +205,6 @@ repo.checkout_tree(last_master_commit, strategy: [:default, :create_missing]) assert_equal("Modified this file.", File.read(path_to_file)) # :overwrite_modified ommited - skip "libgit2 currently segfaults on git_checkout_tree() without a strategy (i.e. only :default)" assert_nil repo.checkout_tree(last_new_file_commit) end From 4e8a00e404465ff9d51da4b0706548f3c2dd0ffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 11:13:31 +0200 Subject: [PATCH 17/34] Minor comment corrections --- ext/rugged/rugged_repo.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index c49e05513..f766a05f5 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -247,15 +247,14 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) } /* Helper struct used to pass information to - * the nonblocking clone functions. Thank MRI for - * requiring this. The parameters are those required - * for the libgit2 clone functions; p_opts is not used for - * bare cloning. */ + * the nonblocking clone functions. The parameters are those + * required for the libgit2 clone functions; p_opts is not + * used for bare cloning. */ struct _clone_params { - git_repository** pp_repo; - git_checkout_opts* p_opts; - char* url; - char* target_path; + git_repository** pp_repo; /* This will get assigned the cloned repo object */ + git_checkout_opts* p_opts; /* Options for the checkout after the clone (if not bare) */ + char* url; /* URL of the remote host with protocol prefix */ + char* target_path; /* path where to clone to */ /* This value will automatically be set by the nonblocking * cloning functions. Setting it is meaningless, but after @@ -341,7 +340,7 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); if (TYPE(ruby_opts) != T_HASH) - ruby_opts = rb_hash_new(); /* Assume empty hash of none given */ + ruby_opts = rb_hash_new(); /* Assume empty hash if none given */ clone_params.pp_repo = &p_repo; clone_params.url = StringValueCStr(url); @@ -803,7 +802,7 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) * call-seq: * repo.checkout_tree(treeish [, opts = {}]) * - * Reads the tree pointed to by +treeish+ into the index and if requested, + * Reads the tree pointed to by +treeish+ into the index and * writes the index out to the working tree afterwards. Note this method * does *not* update the HEAD pointer, so you probably want to do this * afterwards: From 3c3e6ff2fc67e26cdd7efa7c9011489192b92807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 11:51:42 +0200 Subject: [PATCH 18/34] Add tests for Repository#checkout_index --- ext/rugged/rugged_repo.c | 3 +-- test/repo_test.rb | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index f766a05f5..f524c1d21 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -150,7 +150,7 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ /* Convert the Ruby hash values to C stuff */ if (RTEST(opts_disable_filters)) - p_opts->disable_filters = true; + p_opts->disable_filters = 1; /* boolean */ if (TYPE(opts_dir_mode) == T_FIXNUM) p_opts->dir_mode = FIX2INT(opts_dir_mode); if (TYPE(opts_file_mode) == T_FIXNUM) @@ -335,7 +335,6 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) VALUE target_path; VALUE ruby_opts; git_repository *p_repo; - int error; struct _clone_params clone_params; rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); diff --git a/test/repo_test.rb b/test/repo_test.rb index dba684053..67e9b3b35 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -181,7 +181,7 @@ repo = Rugged::Repository.new(repo_path) path_to_file = File.join(repo_path, "another_file.txt") # This file is in the new-file branch - path_to_file2 = File.join(repo_path, "stuf") + path_to_file2 = File.join(repo_path, "stuff") new_file = Rugged::Reference.lookup(repo, "refs/heads/new-file") master = Rugged::Reference.lookup(repo, "refs/heads/master") @@ -208,4 +208,23 @@ assert_nil repo.checkout_tree(last_new_file_commit) end + test "can checkout the index" do + # Clone the test repo and set up the remote-tracking branch new-file + # (checkout doesn't do tracking) + repo_path = temp_repo("testrepo.git") + `cd '#{repo_path}' && git branch new-file refs/remotes/origin/new-file` + + repo = Rugged::Repository.new(repo_path) + new_file = Rugged::Reference.lookup(repo, "refs/heads/new-file") + last_new_file_commit = Rugged::Commit.lookup(repo, new_file.target) + path_to_file = File.join(repo_path, "another_file.txt") # This file is in the new-file branch + + # Read the new-file branch's latest commit's tree into the index, then check + # it out removing anything not related to that tree. + assert !File.file?(path_to_file) + `cd '#{repo_path}' && git read-tree #{last_new_file_commit.tree_oid}` + assert_nil repo.checkout_index(strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert File.file?(path_to_file) + end + end From 57d9aab205e9133d6bf1d47983d60cb4d9e38d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 12:20:05 +0200 Subject: [PATCH 19/34] Use an own testing context for the checkout tests. Removes duplicate setup code. --- test/repo_test.rb | 86 ++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 46 deletions(-) diff --git a/test/repo_test.rb b/test/repo_test.rb index 67e9b3b35..5e96cbaea 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -172,59 +172,53 @@ FileUtils.rm_rf(tmpdir) end end +end - test "can checkout back and fourth" do - # Clone the test repo and set up the remote-tracking branch new-file - # (checkout doesn't do tracking) - repo_path = temp_repo("testrepo.git") - `cd '#{repo_path}' && git branch new-file refs/remotes/origin/new-file` - - repo = Rugged::Repository.new(repo_path) - path_to_file = File.join(repo_path, "another_file.txt") # This file is in the new-file branch - path_to_file2 = File.join(repo_path, "stuff") - new_file = Rugged::Reference.lookup(repo, "refs/heads/new-file") - master = Rugged::Reference.lookup(repo, "refs/heads/master") - - last_new_file_commit = Rugged::Commit.lookup(repo, new_file.target) - last_master_commit = Rugged::Commit.lookup(repo, master.target) - - File.open(path_to_file2, "w"){|f| f.write("more stuff")} - assert !File.file?(path_to_file) - repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) - assert File.file?(path_to_file) - assert !File.file?(path_to_file2) # :remove_untracked - repo.checkout_tree(last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) - assert !File.file?(path_to_file) - repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified, :remove_untracked]) - assert !File.file?(path_to_file) # :create_missing ommited - repo.checkout_tree(last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) - File.open(path_to_file2, "w"){|f| f.write("more stuff")} - repo.checkout_tree(last_new_file_commit, strategy: [:default, :overwrite_modified]) - assert File.file?(path_to_file2) # :remove_untracked ommited - File.open(path_to_file, "w"){|f| f.write("Modified this file.")} - repo.checkout_tree(last_master_commit, strategy: [:default, :create_missing]) - assert_equal("Modified this file.", File.read(path_to_file)) # :overwrite_modified ommited - - assert_nil repo.checkout_tree(last_new_file_commit) +context "Repository checkouts" do + + setup do + @repo_path = temp_repo("testrepo.git") + `cd '#@repo_path' && git branch new-file refs/remotes/origin/new-file` # checkout doesn't do tracking (without this, we cannot checkout a remote branch however) + @repo = Rugged::Repository.new(@repo_path) + @path_to_branched_file = File.join(@repo_path, "another_file.txt") # This file only exists in the new-file branch + @path_to_other_file = File.join(@repo_path, "stuff") # This file doesn't exist in any branch + @last_master_commit = Rugged::Commit.lookup(@repo, Rugged::Reference.lookup(@repo, "refs/heads/master").target) + @last_new_file_commit = Rugged::Commit.lookup(@repo, Rugged::Reference.lookup(@repo, "refs/heads/new-file").target) end - test "can checkout the index" do - # Clone the test repo and set up the remote-tracking branch new-file - # (checkout doesn't do tracking) - repo_path = temp_repo("testrepo.git") - `cd '#{repo_path}' && git branch new-file refs/remotes/origin/new-file` + test "can checkout back and fourth" do + File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} + assert !File.file?(@path_to_branched_file) + + @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert File.file?(@path_to_branched_file) + assert !File.file?(@path_to_other_file) # :remove_untracked - repo = Rugged::Repository.new(repo_path) - new_file = Rugged::Reference.lookup(repo, "refs/heads/new-file") - last_new_file_commit = Rugged::Commit.lookup(repo, new_file.target) - path_to_file = File.join(repo_path, "another_file.txt") # This file is in the new-file branch + @repo.checkout_tree(@last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert !File.file?(@path_to_branched_file) + @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified, :remove_untracked]) + assert !File.file?(@path_to_branched_file) # :create_missing ommited + @repo.checkout_tree(@last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + + File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} + @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified]) + assert File.file?(@path_to_other_file) # :remove_untracked ommited + + File.open(@path_to_branched_file, "w"){|f| f.write("Modified this file.")} + @repo.checkout_tree(@last_master_commit, strategy: [:default, :create_missing]) + assert_equal("Modified this file.", File.read(@path_to_branched_file)) # :overwrite_modified ommited + + assert_nil @repo.checkout_tree(@last_new_file_commit) + end + + test "can checkout the index" do # Read the new-file branch's latest commit's tree into the index, then check # it out removing anything not related to that tree. - assert !File.file?(path_to_file) - `cd '#{repo_path}' && git read-tree #{last_new_file_commit.tree_oid}` - assert_nil repo.checkout_index(strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) - assert File.file?(path_to_file) + assert !File.file?(@path_to_branched_file) + `cd '#@repo_path' && git read-tree #{@last_new_file_commit.tree_oid}` + assert_nil @repo.checkout_index(strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert File.file?(@path_to_branched_file) end end From da989884bd880928918fd41b4a7fd8d719a6232c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 12:45:26 +0200 Subject: [PATCH 20/34] Add tests for Repository#checkout_head and fix it. It was missing Data_Get_Struct()... --- ext/rugged/rugged_repo.c | 2 ++ test/repo_test.rb | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index f524c1d21..165cfb539 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -782,6 +782,8 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) int error; git_checkout_opts opts; + Data_Get_Struct(self, git_repository, repo); + /* Grab the options hash */ rb_scan_args(argc, argv, "01", &ruby_opts); if (TYPE(ruby_opts) != T_HASH) diff --git a/test/repo_test.rb b/test/repo_test.rb index 5e96cbaea..cfc7f304e 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -221,4 +221,11 @@ assert File.file?(@path_to_branched_file) end + test "can checkout the HEAD" do + File.open(@path_to_other_file, "w"){|f| f.write("This stuff is to be discarded.")} + # Now reset the working direcory + assert_nil @repo.checkout_head(strategy: [:default, :remove_untracked]) + assert !File.file?(@path_to_other_file) + end + end From ba82230558368f879ef23e4d9046122bf289860e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 15:01:02 +0200 Subject: [PATCH 21/34] Bump libgit2. There have been a few fixes regarding clone operations (namely regarding cloning of repositories with submodules). --- vendor/libgit2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/libgit2 b/vendor/libgit2 index f9cf12b4f..8ea05f3f8 160000 --- a/vendor/libgit2 +++ b/vendor/libgit2 @@ -1 +1 @@ -Subproject commit f9cf12b4f58588309c0329d0525f1505f579c32d +Subproject commit 8ea05f3f8f3485d39ce4a867b353f45c4eebf54e From 8593fcca6c5d4810addaa8c952b4725b0ea08a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 15:42:09 +0200 Subject: [PATCH 22/34] Use tabs for indentation. I really dislike those. --- ext/rugged/rugged_repo.c | 199 +++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 100 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 165cfb539..02b3a1ffa 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -22,7 +22,6 @@ * THE SOFTWARE. */ -#include #include "rugged.h" extern VALUE rb_mRugged; @@ -133,44 +132,44 @@ void rb_git_repo__free(git_repository *repo) * of the parsing process. */ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) { - VALUE opts_strategy; - VALUE opts_disable_filters; - VALUE opts_dir_mode; - VALUE opts_file_mode; - VALUE opts_file_open_flags; - - /* Ensure we have a clean slate to work from */ - memset(p_opts, 0, sizeof(*p_opts)); - - opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); - opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); - opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); - opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); - opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); - - /* Convert the Ruby hash values to C stuff */ - if (RTEST(opts_disable_filters)) - p_opts->disable_filters = 1; /* boolean */ - if (TYPE(opts_dir_mode) == T_FIXNUM) - p_opts->dir_mode = FIX2INT(opts_dir_mode); - if (TYPE(opts_file_mode) == T_FIXNUM) - p_opts->file_mode = FIX2INT(opts_file_mode); - if (TYPE(opts_file_open_flags) == T_FIXNUM) - p_opts->file_open_flags = FIX2INT(opts_file_open_flags); - - if (TYPE(opts_strategy) == T_ARRAY){ - p_opts->checkout_strategy = 0; - - /* Now OR-in all requested flags */ - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; - } + VALUE opts_strategy; + VALUE opts_disable_filters; + VALUE opts_dir_mode; + VALUE opts_file_mode; + VALUE opts_file_open_flags; + + /* Ensure we have a clean slate to work from */ + memset(p_opts, 0, sizeof(*p_opts)); + + opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); + opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); + opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); + opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); + opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + + /* Convert the Ruby hash values to C stuff */ + if (RTEST(opts_disable_filters)) + p_opts->disable_filters = 1; /* boolean */ + if (TYPE(opts_dir_mode) == T_FIXNUM) + p_opts->dir_mode = FIX2INT(opts_dir_mode); + if (TYPE(opts_file_mode) == T_FIXNUM) + p_opts->file_mode = FIX2INT(opts_file_mode); + if (TYPE(opts_file_open_flags) == T_FIXNUM) + p_opts->file_open_flags = FIX2INT(opts_file_open_flags); + + if (TYPE(opts_strategy) == T_ARRAY){ + p_opts->checkout_strategy = 0; + + /* Now OR-in all requested flags */ + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + } } static VALUE rugged_repo_new(VALUE klass, git_repository *repo) @@ -191,7 +190,7 @@ static VALUE rugged_repo_new(VALUE klass, git_repository *repo) /* * call-seq: - * Rugged::Repository.new(path) -> repository + * new(path) -> 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 @@ -219,7 +218,7 @@ static VALUE rb_git_repo_new(VALUE klass, VALUE rb_path) /* * call-seq: - * Rugged::Repository.init_at(path, is_bare) -> repository + * init_at(path, is_bare) -> repository * * Initialize a Git repository in +path+. This implies creating all the * necessary files on the FS, or re-initializing an already existing @@ -251,50 +250,50 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) * required for the libgit2 clone functions; p_opts is not * used for bare cloning. */ struct _clone_params { - git_repository** pp_repo; /* This will get assigned the cloned repo object */ - git_checkout_opts* p_opts; /* Options for the checkout after the clone (if not bare) */ - char* url; /* URL of the remote host with protocol prefix */ - char* target_path; /* path where to clone to */ - - /* This value will automatically be set by the nonblocking - * cloning functions. Setting it is meaningless, but after - * rb_thread_blocking_region returns this is set to the - * return value of the git_clone* functions. */ - int git_error; + git_repository** pp_repo; /* This will get assigned the cloned repo object */ + git_checkout_opts* p_opts; /* Options for the checkout after the clone (if not bare) */ + char* url; /* URL of the remote host with protocol prefix */ + char* target_path; /* path where to clone to */ + + /* This value will automatically be set by the nonblocking + * cloning functions. Setting it is meaningless, but after + * rb_thread_blocking_region returns this is set to the + * return value of the git_clone* functions. */ + int git_error; }; /* Runs the git-clone operation without the GVL. `params' is a pointer * to a _clone_params struct. Called by rb_git_repo_clone(). */ static VALUE rb_git_repo__nonblocking_clone(void* p_params) { - struct _clone_params* p_clone_params = (struct _clone_params*) p_params; - int error; + struct _clone_params* p_clone_params = (struct _clone_params*) p_params; + int error; - error = git_clone(p_clone_params->pp_repo, - p_clone_params->url, - p_clone_params->target_path, - NULL, /* Can't call back to Ruby for fetch stats without dirty hacks */ - NULL, /* Can't call back to Ruby for checkout stats without dirty hacks */ - p_clone_params->p_opts); - p_clone_params->git_error = error; + error = git_clone( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL, /* Can't call back to Ruby for fetch stats without dirty hacks */ + NULL, /* Can't call back to Ruby for checkout stats without dirty hacks */ + p_clone_params->p_opts); + p_clone_params->git_error = error; - return Qnil; + return Qnil; } /* Runs the git-clone --bare operation without the GVL. `params' is a pointer * to a _clone_params struct. Called by rb_git_repo_clone(). */ static VALUE rb_git_repo__nonblocking_clone_bare(void* p_params) { - struct _clone_params* p_clone_params = (struct _clone_params*) p_params; - int error; + struct _clone_params* p_clone_params = (struct _clone_params*) p_params; + int error; - error = git_clone_bare(p_clone_params->pp_repo, - p_clone_params->url, - p_clone_params->target_path, - NULL); - p_clone_params->git_error = error; + error = git_clone_bare( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL); + p_clone_params->git_error = error; - return Qnil; + return Qnil; } /* Currently libgit2 has no way to abort a running clone @@ -305,13 +304,13 @@ static VALUE rb_git_repo__nonblocking_clone_bare(void* p_params) * initialised). Called by rb_git_repo_clone(). */ static void rb_git_repo__abort_clone(void* p_param) { - /* struct _clone_params* p_repo = (struct _clone_params*) p_param; */ - return; /* Yes, this currently DOES NOTHING. See comments above. */ + /* struct _clone_params* p_repo = (struct _clone_params*) p_param; */ + return; /* Yes, this currently DOES NOTHING. See comments above. */ } /* * call-seq: - * Rugged::Repository.clone_repo(url, target_path [, opts = {} ]) -> repository + * clone_repo(url, target_path [, opts = {} ]) -> repository * * Clone a Git repository from the remote at +url+ into the given * local +target_path+ and return a Repository object pointing to @@ -331,45 +330,45 @@ static void rb_git_repo__abort_clone(void* p_param) */ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) { - VALUE url; - VALUE target_path; - VALUE ruby_opts; + VALUE url; + VALUE target_path; + VALUE ruby_opts; git_repository *p_repo; struct _clone_params clone_params; - rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); - if (TYPE(ruby_opts) != T_HASH) - ruby_opts = rb_hash_new(); /* Assume empty hash if none given */ + rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); + if (TYPE(ruby_opts) != T_HASH) + ruby_opts = rb_hash_new(); /* Assume empty hash if none given */ - clone_params.pp_repo = &p_repo; - clone_params.url = StringValueCStr(url); - clone_params.target_path = StringValueCStr(target_path); + clone_params.pp_repo = &p_repo; + clone_params.url = StringValueCStr(url); + clone_params.target_path = StringValueCStr(target_path); - if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { - clone_params.p_opts = NULL; /* Not required for bare clone */ + if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { + clone_params.p_opts = NULL; /* Not required for bare clone */ - rb_thread_blocking_region(rb_git_repo__nonblocking_clone_bare, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); + rb_thread_blocking_region( rb_git_repo__nonblocking_clone_bare, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); } else { - /* Clone the options hash, so we can safely remove :bare from - * it in order to pass it on to the checkout operation. */ - VALUE checkout_opts = rb_obj_dup(ruby_opts); - rb_hash_delete(checkout_opts, ID2SYM(rb_intern("bare"))); + /* Clone the options hash, so we can safely remove :bare from + * it in order to pass it on to the checkout operation. */ + VALUE checkout_opts = rb_obj_dup(ruby_opts); + rb_hash_delete(checkout_opts, ID2SYM(rb_intern("bare"))); - /* Parse the checkout options */ - git_checkout_opts opts; - rb_git__parse_checkout_options(&ruby_opts, &opts); - clone_params.p_opts = &opts; + /* Parse the checkout options */ + git_checkout_opts opts; + rb_git__parse_checkout_options(&ruby_opts, &opts); + clone_params.p_opts = &opts; - rb_thread_blocking_region(rb_git_repo__nonblocking_clone, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); + rb_thread_blocking_region( rb_git_repo__nonblocking_clone, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); - } + } rugged_exception_check(clone_params.git_error); From 06b9cb22c1dc98cb017846b1e1634311744df56c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 16:19:52 +0200 Subject: [PATCH 23/34] Use old hash syntax. Hopefully Ruby 1.8 will go soon. --- test/repo_test.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/repo_test.rb b/test/repo_test.rb index cfc7f304e..31d6f3b95 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -152,7 +152,7 @@ # Bare repository tmpdir = Dir.mktmpdir("clone-target-dir") begin - repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, bare: true) + repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, :bare => true) assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") ensure FileUtils.rm_rf(tmpdir) @@ -190,23 +190,23 @@ File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} assert !File.file?(@path_to_branched_file) - @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) assert File.file?(@path_to_branched_file) assert !File.file?(@path_to_other_file) # :remove_untracked - @repo.checkout_tree(@last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_master_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) assert !File.file?(@path_to_branched_file) - @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified, :remove_untracked]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified, :remove_untracked]) assert !File.file?(@path_to_branched_file) # :create_missing ommited - @repo.checkout_tree(@last_master_commit, strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_master_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} - @repo.checkout_tree(@last_new_file_commit, strategy: [:default, :overwrite_modified]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified]) assert File.file?(@path_to_other_file) # :remove_untracked ommited File.open(@path_to_branched_file, "w"){|f| f.write("Modified this file.")} - @repo.checkout_tree(@last_master_commit, strategy: [:default, :create_missing]) + @repo.checkout_tree(@last_master_commit, :strategy => [:default, :create_missing]) assert_equal("Modified this file.", File.read(@path_to_branched_file)) # :overwrite_modified ommited assert_nil @repo.checkout_tree(@last_new_file_commit) @@ -217,14 +217,14 @@ # it out removing anything not related to that tree. assert !File.file?(@path_to_branched_file) `cd '#@repo_path' && git read-tree #{@last_new_file_commit.tree_oid}` - assert_nil @repo.checkout_index(strategy: [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert_nil @repo.checkout_index(:strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) assert File.file?(@path_to_branched_file) end test "can checkout the HEAD" do File.open(@path_to_other_file, "w"){|f| f.write("This stuff is to be discarded.")} # Now reset the working direcory - assert_nil @repo.checkout_head(strategy: [:default, :remove_untracked]) + assert_nil @repo.checkout_head(:strategy => [:default, :remove_untracked]) assert !File.file?(@path_to_other_file) end From ef1b71c9324f2f3d8daf675027b0f6770606eb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 16:54:41 +0200 Subject: [PATCH 24/34] Use rb_thread_blocking_region() only on Ruby 1.9. 1.8 guys won't profit from concurrency. --- ext/rugged/rugged_repo.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 02b3a1ffa..f283ce7ec 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -347,10 +347,15 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { clone_params.p_opts = NULL; /* Not required for bare clone */ +#if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo_nonblocking_clone_bare(&clone_params); +#else rb_thread_blocking_region( rb_git_repo__nonblocking_clone_bare, &clone_params, rb_git_repo__abort_clone, &clone_params); +#endif } else { /* Clone the options hash, so we can safely remove :bare from @@ -363,11 +368,15 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); clone_params.p_opts = &opts; +#if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_grepo__nonblocking_clone(&clone_params); +#else rb_thread_blocking_region( rb_git_repo__nonblocking_clone, &clone_params, rb_git_repo__abort_clone, &clone_params); - +#endif } rugged_exception_check(clone_params.git_error); From b2f96fb105d303a8a093e0866793ec903a22beca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 10 Oct 2012 18:00:22 +0200 Subject: [PATCH 25/34] Correct some typos, include ruby/version.h Necessary to get the Ruby version under 1.8. --- ext/rugged/rugged_repo.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index f283ce7ec..92d9854e3 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -23,6 +23,7 @@ */ #include "rugged.h" +#include extern VALUE rb_mRugged; extern VALUE rb_cRuggedIndex; @@ -349,7 +350,7 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) #if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo_nonblocking_clone_bare(&clone_params); + rb_git_repo__nonblocking_clone_bare(&clone_params); #else rb_thread_blocking_region( rb_git_repo__nonblocking_clone_bare, &clone_params, @@ -370,7 +371,7 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) #if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_grepo__nonblocking_clone(&clone_params); + rb_git_repo__nonblocking_clone(&clone_params); #else rb_thread_blocking_region( rb_git_repo__nonblocking_clone, &clone_params, From dc5f81480a43e58b21797a97c4047099386a17e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Thu, 11 Oct 2012 10:28:40 +0200 Subject: [PATCH 26/34] Check Ruby version in extconf.rb, ruby/version.h has been deprecated and isn't even there in all cases. --- ext/rugged/extconf.rb | 8 ++++++++ ext/rugged/rugged_repo.c | 17 ++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/ext/rugged/extconf.rb b/ext/rugged/extconf.rb index 2f0e08b70..564258786 100644 --- a/ext/rugged/extconf.rb +++ b/ext/rugged/extconf.rb @@ -41,4 +41,12 @@ def sys(cmd) end end +print("checking whether we're compiling for Ruby >= 1.9... ") +if RUBY_VERSION >= "1.9" + $CFLAGS += " -DUSING_RUBY_19" + puts "yes" +else + puts "no" +end + create_makefile("rugged/rugged") diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 92d9854e3..c8347f992 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -23,7 +23,6 @@ */ #include "rugged.h" -#include extern VALUE rb_mRugged; extern VALUE rb_cRuggedIndex; @@ -348,14 +347,14 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { clone_params.p_opts = NULL; /* Not required for bare clone */ -#if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__nonblocking_clone_bare(&clone_params); -#else +#ifdef USING_RUBY_19 rb_thread_blocking_region( rb_git_repo__nonblocking_clone_bare, &clone_params, rb_git_repo__abort_clone, &clone_params); +#else + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__nonblocking_clone_bare(&clone_params); #endif } else { @@ -369,14 +368,14 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); clone_params.p_opts = &opts; -#if RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8 - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__nonblocking_clone(&clone_params); -#else +#ifdef USING_RUBY_19 rb_thread_blocking_region( rb_git_repo__nonblocking_clone, &clone_params, rb_git_repo__abort_clone, &clone_params); +#else + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__nonblocking_clone(&clone_params); #endif } From 9761802d8f6c4a4311bee49347f3e9b0e1b5d507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Wed, 31 Oct 2012 18:00:47 +0100 Subject: [PATCH 27/34] Adapt new libgit2 API for clone/checkout. --- ext/rugged/rugged_repo.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index c8347f992..35c249009 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -272,8 +272,8 @@ static VALUE rb_git_repo__nonblocking_clone(void* p_params) error = git_clone( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, - NULL, /* Can't call back to Ruby for fetch stats without dirty hacks */ - NULL, /* Can't call back to Ruby for checkout stats without dirty hacks */ + NULL, /* TODO: Progress block */ + NULL, /* TODO: Progress block */ p_clone_params->p_opts); p_clone_params->git_error = error; @@ -290,7 +290,7 @@ static VALUE rb_git_repo__nonblocking_clone_bare(void* p_params) error = git_clone_bare( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, - NULL); + NULL, NULL); /* TODO: Progress block */ p_clone_params->git_error = error; return Qnil; @@ -767,7 +767,7 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); /* Checkout operation */ - error = git_checkout_index(repo, &opts, NULL); /* TODO: Progress access for block */ + error = git_checkout_index(repo, &opts); rugged_exception_check(error); return Qnil; @@ -801,7 +801,7 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); /* Checkout operation */ - error = git_checkout_head(repo, &opts, NULL); /* TODO: Progress access for block */ + error = git_checkout_head(repo, &opts); rugged_exception_check(error); return Qnil; @@ -852,7 +852,7 @@ static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); /* Update the index */ - error = git_checkout_tree(repo, treeish, &opts, NULL); /* TODO: Progress access for block */ + error = git_checkout_tree(repo, treeish, &opts); rugged_exception_check(error); return Qnil; } From c9d5d494f0cdae57ae8f5d3de3a2e86fa1caec3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Thu, 1 Nov 2012 10:13:54 +0100 Subject: [PATCH 28/34] Repository#clone_repo now accepts a block for transmission statistics. Note that when called with a block, the method will not release the GVL anymore (without one it does). --- ext/rugged/rugged_repo.c | 122 +++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 36 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 35c249009..163920211 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -254,6 +254,7 @@ struct _clone_params { git_checkout_opts* p_opts; /* Options for the checkout after the clone (if not bare) */ char* url; /* URL of the remote host with protocol prefix */ char* target_path; /* path where to clone to */ + int block_given; /* set to 1 if the clone method got a block passed, 0 otherwise (this information would be lost without GVL otherwise) */ /* This value will automatically be set by the nonblocking * cloning functions. Setting it is meaningless, but after @@ -262,35 +263,66 @@ struct _clone_params { int git_error; }; -/* Runs the git-clone operation without the GVL. `params' is a pointer +/* Callback function passed to libgit2 when the clone functions + * are called with a block. */ +void rb_git_repo__transfer_callback(const git_transfer_progress* stats, void* payload) +{ + rb_yield_values( 4, + UINT2NUM(stats->total_objects), + UINT2NUM(stats->indexed_objects), + UINT2NUM(stats->received_objects), + LONG2NUM(stats->received_bytes)); +} + +/* Runs the git-clone operation with or without the GVL. `params' is a pointer * to a _clone_params struct. Called by rb_git_repo_clone(). */ -static VALUE rb_git_repo__nonblocking_clone(void* p_params) +static VALUE rb_git_repo__internal_clone(void* p_params) { struct _clone_params* p_clone_params = (struct _clone_params*) p_params; int error; - error = git_clone( p_clone_params->pp_repo, - p_clone_params->url, - p_clone_params->target_path, - NULL, /* TODO: Progress block */ - NULL, /* TODO: Progress block */ - p_clone_params->p_opts); + if (p_clone_params->block_given) { + error = git_clone( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + rb_git_repo__transfer_callback, + NULL, + p_clone_params->p_opts); + + } + else { /* In 1.9 we're running without the GVL here */ + error = git_clone( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL, NULL, + p_clone_params->p_opts); + } p_clone_params->git_error = error; return Qnil; } -/* Runs the git-clone --bare operation without the GVL. `params' is a pointer +/* Runs the git-clone --bare operation with or without the GVL. `params' is a pointer * to a _clone_params struct. Called by rb_git_repo_clone(). */ -static VALUE rb_git_repo__nonblocking_clone_bare(void* p_params) +static VALUE rb_git_repo__internal_clone_bare(void* p_params) { struct _clone_params* p_clone_params = (struct _clone_params*) p_params; int error; - error = git_clone_bare( p_clone_params->pp_repo, - p_clone_params->url, - p_clone_params->target_path, - NULL, NULL); /* TODO: Progress block */ + if (p_clone_params->block_given){ + error = git_clone_bare( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + rb_git_repo__transfer_callback, + NULL); + } + else { /* In 1.9 we're running without the GVL here */ + error = git_clone_bare( p_clone_params->pp_repo, + p_clone_params->url, + p_clone_params->target_path, + NULL, NULL); + } + p_clone_params->git_error = error; return Qnil; @@ -311,6 +343,7 @@ static void rb_git_repo__abort_clone(void* p_param) /* * call-seq: * clone_repo(url, target_path [, opts = {} ]) -> repository + * clone_rpeo(url, target_path [, opts = {} ]){|total_objects, indexed_objects, received_objects, received_bytes| ...} * * Clone a Git repository from the remote at +url+ into the given * local +target_path+ and return a Repository object pointing to @@ -323,10 +356,16 @@ static void rb_git_repo__abort_clone(void* p_param) * +opts+ will be passed on to the checkout operation, so see * #checkout_index for possible values you can assign to +opts+. * - * This method releases the GVL during the clone operation. + * If a block is given, it gets passed statistics about the fetching process + * (all values are integers). + * + * If no block is given, this method releases the GVL during the clone operation. * * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2") * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2.git", bare: true) + * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2", strategy: [:default, :overwrite_modified, :create_missing]) do |total, indexed, received_objs, bytes| + * print "\rReceived #{received_objs}/#{total} (indexed #{indexed}, #{bytes} bytes)" + * end */ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) { @@ -340,43 +379,54 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* Assume empty hash if none given */ - clone_params.pp_repo = &p_repo; - clone_params.url = StringValueCStr(url); + clone_params.pp_repo = &p_repo; + clone_params.url = StringValueCStr(url); clone_params.target_path = StringValueCStr(target_path); if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { clone_params.p_opts = NULL; /* Not required for bare clone */ + if (rb_block_given_p()) { + /* If a block was given, we cannot release the GVL due to callback */ + clone_params.block_given = 1; + rb_git_repo__internal_clone_bare(&clone_params); + } + else { /* No block given -- release the GVL if possible */ + clone_params.block_given = 0; #ifdef USING_RUBY_19 - rb_thread_blocking_region( rb_git_repo__nonblocking_clone_bare, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); + rb_thread_blocking_region( rb_git_repo__internal_clone_bare, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); #else - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__nonblocking_clone_bare(&clone_params); + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__internal_clone_bare(&clone_params); #endif - } - else { - /* Clone the options hash, so we can safely remove :bare from - * it in order to pass it on to the checkout operation. */ - VALUE checkout_opts = rb_obj_dup(ruby_opts); - rb_hash_delete(checkout_opts, ID2SYM(rb_intern("bare"))); - + } + } + else { /* Full non-bare clone requested */ /* Parse the checkout options */ git_checkout_opts opts; rb_git__parse_checkout_options(&ruby_opts, &opts); clone_params.p_opts = &opts; + if (rb_block_given_p()) { + /* If a block was given, we cannot release the GVL due to callback */ + clone_params.block_given =1; + rb_git_repo__internal_clone(&clone_params); + } + else { /* No block given -- release GVL if possible */ + clone_params.block_given = 0; #ifdef USING_RUBY_19 - rb_thread_blocking_region( rb_git_repo__nonblocking_clone, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); + rb_thread_blocking_region( rb_git_repo__internal_clone, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); #else - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__nonblocking_clone(&clone_params); + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__internal_clone(&clone_params); #endif + } } rugged_exception_check(clone_params.git_error); From 41234dd71be830fba069bdb3b695d5576f2994b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Thu, 6 Dec 2012 16:41:51 +0100 Subject: [PATCH 29/34] Adapt new libgit2 API --- ext/rugged/rugged_repo.c | 52 +++++++++++++++++++++++++++++----------- test/branch_test.rb | 2 ++ test/repo_test.rb | 35 +++++++++++++-------------- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 117489d19..47acdf30c 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -162,12 +162,35 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ /* Now OR-in all requested flags */ if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("default")))) p_opts->checkout_strategy |= GIT_CHECKOUT_DEFAULT; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("overwrite_modified")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_OVERWRITE_MODIFIED; - if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("create_missing")))) - p_opts->checkout_strategy |= GIT_CHECKOUT_CREATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_unmodified")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_UNMODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_missing")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_MISSING; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("safe")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_SAFE; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_modified")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_MODIFIED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_untracked")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_UNTRACKED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("force")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_FORCE; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("allow_conflicts")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_ALLOW_CONFLICTS; if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("remove_untracked")))) p_opts->checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_only")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY; + // The following options are not yet implemented in libgit2 as of 3368c520 + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("skip_unmerged")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("use_ours")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_USE_OURS; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("use_theirs")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_USE_THEIRS; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_submodules")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES; + if (rb_ary_includes(opts_strategy, ID2SYM(rb_intern("update_submodules_if_changed")))) + p_opts->checkout_strategy |= GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED; } } @@ -328,17 +351,18 @@ static VALUE rb_git_repo__internal_clone(void* p_params) error = git_clone( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, + p_clone_params->p_opts, rb_git_repo__transfer_callback, - NULL, - p_clone_params->p_opts); + NULL); } else { /* In 1.9 we're running without the GVL here */ error = git_clone( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, - NULL, NULL, - p_clone_params->p_opts); + p_clone_params->p_opts, + NULL, NULL); + } p_clone_params->git_error = error; @@ -449,7 +473,7 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) } else { /* Full non-bare clone requested */ /* Parse the checkout options */ - git_checkout_opts opts; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; rb_git__parse_checkout_options(&ruby_opts, &opts); clone_params.p_opts = &opts; @@ -876,7 +900,7 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) VALUE ruby_opts; git_repository *repo; int error; - git_checkout_opts opts; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; Data_Get_Struct(self, git_repository, repo); @@ -889,7 +913,7 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) rb_git__parse_checkout_options(&ruby_opts, &opts); /* Checkout operation */ - error = git_checkout_index(repo, &opts); + error = git_checkout_index(repo, NULL, &opts); rugged_exception_check(error); return Qnil; @@ -910,7 +934,7 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) VALUE ruby_opts; git_repository *repo; int error; - git_checkout_opts opts; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; Data_Get_Struct(self, git_repository, repo); @@ -950,14 +974,14 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) * See #checkout_index for the values you can pass via +opts+. +treeish+ may be * a real Tree instance, a Commit or a Tag object. */ -static VALUE rb_git_repo_checkout_tree(VALUE argc, VALUE argv[], VALUE self) +static VALUE rb_git_repo_checkout_tree(int argc, VALUE argv[], VALUE self) { /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ VALUE target; VALUE ruby_opts; git_repository *repo; int error; git_object *treeish; - git_checkout_opts opts; + git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; Data_Get_Struct(self, git_repository, repo); diff --git a/test/branch_test.rb b/test/branch_test.rb index 11c33be1b..c45e4aba5 100644 --- a/test/branch_test.rb +++ b/test/branch_test.rb @@ -18,6 +18,7 @@ "master", "origin/HEAD", "origin/master", + "origin/new-file", "origin/packed", ], Rugged::Branch.each_name(@repo).sort end @@ -30,6 +31,7 @@ assert_equal [ "origin/HEAD", "origin/master", + "origin/new-file", "origin/packed", ], Rugged::Branch.each_name(@repo, :remote).sort end diff --git a/test/repo_test.rb b/test/repo_test.rb index 1f42ec80b..55e45f9d5 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -152,13 +152,13 @@ end it "can enumerate all objects in the odb" do - assert_equal 30, @repo.each_id.to_a.length + assert_equal 34, @repo.each_id.to_a.length end it "can load alternates" do alt_path = File.dirname(__FILE__) + '/fixtures/alternate/objects' repo = Rugged::Repository.new(@path, :alternates => [alt_path]) - assert_equal 33, repo.each_id.to_a.length + assert_equal 37, repo.each_id.to_a.length assert repo.read('146ae76773c91e3b1d00cf7a338ec55ae58297e2') end @@ -166,7 +166,7 @@ # Remote clone tmpdir = Dir.mktmpdir("clone-target-dir") begin - repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir) + repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, :strategy => [:safe]) assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") ensure FileUtils.rm_rf(tmpdir) @@ -182,15 +182,14 @@ end # Local clone - skip "libgit2 currently doesn't support the file:// protocol." # Remove this when libgit2 adds file://. tmpdir = Dir.mktmpdir("clone-target-dir") begin - repo = Rugged::Repository.clone_repo("file://#@path", tmpdir) + repo = Rugged::Repository.clone_repo("file://#@path", tmpdir, :strategy => [:safe]) assert_equal "36060c58702ed4c2a40832c51758d5344201d89a", repo.last_commit.oid - assert_kind_of Rugged::Commit repo.lookup("8496071c1b46c854b31185ea97743be6a8774479") + assert_kind_of Rugged::Commit, repo.lookup("8496071c1b46c854b31185ea97743be6a8774479") refs = repo.refs - assert_equal 4, refs.length + assert_equal 6, refs.length ensure FileUtils.rm_rf(tmpdir) end @@ -213,26 +212,26 @@ File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} assert !File.file?(@path_to_branched_file) - @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:safe, :remove_untracked]) assert File.file?(@path_to_branched_file) assert !File.file?(@path_to_other_file) # :remove_untracked - @repo.checkout_tree(@last_master_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_master_commit, :strategy => [:safe, :remove_untracked]) assert !File.file?(@path_to_branched_file) - @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified, :remove_untracked]) - assert !File.file?(@path_to_branched_file) # :create_missing ommited - @repo.checkout_tree(@last_master_commit, :strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:update_unmodified]) + #### bug in libgit2 prevents this --> #### assert !File.file?(@path_to_branched_file) # :update_missing in :force ommited + @repo.checkout_tree(@last_master_commit, :strategy => [:safe, :remove_untracked]) File.open(@path_to_other_file, "w"){|f| f.write("more stuff")} - @repo.checkout_tree(@last_new_file_commit, :strategy => [:default, :overwrite_modified]) + @repo.checkout_tree(@last_new_file_commit, :strategy => [:safe]) assert File.file?(@path_to_other_file) # :remove_untracked ommited File.open(@path_to_branched_file, "w"){|f| f.write("Modified this file.")} - @repo.checkout_tree(@last_master_commit, :strategy => [:default, :create_missing]) - assert_equal("Modified this file.", File.read(@path_to_branched_file)) # :overwrite_modified ommited + @repo.checkout_tree(@last_master_commit, :strategy => [:safe]) + assert_equal("Modified this file.", File.read(@path_to_branched_file)) # :update_modified in :force ommited - assert_nil @repo.checkout_tree(@last_new_file_commit) + assert_nil @repo.checkout_tree(@last_new_file_commit, :strategy => [:force]) end it "can checkout the index" do @@ -240,14 +239,14 @@ # it out removing anything not related to that tree. assert !File.file?(@path_to_branched_file) `cd '#@repo_path' && git read-tree #{@last_new_file_commit.tree_oid}` - assert_nil @repo.checkout_index(:strategy => [:default, :overwrite_modified, :create_missing, :remove_untracked]) + assert_nil @repo.checkout_index(:strategy => [:force]) assert File.file?(@path_to_branched_file) end it "can checkout the HEAD" do File.open(@path_to_other_file, "w"){|f| f.write("This stuff is to be discarded.")} # Now reset the working direcory - assert_nil @repo.checkout_head(:strategy => [:default, :remove_untracked]) + assert_nil @repo.checkout_head(:strategy => [:remove_untracked]) assert !File.file?(@path_to_other_file) end end From 2002f67c964eeab58e511e496c399c2c2e341b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 7 Dec 2012 13:33:55 +0100 Subject: [PATCH 30/34] Reorganise clone code to support the new callbacks provided by libgit2. Note that bare cloning is in a separate method Repository::clone_repo_bare now as the Repository::clone method grew without bounds. --- ext/rugged/extconf.rb | 2 + ext/rugged/rugged_repo.c | 327 +++++++++++++++++++++++++++++---------- test/repo_test.rb | 2 +- 3 files changed, 252 insertions(+), 79 deletions(-) diff --git a/ext/rugged/extconf.rb b/ext/rugged/extconf.rb index 564258786..bfb6eab3a 100644 --- a/ext/rugged/extconf.rb +++ b/ext/rugged/extconf.rb @@ -1,5 +1,7 @@ require 'mkmf' +$CFLAGS += " -g" + def sys(cmd) puts " -- #{cmd}" unless ret = xsystem(cmd) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 47acdf30c..f95ee1195 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -33,6 +33,10 @@ extern VALUE rb_cRuggedObject; VALUE rb_cRuggedRepo; VALUE rb_cRuggedOdbObject; +int rb_git_repo__conflict_callback( const char* conflicting_path, const git_oid* index_oid, + unsigned int index_mode, unsigned int wd_mode, void* payload); +void rb_git_repo__progress_callback(const char* path, size_t completed_steps, size_t total_steps, void* payload); + /* * call-seq: * odb_obj.oid -> hex_oid @@ -128,7 +132,8 @@ void rb_git_repo__free(git_repository *repo) /* Helper function used by the checkout_* methods to parse the argument hash. * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results - * of the parsing process. */ + * of the parsing process. Be sure to use GIT_CHECKOUT_OPTS_INIT for initialising + * `p_opts'. */ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) { VALUE opts_strategy; @@ -136,15 +141,16 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ VALUE opts_dir_mode; VALUE opts_file_mode; VALUE opts_file_open_flags; + VALUE opts_progress_callback; + VALUE opts_conflict_callback; - /* Ensure we have a clean slate to work from */ - memset(p_opts, 0, sizeof(*p_opts)); - - opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); - opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); - opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); - opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); - opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); + opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); + opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); + opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); + opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); + opts_progress_callback = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("progress_cb"))); + opts_conflict_callback = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("conflict_cb"))); /* Convert the Ruby hash values to C stuff */ if (RTEST(opts_disable_filters)) @@ -155,6 +161,14 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ p_opts->file_mode = FIX2INT(opts_file_mode); if (TYPE(opts_file_open_flags) == T_FIXNUM) p_opts->file_open_flags = FIX2INT(opts_file_open_flags); + if (RTEST(opts_progress_callback)) { + p_opts->progress_payload = &opts_progress_callback; + p_opts->progress_cb = rb_git_repo__progress_callback; + } + if (RTEST(opts_conflict_callback)) { + p_opts->conflict_payload = &opts_conflict_callback; + p_opts->conflict_cb = rb_git_repo__conflict_callback; + } if (TYPE(opts_strategy) == T_ARRAY){ p_opts->checkout_strategy = 0; @@ -314,13 +328,15 @@ static VALUE rb_git_repo_init_at(VALUE klass, VALUE path, VALUE rb_is_bare) /* Helper struct used to pass information to * the nonblocking clone functions. The parameters are those * required for the libgit2 clone functions; p_opts is not - * used for bare cloning. */ + * used for bare cloning. On allocation, please set a default + * value of DEFAULT_CLONE_PARAMS so that default values get + * initialised correctly. */ struct _clone_params { - git_repository** pp_repo; /* This will get assigned the cloned repo object */ - git_checkout_opts* p_opts; /* Options for the checkout after the clone (if not bare) */ - char* url; /* URL of the remote host with protocol prefix */ - char* target_path; /* path where to clone to */ - int block_given; /* set to 1 if the clone method got a block passed, 0 otherwise (this information would be lost without GVL otherwise) */ + git_repository** pp_repo; /* This will get assigned the cloned repo object */ + git_checkout_opts* p_opts; /* Options for the checkout after the clone (not used for bare clones) */ + char* url; /* URL of the remote host with protocol prefix */ + char* target_path; /* path where to clone to */ + VALUE* p_ruby_transfer_proc; /* The block passed to Repo::clone, if any (NULL otherwise) */ /* This value will automatically be set by the nonblocking * cloning functions. Setting it is meaningless, but after @@ -329,15 +345,61 @@ struct _clone_params { int git_error; }; +/* Safely initialize a _clone_params struct by assigning this on allocation */ +const struct _clone_params DEFAULT_CLONE_PARAMS = { + NULL, NULL, NULL, NULL, NULL, 0 +}; + /* Callback function passed to libgit2 when the clone functions - * are called with a block. */ + * are called with a block. `payload' is the block passed to Repo::clone + * as a Proc. */ void rb_git_repo__transfer_callback(const git_transfer_progress* stats, void* payload) { - rb_yield_values( 4, - UINT2NUM(stats->total_objects), - UINT2NUM(stats->indexed_objects), - UINT2NUM(stats->received_objects), - LONG2NUM(stats->received_bytes)); + VALUE* p_ruby_transfer_cb_proc = (VALUE*)payload; + rb_funcall( *p_ruby_transfer_cb_proc, rb_intern("call"), 4, + UINT2NUM(stats->total_objects), + UINT2NUM(stats->indexed_objects), + UINT2NUM(stats->received_objects), + LONG2NUM(stats->received_bytes)); +} + +/* Callback function passed to libgit2 when the checkout functions + * determine a conflict and a conflict callback was specified. + * `payload' is the ruby lambda for the callback. */ +int rb_git_repo__conflict_callback(const char* conflicting_path, const git_oid* index_oid, + unsigned int index_mode, unsigned int wd_mode, void* payload) +{ + VALUE result; + VALUE* p_ruby_lambda = (VALUE*)payload; + char oid_str[GIT_OID_HEXSZ + 1]; + + git_oid_tostr(oid_str, GIT_OID_HEXSZ + 1, index_oid); + result = rb_funcall( *p_ruby_lambda, rb_intern("call"), 4, + rb_str_new2(conflicting_path), + rb_str_new2(oid_str), + UINT2NUM(index_mode), + UINT2NUM(wd_mode)); + + /* Any fixnum value is assumed to be the result the user wants to + * pass to libgit2 here. If we don't get a fixnum, assume no value + * should be returned, which is 0 in this case. */ + if (TYPE(result) == T_FIXNUM) + return NUM2INT(result); + else + return 0; +} + +/* Callback function passed to libgit2 when you passed a callback + * to the checkout functions. `payload' is the ruby lamda for the + * callback. */ +void rb_git_repo__progress_callback(const char* path, size_t completed_steps, size_t total_steps, void* payload) +{ + VALUE* p_ruby_lambda = (VALUE*)payload; + + rb_funcall( *p_ruby_lambda, rb_intern("call"), 3, + rb_str_new2(path), + LONG2NUM(completed_steps), + LONG2NUM(total_steps)); } /* Runs the git-clone operation with or without the GVL. `params' is a pointer @@ -347,16 +409,20 @@ static VALUE rb_git_repo__internal_clone(void* p_params) struct _clone_params* p_clone_params = (struct _clone_params*) p_params; int error; - if (p_clone_params->block_given) { + /* Check if we got a transfer callback passed, and if so, hand it over + * to libgit2. The two checkout-related callbacks for progress and conficts + * have already been handled when parsing the checkout options in the ::clone + * method. */ + if (p_clone_params->p_ruby_transfer_proc) { error = git_clone( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, p_clone_params->p_opts, rb_git_repo__transfer_callback, - NULL); + p_clone_params->p_ruby_transfer_proc); } - else { /* In 1.9 we're running without the GVL here */ + else { /* In 1.9 we may run without the GVL here (if no checkout callbacks have been set) */ error = git_clone( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, @@ -376,12 +442,16 @@ static VALUE rb_git_repo__internal_clone_bare(void* p_params) struct _clone_params* p_clone_params = (struct _clone_params*) p_params; int error; - if (p_clone_params->block_given){ + /* Check if we got a callback passed and if so, hand it over to + * libgit2. A side effect of this check is that if we don't got + * a transfer callback passed, we know for sure that we're currently + * running without the GVL. */ + if (p_clone_params->p_ruby_transfer_proc) { /* With GVL */ error = git_clone_bare( p_clone_params->pp_repo, p_clone_params->url, p_clone_params->target_path, rb_git_repo__transfer_callback, - NULL); + p_clone_params->p_ruby_transfer_proc); } else { /* In 1.9 we're running without the GVL here */ error = git_clone_bare( p_clone_params->pp_repo, @@ -407,6 +477,59 @@ static void rb_git_repo__abort_clone(void* p_param) return; /* Yes, this currently DOES NOTHING. See comments above. */ } +/* + * call-seq: + * clone_repo_bare(url, target_path) -> repository + * clone_repo_bare(url, target_path){|total_objects, indexed_objects, received_objects, received_bytes| ...} -> repository + * + * Creates a bare clone (in the Git sense, think git clone --bare) of + * the remote repository at +url+ in the local directory +target_path+ and returns + * a Repository object pointing to the newly created repo at +target_path+. + * + * If a block is given, it gets passed statistics about the fetching process + * while download is in progress (see method signature). + * + * If no block is given, this method releases the GVL during the clone operation. + * + * Rugged::Repository.clone_repo_bare("git://github.com/libgit2/libgit2.git", "~/libgit2.git") + * Rugged::Repository.clone_repo_bare("git://github.com/libgit2/libgit2.git", "~/libgit2.git") do |total, indexed, received_objs, bytes| + * print "\rReceived #{received_objs}/#{total} (indexed #{indexed}, #{bytes} bytes)" + * end + */ +static VALUE rb_git_repo_clone_bare(VALUE self, VALUE url, VALUE target_path) +{ + git_repository *p_repo; + struct _clone_params clone_params = DEFAULT_CLONE_PARAMS; + VALUE transfer_callback = Qnil; + + clone_params.pp_repo = &p_repo; + clone_params.url = StringValueCStr(url); + clone_params.target_path = StringValueCStr(target_path); + clone_params.p_opts = NULL; /* Not required for bare clone */ + + if (rb_block_given_p()) { + /* If a transfer callback was given, we cannot release the GVL due to callback */ + transfer_callback = rb_block_proc(); /* &block */ + clone_params.p_ruby_transfer_proc = &transfer_callback; + rb_git_repo__internal_clone_bare(&clone_params); + } + else { /* No callback given -- release the GVL if Ruby version allows */ +#ifdef USING_RUBY_19 + rb_thread_blocking_region( rb_git_repo__internal_clone_bare, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); +#else + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__internal_clone_bare(&clone_params); +#endif + } + + /* Check for success and return new Repository instance */ + rugged_exception_check(clone_params.git_error); + return rugged_repo_new(self, p_repo); +} + /* * call-seq: * clone_repo(url, target_path [, opts = {} ]) -> repository @@ -430,7 +553,7 @@ static void rb_git_repo__abort_clone(void* p_param) * * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2") * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2.git", bare: true) - * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2", strategy: [:default, :overwrite_modified, :create_missing]) do |total, indexed, received_objs, bytes| + * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2", strategy: [:force]) do |total, indexed, received_objs, bytes| * print "\rReceived #{received_objs}/#{total} (indexed #{indexed}, #{bytes} bytes)" * end */ @@ -439,65 +562,65 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) VALUE url; VALUE target_path; VALUE ruby_opts; + VALUE transfer_callback = Qnil; git_repository *p_repo; - struct _clone_params clone_params; + git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; + struct _clone_params clone_params = DEFAULT_CLONE_PARAMS; + short can_release_gvl = 1; /* bool */ rb_scan_args(argc, argv, "21", &url, &target_path, &ruby_opts); if (TYPE(ruby_opts) != T_HASH) ruby_opts = rb_hash_new(); /* Assume empty hash if none given */ - clone_params.pp_repo = &p_repo; - clone_params.url = StringValueCStr(url); + clone_params.pp_repo = &p_repo; + clone_params.url = StringValueCStr(url); clone_params.target_path = StringValueCStr(target_path); - if (RTEST(rb_hash_aref(ruby_opts, ID2SYM(rb_intern("bare"))))) { - clone_params.p_opts = NULL; /* Not required for bare clone */ + /* Parse the checkout options */ + rb_git__parse_checkout_options(&ruby_opts, &checkout_opts); + clone_params.p_opts = &checkout_opts; - if (rb_block_given_p()) { - /* If a block was given, we cannot release the GVL due to callback */ - clone_params.block_given = 1; - rb_git_repo__internal_clone_bare(&clone_params); - } - else { /* No block given -- release the GVL if possible */ - clone_params.block_given = 0; -#ifdef USING_RUBY_19 - rb_thread_blocking_region( rb_git_repo__internal_clone_bare, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); -#else - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__internal_clone_bare(&clone_params); -#endif - } + /* Check if we got any of the three callbacks that would prevent us + * from releasing the GVL.*/ + + /* 1. Transfer callback. Note that this callback is specific to the + * clone methods and is not handled by rb_git__parse_checkout_options() + * therefore, hence we must pass this to the C-side callback ourselves. */ + if (rb_block_given_p()) { + transfer_callback = rb_block_proc(); /* &block */ + clone_params.p_ruby_transfer_proc = &transfer_callback; + can_release_gvl = 0; } - else { /* Full non-bare clone requested */ - /* Parse the checkout options */ - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; - rb_git__parse_checkout_options(&ruby_opts, &opts); - clone_params.p_opts = &opts; - - if (rb_block_given_p()) { - /* If a block was given, we cannot release the GVL due to callback */ - clone_params.block_given =1; - rb_git_repo__internal_clone(&clone_params); - } - else { /* No block given -- release GVL if possible */ - clone_params.block_given = 0; + + /* 2. Conflict callback. Handling is done by the checkout option + * parsing function, we just need to know about it for GVL. */ + if (checkout_opts.conflict_payload) + can_release_gvl = 0; + + /* 3. Progress callback. Handling is done by the checkout option + * parsing function, we just need to know about it for GVL. */ + if (checkout_opts.progress_payload) + can_release_gvl = 0; + + /* Release the GVL if the Ruby version permits it */ + if (can_release_gvl) { #ifdef USING_RUBY_19 - rb_thread_blocking_region( rb_git_repo__internal_clone, - &clone_params, - rb_git_repo__abort_clone, - &clone_params); + rb_thread_blocking_region( rb_git_repo__internal_clone, + &clone_params, + rb_git_repo__abort_clone, + &clone_params); #else - /* Poor 1.8 guys, you cannot have concurrency */ - rb_git_repo__internal_clone(&clone_params); + /* Poor 1.8 guys, you cannot have concurrency */ + rb_git_repo__internal_clone(&clone_params); #endif - } + } + else { + /* If any block or callback was given, we cannot release the GVL due to callback */ + rb_git_repo__internal_clone(&clone_params); } + /* Check for success and return new Repository instance */ rugged_exception_check(clone_params.git_error); - return rugged_repo_new(self, p_repo); } @@ -876,18 +999,65 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) * repo.checkout_index( [ opts = {} ] ) * * Writes the files in the index ("staging area") out to the working - * tree. Does not update the HEAD pointer. + * tree. Does not update the HEAD pointer automatically. + * + * Checking out git trees is a fairly complex operation that can + * be adjusted in a variety of ways, the full documentation on this + * including background information can be found in libgit2's + * checkout.h header. Rugged's checkout methods support + * all operations mentioned there, but note that libgit2 itself + * doesn't implement them all yet. * * +opts+ is a hash which can take the following values: * [:strategy ([:default])] * Defines how to exactly check out the given +treeish+. This is an * array containing one or more of the following symbols: - * [:default] Don't overwrite anything. - * [:overwrite_modified] Overwrite files already there. - * [:create_missing] If a file is in the index, but not in the worktree, copy it nevertheless to the work tree. - * [:remove_untracked] Delete files from the working tree not checked into Git. - * [:disable_filters (0)] - * Not documented by libgit2. + * + * [:default] + * Consider everything that would need a change a conflict. + * This is effectively a dry run that does nothing. + * [:update_unmodified] + * Update entries in the working tree that don't have uncommited + * data. + * [:update_missing] + * Update entries that don't exist in the working tree. Note that + * due to bug #1121 libgit2 currently always behaves as if this + * option was passed, i.e. there's no way to prevent this. + * [:safe] + * Update entries that don't have uncommited data. Equal + * to passing :update_unmodified and :update_missing. + * [:update_modified] + * Overwrite entries with uncommitted data. + * [:update_untracked] + * Overwrite entries not checked into git if necessary. + * [:force] + * Force the working tree to like like the index regardless of + * any conflicts. Equal to specifying :safe, :update_modified, + * and :update_untracked. + * [:allow_conflicts] + * Check out even if conflicts are present. This causes libgit2 + * to continue checking out after the conflict callback has been + * called (see below). + * [:remove_untracked] + * Remove files not checked into Git (these would normally be + * ignored). + * [:update_only] + * Only update existing files and don't create new ones. + * [:skip_unmerged (not implemented)] + * Don't check out unmerged files, but continue. + * [:use_ours (not implemented)] + * Accept our version as the resolution of a merge conflict (i.e. + * check out stage 2 from the index). + * [:use_theirs (not implemented)] + * Accept the other's version as the resolution of a merge conflict + * (i.e. check out stage 3 from the index). + * [:update_submodules (not implemented)] + * Recursively check out submodules with the same options. + * [:update_submodules_if_changed (not implemented)] + * Recursively check out submodules if HEAD moved in parent repo. + * + * [:disable_filters (false)] + * Don't apply filters like CR-LF conversion. * [:dir_mode (0755)] * Not documented by libgit2. * [:file_mode (0664)] @@ -965,7 +1135,7 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) * # Checkout the branch "yourbranch" * ref = Rugged::Reference.new(repo, "refs/heads/yourbranch") * commit = Rugged::Commit.lookup(repo, ref.target) - * repo.checkout_tree(commit, strategy: [:overwrite_modified, :create_missing, :remove_untracked]) + * repo.checkout_tree(commit, strategy: [:force]) * * # Update the HEAD pointer to point to "yourbranch" * head = Rugged::Reference.lookup(repo, "HEAD") @@ -1196,6 +1366,7 @@ void Init_rugged_repo() 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, 2); rb_define_singleton_method(rb_cRuggedRepo, "clone_repo", rb_git_repo_clone, -1); /* Cannot name it ::clone b/c Ruby already has a method with this name */ + rb_define_singleton_method(rb_cRuggedRepo, "clone_repo_bare", rb_git_repo_clone_bare, 2); rb_define_singleton_method(rb_cRuggedRepo, "discover", rb_git_repo_discover, -1); rb_define_method(rb_cRuggedRepo, "exists?", rb_git_repo_exists, 1); diff --git a/test/repo_test.rb b/test/repo_test.rb index 55e45f9d5..074d3052d 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -175,7 +175,7 @@ # Bare repository tmpdir = Dir.mktmpdir("clone-target-dir") begin - repo = Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, :bare => true) + repo = Rugged::Repository.clone_repo_bare("git://github.com/libgit2/libgit2.git", tmpdir) assert_kind_of Rugged::Commit, repo.lookup("1a628100534a315bd00361fc3d32df671923c107") ensure FileUtils.rm_rf(tmpdir) From 1518e4690d7155384ec65813aa335aea99b25009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 7 Dec 2012 17:05:39 +0100 Subject: [PATCH 31/34] Correct checkout callback handling --- ext/rugged/rugged_repo.c | 82 +++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index f95ee1195..1f4b30cea 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -134,23 +134,21 @@ void rb_git_repo__free(git_repository *repo) * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results * of the parsing process. Be sure to use GIT_CHECKOUT_OPTS_INIT for initialising * `p_opts'. */ -static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts) +static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts, VALUE* p_progress_cb, VALUE* p_conflict_cb) { VALUE opts_strategy; VALUE opts_disable_filters; VALUE opts_dir_mode; VALUE opts_file_mode; VALUE opts_file_open_flags; - VALUE opts_progress_callback; - VALUE opts_conflict_callback; opts_strategy = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("strategy"))); opts_disable_filters = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("disable_filters"))); opts_dir_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("dir_mode"))); opts_file_mode = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_mode"))); opts_file_open_flags = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("file_open_flags"))); - opts_progress_callback = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("progress_cb"))); - opts_conflict_callback = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("conflict_cb"))); + *p_progress_cb = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("progress_cb"))); + *p_conflict_cb = rb_hash_aref(*ruby_opts, ID2SYM(rb_intern("conflict_cb"))); /* Convert the Ruby hash values to C stuff */ if (RTEST(opts_disable_filters)) @@ -161,12 +159,12 @@ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_ p_opts->file_mode = FIX2INT(opts_file_mode); if (TYPE(opts_file_open_flags) == T_FIXNUM) p_opts->file_open_flags = FIX2INT(opts_file_open_flags); - if (RTEST(opts_progress_callback)) { - p_opts->progress_payload = &opts_progress_callback; + if (RTEST(*p_progress_cb)) { + p_opts->progress_payload = p_progress_cb; p_opts->progress_cb = rb_git_repo__progress_callback; } - if (RTEST(opts_conflict_callback)) { - p_opts->conflict_payload = &opts_conflict_callback; + if (RTEST(*p_conflict_cb)) { + p_opts->conflict_payload = p_conflict_cb; p_opts->conflict_cb = rb_git_repo__conflict_callback; } @@ -380,11 +378,8 @@ int rb_git_repo__conflict_callback(const char* conflicting_path, const git_oid* UINT2NUM(index_mode), UINT2NUM(wd_mode)); - /* Any fixnum value is assumed to be the result the user wants to - * pass to libgit2 here. If we don't get a fixnum, assume no value - * should be returned, which is 0 in this case. */ - if (TYPE(result) == T_FIXNUM) - return NUM2INT(result); + if (RTEST(result)) + return 1; else return 0; } @@ -396,10 +391,18 @@ void rb_git_repo__progress_callback(const char* path, size_t completed_steps, si { VALUE* p_ruby_lambda = (VALUE*)payload; - rb_funcall( *p_ruby_lambda, rb_intern("call"), 3, - rb_str_new2(path), - LONG2NUM(completed_steps), - LONG2NUM(total_steps)); + if (path) { + rb_funcall( *p_ruby_lambda, rb_intern("call"), 3, + rb_str_new2(path), + LONG2NUM(completed_steps), + LONG2NUM(total_steps)); + } + else { + rb_funcall( *p_ruby_lambda, rb_intern("call"), 3, + Qnil, + LONG2NUM(completed_steps), + LONG2NUM(total_steps)); + } } /* Runs the git-clone operation with or without the GVL. `params' is a pointer @@ -563,6 +566,8 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) VALUE target_path; VALUE ruby_opts; VALUE transfer_callback = Qnil; + VALUE progress_callback = Qnil; + VALUE conflict_callback = Qnil; git_repository *p_repo; git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; struct _clone_params clone_params = DEFAULT_CLONE_PARAMS; @@ -577,7 +582,7 @@ static VALUE rb_git_repo_clone(int argc, VALUE argv[], VALUE self) clone_params.target_path = StringValueCStr(target_path); /* Parse the checkout options */ - rb_git__parse_checkout_options(&ruby_opts, &checkout_opts); + rb_git__parse_checkout_options(&ruby_opts, &checkout_opts, &progress_callback, &conflict_callback); clone_params.p_opts = &checkout_opts; /* Check if we got any of the three callbacks that would prevent us @@ -1064,12 +1069,25 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) * Not documented by libgit2. * [:file_open_flags (IO::CREAT | IO::TRUNC | IO::WRONLY)] * Not documented by libgit2. + * [:progress_cb] + * This callback is invoked during the checkout process and gets passed the + * path to the file currently being checked out, the number of completed steps, + * and the number of total steps. + * [:conflict_cb] + * Whenever the checkout algorithm encounters a file that it isn't allowed + * to check out using the policies outlined in :strategy, this callback is + * invoked. It gets passed the path to the conflicting file, the OID + * of the object in the index (as a hex-encoded string), the entry's mode + * in the index and the mode in the working directory. A return value of + * anything Ruby considers to be true will abort the checkout operation. */ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) { - VALUE ruby_opts; - git_repository *repo; - int error; + VALUE ruby_opts; + VALUE progress_callback = Qnil; + VALUE conflict_callback = Qnil; + git_repository *repo; + int error; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; Data_Get_Struct(self, git_repository, repo); @@ -1080,7 +1098,7 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ /* Parse the wealth of options and collect the results in `opts' */ - rb_git__parse_checkout_options(&ruby_opts, &opts); + rb_git__parse_checkout_options(&ruby_opts, &opts, &progress_callback, &conflict_callback); /* Checkout operation */ error = git_checkout_index(repo, NULL, &opts); @@ -1101,8 +1119,10 @@ static VALUE rb_git_repo_checkout_index(int argc, VALUE argv[], VALUE self) */ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) { - VALUE ruby_opts; - git_repository *repo; + VALUE ruby_opts; + VALUE progress_callback = Qnil; + VALUE conflict_callback = Qnil; + git_repository *repo; int error; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; @@ -1114,7 +1134,7 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) ruby_opts = rb_hash_new(); /* If not passed, assume an empty hash */ /* Parse the wealth of options and collect the results in `opts' */ - rb_git__parse_checkout_options(&ruby_opts, &opts); + rb_git__parse_checkout_options(&ruby_opts, &opts, &progress_callback, &conflict_callback); /* Checkout operation */ error = git_checkout_head(repo, &opts); @@ -1146,9 +1166,11 @@ static VALUE rb_git_repo_checkout_head(int argc, VALUE argv[], VALUE self) */ static VALUE rb_git_repo_checkout_tree(int argc, VALUE argv[], VALUE self) { /* NOTE: Checking out a tree is the same as pushing a tree onto the index (read-tree) and then checking out the index (checkout-index) */ - VALUE target; - VALUE ruby_opts; - git_repository *repo; + VALUE target; + VALUE ruby_opts; + VALUE progress_callback = Qnil; + VALUE conflict_callback = Qnil; + git_repository *repo; int error; git_object *treeish; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; @@ -1165,7 +1187,7 @@ static VALUE rb_git_repo_checkout_tree(int argc, VALUE argv[], VALUE self) Data_Get_Struct(target, git_object, treeish); /* Parse the wealth of options and collect the results in `opts' */ - rb_git__parse_checkout_options(&ruby_opts, &opts); + rb_git__parse_checkout_options(&ruby_opts, &opts, &progress_callback, &conflict_callback); /* Update the index */ error = git_checkout_tree(repo, treeish, &opts); From 2a3b6e7555a73e01830d10b7fa0d834086c6e2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 7 Dec 2012 17:26:24 +0100 Subject: [PATCH 32/34] Add tests for callbacks --- test/repo_test.rb | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/repo_test.rb b/test/repo_test.rb index 074d3052d..71626ffdb 100644 --- a/test/repo_test.rb +++ b/test/repo_test.rb @@ -194,6 +194,29 @@ FileUtils.rm_rf(tmpdir) end end + + it "calls transfer and checkout callbacks on cloning" do + Dir.mktmpdir("clone-target-dir") do |tmpdir| + transfer_called = false + progress_called = false + + pcb = proc{progress_called = true} + + Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, :strategy => [:safe], :progress_cb => pcb) do + transfer_called = true + end + + assert(transfer_called, "Didn't call transfer callback") + assert(progress_called, "Didn't call progress callback") + + conflict_called = false + ccb = proc{conflict_called = true} + FileUtils.rm_rf(tmpdir) + Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", tmpdir, :strategy => [:default], :conflict_cb => ccb) + assert(conflict_called, "Didn't call conflict callback") + end + end + end describe "Repository checkouts" do @@ -249,4 +272,18 @@ assert_nil @repo.checkout_head(:strategy => [:remove_untracked]) assert !File.file?(@path_to_other_file) end + + it "calls progress and conflict callbacks" do + progress_called = false + pcb = proc{progress_called = true} + @repo.checkout_tree(@last_new_file_commit, :progress_cb => pcb, :strategy => [:safe]) + assert(progress_called, "Didn't call progress callback") + + conflict_called = false + ccb = proc{conflict_called = true} + `cd '#@repo_path' && rm -rf *` + @repo.checkout_tree(@last_master_commit, :conflict_cb => ccb, :strategy => [:default]) + assert(conflict_called, "Didn't call conflict callback") + end + end From 86b5a4a2f33ada13361f636b0a7bed80fda14255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 7 Dec 2012 17:44:44 +0100 Subject: [PATCH 33/34] Some doc fixes --- ext/rugged/rugged_repo.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ext/rugged/rugged_repo.c b/ext/rugged/rugged_repo.c index 1f4b30cea..97a242337 100644 --- a/ext/rugged/rugged_repo.c +++ b/ext/rugged/rugged_repo.c @@ -132,8 +132,11 @@ void rb_git_repo__free(git_repository *repo) /* Helper function used by the checkout_* methods to parse the argument hash. * Takes the hash (ruby_opts) and a git_checkout_opts struct to fill with the results - * of the parsing process. Be sure to use GIT_CHECKOUT_OPTS_INIT for initialising - * `p_opts'. */ + * of the parsing process. `p_progress_cb' and `p_conflict_cb' are pointers to Ruby + * values which are used to store the callback procs extracted from `ruby_opts'; note + * these pointers are automatically stored in `p_opts', but the VALUEs themself also + * have to be stored somewhere, and firing malloc() on this seems just too big. + * Be sure to use GIT_CHECKOUT_OPTS_INIT for initialising `p_opts'. */ static void rb_git__parse_checkout_options(const VALUE* ruby_opts, git_checkout_opts* p_opts, VALUE* p_progress_cb, VALUE* p_conflict_cb) { VALUE opts_strategy; @@ -385,7 +388,7 @@ int rb_git_repo__conflict_callback(const char* conflicting_path, const git_oid* } /* Callback function passed to libgit2 when you passed a callback - * to the checkout functions. `payload' is the ruby lamda for the + * to the checkout functions. `payload' is the ruby lambda for the * callback. */ void rb_git_repo__progress_callback(const char* path, size_t completed_steps, size_t total_steps, void* payload) { @@ -552,7 +555,8 @@ static VALUE rb_git_repo_clone_bare(VALUE self, VALUE url, VALUE target_path) * If a block is given, it gets passed statistics about the fetching process * (all values are integers). * - * If no block is given, this method releases the GVL during the clone operation. + * If no callback of any kind is given (i.e. no block is passed, and you don't use the + * :progress_cb and :conflict_cb parameters in +opts+), this method releases the GVL. * * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2") * Rugged::Repository.clone_repo("git://github.com/libgit2/libgit2.git", "~/libgit2.git", bare: true) @@ -1013,7 +1017,8 @@ static VALUE rb_git_repo_set_workdir(VALUE self, VALUE rb_workdir) * all operations mentioned there, but note that libgit2 itself * doesn't implement them all yet. * - * +opts+ is a hash which can take the following values: + * +opts+ is a hash which can take the following values (pro tip: + * use :strategy => [:safe] if you're unsure): * [:strategy ([:default])] * Defines how to exactly check out the given +treeish+. This is an * array containing one or more of the following symbols: From 12c1ba2ce7ca1bced9267ed0aafd62fa0d679b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20G=C3=BClker?= Date: Fri, 7 Dec 2012 17:57:56 +0100 Subject: [PATCH 34/34] Remove debugging flags from extconf.rb --- ext/rugged/extconf.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/rugged/extconf.rb b/ext/rugged/extconf.rb index bfb6eab3a..564258786 100644 --- a/ext/rugged/extconf.rb +++ b/ext/rugged/extconf.rb @@ -1,7 +1,5 @@ require 'mkmf' -$CFLAGS += " -g" - def sys(cmd) puts " -- #{cmd}" unless ret = xsystem(cmd)