unpack-trees: allow Porcelain to give different error messages

The plumbing output is sacred as it is an API.  We _could_ change it if it
is broken in such a way that it cannot convey necessary information fully,
but we just do not _reword_ for the sake of rewording.  If somebody does
not like it, s/he is complaining too late.  S/he should have been here in
early May 2005 and make the language used by the API closer to what humans
read.  S/he wasn't here.  Too bad, and it is too late.

And people who complain should look at a bigger picture.  Look at what was
suggested by one of them and think for five seconds:

     $ git checkout mytopic
    -fatal: Entry 'frotz' not uptodate. Cannot merge.
    +fatal: Entry 'frotz' has local changes. Cannot merge.

If you do not see something wrong with this output, your brain has already
been rotten with use of git for too long a time.  Nobody asked us to
"merge" but why are we talking about "Cannot merge"?

This patch introduces a mechanism to allow Porcelains to specify messages
that are different from the ones that is given by the underlying plumbing
implementation of read-tree, so that we can reword the message Porcelains give
without disrupting the output from the plumbing.

    $ git-checkout pu
    error: You have local changes to 'Makefile'; cannot switch branches.

There are other places that ask unpack_trees() to n-way merge, detect
issues  and let it issue error message on its own, but I did this as a
demonstration and replaced only one message.

Yes I know about C99 structure initializers.  I'd love to use them but we
try to be nice to compilers without it.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2008-05-17 12:03:49 -07:00
parent 377d9c409f
commit 8ccba008ee
3 changed files with 52 additions and 14 deletions

View File

@ -236,6 +236,8 @@ static int merge_working_tree(struct checkout_opts *opts,
topts.src_index = &the_index;
topts.dst_index = &the_index;
topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
refresh_cache(REFRESH_QUIET);
if (unmerged_cache()) {

View File

@ -8,6 +8,36 @@
#include "progress.h"
#include "refs.h"
/*
* Error messages expected by scripts out of plumbing commands such as
* read-tree. Non-scripted Porcelain is not required to use these messages
* and in fact are encouraged to reword them to better suit their particular
* situation better. See how "git checkout" replaces not_uptodate_file to
* explain why it does not allow switching between branches when you have
* local changes, for example.
*/
static struct unpack_trees_error_msgs unpack_plumbing_errors = {
/* would_overwrite */
"Entry '%s' would be overwritten by merge. Cannot merge.",
/* not_uptodate_file */
"Entry '%s' not uptodate. Cannot merge.",
/* not_uptodate_dir */
"Updating '%s' would lose untracked files in it",
/* would_lose_untracked */
"Untracked working tree file '%s' would be %s by merge.",
/* bind_overlap */
"Entry '%s' overlaps with '%s'. Cannot bind.",
};
#define ERRORMSG(o,fld) \
( ((o) && (o)->msgs.fld) \
? ((o)->msgs.fld) \
: (unpack_plumbing_errors.fld) )
static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
unsigned int set, unsigned int clear)
{
@ -383,10 +413,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
/* Here come the merge functions */
static int reject_merge(struct cache_entry *ce)
static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
{
return error("Entry '%s' would be overwritten by merge. Cannot merge.",
ce->name);
return error(ERRORMSG(o, would_overwrite), ce->name);
}
static int same(struct cache_entry *a, struct cache_entry *b)
@ -430,7 +459,7 @@ static int verify_uptodate(struct cache_entry *ce,
if (errno == ENOENT)
return 0;
return o->gently ? -1 :
error("Entry '%s' not uptodate. Cannot merge.", ce->name);
error(ERRORMSG(o, not_uptodate_file), ce->name);
}
static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@ -517,8 +546,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
if (i)
return o->gently ? -1 :
error("Updating '%s' would lose untracked files in it",
ce->name);
error(ERRORMSG(o, not_uptodate_dir), ce->name);
free(pathbuf);
return cnt;
}
@ -618,8 +646,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
}
return o->gently ? -1 :
error("Untracked working tree file '%s' "
"would be %s by merge.", ce->name, action);
error(ERRORMSG(o, would_lose_untracked), ce->name, action);
}
return 0;
}
@ -751,7 +778,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
/* #14, #14ALT, #2ALT */
if (remote && !df_conflict_head && head_match && !remote_match) {
if (index && !same(index, remote) && !same(index, head))
return o->gently ? -1 : reject_merge(index);
return o->gently ? -1 : reject_merge(index, o);
return merged_entry(remote, index, o);
}
/*
@ -759,7 +786,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
* make sure that it matches head.
*/
if (index && !same(index, head))
return o->gently ? -1 : reject_merge(index);
return o->gently ? -1 : reject_merge(index, o);
if (head) {
/* #5ALT, #15 */
@ -901,11 +928,11 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
else {
/* all other failures */
if (oldtree)
return o->gently ? -1 : reject_merge(oldtree);
return o->gently ? -1 : reject_merge(oldtree, o);
if (current)
return o->gently ? -1 : reject_merge(current);
return o->gently ? -1 : reject_merge(current, o);
if (newtree)
return o->gently ? -1 : reject_merge(newtree);
return o->gently ? -1 : reject_merge(newtree, o);
return -1;
}
}
@ -931,7 +958,7 @@ int bind_merge(struct cache_entry **src,
o->merge_size);
if (a && old)
return o->gently ? -1 :
error("Entry '%s' overlaps with '%s'. Cannot bind.", a->name, old->name);
error(ERRORMSG(o, bind_overlap), a->name, old->name);
if (!a)
return keep_entry(old, o);
else

View File

@ -8,6 +8,14 @@ struct unpack_trees_options;
typedef int (*merge_fn_t)(struct cache_entry **src,
struct unpack_trees_options *options);
struct unpack_trees_error_msgs {
const char *would_overwrite;
const char *not_uptodate_file;
const char *not_uptodate_dir;
const char *would_lose_untracked;
const char *bind_overlap;
};
struct unpack_trees_options {
unsigned int reset:1,
merge:1,
@ -23,6 +31,7 @@ struct unpack_trees_options {
int pos;
struct dir_struct *dir;
merge_fn_t fn;
struct unpack_trees_error_msgs msgs;
int head_idx;
int merge_size;