virtual bool Run(P4Task& task, const CommandArgs& args)
	{		
		// Old version used "changes -l -s submitted ...#>have" but that does not include submitted
		// changelist only containing files not already synced to current workspace ie. newly added
		// files from another client.
		// Instead do as the p4v tool does:
		// Run a fstat on entire workspace and get headChange for all files. This will give the
		// most recent changelist available.
		// On the same fstate get the headRev and haveRev revision for each file. If they are not equal 
		// then record the changelist id. 
		ClearStatus();
		m_Changelists.clear();

		Conn().Log().Info() << args[0] << "::Run()" << Endl;
		
		// const std::string cmd = string("fstat -T \"depotFile headChange haveRev headRev headAction action\" //depot/...");
		string rootPathWildcard = TrimEnd(TrimEnd(task.GetProjectPath(), '/'), '\\') + "/...";
		const std::string cmd = string("fstat -T \"depotFile headChange haveRev headRev headAction action\" \"") + rootPathWildcard + "\"";
		
		Conn().BeginList();
		
		if (!task.CommandRun(cmd, this))
		{
			// The OutputStat and other callbacks will now output to stdout.
			// We just wrap up the communication here.
			m_Changelists.clear();
			Conn().EndList();
			Conn() << GetStatus();
			Conn().EndResponse();
			return true;
		}
		
		// Fetch the descriptions for the incoming changelists.
		// @TODO: could save some roundtrips by make only one changes call for each sequence of 
		//        changelist ids.
		stringstream ss;
		for (set<int>::const_iterator i = m_Changelists.begin(); i != m_Changelists.end(); ++i) 
		{
			ss.str("");
			ss << "changes -l -s submitted \"@" << *i << ",@" << *i << "\"";
			Conn().Log().Info() << "    " << ss.str() << Endl;
			
			if (!task.CommandRun(ss.str(), this))
			{
				break;
			}
		}
		
		m_Changelists.clear();
		
		// The OutputState and other callbacks will now output to stdout.
		// We just wrap up the communication here.
		Conn().EndList();
		Conn() << GetStatus();
		Conn().EndResponse();
		
		return true;
	}
bool P4FileSetBaseCommand::Run(P4Task& task, const CommandArgs& args)
{
    ClearStatus();
    Conn().Log().Info() << args[0] << "::Run()" << Endl;
    
    string cmd = SetupCommand(args);
    
    VersionedAssetList assetList;
    Conn() >> assetList;
    string paths = ResolvePaths(assetList, GetResolvePathFlags());
    
    Conn().Log().Debug() << "Paths resolved are: " << paths << Endl;
    
    if (paths.empty())
    {
        Conn().WarnLine("No paths in fileset perforce command", MARemote);
        Conn().EndResponse();
        return true;
    }
    
    cmd += " " + paths;
    
    task.CommandRun(cmd, this);
    Conn() << GetStatus();

    // Stat the files to get the most recent state.
    // This could probably be optimized by reading the output of the specific
    // commands and figure out the new state. 
    RunAndSendStatus(task, assetList);    
    
    // The OutputState and other callbacks will now output to stdout.
    // We just wrap up the communication here.
    Conn().EndResponse();
    return true;
}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{				
		ClearStatus();
		Conn().Log().Info() << args[0] << "::Run()" << Endl;
		const string cmd = string("changes -s pending -u ") + Quote(task.GetP4User()) + " -c " + Quote(task.GetP4Client());

		Conn().BeginList();
		
		// The default list is always there
		Changelist defaultItem;
		const char * kDefaultList = "default";		
		defaultItem.SetDescription(kDefaultList);
		defaultItem.SetRevision(kDefaultListRevision);
		
		Conn() << defaultItem;
		
		task.CommandRun(cmd, this);
		Conn().EndList();
		Conn() << GetStatus();

		// The OutputState and other callbacks will now output to stdout.
		// We just wrap up the communication here.
		Conn().EndResponse();
		
		return true;
	}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{
		if (!task.IsConnected()) // Cannot login without being connected
		{
			Conn().Log().Info() << "Cannot login when not connected" << Endl;
			return false;
		}
		
		ClearStatus();
		
		m_LoggedIn = false;
		m_Password = task.GetP4Password();
		m_CheckingForLoggedIn = args.size() > 1;
		const string cmd = string("login") + (m_CheckingForLoggedIn ? string(" " ) + args[1] : string());

		if (!task.CommandRun(cmd, this) && !m_CheckingForLoggedIn)
		{
			string errorMessage = GetStatusMessage();			
			Conn().Log().Notice() << "ERROR: " << errorMessage << Endl;
		}
		
		if (m_CheckingForLoggedIn)
			Conn().Log().Debug() << "Is logged in: " << (m_LoggedIn ? "yes" : "no") << Endl;
		else
			Conn().Log().Info() << "Login " << (m_LoggedIn ? "succeeded" : "failed") << Endl;

		m_CheckingForLoggedIn = false;
		return m_LoggedIn;
	}
