- Changed the wording on most error messages and diagnostics so that
  the user is not alarmed, and is somewhat guided toward a solution.
This commit is contained in:
Paul Beckingham
2010-10-06 23:29:45 -04:00
parent 4028a2fce4
commit 74dcdd897a
11 changed files with 118 additions and 123 deletions

View File

@@ -1106,7 +1106,7 @@ void TDB::merge (const std::string& mergeFile)
// file has to contain at least one entry // file has to contain at least one entry
if (r.size () < 3) if (r.size () < 3)
throw std::string ("There are no transactions to apply."); throw std::string ("There are no changes to merge.");
// load undo file (left/local branch) // load undo file (left/local branch)
Directory location (context.config.get ("data.location")); Directory location (context.config.get ("data.location"));
@@ -1125,7 +1125,7 @@ void TDB::merge (const std::string& mergeFile)
if (rit != r.end()) if (rit != r.end())
rline = *rit; rline = *rit;
if (lit != l.end()) if (lit != l.end())
lline = *lit; lline = *lit;
/////////////////////////////////////// ///////////////////////////////////////
@@ -1198,7 +1198,7 @@ void TDB::merge (const std::string& mergeFile)
{ {
if ( (*lmod_it).isNew ()) if ( (*lmod_it).isNew ())
{ {
std::cout << "Skipping the new local task " std::cout << "Skipping new local task "
<< (*lmod_it).getUuid() << (*lmod_it).getUuid()
<< "\n"; << "\n";
@@ -1222,7 +1222,7 @@ void TDB::merge (const std::string& mergeFile)
// new uuid? // new uuid?
if (tmod.isNew ()) if (tmod.isNew ())
{ {
std::cout << "Adding the new remote task " std::cout << "Adding new remote task "
<< tmod.getUuid() << tmod.getUuid()
<< "\n"; << "\n";
uuid_new.insert (tmod.getUuid ()); uuid_new.insert (tmod.getUuid ());
@@ -1357,12 +1357,12 @@ void TDB::merge (const std::string& mergeFile)
{ {
// nothing happend on the remote branch // nothing happend on the remote branch
// local branch is up-to-date // local branch is up-to-date
throw std::string ("Database is already up-to-date."); throw std::string ("Database is up to date.");
} }
else // lit == undo.end () else // lit == undo.end ()
{ {
// nothing happend on the local branch // nothing happend on the local branch
std::cout << "No changes were made on the local database. Appending changes...\n"; std::cout << "No changes were made on the local database. Adding remote changes...\n";
// add remaining lines (remote branch) to the list of modifications // add remaining lines (remote branch) to the list of modifications
readTaskmods (r, rit, mods); readTaskmods (r, rit, mods);
@@ -1515,9 +1515,9 @@ void TDB::merge (const std::string& mergeFile)
} }
else else
{ {
std::cout << "Not adding duplicate " << uuid << "\n"; std::cout << "Skipping duplicate " << uuid << "\n";
mods.erase (current); mods.erase (current);
} }
} }
} }
@@ -1546,7 +1546,7 @@ void TDB::merge (const std::string& mergeFile)
} }
else // nothing to be done else // nothing to be done
{ {
std::cout << "Nothing to be done.\n"; std::cout << "No merge required.\n";
} }
// delete objects // delete objects

View File

@@ -61,8 +61,8 @@ Transport* Transport::getTransport(const Uri& uri)
|| (uri.protocol == "https") || (uri.protocol == "https")
|| (uri.protocol == "ftp") ) || (uri.protocol == "ftp") )
{ {
return new TransportCurl(uri); return new TransportCurl(uri);
} }
return NULL; return NULL;
} }
@@ -95,7 +95,7 @@ int Transport::execute()
argv[1] = opt; // -c argv[1] = opt; // -c
argv[2] = (char*)cmdline.c_str(); // e.g. scp undo.data user@host:.task/ argv[2] = (char*)cmdline.c_str(); // e.g. scp undo.data user@host:.task/
argv[3] = NULL; // required by execv argv[3] = NULL; // required by execv
int ret = execvp("sh", argv); int ret = execvp("sh", argv);
delete[] argv; delete[] argv;

