/* branch.cc
*
* vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2
****************************************************************
* Copyright (C) 2003 Tom Lord
* Portions (C) 2004 Canonical Ltd
* Author Robert Collins <robert.collins@canonical.com>
* Rob Weir <rob.weir@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/exception.h"
#include "hackerlab/cmd/main.h"
#include "hackerlab/fs/file-names.h"
#include "hackerlab/fs/cwd.h"
#include "hackerlab/os/errno.h"
#include "libfsutils/tmp-files.h"
#include "libfsutils/file-contents.h"
#include "libfsutils/rmrf.h"
#include "libarch/archive-setup.h"
#include "libarch/inv-ids.h"
#include "libarch/namespace.h"
#include "libarch/project-tree.h"
#include "libarch/patch-logs.h"
#include "libarch/archive.h"
#include "libarch/build-revision.h"
#include "libarch/proj-tree-lint.h"
#include "libarch/tag.h"
#include "libarch/pfs.h"
#include "libarch/conflict-handling.h"
#include "commands/cmd.h"
#include "commands/apply-delta.h"
#include "commands/branch.h"
#include "commands/cmdutils.h"
#include "commands/version.h"
static t_uchar * usage = N_("[options] [SOURCE] BRANCH");
#define OPTS(OP) \
OP (opt_help_msg, "h", "help", 0, \
N_("Display a help message and exit.")) \
OP (opt_long_help, "H", 0, 0, \
N_("Display a verbose help message and exit.")) \
OP (opt_version, "V", "version", 0, \
N_("Display a release identifier string\n" \
"and exit.")) \
OP (opt_log, "l", "log FILE", 1, \
N_("commit with log file FILE")) \
OP (opt_no_cacherev, 0, "no-cacherev", 0, \
N_("Do not cacherev tag even if different archive")) \
OP (opt_seal, 0, "seal", 0, \
N_("create a version-0 revision")) \
OP (opt_fix, 0, "fix", 0, \
N_("create a versionfix revision")) \
OP (opt_dir, "d", "dir DIR", 1, \
"cd to DIR first")
t_uchar arch_cmd_branch_help[] = N_("create a branch\n"
"Create the continuation at the next patch level of BRANCH\n"
"which is equivalent to SOURCE.\n"
"\n"
"If SOURCE is not specified, the current project tree revision is used, \n"
"and the project tree is switched to BRANCH\n");
/* FIXME: describe the search path for SOURCE - patch level in tree-version. etc */
enum options
{
OPTS (OPT_ENUM)
};
static struct opt_desc opts[] =
{
OPTS (OPT_DESC)
{-1, 0, 0, 0, 0}
};
int
arch_cmd_branch (t_uchar * program_name, int argc, char * argv[])
{
int o;
struct opt_parsed * option;
t_uchar * log_file = 0;
char * dir = ".";
int do_cacherev = 1;
int seal = 0;
int fix = 0;
safe_buffer_fd (1, 0, O_WRONLY, 0);
option = 0;
while (1)
{
o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_cmd_branch_help, opt_help_msg, opt_long_help, opt_version);
if (o == opt_none)
break;
switch (o)
{
default:
safe_printfmt (2, "unhandled option `%s'\n", option->opt_string);
panic ("internal error parsing arguments");
usage_error:
opt_usage (2, argv[0], program_name, usage, 1);
exit (1);
case opt_log:
{
log_file = str_save (0, option->arg_string);
break;
}
case opt_dir:
{
dir = str_save (0, option->arg_string);
break;
}
case opt_no_cacherev:
{
do_cacherev = 0;
break;
}
case opt_seal:
{
seal = 1;
break;
}
case opt_fix:
{
fix = 1;
break;
}
}
}
if (argc < 2 || argc > 3)
goto usage_error;
{
t_uchar * tag_spec;
t_uchar * source_revision = 0;
t_uchar * tag_version = 0;
t_uchar * log = 0;
struct arch_archive * source_arch = 0;
struct arch_archive * tag_arch = NULL;
rel_table tag_version_revisions = 0;
t_uchar * last_level = 0;
enum arch_patch_level_type last_level_type;
enum arch_patch_level_type desired_level_type;
t_ulong last_n;
t_ulong desired_n;
t_uchar * desired_level = 0;
t_uchar * tag_revision = 0;
t_uchar * tree_root = 0;
arch_project_tree_t * tree = arch_project_tree_new (talloc_context, dir);
if (argc == 3)
{
source_revision = str_replace (source_revision, arch_determine_revision (&source_arch,
tree->archive,
argv[1],
argv[0]));
tag_spec = argv[2];
}
else
{
if (!tree->fqrevision)
{
safe_printfmt (2, "%s: could not determine source revision from directory: %s\n",
argv[0], arch_abs_path (dir));
exit (2);
}
source_arch = arch_archive_connect_branch (tree->fqrevision, &source_revision);
if (!source_arch)
{
safe_printfmt (2, _("branch: could not connect to source archive to verify official name %s\n"), tree->fqrevision);
exit (2);
}
source_revision = str_replace (source_revision, arch_parse_package_name (arch_ret_non_archive, NULL, source_revision));
tag_spec = argv[1];
arch_tree_ensure_no_conflicts (tree);
}
if (log_file)
{
log = file_contents (log_file);
if (!arch_valid_log_file (log))
{
safe_printfmt (2, "%s: invalid log file (%s)\n",
argv[0], log_file);
exit (1);
}
}
/* connect to the tag spec */
arch_project_tree_check_commitable_name (tree, &tag_arch, &tag_version, tag_spec);
if (!tag_arch)
{
safe_printfmt (2, _("%s: could not connect to destination archive for %s\n"),
argv[0], tag_spec);
exit (1);
}
if (!arch_valid_package_name (tag_version, arch_req_archive, arch_req_version, 0))
{
safe_printfmt (2, _("%s: invalid branch target %s\n"), argv[0], tag_version);
exit (1);
}
tag_version = str_replace (tag_version, arch_parse_package_name (arch_ret_non_archive, NULL, tag_version));
if (arch_is_system_package_name (tag_version))
{
safe_printfmt (2, "%s: user's can not tag in system versions\n version: %s\n", argv[0], tag_version);
exit (2);
}
arch_setup_archive_simple_ext (1, tag_arch, tag_version);
tag_version_revisions = arch_archive_revisions (tag_arch, tag_version, 0);
if (!tag_version_revisions)
{
desired_level_type = arch_is_base0_level;
desired_n = 0;
}
else
{
last_level = str_save (0, tag_version_revisions[rel_n_records (tag_version_revisions) - 1][0]);
last_level_type = arch_analyze_patch_level (&last_n, last_level);
switch (last_level_type)
{
default:
Throw (exception (EINVAL, "Unrecognized patch level"));
break;
case arch_is_base0_level:
{
if (seal)
{
desired_level_type = arch_is_version_level;
desired_n = 0;
}
else if (fix)
{
safe_printfmt (2, "%s: can not --fix before --seal",
argv[0]);
exit (2);
}
else
{
desired_level_type = arch_is_patch_level;
desired_n = 1;
}
break;
}
case arch_is_patch_level:
{
if (seal)
{
desired_level_type = arch_is_version_level;
desired_n = 0;
}
else if (fix)
{
safe_printfmt (2, "%s: can not --fix before --seal",
argv[0]);
exit (2);
}
else
{
desired_level_type = arch_is_patch_level;
desired_n = last_n + 1;
}
break;
}
case arch_is_version_level:
{
if (seal)
{
safe_printfmt (2, "%s: version already sealed", argv[0]);
exit (2);
}
else if (fix)
{
desired_level_type = arch_is_versionfix_level;
desired_n = 1;
}
else
{
safe_printfmt (2, "%s: cannot commit to sealed version with --fix\n",
argv[0]);
exit (2);
}
break;
}
case arch_is_versionfix_level:
{
if (seal)
{
safe_printfmt (2, "%s: version already sealed", argv[0]);
exit (2);
}
else if (fix)
{
desired_level_type = arch_is_versionfix_level;
desired_n = last_n + 1;
}
else
{
safe_printfmt (2, "%s: cannot commit to sealed version with --fix\n",
argv[0]);
exit (2);
}
break;
}
}
}
desired_level = arch_form_patch_level (desired_level_type, desired_n);
tag_revision = str_alloc_cat_many (0, tag_version, "--", desired_level, str_end);
{
int tag_cacherev = do_cacherev && str_cmp(tag_arch->official_name, source_arch->official_name);
tag_method_t * tag = arch_tag (tree, 1, tag_arch, tag_revision, source_arch, source_revision, log);
if (tag_cacherev)
arch_tag_cacherev (tag);
if (tree->root && argc == 2)
{
t_uchar * tag_fqversion = arch_fully_qualify (tag_arch->official_name, tag_version);
arch_patch_id * patch_id = arch_patch_id_new_archive (tag_arch->official_name, tag_version);
struct arch_apply_changeset_report * r;
arch_set_tree_version (tree->root, patch_id);
r = arch_apply_changeset (tag->changeset, tag,
tree,
arch_unspecified_id_tagging,
arch_inventory_unrecognized,
0, 0, arch_escape_classes,
NULL, NULL);
/* conflicts are impossible - tag changesets are just new log data */
invariant (!arch_conflicts_occurred (r));
lim_free (0, tag_fqversion);
talloc_free (patch_id);
}
talloc_free (tag);
}
if (log_file)
safe_unlink (log_file);
lim_free (0, source_revision);
lim_free (0, tag_version);
lim_free (0, log);
arch_archive_close (source_arch);
arch_archive_close (tag_arch);
rel_free_table (tag_version_revisions);
lim_free (0, last_level);
lim_free (0, desired_level);
lim_free (0, tag_revision);
lim_free (0, tree_root);
arch_project_tree_delete (tree);
}
return 0;
}
/* tag: Tom Lord Tue May 27 23:03:10 2003 (tagrev.c)
*/
syntax highlighted by Code2HTML, v. 0.9.1