]> git.ipfire.org Git - thirdparty/git.git/commitdiff
mergetool: add hideResolved configuration
authorSeth House <seth@eseth.com>
Tue, 9 Feb 2021 20:07:10 +0000 (13:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Feb 2021 22:09:16 +0000 (14:09 -0800)
The purpose of a mergetool is to help the user resolve any conflicts
that Git cannot automatically resolve. If there is a conflict that must
be resolved manually Git will write a file named MERGED which contains
everything Git was able to resolve by itself and also everything that it
was not able to resolve wrapped in conflict markers.

One way to think of MERGED is as a two- or three-way diff. If each
"side" of the conflict markers is separately extracted an external tool
can represent those conflicts as a side-by-side diff.

However many mergetools instead diff LOCAL and REMOTE both of which
contain versions of the file from before the merge. Since the conflicts
Git resolved automatically are not present it forces the user to
manually re-resolve those conflicts. Some mergetools also show MERGED
but often only for reference and not as the focal point to resolve the
conflicts.

This adds a `mergetool.hideResolved` flag that will overwrite LOCAL and
REMOTE with each corresponding "side" of a conflicted file and thus hide
all conflicts that Git was able to resolve itself. Overwriting these
files will immediately benefit any mergetool that uses them without
requiring any changes to the tool.

No adverse effects were noted in a small survey of popular mergetools[1]
so this behavior defaults to `true`. However it can be globally disabled
by setting `mergetool.hideResolved` to `false`.

[1] https://www.eseth.org/2020/mergetools.html
    https://github.com/whiteinge/eseth/blob/c884424769fffb05d87afb33b2cf80cecb4044c3/2020/mergetools.md

Original-implementation-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Seth House <seth@eseth.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/mergetool.txt
git-mergetool.sh
t/t7610-mergetool.sh

index 16a27443a3edc1d1a1f25ea03bad04896caa59c8..b858191970c35c5fcc9e6573585743f55a35b604 100644 (file)
@@ -40,6 +40,16 @@ mergetool.meld.useAutoMerge::
        value of `false` avoids using `--auto-merge` altogether, and is the
        default value.
 
+mergetool.hideResolved::
+       During a merge Git will automatically resolve as many conflicts as
+       possible and write the 'MERGED' file containing conflict markers around
+       any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
+       represent the versions of the file from before Git's conflict
+       resolution. This flag causes 'LOCAL' and 'REMOTE' to be overwriten so
+       that only the unresolved conflicts are presented to the merge tool. Can
+       be configured per-tool via the `mergetool.<tool>.hideResolved`
+       configuration variable. Defaults to `true`.
+
 mergetool.keepBackup::
        After performing a merge, the original file with conflict markers
        can be saved as a file with a `.orig` extension.  If this variable
index e3f6d543fb5bb0777483081fadaa7b66035ab5fa..40a103443d9881353befe9baca8f1e574730b020 100755 (executable)
@@ -239,6 +239,13 @@ checkout_staged_file () {
        fi
 }
 
+hide_resolved () {
+       git merge-file --ours -q -p "$LOCAL" "$BASE" "$REMOTE" >"$LCONFL"
+       git merge-file --theirs -q -p "$LOCAL" "$BASE" "$REMOTE" >"$RCONFL"
+       mv -- "$LCONFL" "$LOCAL"
+       mv -- "$RCONFL" "$REMOTE"
+}
+
 merge_file () {
        MERGED="$1"
 
@@ -276,7 +283,9 @@ merge_file () {
 
        BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
        LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+       LCONFL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_LCONFL_$$$ext"
        REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+       RCONFL="$MERGETOOL_TMPDIR/${BASE}_REMOTE_RCONFL_$$$ext"
        BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
        base_mode= local_mode= remote_mode=
@@ -322,6 +331,11 @@ merge_file () {
        checkout_staged_file 2 "$MERGED" "$LOCAL"
        checkout_staged_file 3 "$MERGED" "$REMOTE"
 
+       if test "$(git config --type=bool mergetool.hideResolved)" != "false"
+       then
+               hide_resolved
+       fi
+
        if test -z "$local_mode" || test -z "$remote_mode"
        then
                echo "Deleted merge conflict for '$MERGED':"
index 70afdd06fa7208a20ac66f1eb6e9e3690d5fe528..0e34b87e376accbd613d96645f368fb5300c43e3 100755 (executable)
@@ -828,4 +828,22 @@ test_expect_success 'mergetool -Oorder-file is honored' '
        test_cmp expect actual
 '
 
+test_expect_success 'mergetool hideResolved' '
+       test_config mergetool.hideResolved true &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test${test_count}_b master &&
+       test_write_lines >file1 base "" a &&
+       git commit -a -m "base" &&
+       test_write_lines >file1 base "" c &&
+       git commit -a -m "remote update" &&
+       git checkout -b test${test_count}_a HEAD~ &&
+       test_write_lines >file1 local "" b &&
+       git commit -a -m "local update" &&
+       test_must_fail git merge test${test_count}_b &&
+       yes "" | git mergetool file1 &&
+       test_write_lines >expect local "" c &&
+       test_cmp expect file1 &&
+       git commit -m "test resolved with mergetool"
+'
+
 test_done