View File

@@ -35,19 +35,19 @@ class Transport {
public: public:
Transport (const Uri&); Transport (const Uri&);
~Transport (); ~Transport ();
static Transport* getTransport(const Uri&); static Transport* getTransport(const Uri&);
virtual void send (const std::string&) = 0; virtual void send (const std::string&) = 0;
virtual void recv (std::string) = 0; virtual void recv (std::string) = 0;
static bool is_directory(const std::string&); static bool is_directory(const std::string&);
static bool is_filelist(const std::string&); static bool is_filelist(const std::string&);
protected: protected:
std::string executable; std::string executable;
std::vector<std::string> arguments; std::vector<std::string> arguments;
Uri uri; Uri uri;
int execute(); int execute();

View File

@@ -38,33 +38,32 @@ TransportCurl::TransportCurl(const Uri& uri) : Transport(uri)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportCurl::send(const std::string& source) void TransportCurl::send(const std::string& source)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'curl' protocol, the uri must contain a hostname.");
}
if (is_filelist(source)) if (is_filelist(source))
{ {
std::string::size_type pos; std::string::size_type pos;
pos = source.find("{"); pos = source.find("{");
if (pos == std::string::npos) if (pos == std::string::npos)
throw std::string ("Curl does not support wildcards!"); throw std::string ("When using the 'curl' protocol, wildcards are not supported.");
if (!uri.is_directory()) if (!uri.is_directory())
throw std::string ("'" + uri.path + "' is not a directory!"); throw std::string ("The uri '"); + uri.path + "' does not appear to be a directory.";
std::string toSplit; std::string toSplit;
std::string suffix; std::string suffix;
std::string prefix; std::string prefix;
std::vector<std::string> splitted; std::vector<std::string> splitted;
prefix = source.substr (0, pos); prefix = source.substr (0, pos);
toSplit = source.substr (pos+1); toSplit = source.substr (pos+1);
pos = toSplit.find ("}"); pos = toSplit.find ("}");
suffix = toSplit.substr (pos+1); suffix = toSplit.substr (pos+1);
split (splitted, toSplit.substr(0, pos), ','); split (splitted, toSplit.substr(0, pos), ',');
foreach (file, splitted) foreach (file, splitted)
{ {
arguments.push_back ("-T"); arguments.push_back ("-T");
@@ -88,27 +87,26 @@ void TransportCurl::send(const std::string& source)
} }
if (execute()) if (execute())
throw std::string ("Failed to run curl!"); throw std::string ("Could not run curl. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportCurl::recv(std::string target) void TransportCurl::recv(std::string target)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'curl' protocol, the uri must contain a hostname.");
}
if (is_filelist(uri.path)) if (is_filelist(uri.path))
{ {
std::string::size_type pos; std::string::size_type pos;
pos = uri.path.find("{"); pos = uri.path.find("{");
if (pos == std::string::npos) if (pos == std::string::npos)
throw std::string ("Curl does not support wildcards!"); throw std::string ("When using the 'curl' protocol, wildcards are not supported.");
if (!is_directory(target)) if (!is_directory(target))
throw std::string ("'" + target + "' is not a directory!"); throw std::string ("The uri '"); + target + "' does not appear to be a directory.";
std::string toSplit; std::string toSplit;
std::string suffix; std::string suffix;
std::string prefix = target; std::string prefix = target;
@@ -117,7 +115,7 @@ void TransportCurl::recv(std::string target)
pos = toSplit.find ("}"); pos = toSplit.find ("}");
suffix = toSplit.substr (pos+1); suffix = toSplit.substr (pos+1);
split (splitted, toSplit.substr(0, pos), ','); split (splitted, toSplit.substr(0, pos), ',');
target = ""; target = "";
foreach (file, splitted) foreach (file, splitted)
{ {
@@ -128,7 +126,7 @@ void TransportCurl::recv(std::string target)
{ {
target = "-o " + target; target = "-o " + target;
} }
// cmd line is: curl protocol://host:port/path/to/source/file -o path/to/target/file // cmd line is: curl protocol://host:port/path/to/source/file -o path/to/target/file
if (uri.port != "") if (uri.port != "")
{ {
@@ -138,11 +136,11 @@ void TransportCurl::recv(std::string target)
{ {
arguments.push_back (uri.protocol + "://" + uri.host + "/" + uri.path); arguments.push_back (uri.protocol + "://" + uri.host + "/" + uri.path);
} }
arguments.push_back (target); arguments.push_back (target);
if (execute()) if (execute())
throw std::string ("Failed to run curl!"); throw std::string ("Could not run curl. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -32,12 +32,12 @@
class TransportCurl : public Transport { class TransportCurl : public Transport {
public: public:
TransportCurl (const Uri&); TransportCurl (const Uri&);
virtual void send (const std::string&); virtual void send (const std::string&);
virtual void recv (std::string); virtual void recv (std::string);
}; };
#endif #endif

View File

@@ -36,19 +36,18 @@ TransportRSYNC::TransportRSYNC(const Uri& uri) : Transport(uri)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportRSYNC::send(const std::string& source) void TransportRSYNC::send(const std::string& source)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'rsync' protocol, the uri must contain a hostname.");
}
// Is there more than one file to transfer? // Is there more than one file to transfer?
// Then path has to end with a '/' // Then path has to end with a '/'
if (is_filelist(source) && !uri.is_directory()) if (is_filelist(source) && !uri.is_directory())
throw std::string ("'" + uri.path + "' is not a directory!"); throw std::string ("The uri '"); + uri.path + "' does not appear to be a directory.";
// cmd line is: rsync [--port=PORT] source [user@]host::path // cmd line is: rsync [--port=PORT] source [user@]host::path
if (uri.port != "") if (uri.port != "")
{ {
arguments.push_back ("--port=" + uri.port); arguments.push_back ("--port=" + uri.port);
} }
arguments.push_back (source); arguments.push_back (source);
@@ -63,26 +62,23 @@ void TransportRSYNC::send(const std::string& source)
} }
if (execute()) if (execute())
throw std::string ("Failed to run rsync!"); throw std::string ("Could not run rsync. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportRSYNC::recv(std::string target) void TransportRSYNC::recv(std::string target)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'rsync' protocol, the uri must contain a hostname.");
}
// Is there more than one file to transfer? // Is there more than one file to transfer?
// Then target has to end with a '/' // Then target has to end with a '/'
if (is_filelist(uri.path) && !is_directory(target)) if (is_filelist(uri.path) && !is_directory(target))
throw std::string ("'" + target + "' is not a directory!"); throw std::string ("The uri '"); + target + "' does not appear to be a directory.";
// cmd line is: rsync [--port=PORT] [user@]host::path target // cmd line is: rsync [--port=PORT] [user@]host::path target
if (uri.port != "") if (uri.port != "")
{ arguments.push_back ("--port=" + uri.port);
arguments.push_back ("--port=" + uri.port);
}
if (uri.user != "") if (uri.user != "")
{ {
@@ -96,7 +92,7 @@ void TransportRSYNC::recv(std::string target)
arguments.push_back (target); arguments.push_back (target);
if (execute()) if (execute())
throw std::string ("Failed to run rsync!"); throw std::string ("Could not run rsync. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -32,11 +32,11 @@
class TransportRSYNC : public Transport { class TransportRSYNC : public Transport {
public: public:
TransportRSYNC (const Uri&); TransportRSYNC (const Uri&);
virtual void send (const std::string&); virtual void send (const std::string&);
virtual void recv (std::string); virtual void recv (std::string);
}; };
#endif #endif

View File

@@ -36,14 +36,13 @@ TransportSSH::TransportSSH(const Uri& uri) : Transport(uri)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportSSH::send(const std::string& source) void TransportSSH::send(const std::string& source)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'ssh' protocol, the uri must contain a hostname.");
}
// Is there more than one file to transfer? // Is there more than one file to transfer?
// Then path has to end with a '/' // Then path has to end with a '/'
if (is_filelist(source) && !uri.is_directory()) if (is_filelist(source) && !uri.is_directory())
throw std::string ("'" + uri.path + "' is not a directory!"); throw std::string ("The uri '"); + uri.path + "' does not appear to be a directory.";
// cmd line is: scp [-p port] [user@]host:path // cmd line is: scp [-p port] [user@]host:path
if (uri.port != "") if (uri.port != "")
@@ -64,20 +63,19 @@ void TransportSSH::send(const std::string& source)
} }
if (execute()) if (execute())
throw std::string ("Failed to run scp!"); throw std::string ("Could not run scp. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TransportSSH::recv(std::string target) void TransportSSH::recv(std::string target)
{ {
if (uri.host == "") { if (uri.host == "")
throw std::string ("Hostname is empty"); throw std::string ("when using the 'ssh' protocol, the uri must contain a hostname.");
}
// Is there more than one file to transfer? // Is there more than one file to transfer?
// Then target has to end with a '/' // Then target has to end with a '/'
if (is_filelist(uri.path) && !is_directory(target)) if (is_filelist(uri.path) && !is_directory(target))
throw std::string ("'" + target + "' is not a directory!"); throw std::string ("The uri '"); + target + "' does not appear to be a directory.";
// cmd line is: scp [-p port] [user@]host:path // cmd line is: scp [-p port] [user@]host:path
if (uri.port != "") if (uri.port != "")
@@ -98,7 +96,7 @@ void TransportSSH::recv(std::string target)
arguments.push_back (target); arguments.push_back (target);
if (execute()) if (execute())
throw std::string ("Failed to run scp!"); throw std::string ("Could not run scp. Is it installed, and available in $PATH?");
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -32,11 +32,11 @@
class TransportSSH : public Transport { class TransportSSH : public Transport {
public: public:
TransportSSH (const Uri&); TransportSSH (const Uri&);
virtual void send (const std::string&); virtual void send (const std::string&);
virtual void recv (std::string); virtual void recv (std::string);
}; };
#endif #endif

View File

@@ -157,20 +157,20 @@ bool Uri::expand (const std::string& configPrefix )
if (data.length ()) if (data.length ())
{ {
// try to replace argument with uri from config // try to replace argument with uri from config
tmp = context.config.get (configPrefix + "." + data + ".uri"); tmp = context.config.get (configPrefix + "." + data + ".uri");
} }
else else
{ {
// get default target from config // get default target from config
tmp = context.config.get (configPrefix + ".default.uri"); tmp = context.config.get (configPrefix + ".default.uri");
} }
if (tmp != "") if (tmp != "")
{ {
data = tmp; data = tmp;
return true; return true;
} }
return false; return false;
} }
@@ -181,8 +181,8 @@ void Uri::parse ()
{ {
path = data; path = data;
return; return;
} }
std::string::size_type pos; std::string::size_type pos;
std::string uripart; std::string uripart;
std::string pathDelimiter = "/"; std::string pathDelimiter = "/";
@@ -213,7 +213,7 @@ void Uri::parse ()
} }
else else
{ {
throw std::string ("Could not parse \""+data+"\""); throw std::string ("The uri '") + data + "' is not in the expected format.";
} }
// parse host // parse host
@@ -232,4 +232,4 @@ void Uri::parse ()
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -585,13 +585,13 @@ void handleMerge (std::string& outs)
std::string file = trim (context.task.get ("description")); std::string file = trim (context.task.get ("description"));
std::string pushfile = ""; std::string pushfile = "";
std::string tmpfile = ""; std::string tmpfile = "";
std::string sAutopush = context.config.get ("merge.autopush"); std::string sAutopush = lowerCase (context.config.get ("merge.autopush"));
bool bAutopush = context.config.getBoolean("merge.autopush"); bool bAutopush = context.config.getBoolean ("merge.autopush");
Uri uri (file, "merge"); Uri uri (file, "merge");
uri.parse(); uri.parse();
if (sAutopush == "ask") if (sAutopush == "ask")
{ {
// expand uri // expand uri
@@ -602,7 +602,7 @@ void handleMerge (std::string& outs)
if (uri.data.length ()) if (uri.data.length ())
{ {
Directory location (context.config.get ("data.location")); Directory location (context.config.get ("data.location"));
// be sure that uri points to a file // be sure that uri points to a file
uri.append("undo.data"); uri.append("undo.data");
@@ -625,20 +625,19 @@ void handleMerge (std::string& outs)
context.hooks.trigger ("post-merge-command"); context.hooks.trigger ("post-merge-command");
if (tmpfile != "") if (tmpfile != "")
{
remove (tmpfile.c_str()); remove (tmpfile.c_str());
}
if ( ((sAutopush == "ask") && (confirm ("Would you like to push the changes to \'" + pushfile + "\'?")) )
if ( ((sAutopush == "ask") && (confirm ("Do you want to push the changes to \'" + pushfile + "\'?")) )
|| (bAutopush) ) || (bAutopush) )
{ {
std::string out; std::string out;
handlePush(out); handlePush(out);
} }
} }
else else
throw std::string ("You must specify a file to merge."); throw std::string ("No uri was specified for the merge. Either specify "
"the uri of a remote .task directory, or create a "
"'merge.default.uri' entry in your .taskrc file.");
} }
} }
@@ -648,7 +647,7 @@ void handlePush (std::string& outs)
if (context.hooks.trigger ("pre-push-command")) if (context.hooks.trigger ("pre-push-command"))
{ {
std::string file = trim (context.task.get ("description")); std::string file = trim (context.task.get ("description"));
Uri uri (file, "push"); Uri uri (file, "push");
uri.parse (); uri.parse ();
@@ -658,7 +657,7 @@ void handlePush (std::string& outs)
Transport* transport; Transport* transport;
if ((transport = Transport::getTransport (uri)) != NULL ) if ((transport = Transport::getTransport (uri)) != NULL )
{ {
transport->send (location.data + "/{pending,undo,completed}.data"); transport->send (location.data + "/{pending,undo,completed}.data");
delete transport; delete transport;
} }
@@ -666,25 +665,27 @@ void handlePush (std::string& outs)
{ {
// copy files locally // copy files locally
if (!uri.is_directory()) if (!uri.is_directory())
throw std::string ("'" + uri.path + "' is not a directory!"); throw std::string ("The uri '"); + uri.path + "' does not appear to be a directory.";
std::ifstream ifile1 ((location.data + "/undo.data").c_str(), std::ios_base::binary); std::ifstream ifile1 ((location.data + "/undo.data").c_str(), std::ios_base::binary);
std::ofstream ofile1 ((uri.path + "undo.data").c_str(), std::ios_base::binary); std::ofstream ofile1 ((uri.path + "undo.data").c_str(), std::ios_base::binary);
ofile1 << ifile1.rdbuf(); ofile1 << ifile1.rdbuf();
std::ifstream ifile2 ((location.data + "/pending.data").c_str(), std::ios_base::binary); std::ifstream ifile2 ((location.data + "/pending.data").c_str(), std::ios_base::binary);
std::ofstream ofile2 ((uri.path + "pending.data").c_str(), std::ios_base::binary); std::ofstream ofile2 ((uri.path + "pending.data").c_str(), std::ios_base::binary);
ofile2 << ifile2.rdbuf(); ofile2 << ifile2.rdbuf();
std::ifstream ifile3 ((location.data + "/completed.data").c_str(), std::ios_base::binary); std::ifstream ifile3 ((location.data + "/completed.data").c_str(), std::ios_base::binary);
std::ofstream ofile3 ((uri.path + "completed.data").c_str(), std::ios_base::binary); std::ofstream ofile3 ((uri.path + "completed.data").c_str(), std::ios_base::binary);
ofile3 << ifile3.rdbuf(); ofile3 << ifile3.rdbuf();
} }
context.hooks.trigger ("post-push-command"); context.hooks.trigger ("post-push-command");
} }
else else
throw std::string ("You must specify a target."); throw std::string ("No uri was specified for the push. Either specify "
"the uri of a remote .task directory, or create a "
"'push.default.uri' entry in your .taskrc file.");
} }
} }
@@ -694,7 +695,7 @@ void handlePull (std::string& outs)
if (context.hooks.trigger ("pre-pull-command")) if (context.hooks.trigger ("pre-pull-command"))
{ {
std::string file = trim (context.task.get ("description")); std::string file = trim (context.task.get ("description"));
Uri uri (file, "pull"); Uri uri (file, "pull");
uri.parse (); uri.parse ();
@@ -703,49 +704,51 @@ void handlePull (std::string& outs)
Directory location (context.config.get ("data.location")); Directory location (context.config.get ("data.location"));
if (!uri.append ("{pending,undo,completed}.data")) if (!uri.append ("{pending,undo,completed}.data"))
throw std::string ("'" + uri.path + "' is not a directory!"); throw std::string ("The uri '"); + uri.path + "' does not appear to be a directory.";
Transport* transport; Transport* transport;
if ((transport = Transport::getTransport (uri)) != NULL ) if ((transport = Transport::getTransport (uri)) != NULL)
{ {
transport->recv (location.data + "/"); transport->recv (location.data + "/");
delete transport; delete transport;
} }
else else
{ {
// copy files locally // copy files locally
// remove {pending,undo,completed}.data // remove {pending,undo,completed}.data
uri.path = uri.parent(); uri.path = uri.parent();
Path path1 (uri.path + "undo.data"); Path path1 (uri.path + "undo.data");
Path path2 (uri.path + "pending.data"); Path path2 (uri.path + "pending.data");
Path path3 (uri.path + "completed.data"); Path path3 (uri.path + "completed.data");
if (path1.exists() && path2.exists() && path3.exists()) if (path1.exists() && path2.exists() && path3.exists())
{ {
std::ofstream ofile1 ((location.data + "/undo.data").c_str(), std::ios_base::binary); std::ofstream ofile1 ((location.data + "/undo.data").c_str(), std::ios_base::binary);
std::ifstream ifile1 (path1.data.c_str() , std::ios_base::binary); std::ifstream ifile1 (path1.data.c_str() , std::ios_base::binary);
ofile1 << ifile1.rdbuf(); ofile1 << ifile1.rdbuf();
std::ofstream ofile2 ((location.data + "/pending.data").c_str(), std::ios_base::binary); std::ofstream ofile2 ((location.data + "/pending.data").c_str(), std::ios_base::binary);
std::ifstream ifile2 (path2.data.c_str() , std::ios_base::binary); std::ifstream ifile2 (path2.data.c_str() , std::ios_base::binary);
ofile2 << ifile2.rdbuf(); ofile2 << ifile2.rdbuf();
std::ofstream ofile3 ((location.data + "/completed.data").c_str(), std::ios_base::binary); std::ofstream ofile3 ((location.data + "/completed.data").c_str(), std::ios_base::binary);
std::ifstream ifile3 (path3.data.c_str() , std::ios_base::binary); std::ifstream ifile3 (path3.data.c_str() , std::ios_base::binary);
ofile3 << ifile3.rdbuf(); ofile3 << ifile3.rdbuf();
} }
else else
{ {
throw std::string ("At least one of the database files in '" + uri.path + "' is not present."); throw std::string ("At least one of the database files in '" + uri.path + "' is not present.");
} }
} }
context.hooks.trigger ("post-pull-command"); context.hooks.trigger ("post-pull-command");
} }
else else
throw std::string ("You must specify a target."); throw std::string ("No uri was specified for the pull. Either specify "
"the uri of a remote .task directory, or create a "
"'pull.default.uri' entry in your .taskrc file.");
} }
} }