.SH "DESCRIPTION"
.LP
Snapper is a command\-line program for filesystem snapshot management. It can
-create, delete and compare snapshots and rollback changes between
+create, delete and compare snapshots and undo changes between
snapshots. Supported filesystems are btrfs and ext4.
.SH CONCEPTS
Unmount a snapshot. Not required for all filesystem types.
.TP
-.B diff [options] <number1> <number2>
+.B diff [options] <number1>..<number2>
Compare the snapshots number1 and number2. This will show a list of files and
directories that have been created, modified or deleted in the time between
the two snapshots have been made.
.TP
\fI\-o, \-\-output\fR <file>
Write output to file <file>.
+
.TP
-\fI\-f, \-\-file\fR <file>
-Compare the file <file> between the two snapshots.
+.B contentdiff [options] <number1>..<number2> [files]
+Compare the snapshots number1 and number2. This will show a diff of the
+content of files and directories that have been created, modified or deleted
+in the time between the two snapshots have been made.
.TP
-.B rollback [options] <number1> <number2>
-Rollback changes done between snapshot number1 and number2.
+.B undochange [options] <number1>..<number2> [files]
+Undo changes done between snapshot number1 and number2.
.TP
-\fI\-f, \-\-file\fR <file>
-Read the files to rollback from the file <file>.
+\fI\-i, \-\-input\fR <file>
+Read files for which to undo changes from file <file>.
.TP
.B cleanup <cleanup-algorithm>
+-------------------------------------------------------------------
+Thu Aug 11 12:09:19 CEST 2011 - aschnell@suse.de
+
+- improvements to command-line interface
+
-------------------------------------------------------------------
Thu Aug 04 20:51:59 CEST 2011 - aschnell@suse.de
{
if (snapshot1 == snapper->getSnapshots().end() ||
snapshot2 == snapper->getSnapshots().end() ||
- snapshot1 == snapshot2)
+ snapshot1 == snapshot2 || snapshot1->isCurrent())
throw IllegalSnapshotException();
y2mil("num1:" << snapshot1->getNum() << " num2:" << snapshot2->getNum());
}
- RollbackStatistic
- Comparison::getRollbackStatistic() const
+ UndoStatistic
+ Comparison::getUndoStatistic() const
{
- return files.getRollbackStatistic();
+ return files.getUndoStatistic();
}
bool
- Comparison::doRollback()
+ Comparison::doUndo()
{
- return files.doRollback();
+ return files.doUndo();
}
}
Files& getFiles() { return files; }
const Files& getFiles() const { return files; }
- RollbackStatistic getRollbackStatistic() const;
+ UndoStatistic getUndoStatistic() const;
- bool doRollback();
+ bool doUndo();
private:
namespace snapper
{
- std::ostream& operator<<(std::ostream& s, const RollbackStatistic& rs)
+ std::ostream& operator<<(std::ostream& s, const UndoStatistic& rs)
{
s << "numCreate:" << rs.numCreate
<< " numModify:" << rs.numModify
}
- RollbackStatistic::RollbackStatistic()
+ UndoStatistic::UndoStatistic()
: numCreate(0), numModify(0), numDelete(0)
{
}
bool
- RollbackStatistic::empty() const
+ UndoStatistic::empty() const
{
return numCreate == 0 && numModify == 0 && numDelete == 0;
}
bool
- File::doRollback()
+ File::doUndo()
{
- if (getSnapper()->getRollbackCallback())
+ if (getSnapper()->getUndoCallback())
{
switch (getAction())
{
- case CREATE: getSnapper()->getRollbackCallback()->createInfo(name); break;
- case MODIFY: getSnapper()->getRollbackCallback()->modifyInfo(name); break;
- case DELETE: getSnapper()->getRollbackCallback()->deleteInfo(name); break;
+ case CREATE: getSnapper()->getUndoCallback()->createInfo(name); break;
+ case MODIFY: getSnapper()->getUndoCallback()->modifyInfo(name); break;
+ case DELETE: getSnapper()->getUndoCallback()->deleteInfo(name); break;
}
}
error = true;
}
- if (error && getSnapper()->getRollbackCallback())
+ if (error && getSnapper()->getUndoCallback())
{
switch (getAction())
{
- case CREATE: getSnapper()->getRollbackCallback()->createError(name); break;
- case MODIFY: getSnapper()->getRollbackCallback()->modifyError(name); break;
- case DELETE: getSnapper()->getRollbackCallback()->deleteError(name); break;
+ case CREATE: getSnapper()->getUndoCallback()->createError(name); break;
+ case MODIFY: getSnapper()->getUndoCallback()->modifyError(name); break;
+ case DELETE: getSnapper()->getUndoCallback()->deleteError(name); break;
}
}
}
- RollbackStatistic
- Files::getRollbackStatistic() const
+ UndoStatistic
+ Files::getUndoStatistic() const
{
- RollbackStatistic rs;
+ UndoStatistic rs;
for (vector<File>::const_iterator it = entries.begin(); it != entries.end(); ++it)
{
- if (it->getRollback())
+ if (it->getUndo())
{
switch (it->getAction())
{
bool
- Files::doRollback()
+ Files::doUndo()
{
- y2mil("begin rollback");
+ y2mil("begin doUndo");
- if (getSnapper()->getRollbackCallback())
- getSnapper()->getRollbackCallback()->start();
+ if (getSnapper()->getUndoCallback())
+ getSnapper()->getUndoCallback()->start();
for (vector<File>::reverse_iterator it = entries.rbegin(); it != entries.rend(); ++it)
{
- if (it->getRollback())
+ if (it->getUndo())
{
if (it->getPreToPostStatus() == CREATED)
{
- it->doRollback();
+ it->doUndo();
}
}
}
for (vector<File>::iterator it = entries.begin(); it != entries.end(); ++it)
{
- if (it->getRollback())
+ if (it->getUndo())
{
if (it->getPreToPostStatus() != CREATED)
{
- it->doRollback();
+ it->doUndo();
}
}
}
- if (getSnapper()->getRollbackCallback())
- getSnapper()->getRollbackCallback()->stop();
+ if (getSnapper()->getUndoCallback())
+ getSnapper()->getUndoCallback()->stop();
- y2mil("end rollback");
+ y2mil("end doUndo");
return true;
}
};
- struct RollbackStatistic
+ struct UndoStatistic
{
- RollbackStatistic();
+ UndoStatistic();
bool empty() const;
unsigned int numModify;
unsigned int numDelete;
- friend std::ostream& operator<<(std::ostream& s, const RollbackStatistic& rs);
+ friend std::ostream& operator<<(std::ostream& s, const UndoStatistic& rs);
};
File(const Comparison* comparison, const string& name,
unsigned int pre_to_post_status)
: comparison(comparison), name(name), pre_to_post_status(pre_to_post_status),
- pre_to_system_status(-1), post_to_system_status(-1), rollback(false)
+ pre_to_system_status(-1), post_to_system_status(-1), undo(false)
{}
const string& getName() const { return name; }
vector<string> getDiff(const string& options) const;
- bool getRollback() const { return rollback; }
- void setRollback(bool value) { rollback = value; }
+ bool getUndo() const { return undo; }
+ void setUndo(bool value) { undo = value; }
+ bool doUndo();
- bool doRollback();
+ void setRollback(bool value) __attribute__ ((deprecated)) { setUndo(value); }
+ bool doRollback() __attribute__ ((deprecated)) { return doUndo(); }
enum Action { CREATE, MODIFY, DELETE };
unsigned int pre_to_system_status; // -1 if invalid
unsigned int post_to_system_status; // -1 if invalid
- bool rollback;
+ bool undo;
};
bool save();
void filter();
- RollbackStatistic getRollbackStatistic() const;
+ UndoStatistic getUndoStatistic() const;
- bool doRollback();
+ bool doUndo();
const Comparison* comparison;
Snapper::Snapper(const string& config_name, bool disable_filters)
: config_name(config_name), config(NULL), subvolume("/"), filesystem(NULL),
- snapshots(this), compare_callback(NULL), rollback_callback(NULL)
+ snapshots(this), compare_callback(NULL), undo_callback(NULL)
{
y2mil("Snapper constructor");
y2mil("libsnapper version " VERSION);
};
- struct RollbackCallback
+ struct UndoCallback
{
- RollbackCallback() {}
- virtual ~RollbackCallback() {}
+ UndoCallback() {}
+ virtual ~UndoCallback() {}
virtual void start() = 0;
virtual void stop() = 0;
void setCompareCallback(CompareCallback* p) { compare_callback = p; }
CompareCallback* getCompareCallback() const { return compare_callback; }
- void setRollbackCallback(RollbackCallback* p) { rollback_callback = p; }
- RollbackCallback* getRollbackCallback() const { return rollback_callback; }
+ void setUndoCallback(UndoCallback* p) { undo_callback = p; }
+ UndoCallback* getUndoCallback() const { return undo_callback; }
const vector<string>& getIgnorePatterns() const { return ignore_patterns; }
Snapshots snapshots;
CompareCallback* compare_callback;
- RollbackCallback* rollback_callback;
+ UndoCallback* undo_callback;
};
CompareCallbackImpl compare_callback_impl;
-struct RollbackCallbackImpl : public RollbackCallback
+struct UndoCallbackImpl : public UndoCallback
{
- void start() { cout << "running rollback..." << endl; }
- void stop() { cout << "rollback done" << endl; }
+ void start() { cout << "undoing..." << endl; }
+ void stop() { cout << "undoing done" << endl; }
void createInfo(const string& name) { cout << "creating " << name << endl; }
void modifyInfo(const string& name) { cout << "modifying " << name << endl; }
void deleteError(const string& name) { cout << "failed to delete " << name << endl; numDeleteErrors++; }
};
-RollbackCallbackImpl rollback_callback_impl;
+UndoCallbackImpl undo_callback_impl;
void
sh = createSnapper("testsuite");
sh->setCompareCallback(&compare_callback_impl);
- sh->setRollbackCallback(&rollback_callback_impl);
+ sh->setUndoCallback(&undo_callback_impl);
}
void
-check_rollback_statistics(unsigned int numCreate, unsigned int numModify, unsigned int numDelete)
+check_undo_statistics(unsigned int numCreate, unsigned int numModify, unsigned int numDelete)
{
Comparison comparison(sh, first, second);
Files& files = comparison.getFiles();
for (Files::iterator it = files.begin(); it != files.end(); ++it)
- it->setRollback(true);
+ it->setUndo(true);
- RollbackStatistic rs = comparison.getRollbackStatistic();
+ UndoStatistic rs = comparison.getUndoStatistic();
check_equal(rs.numCreate, numCreate);
check_equal(rs.numModify, numModify);
void
-rollback()
+undo()
{
numCreateErrors = numModifyErrors = numDeleteErrors = 0;
Files& files = comparison.getFiles();
for (Files::iterator it = files.begin(); it != files.end(); ++it)
- it->setRollback(true);
+ it->setUndo(true);
- comparison.doRollback();
+ comparison.doUndo();
}
void
-check_rollback_errors(unsigned int numCreate, unsigned int numModify, unsigned int numDelete)
+check_undo_errors(unsigned int numCreate, unsigned int numModify, unsigned int numDelete)
{
check_equal(numCreateErrors, numCreate);
check_equal(numModifyErrors, numModify);
void setup();
void first_snapshot();
void second_snapshot();
-void check_rollback_statistics(unsigned int numCreate, unsigned int numModify,
- unsigned int numDelete);
-void rollback();
-void check_rollback_errors(unsigned int numCreate, unsigned int numModify,
+void check_undo_statistics(unsigned int numCreate, unsigned int numModify,
unsigned int numDelete);
+void undo();
+void check_undo_errors(unsigned int numCreate, unsigned int numModify,
+ unsigned int numDelete);
void check_first();
void run_command(const char* command);
run_command("mkdir already-here");
- check_rollback_statistics(1, 0, 0);
+ check_undo_statistics(1, 0, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
run_command("touch not-empty/bad");
- check_rollback_statistics(0, 0, 1);
+ check_undo_statistics(0, 0, 1);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 1);
+ check_undo_errors(0, 0, 1);
exit(EXIT_SUCCESS);
}
run_command("rm not-here");
- check_rollback_statistics(0, 1, 0);
+ check_undo_statistics(0, 1, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 1, 0);
+ check_undo_errors(0, 1, 0);
exit(EXIT_SUCCESS);
}
run_command("rmdir wrong-type");
run_command("touch wrong-type");
- check_rollback_statistics(1, 0, 0);
+ check_undo_statistics(1, 0, 0);
- rollback();
+ undo();
- check_rollback_errors(1, 0, 0);
+ check_undo_errors(1, 0, 0);
exit(EXIT_SUCCESS);
}
run_command("rmdir not-here");
- check_rollback_statistics(2, 0, 0);
+ check_undo_statistics(2, 0, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(3, 0, 0);
+ check_undo_statistics(3, 0, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(0, 3, 0);
+ check_undo_statistics(0, 3, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(0, 1, 0);
+ check_undo_statistics(0, 1, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(4, 0, 0);
+ check_undo_statistics(4, 0, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(0, 2, 0);
+ check_undo_statistics(0, 2, 0);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
second_snapshot();
- check_rollback_statistics(3, 0, 3);
+ check_undo_statistics(3, 0, 3);
- rollback();
+ undo();
- check_rollback_errors(0, 0, 0);
+ check_undo_errors(0, 0, 0);
check_first();
}
+pair<Snapshots::iterator, Snapshots::iterator>
+readNums(const string& str)
+{
+ string::size_type pos = str.find("..");
+ if (pos == string::npos)
+ {
+ cerr << _("Invalid snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ Snapshots::iterator snap1 = readNum(str.substr(0, pos));
+ Snapshots::iterator snap2 = readNum(str.substr(pos + 2));
+
+ if (snap1 == snap2 || snap1->isCurrent())
+ {
+ cerr << _("Invalid snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ return pair<Snapshots::iterator, Snapshots::iterator>(snap1, snap2);
+}
+
+
void
help_list()
{
help_diff()
{
cout << _(" Comparing snapshots:") << endl
- << _("\tsnapper diff <number1> <number2>") << endl
+ << _("\tsnapper diff <number1>..<number2>") << endl
<< endl
<< _(" Options for 'diff' command:") << endl
<< _("\t--output, -o <file>\t\tSave diff to file.") << endl
- << _("\t--file, -f <file>\t\tRun diff for file.") << endl
<< endl;
}
{
const struct option options[] = {
{ "output", required_argument, 0, 'o' },
- { "file", required_argument, 0, 'f' },
{ 0, 0, 0, 0 }
};
GetOpts::parsed_opts opts = getopts.parse("diff", options);
- if (getopts.numArgs() != 2)
+ if (getopts.numArgs() != 1)
{
- cerr << _("Command 'diff' needs two arguments.") << endl;
+ cerr << _("Command 'diff' needs one argument.") << endl;
exit(EXIT_FAILURE);
}
GetOpts::parsed_opts::const_iterator opt;
- Snapshots::const_iterator snap1 = readNum(getopts.popArg());
- Snapshots::const_iterator snap2 = readNum(getopts.popArg());
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(readNums(getopts.popArg()));
- Comparison comparison(sh, snap1, snap2);
+ Comparison comparison(sh, snaps.first, snaps.second);
const Files& files = comparison.getFiles();
- Files::const_iterator tmp = files.end();
-
- if ((opt = opts.find("file")) != opts.end())
- {
- tmp = files.findAbsolutePath(opt->second);
- if (tmp == files.end())
- {
- cerr << sformat(_("File '%s' not included in diff."), opt->second.c_str()) << endl;
- exit(EXIT_FAILURE);
- }
- }
-
FILE* file = stdout;
if ((opt = opts.find("output")) != opts.end())
}
}
- if (tmp == files.end())
+ for (Files::const_iterator it = files.begin(); it != files.end(); ++it)
+ fprintf(file, "%s %s\n", statusToString(it->getPreToPostStatus()).c_str(),
+ it->getAbsolutePath(LOC_SYSTEM).c_str());
+
+ if (file != stdout)
+ fclose(file);
+}
+
+
+void
+help_contentdiff()
+{
+ cout << _(" Comparing snapshots:") << endl
+ << _("\tsnapper contentdiff <number1>..<number2> [files]") << endl
+ << endl;
+}
+
+
+void
+command_contentdiff()
+{
+ GetOpts::parsed_opts opts = getopts.parse("contentdiff", GetOpts::no_options);
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(readNums(getopts.popArg()));
+
+ Comparison comparison(sh, snaps.first, snaps.second);
+
+ const Files& files = comparison.getFiles();
+
+ if (getopts.numArgs() == 0)
{
- for (Files::const_iterator it = files.begin(); it != files.end(); ++it)
- fprintf(file, "%s %s\n", statusToString(it->getPreToPostStatus()).c_str(),
- it->getAbsolutePath(LOC_SYSTEM).c_str());
+ for (Files::const_iterator it1 = files.begin(); it1 != files.end(); ++it1)
+ {
+ vector<string> lines = it1->getDiff("--unified --new-file");
+ for (vector<string>::const_iterator it2 = lines.begin(); it2 != lines.end(); ++it2)
+ cout << it2->c_str() << endl;
+ }
}
else
{
- vector<string> lines = tmp->getDiff("--unified --new-file");
- for (vector<string>::const_iterator it = lines.begin(); it != lines.end(); ++it)
- fprintf(file, "%s\n", it->c_str());
- }
+ while (getopts.numArgs() > 0)
+ {
+ string name = getopts.popArg();
- if (file != stdout)
- fclose(file);
+ Files::const_iterator tmp = files.findAbsolutePath(name);
+ if (tmp == files.end())
+ continue;
+
+ vector<string> lines = tmp->getDiff("--unified --new-file");
+ for (vector<string>::const_iterator it2 = lines.begin(); it2 != lines.end(); ++it2)
+ cout << it2->c_str() << endl;
+ }
+ }
}
void
-help_rollback()
+help_undo()
{
- cout << _(" Rollback snapshots:") << endl
- << _("\tsnapper rollback <number1> <number2>") << endl
+ cout << _(" Undo changes:") << endl
+ << _("\tsnapper undochange <number1>..<number2> [files]") << endl
<< endl
- << _(" Options for 'rollback' command:") << endl
- << _("\t--file, -f <file>\t\tRead files to rollback from file.") << endl
+ << _(" Options for 'undochange' command:") << endl
+ << _("\t--input, -i <file>\t\tRead files for which to undo changes from file.") << endl
<< endl;
}
void
-command_rollback()
+command_undo()
{
const struct option options[] = {
- { "file", required_argument, 0, 'f' },
+ { "input", required_argument, 0, 'i' },
{ 0, 0, 0, 0 }
};
- GetOpts::parsed_opts opts = getopts.parse("rollback", options);
- if (getopts.numArgs() != 2)
+ GetOpts::parsed_opts opts = getopts.parse("undochange", options);
+ if (getopts.numArgs() < 1)
{
- cerr << _("Command 'rollback' needs two arguments.") << endl;
+ cerr << _("Command 'undochange' needs at least one argument.") << endl;
exit(EXIT_FAILURE);
}
- Snapshots::const_iterator snap1 = readNum(getopts.popArg());
- Snapshots::const_iterator snap2 = readNum(getopts.popArg());
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(readNums(getopts.popArg()));
FILE* file = NULL;
GetOpts::parsed_opts::const_iterator opt;
- if ((opt = opts.find("file")) != opts.end())
+ if ((opt = opts.find("input")) != opts.end())
{
file = fopen(opt->second.c_str(), "r");
if (!file)
}
}
- Comparison comparison(sh, snap1, snap2);
+ Comparison comparison(sh, snaps.first, snaps.second);
Files& files = comparison.getFiles();
exit(EXIT_FAILURE);
}
- it->setRollback(true);
+ it->setUndo(true);
}
}
else
{
- for (Files::iterator it = files.begin(); it != files.end(); ++it)
- it->setRollback(true);
+ if (getopts.numArgs() == 0)
+ {
+ for (Files::iterator it = files.begin(); it != files.end(); ++it)
+ it->setUndo(true);
+ }
+ else
+ {
+ while (getopts.numArgs() > 0)
+ {
+ string name = getopts.popArg();
+
+ Files::iterator tmp = files.findAbsolutePath(name);
+ if (tmp == files.end())
+ continue;
+
+ tmp->setUndo(true);
+ }
+ }
}
- RollbackStatistic rs = comparison.getRollbackStatistic();
+ UndoStatistic rs = comparison.getUndoStatistic();
if (rs.empty())
{
cout << "create:" << rs.numCreate << " modify:" << rs.numModify << " delete:" << rs.numDelete
<< endl;
- comparison.doRollback();
+ comparison.doUndo();
}
help_mount();
help_umount();
help_diff();
- help_rollback();
+ help_contentdiff();
+ help_undo();
help_cleanup();
}
CompareCallbackImpl compare_callback_impl;
-struct RollbackCallbackImpl : public RollbackCallback
+struct UndoCallbackImpl : public UndoCallback
{
- void start() { cout << _("running rollback...") << endl; }
- void stop() { cout << _("rollback done") << endl; }
+ void start() { cout << _("undoing change...") << endl; }
+ void stop() { cout << _("undoing change done") << endl; }
void createInfo(const string& name)
{ if (verbose) cout << sformat(_("creating %s"), name.c_str()) << endl; }
{ cerr << sformat(_("failed to delete %s"), name.c_str()) << endl; }
};
-RollbackCallbackImpl rollback_callback_impl;
+UndoCallbackImpl undo_callback_impl;
int
cmds["mount"] = command_mount;
cmds["umount"] = command_umount;
cmds["diff"] = command_diff;
- cmds["rollback"] = command_rollback;
+ cmds["contentdiff"] = command_contentdiff;
+ cmds["undochange"] = command_undo;
cmds["cleanup"] = command_cleanup;
cmds["help"] = command_help;
if (!quiet)
{
sh->setCompareCallback(&compare_callback_impl);
- sh->setRollbackCallback(&rollback_callback_impl);
+ sh->setUndoCallback(&undo_callback_impl);
}
(*cmd->second)();