/* merge.c: * * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2 **************************************************************** * Copyright (C) 2003 Tom Lord * Copyright (C) 2004 Canonical Limited * Authors: Robert Collins * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "config-options.h" #include "po/gettext.h" #include "hackerlab/bugs/panic.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/arrays/ar.h" #include "hackerlab/mem/mem.h" #include "hackerlab/char/str.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/vu/safe.h" #include "libfsutils/file-contents.h" #include "libfsutils/tmp-files.h" #include "libfsutils/rmrf.h" #include "libawk/relassoc.h" #include "libarch/ancestry.h" #include "libarch/namespace.h" #include "libarch/patch-logs.h" #include "libarch/project-tree.h" #include "libarch/merge-points.h" #include "libarch/local-cache.h" #include "libarch/make-changeset.h" #include "libarch/apply-changeset.h" #include "libarch/merge-three-way.h" #include "libarch/chatter.h" #include "libarch/inode-sig.h" #include "libarch/invent.h" #include "libarch/merge.h" static void star_merge_callback (void * vfd, char * fmt, va_list ap); static t_uchar * arch_nearest_star_ancestor (arch_project_tree_t * from_tree, t_uchar * from_archive, t_uchar * from_revision, arch_project_tree_t * to_tree, t_uchar * to_archive, t_uchar * to_version, int trace) { t_uchar * from_version = 0; rel_table to_logs_for_from_version = 0; rel_table from_logs_for_from_version = 0; rel_table common_from_logs = 0; t_uchar * latest_common_from_revision = 0; rel_table from_merges_from_to = 0; t_uchar * highest_common_to_revision = 0; t_uchar * from_rev_of_highest_common_to_revision = 0; t_uchar * answer = 0; int x; from_version = arch_parse_package_name (arch_ret_package_version, 0, from_revision); to_logs_for_from_version = arch_logs (to_tree->root, from_archive, from_version, 0); from_logs_for_from_version = arch_logs (from_tree->root, from_archive, from_version, 0); rel_sort_table_by_field (0, to_logs_for_from_version, 0); rel_sort_table_by_field (0, from_logs_for_from_version, 0); common_from_logs = rel_join (-1, rel_join_output (1,0, -1), 0, 0, to_logs_for_from_version, from_logs_for_from_version); arch_sort_table_by_patch_level_field (1, common_from_logs, 0); if (common_from_logs) latest_common_from_revision = str_alloc_cat_many (0, from_version, "--", common_from_logs[0][0], str_end); from_merges_from_to = arch_tree_merge_points (from_tree->root, from_archive, from_version, to_archive, to_version); arch_sort_table_by_name_field (1, from_merges_from_to, 1); for (x = 0; !highest_common_to_revision && (x < rel_n_records (from_merges_from_to)); ++x) { if (arch_tree_has_log (to_tree->root, to_archive, from_merges_from_to[x][1])) { highest_common_to_revision = str_save (0, from_merges_from_to[x][1]); from_rev_of_highest_common_to_revision = str_alloc_cat_many (0, from_version, "--", from_merges_from_to[x][0], str_end); } } if (!from_rev_of_highest_common_to_revision && !latest_common_from_revision) { answer = 0; } else if (!from_rev_of_highest_common_to_revision) { answer = arch_fully_qualify (from_archive, latest_common_from_revision); } else if (!latest_common_from_revision) { answer = arch_fully_qualify (to_archive, highest_common_to_revision); } else if (0 < arch_names_cmp (from_rev_of_highest_common_to_revision, latest_common_from_revision)) { answer = arch_fully_qualify (to_archive, highest_common_to_revision); } else { answer = arch_fully_qualify (from_archive, latest_common_from_revision); } lim_free (0, from_version); rel_free_table (to_logs_for_from_version); rel_free_table (from_logs_for_from_version); rel_free_table (common_from_logs); lim_free (0, latest_common_from_revision); rel_free_table (from_merges_from_to); lim_free (0, highest_common_to_revision); lim_free (0, from_rev_of_highest_common_to_revision); return answer; } struct arch_common_ancestor_search { rel_table todo; assoc_table reached; rel_table pruned; int trace; } ; static void arch_common_ancestor_search_prune_warning(struct arch_common_ancestor_search *search, t_uchar *tree_root) { if (rel_n_records (search->pruned)) { int position; for (position = 0; position < rel_n_records (search->pruned); ++position) safe_printfmt (2, _("WARNING: patch log %s has been deleted from tree %s.\n" "The best merge point may not be chosen\n"), search->pruned[position][0], tree_root); } rel_free_table (search->pruned); search->pruned = NULL; } /* append return the directly reachable patches from fqrevision to out * , in no particular order */ static void arch_included_from_patch (struct arch_common_ancestor_search *search, rel_table * out, arch_project_tree_t *tree, t_uchar * fqrevision) { t_uchar * log = arch_project_tree_patch_log (tree, fqrevision); assoc_table headers = 0; t_uchar * new_patches_header; rel_table new_patches = 0; int x; t_uchar * continuation_header; if (!log) { /* TODO FIXME rbc 20041215 this should be replaced with 'try and get the log, if failed, then warn */ rel_add_records (&search->pruned, rel_make_record (fqrevision, 0), 0); return; } arch_parse_log (0, &headers, 0, log); new_patches_header = assoc_ref (headers, "new-patches"); continuation_header = assoc_ref (headers, "continuation-of"); if (continuation_header) { /* TODO FIXME: get the headers present in the source and do * a subtraction to get the real added headers. * then remove fqrevision and *boom* we are done. */ rel_add_records (out, rel_make_record (continuation_header, 0), 0); } else { new_patches = rel_ws_split (new_patches_header); rel_uniq_by_field (&new_patches, 0); for (x = 0; x < rel_n_records (new_patches); ++x) if (str_cmp( fqrevision, new_patches[x][0])) rel_add_records (out, rel_make_record (new_patches[x][0], 0), 0); /* add the patch ancestor predecessor */ if (!str_cmp("-0", fqrevision + str_length(fqrevision) - 2)) /* version-0, cannot determine the ancestor from the tree */ { struct exception * e; Try { arch_patch_id * patch_id = talloc_steal (talloc_context, arch_patch_id_new (fqrevision)); arch_patch_id * predecessor = arch_patch_ancestor (talloc_context, patch_id, 0); if (predecessor) { if (arch_project_tree_has_patch (tree, arch_patch_id_patch_id (predecessor))) rel_add_records (out, rel_make_record (arch_patch_id_patch_id (predecessor), 0), 0); } /* if the patch ancestry isn't available, we definately cannot build a * reference tree, so we just don't record it as reachable */ talloc_free (patch_id); } Catch (e) { /* dont mask critical errors */ if (e->code < 0) Throw (e); talloc_free (e); } } else /* tree ancestry is sufficient for our purposes */ { t_uchar *predecessor = assoc_ref (tree->local_ancestry, fqrevision); if (predecessor) { if (str_length (predecessor) && str_cmp (predecessor, ARCH_ANCESTRY_PATCH_PARTIAL)) rel_add_records (out, rel_make_record (predecessor, 0), 0); } else { predecessor = arch_highest_patch_level_before (tree, fqrevision); if (predecessor) rel_add_records (out, rel_make_record (predecessor, 0), 0); lim_free (0, predecessor); } } } free_assoc_table (headers); rel_free_table (new_patches); lim_free (0, log); } /* FIXME: this should be a simply adjacency list djikstra implementation. */ /* topologically sort patches according to ancestry */ static rel_table arch_topological_sort (struct arch_common_ancestor_search *search, rel_table fqrevisions, arch_project_tree_t *tree, int trace) { /* generate predecessors */ /* col 0 is the node. cols beyond that are the reachables */ rel_table graph = NULL; rel_table answer = NULL; assoc_table found = 0; int found_count = 0; int loop; for (loop=0; loop < rel_n_records (fqrevisions); ++loop) { rel_table predecessors = 0; int fields; int graph_row; rel_add_records (&graph, rel_make_record (fqrevisions[loop][0], 0), 0); arch_included_from_patch (search, &predecessors, tree, fqrevisions[loop][0]); graph_row = rel_n_records (graph) -1; for (fields=0; fields < rel_n_records (predecessors); ++fields) { rel_add_field (&graph[graph_row], predecessors[fields][0]); } rel_free_table (predecessors); } if (trace) { safe_printfmt (2, "brute force adjacency searching in table\n"); rel_print_table (2, graph); } while (found_count < rel_n_records (graph)) { assoc_table links = 0; for (loop = 0; loop < rel_n_records (graph); ++ loop) /* have we done this one already ? */ if (!assoc_ref (found, graph[loop][0])) { int field; for (field = 1; field < rel_n_fields (graph[loop]); ++field) { //safe_printfmt(2, "%s->%s\n", graph[loop][0],graph[loop][field]); assoc_set (&links, graph[loop][field],graph[loop][0]); } } for (loop=0; loop < rel_n_records (graph); ++loop) { //safe_printfmt(2, "checking %s\n", graph[loop][0]); if (!assoc_ref (links, graph[loop][0]) && !assoc_ref (found, graph[loop][0])) { //safe_printfmt(2, "unreachable %s\n", graph[loop][0]); /* unreachable */ ++found_count; rel_add_records (&answer, rel_make_record (graph[loop][0], 0), 0); assoc_set (&found, graph[loop][0], graph[loop][0]); } //else // safe_printfmt(2, "reachable %s\n", graph[loop][0]); } free_assoc_table (links); } free_assoc_table (found); //safe_printfmt (2, "sorted table\n"); //rel_print_table (2, answer); //safe_printfmt (2, "============\n"); rel_free_table (graph); return answer; } /* add the new patches reachable from fqrevision to out, * IF log is a continuation, ONLY the logs id itself is added. * that is, if A-patch-1 merges B patch-1 and 2, and B-2 is not * a continuation, out will have B-2 and B-1 appended. */ /* FIXME: patches in the line of ancestry for search, should not be reachable by any patch * other than its successor in the search line of ancestry. * i.e. A0 A1 A2 * \-B1 B2 -/ * (B1 from A-0, B2 merges in A1, and A2 merges in B2, reachable from B2 should list B1 but not * A1 even though A1 is a 'new patch' */ static void arch_common_ancestor_search_reachable_from_patch (struct arch_common_ancestor_search * search, arch_project_tree_t *tree, t_uchar * fqrevision) { rel_table reachable = 0; if (search->trace) safe_printfmt (2, "Adding to todo queue as reachable from %s:\n", fqrevision); arch_included_from_patch (search, &reachable, tree, fqrevision); if (search->trace) { int loop; for (loop=0; loop < rel_n_records (reachable); ++loop) safe_printfmt (2, "%s\n", reachable[loop][0]); } rel_append_x (&search->todo, reachable); rel_free_table (reachable); } /* add the new patches reachable from fqrevision in tree tree_root, * to out. This means * * All patches not present in the archive copy of the tree * * Patches that are ancestors or the named fqrevision in the tree, * as per arch_common_ancestor_search_reachable_from_patch. */ static void arch_reachable_from_tree (struct arch_common_ancestor_search *search, arch_project_tree_t *tree, t_uchar * fqrevision, t_uchar * tmp_dir, arch_project_tree_t * cache) { t_uchar * reference_tree_root; rel_table reachable = 0; rel_table new_in_tree; if (search->trace) safe_printfmt (2, "Adding to todo queue as reachable (from tree) %s:\n", fqrevision); /* patches in the tree from fqrevision */ arch_included_from_patch (search, &reachable, tree, fqrevision); reference_tree_root = arch_find_or_make_tmp_local_copy (-1, tmp_dir, tree, cache, 0, tree->archive, tree->revision); new_in_tree = arch_new_in_tree (tree, reference_tree_root); /* I'm sure there is a function to do this trivially */ { int loop; for (loop=0; loop < rel_n_records (new_in_tree); ++loop) { rel_add_records(&reachable, rel_make_record (new_in_tree[loop][0], 0), 0); } } if (search->trace) { int loop; for (loop=0; loop < rel_n_records (reachable); ++loop) safe_printfmt (2, "%s\n", reachable[loop][0]); } rel_append_x (&search->todo, reachable); rel_free_table (reachable); rel_free_table (new_in_tree); /* tmp_dir will be rmed by the caller */ lim_free (0, reference_tree_root); } static int arch_visited (assoc_table *visited, t_uchar * fqrevision, int trace) { t_uchar *exists = assoc_ref (*visited, fqrevision); int result = exists != NULL; if (!result) { if (trace) safe_printfmt (2, "%s visited\n", fqrevision); assoc_set (visited, fqrevision, fqrevision); } return result; } static void arch_common_ancestor_search_init(struct arch_common_ancestor_search *search) { search->todo = 0; search->trace = 0; search->reached = 0; search->pruned = NULL; } static void arch_common_ancestor_search_finalise(struct arch_common_ancestor_search *search) { rel_free_table (search->todo); free_assoc_table (search->reached); rel_free_table (search->pruned); } static void arch_common_ancestor_search_reach (struct arch_common_ancestor_search *search, arch_project_tree_t *search_tree, int trace) { rel_table current=search->todo; int position; rel_table filtered = 0; search->todo = NULL; for (position=0; position < rel_n_records (current); ++position) { if (!assoc_ref (search->reached, current[position][0])) { arch_visited (&search->reached, current[position][0], trace); rel_add_records (&filtered, rel_copy_record (current[position]), 0); } else if (trace) { safe_printfmt (2, "Skipping %s (already reached on this side)\n", current[position][0]); } } for (position=0; position < rel_n_records (filtered); ++position) { arch_common_ancestor_search_reachable_from_patch (search, search_tree, filtered[position][0]); } rel_free_table (current); rel_free_table (filtered); } typedef struct arch_common_ancestor_state { int trace; struct arch_common_ancestor_search from; struct arch_common_ancestor_search to; } common_ancestor; static t_uchar * arch_project_tree_patch_ancestor (arch_project_tree_t *tree, t_uchar const *patch_id) { t_uchar * log; assoc_table headers = 0; t_uchar * new_patches_header; rel_table new_patches = 0; t_uchar * continuation_header; t_uchar *result; result = assoc_ref (tree->local_ancestry, patch_id); if (result) { if (str_length (result)) return str_save (0, result); else return NULL; } log = arch_project_tree_patch_log (tree, patch_id); if (!log) { assoc_set (&tree->local_ancestry, patch_id, ARCH_ANCESTRY_PATCH_PARTIAL); return str_save (0, ARCH_ANCESTRY_PATCH_PARTIAL); } arch_parse_log (0, &headers, 0, log); new_patches_header = assoc_ref (headers, "new-patches"); continuation_header = assoc_ref (headers, "continuation-of"); if (continuation_header) { result = str_save (0, continuation_header); } else /* namespace predecessor - may be wrong in double-import cases * or if their is a sealed version and any non sealed patch levels * were removed */ { /* add the patch ancestor predecessor */ if (!str_cmp("-0", patch_id + str_length(patch_id) - 2)) { /* version-0, cannot determine the ancestor from the tree */ struct exception * e; Try { arch_patch_id * patch_id_ = talloc_steal (talloc_context, arch_patch_id_new (patch_id)); arch_patch_id * patch_result = arch_patch_ancestor (talloc_context, patch_id_, 0); if (patch_result) result = str_save (0, arch_patch_id_patch_id (patch_result)); else result = NULL; } Catch (e) { /* dont mask critical errors */ if (e->code < 0) Throw (e); talloc_free (e); result = str_save (0, ARCH_ANCESTRY_PATCH_PARTIAL); } } else /* tree ancestry is sufficient for our purposes */ result = arch_highest_patch_level_before (tree, patch_id); } if (!result) assoc_set (&tree->local_ancestry, patch_id, ""); else assoc_set (&tree->local_ancestry, patch_id, result); free_assoc_table (headers); rel_free_table (new_patches); lim_free (0, log); return result; } /* determine the ancestry of patch_id that is distinct from the ancestry for tree) */ static rel_table arch_ancestry_unique_to_branch (t_uchar * patch_id, arch_project_tree_t *tree) { assoc_table base_ancestry = rel_to_assoc (arch_project_tree_ancestry(tree), 0, 0); t_uchar *current = str_save (0, patch_id); rel_table result = NULL; while (1) { /* may hit the archive, thats ok */ t_uchar * next = NULL; struct exception * e; Try { arch_patch_id * patch_id = talloc_steal (talloc_context, arch_patch_id_new (current)); arch_patch_id * patch_result = arch_patch_ancestor (talloc_context, patch_id, 0); if (patch_result) next = str_save (0, arch_patch_id_patch_id (patch_result)); else next = NULL; } Catch (e) { /* dont mask critical errors */ if (e->code < 0) Throw (e); talloc_free (e); /* no ancestor from ancestry, use local cache */ next = arch_project_tree_patch_ancestor (tree, current); } if (!str_cmp (next, ARCH_ANCESTRY_PATCH_PARTIAL)) /* still couldn't */ { rel_add_records (&result, rel_make_record (ARCH_ANCESTRY_PATCH_PARTIAL, 0), 0); lim_free (0, next); break; } if (!next) /* end of the branch - unrelated trees ? */ break; /* ok have an ancestor */ rel_add_records (&result, rel_make_record (next, 0), 0); current = str_replace (current, next); if (assoc_ref (base_ancestry, next)) /* we've met the branch */ break; } lim_free (0, current); free_assoc_table (base_ancestry); return result; } /* determine if patch_id is present in tree via a cherry pick or a merge operation */ static int arch_patch_is_cherrypicked (t_uchar * patch_id, arch_project_tree_t *tree, int trace) { int position; rel_table unchecked; int result = 0; if (trace) safe_printfmt (2, "Checking for cherrypicked patch %s in tree %s\n", patch_id, tree->root); /* this was cherry picked if its ancestor, or any of its ancestors up to the point it was * import / branched from tree are missing. */ unchecked = arch_ancestry_unique_to_branch (patch_id, tree); for (position=0; position < rel_n_records (unchecked); ++position) { if (!str_cmp (unchecked[position][0], ARCH_ANCESTRY_PATCH_PARTIAL)) { if (trace) safe_printfmt (2, "full ancestry to the branch was not available from tree %s\n", tree->root); result = -1; } else if (!arch_project_tree_has_patch (tree, unchecked[position][0])) { if (trace) safe_printfmt (2, "missing patch %s from tree %s\n", unchecked[position][0], tree->root); result = -1; } } rel_free_table (unchecked); return result; } static rel_table arch_ancestry_cherrypicked (rel_table patches, arch_project_tree_t *tree, int trace) { rel_table answer = NULL; int position; for (position = 0; position < rel_n_records (patches); ++position) { if (arch_patch_is_cherrypicked (patches[position][0], tree, trace)) rel_add_records (&answer, rel_copy_record (patches[position]), 0); } return answer; } /* rel_remove_common: remove all elements of other from me */ static void rel_remove_common (rel_table *me, rel_table other) { rel_table result = rel_set_subtract (*me, other); rel_free_table (*me); *me = result; } /* Find the nearest common ancestor from two revisions. * NOTE: until --reference is removed from the star-merge interfance, to_version and to_archive MAY DIFFER * from to_tree->version and to_tree->archive. * from_tree is presumed to be a reference tree, with no local changes */ static t_uchar * arch_nearest_common_ancestor (arch_project_tree_t * from_tree, t_uchar * from_archive, t_uchar * from_revision, arch_project_tree_t * to_tree, t_uchar * to_archive, t_uchar * to_version, int trace, t_uchar *tmp_dir, arch_project_tree_t * cache) { common_ancestor state; t_uchar * from_fqrevision = 0; t_uchar * to_fqrevision = 0; rel_table intersection = 0; t_uchar * result = NULL; arch_common_ancestor_search_init (&state.from); arch_common_ancestor_search_init (&state.to); /* TODO refactor this */ state.trace = trace; state.from.trace = trace; state.to.trace = trace; { t_uchar * patch_level = arch_highest_patch_level (to_tree->root, to_archive, to_version); to_fqrevision = str_alloc_cat_many (0, to_archive, "/", to_version, "--", patch_level, str_end); lim_free (0, patch_level); } from_fqrevision = arch_fully_qualify (from_archive, from_revision); /* FIXME pass in the chatter_fd */ safe_printfmt (1, "* Searching for best merge point: "); /* from_tree is always a pristine */ arch_common_ancestor_search_reachable_from_patch (&state.from, from_tree, from_fqrevision); arch_reachable_from_tree (&state.to, to_tree, to_fqrevision, tmp_dir, cache); arch_visited (&state.from.reached, from_fqrevision, trace); arch_visited (&state.to.reached, to_fqrevision, trace); while (!result && ( rel_n_records (state.to.todo) || rel_n_records (state.from.todo))) { if (state.trace) safe_printfmt (2, "Checking next reachable nodes\n"); safe_printfmt (1, "."); safe_flush (1); arch_common_ancestor_search_reach (&state.from, from_tree, state.trace); arch_common_ancestor_search_reach (&state.to, to_tree, state.trace); intersection = assoc_intersection (state.from.reached, state.to.reached); if (!intersection) { arch_common_ancestor_search_prune_warning(&state.from, from_tree->root); arch_common_ancestor_search_prune_warning(&state.to,to_tree->root); } if (intersection) { rel_table from_cherries; rel_table to_cherries; if (state.trace) { safe_printfmt (2, "Found intersection:\n"); rel_print_table (2, intersection); } /* Note: Bad O here: intersection is fully regenerated every time.. * that said, long cherry picked trees are uncommon AFAICT. Robert Collins 20050118 */ from_cherries = arch_ancestry_cherrypicked (intersection, from_tree, trace); to_cherries = arch_ancestry_cherrypicked (intersection, to_tree, trace); rel_remove_common (&intersection, from_cherries); rel_remove_common (&intersection, to_cherries); { rel_table temp_table = arch_topological_sort (&state.from, intersection, from_tree, trace); rel_free_table (intersection); intersection = temp_table; } /* don't warn on pruned from this sort, they are irrelevant */ if (state.trace) { safe_printfmt (2, "Sorted intersection:\n"); rel_print_table (2, intersection); } if (rel_n_records (intersection)) result = str_save (0, intersection[0][0]); rel_free_table (intersection); intersection = NULL; rel_free_table (from_cherries); rel_free_table (to_cherries); } } lim_free (0, from_fqrevision); lim_free (0, to_fqrevision); arch_common_ancestor_search_finalise (&state.from); arch_common_ancestor_search_finalise (&state.to); safe_printfmt (1, " done\n"); return result; } /* * * star-merge FROM MY-TREE [MY-VERSION] * * from-patch-j is the latest rev from FROM's version in both my tree * and FROM * * from-patch-k is the latest rev in FROM merging * my-version-m into FROM where my-version-m * is present in MY-TREE. * * * if j > k * delta(from-patch-j,from-patch-k)[my-tree] * else * delta(my-version-m,from-patch-k)[my-tree] * fi */ int arch_star_merge (int chatter_fd, struct arch_archive * from_arch, t_uchar * from_archive, t_uchar * from_revision, arch_project_tree_t * to_tree, struct arch_archive * to_arch, t_uchar * to_archive, t_uchar * to_version, arch_project_tree_t * cache, t_uchar * changeset_output, int use_diff3, int forward, int escape_classes, int show_points_only, int graph_merge, int trace) { int answer = 2; t_uchar * tmp_dir = 0; t_uchar * changeset = 0; t_uchar * from_tree_root = 0; t_uchar * fq_orig_revision = 0; t_uchar * orig_archive = 0; t_uchar * orig_revision = 0; arch_project_tree_t * from_tree; arch_project_tree_t * orig_tree = NULL; tmp_dir = talloc_tmp_file_name (talloc_context, to_tree->root, ",,merge"); if (!changeset_output) changeset = file_name_in_vicinity (0, tmp_dir, ",,changeset"); else changeset = str_save (0, changeset_output); rmrf_file (tmp_dir); safe_mkdir (tmp_dir, 0777); from_tree_root = arch_find_or_make_tmp_local_copy (chatter_fd, tmp_dir, to_tree, cache, from_arch, from_archive, from_revision); from_tree = arch_project_tree_new (talloc_context, from_tree_root); if (graph_merge) fq_orig_revision = arch_nearest_common_ancestor (from_tree, from_archive, from_revision, to_tree, to_archive, to_version, trace, tmp_dir, cache); else fq_orig_revision = arch_nearest_star_ancestor (from_tree, from_archive, from_revision, to_tree, to_archive, to_version, trace); if (!fq_orig_revision) { safe_printfmt (2, "merge: unable to merge unrelated trees.\n"); answer = 2; } else { t_uchar * orig_tree_root = 0; struct arch_make_changeset_report make_report = {0, }; struct arch_apply_changeset_report * apply_report = NULL; assoc_table inode_shortcut = 0; if (show_points_only) { arch_chatter (chatter_fd, "* merge points are:\n"); arch_chatter (chatter_fd, "base: %s\n", fq_orig_revision); arch_chatter (chatter_fd, "other: %s/%s\n", from_archive, from_revision); arch_chatter (chatter_fd, "this: %s\n", to_tree->fqrevision); return 0; } arch_chatter (chatter_fd, "* merge by delta(%s,%s/%s)[%s]\n", fq_orig_revision, from_archive, from_revision, to_tree->root); { t_uchar *fq_from = str_alloc_cat_many (0, from_archive,"/", from_revision, str_end); int empty = !str_cmp (fq_from, fq_orig_revision); lim_free (0, fq_from); if (empty) { arch_chatter (chatter_fd, "* skipping (empty delta)\n"); answer = 0; goto star_merge_cleanup; } } orig_archive = arch_parse_package_name (arch_ret_archive, 0, fq_orig_revision); orig_revision = arch_parse_package_name (arch_ret_non_archive, 0, fq_orig_revision); orig_tree_root = arch_find_or_make_tmp_local_copy (chatter_fd, tmp_dir, to_tree, cache, (str_cmp (orig_archive, from_archive) ? 0 : from_arch), orig_archive, orig_revision); orig_tree = arch_project_tree_new (talloc_context, orig_tree_root); if (changeset_output) { make_report.callback = star_merge_callback; make_report.thunk = (void *)(long)chatter_fd; } if (use_diff3) { arch_chatter (chatter_fd, "* performing merge\n"); answer = arch_merge3 (chatter_fd, to_tree, orig_tree, from_tree, escape_classes, 1); lim_free (0, orig_tree_root); goto star_merge_cleanup; } arch_read_inode_sig_ids (0, &inode_shortcut, from_tree_root, orig_archive, orig_revision); arch_make_changeset (&make_report, orig_tree, from_tree, changeset, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, inode_shortcut, 0, escape_classes); if (changeset_output) answer = 0; else { assoc_table older_files_table = 0; assoc_table yours_files_table = 0; arch_chatter (chatter_fd, "* applying changeset\n"); safe_flush (chatter_fd); apply_report = arch_apply_changeset (changeset, talloc_context, to_tree, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, forward, escape_classes, star_merge_callback, (void *)(long)chatter_fd); if (arch_conflicts_occurred (apply_report)) answer = 1; else answer = 0; free_assoc_table (older_files_table); free_assoc_table (yours_files_table); } arch_free_make_changeset_report_data (&make_report); talloc_free (apply_report); lim_free (0, orig_tree_root); free_assoc_table (inode_shortcut); } star_merge_cleanup: rmrf_file (tmp_dir); talloc_free (tmp_dir); lim_free (0, from_tree_root); lim_free (0, fq_orig_revision); lim_free (0, orig_archive); lim_free (0, orig_revision); lim_free (0, changeset); arch_project_tree_delete (from_tree); arch_project_tree_delete (orig_tree); return answer; } static void star_merge_callback (void * vfd, char * fmt, va_list ap) { int fd; fd = (int)(t_ulong)vfd; if (fd >= 0) { safe_printfmt_va_list (fd, fmt, ap); safe_flush (fd); } } /* tag: Tom Lord Sat Jun 28 16:40:26 2003 (star-merge.c) */