1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-11 08:56:09 +02:00

Be more user-friendly when refusing to do something because of conflict.

Various commands refuse to run in the presence of conflicts (commit,
merge, pull, cherry-pick/revert). They all used to provide rough, and
inconsistant error messages.

A new variable advice.resolveconflict is introduced, and allows more
verbose messages, pointing the user to the appropriate solution.

For commit, the error message used to look like this:

$ git commit
foo.txt: needs merge
foo.txt: unmerged (c34a92682e0394bc0d6f4d4a67a8e2d32395c169)
foo.txt: unmerged (3afcd75de8de0bb5076942fcb17446be50451030)
foo.txt: unmerged (c9785d77b76dfe4fb038bf927ee518f6ae45ede4)
error: Error building trees

The "need merge" line is given by refresh_cache. We add the IN_PORCELAIN
option to make the output more consistant with the other porcelain
commands, and catch the error in return, to stop with a clean error
message. The next lines were displayed by a call to cache_tree_update(),
which is not reached anymore if we noticed the conflict.

The new output looks like:

U       foo.txt
fatal: 'commit' is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use 'git add/rm <file>' as
appropriate to mark resolution and make a commit, or use 'git commit -a'.

Pull is slightly modified to abort immediately if $GIT_DIR/MERGE_HEAD
exists instead of waiting for merge to complain.

The behavior of merge and the test-case are slightly modified to reflect
the usual flow: start with conflicts, fix them, and afterwards get rid of
MERGE_HEAD, with different error messages at each stage.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Matthieu Moy 2010-01-12 10:54:44 +01:00 committed by Junio C Hamano
parent 902f235378
commit d38a30df7d
9 changed files with 93 additions and 13 deletions

View File

@ -130,6 +130,10 @@ advice.*::
Advice shown when linkgit:git-merge[1] refuses to Advice shown when linkgit:git-merge[1] refuses to
merge to avoid overwritting local changes. merge to avoid overwritting local changes.
Default: true. Default: true.
resolveConflict::
Advices shown by various commands when conflicts
prevent the operation from being performed.
Default: true.
-- --
core.fileMode:: core.fileMode::

View File

@ -3,6 +3,7 @@
int advice_push_nonfastforward = 1; int advice_push_nonfastforward = 1;
int advice_status_hints = 1; int advice_status_hints = 1;
int advice_commit_before_merge = 1; int advice_commit_before_merge = 1;
int advice_resolve_conflict = 1;
static struct { static struct {
const char *name; const char *name;
@ -11,6 +12,7 @@ static struct {
{ "pushnonfastforward", &advice_push_nonfastforward }, { "pushnonfastforward", &advice_push_nonfastforward },
{ "statushints", &advice_status_hints }, { "statushints", &advice_status_hints },
{ "commitbeforemerge", &advice_commit_before_merge }, { "commitbeforemerge", &advice_commit_before_merge },
{ "resolveconflict", &advice_resolve_conflict },
}; };
int git_default_advice_config(const char *var, const char *value) int git_default_advice_config(const char *var, const char *value)
@ -27,3 +29,17 @@ int git_default_advice_config(const char *var, const char *value)
return 0; return 0;
} }
void NORETURN die_resolve_conflict(const char *me)
{
if (advice_resolve_conflict)
/*
* Message used both when 'git commit' fails and when
* other commands doing a merge do.
*/
die("'%s' is not possible because you have unmerged files.\n"
"Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
"appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
else
die("'%s' is not possible because you have unmerged files.", me);
}

View File

@ -1,10 +1,15 @@
#ifndef ADVICE_H #ifndef ADVICE_H
#define ADVICE_H #define ADVICE_H
#include "git-compat-util.h"
extern int advice_push_nonfastforward; extern int advice_push_nonfastforward;
extern int advice_status_hints; extern int advice_status_hints;
extern int advice_commit_before_merge; extern int advice_commit_before_merge;
extern int advice_resolve_conflict;
int git_default_advice_config(const char *var, const char *value); int git_default_advice_config(const char *var, const char *value);
extern void NORETURN die_resolve_conflict(const char *me);
#endif /* ADVICE_H */ #endif /* ADVICE_H */

View File

@ -219,6 +219,16 @@ static void create_base_index(void)
exit(128); /* We've already reported the error, finish dying */ exit(128); /* We've already reported the error, finish dying */
} }
static void refresh_cache_or_die(int refresh_flags)
{
/*
* refresh_flags contains REFRESH_QUIET, so the only errors
* are for unmerged entries.
*/
if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
die_resolve_conflict("commit");
}
static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status) static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
{ {
int fd; int fd;
@ -258,7 +268,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
if (all || (also && pathspec && *pathspec)) { if (all || (also && pathspec && *pathspec)) {
int fd = hold_locked_index(&index_lock, 1); int fd = hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, pathspec, 0); add_files_to_cache(also ? prefix : NULL, pathspec, 0);
refresh_cache(refresh_flags); refresh_cache_or_die(refresh_flags);
if (write_cache(fd, active_cache, active_nr) || if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock)) close_lock_file(&index_lock))
die("unable to write new_index file"); die("unable to write new_index file");
@ -277,7 +287,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
*/ */
if (!pathspec || !*pathspec) { if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1); fd = hold_locked_index(&index_lock, 1);
refresh_cache(refresh_flags); refresh_cache_or_die(refresh_flags);
if (write_cache(fd, active_cache, active_nr) || if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock)) commit_locked_index(&index_lock))
die("unable to write new_index file"); die("unable to write new_index file");