void P4StatusCommand::RunAndSend(P4Task& task, const VersionedAssetList& assetList, bool recursive)
{
	string paths = ResolvePaths(assetList, kPathWild | kPathSkipFolders | (recursive ? kPathRecursive : kNone) );
	
	Pipe().Log().Debug() << "Paths to stat are: " << paths << unityplugin::Endl;
	
	Pipe().BeginList();
	
	if (paths.empty())
	{
		Pipe().EndList();
		// Pipe().ErrorLine("No paths to stat", MASystem);
		return;
	}
	
	string cmd = "fstat -T \"depotFile,clientFile,action,ourLock,unresolved,headAction,otherOpen,otherLock,headRev,haveRev\" ";
	cmd += " " + paths;

	// We're sending along an asset list with an unknown size.
	task.CommandRun(cmd, this);
	
	// The OutputState and other callbacks will now output to stdout.
	// We just wrap up the communication here.
	Pipe().EndList();
}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{
		incomingAssetList.clear();
		ClearStatus();
		Pipe().Log().Info() << args[0] << "::Run()" << unityplugin::Endl;
		
		string cmd = "sync";
		
		VersionedAssetList assetList;
		Pipe() >> assetList;
		string paths = ResolvePaths(assetList, kPathWild | kPathRecursive);
		
		Pipe().Log().Debug() << "Paths resolved are: " << paths << unityplugin::Endl;
		
		if (paths.empty())
		{
			Pipe().WarnLine("No paths in getlatest perforce command", MARemote);
			Pipe().EndResponse();
			return true;
		}
		
		cmd += " " + paths;
		
		task.CommandRun(cmd, this);
		Pipe() << GetStatus();
		
		// Stat the files to get the most recent state.
		// This could probably be optimized by reading the output of the command better
		RunAndSendStatus(task, incomingAssetList);
		
		// The OutputState and other callbacks will now output to stdout.
		// We just wrap up the communication here.
		Pipe().EndResponse();
		return true;
	}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{
		ClearStatus();
		m_ProjectPath = task.GetP4Root();
		
		ChangelistRevision cl;
		upipe >> cl;
		
		upipe.BeginList();
		
		vector<string> toks;
		if (Tokenize(toks, m_ProjectPath, "/") == 0)
		{
			upipe.ErrorLine(string("Project path invalid - ") + m_ProjectPath);
			upipe.EndList();
			upipe.ErrorLine("Invalid project path", MARemote);
			upipe.EndResponse();
			return true;
		}
		
		upipe.Log() << "Project path is " << m_ProjectPath << endl;
		
		string rev = cl == kDefaultListRevision ? string("default") : cl;
		const std::string cmd = string("describe -s ") + rev;
		
		task.CommandRun(cmd, this);
		
		// The OutputState and other callbacks will now output to stdout.
		// We just wrap up the communication here.
		upipe.EndList();
		upipe << GetStatus();
		upipe.EndResponse();
		
		return true;
	}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{
		if (!task.IsConnected()) // Cannot logout without being connected
		{
			Pipe().Log().Info() << "Cannot logout when not connected" << unityplugin::Endl;
			return false;
		}
		
		ClearStatus();
		
		if (!task.CommandRun("logout", this))
		{
			string errorMessage = GetStatusMessage();			
			Pipe().Log().Notice() << errorMessage << unityplugin::Endl;
		}
		
		return true;
	}
	virtual bool Run(P4Task& task, const CommandArgs& args)
	{
		ClearStatus();
		m_ProjectPath = task.GetP4Root();
		m_Result.clear();

		ChangelistRevision cl;
		Pipe() >> cl;
		
		vector<string> toks;
		if (Tokenize(toks, m_ProjectPath, "/") == 0)
		{
			Pipe().BeginList();
			Pipe().WarnLine(string("Project path invalid - ") + m_ProjectPath);
			Pipe().EndList();
			Pipe().EndResponse();
			return true;
		}
		
		Pipe().Log().Debug() << "Project path is " << m_ProjectPath << unityplugin::Endl;
		
		string rev = cl == kDefaultListRevision ? string("default") : cl;
		const std::string cmd = string("describe -s ") + rev;
		
		task.CommandRun(cmd, this);
		
		if (!MapToLocal(task, m_Result))
		{
			// Abort since there was an error mapping files to depot path
			Pipe().BeginList();
			Pipe().WarnLine("Files couldn't be mapped in perforce view");
			Pipe().EndList();
			Pipe().EndResponse();
			return true;
		}

		Pipe() << m_Result;
		m_Result.clear();
		Pipe() << GetStatus();

		Pipe().EndResponse();
		
		return true;
	}
 virtual bool Run(P4Task& task, const CommandArgs& args)
 {
     ClearStatus();
     Conn().Log().Info() << "ChangeDescriptionCommand::Run()"  << Endl;
     
     ChangelistRevision cl;
     Conn() >> cl;
     
     const string cmd = string("change -o ") + (cl == kDefaultListRevision ? string("") : cl);
             
     task.CommandRun(cmd, this);
     Conn() << GetStatus();
     
     // The OutputState and other callbacks will now output to stdout.
     // We just wrap up the communication here.
     Conn().EndResponse();
     
     return true;
 }
    virtual bool Run(P4Task& task, const CommandArgs& args)
    {
        ClearStatus();
        Conn().Log().Info() << args[0] << "::Run()" << Endl;
        
        bool noLocalFileMove = args.size() > 1 && args[1] == "noLocalFileMove";

        VersionedAssetList assetList;
        Conn() >> assetList;
        
        if ( assetList.empty() ) 
        {
            Conn().EndResponse();
            return true;
        }
        
        // Process two assets at a time ie. src,dest
        if ( assetList.size() % 2 ) 
        {
            Conn().WarnLine("uneven number of assets during move", MASystem);
            Conn().EndResponse();
            return true;
        }

        VersionedAssetList::const_iterator b = assetList.begin();
        VersionedAssetList::const_iterator e = b;
        
        // Split into two steps. 1st make everything editable and 2nd do the move.
        // this makes changes more atomic.
        while (b != assetList.end())
        {
            e += 2;
            const VersionedAsset& src = *b;
            
            string paths = ResolvePaths(b, e, kPathWild | kPathRecursive);
            
            Conn().Log().Debug() << "Ensure editable source " << paths << Endl;
            
            string err;
            bool editable = (src.GetState() & (kCheckedOutLocal | kAddedLocal | kLockedLocal)) != 0;
            
            if (!editable)
            {
                string srcPath = ResolvePaths(b, b+1, kPathWild | kPathRecursive);
                Conn().Log().Info() << "edit " << srcPath << Endl;
                if (!task.CommandRun("edit " + srcPath, this))
                {
                    break;
                }
            }
            b = e;
        }

        b = assetList.begin();
        e = b;

        VersionedAssetList targetAssetList;
        string noLocalFileMoveFlag = noLocalFileMove ? " -k " : "";
        while (!HasErrors() && b != assetList.end())
        {
            e += 2;
            
            const VersionedAsset& src = *b;
            const VersionedAsset& dest = *(b+1);

            targetAssetList.push_back(dest);
            
            string paths = ResolvePaths(b, e, kPathWild | kPathRecursive);
            
            Conn().Log().Info() << "move " << noLocalFileMoveFlag << paths << Endl;
            if (!task.CommandRun("move " + noLocalFileMoveFlag + paths, this))
            {
                break;
            }
            
            // Make the actual file system move if perforce didn't do it ie. in
            // the case of an empty folder rename or a non versioned asset/folder move/rename
            if (!PathExists(dest.GetPath()))
            {
                // Move the file
                if (!MoveAFile(src.GetPath(), dest.GetPath()))
                {
                    string errorMessage = "Error moving file ";
                    errorMessage += src.GetPath();
                    errorMessage += " to ";
                    errorMessage += dest.GetPath();
                    Conn().WarnLine(errorMessage);
                }
            }

            // Delete move folder src since perforce leaves around empty folders.
            // This only works because unity will not send embedded moves.
            if (src.IsFolder() && IsDirectory(src.GetPath()))
            {
                DeleteRecursive(src.GetPath());
            }

            b = e;
        }
        
        // We just wrap up the communication here.
        Conn() << GetStatus();
        
        RunAndSendStatus(task, targetAssetList);
        
        Conn().EndResponse();

        return true;
    }