예제 #1
0
/** Parse the array of strings results of a 'git status' command
*
* Example git status results:
M  Content/Textures/T_Perlin_Noise_M.uasset
R  Content/Textures/T_Perlin_Noise_M.uasset -> Content/Textures/T_Perlin_Noise_M2.uasset
?? Content/Materials/M_Basic_Wall.uasset
!! BasicCode.sln
*/
static void ParseStatusResults(const TArray<FString>& InFiles, const TArray<FString>& InResults, TArray<FGitSourceControlState>& OutStates)
{
	// Iterate on all files explicitely listed in the command
	for(const auto& File : InFiles)
	{
		FGitSourceControlState FileState(File);
		// Search the file in the list of status
		int32 IdxResult = InResults.IndexOfByPredicate(FGitStatusFileMatcher(File));
		if(IdxResult != INDEX_NONE)
		{
			// File found in status results; only the case for "changed" files
			FGitStatusParser StatusParser(InResults[IdxResult]);
			FileState.WorkingCopyState = StatusParser.State;
		}
		else
		{
			// File not found in status
			if(FPaths::FileExists(File))
			{
				// usually means the file is unchanged,
				FileState.WorkingCopyState = EWorkingCopyState::Unchanged;
			}
			else
			{
				// but also the case for newly created content: there is no file on disk until the content is saved for the first time
				FileState.WorkingCopyState = EWorkingCopyState::NotControlled;
			}
		}
		FileState.TimeStamp.Now();
		OutStates.Add(FileState);
	}
}
예제 #2
0
namespace chronoshare {

const std::string INIT_DATABASE = "\
                                                                        \n\
CREATE TABLE FileState (                                                \n\
    type        INTEGER NOT NULL, /* 0 - newest, 1 - oldest */          \n\
    filename    TEXT NOT NULL,                                          \n\
    version     INTEGER,                                                \n\
    directory   TEXT,                                                   \n\
    device_name BLOB NOT NULL,                                          \n\
    seq_no      INTEGER NOT NULL,                                       \n\
    file_hash   BLOB NOT NULL,                                          \n\
    file_atime  TIMESTAMP,                                              \n\
    file_mtime  TIMESTAMP,                                              \n\
    file_ctime  TIMESTAMP,                                              \n\
    file_chmod  INTEGER,                                                \n\
    file_seg_num INTEGER,                                               \n\
    is_complete INTEGER,                                               \n\
                                                                        \n\
    PRIMARY KEY (type, filename)                                        \n\
);                                                                      \n\
                                                                        \n\
CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash);   \n\
";

FileState::FileState(const boost::filesystem::path& path)
  : DbHelper(path / ".chronoshare", "file-state.db")
{
  sqlite3_exec(m_db, INIT_DATABASE.c_str(), NULL, NULL, NULL);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));
}

FileState::~FileState()
{
}

void
FileState::UpdateFile(const std::string& filename, sqlite3_int64 version, const Buffer& hash,
                      const Buffer& device_name, sqlite3_int64 seq_no, time_t atime, time_t mtime,
                      time_t ctime, int mode, int seg_num)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db, "UPDATE FileState "
                           "SET "
                           "device_name=?, seq_no=?, "
                           "version=?,"
                           "file_hash=?,"
                           "file_atime=datetime(?, 'unixepoch'),"
                           "file_mtime=datetime(?, 'unixepoch'),"
                           "file_ctime=datetime(?, 'unixepoch'),"
                           "file_chmod=?, "
                           "file_seg_num=? "
                           "WHERE type=0 AND filename=?",
                     -1, &stmt, 0);

  sqlite3_bind_blob(stmt, 1, device_name.buf(), device_name.size(), SQLITE_STATIC);
  sqlite3_bind_int64(stmt, 2, seq_no);
  sqlite3_bind_int64(stmt, 3, version);
  sqlite3_bind_blob(stmt, 4, hash.buf(), hash.size(), SQLITE_STATIC);
  sqlite3_bind_int64(stmt, 5, atime);
  sqlite3_bind_int64(stmt, 6, mtime);
  sqlite3_bind_int64(stmt, 7, ctime);
  sqlite3_bind_int(stmt, 8, mode);
  sqlite3_bind_int(stmt, 9, seg_num);
  sqlite3_bind_text(stmt, 10, filename.c_str(), -1, SQLITE_STATIC);

  sqlite3_step(stmt);

  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_ROW && sqlite3_errcode(m_db) != SQLITE_DONE,
                  sqlite3_errmsg(m_db));

  sqlite3_finalize(stmt);

  int affected_rows = sqlite3_changes(m_db);
  if (affected_rows == 0) // file didn't exist
  {
    sqlite3_stmt* stmt;
    sqlite3_prepare_v2(m_db,
                       "INSERT INTO FileState "
                       "(type,filename,version,device_name,seq_no,file_hash,"
                       "file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
                       "VALUES (0, ?, ?, ?, ?, ?, "
                       "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)",
                       -1, &stmt, 0);

    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

    sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 2, version);
    sqlite3_bind_blob(stmt, 3, device_name.buf(), device_name.size(), SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 4, seq_no);
    sqlite3_bind_blob(stmt, 5, hash.buf(), hash.size(), SQLITE_STATIC);
    sqlite3_bind_int64(stmt, 6, atime);
    sqlite3_bind_int64(stmt, 7, mtime);
    sqlite3_bind_int64(stmt, 8, ctime);
    sqlite3_bind_int(stmt, 9, mode);
    sqlite3_bind_int(stmt, 10, seg_num);

    sqlite3_step(stmt);
    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));
    sqlite3_finalize(stmt);

    sqlite3_prepare_v2(m_db,
                       "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?",
                       -1, &stmt, 0);
    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

    sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
    sqlite3_step(stmt);
    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));
    sqlite3_finalize(stmt);
  }
}

