/* commit the changes to a tree to the archive Copyright (C) 2001, 2002, 2003 Tom Lord Copyright (C) 2002, 2003 Walter Landry and the Regents of the University of California Copyright (C) 2004 Walter Landry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "parse_common_options.hpp" #include "parse_unknown_options.hpp" #include "parse_rfc822.hpp" #include "check_extra_args.hpp" #include "command_initializer.hpp" #include "arx_error.hpp" #include #include "get_option_from_file.hpp" #include "boost/filesystem/operations.hpp" #include #include "commit_revision.hpp" #include "tree_root.hpp" #include "Parsed_Name.hpp" #include "check_tree.hpp" #include "fill_path_list.hpp" #include "valid_package_name.hpp" #include "tree_branch.hpp" #include "get_config_option.hpp" using namespace std; using namespace boost; namespace fs=boost::filesystem; using fs::path; int commit(list &argument_list, const command &cmd); static command_initializer commit_init(command("commit", "Store the current project tree in the archive", "usage: commit [options] [path ...]", " --dir DIR commit project tree in DIR\n\ -L --log-file FILE Use FILE as the log file\n\ -s log-message use LOG-MESSAGE as the description\n\ --branch branch commit changes to BRANCH\n\ --unchanged-ok commit even if project tree is unmodified\n\ --out-of-date-ok commit even if project tree seems out of date\n\ --paths-file PATHS-FILE restrict commit to selected paths listed\n\ in PATHS-FILE\n\ --key KEY sign the revision with KEY\n\ --date DATE override the date/time for commit\n\ --author AUTHOR override the author for commit\n\ \n\ Store a copy of the current revision of the project tree in the archive.\n\ It actually stores only the changes since the last revision. Unless\n\ invoked with --unchanged-ok, no commit will occur if there have\n\ been no changes since the last archived revision. Commit requires either\n\ a log message (with -s) or an RFC822 style log file with the Summary: header\n\ filled out.\n\ \n\ Unless invoked with --out-of-date-ok, it will refuse to commit if any\n\ patch logs are missing, because that is indicative of a tree that is not\n\ up to date.\n\ \n\ If there are any extra paths on the command line, commit will make a patch\n\ that only includes those paths. With the --paths-file option,\n\ commit reads the list of paths from a file. You may combine the two\n\ options, reading files from both the command line and a file.\n\ \n\ If a path is a file, commit includes that file. If the path is a\n\ directory, commit includes that directory and all of its contents.\n\ All paths must refer to the current location in the modified tree. If\n\ the path has been deleted in the modified tree, it should point to where\n\ the path would be in the modified tree.\n\ \n\ commit will check whether the list of paths will create a consistent patch\n\ and complain if it won't. It makes sure that\n\ \n\ 1) For any files that have been created or moved:\n\ a) The destination directory already exists or is included in the\n\ list of paths.\n\ b) If the destination path existed before and has been renamed, that\n\ new name is included.\n\ 2) If a directory has been moved or deleted, all of the subdirectories\n\ are included. If they were moved along with the parent directory,\n\ they are already included. That is, if you have a rearrangement like\n\ \n\ a/ --> aa/\n\ a/b --> aa/b\n\ a/b/c --> aa/b/c\n\ \n\ Then a command like\n\ \n\ $ arx commit aa/\n\ \n\ should work. However, if you have a rearrangement like\n\ \n\ a/ --> aa/\n\ a/b --> bb/\n\ a/b/c --> deleted\n\ \n\ Then you have to include directory bb/ and a/b/c if you include\n\ directory aa/\n\ \n\ $ arx commit aa/ bb/ a/b/c", commit,"Basic",true)); int commit(list &argument_list, const command &cmd) { Command_Info info(cmd); bool unchanged_ok(false), out_of_date_ok(false); string paths_file, log_message, gpg_key(get_config_option("gpg-key")); Parsed_Name input_branch; path tree_directory(fs::current_path()), log_file; map log_headers; while(!argument_list.empty()) if(!parse_common_options(argument_list,info)) { if(*(argument_list.begin())=="--paths-file") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --paths-file"); } paths_file=*(argument_list.begin()); argument_list.pop_front(); } else if(*(argument_list.begin())=="--dir") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --dir"); } tree_directory=path(*(argument_list.begin())); argument_list.pop_front(); } else if(*(argument_list.begin())=="-L" || *(argument_list.begin())=="--log-file") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for -L and --log-file"); } log_file=path(*(argument_list.begin())); argument_list.pop_front(); } else if(*(argument_list.begin())=="--branch") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --branch"); } input_branch=*(argument_list.begin()); argument_list.pop_front(); } else if(*(argument_list.begin())=="-s") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for -s"); } log_message=*(argument_list.begin()); argument_list.pop_front(); } else if(*(argument_list.begin())=="--unchanged-ok") { unchanged_ok=true; argument_list.pop_front(); } else if(*(argument_list.begin())=="--out-of-date-ok") { out_of_date_ok=true; argument_list.pop_front(); } else if(*(argument_list.begin())=="--key") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --key"); } gpg_key=*(argument_list.begin()); argument_list.pop_front(); } else if(*(argument_list.begin())=="--date") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --date"); } log_headers["Standard-date"]=log_headers["Date"]= *(argument_list.begin()); argument_list.pop_front(); } else if(*(argument_list.begin())=="--author") { argument_list.pop_front(); if(argument_list.empty()) { throw arx_error("Need an argument for --author"); } log_headers["Creator"]=*(argument_list.begin()); argument_list.pop_front(); } else { parse_unknown_options(argument_list); break; } } if(!log_file.empty() && !log_message.empty()) throw arx_error("Can't specify both -s and -L"); if(log_file.empty() && log_message.empty()) throw arx_error("You must either specify a log message with -s, or a log file with -L."); path root(tree_root(tree_directory)); /* Check for initial imports */ if(lexists(root / "_arx/++import")) { if(!input_branch.empty()) throw arx_error ("You may not specify a branch when doing the first commit. The branch\nwas already specified with \"init-tree\"."); input_branch=get_option_from_file(root / "_arx/++import"); } /* Get the branch to commit. */ if(input_branch.empty()) input_branch=tree_branch(root); /* Fill the list of paths for partial commits. */ Path_List path_list; bool partial_commit(fill_path_list(tree_directory,paths_file,argument_list, path_list)); check_extra_args(argument_list,info); string log_body; /* Read in the patch log if needed. */ if(!log_file.empty()) { parse_rfc822(log_file,log_headers,log_body,"\n\t"); if(log_headers.find("Summary")==log_headers.end() || log_headers["Summary"].empty()) throw arx_error("The log file must have a non-empty Summary: line"); } else { log_headers["Summary"]=log_message; } /* Now commit the revision. */ commit_revision(tree_directory,input_branch,path_list,log_headers,log_body, unchanged_ok,out_of_date_ok,partial_commit,gpg_key); /* Delete the log file. */ if(!log_file.empty()) remove(log_file); return 0; }