When no merge.tool or diff.tool is configured or manually selected, the
selection of a default tool is sensitive to the DISPLAY variable; in a
GUI session a gui-specific tool will be proposed if found, and
otherwise a terminal-based one. This "GUI-optimizing" behavior is
important because a GUI can make a huge difference to a user's ability
to understand and correctly complete a non-trivial conflicting merge.
Some time ago the merge.guitool and diff.guitool config options were
introduced to enable users to configure both a GUI tool, and a non-GUI
tool (with fallback if no GUI tool configured), in the same environment.
Unfortunately, the --gui argument introduced to support the selection of
the guitool is still explicit. When using configured tools, there is no
equivalent of the no-tool-configured "propose a GUI tool if we are in a GUI
environment" behavior.
As proposed in <xmqqmtb8jsej.fsf@gitster.g>, introduce new configuration
options, difftool.guiDefault and mergetool.guiDefault, supporting a special
value "auto" which causes the corresponding tool or guitool to be selected
depending on the presence of a non-empty DISPLAY value. Also support "true"
to say "default to the guitool (unless --no-gui is passed on the
commandline)", and "false" as the previous default behavior when these new
configuration options are not specified.
Signed-off-by: Tao Klerks <tao@klerks.biz>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
When 98ea309b3f (mergetool: add hideResolved configuration,
2021-02-09) introduced the mergetool.hideResolved setting to reduce
the clutter in viewing non-conflicted sections of files in a
mergetool, it enabled it by default, explaining:
No adverse effects were noted in a small survey of popular mergetools[1]
so this behavior defaults to `true`.
In practice, alas, adverse effects do appear. A few issues:
1. No indication is shown in the UI that the base, local, and remote
versions shown have been modified by additional resolution. This
is inherent in the design: the idea of mergetool.hideResolved is to
convince a mergetool that expects pristine local, base, and remote
files to show partially resolved verisons of those files instead;
there is no additional source of information accessible to the
mergetool to see where the resolution has happened.
(By contrast, a mergetool generating the partial resolution from
conflict markers for itself would be able to hilight the resolved
sections with a different color.)
A user accustomed to seeing the files without partial resolution
gets no indication that this behavior has changed when they upgrade
Git.
2. If the computed merge did not line up the files correctly (for
example due to repeated sections in the file), the partially
resolved files can be misleading and do not have enough information
to reconstruct what happened and compute the correct merge result.
3. Resolving a conflict can involve information beyond the textual
conflict. For example, if the local and remote versions added
overlapping functionality in different ways, seeing the full
unresolved versions of each alongside the base gives information
about each side's intent that makes it possible to come up with a
resolution that combines those two intents. By contrast, when
starting with partially resolved versions of those files, one can
produce a subtly wrong resolution that includes redundant extra
code added by one side that is not needed in the approach taken
on the other.
All that said, a user wanting to focus on textual conflicts with
reduced clutter can still benefit from mergetool.hideResolved=true as
a way to deemphasize sections of the code that resolve cleanly without
requiring any changes to the invoked mergetool. The caveats described
above are reduced when the user has explicitly turned this on, because
then the user is aware of them.
Flip the default to 'false'.
Reported-by: Dana Dahlstrom <dahlstrom@google.com>
Helped-by: Seth House <seth@eseth.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Add a per-tool override flag so that users may enable the flag for one
tool and disable it for another by setting
`mergetool.<tool>.hideResolved` to `false`.
In addition, the author or maintainer of a mergetool may optionally
override the default `hideResolved` value for that mergetool. If the
`mergetools/<tool>` shell script contains a `hide_resolved_enabled`
function it will be called when the mergetool is invoked and the return
value will be used as the default for the `hideResolved` flag.
hide_resolved_enabled () {
return 1
}
Disabling may be desirable if the mergetool wants or needs access to the
original, unmodified 'LOCAL' and 'REMOTE' versions of the conflicted
file. For example:
- A tool may use a custom conflict resolution algorithm and prefer to
ignore the results of Git's conflict resolution.
- A tool may want to visually compare/constrast the version of the file
from before the merge (saved to 'LOCAL', 'REMOTE', and 'BASE') with
Git's conflict resolution results (saved to 'MERGED').
Helped-by: Johannes Sixt <j6t@kdbg.org>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Seth House <seth@eseth.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This is preparation for the following commit where we need to source the
mergetool shell script to look for overrides before `run_merge_tool` is
called. Previously `run_merge_tool` both sourced that script and invoked
the mergetool.
In the case of the following commit, we need the result of the
`hide_resolved` override, if present, before we actually run
`run_merge_tool`.
The new `initialize_merge_tool` wrapper is exposed and documented as
a public interface for consistency with the existing `run_merge_tool`
which is also public. Although `setup_tool` could instead be exposed
directly, the related `setup_user_tool` would probably also want to be
elevated to match and this felt the cleanest to me.
Signed-off-by: Seth House <seth@eseth.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
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.htmlc884424769/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>
git-mergetool spawns an enormous amount of processes. For this reason,
the test script, t7610, is exceptionally slow, in particular, on
Windows. Most of the processes are invocations of git. There are
also some that can be replaced with shell builtins. Avoid repeated
calls of `git ls-files` and `awk`.
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-mergetool spawns an enormous amount of processes. For this reason,
the test script, t7610, is exceptionally slow, in particular, on
Windows. Most of the processes are invocations of git. There are
also some that can be replaced with shell builtins. Do so with `expr`.
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
In git-mergetool, the logic for getting which merge tool to use is
duplicated in git-mergetool--lib, except for the fact that it needs to
know whether the tool was guessed or not.
Rewrite `get_merge_tool` to return whether or not the tool was guessed
through the return code and make git-mergetool call this function
instead of duplicating the logic. Note that 1 was chosen to be the
return code of when a tool is guessed because it seems like a slightly
more abnormal condition than getting a tool that's explicitly specified
but this is completely arbitrary.
Also, let `$GIT_MERGETOOL_GUI` be set to determine whether or not the
guitool will be selected.
This change is not completely backwards compatible as there may be
external users of git-mergetool--lib. However, only one user,
git-diffall[1], was found from searching GitHub and Google, and this
tool is superseded by `git difftool --dir-diff` anyway. It seems very
unlikely that there exists an external caller that would take into
account the return code of `get_merge_tool` as it would always return 0
before this change so this change probably does not affect any external
users.
[1]: https://github.com/thenigan/git-diffall
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
In line with how difftool accepts a -g/--[no-]gui option, make mergetool
accept the same option in order to use the `merge.guitool` variable to
find the default mergetool instead of `merge.tool`.
Signed-off-by: Denton Liu <liu.denton@gmail.com>
Signed-off-by: Anmol Mago <anmolmago@gmail.com>
Signed-off-by: Brian Ho <briankyho@gmail.com>
Signed-off-by: David Lu <david.lu97@outlook.com>
Signed-off-by: Ryan Wang <shirui.wang@hotmail.com>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Eliminate an unnecessary prompt to continue after failed merger, by
not calling the prompt_after_failed_merge function when only one
iteration remains.
Uses positional parameters to count files in the list to make it
easier to see if we have any more paths to process from within the
loop.
Signed-off-by: Nicholas Guriev <guriev-ns@ya.ru>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
"git mergetool" (without any pathspec on the command line) that is
not run from the top-level of the working tree no longer works in
Git v2.11, failing to get the list of unmerged paths from the output
of "git rerere remaining". This regression was introduced by
57937f70a0 ("mergetool: honor diff.orderFile", 2016-10-07).
This is because the pathnames output by the 'git rerere remaining'
command are relative to the top-level directory but the 'git diff
--name-only' command expects its pathname arguments to be relative
to the current working directory. To make everything consistent,
cd_to_toplevel before running 'git diff --name-only' and adjust any
relative pathnames.
Signed-off-by: Richard Hansen <hansenr@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This will make it easier for a future commit to convert a relative
orderfile pathname to either absolute or relative to the top-level
directory. It also improves code readability.
Signed-off-by: Richard Hansen <hansenr@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Teach mergetool to pass "-O<orderfile>" down to `git diff` when
specified on the command-line.
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: David Aguilar <davvid@gmail.com>
Reviewed-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Teach mergetool to get the list of files to edit via `diff` so that we
gain support for diff.orderFile.
Suggested-by: Luis Gutierrez <luisgutz@gmail.com>
Helped-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: David Aguilar <davvid@gmail.com>
Reviewed-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Make it easier to follow the program's flow by isolating all
logic into functions. Isolate the main execution code path into
a single unit instead of having prompt_after_failed_merge()
interrupt it partyway through.
The use of a main() function is borrowing a convention from C,
Python, Perl, and many other languages. This helps readers more
familiar with other languages understand the purpose of each
function when diving into the codebase with fresh eyes.
Signed-off-by: David Aguilar <davvid@gmail.com>
Reviewed-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Every yes/no question in difftool/mergetool scripts has slightly
different form, and none of them is consistent with the form git
itself uses.
Make the form of all the questions consistent with the form used
by git.
Reviewed-by: John Keeping <john@keeping.me.uk>
Signed-off-by: Nikola Forró <nforro@redhat.com>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Teach resolve_deleted_merge() to honor the mergetool.keepBackup and
mergetool.keepTemporaries configuration knobs.
This ensures that the worktree is kept pristine when resolving deletion
conflicts with the variables both set to false.
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
If two branches each move a file into different directories then
mergetool will fail because it assumes that the file being merged, and
its parent directory, are present in the worktree.
Create the merge file's parent directory to allow using the
deleted base version of the file for merge resolution when
encountering a delete/delete conflict.
The end result is that a delete/delete conflict is presented for the
user to resolve.
Reported-by: Joe Einertson <joe@kidblog.org>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-sh-setup sets IFS but it is not used by git-difftool--helper.
Set IFS in git-mergetool--lib so that the mergetool scriptlets,
difftool, and mergetool do not need to do so.
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Combine the $last_status checks into a single conditional.
Replace $last_status and $rollup_status with a single variable.
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Allow a temporary directory specified to be used while running "git
mergetool" backend.
* da/mergetool-temporary-directory:
t7610-mergetool: add test cases for mergetool.writeToTemp
mergetool: add an option for writing to a temporary directory
Allow "git mergetool --help" to run outside a Git repository.
* da/mergetool-tool-help:
difftool: don't assume that default sh is sane
mergetool: don't require a work tree for --tool-help
git-sh-setup: move GIT_DIR initialization into a function
mergetool: use more conservative temporary filenames
test-lib-functions: adjust style to match CodingGuidelines
t7610-mergetool: prefer test_config over git config
Tweak the names of the three throw-away files "git mergetool" comes
up with to feed the merge tool backend, so that a file with a
single dot in its name in the original (e.g. "hello.c") will have
only one dot in these variants (e.g. "hello_BASE_4321.c").
* da/mergetool-temporary-filename:
mergetool: use more conservative temporary filenames
Teach mergetool to write files in a temporary directory when
'mergetool.writeToTemp' is true.
This is helpful for tools such as Eclipse which cannot cope with
multiple copies of the same file in the worktree.
Suggested-by: Charles Bailey <charles@hashpling.org>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Avoid filenames with multiple dots so that overly-picky tools do
not misinterpret their extension.
Previously, foo/bar.ext in the worktree would result in e.g.
./foo/bar.ext.BASE.1234.ext
This can be improved by having only a single .ext and using
underscore instead of dot so that the extension cannot be
misinterpreted. The resulting path becomes:
./foo/bar_BASE_1234.ext
Suggested-by: Sergio Ferrero <sferrero@ensoftcorp.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-difftool used to create a command list script containing $( ... )
and explicitly calls "sh -c" with this list.
Instead, allow mergetool --tool-help to take a mode parameter and call
mergetool directly to invoke the show_tool_help function. This mode
parameter is intented for use solely by difftool.
Signed-off-by: Charles Bailey <cbailey32@bloomberg.net>
Helped-by: John Keeping <john@keeping.me.uk>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Charles Bailey <cbailey32@bloomberg.net>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Avoid filenames with multiple dots so that overly-picky tools do
not misinterpret their extension.
Previously, foo/bar.ext in the worktree would result in e.g.
./foo/bar.ext.BASE.1234.ext
This can be improved by having only a single .ext and using
underscore instead of dot so that the extension cannot be
misinterpreted. The resulting path becomes:
./foo/bar_BASE_1234.ext
Suggested-by: Sergio Ferrero <sferrero@ensoftcorp.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
The construct is error-prone; "test" being built-in in most modern
shells, the reason to avoid "test <cond> && test <cond>" spawning
one extra process by using a single "test <cond> -a <cond>" no
longer exists.
Signed-off-by: Elia Pinto <gitter.spiros@gmail.com>
Reviewed-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
It's annoying to see the prompt:
Hit return to start merge resolution tool (foo):
Every time the user does 'git mergetool' even if the user already
configured 'foo' as the wanted tool.
Display this prompt only when the user hasn't explicitly configured a
tool.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
If git-mergetool was invoked with files with a percent sign (%) in
their names, it would print an error. For example, if you were
calling mergetool on a file called "%2F":
printf: %2F: invalid directive
Do not pass random string to printf as if it were a valid format.
Use format string "%s" and pass the string as data to be formatted
instead.
Signed-off-by: Asheesh Laroia <asheesh@asheesh.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This is the first step in unifying "git difftool --tool-help" and
"git mergetool --tool-help".
Signed-off-by: John Keeping <john@keeping.me.uk>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This script is one of the sizeable ones that tempted people to copy
its "neibouring style" in their new code, but was littered with
styles incompatible with our style guide.
- use one tab, not four spaces, per indent level;
- long lines can be wrapped after '|', '&&', or '||' for
readability.
- structures like "if .. then .. else .. fi", "while .. do .. done"
are split into lines in such a way that does not require
unnecessary semicolon.
- case, esac and case-arms align at the same column.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This way we do not have to risk the list of tools going out of sync
between the implementation and the documentation.
In the same spirit as bf73fc2 (difftool: print list of valid tools
with '--tool-help', 2012-03-29), trim the list of merge backends in
the documentation. We do not want to have a complete list of valid
tools; we only want a list to help people guess what kind of things
the tools do to be specified there, and refer them to --tool-help
for a complete list.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Some merge tools cannot cope when $LOCAL, $BASE, or $REMOTE are missing.
$BASE can be missing when two branches independently add the same
filename.
Provide an empty file to make these tools happy.
When a delete/modify conflict occurs, $LOCAL and $REMOTE can also be
missing. We have special case code to handle such case so this change
may not affect that codepath, but try to be consistent and create an
empty file for them anyway.
Reported-by: Jason Wenger <jcwenger@gmail.com>
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Earlier code wanted to run merge_file and prompt_after_failed_merge
both of which wanted to read from the standard input of the entire
script inside a while loop, which read from a pipe, and in order to
do so, it redirected the original standard input to another file
descriptor. We no longer need to do so after the previous change.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Mergetool now treats its path arguments as a pathspec (like other git
subcommands), restricting action to the given files and directories.
Files matching the pathspec are filtered so mergetool only acts on
unmerged paths; previously it would assume each path argument was in an
unresolved state, and get confused when it couldn't check out their
other stages.
Running "git mergetool subdir" will prompt to resolve all conflicted
blobs under subdir.
Signed-off-by: Jonathon Mah <me@JonathonMah.com>
Acked-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Mostly fixed already by 6b44577 (mergetool: check return value
from read, 2011-07-01). Catch two uses it missed.
Signed-off-by: Jay Soffian <jaysoffian@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
The process may not even have the standard input open in which case it
will get stuck in an infinite loop to prompt and read nothing.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
When the index has conflicted submodules, mergetool used to mildly
clobber the module, renaming it to mymodule.BACKUP.nnnn, then failing to
copy it non-recursively.
Recognize submodules and offer a resolution instead:
Submodule merge conflict for 'Shared':
{local}: submodule commit ad9f12e3e6205381bf2163a793d1e596a9e211d0
{remote}: submodule commit f5893fb70ec5646efcd9aa643c5136753ac89253
Use (l)ocal or (r)emote, or (a)bort?
Selecting a commit will stage it, but not update the submodule (as git
does had there been no conflict). Type changes are also supported,
should the path be a submodule on one side, and a file, symlink,
directory, or deleted on the other.
Signed-off-by: Jonathon Mah <me@JonathonMah.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Since bb0a484 (mergetool: Skip autoresolved paths, 2010-08-17),
mergetool uses different ways of figuring out the list of files with
merge conflicts depending on whether rerere is active. If rerere is
active, mergetool will use 'git rerere status' to list the files with
remaining conflicts. However, the output from that command does not
list conflicts of types that rerere does not handle, such as
modify/remove conflicts.
Another problem with solely relying on the output from 'git rerere
status' is that, for new conflicts that are not yet known to rerere,
the output from the command will list the files even after adding them
to the index. This means that if the conflicts in some files have been
resolved and 'git mergetool' is run again, it will ask the user
something like the following for each of those files.
file1: file does not need merging
Continue merging other unresolved paths (y/n) ?
Solve both of these problems by replacing the call to 'git rerere
status' with a call to the new 'git rerere remaining' that was
introduced in the previous commit.
Signed-off-by: Martin von Zweigbergk <martin.von.zweigbergk@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
mergetool used /dev/tty to switch back to receiving input from the user
via inside a block with a redirected stdin.
This harms testability, so change mergetool to save its original stdin
to an alternative fd in this block and restore it for those sub-commands
that need the original stdin.
Includes additional compatibility fix from Jonathan Nieder.
Tested-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Charles Bailey <charles@hashpling.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
When mergetool is run without path limiters it loops
over each entry in 'git ls-files -u'. This includes
autoresolved paths.
Teach mergetool to only merge files listed in 'rerere status'
when rerere is enabled.
There are some subtle but harmless changes in behavior.
We now call cd_to_toplevel when no paths are given.
We do this because 'rerere status' paths are always relative
to the root. This is beneficial for the non-rerere use as
well in that mergetool now runs against all unmerged files
regardless of the current directory.
This also slightly tweaks the output when run without paths
to be more readable.
The old output:
Merging the files: foo
bar
baz
The new output:
Merging:
foo
bar
baz
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
The mergetool--lib scriplet was tricky to use because it relied upon
the existance of several global shell variables. This removes more
global variables so that things are simpler for callers.
A side effect is that some variables are recomputed each time
run_merge_tool() is called, but the overhead for recomputing
them is justified by the simpler implementation.
Signed-off-by: David Aguilar <davvid@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
In several places mergetool.keepBackup was misspelled as merge.keepBackup.
Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
Signed-off-by: Junio C Hamano <gitster@pobox.com>