]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- improvements to command-line interface
authorArvin Schnell <aschnell@suse.de>
Thu, 11 Aug 2011 10:10:50 +0000 (12:10 +0200)
committerArvin Schnell <aschnell@suse.de>
Thu, 11 Aug 2011 10:10:50 +0000 (12:10 +0200)
23 files changed:
LIBVERSION
doc/snapper.8.in
package/snapper.changes
snapper/Comparison.cc
snapper/Comparison.h
snapper/File.cc
snapper/File.h
snapper/Snapper.cc
snapper/Snapper.h
testsuite-real/common.cc
testsuite-real/common.h
testsuite-real/directory1.cc
testsuite-real/error1.cc
testsuite-real/error2.cc
testsuite-real/error4.cc
testsuite-real/missing-directory1.cc
testsuite-real/owner1.cc
testsuite-real/owner2.cc
testsuite-real/owner3.cc
testsuite-real/permissions1.cc
testsuite-real/permissions2.cc
testsuite-real/simple1.cc
tools/snapper.cc

index 26aaba0e86632e4d537006e45b0ec918d780b3b4..f0bb29e76388856b273698ae6064b0380ce5e5d2 100644 (file)
@@ -1 +1 @@
-1.2.0
+1.3.0
index b59cfd2815373cefac06ff0f82339a00fc064404..cc28afea007fcb31534ffca795e4f622fadec6da 100644 (file)
@@ -13,7 +13,7 @@ snapper \fBhelp\fR
 .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
@@ -156,23 +156,26 @@ Mount a snapshot. Not required for all filesystem types.
 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>
index 03b481914765fe4417e46020fb91c2ba961557a9..67a8a5b66c1b8382e8cfc9b68dbae14a2cdb8450 100644 (file)
@@ -1,3 +1,8 @@
+-------------------------------------------------------------------
+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
 
index a19e79c3f48b08abc5b780776c50d8cabd362fe6..05c80272ad01a4c2217837b87d6270c66324126c 100644 (file)
@@ -36,7 +36,7 @@ namespace snapper
     {
        if (snapshot1 == snapper->getSnapshots().end() ||
            snapshot2 == snapper->getSnapshots().end() ||
-           snapshot1 == snapshot2)
+           snapshot1 == snapshot2 || snapshot1->isCurrent())
            throw IllegalSnapshotException();
 
        y2mil("num1:" << snapshot1->getNum() << " num2:" << snapshot2->getNum());
@@ -45,17 +45,17 @@ namespace snapper
     }
 
 
-    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();
     }
 
 }
index 95ffaa8e8b0ee13ae1d2d9f483fca14440d4bbfe..8a677a7157c9a9d84d171a6e58cd8c0a735b97fa 100644 (file)
@@ -47,9 +47,9 @@ namespace snapper
        Files& getFiles() { return files; }
        const Files& getFiles() const { return files; }
 
-       RollbackStatistic getRollbackStatistic() const;
+       UndoStatistic getUndoStatistic() const;
 
-       bool doRollback();
+       bool doUndo();
 
     private:
 
index bdf295a8d76f357fac43a779ebe63e8a41d60cef..aacef6da6db292aedadd0dbcb5c9c38cf3980f17 100644 (file)
@@ -45,7 +45,7 @@
 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
@@ -55,14 +55,14 @@ namespace snapper
     }
 
 
-    RollbackStatistic::RollbackStatistic()
+    UndoStatistic::UndoStatistic()
        : numCreate(0), numModify(0), numDelete(0)
     {
     }
 
 
     bool
-    RollbackStatistic::empty() const
+    UndoStatistic::empty() const
     {
        return numCreate == 0 && numModify == 0 && numDelete == 0;
     }
@@ -641,15 +641,15 @@ namespace snapper
 
 
     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;
            }
        }
 
@@ -673,13 +673,13 @@ namespace snapper
                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;
            }
        }
 
@@ -698,14 +698,14 @@ namespace snapper
     }
 
 
-    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())
                {
@@ -721,39 +721,39 @@ namespace snapper
 
 
     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;
     }
