Code Cleanup

- Removed deprecated 'push', 'pull' and 'merge' commands.
This commit is contained in:
Paul Beckingham
2014-01-18 19:12:48 -05:00
parent 341c2fb474
commit 8b736934d7
47 changed files with 22 additions and 3941 deletions

View File

@@ -44,22 +44,6 @@
extern Context context;
#define NDEBUG
#include <assert.h>
#include <Taskmod.h>
#define DEBUG_OUTPUT 0
#if DEBUG_OUTPUT > 0
#define DEBUG_STR(str) std::cerr << "DEBUG: " << str << "\n"; std::cerr.flush()
#define DEBUG_STR_PART(str) std::cerr << "DEBUG: " << str; std::cerr.flush()
#define DEBUG_STR_END(str) std::cerr << str << "\n"; std::cerr.flush()
#else
#define DEBUG_STR(str)
#define DEBUG_STR_PART(str)
#define DEBUG_STR_END(str)
#endif
////////////////////////////////////////////////////////////////////////////////
TF2::TF2 ()
: _read_only (false)
@@ -643,637 +627,6 @@ void TDB2::commit ()
context.timer_commit.stop ();
}
////////////////////////////////////////////////////////////////////////////////
// Helper function for TDB::merge
void readTaskmods (std::vector <std::string> &input,
std::vector <std::string>::iterator &start,
std::list<Taskmod> &list)
{
static int resourceID = 1;
std::string line;
Taskmod tmod_tmp(resourceID++);
DEBUG_STR ("reading taskmods from file: ");
for ( ; start != input.end (); ++start)
{
line = *start;
if (line.substr (0, 4) == "time")
{
std::stringstream stream (line.substr (5));
long ts;
stream >> ts;
if (stream.fail ())
throw std::string (STRING_TDB2_UNDO_TIMESTAMP);
// 'time' is the first line of a modification
// thus we will (re)set the taskmod object
tmod_tmp.reset (ts);
}
else if (line.substr (0, 3) == "old")
{
tmod_tmp.setBefore (Task (line.substr (4)));
}
else if (line.substr (0, 3) == "new")
{
tmod_tmp.setAfter (Task (line.substr (4)));
// 'new' is the last line of a modification,
// thus we can push to the list
list.push_back (tmod_tmp);
assert (tmod_tmp.isValid ());
DEBUG_STR (" taskmod complete");
}
}
DEBUG_STR ("DONE");
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::merge (const std::string& mergeFile)
{
///////////////////////////////////////
// Copyright 2010 - 2014, Johannes Schlatow.
///////////////////////////////////////
// list of modifications that we want to add to the local database
std::list<Taskmod> mods;
// list of modifications that we want to add to the local history
std::list<Taskmod> mods_history;
// list of modifications on the local database
// has to be merged with mods to create the new undo.data
std::list<Taskmod> lmods;
// will contain the NEW undo.data
std::vector <std::string> undo_lines;
///////////////////////////////////////
// initialize the files:
// load merge file (undo file of right/remote branch)
std::vector <std::string> r;
if (! File::read (mergeFile, r))
throw format (STRING_TDB2_UNREADABLE, mergeFile);
// file has to contain at least one entry
if (r.size () < 3)
throw std::string (STRING_TDB2_NO_CHANGES);
if (! undo._file.exists ())
undo._file.create ();
// load undo file (left/local branch)
std::vector <std::string> l;
if (! File::read (undo._file._data, l))
throw format (STRING_TDB2_UNREADABLE, undo._file._data);
std::string rline, lline;
std::vector <std::string>::iterator rit, lit;
// read first line
rit = r.begin ();
lit = l.begin ();
if (rit != r.end())
rline = *rit;
if (lit != l.end())
lline = *lit;
///////////////////////////////////////
// find the branch-off point:
// first mods are not equal => assuming mergeFile starts at a
// later point in time
if (lline.compare (rline) == 0) {
std::vector<std::string>::const_iterator tmp_lit = lit;
std::vector<std::string>::const_iterator tmp_rit = rit;
tmp_lit++;
tmp_rit++;
int lookahead = 1;
if (tmp_lit->substr (0, 3) == "old") {
lookahead = 2;
}
while (lookahead--) {
if (tmp_lit->compare(*tmp_rit) != 0) {
break;
}
tmp_lit++;
tmp_rit++;
}
if (lookahead == -1) {
// at this point we know that the first lines are the same
undo_lines.push_back (lline + "\n");
}
}
// Add some color.
Color colorAdded (context.config.get ("color.sync.added"));
Color colorChanged (context.config.get ("color.sync.changed"));
Color colorRejected (context.config.get ("color.sync.rejected"));
// at this point we can assume: (lline==rline) || (lit == l.end())
// thus we search for the first non-equal lines or the EOF
bool found = false;
for (std::vector<std::string>::const_iterator tmp_lit = lit, tmp_rit = rit;
(tmp_lit != l.end ()) && (tmp_rit != r.end ());
++tmp_lit, ++tmp_rit)
{
lline = *tmp_lit;
rline = *tmp_rit;
// found first non-matching lines?
if (lline.compare (rline) != 0)
{
found = true;
break;
}
else if (tmp_lit->substr (0, 3) == "---")
{
while (lit != tmp_lit)
{
++lit;
++rit;
undo_lines.push_back(*lit + "\n");
}
// at this point, all iterators (tmp_{lit,rit}, lit and rit) are at the same line
}
}
if (!found)
{
// set iterators to r.end() or l.end() if they point to the last line
if (++rit != r.end())
--rit;
if (++lit != l.end())
--lit;
}
///////////////////////////////////////
// branch-off point found:
if (found)
{
DEBUG_STR_PART ("Branch-off point found at: ");
DEBUG_STR_END (lline);
std::list<Taskmod> rmods;
// helper lists
std::set<std::string> uuid_new, uuid_left;
// 1. read taskmods out of the remaining lines
readTaskmods (r, rit, rmods);
readTaskmods (l, lit, lmods);
// 2. move new uuids into mods
DEBUG_STR_PART ("adding new uuids (left) to skip list...");
// modifications on the left side are already in the database
// we just need them to merge conflicts, so we add the mods for
// new uuids to the skip-list 'uuid_left'
std::list<Taskmod>::iterator lmod_it;
for (lmod_it = lmods.begin (); lmod_it != lmods.end (); lmod_it++)
{
if (lmod_it->isNew ())
{
/*
// TODO Don't forget L10N.
std::cout << "New local task "
<< (context.color () ? colorAdded.colorize (lmod_it->getUuid ()) : lmod_it->getUuid ())
<< "\n";
*/
uuid_left.insert (lmod_it->getUuid ());
}
}
DEBUG_STR_END ("done");
DEBUG_STR_PART ("move new uuids (right) to redo list...");
// new items on the right side need to be inserted into the
// local database
std::list<Taskmod>::iterator rmod_it;
for (rmod_it = rmods.begin (); rmod_it != rmods.end (); )
{
// we have to save and increment the iterator because we may want to delete
// the object from the list
std::list<Taskmod>::iterator current = rmod_it++;
Taskmod tmod = *current;
if (uuid_left.find (tmod.getUuid ()) != uuid_left.end ())
{
// check whether the remote side has added a task with the same UUID
// this happens if it inserted a modification with an older timestamp
// into the undo.data and thereby moved the branch point to an earlier
// point in time. Normally this case will be solved by the merge logic,
// BUT if the UUID is considered new the merge logic will be skipped.
//
// This flaw resulted in a couple of duplication issues and bloated
// undo files (e.g. #1104).
//
// This is just a "hack" which discards all the modifications of the
// remote side to UUIDs that are considered new by both sides.
// There may be more issues with the algorithm; probably a redesign
// and proper encapsulation of the merge algorithm is due.
rmods.erase(current);
}
else if (tmod.isNew ())
{
// new uuid?
/*
// TODO Don't forget L10N.
std::cout << "Adding new remote task "
<< (context.color () ? colorAdded.colorize (tmod.getUuid ()) : tmod.getUuid ())
<< "\n";
*/
uuid_new.insert (tmod.getUuid ());
mods.push_back (tmod);
rmods.erase (current);
}
else if (uuid_new.find (tmod.getUuid ()) != uuid_new.end ())
{
// uuid of modification was new
mods.push_back (tmod);
rmods.erase (current);
}
}
DEBUG_STR_END ("done");
///////////////////////////////////////
// merge modifications:
DEBUG_STR ("Merging modifications:");
// we iterate backwards to resolve conflicts by timestamps (newest one wins)
std::list<Taskmod>::reverse_iterator lmod_rit;
std::list<Taskmod>::reverse_iterator rmod_rit;
for (lmod_rit = lmods.rbegin (); lmod_rit != lmods.rend (); ++lmod_rit)
{
Taskmod tmod_l = *lmod_rit;
std::string uuid = tmod_l.getUuid ();
DEBUG_STR (" left uuid: " + uuid);
// skip if uuid had already been merged
if (uuid_left.find (uuid) == uuid_left.end ())
{
bool rwin = false;
bool lwin = false;
for (rmod_rit = rmods.rbegin (); rmod_rit != rmods.rend (); rmod_rit++)
{
Taskmod tmod_r = *rmod_rit;
DEBUG_STR (" right uuid: " + tmod_r.getUuid ());
if (tmod_r.getUuid () == uuid)
{
DEBUG_STR (" uuid match found for " + uuid);
// we already decided to take the mods from the right side
// but we have to find the first modification newer than
// the one on the left side to merge the history too
if (rwin)
{
DEBUG_STR (" scanning right side");
if (tmod_r > tmod_l)
mods.push_front (tmod_r);
std::list<Taskmod>::iterator tmp_it = rmod_rit.base ();
rmods.erase (--tmp_it);
rmod_rit--;
}
else if (lwin)
{
DEBUG_STR (" cleaning up right side");
// add tmod_r to local history
mods_history.push_front (tmod_r);
std::list<Taskmod>::iterator tmp_it = rmod_rit.base ();
rmods.erase (--tmp_it);
rmod_rit--;
}
else
{
// which one is newer?
if (tmod_r > tmod_l)
{
std::cout << format (STRING_TDB2_REMOTE_CHANGE,
(context.color () ? colorChanged.colorize (uuid) : uuid),
cutOff (tmod_r.getBefore ().get ("description"), 10))
<< "\n";
mods.push_front(tmod_r);
// delete tmod from right side
std::list<Taskmod>::iterator tmp_it = rmod_rit.base ();
rmods.erase (--tmp_it);
rmod_rit--;
rwin = true;
}
else
{
std::cout << format (STRING_TDB2_LOCAL_CHANGE,
(context.color () ? colorChanged.colorize (uuid) : uuid),
cutOff (tmod_l.getBefore ().get ("description"), 10))
<< "\n";
// inserting right mod into history of local database
// so that it can be restored later
// AND more important: create a history that looks the same
// as if we switched the roles 'remote' and 'local'
// thus we have to find the oldest change on the local branch that is not on remote branch
std::list<Taskmod>::iterator lmod_it;
std::list<Taskmod>::iterator last = lmod_it;
for (lmod_it = lmods.begin (); lmod_it != lmods.end (); ++lmod_it) {
if ((*lmod_it).getUuid () == uuid) {
last = lmod_it;
}
}
if (tmod_l > tmod_r) { // local change is newer
last->setBefore(tmod_r.getAfter ());
// add tmod_r to local history
lmods.push_back(tmod_r);
}
else { // both mods have equal timestamps
// in this case the local branch wins as above, but the remote change with the
// same timestamp will be discarded
// find next (i.e. older) mod of this uuid on remote side
std::list<Taskmod>::reverse_iterator rmod_rit2;
for (rmod_rit2 = rmod_rit, ++rmod_rit2; rmod_rit2 != rmods.rend (); ++rmod_rit2) {
Taskmod tmp_mod = *rmod_rit2;
if (tmp_mod.getUuid () == uuid) {
last->setBefore (tmp_mod.getAfter ());
break;
}
}
}
// TODO feature: restore command? We would have to add a marker to the undo.file.
// delete tmod from right side
std::list<Taskmod>::iterator tmp_it = rmod_rit.base ();
rmods.erase (--tmp_it);
rmod_rit--;
// mark this uuid as merged
uuid_left.insert (uuid);
lwin = true;
}
}
}
} // for
if (rwin)
{
DEBUG_STR (" concat the first match to left branch");
// concat the oldest (but still newer) modification on the right
// to the endpoint on the left
mods.front ().setBefore(tmod_l.getAfter ());
}
}
} // for
DEBUG_STR ("adding non-conflicting changes from the right branch");
mods.splice (mods.begin (), rmods);
DEBUG_STR ("sorting taskmod list");
mods.sort (compareTaskmod);
mods_history.sort (compareTaskmod);
}
else if (rit == r.end ())
{
// nothing happened on the remote branch
// local branch is up-to-date
// nothing happened on the local branch either
// break, to suppress autopush
if (lit == l.end ())
{
mods.clear ();
lmods.clear ();
throw std::string (STRING_TDB2_UP_TO_DATE);
}
}
else // lit == l.end ()
{
// nothing happened on the local branch
/*
std::cout << "No local changes detected.\n";
*/
// add remaining lines (remote branch) to the list of modifications
/*
std::cout << "Remote changes detected.\n";
*/
readTaskmods (r, rit, mods);
}
///////////////////////////////////////
// Now apply the changes.
// redo command:
if (!mods.empty ())
{
std::vector <std::string> pending_lines;
std::vector <std::string> completed_lines;
if (! File::read (pending._file._data, pending_lines))
throw format (STRING_TDB2_UNREADABLE, pending._file._data);
if (! File::read (completed._file._data, completed_lines))
throw format (STRING_TDB2_UNREADABLE, completed._file._data);
// iterate over taskmod list
std::list<Taskmod>::iterator it;
for (it = mods.begin (); it != mods.end (); )
{
std::list<Taskmod>::iterator current = it++;
Taskmod tmod = *current;
// Modification to an existing task.
if (!tmod.isNew ())
{
std::string uuid = tmod.getUuid ();
Task::status statusBefore = tmod.getBefore().getStatus ();
Task::status statusAfter = tmod.getAfter().getStatus ();
std::vector <std::string>::iterator it;
bool found = false;
if ( (statusBefore == Task::completed)
|| (statusBefore == Task::deleted) )
{
// Find the same uuid in completed data
for (it = completed_lines.begin (); it != completed_lines.end (); ++it)
{
if (it->find ("uuid:\"" + uuid) != std::string::npos)
{
// Update the completed record.
/*
std::cout << "Modifying "
<< (context.color () ? colorChanged.colorize (uuid) : uuid)
<< "\n";
*/
std::string newline = tmod.getAfter ().composeF4 ();
// does the tasks move to pending data?
// this taskmod will not arise from
// normal usage of task, but those kinds of
// taskmods may be constructed to merge databases
if ( (statusAfter != Task::completed)
&& (statusAfter != Task::deleted) )
{
// insert task into pending data
pending_lines.push_back (newline);
// remove task from completed data
completed_lines.erase (it);
}
else
{
// replace the current line
*it = newline;
}
found = true;
break;
}
}
}
else
{
// Find the same uuid in the pending data.
for (it = pending_lines.begin (); it != pending_lines.end (); ++it)
{
if (it->find ("uuid:\"" + uuid) != std::string::npos)
{
// Update the pending record.
std::cout << format (STRING_TDB2_REMOTE_CHANGE,
(context.color () ? colorChanged.colorize (uuid) : uuid),
cutOff (tmod.getBefore ().get ("description"), 10))
<< "\n";
std::string newline = tmod.getAfter ().composeF4 ();
// does the tasks move to completed data
if ( (statusAfter == Task::completed)
|| (statusAfter == Task::deleted) )
{
// insert task into completed data
completed_lines.push_back (newline);
// remove task from pending data
pending_lines.erase (it);
}
else
{
// replace the current line
*it = newline;
}
found = true;
break;
}
}
}
if (!found)
{
std::cout << format (STRING_TDB2_MISSING,
(context.color () ? colorRejected.colorize (uuid) : uuid),
cutOff (tmod.getBefore ().get ("description"), 10))
<< "\n";
mods.erase (current);
}
}
else
{
// Check for dups.
std::string uuid = tmod.getAfter ().get ("uuid");
// Find the same uuid in the pending data.
bool found = false;
std::vector <std::string>::iterator pit;
for (pit = pending_lines.begin (); pit != pending_lines.end (); ++pit)
{
if (pit->find ("uuid:\"" + uuid) != std::string::npos)
{
found = true;
break;
}
}
if (!found)
{
std::cout << format (STRING_TDB2_MERGING,
(context.color () ? colorAdded.colorize (uuid) : uuid),
cutOff (tmod.getAfter ().get ("description"), 10))
<< "\n";
pending_lines.push_back (tmod.getAfter ().composeF4 ());
}
else
{
mods.erase (current);
}
}
}
// write pending file
if (! File::write (pending._file._data, pending_lines))
throw format (STRING_TDB2_UNWRITABLE, pending._file._data);
// write completed file
if (! File::write (completed._file._data, completed_lines))
throw format (STRING_TDB2_UNWRITABLE, completed._file._data);
}
if (!mods.empty() || !lmods.empty() || !mods_history.empty()) {
// 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(compareTaskmod);
mods.merge (lmods, compareTaskmod);
mods.merge (mods_history, compareTaskmod);
// generate undo.data format
std::list<Taskmod>::iterator it;
for (it = mods.begin (); it != mods.end (); it++)
undo_lines.push_back(it->toString ());
// write undo file
if (! File::write (undo._file._data, undo_lines, false))
throw format (STRING_TDB2_UNWRITABLE, undo._file._data);
}
// delete objects
lmods.clear ();
mods.clear ();
mods_history.clear ();
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::revert ()
{