unpack-trees: allow Porcelain to give different error messages
authorJunio C Hamano <gitster@pobox.com>
Sat, 17 May 2008 19:03:49 +0000 (12:03 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 20 May 2008 02:30:13 +0000 (19:30 -0700)
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>
builtin-checkout.c
unpack-trees.c
unpack-trees.h

index 10ec137ccec7d7f281562817d81a1035c4aa6124..83da7ca9ffae6a7fae94fcd012078bdec36ed72e 100644 (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()) {
index 1ab28fda45be940b300b504ae029f3fba2c28fa9..0de5a31c0b68f87491e540fdc1978809fee00240 100644 (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
index d436d6ced9939beeb4599dc8fddebe0890e55db8..94e567265af9a69a30dd5c578439b6444e50004d 100644 (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;