/* commit.c:
 *
 ****************************************************************
 * Copyright (C) 2003 Tom Lord
 *
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/bugs/panic.h"
#include "hackerlab/os/time.h"
#include "hackerlab/char/str.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/vu/safe.h"
#include "tla/libdate/date-string.h"
#include "tla/libfsutils/string-files.h"
#include "tla/libfsutils/copy-file.h"
#include "tla/libfsutils/tmp-files.h"
#include "tla/libfsutils/rmrf.h"
#include "tla/libarch/diffs.h"
#include "tla/libarch/patch-logs.h"
#include "tla/libarch/invent.h"
#include "tla/libarch/my.h"
#include "tla/libarch/hooks.h"
#include "tla/libarch/namespace.h"
#include "tla/libarch/pristines.h"
#include "tla/libarch/project-tree.h"
#include "tla/libarch/make-changeset-files.h"
#include "tla/libarch/make-changeset.h"
#include "tla/libarch/changeset-report.h"
#include "tla/libarch/local-cache.h"
#include "tla/libarch/inv-ids.h"
#include "tla/libarch/changelogs.h"
#include "tla/libarch/apply-changeset.h"
#include "tla/libarch/inode-sig.h"
#include "tla/libarch/chatter.h"
#include "tla/libarch/commit.h"


/* __STDC__ prototypes for static functions */
static t_uchar * arch_prepare_commit_changeset (t_uchar ** changelog_loc_ret,
                                                int chatter_fd,
                                                t_uchar ** cooked_log_ret,
                                                t_uchar * tree_root,
                                                t_uchar * raw_log,
                                                struct arch_archive * arch,
                                                t_uchar * revision,
                                                t_uchar * prev_level,
                                                rel_table file_list,
                                                int escape_classes);
static rel_table pick_non_control (rel_table table);
static rel_table pick_patch_logs (rel_table table);
static void arch_commit_mid_commit (t_uchar * tree_root, t_uchar * cooked_log);
static void arch_finish_commit (int chatter_fd, t_uchar * tree_root, t_uchar * archive, t_uchar * prev_revision, t_uchar * revision, t_uchar * changeset, t_uchar * changelog_loc, int escape_classes);
static void arch_commit_failed (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar * changeset);
static void commit_make_changeset_callback (void * vfd, char * fmt, va_list ap);



