merge: use editor by default in interactive sessions

Traditionally, a cleanly resolved merge was committed by "git merge" using
the auto-generated merge commit log message without invoking the editor.

After 5 years of use in the field, it turns out that people perform too
many unjustified merges of the upstream history into their topic branches.
These merges are not just useless, but they are often not explained well,
and making the end result unreadable when it gets time for merging their
history back to their upstream.

Earlier we added the "--edit" option to the command, so that people can
edit the log message to explain and justify their merge commits. Let's
take it one step further and spawn the editor by default when we are in an
interactive session (i.e. the standard input and the standard output are
pointing at the same tty device).

There may be existing scripts that leave the standard input and the
standard output of the "git merge" connected to whatever environment the
scripts were started, and such invocation might trigger the above
"interactive session" heuristics.  GIT_MERGE_AUTOEDIT environment variable
can be set to "no" at the beginning of such scripts to use the historical
behaviour while the script runs.

Note that this backward compatibility is meant only for scripts, and we
deliberately do *not* support "merge.edit = yes/no/auto" configuration
option to allow people to keep the historical behaviour.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2012-01-10 22:44:45 -08:00
parent 0074d18dc0
commit f8246281af
4 changed files with 50 additions and 9 deletions

View file

@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
SYNOPSIS
--------
[verse]
'git merge' [-n] [--stat] [--no-commit] [--squash]
'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
[-s <strategy>] [-X <strategy-option>]
[--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
'git merge' <msg> HEAD <commit>...

View file

@ -8,10 +8,20 @@ failed and do not autocommit, to give the user a chance to
inspect and further tweak the merge result before committing.
--edit::
-e::
--no-edit::
Invoke an editor before committing successful mechanical merge to
further edit the auto-generated merge message, so that the user
can explain and justify the merge. The `--no-edit` option can be
used to accept the auto-generated message (this is generally
discouraged). The `--edit` option is still useful if you are
giving a draft message with the `-m` option from the command line
and want to edit it in the editor.
+
Invoke editor before committing successful merge to further
edit the default merge message.
Older scripts may depend on the historical behaviour of not allowing the
user to edit the merge log message. They will see an editor opened when
they run `git merge`. To make it easier to adjust such scripts to the
updated behaviour, the environment variable `GIT_MERGE_AUTOEDIT` can be
set to `no` at the beginning of them.
--ff::
--no-ff::

View file

@ -46,7 +46,7 @@ static const char * const builtin_merge_usage[] = {
static int show_diffstat = 1, shortlog_len, squash;
static int option_commit = 1, allow_fast_forward = 1;
static int fast_forward_only, option_edit;
static int fast_forward_only, option_edit = -1;
static int allow_trivial = 1, have_message;
static struct strbuf merge_msg;
static struct commit_list *remoteheads;
@ -189,7 +189,7 @@ static struct option builtin_merge_options[] = {
"create a single commit instead of doing a merge"),
OPT_BOOLEAN(0, "commit", &option_commit,
"perform a commit if the merge succeeds (default)"),
OPT_BOOLEAN('e', "edit", &option_edit,
OPT_BOOL('e', "edit", &option_edit,
"edit message before committing"),
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
"allow fast-forward (default)"),
@ -877,12 +877,12 @@ static void prepare_to_commit(void)
write_merge_msg(&msg);
run_hook(get_index_file(), "prepare-commit-msg",
git_path("MERGE_MSG"), "merge", NULL, NULL);
if (option_edit) {
if (0 < option_edit) {
if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
abort_commit(NULL);
}
read_merge_msg(&msg);
stripspace(&msg, option_edit);
stripspace(&msg, 0 < option_edit);
if (!msg.len)
abort_commit(_("Empty commit message."));
strbuf_release(&merge_msg);
@ -1076,6 +1076,33 @@ static void write_merge_state(void)
close(fd);
}
static int default_edit_option(void)
{
static const char name[] = "GIT_MERGE_AUTOEDIT";
const char *e = getenv(name);
struct stat st_stdin, st_stdout;
if (have_message)
/* an explicit -m msg without --[no-]edit */
return 0;
if (e) {
int v = git_config_maybe_bool(name, e);
if (v < 0)
die("Bad value '%s' in environment '%s'", e, name);
return v;
}
/* Use editor if stdin and stdout are the same and is a tty */
return (!fstat(0, &st_stdin) &&
!fstat(1, &st_stdout) &&
isatty(0) &&
st_stdin.st_dev == st_stdout.st_dev &&
st_stdin.st_ino == st_stdout.st_ino &&
st_stdin.st_mode == st_stdout.st_mode);
}
int cmd_merge(int argc, const char **argv, const char *prefix)
{
unsigned char result_tree[20];
@ -1261,6 +1288,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
}
if (option_edit < 0)
option_edit = default_edit_option();
if (!use_strategies) {
if (!remoteheads->next)
add_strategies(pull_twohead, DEFAULT_TWOHEAD);

View file

@ -63,7 +63,8 @@ GIT_AUTHOR_NAME='A U Thor'
GIT_COMMITTER_EMAIL=committer@example.com
GIT_COMMITTER_NAME='C O Mitter'
GIT_MERGE_VERBOSITY=5
export GIT_MERGE_VERBOSITY
GIT_MERGE_AUTOEDIT=no
export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT
export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR