X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=unpack-trees.c;h=cba0aca062f201c5cd5f8799f2190d4a6f06e7c7;hb=6e1c23442a0315ad440bb8457703dcf1ad943b96;hp=a59f47557a2b3760c27b93fa678697c35211f952;hpb=3c993de9f25d052b791c55609468c59bcd47fc19;p=git.git diff --git a/unpack-trees.c b/unpack-trees.c index a59f47557..cba0aca06 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -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) { @@ -26,11 +56,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, * directories, in case this unlink is the removal of the * last entry in the directory -- empty directories are removed. */ -static void unlink_entry(char *name, char *last_symlink) +static void unlink_entry(struct cache_entry *ce) { char *cp, *prev; + char *name = ce->name; - if (has_symlink_leading_path(name, last_symlink)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return; if (unlink(name)) return; @@ -58,7 +89,6 @@ static int check_updates(struct unpack_trees_options *o) { unsigned cnt = 0, total = 0; struct progress *progress = NULL; - char last_symlink[PATH_MAX]; struct index_state *index = &o->result; int i; int errs = 0; @@ -75,24 +105,27 @@ static int check_updates(struct unpack_trees_options *o) cnt = 0; } - *last_symlink = '\0'; for (i = 0; i < index->cache_nr; i++) { struct cache_entry *ce = index->cache[i]; - if (ce->ce_flags & (CE_UPDATE | CE_REMOVE)) - display_progress(progress, ++cnt); if (ce->ce_flags & CE_REMOVE) { + display_progress(progress, ++cnt); if (o->update) - unlink_entry(ce->name, last_symlink); + unlink_entry(ce); remove_index_entry_at(&o->result, i); i--; continue; } + } + + for (i = 0; i < index->cache_nr; i++) { + struct cache_entry *ce = index->cache[i]; + if (ce->ce_flags & CE_UPDATE) { + display_progress(progress, ++cnt); ce->ce_flags &= ~CE_UPDATE; if (o->update) { errs |= checkout_entry(ce, &state, NULL); - *last_symlink = '\0'; } } } @@ -325,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) @@ -371,19 +409,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) @@ -427,7 +463,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) @@ -514,12 +550,27 @@ 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; } +/* + * This gets called when there was no index entry for the tree entry 'dst', + * but we found a file in the working tree that 'lstat()' said was fine, + * and we're on a case-insensitive filesystem. + * + * See if we can find a case-insensitive match in the index that also + * matches the stat information, and assume it's that other file! + */ +static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st) +{ + struct cache_entry *src; + + src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1); + return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID); +} + /* * We do not want to remove or overwrite a working tree file that * is not tracked, unless it is ignored. @@ -532,12 +583,23 @@ static int verify_absent(struct cache_entry *ce, const char *action, if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_leading_path(ce->name, NULL)) + if (has_symlink_leading_path(ce_namelen(ce), ce->name)) return 0; if (!lstat(ce->name, &st)) { int cnt; int dtype = ce_to_dtype(ce); + struct cache_entry *result; + + /* + * It may be that the 'lstat()' succeeded even though + * target 'ce' was absent, because there is an old + * entry that is different only in case.. + * + * Ignore that lstat() if it matches. + */ + if (ignore_case && icase_exists(o, ce, &st)) + return 0; if (o->dir && excluded(o->dir, ce->name, &dtype)) /* @@ -581,16 +643,14 @@ static int verify_absent(struct cache_entry *ce, const char *action, * delete this path, which is in a subdirectory that * is being replaced with a blob. */ - cnt = index_name_pos(&o->result, ce->name, strlen(ce->name)); - if (0 <= cnt) { - struct cache_entry *ce = o->result.cache[cnt]; - if (ce->ce_flags & CE_REMOVE) + result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0); + if (result) { + if (result->ce_flags & CE_REMOVE) 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; } @@ -722,7 +782,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); } /* @@ -730,7 +790,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 */ @@ -872,11 +932,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; } } @@ -902,7 +962,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