void
arch_commit (int chatter_fd,
             struct arch_archive * arch,
             t_uchar * revision,
             t_uchar * tree_root,
             t_uchar * raw_log,
             rel_table file_list,
             int escape_classes)
{
  t_uchar * errstr;
  t_uchar * version = 0;
  t_uchar * this_level = 0;
  t_uchar * prev_revision = 0;
  t_uchar * prev_level = 0;
  t_uchar * changelog_loc = 0;
  t_uchar * changeset_path = 0;
  t_uchar * cooked_log = 0;
  t_uchar * my_uid = 0;
  t_uchar * txn_id = 0;
  int error;

  invariant (!!raw_log);

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  this_level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
  prev_revision = arch_previous_revision (arch, revision);
  prev_level = arch_parse_package_name (arch_ret_patch_level, 0, prev_revision);

  changeset_path = arch_prepare_commit_changeset (&changelog_loc, chatter_fd, &cooked_log, tree_root, raw_log, arch, revision, prev_level, file_list, escape_classes);

  invariant (!!prev_level);

  /* Check the error return code for the "precommit" hook and exit if non-zero.
   */
  error = arch_run_hook ("precommit", "ARCH_ARCHIVE", arch->name, "ARCH_REVISION", revision, "ARCH_TREE_ROOT", tree_root, 0) ;
  if (error)
    {
      safe_printfmt (2, "arch_commit: precommit hook function failed with error (%d)\n commit cancelled.\n", error);
      exit (2);
    }

  my_uid = arch_my_id_uid ();
  txn_id = arch_generate_txn_id ();

  if (arch_archive_lock_revision (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      safe_printfmt (2, "arch_commit: unable to acquire revision lock (%s)\n    tree: %s\n    revision: %s/%s\n",
                     errstr, tree_root, arch->name, revision);
      exit (2);
    }

  if (arch_archive_put_log (&errstr, arch, version, prev_level, my_uid, txn_id, cooked_log))
    {
      safe_printfmt (2, "arch_commit: unable to send log message to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
                     errstr, tree_root, arch->name, version);
      exit (2);
    }

  if (arch_archive_put_changeset (&errstr, arch, version, prev_level, my_uid, txn_id, this_level, changeset_path))
    {
      safe_printfmt (2, "arch_commit: unable to send changeset to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
                     errstr, tree_root, arch->name, revision);
      exit (2);
    }

  if (arch_revision_ready (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      safe_printfmt (2, "arch_commit: error sending revision to archive (%s)\n    tree: %s\n    revision: %s/%s\n",
                     errstr, tree_root, arch->name, revision);
      exit (2);
    }


  arch_commit_mid_commit (tree_root, cooked_log);

  if (arch_archive_finish_revision (&errstr, arch, version, prev_level, my_uid, txn_id, this_level))
    {
      arch_commit_failed (tree_root, arch->name, version, changeset_path);
      safe_printfmt (2, "arch_commit: unable to complete commit transaction (%s)\n    tree: %s\n    revision: %s/%s\n",
                     errstr, tree_root, arch->name, version);
      exit (2);
    }

  arch_finish_commit (chatter_fd, tree_root, arch->name, prev_revision, revision, changeset_path, changelog_loc, escape_classes);

  if (rel_n_records (file_list) == 0) 
    arch_snap_inode_sig (tree_root, arch->name, revision);
  else
  {
    arch_snap_inode_sig_files(tree_root, arch->name, prev_revision, revision, file_list);
  }

  arch_run_hook ("commit", "ARCH_ARCHIVE", arch->name, "ARCH_REVISION", revision, "ARCH_TREE_ROOT", tree_root, 0);

  lim_free (0, version);
  lim_free (0, this_level);
  lim_free (0, prev_revision);
  lim_free (0, prev_level);
  lim_free (0, changeset_path);
  lim_free (0, cooked_log);
  lim_free (0, changelog_loc);
  lim_free (0, my_uid);
  lim_free (0, txn_id);
}




static t_uchar *
arch_prepare_commit_changeset (t_uchar ** changelog_loc_ret,
                               int chatter_fd,
                               t_uchar ** cooked_log_ret,
                               t_uchar * tree_root,
                               t_uchar * raw_log,
                               struct arch_archive * arch,
                               t_uchar * revision,
                               t_uchar * prev_level,
                               rel_table file_list,
                               int escape_classes)
{
  t_uchar * archive = arch->name;
  t_uchar * tmp_stem = 0;
  t_uchar * tmp_path = 0;
  t_uchar * changeset_basename = 0;
  t_uchar * changeset_path = 0; /* return value */
  t_uchar * version = 0;
  t_uchar * level = 0;
  t_uchar * prev_revision = 0;
  t_uchar * prev_rev_path = 0;
  struct arch_make_changeset_report make_report = {0, }; /* should have a chatter callback here */
  t_uchar * changelog_id_suffix = 0;
  t_uchar * changelog_x_id = 0;
  t_uchar * changelog_i_id = 0;
  t_uchar * changelog_id = 0;
  t_uchar * changelog_orig_loc = 0;
  t_uchar * changelog_mod_loc = 0;
  struct arch_changeset_report csr = {0, };
  int changelog_diffs_fd = -1;
  int changelog_add_fd = -1;
  t_uchar * new_log_id = 0;
  t_uchar * new_log_loc = 0;
  int new_log_fd = -1;
  t_uchar * new_log_path = 0;
  t_uchar * cooked_log = 0;
  t_uchar * new_changelog_path = 0;



  /****************************************************************
   * double check that we were handed a valid log message, if any
   */
  invariant (arch_valid_log_file (raw_log));


  /****************************************************************
   * make a temp dir for the changeset
   */
  tmp_stem = str_alloc_cat_many (0, ",,commit.", revision, "--", archive, str_end);
  tmp_path = tmp_file_name (tree_root, tmp_stem);

  rmrf_file (tmp_path);
  safe_mkdir (tmp_path, 0777);

  changeset_basename = str_alloc_cat (0, revision, ".patches");
  changeset_path = file_name_in_vicinity (0, tmp_path, changeset_basename);


  /****************************************************************
   * Compute the raw changeset
   *
   * The changeset computed here does _not_ include an add of the new log
   * message and does _not_ include updates to automatic changelogs.
   */

  version = arch_parse_package_name (arch_ret_package_version, 0, revision);
  level = arch_parse_package_name (arch_ret_patch_level, 0, revision);
  prev_revision = str_alloc_cat_many (0, version, "--", prev_level, str_end);
  prev_rev_path = arch_find_or_make_local_copy (chatter_fd, tree_root, 0, arch, archive, prev_revision);

  if (chatter_fd >= 0)
    {
      make_report.callback = commit_make_changeset_callback;
      make_report.thunk = (void *)(long)chatter_fd;
    }

  if (!file_list)
    {
      assoc_table inode_shortcut = 0;

      arch_read_inode_sig (0, &inode_shortcut, tree_root, archive, prev_revision);

      arch_make_changeset (&make_report, prev_rev_path, tree_root, changeset_path, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, inode_shortcut, 0, escape_classes);

      free_assoc_table (inode_shortcut);
    }
  else
    arch_make_files_changeset (&make_report, changeset_path, file_list, prev_rev_path, tree_root, arch_unspecified_id_tagging, arch_inventory_unrecognized, escape_classes);

  /****************************************************************
   * Look for a Changelog for the version we're committing to.
   */

  changelog_id_suffix = str_alloc_cat_many (0, "_automatic-ChangeLog--", archive, "/", version, str_end);
  changelog_x_id = str_alloc_cat (0, "x", changelog_id_suffix);
  changelog_i_id = str_alloc_cat (0, "i", changelog_id_suffix);

  changelog_mod_loc = assoc_ref (make_report.mod_file_loc_of, changelog_x_id);
  if (changelog_mod_loc)
    {
      changelog_mod_loc = str_save (0, changelog_mod_loc);
      changelog_id = str_save (0, changelog_x_id);
    }
  else
    {
      changelog_mod_loc = assoc_ref (make_report.mod_file_loc_of, changelog_i_id);
      if (changelog_mod_loc)
        {
          changelog_mod_loc = str_save (0, changelog_mod_loc);
          changelog_id = str_save (0, changelog_i_id);
        }
    }

  if (changelog_mod_loc)
    {
      if (changelog_loc_ret)
        {
          *changelog_loc_ret = str_save (0, changelog_mod_loc);
        }

      changelog_orig_loc = assoc_ref (make_report.orig_file_loc_of, changelog_id);
      if (changelog_orig_loc)
        changelog_orig_loc = str_save (0, changelog_orig_loc);
    }

  /* post-condition:
   *
   * changelog_id, changelog_mod_loc: 0 if no MOD tree changelog
   *                                        set if there is a changelog
   *
   * changelog_orig_loc: 0 if no MOD or no ORIG changelog, set if both
   */


  /****************************************************************
   * Get a report about this changeset
   */

  arch_evaluate_changeset (&csr, changeset_path);


  /****************************************************************
   * Update the changeset indexes to reflect the patch log and changelog
   *
   * The changelog for this version, if any, is going to change
   * as a result of the new patch log for this commit.
   * Usually (though we don't count on it) it didn't change between
   * pristine and project tree.
   *
   * So, we have to touch up the changeset to reflect a diff to that
   * changelog.  At this stage, only the file and dir indexes are
   * modified to reflect that (we haven't generated the new log entry
   * yet, so we can't produce the actual changelog diffs).
   *
   * Also, we know we're adding a new patch log, so the indexes should
   * reflect that, too.
   *
   * The reason to touch up the indexes early is that the patch log
   * is derived, in part, from those indexes.
   */

  if (changelog_mod_loc && changelog_orig_loc)
    {
      changelog_diffs_fd = arch_changeset_add_diffs (&csr, &make_report, changeset_path, changelog_orig_loc, changelog_mod_loc, changelog_id);
    }
  else if (changelog_mod_loc)
    {
      changelog_add_fd = arch_changeset_add_file (&new_changelog_path, &csr, &make_report, changeset_path, changelog_mod_loc, changelog_id);
    }

  new_log_loc = arch_log_file (".", archive, revision);
  new_log_id = arch_log_file_id (archive, revision);
  new_log_fd = arch_changeset_add_file (&new_log_path, &csr, &make_report, changeset_path, new_log_loc, new_log_id);

  arch_changeset_rewrite_indexes (changeset_path, &csr);


  /****************************************************************
   * Generate the Cooked Log Entry
   */
  {
    t_uchar * my_id = 0;
    time_t now;
    t_uchar * std_date = 0;
    t_uchar * human_date = 0;
    int log_fd;

    my_id = arch_my_id ();
    now = time (0);
    std_date = standard_date (now);
    human_date = pretty_date (now);

    log_fd = make_output_to_string_fd ();

    safe_printfmt (log_fd, "Revision: %s\n", revision);
    safe_printfmt (log_fd, "Archive: %s\n", archive);
    safe_printfmt (log_fd, "Creator: %s\n", my_id);
    safe_printfmt (log_fd, "Date: %s\n", human_date);
    safe_printfmt (log_fd, "Standard-date: %s\n", std_date);

    /********************************
     * automatic headers for various file and patch lists
     */

    {
      rel_table new_files = 0;
      rel_table removed_files = 0;
      rel_table new_directories = 0;
      rel_table removed_directories = 0;
      rel_table modified_files = 0;
      rel_table new_patches = 0;
      rel_table removed_patches = 0;

      new_files = pick_non_control (csr.added_files);
      rel_append_x (&new_files, csr.added_symlinks);
      rel_sort_table_by_field (0, new_files, 0);

      removed_files = pick_non_control (csr.removed_files);
      rel_append_x (&removed_files, csr.removed_symlinks);
      rel_sort_table_by_field (0, removed_files, 0);

      new_directories = pick_non_control (csr.added_dirs);
      removed_directories = pick_non_control (csr.removed_dirs);

      modified_files = rel_copy_table (csr.patched_regular_files);
      rel_append_x (&modified_files, csr.patched_symlinks);
      rel_append_x (&modified_files, csr.patched_binaries);
      rel_append_x (&modified_files, csr.file_metadata_changed);
      rel_append_x (&modified_files, csr.symlink_to_file);
      rel_append_x (&modified_files, csr.file_to_symlink);
      rel_sort_table_by_field (0, modified_files, 0);
      rel_uniq_by_field (&modified_files, 0);

      new_patches = pick_patch_logs (csr.added_files);
      removed_patches = pick_patch_logs (csr.removed_files);

      arch_print_log_list_header (log_fd, "New-files", new_files, 0);
      arch_print_log_list_header (log_fd, "New-directories", new_directories, 0);
      arch_print_log_list_header (log_fd, "Removed-files", removed_files, 0);
      arch_print_log_list_header (log_fd, "Removed-directories", removed_directories, 0);
      arch_print_log_pairs_header (log_fd, "Renamed-files", csr.renamed_files, 0, 1);
      arch_print_log_pairs_header (log_fd, "Renamed-directories", csr.renamed_dirs, 0, 1);
      arch_print_log_list_header (log_fd, "Modified-files", modified_files, 0);
      arch_print_log_list_header (log_fd, "Modified-directories", csr.dir_metadata_changed, 0);
      arch_print_log_list_header (log_fd, "New-patches", new_patches, 0);
      arch_print_log_list_header (log_fd, "Removed-patches", removed_patches, 0);

      rel_free_table (new_files);
      rel_free_table (removed_files);
      rel_free_table (new_directories);
      rel_free_table (removed_directories);
      rel_free_table (modified_files);
      rel_free_table (new_patches);
      rel_free_table (removed_patches);
    }


    /********************************
     * copy the user headers, and copy or generate the log body
     */
    {
      t_uchar * eoh = raw_log;

      while (1)
        {
          eoh = str_chr_index (eoh, '\n');
          if (!eoh || (eoh[1] == '\n') || (!eoh[1]))
            break;
          else
            ++eoh;
        }

      if (eoh)
        {
          eoh = eoh + 1;
          safe_printfmt (log_fd, "%.*s", (int)(eoh - raw_log), raw_log);
        }

      if (eoh && *eoh)
        {
          safe_printfmt (log_fd, "%s", eoh);
        }
      else
        {
          safe_printfmt (log_fd, "\n\n");
        }
    }

    /********************************
     * oh... did i mention we were writing
     * the log to a string?
     */
    cooked_log = string_fd_close (log_fd);

    lim_free (0, my_id);
    lim_free (0, std_date);
    lim_free (0, human_date);
  }

  /****************************************************************
   * Write the log into the changeset
   */
  safe_printfmt (new_log_fd, "%s", cooked_log);
  safe_close (new_log_fd);


  /****************************************************************
   * Give a copy of the log to the caller.
   */
  if (cooked_log_ret)
    *cooked_log_ret = str_save (0, cooked_log);


  /****************************************************************
   * Generate the new changelog.
   */

  if (changelog_add_fd > 0)
    {
      invariant (changelog_add_fd >= 0);
      invariant (changelog_diffs_fd < 0);

      arch_generate_changelog (changelog_add_fd, tree_root, 0, 0, level, new_log_path, archive, version);
      /* new_changelog_path already set. */
      safe_close (changelog_add_fd);
    }
  else if (changelog_diffs_fd > 0)
    {
      int fd = -1;
      t_uchar * changelog_orig_path = 0;

      new_changelog_path = file_name_in_vicinity (0, changeset_path, ",,changelog");
      fd = safe_open (new_changelog_path, O_WRONLY | O_CREAT | O_EXCL, 0444);
      arch_generate_changelog (fd, tree_root, 0, 0, level, new_log_path, archive, version);
      safe_close (fd);

      changelog_orig_path = file_name_in_vicinity (0, prev_rev_path, changelog_orig_loc);

      invariant (changelog_diffs_fd >= 0);
      invariant (changelog_add_fd < 0);

      arch_invoke_diff (changelog_diffs_fd, changelog_orig_path, changelog_orig_loc, new_changelog_path, changelog_mod_loc, 0, 0);

      safe_close (changelog_diffs_fd);

      rmrf_file (new_changelog_path);

      lim_free (0, changelog_orig_path);
    }

  lim_free (0, tmp_stem);
  lim_free (0, tmp_path);
  lim_free (0, changeset_basename);
  lim_free (0, version);
  lim_free (0, level);
  lim_free (0, prev_revision);
  lim_free (0, prev_rev_path);
  arch_free_make_changeset_report_data (&make_report);
  lim_free (0, changelog_id_suffix);
  lim_free (0, changelog_x_id);
  lim_free (0, changelog_i_id);
  lim_free (0, changelog_id);
  lim_free (0, changelog_orig_loc);
  lim_free (0, changelog_mod_loc);
  arch_free_changeset_report_data (&csr);
  lim_free (0, new_log_id);
  lim_free (0, new_log_loc);
  lim_free (0, new_log_path);
  lim_free (0, cooked_log);
  lim_free (0, new_changelog_path);

  /****************************************************************
   * Give the user the path to the pristine tree for base-0.
   * It's up tot he caller to stash this in the archive.
   */
  return changeset_path;
}


static rel_table
pick_non_control (rel_table table)
{
  int x;
  rel_table answer = 0;

  for (x = 0; x < rel_n_records (table); ++x)
    {
      t_uchar * id;

      id = table[x][1];

      if (str_cmp_prefix ("A_", id))
        rel_add_records (&answer, rel_copy_record (table[x]), 0);
    }

  return answer;
}


static rel_table
pick_patch_logs (rel_table table)
{
  int x;
  rel_table answer = 0;

  for (x = 0; x < rel_n_records (table); ++x)
    {
      t_uchar * f = 0;
      t_uchar * t = 0;
      t_uchar * d = 0;
      t_uchar * level = 0;
      t_uchar * archive = 0;
      t_uchar * version = 0;
      t_uchar * revision = 0;
      t_uchar * fqrev = 0;

      f = str_save (0, table[x][0]);
      t = file_name_tail (0, f);
      d = file_name_directory_file (0, f);

      if (arch_valid_patch_level_name (t))
        {
          level = str_save (0, t);

          lim_free (0, f);
          f = d;
          t = file_name_tail (0, f);
          d = file_name_directory_file (0, f);

          if (!str_cmp ("patch-log", t))
            {
              lim_free (0, f);
              f = d;
              t = file_name_tail (0, f);
              d = file_name_directory_file (0, f);

              if (arch_valid_archive_name (t))
                {
                  archive = str_save (0, t);

                  lim_free (0, f);
                  f = d;
                  t = file_name_tail (0, f);
                  d = file_name_directory_file (0, f);

                  if (arch_valid_package_name (t, arch_no_archive, arch_req_version, 0))
                    {
                      version = str_save (0, t);

                      lim_free (0, f);
                      f = d;
                      t = file_name_tail (0, f);
                      d = file_name_directory_file (0, f);

                      if (arch_valid_package_name (t, arch_no_archive, arch_req_package, 0))
                        {
                          lim_free (0, f);
                          f = d;
                          t = file_name_tail (0, f);
                          d = file_name_directory_file (0, f);

                          if (arch_valid_package_name (t, arch_no_archive, arch_req_category, 0))
                            {
                              if (!str_cmp ("./{arch}", d))
                                {
                                  revision = str_alloc_cat_many (0, version, "--", level, str_end);
                                  fqrev = arch_fully_qualify (archive, revision);
                                  rel_add_records (&answer, rel_make_record (fqrev, table[x][1], table[x][2], 0), 0);
                                }
                            }
                        }

                    }
                }
            }
          lim_free (0, f);
          lim_free (0, t);
          lim_free (0, d);
          lim_free (0, level);
          lim_free (0, archive);
          lim_free (0, version);
          lim_free (0, revision);
          lim_free (0, fqrev);
        }
    }

  arch_sort_table_by_name_field (0, answer, 0);

  return answer;
}


static void
arch_commit_mid_commit (t_uchar * tree_root, t_uchar * cooked_log)
{
  arch_start_tree_commit (tree_root, cooked_log);
}


static void
arch_finish_commit (int chatter_fd, t_uchar * tree_root, t_uchar * archive, t_uchar * prev_revision, t_uchar * revision, t_uchar * changeset, t_uchar * changelog_loc, int escape_classes)
{
  t_uchar * dir = 0;
  t_uchar * dir_tail = 0;
  t_uchar * recycled_pristine_src = 0;
  t_uchar * recycled_pristine_tmp = 0;

  dir = file_name_directory_file (0, changeset);
  dir_tail = file_name_tail (0, dir);
  invariant (!str_cmp_prefix (",,", dir_tail));

  arch_finish_tree_commit (tree_root, archive, revision, changelog_loc);

  recycled_pristine_src = arch_find_pristine (0, tree_root, archive, prev_revision, arch_unlocked_pristine, arch_tree_pristine_search);
  if (recycled_pristine_src)
    {
      t_uchar * inventory_path = 0;
      struct arch_apply_changeset_report report = {0, };

      arch_chatter (chatter_fd, "* update pristine tree (%s/%s => %s)\n", archive, prev_revision, revision);

      recycled_pristine_tmp = tmp_file_name (dir, ",,pristine");
      safe_rename (recycled_pristine_src, recycled_pristine_tmp);

      inventory_path = file_name_in_vicinity (0, recycled_pristine_tmp, ",,index");
      rmrf_file (inventory_path);

      arch_apply_changeset (&report, changeset, recycled_pristine_tmp, arch_unspecified_id_tagging, arch_inventory_unrecognized, 0, 0, 0, 0, 0, escape_classes);
      invariant (!arch_conflicts_occured (&report));

      arch_install_pristine (tree_root, archive, revision, recycled_pristine_tmp);

      lim_free (0, inventory_path);
    }

  rmrf_file (dir);

  lim_free (0, dir);
  lim_free (0, dir_tail);
  lim_free (0, recycled_pristine_src);
  lim_free (0, recycled_pristine_tmp);
}


static void
arch_commit_failed (t_uchar * tree_root, t_uchar * archive, t_uchar * revision, t_uchar * changeset)
{
  t_uchar * dir = 0;
  t_uchar * dir_tail = 0;

  dir = file_name_directory_file (0, changeset);
  dir_tail = file_name_tail (0, dir);
  invariant (!str_cmp_prefix (",,", dir_tail));

  arch_abort_tree_commit (tree_root, archive, revision);


  rmrf_file (dir);

  lim_free (0, dir);
  lim_free (0, dir_tail);
}


static void
commit_make_changeset_callback (void * vfd, char * fmt, va_list ap)
{
  int fd;

  fd = (int)(t_ulong)vfd;
  safe_printfmt_va_list (fd, fmt, ap);
  safe_flush (1);
}






/* tag: Tom Lord Mon May 26 12:05:54 2003 (commit.c)
 */
