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:
parent
377d9c409f
commit
8ccba008ee
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user