View File

@ -847,11 +847,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
const char *best_strategy = NULL, *wt_strategy = NULL; const char *best_strategy = NULL, *wt_strategy = NULL;
struct commit_list **remotes = &remoteheads; struct commit_list **remotes = &remoteheads;
if (file_exists(git_path("MERGE_HEAD"))) if (read_cache_unmerged()) {
die("You have not concluded your merge. (MERGE_HEAD exists)"); die_resolve_conflict("merge");
if (read_cache_unmerged()) }
die("You are in the middle of a conflicted merge." if (file_exists(git_path("MERGE_HEAD"))) {
" (index unmerged)"); /*
* There is no unmerged entry, don't advise 'git
* add/rm <file>', just 'git commit'.
*/
if (advice_resolve_conflict)
die("You have not concluded your merge (MERGE_HEAD exists).\n"
"Please, commit your changes before you can merge.");
else
die("You have not concluded your merge (MERGE_HEAD exists).");
}
/* /*
* Check if we are _not_ on a detached HEAD, i.e. if there is a * Check if we are _not_ on a detached HEAD, i.e. if there is a

View File

@ -233,6 +233,19 @@ static struct tree *empty_tree(void)
return tree; return tree;
} }
static NORETURN void die_dirty_index(const char *me)
{
if (read_cache_unmerged()) {
die_resolve_conflict(me);
} else {
if (advice_commit_before_merge)
die("Your local changes would be overwritten by %s.\n"
"Please, commit your changes or stash them to proceed.", me);
else
die("Your local changes would be overwritten by %s.\n", me);
}
}
static int revert_or_cherry_pick(int argc, const char **argv) static int revert_or_cherry_pick(int argc, const char **argv)
{ {
unsigned char head[20]; unsigned char head[20];
@ -269,7 +282,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
if (get_sha1("HEAD", head)) if (get_sha1("HEAD", head))
die ("You do not have a valid HEAD"); die ("You do not have a valid HEAD");
if (index_differs_from("HEAD", 0)) if (index_differs_from("HEAD", 0))
die ("Dirty index: cannot %s", me); die_dirty_index(me);
} }
discard_cache(); discard_cache();

View File

@ -13,8 +13,29 @@ set_reflog_action "pull $*"
require_work_tree require_work_tree
cd_to_toplevel cd_to_toplevel
test -z "$(git ls-files -u)" ||
die "You are in the middle of a conflicted merge." die_conflict () {
git diff-index --cached --name-status -r --ignore-submodules HEAD --
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
die "Pull is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use 'git add/rm <file>'
as appropriate to mark resolution, or use 'git commit -a'."
else
die "Pull is not possible because you have unmerged files."
fi
}
die_merge () {
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
die "You have not concluded your merge (MERGE_HEAD exists).
Please, commit your changes before you can merge."
else
die "You have not concluded your merge (MERGE_HEAD exists)."
fi
}
test -z "$(git ls-files -u)" || die_conflict
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
strategy_args= diffstat= no_commit= squash= no_ff= ff_only= strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
log_arg= verbosity= log_arg= verbosity=

View File

@ -276,11 +276,13 @@ test_expect_success 'fail if the index has unresolved entries' '
test_must_fail git merge "$c5" && test_must_fail git merge "$c5" &&
test_must_fail git merge "$c5" 2> out && test_must_fail git merge "$c5" 2> out &&
grep "not possible because you have unmerged files" out &&
git add -u &&
test_must_fail git merge "$c5" 2> out &&
grep "You have not concluded your merge" out && grep "You have not concluded your merge" out &&
rm -f .git/MERGE_HEAD && rm -f .git/MERGE_HEAD &&
test_must_fail git merge "$c5" 2> out && test_must_fail git merge "$c5" 2> out &&
grep "You are in the middle of a conflicted merge" out grep "Your local changes to .* would be overwritten by merge." out
' '
test_expect_success 'merge-recursive remove conflict' ' test_expect_success 'merge-recursive remove conflict' '

View File

@ -66,7 +66,7 @@ test_expect_success 'revert forbidden on dirty working tree' '
echo content >extra_file && echo content >extra_file &&
git add extra_file && git add extra_file &&
test_must_fail git revert HEAD 2>errors && test_must_fail git revert HEAD 2>errors &&
grep "Dirty index" errors grep "Your local changes would be overwritten by " errors
' '