diff --git a/ChangeLog b/ChangeLog index c7d9e8e5c..c198abdfd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,12 @@ ------ current release --------------------------- -2.2.0 () +2.1.2 (2012-09-18) 1e3176ed70d2b50faf03838d0df279b2a4ae93b2 + +Bugs + + Bug fix release regarding #1104, which causes duplicate UUIDs during + the merge command. + + Fixed bug where shadow files are not properly created when there is a missing + .taskrc file (thanks to Pietro Cerutti). Features + Added Feature #1069, which gives a clearer error when a UDA diff --git a/NEWS b/NEWS index f98a6233e..5cb1084ab 100644 --- a/NEWS +++ b/NEWS @@ -6,20 +6,17 @@ New Features in taskwarrior 2.2.0 - Deprectated 'fg' and 'bg' attributes removed. Any residual use of those will appear as orphaned UDAs. - Please refer to the ChangeLog file for full details. There are too many to - list here. - New commands in taskwarrior 2.2.0 - New '_aliases' helper command lists aliases for completion purposes. New configuration options in taskwarrior 2.2.0 - - + - Newly deprecated features in taskwarrior 2.2.0 - - - None + + - --- diff --git a/doc/man/task-color.5.in b/doc/man/task-color.5.in index a913ca640..f80ea80f1 100644 --- a/doc/man/task-color.5.in +++ b/doc/man/task-color.5.in @@ -1,4 +1,4 @@ -.TH task-color 5 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH task-color 5 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME task-color \- A color tutorial for the taskwarrior command line todo manager. diff --git a/doc/man/task-faq.5.in b/doc/man/task-faq.5.in index b3a02adcb..d27440b57 100644 --- a/doc/man/task-faq.5.in +++ b/doc/man/task-faq.5.in @@ -1,4 +1,4 @@ -.TH task-faq 5 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH task-faq 5 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME task-faq \- A FAQ for the task(1) command line todo manager. diff --git a/doc/man/task-sync.5.in b/doc/man/task-sync.5.in index 458f38d3e..cb5da60e8 100644 --- a/doc/man/task-sync.5.in +++ b/doc/man/task-sync.5.in @@ -1,4 +1,4 @@ -.TH task-sync 5 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH task-sync 5 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME task-sync \- A tutorial for the task(1) data synchronization capabilities. diff --git a/doc/man/task-tutorial.5.in b/doc/man/task-tutorial.5.in index 9feecfd11..fc6a0c657 100644 --- a/doc/man/task-tutorial.5.in +++ b/doc/man/task-tutorial.5.in @@ -1,4 +1,4 @@ -.TH task-tutorial 5 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH task-tutorial 5 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME task-tutorial \- A tutorial for the task(1) command line todo manager. diff --git a/doc/man/task.1.in b/doc/man/task.1.in index 0ee3f5efe..5b4cb383d 100644 --- a/doc/man/task.1.in +++ b/doc/man/task.1.in @@ -1,4 +1,4 @@ -.TH task 1 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH task 1 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME task \- A command line todo manager. diff --git a/doc/man/taskrc.5.in b/doc/man/taskrc.5.in index d9c00fbf7..3038a980e 100644 --- a/doc/man/taskrc.5.in +++ b/doc/man/taskrc.5.in @@ -1,4 +1,4 @@ -.TH taskrc 5 2012-07-24 "${PACKAGE_STRING}" "User Manuals" +.TH taskrc 5 2012-09-18 "${PACKAGE_STRING}" "User Manuals" .SH NAME taskrc \- Configuration file for the task(1) command diff --git a/doc/ref/task-ref.pages b/doc/ref/task-ref.pages index 6a524f977..e6186f1cb 100644 Binary files a/doc/ref/task-ref.pages and b/doc/ref/task-ref.pages differ diff --git a/doc/ref/task-ref.pdf b/doc/ref/task-ref.pdf index 3efe819cc..d809c81ba 100644 Binary files a/doc/ref/task-ref.pdf and b/doc/ref/task-ref.pdf differ diff --git a/src/Context.cpp b/src/Context.cpp index eed43cc6d..4ef958691 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -503,6 +503,7 @@ void Context::shadow () { std::string file_name = config.get ("shadow.file"); std::string command = config.get ("shadow.command"); + std::string rcfile = rc_file; // A missing shadow file command uses the default command instead. if (command == "") @@ -534,13 +535,14 @@ void Context::shadow () // Compose the command. Put the rc overrides up front, so that they may // be overridden by rc.shadow.command. - command = program + - " rc.detection:off" + // No need to determine terminal size - " rc.color:off" + // Color off by default - " rc.gc:off " + // GC off, to reduce headaches - command + // User specified command - " >" + // Capture - shadow_file._data; // User specified file + command = program + + " rc.detection:off" + // No need to determine terminal size + " rc.color:off" + // Color off by default + " rc.gc:off " + // GC off, to reduce headaches + " rc:" + rcfile + " " + // Use specified rc file + command + // User specified command + " >" + // Capture + shadow_file._data; // User specified file debug ("Running shadow command: " + command); system (command.c_str ()); diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 3ce0d90c4..2ae5a883a 100644 --- a/src/TDB2.cpp +++ b/src/TDB2.cpp @@ -1024,8 +1024,8 @@ void TDB2::merge (const std::string& mergeFile) mods.splice (mods.begin (), rmods); DEBUG_STR ("sorting taskmod list"); - mods.sort (); - mods_history.sort (); + mods.sort (compareTaskmod); + mods_history.sort (compareTaskmod); } else if (rit == r.end ()) { @@ -1232,7 +1232,7 @@ void TDB2::merge (const std::string& mergeFile) // at this point undo contains the lines up to the branch-off point // now we merge mods (new modifications from mergefile) // with lmods (part of old undo.data) - lmods.sort(); + lmods.sort(compareTaskmod); mods.merge (lmods); mods.merge (mods_history); diff --git a/src/Taskmod.cpp b/src/Taskmod.cpp index 8ae89809e..96b9eef67 100644 --- a/src/Taskmod.cpp +++ b/src/Taskmod.cpp @@ -33,22 +33,38 @@ #include #include +unsigned long Taskmod::curSequenceNumber = 0; + +bool compareTaskmod (Taskmod first, Taskmod second) +{ + if (first._timestamp == second._timestamp) + { + return first._sequenceNumber < second._sequenceNumber; + } + else + { + return first._timestamp < second._timestamp; + } +} + //////////////////////////////////////////////////////////////////////////////// Taskmod::Taskmod () { _timestamp = 0; _bAfterSet = false; _bBeforeSet = false; + _sequenceNumber = curSequenceNumber++; } //////////////////////////////////////////////////////////////////////////////// Taskmod::Taskmod (const Taskmod& other) { - this->_before = other._before; - this->_after = other._after; - this->_timestamp = other._timestamp; - this->_bAfterSet = other._bAfterSet; - this->_bBeforeSet = other._bBeforeSet; + this->_before = other._before; + this->_after = other._after; + this->_timestamp = other._timestamp; + this->_bAfterSet = other._bAfterSet; + this->_bBeforeSet = other._bBeforeSet; + this->_sequenceNumber = other._sequenceNumber; } //////////////////////////////////////////////////////////////////////////////// @@ -87,11 +103,12 @@ Taskmod& Taskmod::operator= (const Taskmod& other) { if (this != &other) { - this->_before = other._before; - this->_after = other._after; - this->_timestamp = other._timestamp; - this->_bAfterSet = other._bAfterSet; - this->_bBeforeSet = other._bBeforeSet; + this->_before = other._before; + this->_after = other._after; + this->_timestamp = other._timestamp; + this->_bAfterSet = other._bAfterSet; + this->_bBeforeSet = other._bBeforeSet; + this->_sequenceNumber = other._sequenceNumber; } return *this; @@ -100,9 +117,10 @@ Taskmod& Taskmod::operator= (const Taskmod& other) //////////////////////////////////////////////////////////////////////////////// void Taskmod::reset (long timestamp) { - this->_bAfterSet = false; - this->_bBeforeSet = false; - this->_timestamp = timestamp; + this->_bAfterSet = false; + this->_bBeforeSet = false; + this->_timestamp = timestamp; + this->_sequenceNumber = curSequenceNumber++; } //////////////////////////////////////////////////////////////////////////////// @@ -177,6 +195,12 @@ void Taskmod::setTimestamp (long timestamp) this->_timestamp = timestamp; } +//////////////////////////////////////////////////////////////////////////////// +void Taskmod::incSequenceNumber () +{ + this->_sequenceNumber++; +} + //////////////////////////////////////////////////////////////////////////////// Task& Taskmod::getAfter () { @@ -195,6 +219,12 @@ long Taskmod::getTimestamp () const return _timestamp; } +//////////////////////////////////////////////////////////////////////////////// +unsigned long Taskmod::getSequenceNumber () const +{ + return _sequenceNumber; +} + //////////////////////////////////////////////////////////////////////////////// std::string Taskmod::getTimeStr () const { diff --git a/src/Taskmod.h b/src/Taskmod.h index 219a89f73..8e3a04513 100644 --- a/src/Taskmod.h +++ b/src/Taskmod.h @@ -33,6 +33,8 @@ #include class Taskmod { + friend bool compareTaskmod (Taskmod first, Taskmod second); + public: Taskmod (); Taskmod (const Taskmod& other); @@ -59,11 +61,13 @@ public: void setAfter (const Task& after); void setBefore (const Task& before); void setTimestamp (long timestamp); + void incSequenceNumber (); // getter Task& getAfter (); Task& getBefore (); long getTimestamp () const; + unsigned long getSequenceNumber () const; std::string getTimeStr () const; protected: @@ -72,7 +76,12 @@ protected: long _timestamp; bool _bAfterSet; bool _bBeforeSet; + unsigned long _sequenceNumber; + + static unsigned long curSequenceNumber; }; +bool compareTaskmod (Taskmod first, Taskmod second); + #endif diff --git a/test/.gitignore b/test/.gitignore index dc405b6c7..b4b29c42e 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -26,3 +26,4 @@ view.t json_test +run_all diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a54f11c2d..ca38fbbab 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -10,7 +10,17 @@ set (test_SRCS autocomplete.t color.t config.t date.t directory.t dom.t duration.t file.t i18n.t json.t list.t nibbler.t path.t rx.t t.t t2.t taskmod.t tdb2.t text.t uri.t util.t view.t json_test) -add_custom_target (test ./run_all DEPENDS ${test_SRCS} task_executable +message ("-- Configuring run_all") +set (TESTBLOB "*.t") +if (CYGWIN) +set (TESTBLOB "*.t *.t.exe") +endif (CYGWIN) +configure_file ( + ${CMAKE_SOURCE_DIR}/test/run_all.in + ${CMAKE_SOURCE_DIR}/test/run_all) + +add_custom_target (test ./run_all --verbose + DEPENDS ${test_SRCS} task_executable WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test) add_custom_target (build_tests DEPENDS ${test_SRCS} diff --git a/test/bug.1104.t b/test/bug.1104.t index 7d580d465..955d637d8 100755 --- a/test/bug.1104.t +++ b/test/bug.1104.t @@ -30,14 +30,16 @@ use strict; use warnings; use File::Copy; use File::Path; -use Test::More tests => 18; +use Test::More tests => 28; mkdir("1", 0755); mkdir("2", 0755); +mkdir("3", 0755); mkdir("dropbox", 0755); ok (-e "1", 'Created directory 1/'); ok (-e "2", 'Created directory 2/'); +ok (-e "3", 'Created directory 3/'); ok (-e "dropbox", 'Created directory dropbox/'); # Create the rc file. @@ -68,25 +70,47 @@ if (open my $fh, '>', '2.rc') ok (-r '2.rc', 'Created 2.rc'); } +# Create the rc file. +if (open my $fh, '>', '3.rc') +{ + print $fh "data.location=3/\n"; + print $fh "confirmation=no\n"; + print $fh "merge.autopush=yes\n"; + print $fh "merge.default.uri=./dropbox/\n"; + print $fh "push.default.uri=./dropbox/\n"; + print $fh "pull.default.uri=./dropbox/\n"; + + close $fh; + ok (-r '3.rc', 'Created 3.rc'); +} + # Once-only push from 1 --> dropbox my $output = qx{../src/task rc:1.rc add one 2>&1}; ok ($? == 0, 'Exit status check'); +$output = qx{../src/task rc:1.rc add two 2>&1}; +ok ($? == 0, 'Exit status check'); +$output = qx{../src/task rc:1.rc add three 2>&1}; +ok ($? == 0, 'Exit status check'); $output = qx{../src/task rc:1.rc push 2>&1}; ok ($? == 0, 'Exit status check'); -# Merges to 2 +# Merges to 2 and 3 $output = qx{../src/task rc:2.rc merge 2>&1}; ok ($? == 0, 'Exit status check'); +$output = qx{../src/task rc:3.rc merge 2>&1}; +ok ($? == 0, 'Exit status check'); # Make a different change in both locations -$output = qx{../src/task rc:1.rc add two 2>&1}; +$output = qx{../src/task rc:1.rc add four 2>&1}; ok ($? == 0, 'Exit status check'); -$output = qx{../src/task rc:1.rc two done 2>&1}; +$output = qx{../src/task rc:1.rc four done 2>&1}; ok ($? == 0, 'Exit status check'); -$output = qx{../src/task rc:2.rc 1 done 2>&1}; +$output = qx{../src/task rc:2.rc one done 2>&1}; +ok ($? == 0, 'Exit status check'); +$output = qx{../src/task rc:3.rc three delete 2>&1}; ok ($? == 0, 'Exit status check'); -# Merges everywhere +# Merges 1 and 2 $output = qx{../src/task rc:1.rc merge 2>&1}; ok ($? == 0, 'Exit status check'); $output = qx{../src/task rc:2.rc merge 2>&1}; @@ -102,8 +126,18 @@ ok ($? == 0, 'Exit status check'); $output = qx{../src/task rc:1.rc diag 2>&1}; unlike ($output, qr/Found duplicate/, "Found duplicate"); +# Merges 3 +$output = qx{../src/task rc:3.rc merge 2>&1}; +ok ($? == 0, 'Exit status check'); +unlike ($output, qr/Retaining/, "Must not retain changes"); + +# Merges 1 +$output = qx{../src/task rc:1.rc merge 2>&1}; +ok ($? == 0, 'Exit status check'); +unlike ($output, qr/Retaining/, "Must not retain changes"); + # Cleanup. -unlink qw(1.rc 1/pending.data 1/completed.data 1/undo.data 1/backlog.data 1/synch.key 2/pending.data 2/completed.data 2/undo.data 2.rc 2/backlog.data 2/synch.key dropbox/completed.data dropbox/pending.data dropbox/undo.data); +unlink qw(1.rc 1/pending.data 1/completed.data 1/undo.data 1/backlog.data 1/synch.key 2/pending.data 2/completed.data 2/undo.data 2.rc 2/backlog.data 2/synch.key dropbox/completed.data dropbox/pending.data dropbox/undo.data 3/pending.data 3/undo.data 3/completed.data 3/backlog.data 3/synch.key 3.rc); ok (! -r '1/pending.data' && ! -r '1/completed.data' && ! -r '1/undo.data' && @@ -116,13 +150,20 @@ ok (! -r '1/pending.data' && ! -r '2/backlog.data' && ! -r '2/synch.key' && ! -r '2.rc' && + ! -r '3/pending.data' && + ! -r '3/completed.data' && + ! -r '3/undo.data' && + ! -r '3/backlog.data' && + ! -r '3/synch.key' && + ! -r '3.rc' && ! -r 'dropbox/pending.data' && ! -r 'dropbox/completed.data' && ! -r 'dropbox/undo.data' , 'Cleanup'); -rmtree (['1', '2', 'dropbox'], 0, 1); +rmtree (['1', '2', '3', 'dropbox'], 0, 1); ok (! -e '1' && ! -e '2' && + ! -e '3' && ! -e 'dropbox', 'Removed directories'); exit 0; diff --git a/test/json.t.cpp b/test/json.t.cpp index 621c4f3b8..8bfbf0fe3 100644 --- a/test/json.t.cpp +++ b/test/json.t.cpp @@ -142,37 +142,37 @@ int main (int argc, char** argv) try { // Regular unit tests. - t.is (json::encode ("1\b2"), "1\\b2", "json::encode \\b -> \\\\b"); - t.is (json::decode ("1\\b2"), "1\b2", "json::decode \\\\b -> \\b"); + t.is (json::encode ("1\b2"), "1\\b2", "json::encode slashslashb -> slashslashslashslashb"); + t.is (json::decode ("1\\b2"), "1\b2", "json::decode slashslashslashslashb -> slashslashb"); - t.is (json::encode ("1\n2"), "1\\n2", "json::encode \\n -> \\\\n"); - t.is (json::decode ("1\\n2"), "1\n2", "json::decode \\\\n -> \\n"); + t.is (json::encode ("1\n2"), "1\\n2", "json::encode slashslashn -> slashslashslashslashn"); + t.is (json::decode ("1\\n2"), "1\n2", "json::decode slashslashslashslashn -> slashslashn"); - t.is (json::encode ("1\r2"), "1\\r2", "json::encode \\r -> \\\\r"); - t.is (json::decode ("1\\r2"), "1\r2", "json::decode \\\\r -> \\r"); + t.is (json::encode ("1\r2"), "1\\r2", "json::encode slashslashr -> slashslashslashslashr"); + t.is (json::decode ("1\\r2"), "1\r2", "json::decode slashslashslashslashr -> slashslashr"); - t.is (json::encode ("1\t2"), "1\\t2", "json::encode \\t -> \\\\t"); - t.is (json::decode ("1\\t2"), "1\t2", "json::decode \\\\t -> \\t"); + t.is (json::encode ("1\t2"), "1\\t2", "json::encode slashslasht -> slashslashslashslasht"); + t.is (json::decode ("1\\t2"), "1\t2", "json::decode slashslashslashslasht -> slashslasht"); - t.is (json::encode ("1\\2"), "1\\\\2", "json::encode \\ -> \\\\"); - t.is (json::decode ("1\\\\2"), "1\\2", "json::decode \\\\ -> \\"); + t.is (json::encode ("1\\2"), "1\\\\2", "json::encode slashslash -> slashslashslashslash"); + t.is (json::decode ("1\\\\2"), "1\\2", "json::decode slashslashslashslash -> slashslash"); - t.is (json::encode ("1\x2"), "1\x2", "json::encode \\x -> \\x (NOP)"); - t.is (json::decode ("1\x2"), "1\x2", "json::decode \\x -> \\x (NOP)"); + t.is (json::encode ("1\x2"), "1\x2", "json::encode slashslashx -> slashslashx(NOP)"); + t.is (json::decode ("1\x2"), "1\x2", "json::decode slashslashx -> slashslashx(NOP)"); t.is (json::encode ("1€2"), "1€2", "json::encode € -> €"); - t.is (json::decode ("1\\u20ac2"), "1€2", "json::decode \\u20ac -> €"); + t.is (json::decode ("1\\u20ac2"), "1€2", "json::decode slashslashu20ac -> €"); std::string encoded = json::encode ("one\\"); - t.is (encoded, "one\\\\", "json::encode one\\\\ -> one\\\\\\\\"); - t.is ((int)encoded.length (), 5, "json::encode one\\\\ -> length 5"); - t.is (encoded[0], 'o', "json::encode one\\\\[0] -> o"); - t.is (encoded[1], 'n', "json::encode one\\\\[1] -> n"); - t.is (encoded[2], 'e', "json::encode one\\\\[2] -> e"); - t.is (encoded[3], '\\', "json::encode one\\\\[3] -> \\"); - t.is (encoded[4], '\\', "json::encode one\\\\[4] -> \\"); + t.is (encoded, "one\\\\", "json::encode oneslashslashslashslash -> oneslashslashslashslashslashslashslashslash"); + t.is ((int)encoded.length (), 5, "json::encode oneslashslashslashslash -> length 5"); + t.is (encoded[0], 'o', "json::encode oneslashslashslashslash[0] -> o"); + t.is (encoded[1], 'n', "json::encode oneslashslashslashslash[1] -> n"); + t.is (encoded[2], 'e', "json::encode oneslashslashslashslash[2] -> e"); + t.is (encoded[3], '\\', "json::encode oneslashslashslashslash[3] -> slashslash"); + t.is (encoded[4], '\\', "json::encode oneslashslashslashslash[4] -> slashslash"); - t.is (json::decode (encoded), "one\\", "json::decode one\\\\\\\\ -> one\\\\"); + t.is (json::decode (encoded), "one\\", "json::decode oneslashslashslashslashslashslashslashslash -> oneslashslashslashslash"); } catch (const std::string& e) {t.diag (e);} diff --git a/test/run_all b/test/run_all.in similarity index 66% rename from test/run_all rename to test/run_all.in index 270fe6dda..20d178427 100755 --- a/test/run_all +++ b/test/run_all.in @@ -2,7 +2,7 @@ if [ x"$1" = x"--verbose" ]; then - for i in *.t *.t.exe + for i in ${TESTBLOB} do echo '#' $i ./$i > test.log 2>&1 @@ -22,26 +22,26 @@ else VRAMSTEG=`which vramsteg` BAR=0 if [ -x "$VRAMSTEG" ]; then - BAR=1 - COUNT=0 - TOTAL=`ls *.t | wc -l` - START=`$VRAMSTEG --now` + BAR=1 + COUNT=0 + TOTAL=`ls ${TESTBLOB} | wc -l` + START=`$VRAMSTEG --now` fi - for i in *.t *.t.exe + for i in ${TESTBLOB} do - echo '#' $i >>all.log + echo '#' $i >>all.log - if [ $BAR -eq 1 ]; then - $VRAMSTEG --label 'All tests' --min 0 --max $TOTAL --current $COUNT --percentage --start $START --estimate - COUNT=`expr $COUNT + 1` - fi + if [ $BAR -eq 1 ]; then + $VRAMSTEG --label 'All tests' --min 0 --max $TOTAL --current $COUNT --percentage --start $START --estimate + COUNT=`expr $COUNT + 1` + fi - ./$i >> all.log 2>&1 + ./$i >> all.log 2>&1 done if [ $BAR -eq 1 ]; then - $VRAMSTEG --remove + $VRAMSTEG --remove fi date >> all.log