The merge-recursive logic already had the ability to ignore the working directory and operate entirely on the index -- it needed to do this when creating a virtual merge base, i.e. when o->call_depth > 0. The only trick here is that o->call_depth > 0 was also checked to determine whether all merges conflicts should be forcibly immediately resolved (typically by treating a copy of the code with conflict markers still in it as the resolution) in order to get us some actual base tree to work with. Introduce a new merge option o->index_only which is true whenver either --index-only is passed or o->call_depth > 0, and make the portions of merge-recursive that are about operating only on the index look at o->index_only, and the portions that are about forcibly immediately resolving conflicts check o->call_depth. Signed-off-by: Elijah Newren <newren@xxxxxxxxx> --- merge-recursive.c | 37 ++++++++++++++++++++++--------------- t/t6043-merge-index-only.sh | 12 ++++++------ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/merge-recursive.c b/merge-recursive.c index b346ed6..6af37ed 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -406,10 +406,11 @@ static void record_df_conflict_files(struct merge_options *o, int i; /* - * If we're merging merge-bases, we don't want to bother with - * any working directory changes. + * If we're doing an index-only merge, we don't need to check for + * which files to remove from the working copy to make room for + * paths below directories of D/F conflicts; we can just exit early. */ - if (o->call_depth) + if (o->index_only) return; /* Ensure D/F conflicts are adjacent in the entries list. */ @@ -581,7 +582,7 @@ static int remove_file(struct merge_options *o, int clean, const char *path, int no_wd) { int update_cache = o->call_depth || clean; - int update_working_directory = !o->call_depth && !no_wd; + int update_working_directory = !o->index_only && !no_wd; if (update_cache) { if (remove_file_from_cache(path)) @@ -622,7 +623,7 @@ static char *unique_path(struct merge_options *o, const char *path, const char * base_len = newpath.len; while (string_list_has_string(&o->current_file_set, newpath.buf) || string_list_has_string(&o->current_directory_set, newpath.buf) || - (!o->call_depth && file_exists(newpath.buf))) { + (!o->index_only && file_exists(newpath.buf))) { strbuf_setlen(&newpath, base_len); strbuf_addf(&newpath, "_%d", suffix++); } @@ -742,7 +743,7 @@ static void update_file_flags(struct merge_options *o, int update_cache, int update_wd) { - if (o->call_depth) + if (o->index_only) update_wd = 0; if (update_wd) { @@ -813,7 +814,7 @@ static void update_file(struct merge_options *o, unsigned mode, const char *path) { - update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->call_depth); + update_file_flags(o, sha, mode, path, o->call_depth || clean, !o->index_only); } /* Low level file merging, update and removal */ @@ -1017,7 +1018,7 @@ static void handle_change_delete(struct merge_options *o, const char *change, const char *change_past) { char *renamed = NULL; - if (dir_in_way(path, !o->call_depth)) { + if (dir_in_way(path, !o->index_only)) { renamed = unique_path(o, path, a_sha ? o->branch1 : o->branch2); } @@ -1145,7 +1146,7 @@ static void handle_file(struct merge_options *o, remove_file(o, 0, rename->path, 0); dst_name = unique_path(o, rename->path, cur_branch); } else { - if (dir_in_way(rename->path, !o->call_depth)) { + if (dir_in_way(rename->path, !o->index_only)) { dst_name = unique_path(o, rename->path, cur_branch); output(o, 1, _("%s is a directory in %s adding as %s instead"), rename->path, other_branch, dst_name); @@ -1234,8 +1235,8 @@ static void conflict_rename_rename_2to1(struct merge_options *o, a->path, c1->path, ci->branch1, b->path, c2->path, ci->branch2); - remove_file(o, 1, a->path, o->call_depth || would_lose_untracked(a->path)); - remove_file(o, 1, b->path, o->call_depth || would_lose_untracked(b->path)); + remove_file(o, 1, a->path, o->index_only || would_lose_untracked(a->path)); + remove_file(o, 1, b->path, o->index_only || would_lose_untracked(b->path)); mfi_c1 = merge_file_special_markers(o, a, c1, &ci->ren1_other, o->branch1, c1->path, @@ -1619,7 +1620,7 @@ static int merge_content(struct merge_options *o, o->branch2 == rename_conflict_info->branch1) ? pair1->two->path : pair1->one->path; - if (dir_in_way(path, !o->call_depth)) + if (dir_in_way(path, !o->index_only)) df_conflict_remains = 1; } mfi = merge_file_special_markers(o, &one, &a, &b, @@ -1639,7 +1640,7 @@ static int merge_content(struct merge_options *o, path_renamed_outside_HEAD = !path2 || !strcmp(path, path2); if (!path_renamed_outside_HEAD) { add_cacheinfo(mfi.mode, mfi.sha, path, - 0, (!o->call_depth), 0); + 0, (!o->index_only), 0); return mfi.clean; } } else @@ -1767,7 +1768,7 @@ static int process_entry(struct merge_options *o, sha = b_sha; conf = _("directory/file"); } - if (dir_in_way(path, !o->call_depth)) { + if (dir_in_way(path, !o->index_only)) { char *new_path = unique_path(o, path, add_branch); clean_merge = 0; output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. " @@ -1819,7 +1820,7 @@ int merge_trees(struct merge_options *o, return 1; } - code = git_merge_trees(o->call_depth, common, head, merge); + code = git_merge_trees(o->index_only, common, head, merge); if (code != 0) { if (show(o, 4) || o->call_depth) @@ -1899,6 +1900,7 @@ int merge_recursive(struct merge_options *o, struct commit *merged_common_ancestors; struct tree *mrtree = mrtree; int clean; + int prev_index_only_setting; if (show(o, 4)) { output(o, 4, _("Merging:")); @@ -1929,9 +1931,12 @@ int merge_recursive(struct merge_options *o, merged_common_ancestors = make_virtual_commit(tree, "ancestor"); } + prev_index_only_setting = o->index_only; + for (iter = ca; iter; iter = iter->next) { const char *saved_b1, *saved_b2; o->call_depth++; + o->index_only = 1; /* * When the merge fails, the result contains files * with conflict markers. The cleanness flag is @@ -1954,6 +1959,8 @@ int merge_recursive(struct merge_options *o, die(_("merge returned no commit")); } + o->index_only = prev_index_only_setting; + discard_cache(); if (!o->call_depth) read_cache(); diff --git a/t/t6043-merge-index-only.sh b/t/t6043-merge-index-only.sh index 9bb64d8..2e1d953 100755 --- a/t/t6043-merge-index-only.sh +++ b/t/t6043-merge-index-only.sh @@ -28,7 +28,7 @@ test_expect_success 'setup rename/modify merge' ' git commit -m C ' -test_expect_failure '--index-only with rename/modify works in non-bare-clone' ' +test_expect_success '--index-only with rename/modify works in non-bare-clone' ' git checkout B^0 && git merge --index-only -s recursive C^0 && @@ -43,7 +43,7 @@ test_expect_failure '--index-only with rename/modify works in non-bare-clone' ' test $(git rev-parse B:a) = $(git rev-parse :b) ' -test_expect_failure '--index-only with rename/modify works in a bare clone' ' +test_expect_success '--index-only with rename/modify works in a bare clone' ' git clone --bare . bare.clone && (cd bare.clone && @@ -136,7 +136,7 @@ test_expect_success 'setup single-file criss-cross resolvable with recursive str rm -f answer ' -test_expect_failure 'recursive --index-only in non-bare repo' ' +test_expect_success 'recursive --index-only in non-bare repo' ' git reset --hard && git checkout L2^0 && @@ -150,7 +150,7 @@ test_expect_failure 'recursive --index-only in non-bare repo' ' test $(git rev-parse L2:contents) = $(git hash-object contents) ' -test_expect_failure 'recursive --index-only in bare repo' ' +test_expect_success 'recursive --index-only in bare repo' ' git clone --bare . bare.clone && (cd bare.clone && @@ -406,7 +406,7 @@ test_expect_success '--index-only ours, bare' ' ) ' -test_expect_failure '--index-only subtree, non-bare' ' +test_expect_success '--index-only subtree, non-bare' ' git reset --hard && git checkout B^0 && @@ -420,7 +420,7 @@ test_expect_failure '--index-only subtree, non-bare' ' test ! -f e ' -test_expect_failure '--index-only subtree, bare' ' +test_expect_success '--index-only subtree, bare' ' rm -rf bare.clone && git clone --bare . bare.clone && (cd bare.clone && -- 2.8.0.18.gc685494 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html