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>
			
			
				maint
			
			
		
							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
	
	 Junio C Hamano
						Junio C Hamano