Logo Search packages:      
Sourcecode: bazaar version File versions

merge.c

/* 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/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 "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;
}

static t_uchar *
log_text (t_uchar * tree_root, t_uchar const * fqrevision)
{
    t_uchar *archive=arch_parse_package_name (arch_ret_archive, 0, fqrevision);
    t_uchar *version=arch_parse_package_name (arch_ret_package_version, 0, fqrevision);
    rel_table source_logs = arch_logs (tree_root, archive, version, 1);
    t_uchar * log_text = 0;
    int x;

    for (x = 0; x < rel_n_records (source_logs); ++x)
      if (!str_cmp (fqrevision, source_logs[x][0]))
        {
          log_text = file_contents (source_logs[x][1]);
          break;
        }

    lim_free (0, archive);
    lim_free (0, version);
    rel_free_table (source_logs);
    return log_text;
}

/* 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, t_uchar *tree_root, t_uchar * fqrevision)
{
  t_uchar * log = log_text (tree_root, 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, tree_root, 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], tree_root, 0), 0);

      /* add the namespace predecessor */
      {
        t_uchar * predecessor = arch_highest_patch_level_before (tree_root, fqrevision);
        if (predecessor)
            rel_add_records (out, rel_make_record (predecessor, tree_root, 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, t_uchar *tree_root, int trace)
{
    /* generate predecessors */
    /* col 0 is the node. cols beyond that are the reachables */
    rel_table graph = 0;
    rel_table answer = 0;
    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_root, 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], tree_root, 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");

    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, t_uchar *tree_root, 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_root, 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, t_uchar *cache_dir)
{
  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->root, fqrevision);
  reference_tree_root = arch_find_or_make_tmp_local_copy (-1, tmp_dir, 0, cache_dir, 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], tree->root, 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, 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, filtered[position][1], 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 = log_text (tree->root, patch_id);
  assoc_table headers = 0;
  t_uchar * new_patches_header;
  rel_table new_patches = 0;
  t_uchar * continuation_header;
  t_uchar *result;

  if (!log)
    {
      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
     */
    {
      t_uchar * predecessor = arch_highest_patch_level_before (tree->root, patch_id);
      if (predecessor)
          result = str_save (0, predecessor);
      else
          result = NULL;
      lim_free (0, predecessor);
    }
  free_assoc_table (headers);
  rel_free_table (new_patches);
  lim_free (0, log);
  return result;
}

/* find value in the set set 
 * currently does not depend on any sort order
 * but maybe somewhat inefficient 
 */
static int
rel_set_find (rel_table set, t_uchar const *value)
{
    int position;
    for (position = 0; position < rel_n_records (set); ++position)
        if (!str_cmp (set[position][0], value))
            return -1;
    return 0;
}

/* 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)
{
    rel_table base_ancestry = arch_project_tree_ancestry (tree);
    t_uchar *current = str_save (0, patch_id);
    rel_table result = NULL;
    while (1)
      {
        /* may hit the archive, thats ok */
        t_uchar *next = arch_patch_ancestor (current, 1, 0);
        if (!str_cmp (next, ARCH_ANCESTRY_PATCH_PARTIAL))
          /* couldn't find it */
          {
            lim_free (0, next);
            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);
        lim_free (0, current);
        current = next;
        if (rel_set_find (base_ancestry, next))
            /* we've met the branch */
            break;
      }
    lim_free (0, current);
    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 (!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;
          }
      }
    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, t_uchar *cache_dir)
{
  common_ancestor state;
  t_uchar * from_fqrevision = 0;
  t_uchar * to_fqrevision = 0;
  rel_table intersection = 0;
  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 * result = NULL;

    {
      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\n");
  
  /* from_tree is always a pristine */
  arch_common_ancestor_search_reachable_from_patch (&state.from, from_tree->root, from_fqrevision);
  arch_reachable_from_tree (&state.to, to_tree, to_fqrevision, tmp_dir, cache_dir);
  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, ".");
      
      arch_common_ancestor_search_reach (&state.from, state.trace);
      arch_common_ancestor_search_reach (&state.to, 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);
          
          intersection = arch_topological_sort (&state.from, intersection, from_tree->root, trace);
          /* 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);
        }
    }
  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,
                 t_uchar * to_tree_root, struct arch_archive * to_arch, t_uchar * to_archive, t_uchar * to_version,
                 t_uchar * cache_dir, 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 to_tree;

  arch_project_tree_init (&to_tree, to_tree_root);

  tmp_dir = tmp_file_name (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_root, cache_dir, from_arch, from_archive, from_revision);
  arch_project_tree_init (&from_tree, 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_dir);
  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 = {0, };
      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_root, cache_dir, (str_cmp (orig_archive, from_archive) ? 0 : from_arch), orig_archive, orig_revision);

      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_root, orig_tree_root, from_tree_root, escape_classes, 1);
        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_root, from_tree_root, 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;

          apply_report.callback = star_merge_callback;
          apply_report.thunk = (void *)(long)chatter_fd;

          arch_chatter (chatter_fd, "* applying changeset\n");
          safe_flush (chatter_fd);
          arch_apply_changeset (&apply_report, changeset, to_tree_root, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, forward, escape_classes);

          if (arch_conflicts_occured (&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);
      arch_free_apply_changeset_report_data (&apply_report);

      lim_free (0, orig_tree_root);
      free_assoc_table (inode_shortcut);
    }

  star_merge_cleanup:
  rmrf_file (tmp_dir);

  lim_free (0, tmp_dir);
  lim_free (0, from_tree_root);
  lim_free (0, fq_orig_revision);
  lim_free (0, orig_archive);
  lim_free (0, orig_revision);
  arch_project_tree_finalise (&to_tree);
  arch_project_tree_finalise (&from_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)
 */

Generated by  Doxygen 1.6.0   Back to index