void
FileState::DeleteFile(const std::string& filename)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
  sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);

  _LOG_DEBUG("Delete " << filename);

  sqlite3_step(stmt);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));
  sqlite3_finalize(stmt);
}

void
FileState::SetFileComplete(const std::string& filename)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db, "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1,
                     &stmt, 0);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));
  sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);

  sqlite3_step(stmt);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));

  sqlite3_finalize(stmt);
}

/**
 * @todo Implement checking modification time and permissions
 */
FileItemPtr
FileState::LookupFile(const std::string& filename)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db,
                     "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
                     "       FROM FileState "
                     "       WHERE type = 0 AND filename = ?",
                     -1, &stmt, 0);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));
  sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);

  FileItemPtr retval;
  if (sqlite3_step(stmt) == SQLITE_ROW) {
    retval = make_shared<FileItem>();
    retval->set_filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
                         sqlite3_column_bytes(stmt, 0));
    retval->set_version(sqlite3_column_int64(stmt, 1));
    retval->set_device_name(sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
    retval->set_seq_no(sqlite3_column_int64(stmt, 3));
    retval->set_file_hash(sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
    retval->set_mtime(sqlite3_column_int(stmt, 5));
    retval->set_mode(sqlite3_column_int(stmt, 6));
    retval->set_seg_num(sqlite3_column_int64(stmt, 7));
    retval->set_is_complete(sqlite3_column_int(stmt, 8));
  }
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));
  sqlite3_finalize(stmt);

  return retval;
}

FileItemsPtr
FileState::LookupFilesForHash(const Buffer& hash)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db,
                     "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
                     "   FROM FileState "
                     "   WHERE type = 0 AND file_hash = ?",
                     -1, &stmt, 0);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));
  sqlite3_bind_blob(stmt, 1, hash.buf(), hash.size(), SQLITE_STATIC);
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

  FileItemsPtr retval = make_shared<FileItems>();
  while (sqlite3_step(stmt) == SQLITE_ROW) {
    FileItem file;
    file.set_filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
                      sqlite3_column_bytes(stmt, 0));
    file.set_version(sqlite3_column_int64(stmt, 1));
    file.set_device_name(sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
    file.set_seq_no(sqlite3_column_int64(stmt, 3));
    file.set_file_hash(sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
    file.set_mtime(sqlite3_column_int(stmt, 5));
    file.set_mode(sqlite3_column_int(stmt, 6));
    file.set_seg_num(sqlite3_column_int64(stmt, 7));
    file.set_is_complete(sqlite3_column_int(stmt, 8));

    retval->push_back(file);
  }
  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));

  sqlite3_finalize(stmt);

  return retval;
}

