diff options
-rw-r--r-- | merge-ort.c | 210 |
1 files changed, 191 insertions, 19 deletions
diff --git a/merge-ort.c b/merge-ort.c index 51b049358e..414e7b7eea 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -17,7 +17,9 @@ #include "cache.h" #include "merge-ort.h" +#include "blob.h" #include "cache-tree.h" +#include "commit-reach.h" #include "diff.h" #include "diffcore.h" #include "dir.h" @@ -56,6 +58,8 @@ struct merge_options_internal { * * these keys serve to intern all the path strings, which allows * us to do pointer comparison on directory names instead of * strcmp; we just have to be careful to use the interned strings. + * (Technically paths_to_free may track some strings that were + * removed from froms paths.) * * The values of paths: * * either a pointer to a merged_info, or a conflict_info struct @@ -91,6 +95,25 @@ struct merge_options_internal { struct strmap conflicted; /* + * paths_to_free: additional list of strings to free + * + * If keys are removed from "paths", they are added to paths_to_free + * to ensure they are later freed. We avoid free'ing immediately since + * other places (e.g. conflict_info.pathnames[]) may still be + * referencing these paths. + */ + struct string_list paths_to_free; + + /* + * output: special messages and conflict notices for various paths + * + * This is a map of pathnames (a subset of the keys in "paths" above) + * to strbufs. It gathers various warning/conflict/notice messages + * for later processing. + */ + struct strmap output; + + /* * current_dir_name: temporary var used in collect_merge_info_callback() * * Used to set merged_info.directory_name; see documentation for that @@ -164,6 +187,13 @@ struct conflict_info { unsigned df_conflict:1; /* + * Whether this path is/was involved in a non-content conflict other + * than a directory/file conflict (e.g. rename/rename, rename/delete, + * file location based on possible directory rename). + */ + unsigned path_conflict:1; + + /* * For filemask and dirmask, the ith bit corresponds to whether the * ith entry is a file (filemask) or a directory (dirmask). Thus, * filemask & dirmask is always zero, and filemask | dirmask is at @@ -189,6 +219,8 @@ struct conflict_info { unsigned match_mask:3; }; +/*** Function Grouping: various utility functions ***/ + /* * For the next three macros, see warning for conflict_info.merged. * @@ -219,6 +251,61 @@ static void free_strmap_strings(struct strmap *map) } } +static void clear_internal_opts(struct merge_options_internal *opti, + int reinitialize) +{ + assert(!reinitialize); + + /* + * We marked opti->paths with strdup_strings = 0, so that we + * wouldn't have to make another copy of the fullpath created by + * make_traverse_path from setup_path_info(). But, now that we've + * used it and have no other references to these strings, it is time + * to deallocate them. + */ + free_strmap_strings(&opti->paths); + strmap_clear(&opti->paths, 1); + + /* + * All keys and values in opti->conflicted are a subset of those in + * opti->paths. We don't want to deallocate anything twice, so we + * don't free the keys and we pass 0 for free_values. + */ + strmap_clear(&opti->conflicted, 0); + + /* + * opti->paths_to_free is similar to opti->paths; we created it with + * strdup_strings = 0 to avoid making _another_ copy of the fullpath + * but now that we've used it and have no other references to these + * strings, it is time to deallocate them. We do so by temporarily + * setting strdup_strings to 1. + */ + opti->paths_to_free.strdup_strings = 1; + string_list_clear(&opti->paths_to_free, 0); + opti->paths_to_free.strdup_strings = 0; + + if (!reinitialize) { + struct hashmap_iter iter; + struct strmap_entry *e; + + /* Release and free each strbuf found in output */ + strmap_for_each_entry(&opti->output, &iter, e) { + struct strbuf *sb = e->value; + strbuf_release(sb); + /* + * While strictly speaking we don't need to free(sb) + * here because we could pass free_values=1 when + * calling strmap_clear() on opti->output, that would + * require strmap_clear to do another + * strmap_for_each_entry() loop, so we just free it + * while we're iterating anyway. + */ + free(sb); + } + strmap_clear(&opti->output, 0); + } +} + static int err(struct merge_options *opt, const char *err, ...) { va_list params; @@ -235,6 +322,29 @@ static int err(struct merge_options *opt, const char *err, ...) return -1; } +__attribute__((format (printf, 4, 5))) +static void path_msg(struct merge_options *opt, + const char *path, + int omittable_hint, /* skippable under --remerge-diff */ + const char *fmt, ...) +{ + va_list ap; + struct strbuf *sb = strmap_get(&opt->priv->output, path); + if (!sb) { + sb = xmalloc(sizeof(*sb)); + strbuf_init(sb, 0); + strmap_put(&opt->priv->output, path, sb); + } + + va_start(ap, fmt); + strbuf_vaddf(sb, fmt, ap); + va_end(ap); + + strbuf_addch(sb, '\n'); +} + +/*** Function Grouping: functions related to collect_merge_info() ***/ + static void setup_path_info(struct merge_options *opt, struct string_list_item *result, const char *current_dir_name, @@ -489,6 +599,27 @@ static int collect_merge_info(struct merge_options *opt, return ret; } +/*** Function Grouping: functions related to threeway content merges ***/ + +static int handle_content_merge(struct merge_options *opt, + const char *path, + const struct version_info *o, + const struct version_info *a, + const struct version_info *b, + const char *pathnames[3], + const int extra_marker_size, + struct version_info *result) +{ + die("Not yet implemented"); +} + +/*** Function Grouping: functions related to detect_and_process_renames(), *** + *** which are split into directory and regular rename detection sections. ***/ + +/*** Function Grouping: functions related to directory rename detection ***/ + +/*** Function Grouping: functions related to regular rename detection ***/ + static int detect_and_process_renames(struct merge_options *opt, struct tree *merge_base, struct tree *side1, @@ -506,6 +637,8 @@ static int detect_and_process_renames(struct merge_options *opt, return clean; } +/*** Function Grouping: functions related to process_entries() ***/ + static int string_list_df_name_compare(const char *one, const char *two) { int onelen = strlen(one); @@ -887,9 +1020,27 @@ static void process_entry(struct merge_options *opt, ci->merged.clean = 0; ci->merged.result.mode = ci->stages[1].mode; oidcpy(&ci->merged.result.oid, &ci->stages[1].oid); + /* When we fix above, we'll call handle_content_merge() */ + (void)handle_content_merge; } else if (ci->filemask == 3 || ci->filemask == 5) { /* Modify/delete */ - die("Not yet implemented."); + const char *modify_branch, *delete_branch; + int side = (ci->filemask == 5) ? 2 : 1; + int index = opt->priv->call_depth ? 0 : side; + + ci->merged.result.mode = ci->stages[index].mode; + oidcpy(&ci->merged.result.oid, &ci->stages[index].oid); + ci->merged.clean = 0; + + modify_branch = (side == 1) ? opt->branch1 : opt->branch2; + delete_branch = (side == 1) ? opt->branch2 : opt->branch1; + + path_msg(opt, path, 0, + _("CONFLICT (modify/delete): %s deleted in %s " + "and modified in %s. Version %s of %s left " + "in tree."), + path, delete_branch, modify_branch, + modify_branch, path); } else if (ci->filemask == 2 || ci->filemask == 4) { /* Added on one side */ int side = (ci->filemask == 4) ? 2 : 1; @@ -984,6 +1135,8 @@ static void process_entries(struct merge_options *opt, string_list_clear(&dir_metadata.offsets, 0); } +/*** Function Grouping: functions related to merge_switch_to_result() ***/ + static int checkout(struct merge_options *opt, struct tree *prev, struct tree *next) @@ -1154,7 +1307,29 @@ void merge_switch_to_result(struct merge_options *opt, } if (display_update_msgs) { - /* TODO: print out CONFLICT and other informational messages. */ + struct merge_options_internal *opti = result->priv; + struct hashmap_iter iter; + struct strmap_entry *e; + struct string_list olist = STRING_LIST_INIT_NODUP; + int i; + + /* Hack to pre-allocate olist to the desired size */ + ALLOC_GROW(olist.items, strmap_get_size(&opti->output), + olist.alloc); + + /* Put every entry from output into olist, then sort */ + strmap_for_each_entry(&opti->output, &iter, e) { + string_list_append(&olist, e->key)->util = e->value; + } + string_list_sort(&olist); + + /* Iterate over the items, printing them */ + for (i = 0; i < olist.nr; ++i) { + struct strbuf *sb = olist.items[i].util; + + printf("%s", sb->buf); + } + string_list_clear(&olist, 0); } merge_finalize(opt, result); @@ -1167,25 +1342,12 @@ void merge_finalize(struct merge_options *opt, assert(opt->priv == NULL); - /* - * We marked opti->paths with strdup_strings = 0, so that we - * wouldn't have to make another copy of the fullpath created by - * make_traverse_path from setup_path_info(). But, now that we've - * used it and have no other references to these strings, it is time - * to deallocate them. - */ - free_strmap_strings(&opti->paths); - strmap_clear(&opti->paths, 1); - - /* - * All keys and values in opti->conflicted are a subset of those in - * opti->paths. We don't want to deallocate anything twice, so we - * don't free the keys and we pass 0 for free_values. - */ - strmap_clear(&opti->conflicted, 0); + clear_internal_opts(opti, 0); FREE_AND_NULL(opti); } +/*** Function Grouping: helper functions for merge_incore_*() ***/ + static void merge_start(struct merge_options *opt, struct merge_result *result) { /* Sanity checks on opt */ @@ -1226,15 +1388,25 @@ static void merge_start(struct merge_options *opt, struct merge_result *result) * Although we initialize opt->priv->paths with strdup_strings=0, * that's just to avoid making yet another copy of an allocated * string. Putting the entry into paths means we are taking - * ownership, so we will later free it. + * ownership, so we will later free it. paths_to_free is similar. * * In contrast, conflicted just has a subset of keys from paths, so * we don't want to free those (it'd be a duplicate free). */ strmap_init_with_options(&opt->priv->paths, NULL, 0); strmap_init_with_options(&opt->priv->conflicted, NULL, 0); + string_list_init(&opt->priv->paths_to_free, 0); + + /* + * keys & strbufs in output will sometimes need to outlive "paths", + * so it will have a copy of relevant keys. It's probably a small + * subset of the overall paths that have special output. + */ + strmap_init(&opt->priv->output); } +/*** Function Grouping: merge_incore_*() and their internal variants ***/ + /* * Originally from merge_trees_internal(); heavily adapted, though. */ |