index ce37c6e697ee110c4b6741521cc854f106d77cff..503ebfc3a3fb67d7777c3eee6f362c64bb75b759 100644 (file)
@@ -57,9 +57,9 @@ namespace snapper
     };
 
 
-    struct RollbackStatistic
+    struct UndoStatistic
     {
-       RollbackStatistic();
+       UndoStatistic();
 
        bool empty() const;
 
@@ -67,7 +67,7 @@ namespace snapper
        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);
     };
 
 
@@ -78,7 +78,7 @@ namespace snapper
        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; }
@@ -93,10 +93,12 @@ namespace snapper
 
        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 };
 
@@ -127,7 +129,7 @@ namespace snapper
        unsigned int pre_to_system_status; // -1 if invalid
        unsigned int post_to_system_status; // -1 if invalid
 
-       bool rollback;
+       bool undo;
 
     };
 
@@ -175,9 +177,9 @@ namespace snapper
        bool save();
        void filter();
 
-       RollbackStatistic getRollbackStatistic() const;
+       UndoStatistic getUndoStatistic() const;
 
-       bool doRollback();
+       bool doUndo();
 
        const Comparison* comparison;
 
index 6b97757fe8adea8250cb08f4d1be63cce6d68f5a..02562859761f5d7753096472e5d7c1f3f35ba5b5 100644 (file)
@@ -48,7 +48,7 @@ namespace snapper
 
     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);
index 90a6f635c6bbbf3909cec872e310532596713822..6ed9a080b3c43a07cfdf93372ad23202a9d42392 100644 (file)
@@ -48,10 +48,10 @@ namespace snapper
     };
 
 
-    struct RollbackCallback
+    struct UndoCallback
     {
-       RollbackCallback() {}
-       virtual ~RollbackCallback() {}
+       UndoCallback() {}
+       virtual ~UndoCallback() {}
 
        virtual void start() = 0;
        virtual void stop() = 0;
@@ -133,8 +133,8 @@ namespace snapper
        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; }
 
@@ -166,7 +166,7 @@ namespace snapper
        Snapshots snapshots;
 
        CompareCallback* compare_callback;
-       RollbackCallback* rollback_callback;
+       UndoCallback* undo_callback;
 
     };
 
index 6e35ff32582d7b547ca91e988c1e7b65442928ec..e3da091611a4df45179bcdc3b8acd860ca6bfc09 100644 (file)
@@ -40,10 +40,10 @@ struct CompareCallbackImpl : public CompareCallback
 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; }
@@ -54,7 +54,7 @@ struct RollbackCallbackImpl : public RollbackCallback
     void deleteError(const string& name) { cout << "failed to delete " << name << endl; numDeleteErrors++; }
 };
 
-RollbackCallbackImpl rollback_callback_impl;
+UndoCallbackImpl undo_callback_impl;
 
 
 void
@@ -68,7 +68,7 @@ setup()
     sh = createSnapper("testsuite");
 
     sh->setCompareCallback(&compare_callback_impl);
-    sh->setRollbackCallback(&rollback_callback_impl);
+    sh->setUndoCallback(&undo_callback_impl);
 }
 
 
@@ -89,15 +89,15 @@ second_snapshot()
 
 
 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);
@@ -106,7 +106,7 @@ check_rollback_statistics(unsigned int numCreate, unsigned int numModify, unsign
 
 
 void
-rollback()
+undo()
 {
     numCreateErrors = numModifyErrors = numDeleteErrors = 0;
 
@@ -114,14 +114,14 @@ rollback()
 
     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);
index 9983ceda66f8c1568681c943b76be177b10f477b..ef01950e2a89997373356c8a416b8593048333b9 100644 (file)
@@ -46,11 +46,11 @@ check_failure(const char* str, Type actual, Type expected, const char* file,
 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);
index 1275b72d3a49cc9f67458f37c35bc99e11a0eb49..daf4d0f59d14a57f7085e59db1c188f0f582871d 100644 (file)
@@ -22,11 +22,11 @@ main()
 
     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();
 
index 4a7a4def52eacf40abb95d1fa3cbc6f399652434..adee53662379eeea9329b2b44bbd4c5e65f2b670 100644 (file)
@@ -20,11 +20,11 @@ main()
 
     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);
 }