void
FileState::LookupFilesInFolder(const function<void(const FileItem&)>& visitor,
                               const std::string& folder, int offset /*=0*/, int limit /*=-1*/)
{
  sqlite3_stmt* stmt;
  sqlite3_prepare_v2(m_db,
                     "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
                     "   FROM FileState "
                     "   WHERE type = 0 AND directory = ?"
                     "   LIMIT ? OFFSET ?",
                     -1, &stmt, 0);
  if (folder.size() == 0)
    sqlite3_bind_null(stmt, 1);
  else
    sqlite3_bind_text(stmt, 1, folder.c_str(), folder.size(), SQLITE_STATIC);

  sqlite3_bind_int(stmt, 2, limit);
  sqlite3_bind_int(stmt, 3, offset);

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    FileItem file;
    file.set_filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
                      sqlite3_column_bytes(stmt, 0));
    file.set_version(sqlite3_column_int64(stmt, 1));
    file.set_device_name(sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
    file.set_seq_no(sqlite3_column_int64(stmt, 3));
    file.set_file_hash(sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
    file.set_mtime(sqlite3_column_int(stmt, 5));
    file.set_mode(sqlite3_column_int(stmt, 6));
    file.set_seg_num(sqlite3_column_int64(stmt, 7));
    file.set_is_complete(sqlite3_column_int(stmt, 8));

    visitor(file);
  }

  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));

  sqlite3_finalize(stmt);
}

FileItemsPtr
FileState::LookupFilesInFolder(const std::string& folder, int offset /*=0*/, int limit /*=-1*/)
{
  FileItemsPtr retval = make_shared<FileItems>();
  LookupFilesInFolder(bind(static_cast<void (FileItems::*)(const FileItem&)>(&FileItems::push_back),
                           retval.get(), _1),
                      folder, offset, limit);

  return retval;
}

bool
FileState::LookupFilesInFolderRecursively(const function<void(const FileItem&)>& visitor,
                                          const std::string& folder, int offset /*=0*/,
                                          int limit /*=-1*/)
{
  _LOG_DEBUG("LookupFilesInFolderRecursively: [" << folder << "]");

  if (limit >= 0)
    limit++;

  sqlite3_stmt* stmt;
  if (folder != "") {
    /// @todo Do something to improve efficiency of this query. Right now it is basically scanning the whole database

    sqlite3_prepare_v2(m_db,
                       "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
                       "   FROM FileState "
                       "   WHERE type = 0 AND is_dir_prefix(?, directory)=1 "
                       "   ORDER BY filename "
                       "   LIMIT ? OFFSET ?",
                       -1, &stmt,
                       0); // there is a small ambiguity with is_prefix matching, but should be ok for now
    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

    sqlite3_bind_text(stmt, 1, folder.c_str(), folder.size(), SQLITE_STATIC);
    _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

    sqlite3_bind_int(stmt, 2, limit);
    sqlite3_bind_int(stmt, 3, offset);
  }
  else {
    sqlite3_prepare_v2(m_db,
                       "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
                       "   FROM FileState "
                       "   WHERE type = 0"
                       "   ORDER BY filename "
                       "   LIMIT ? OFFSET ?",
                       -1, &stmt, 0);
    sqlite3_bind_int(stmt, 1, limit);
    sqlite3_bind_int(stmt, 2, offset);
  }

  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_OK, sqlite3_errmsg(m_db));

  while (sqlite3_step(stmt) == SQLITE_ROW) {
    if (limit == 1)
      break;

    FileItem file;
    file.set_filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
                      sqlite3_column_bytes(stmt, 0));
    file.set_version(sqlite3_column_int64(stmt, 1));
    file.set_device_name(sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
    file.set_seq_no(sqlite3_column_int64(stmt, 3));
    file.set_file_hash(sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
    file.set_mtime(sqlite3_column_int(stmt, 5));
    file.set_mode(sqlite3_column_int(stmt, 6));
    file.set_seg_num(sqlite3_column_int64(stmt, 7));
    file.set_is_complete(sqlite3_column_int(stmt, 8));

    visitor(file);
    limit--;
  }

  _LOG_DEBUG_COND(sqlite3_errcode(m_db) != SQLITE_DONE, sqlite3_errmsg(m_db));

  sqlite3_finalize(stmt);

  return (limit == 1);
}

FileItemsPtr
FileState::LookupFilesInFolderRecursively(const std::string& folder, int offset /*=0*/,
                                          int limit /*=-1*/)
{
  FileItemsPtr retval = make_shared<FileItems>();
  LookupFilesInFolder(bind(static_cast<void (FileItems::*)(const FileItem&)>(&FileItems::push_back),
                           retval.get(), _1),
                      folder, offset, limit);

  return retval;
}

} // namespace chronoshare