]> git.ipfire.org Git - thirdparty/git.git/commitdiff
git-apply: allow simultaneous --cached and --3way options
authorJerry Zhang <jerry@skydio.com>
Thu, 8 Apr 2021 02:13:44 +0000 (19:13 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 8 Apr 2021 05:20:33 +0000 (22:20 -0700)
"git apply" does not allow "--cached" and "--3way" to be used
together, since "--3way" writes conflict markers into the working
tree.

Allow "git apply" to accept "--cached" and "--3way" at the same
time.  When a single file auto-resolves cleanly, the result is
placed in the index at stage #0 and the command exits with 0 status.

For a file that has a conflict which cannot be cleanly
auto-resolved, the original contents from common ancestor (stage
conflict at the content level, and the command exists with non-zero
status, because there is no place (like the working tree) to leave a
half-resolved merge for the user to resolve.

The user can use `git diff` to view the contents of the conflict, or
`git checkout -m -- .` to regenerate the conflict markers in the
working directory.

Don't attempt rerere in this case since it depends on conflict
markers written to file for its database storage and lookup. There
would be two main changes required to get rerere working:

1. Allow the rerere api to accept in memory object rather than
   files, which would allow us to pass in the conflict markers
   contained in the result from ll_merge().

2. Rerere can't write to the working directory, so it would have to
   apply the result to cache stage #0 directly. A flag would be
   needed to control this.

Signed-off-by: Jerry Zhang <jerry@skydio.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-apply.txt
apply.c
t/t4108-apply-threeway.sh

index 9144575299c264dd299b542b7b5948eef35f211c..aa1ae56a25e0428cabcfa2539900ef2a09abcb7c 100644 (file)
@@ -87,8 +87,10 @@ OPTIONS
        Attempt 3-way merge if the patch records the identity of blobs it is supposed
        to apply to and we have those blobs available locally, possibly leaving the
        conflict markers in the files in the working tree for the user to
-       resolve.  This option implies the `--index` option, and is incompatible
-       with the `--reject` and the `--cached` options.
+       resolve.  This option implies the `--index` option unless the
+       `--cached` option is used, and is incompatible with the `--reject` option.
+       When used with the `--cached` option, any conflicts are left at higher stages
+       in the cache.
 
 --build-fake-ancestor=<file>::
        Newer 'git diff' output has embedded 'index information'
diff --git a/apply.c b/apply.c
index 69197268ccccbc18d70b865e47d5e3f062c0771a..8c5b29809b25e8de5b53bcfb49d2747a8c6ab219 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -134,8 +134,6 @@ int check_apply_state(struct apply_state *state, int force_apply)
 
        if (state->apply_with_reject && state->threeway)
                return error(_("--reject and --3way cannot be used together."));
-       if (state->cached && state->threeway)
-               return error(_("--cached and --3way cannot be used together."));
        if (state->threeway) {
                if (is_not_gitdir)
                        return error(_("--3way outside a repository"));
@@ -4646,7 +4644,12 @@ static int write_out_results(struct apply_state *state, struct patch *list)
                }
                string_list_clear(&cpath, 0);
 
-               repo_rerere(state->repo, 0);
+               /*
+                * rerere relies on the partially merged result being in the working
+                * tree with conflict markers, but that isn't written with --cached.
+                */
+               if (!state->cached)
+                       repo_rerere(state->repo, 0);
        }
 
        return errs;
index 9ff313f976422f9c12dc8032d14567b54cfe3765..65147efdea9a00e30d156e6f4d5d72a3987f230d 100755 (executable)
@@ -180,4 +180,54 @@ test_expect_success 'apply -3 with ambiguous repeating file' '
        test_cmp expect one_two_repeat
 '
 
+test_expect_success 'apply with --3way --cached clean apply' '
+       # Merging side should be similar to applying this patch
+       git diff ...side >P.diff &&
+
+       # The corresponding cleanly applied merge
+       git reset --hard &&
+       git checkout main~ &&
+       git merge --no-commit side &&
+       git ls-files -s >expect.ls &&
+
+       # should succeed
+       git reset --hard &&
+       git checkout main~ &&
+       git apply --cached --3way P.diff &&
+       git ls-files -s >actual.ls &&
+       print_sanitized_conflicted_diff >actual.diff &&
+
+       # The cache should resemble the corresponding merge
+       # (both files at stage #0)
+       test_cmp expect.ls actual.ls &&
+       # However the working directory should not change
+       >expect.diff &&
+       test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply with --3way --cached and conflicts' '
+       # Merging side should be similar to applying this patch
+       git diff ...side >P.diff &&
+
+       # The corresponding conflicted merge
+       git reset --hard &&
+       git checkout main^0 &&
+       test_must_fail git merge --no-commit side &&
+       git ls-files -s >expect.ls &&
+
+       # should fail to apply
+       git reset --hard &&
+       git checkout main^0 &&
+       test_must_fail git apply --cached --3way P.diff &&
+       git ls-files -s >actual.ls &&
+       print_sanitized_conflicted_diff >actual.diff &&
+
+       # The cache should resemble the corresponding merge
+       # (one file at stage #0, one file at stages #1 #2 #3)
+       test_cmp expect.ls actual.ls &&
+       # However the working directory should not change
+       >expect.diff &&
+       test_cmp expect.diff actual.diff
+'
+
 test_done