index 4701cdcfdddfc4c310f52733b7b5e0dc54b5d50d..abccbae44c72bbbcf12e286c1a350dc348df5e3a 100644 (file)
@@ -23,11 +23,11 @@ main()
 
     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);
 }
index 349818e89af8fea55390df4f16ffb6979354b4ed..cd374c6569bcd82dca405be75a43b17e3389bc80 100644 (file)
@@ -24,11 +24,11 @@ main()
     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);
 }
index d5b0369d6ad29b77afb5c1cb624185645debd64a..e47c092ed41a46c6aa7aa86e87a7afa64dd2f675 100644 (file)
@@ -25,11 +25,11 @@ main()
 
     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();
 
index fe362ab7349db9b01f0d7c0f0c1238b498dfaee2..b4475d85b7da59591683629cbd175f593e6600fe 100644 (file)
@@ -29,11 +29,11 @@ main()
 
     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();
 
index 65b25d2f6d1ade4e1f2aedc20b33e147dc855cbd..9af2b8fc18a94527a79d3d5f6434028f4318db69 100644 (file)
@@ -24,11 +24,11 @@ main()
 
     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();
 
index 1b1c8326af50b1319ad6dc7a3904c41650bba691..f8dd7328e49dcdf727082320011ca5e3f027a73c 100644 (file)
@@ -23,11 +23,11 @@ main()
 
     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();
 
index c392dc8857d57139e22e72e73884285044f36762..d6cb22a47bea352087da8c2ccf21b5db96e5819e 100644 (file)
@@ -30,11 +30,11 @@ main()
 
     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();
 
index da611249fbc63bb178db1ff95656a8b2c5d9dd13..8232fed3332a178a7ba59184843acebb71dc6fbe 100644 (file)
@@ -24,11 +24,11 @@ main()
 
     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();
 
index 21abcbf20e2a3cb3952c6543bb0c8abde12d08ba..2fcbe8e3b001765fef6a66db68f1ea72d0860661 100644 (file)
@@ -28,11 +28,11 @@ main()
 
     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();
 
index b790154e581ed27408364b08de6d7cc24d7d9379..ac4cb815c93f18b8b2dc4e8942f00ff38ac181d3 100644 (file)
@@ -194,6 +194,29 @@ readNum(const string& str)
 }
 
 
+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()
 {
@@ -573,11 +596,10 @@ void
 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;
 }
 
@@ -587,38 +609,24 @@ command_diff()
 {
     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())
@@ -631,59 +639,98 @@ command_diff()
        }
     }
 
-    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)
@@ -693,7 +740,7 @@ command_rollback()
        }
     }
 
-    Comparison comparison(sh, snap1, snap2);
+    Comparison comparison(sh, snaps.first, snaps.second);
 
     Files& files = comparison.getFiles();
 
@@ -726,16 +773,32 @@ command_rollback()
                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())
     {
@@ -746,7 +809,7 @@ command_rollback()
     cout << "create:" << rs.numCreate << " modify:" << rs.numModify << " delete:" << rs.numDelete
         << endl;
 
-    comparison.doRollback();
+    comparison.doUndo();
 }
 
 
@@ -826,7 +889,8 @@ command_help()
     help_mount();
     help_umount();
     help_diff();
-    help_rollback();
+    help_contentdiff();
+    help_undo();
     help_cleanup();
 }
 
@@ -840,10 +904,10 @@ struct CompareCallbackImpl : public CompareCallback
 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; }
@@ -860,7 +924,7 @@ struct RollbackCallbackImpl : public RollbackCallback
        { cerr << sformat(_("failed to delete %s"), name.c_str()) << endl; }
 };
 
-RollbackCallbackImpl rollback_callback_impl;
+UndoCallbackImpl undo_callback_impl;
 
 
 int
@@ -879,7 +943,8 @@ main(int argc, char** argv)
     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;
 
@@ -970,7 +1035,7 @@ main(int argc, char** argv)
        if (!quiet)
        {
            sh->setCompareCallback(&compare_callback_impl);
-           sh->setRollbackCallback(&rollback_callback_impl);
+           sh->setUndoCallback(&undo_callback_impl);
        }
 
        (*cmd->second)();