refactor userdiff textconv code
[gitweb.git] / unpack-trees.c
index 1ab28fda45be940b300b504ae029f3fba2c28fa9..e59d144d28164f2451784513105f6269f0e9167c 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)
 {
@@ -328,8 +358,13 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
        return -1;
 }
 
+/*
+ * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
+ * resulting index, -2 on failure to reflect the changes to the work tree.
+ */
 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
+       int ret;
        static struct cache_entry *dfc;
 
        if (len > MAX_UNPACK_TREES)
@@ -341,6 +376,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        state.refresh_cache = 1;
 
        memset(&o->result, 0, sizeof(o->result));
+       o->result.initialized = 1;
        if (o->src_index)
                o->result.timestamp = o->src_index->timestamp;
        o->merge_size = len;
@@ -374,19 +410,17 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                return unpack_failed(o, "Merge requires file-level merging");
 
        o->src_index = NULL;
-       if (check_updates(o))
-               return -1;
+       ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index)
                *o->dst_index = o->result;
-       return 0;
+       return ret;
 }
 
 /* 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 +464,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 +551,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 +651,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 +783,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 +791,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,16 +933,25 @@ 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;
                }
        }
-       else if (newtree)
+       else if (newtree) {
+               if (oldtree && !o->initial_checkout) {
+                       /*
+                        * deletion of the path was staged;
+                        */
+                       if (same(oldtree, newtree))
+                               return 1;
+                       return reject_merge(oldtree, o);
+               }
                return merged_entry(newtree, current, o);
+       }
        return deleted_entry(oldtree, current, o);
 }
 
@@ -931,7 +972,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