fuel-scm/src/Workspace.cpp
kostas f60fbc2545 Support for the versionable .fossil-settings/ignore-glob file
FossilOrigin-Name: 511954d6d9c5a74454ffe3bc3e552b93e1cbf985
2015-08-23 13:59:07 +00:00

427 lines
11 KiB
C++

#include "Workspace.h"
#include <QCoreApplication>
#include "Utils.h"
//-----------------------------------------------------------------------------
Workspace::Workspace()
{
}
//-----------------------------------------------------------------------------
Workspace::~Workspace()
{
clearState();
remotes.clear();
}
//------------------------------------------------------------------------------
void Workspace::clearState()
{
// Dispose RepoFiles
foreach(WorkspaceFile *r, getFiles())
delete r;
getFiles().clear();
getPaths().clear();
pathState.clear();
stashMap.clear();
branchList.clear();
tags.clear();
isIntegrated = false;
}
//------------------------------------------------------------------------------
void Workspace::storeWorkspace(QSettings &store)
{
QString workspace = fossil().getWorkspacePath();
if(workspace.isEmpty())
return;
store.beginGroup("Remotes");
QString workspace_hash = HashString(QDir::toNativeSeparators(workspace));
store.beginWriteArray(workspace_hash);
int index = 0;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it, ++index)
{
store.setArrayIndex(index);
store.setValue("Name", it->name);
QUrl url = it->url;
url.setPassword("");
store.setValue("Url", url);
if(it->isDefault)
store.setValue("Default", it->isDefault);
else
store.remove("Default");
}
store.endArray();
store.endGroup();
}
//------------------------------------------------------------------------------
bool Workspace::switchWorkspace(const QString& workspace, QSettings &store)
{
// Save Remotes
storeWorkspace(store);
clearState();
remotes.clear();
fossil().setWorkspacePath("");
if(workspace.isEmpty())
return true;
QString new_workspace = QFileInfo(workspace).absoluteFilePath();
if(!QDir::setCurrent(new_workspace))
return false;
fossil().setWorkspacePath(new_workspace);
// Load Remotes
QString workspace_hash = HashString(QDir::toNativeSeparators(new_workspace));
QString gr = store.group();
store.beginGroup("Remotes");
gr = store.group();
int num_remotes = store.beginReadArray(workspace_hash);
for(int i=0; i<num_remotes; ++i)
{
store.setArrayIndex(i);
QString name = store.value("Name").toString();
QUrl url = store.value("Url").toUrl();
bool def = store.value("Default", false).toBool();
addRemote(url, name);
if(def)
setRemoteDefault(url);
}
store.endArray();
store.endGroup();
#if 0 // FIXME: Disabled this because if fossil's remote does not match exactly what we have stored (url and username), it will be automatically added every-time
// Add the default url from fossil
QUrl default_remote;
if(fossil().getRemoteUrl(default_remote) && default_remote.isValid() && !default_remote.isEmpty())
{
default_remote.setPassword("");
// Add Default remote if not available already
if(findRemote(default_remote)==NULL)
{
addRemote(default_remote, UrlToStringDisplay(default_remote));
if(getRemoteDefault().isEmpty())
setRemoteDefault(default_remote);
}
}
#endif
return true;
}
//------------------------------------------------------------------------------
bool Workspace::scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QStringList &ignorePatterns, const bool &abort, UICallback &uiCallback)
{
QDir dir(dirPath);
uiCallback.updateProcess(dirPath);
QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot);
for (int i=0; i<list.count(); ++i)
{
if(abort)
return false;
QFileInfo info = list[i];
QString filepath = info.filePath();
QString rel_path = filepath;
rel_path.remove(baseDir+PATH_SEPARATOR);
// Skip ignored files
if(!ignorePatterns.isEmpty() && QDir::match(ignorePatterns, rel_path))
continue;
if (info.isDir())
{
if(!scanDirectory(entries, filepath, baseDir, ignorePatterns, abort, uiCallback))
return false;
}
else
entries.push_back(info);
}
return true;
}
//------------------------------------------------------------------------------
static bool StringLengthDescending(const QString &l, const QString &r)
{
return l.length() > r.length();
}
//------------------------------------------------------------------------------
void Workspace::scanWorkspace(bool scanLocal, bool scanIgnored, bool scanModified, bool scanUnchanged, const QStringList &ignorePatterns, UICallback &uiCallback, bool &operationAborted)
{
// Scan all workspace files
QFileInfoList all_files;
QString wkdir = fossil().getWorkspacePath();
if(wkdir.isEmpty())
return;
// Retrieve the status of files tracked by fossil
QStringList res;
if(!fossil().listFiles(res))
return;
bool scan_files = scanLocal;
clearState();
QStringList paths;
operationAborted = false;
uiCallback.beginProcess("");
if(scan_files)
{
QCoreApplication::processEvents();
QStringList ignore;
if(!scanIgnored)
ignore = ignorePatterns;
if(!scanDirectory(all_files, wkdir, wkdir, ignore, operationAborted, uiCallback))
goto _done;
for(QFileInfoList::iterator it=all_files.begin(); it!=all_files.end(); ++it)
{
QString filename = it->fileName();
QString fullpath = it->absoluteFilePath();
// Skip fossil files
if(filename == FOSSIL_CHECKOUT1 || filename == FOSSIL_CHECKOUT2 || (!fossil().getRepositoryFile().isEmpty() && QFileInfo(fullpath) == QFileInfo(fossil().getRepositoryFile())))
continue;
WorkspaceFile::Type type = WorkspaceFile::TYPE_UNKNOWN;
WorkspaceFile *rf = new WorkspaceFile(*it, type, wkdir);
const QString &path = rf->getPath();
getFiles().insert(rf->getFilePath(), rf);
getPaths().insert(path);
// Add or merge file state into directory state
pathstate_map_t::iterator state_it = pathState.find(path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | type);
else
{
pathState.insert(path, type);
paths.append(path); // keep path in list for depth sort
}
}
}
uiCallback.endProcess();
uiCallback.beginProcess(QObject::tr("Updating..."));
// Update Files and Directories
for(QStringList::iterator line_it=res.begin(); line_it!=res.end(); ++line_it)
{
QString line = (*line_it).trimmed();
if(line.length()==0)
continue;
int space_index = line.indexOf(' ');
if(space_index==-1)
continue;
QString status_text = line.left(space_index);
QString fname = line.right(line.length() - space_index).trimmed();
WorkspaceFile::Type type = WorkspaceFile::TYPE_UNKNOWN;
// Generate a RepoFile for all non-existant fossil files
// or for all files if we skipped scanning the workspace
bool add_missing = !scan_files;
if(status_text=="EDITED")
type = WorkspaceFile::TYPE_EDITTED;
else if(status_text=="ADDED")
type = WorkspaceFile::TYPE_ADDED;
else if(status_text=="DELETED")
{
type = WorkspaceFile::TYPE_DELETED;
add_missing = true;
}
else if(status_text=="MISSING")
{
type = WorkspaceFile::TYPE_MISSING;
add_missing = true;
}
else if(status_text=="RENAMED")
type = WorkspaceFile::TYPE_RENAMED;
else if(status_text=="UNCHANGED")
type = WorkspaceFile::TYPE_UNCHANGED;
else if(status_text=="CONFLICT")
type = WorkspaceFile::TYPE_CONFLICTED;
else if(status_text=="UPDATED_BY_MERGE" || status_text=="ADDED_BY_MERGE" || status_text=="ADDED_BY_INTEGRATE" || status_text=="UPDATED_BY_INTEGRATE")
type = WorkspaceFile::TYPE_MERGED;
// Filter unwanted file types
if( ((type & WorkspaceFile::TYPE_MODIFIED) && !scanModified) ||
((type & WorkspaceFile::TYPE_UNCHANGED) && !scanUnchanged))
{
getFiles().remove(fname);
continue;
}
else
add_missing = true;
Workspace::filemap_t::iterator it = getFiles().find(fname);
WorkspaceFile *rf = 0;
if(add_missing && it==getFiles().end())
{
QFileInfo info(wkdir+QDir::separator()+fname);
rf = new WorkspaceFile(info, type, wkdir);
getFiles().insert(rf->getFilePath(), rf);
}
if(!rf)
{
it = getFiles().find(fname);
Q_ASSERT(it!=getFiles().end());
rf = *it;
}
rf->setType(type);
QString path = rf->getPath();
getPaths().insert(path);
// Add or merge file state into directory state
pathstate_map_t::iterator state_it = pathState.find(path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | type);
else
{
pathState.insert(path, type);
paths.append(path); // keep path in list for depth sort
}
}
// Sort paths, so that children (longer path) are before parents (shorter path)
std::sort(paths.begin(), paths.end(), StringLengthDescending);
foreach(const QString &p, paths)
{
pathstate_map_t::iterator state_it = pathState.find(p);
Q_ASSERT(state_it != pathState.end());
WorkspaceFile::Type state = state_it.value();
// Propagate child dir state to parents
QString parent_path = p;
while(!parent_path.isEmpty())
{
// Extract parent path
int sep_index = parent_path.lastIndexOf(PATH_SEPARATOR);
if(sep_index>=0)
parent_path = parent_path.left(sep_index);
else
parent_path = "";
// Merge path of child to parent
pathstate_map_t::iterator state_it = pathState.find(parent_path);
if(state_it != pathState.end())
state_it.value() = static_cast<WorkspaceFile::Type>(state_it.value() | state);
else
pathState.insert(parent_path, state);
}
}
// Check if the repository needs integration
res.clear();
fossil().status(res);
isIntegrated = false;
foreach(const QString &l, res)
{
if(l.trimmed().indexOf("INTEGRATE")==0)
{
isIntegrated = true;
break;
}
}
// Load the stashes, branches and tags
fossil().stashList(getStashes());
fossil().branchList(branchList, branchList);
fossil().tagList(tags);
// Fossil includes the branches in the tag list
// So remove them
foreach(const QString &name, branchList)
tags.remove(name);
_done:
uiCallback.endProcess();
}
//------------------------------------------------------------------------------
bool Workspace::addRemote(const QUrl& url, const QString& name)
{
if(remotes.contains(url))
return false;
Q_ASSERT(url.password().isEmpty());
Remote r(name, url);
remotes.insert(url, r);
return true;
}
//------------------------------------------------------------------------------
bool Workspace::removeRemote(const QUrl& url)
{
return remotes.remove(url) > 0;
}
//------------------------------------------------------------------------------
bool Workspace::setRemoteDefault(const QUrl& url)
{
Q_ASSERT(url.password().isEmpty());
bool found = false;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it)
{
if(it->url == url)
{
it->isDefault = true;
found = true;
}
else
it->isDefault = false;
}
return found;
}
//------------------------------------------------------------------------------
QUrl Workspace::getRemoteDefault() const
{
for(remote_map_t::const_iterator it=remotes.begin(); it!=remotes.end(); ++it)
{
if(it->isDefault)
return it->url;
}
return QUrl();
}
//------------------------------------------------------------------------------
Remote * Workspace::findRemote(const QUrl& url)
{
remote_map_t::iterator it = remotes.find(url);
if(it!=remotes.end())
return &(*it);
return NULL;
}