#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)
{
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)
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)
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)
* anything in the existing directory there.
*/
int namelen;
- int pos, i;
+ int i;
struct dir_struct d;
char *pathbuf;
int cnt = 0;
* in that directory.
*/
namelen = strlen(ce->name);
- pos = index_name_pos(o->src_index, ce->name, namelen);
- if (0 <= pos)
- return cnt; /* we have it as nondirectory */
- pos = -pos - 1;
- for (i = pos; i < o->src_index->cache_nr; i++) {
- struct cache_entry *ce = o->src_index->cache[i];
- int len = ce_namelen(ce);
+ for (i = o->pos; i < o->src_index->cache_nr; i++) {
+ struct cache_entry *ce2 = o->src_index->cache[i];
+ int len = ce_namelen(ce2);
if (len < namelen ||
- strncmp(ce->name, ce->name, namelen) ||
- ce->name[namelen] != '/')
+ strncmp(ce->name, ce2->name, namelen) ||
+ ce2->name[namelen] != '/')
break;
/*
- * ce->name is an entry in the subdirectory.
+ * ce2->name is an entry in the subdirectory.
*/
- if (!ce_stage(ce)) {
- if (verify_uptodate(ce, o))
+ if (!ce_stage(ce2)) {
+ if (verify_uptodate(ce2, o))
return -1;
- add_entry(o, ce, CE_REMOVE, 0);
+ add_entry(o, ce2, CE_REMOVE, 0);
}
cnt++;
}
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;
}
return 0;
if (!lstat(ce->name, &st)) {
- int cnt;
+ int ret;
int dtype = ce_to_dtype(ce);
struct cache_entry *result;
* files that are in "foo/" we would lose
* it.
*/
- cnt = verify_clean_subdirectory(ce, action, o);
+ ret = verify_clean_subdirectory(ce, action, o);
+ if (ret < 0)
+ return ret;
/*
* If this removed entries from the index,
* what that means is:
*
- * (1) the caller unpack_trees_rec() saw path/foo
+ * (1) the caller unpack_callback() saw path/foo
* in the index, and it has not removed it because
* it thinks it is handling 'path' as blob with
* D/F conflict;
* We need to increment it by the number of
* deleted entries here.
*/
- o->pos += cnt;
+ o->pos += ret;
return 0;
}
}
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;
}
/* #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);
}
/*
* 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 */
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;
}
}
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