/* cmd-undo.c * * vim:smartindent ts=8:sts=2:sta:et:ai:shiftwidth=2 **************************************************************** * Copyright (C) 2003 Tom Lord * * 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 "libfsutils/rmrf.h" #include "libarch/namespace.h" #include "libarch/patch-logs.h" #include "libarch/pfs.h" #include "libarch/project-tree.h" #include "libarch/local-cache.h" #include "libarch/proj-tree-lint.h" #include "libarch/inode-sig.h" #include "libarch/undo.h" #include "commands/cmd.h" #include "commands/cmdutils.h" #include "commands/undo.h" #include "commands/version.h" /* __STDC__ prototypes for static functions */ static t_uchar * usage = N_("[options] [revision] [-- file ...]"); #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_archive, "A", "archive", 1, \ N_("Override `my-default-archive'")) \ OP (opt_dir, "d", "dir DIR", 1, \ N_("cd to DIR first")) \ OP (opt_output_dir, "o", "output PATCH-DIR", 1, \ N_("save changeset in PATCH-DIR")) \ OP (opt_no_output, "n", "no-output", 0, \ N_("do not save the changeset")) \ OP (opt_quiet, "q", "quiet", 0, \ N_("no progress reports while computing changeset")) \ OP (opt_unescaped, 0, "unescaped", 0, \ N_("show filenames in unescaped form")) t_uchar arch_cmd_undo_help[] = N_("undo and save changes in a project tree\n" "Compute a patch set describing the changes from REVISION\n" "to the project tree containing DIR. Save the patch in\n" "PATCH-DIR (which must not already exist) and apply the patch\n" "in reverse to the project tree containing DIR.\n" "\n" "The effect is to remove local changes in the DIR project tree\n" "but save them in a convenient form.\n" "\n" "If REVISION is not specified, the latest ancestor of the\n" "default version of project tree is used.\n" "\n" "If REVISION is specified as a VERSION, the latest ancestor\n" "of the project tree in that VERSION is used.\n" "\n" "If DIR is not specified, \".\" is assumed.\n" "\n" "If PATCH-DIR is not specified, a temporary file-name of the\n" "matching ,,undo-* is used.\n" "\n" "If --no-output is specified, the patch set is not saved.\n" "\n" "See also \"baz redo -H\" and \"baz diff -H\".\n"); enum options { OPTS (OPT_ENUM) }; static struct opt_desc opts[] = { OPTS (OPT_DESC) {-1, 0, 0, 0, 0} }; int arch_cmd_undo (t_uchar * program_name, int argc, char * argv[]) { int o; struct opt_parsed * option; t_uchar * dir = 0; t_uchar * output = 0; t_uchar * default_archive = 0; int quiet = 0; int no_output = 0; int exit_status = 0; int opt_end_with_double_dash = 0; int escape_classes = arch_escape_classes; dir = str_save (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_undo_help, opt_help_msg, opt_long_help, opt_version); if (o == opt_none) break; if (o == opt_double_dash) { opt_end_with_double_dash = 1; 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); /* bogus_arg: */ safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string); goto usage_error; case opt_archive: { lim_free (0, default_archive); default_archive = str_save (0, option->arg_string); break; } case opt_dir: { lim_free (0, dir); dir = str_save (0, option->arg_string); break; } case opt_output_dir: { lim_free (0, output); output = str_save (0, option->arg_string); break; } case opt_no_output: { no_output = 1; break; } case opt_quiet: { quiet = 1; break; } case opt_unescaped: { escape_classes = 0; break; } } } { arch_project_tree_t * tree; rel_table file_list = 0; t_uchar * rvsnspec = 0; t_uchar * archive = 0; t_uchar * revision = 0; if (default_archive && !arch_valid_archive_name (default_archive)) { safe_printfmt (2, "%s: invalid archive name: %s\n", argv[0], default_archive); exit (2); } tree = arch_project_tree_new (talloc_context, dir); if (!tree->root) { safe_printfmt (2, "%s: not in a project tree\n dir: %s\n", argv[0], dir); exit (2); } if (argc == 1 || opt_end_with_double_dash) { rvsnspec = tree->fqrevision; } else { rvsnspec = arch_fqrvsn_from_tree_and_input (tree, argv[0], argv[1], tree->root); if (!rvsnspec) { safe_printfmt (2, "%s: illegal revision name: %s\n", argv[0], rvsnspec); exit (2); } } if (argc > 2 || opt_end_with_double_dash) /* File arguments. */ { int argx = opt_end_with_double_dash ? 1 : 2; if (!opt_end_with_double_dash && str_cmp (argv[argx], "--") == 0) argx++; if (argx == argc) /* --, but no files specified; should this be an error? */ goto usage_error; while (argx < argc) { t_uchar * undo_file; undo_file = arch_abs_path (argv[argx]); if ( str_cmp_prefix (tree->root, undo_file) ) safe_printfmt(2, _("%s not found in project tree. Skipping\n"), argv[argx]); else rel_add_records (&file_list, rel_make_record (undo_file, 0), 0); lim_free (0, undo_file); argx++; } } archive = arch_parse_package_name (arch_ret_archive, default_archive, rvsnspec); revision = arch_parse_package_name (arch_ret_non_archive, 0, rvsnspec); if (!output) { output = arch_next_undo_changeset (0, tree->root); } { struct arch_tree_lint_result * lint = 0; if (!quiet) safe_printfmt (1, "* linting the source tree\n"); lint = arch_tree_lint (tree); if (0 > arch_print_tree_lint_report ((quiet ? 2 : 1), lint, escape_classes)) { exit (1); } } { t_uchar * orig_tree_path = 0; assoc_table inode_shortcut = 0; arch_project_tree_t * orig_tree; arch_patch_id * patch_id = arch_patch_id_new_archive (archive, revision); orig_tree_path = arch_find_or_make_local_copy ((quiet ? -1 : 1), tree, 0, 0, archive, revision); if (!orig_tree_path) { safe_printfmt (2, "%s: no local copies to compare to (%s/%s)\n consider `add-pristine --help'\n", argv[0], archive, revision); exit (2); } orig_tree = arch_project_tree_new (talloc_context, orig_tree_path); arch_read_inode_sig_ids (0, &inode_shortcut, tree->root, archive, revision); arch_undo ((quiet ? -1 : 1), output, no_output, tree, orig_tree, file_list, inode_shortcut, 0, escape_classes); /* this should be in arch_undo, but the API isn't quite right, * TODO adjust the API. RBC 20050203 */ if (rel_n_records (file_list) == 0) arch_snap_inode_sig (tree, patch_id); lim_free (0, orig_tree_path); arch_project_tree_delete (orig_tree); free_assoc_table (inode_shortcut); talloc_free (patch_id); } rel_free_table (file_list); talloc_free (tree); lim_free (0, archive); lim_free (0, revision); } lim_free (0, dir); lim_free (0, output); lim_free (0, default_archive); return exit_status; } /* tag: Tom Lord Wed Jun 4 23:33:25 2003 (cmd-undo-changes.c) */