From f28bae0c380467409515ffd25247f3204dcc4019 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Mon, 15 Feb 2016 17:16:00 +0000 Subject: rebase: persist a single in-memory index When performing an in-memory rebase, keep a single index for the duration, so that callers have the expected index lifecycle and do not hold on to an index that is free'd out from under them. --- include/git2/rebase.h | 21 +++++++++++++++------ src/rebase.c | 35 +++++++++++++++++++++++------------ tests/rebase/inmemory.c | 10 ++++++---- tests/rebase/iterator.c | 20 ++++++++++++++++++-- 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/include/git2/rebase.h b/include/git2/rebase.h index 4fda1fd2f..ece8b3664 100644 --- a/include/git2/rebase.h +++ b/include/git2/rebase.h @@ -142,12 +142,6 @@ typedef struct { * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. */ const char *exec; - - /** - * The index that is the result of an operation. - * This is set only for in-memory rebases. - */ - git_index *index; } git_rebase_operation; /** @@ -247,6 +241,21 @@ GIT_EXTERN(int) git_rebase_next( git_rebase_operation **operation, git_rebase *rebase); +/** + * Gets the index produced by the last operation, which is the result + * of `git_rebase_next` and which will be committed by the next + * invocation of `git_rebase_commit`. This is useful for resolving + * conflicts in an in-memory rebase before committing them. You must + * call `git_index_free` when you are finished with this. + * + * This is only applicable for in-memory rebases; for rebases within + * a working directory, the changes were applied to the repository's + * index. + */ +GIT_EXTERN(int) git_rebase_inmemory_index( + git_index **index, + git_rebase *rebase); + /** * Commits the current patch. You must have resolved any conflicts that * were introduced during the patch application from the `git_rebase_next` diff --git a/src/rebase.c b/src/rebase.c index 52a6df032..b9d1d4fc5 100644 --- a/src/rebase.c +++ b/src/rebase.c @@ -71,8 +71,8 @@ struct git_rebase { size_t current; /* Used by in-memory rebase */ + git_index *index; git_commit *last_commit; - git_index *last_index; /* Used by regular (not in-memory) merge-style rebase */ git_oid orig_head_id; @@ -856,10 +856,13 @@ static int rebase_next_inmemory( (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0) goto done; - git_index_free(rebase->last_index); - rebase->last_index = index; - operation->index = index; - index = NULL; + if (!rebase->index) { + rebase->index = index; + index = NULL; + } else { + if ((error = git_index_read_index(rebase->index, index)) < 0) + goto done; + } *out = operation; @@ -895,6 +898,18 @@ int git_rebase_next( return error; } +int git_rebase_inmemory_index( + git_index **out, + git_rebase *rebase) +{ + assert(out && rebase && rebase->index); + + GIT_REFCOUNT_INC(rebase->index); + *out = rebase->index; + + return 0; +} + static int rebase_commit__create( git_commit **out, git_rebase *rebase, @@ -1018,17 +1033,13 @@ static int rebase_commit_inmemory( operation = git_array_get(rebase->operations, rebase->current); assert(operation); - assert(operation->index); + assert(rebase->index); assert(rebase->last_commit); - if ((error = rebase_commit__create(&commit, rebase, operation->index, + if ((error = rebase_commit__create(&commit, rebase, rebase->index, rebase->last_commit, author, committer, message_encoding, message)) < 0) goto done; - git_index_free(rebase->last_index); - operation->index = NULL; - rebase->last_index = NULL; - git_commit_free(rebase->last_commit); rebase->last_commit = commit; @@ -1313,7 +1324,7 @@ void git_rebase_free(git_rebase *rebase) if (rebase == NULL) return; - git_index_free(rebase->last_index); + git_index_free(rebase->index); git_commit_free(rebase->last_commit); git__free(rebase->onto_name); git__free(rebase->orig_head_name); diff --git a/tests/rebase/inmemory.c b/tests/rebase/inmemory.c index c7942d6af..d5d89c719 100644 --- a/tests/rebase/inmemory.c +++ b/tests/rebase/inmemory.c @@ -54,7 +54,7 @@ void test_rebase_inmemory__can_resolve_conflicts(void) git_status_list *status_list; git_oid pick_id, commit_id, expected_commit_id; git_signature *signature; - git_index *repo_index; + git_index *rebase_index, *repo_index; git_index_entry resolution = {{0}}; git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; @@ -86,7 +86,8 @@ void test_rebase_inmemory__can_resolve_conflicts(void) cl_assert_equal_i(0, git_status_list_entrycount(status_list)); /* but that the index returned from rebase does have conflicts */ - cl_assert(git_index_has_conflicts(rebase_operation->index)); + cl_git_pass(git_rebase_inmemory_index(&rebase_index, rebase)); + cl_assert(git_index_has_conflicts(rebase_index)); cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -94,8 +95,8 @@ void test_rebase_inmemory__can_resolve_conflicts(void) resolution.path = "asparagus.txt"; resolution.mode = GIT_FILEMODE_BLOB; git_oid_fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9"); - cl_git_pass(git_index_conflict_remove(rebase_operation->index, "asparagus.txt")); - cl_git_pass(git_index_add(rebase_operation->index, &resolution)); + cl_git_pass(git_index_conflict_remove(rebase_index, "asparagus.txt")); + cl_git_pass(git_index_add(rebase_index, &resolution)); /* and finally create a commit for the resolved rebase operation */ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); @@ -110,5 +111,6 @@ void test_rebase_inmemory__can_resolve_conflicts(void) git_reference_free(branch_ref); git_reference_free(upstream_ref); git_index_free(repo_index); + git_index_free(rebase_index); git_rebase_free(rebase); } diff --git a/tests/rebase/iterator.c b/tests/rebase/iterator.c index e761d39d3..db57b0a83 100644 --- a/tests/rebase/iterator.c +++ b/tests/rebase/iterator.c @@ -13,7 +13,8 @@ void test_rebase_iterator__initialize(void) { repo = cl_git_sandbox_init("rebase"); cl_git_pass(git_repository_index(&_index, repo)); - cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb")); + cl_git_pass(git_signature_new(&signature, "Rebaser", + "rebaser@rebaser.rb", 1405694510, 0)); } void test_rebase_iterator__cleanup(void) @@ -53,7 +54,7 @@ void test_iterator(bool inmemory) git_reference *branch_ref, *upstream_ref; git_annotated_commit *branch_head, *upstream_head; git_rebase_operation *rebase_operation; - git_oid commit_id; + git_oid commit_id, expected_id; int error; opts.inmemory = inmemory; @@ -77,16 +78,25 @@ void test_iterator(bool inmemory) NULL, NULL)); test_operations(rebase, 0); + git_oid_fromstr(&expected_id, "776e4c48922799f903f03f5f6e51da8b01e4cce0"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 1); + git_oid_fromstr(&expected_id, "ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 2); + git_oid_fromstr(&expected_id, "948b12fe18b84f756223a61bece4c307787cd5d4"); + cl_assert_equal_oid(&expected_id, &commit_id); + if (!inmemory) { git_rebase_free(rebase); cl_git_pass(git_rebase_open(&rebase, repo, NULL)); @@ -97,11 +107,17 @@ void test_iterator(bool inmemory) NULL, NULL)); test_operations(rebase, 3); + git_oid_fromstr(&expected_id, "d9d5d59d72c9968687f9462578d79878cd80e781"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_pass(git_rebase_next(&rebase_operation, rebase)); cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL)); test_operations(rebase, 4); + git_oid_fromstr(&expected_id, "9cf383c0a125d89e742c5dec58ed277dd07588b3"); + cl_assert_equal_oid(&expected_id, &commit_id); + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase)); cl_assert_equal_i(GIT_ITEROVER, error); test_operations(rebase, 4); -- cgit v1.2.1