/* Undo the changes to a file. Copyright (C) 2001, 2002 Tom Lord Copyright (C) 2002 Karel Gardas Copyright (C) 2003 Walter Landry and the Regents of the University of California 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 "check_extra_args.hpp" #include "command_initializer.hpp" #include "arx_error.hpp" #include #include "tree_root.hpp" #include "find_or_make_pristine.hpp" #include "boost/filesystem/operations.hpp" #include #include "add_path_and_checksum.hpp" #include "read_checksums.hpp" #include "Temp_Directory.hpp" #include "tempdir.hpp" using namespace std; using namespace boost; namespace fs=boost::filesystem; using fs::path; int file_undo(list &argument_list, const command &cmd); static command_initializer file_undo_init(command("file-undo", "Undo the changes to a file", "usage: file-undo [options] file [revision]", "Undo changes done in FILE from the corresponding file in a a cached\n\ copy of REVISION. If no revision is given, it uses the last patch level\n\ of the branch given by tree-branch.\n\ \n\ This command will not work properly if the file has been deleted\n\ with \"arx rm\".", file_undo,"Miscellaneous Advanced",true)); int file_undo(list &argument_list, const command &cmd) { Command_Info info(cmd); Parsed_Name cached_revision; while(!argument_list.empty()) if(!parse_common_options(argument_list,info)) { parse_unknown_options(argument_list); break; } /* Get the file to be undone. */ if(argument_list.empty()) throw arx_error("Need at least one argument"); path file_path; file_path=*(argument_list.begin()); file_path=system_complete(file_path); argument_list.pop_front(); const path root(tree_root(system_complete(file_path).branch_path())); const path relative_file_path=relative_path(file_path,root); /* Get the revision */ if(!argument_list.empty()) { cached_revision=Parsed_Name(*(argument_list.begin())); argument_list.pop_front(); } check_extra_args(argument_list,info); Temp_Directory temp_dir(tempdir(),",,pristine",false); const path pristine(find_or_make_pristine(root,temp_dir.path, cached_revision)); if(!lexists(pristine/relative_file_path)) { throw arx_error("Can't find this file in the pristine tree\n\t" + file_path.native_file_string() + "\n\t" + (pristine/relative_file_path).native_file_string()); } /* Make sure that we're dealing with files. */ if((lexists(file_path) && (symbolic_link_exists(file_path) || is_directory(file_path))) || (symbolic_link_exists(pristine/relative_file_path) || is_directory(pristine/relative_file_path))) { throw arx_error("Can't use file-undo to revert symlinks or directories. Try undo instead.\n\t" + file_path.native_file_string()); } /* Move the old branch. */ if(lexists(file_path)) { string temp_string((file_path.branch_path() / (",," + file_path.leaf() + "--undo-")).string()); stringstream temp_stream; int i=0; temp_stream << temp_string << i; while(lexists(path(temp_stream.str()))) { ++i; temp_stream.str(""); temp_stream << temp_string << i; } rename(file_path,temp_stream.str()); } copy_file(pristine/relative_file_path,file_path); /* If there is no inventory id for that file, and there is one in the pristine tree, then copy that over as well. */ Checksums tree_checksums; read_checksums(root,tree_checksums); Checksums::iterator i=tree_checksums.begin(); for(; i!=tree_checksums.end(); ++i) { if(i->first.file_path.string()==relative_file_path.string()) break; } if(i==tree_checksums.end()) { Checksums pristine_checksums; read_checksums(pristine,pristine_checksums); for(Checksums::iterator j=pristine_checksums.begin(); j!=pristine_checksums.begin(); ++j) { if(j->first.file_path.string()==relative_file_path.string()) { add_path_and_checksum(root,j->first,j->second); break; } } } return 0; }