/* 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 <robert.collins@canonical.com>
 *
 * 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)
 */


syntax highlighted by Code2HTML, v. 0.9.1