forked from qt-creator/qt-creator
Git: Colorize modified files in projects view
Fixes: QTCREATORBUG-8857 Change-Id: I9922f731cf3c7a7f25a72cbe6eab64391f4f8054 Reviewed-by: André Hartmann <aha_1980@gmx.de> Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -63,6 +63,22 @@ FilePaths IVersionControl::additionalToolsPath() const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IVersionControl::hasModification(const FilePath &path) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(path)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IVersionControl::monitorDirectory(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
Q_UNUSED(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
void IVersionControl::stopMonitoringDirectory(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
Q_UNUSED(path)
|
||||||
|
}
|
||||||
|
|
||||||
IVersionControl::RepoUrl::RepoUrl(const QString &location)
|
IVersionControl::RepoUrl::RepoUrl(const QString &location)
|
||||||
{
|
{
|
||||||
if (location.isEmpty())
|
if (location.isEmpty())
|
||||||
|
@@ -92,6 +92,22 @@ public:
|
|||||||
* Returns true is the VCS is configured to run.
|
* Returns true is the VCS is configured to run.
|
||||||
*/
|
*/
|
||||||
virtual bool isConfigured() const = 0;
|
virtual bool isConfigured() const = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns true is the file has modification compare to version control
|
||||||
|
*/
|
||||||
|
virtual bool hasModification(const Utils::FilePath &path) const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Starts monitoring modified files inside path
|
||||||
|
*/
|
||||||
|
virtual void monitorDirectory(const Utils::FilePath &path);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Stops monitoring modified files inside path
|
||||||
|
*/
|
||||||
|
virtual void stopMonitoringDirectory(const Utils::FilePath &path);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Called to query whether a VCS supports the respective operations.
|
* Called to query whether a VCS supports the respective operations.
|
||||||
*
|
*
|
||||||
@@ -207,6 +223,7 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void repositoryChanged(const Utils::FilePath &repository);
|
void repositoryChanged(const Utils::FilePath &repository);
|
||||||
void filesChanged(const QStringList &files);
|
void filesChanged(const QStringList &files);
|
||||||
|
void updateFileStatus(const Utils::FilePath &repository, const QStringList &files);
|
||||||
void configurationChanged();
|
void configurationChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -137,6 +137,17 @@ static void stage(DiffEditorController *diffController, const QString &patch, bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
|
||||||
|
static QList<FilePath> submoduleDataToAbsolutePath(const SubmoduleDataMap &submodules,
|
||||||
|
const FilePath &rootDir)
|
||||||
|
{
|
||||||
|
QList<FilePath> res;
|
||||||
|
std::transform(std::begin(submodules), std::end(submodules), std::back_inserter(res),
|
||||||
|
[rootDir](const SubmoduleData &data) { return rootDir.pathAppended(data.dir); });
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
class GitBaseDiffEditorController : public VcsBaseDiffEditorController
|
class GitBaseDiffEditorController : public VcsBaseDiffEditorController
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -822,10 +833,14 @@ GitClient &gitClient()
|
|||||||
|
|
||||||
GitClient::GitClient()
|
GitClient::GitClient()
|
||||||
: VcsBase::VcsBaseClientImpl(&Internal::settings())
|
: VcsBase::VcsBaseClientImpl(&Internal::settings())
|
||||||
|
, m_timer(new QTimer)
|
||||||
{
|
{
|
||||||
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
|
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
|
||||||
.arg(QCoreApplication::applicationFilePath())
|
.arg(QCoreApplication::applicationFilePath())
|
||||||
.arg(QCoreApplication::applicationPid());
|
.arg(QCoreApplication::applicationPid());
|
||||||
|
|
||||||
|
connect(m_timer.get(), &QTimer::timeout, this, &GitClient::updateModificationInfos);
|
||||||
|
m_timer->setInterval(10000); // 10s
|
||||||
}
|
}
|
||||||
|
|
||||||
GitClient::~GitClient() = default;
|
GitClient::~GitClient() = default;
|
||||||
@@ -899,6 +914,83 @@ FilePaths GitClient::unmanagedFiles(const FilePaths &filePaths) const
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitClient::hasModification(const Utils::FilePath &workingDirectory,
|
||||||
|
const Utils::FilePath &fileName) const
|
||||||
|
{
|
||||||
|
const ModificationInfo &info = m_modifInfos[workingDirectory];
|
||||||
|
QString fileNameFromRoot = fileName.absoluteFilePath().toString();
|
||||||
|
int length = workingDirectory.toString().size();
|
||||||
|
fileNameFromRoot.remove(0, length + 1);
|
||||||
|
return info.modifiedFiles.contains(fileNameFromRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitClient::stopMonitoring(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
const FilePath directory = path;
|
||||||
|
// Submodule management
|
||||||
|
QList<FilePath> subPaths = submoduleDataToAbsolutePath(submoduleList(directory), directory);
|
||||||
|
std::for_each(std::begin(subPaths), std::end(subPaths),
|
||||||
|
[this](const FilePath &subModule) { m_modifInfos.remove(subModule); });
|
||||||
|
m_modifInfos.remove(directory);
|
||||||
|
if (m_modifInfos.isEmpty())
|
||||||
|
m_timer->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitClient::monitorDirectory(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
const FilePath directory = path;
|
||||||
|
if (directory.isEmpty())
|
||||||
|
return;
|
||||||
|
m_modifInfos.insert(directory, {directory, {}});
|
||||||
|
// Submodule management
|
||||||
|
QList<FilePath> subPaths = submoduleDataToAbsolutePath(submoduleList(directory), directory);
|
||||||
|
std::for_each(std::begin(subPaths), std::end(subPaths), [this](const FilePath &subModule) {
|
||||||
|
m_modifInfos.insert(subModule, {subModule, {}});
|
||||||
|
});
|
||||||
|
if (!m_timer->isActive())
|
||||||
|
m_timer->start();
|
||||||
|
updateModificationInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitClient::updateModificationInfos()
|
||||||
|
{
|
||||||
|
for (ModificationInfo &info : m_modifInfos) {
|
||||||
|
const FilePath &path = info.rootPath;
|
||||||
|
const auto command = [&info](const CommandResult &result){
|
||||||
|
const QStringList res = result.cleanedStdOut().split("\n", Qt::SkipEmptyParts);
|
||||||
|
QSet<QString> modifiedFiles;
|
||||||
|
for (const QString &line : res)
|
||||||
|
{
|
||||||
|
if (line.size() <= 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QString file = line;
|
||||||
|
// can't use stateFor() function from commitdata.cpp
|
||||||
|
static const QSet<QChar> gitStates{'M', 'A'};
|
||||||
|
|
||||||
|
if (gitStates.contains(line.at(0)) || gitStates.contains(line.at(1)))
|
||||||
|
modifiedFiles.insert(file.remove(0, 3).trimmed());
|
||||||
|
}
|
||||||
|
|
||||||
|
const QSet<QString> oldfiles = info.modifiedFiles;
|
||||||
|
info.modifiedFiles = modifiedFiles;
|
||||||
|
|
||||||
|
QStringList newList = modifiedFiles.values();
|
||||||
|
QStringList list = oldfiles.values();
|
||||||
|
std::sort(std::begin(list), std::end(list));
|
||||||
|
std::sort(std::begin(newList), std::end(newList));
|
||||||
|
QStringList statusChangedFiles;
|
||||||
|
|
||||||
|
std::set_symmetric_difference(std::begin(list), std::end(list),
|
||||||
|
std::begin(newList), std::end(newList),
|
||||||
|
std::back_inserter(statusChangedFiles));
|
||||||
|
|
||||||
|
emitFileStatusChanged(info.rootPath, statusChangedFiles);
|
||||||
|
};
|
||||||
|
vcsExecWithHandler(path, {"status", "-s", "--porcelain"}, this, command, RunFlags::NoOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QTextCodec *GitClient::defaultCommitEncoding() const
|
QTextCodec *GitClient::defaultCommitEncoding() const
|
||||||
{
|
{
|
||||||
// Set default commit encoding to 'UTF-8', when it's not set,
|
// Set default commit encoding to 'UTF-8', when it's not set,
|
||||||
|
@@ -109,6 +109,12 @@ public:
|
|||||||
PushAction m_pushAction = NoPush;
|
PushAction m_pushAction = NoPush;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ModificationInfo
|
||||||
|
{
|
||||||
|
Utils::FilePath rootPath;
|
||||||
|
QSet<QString> modifiedFiles;
|
||||||
|
};
|
||||||
|
|
||||||
GitClient();
|
GitClient();
|
||||||
~GitClient();
|
~GitClient();
|
||||||
|
|
||||||
@@ -124,6 +130,10 @@ public:
|
|||||||
Utils::FilePath findGitDirForRepository(const Utils::FilePath &repositoryDir) const;
|
Utils::FilePath findGitDirForRepository(const Utils::FilePath &repositoryDir) const;
|
||||||
bool managesFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
|
bool managesFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
|
||||||
Utils::FilePaths unmanagedFiles(const Utils::FilePaths &filePaths) const;
|
Utils::FilePaths unmanagedFiles(const Utils::FilePaths &filePaths) const;
|
||||||
|
bool hasModification(const Utils::FilePath &workingDirectory,
|
||||||
|
const Utils::FilePath &fileName) const;
|
||||||
|
void monitorDirectory(const Utils::FilePath &path);
|
||||||
|
void stopMonitoring(const Utils::FilePath &path);
|
||||||
|
|
||||||
void diffFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
|
void diffFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
|
||||||
void diffFiles(const Utils::FilePath &workingDirectory,
|
void diffFiles(const Utils::FilePath &workingDirectory,
|
||||||
@@ -373,6 +383,7 @@ private:
|
|||||||
const Utils::FilePath &oldGitBinDir) const;
|
const Utils::FilePath &oldGitBinDir) const;
|
||||||
bool cleanList(const Utils::FilePath &workingDirectory, const QString &modulePath,
|
bool cleanList(const Utils::FilePath &workingDirectory, const QString &modulePath,
|
||||||
const QString &flag, QStringList *files, QString *errorMessage);
|
const QString &flag, QStringList *files, QString *errorMessage);
|
||||||
|
void updateModificationInfos();
|
||||||
|
|
||||||
enum ContinueCommandMode {
|
enum ContinueCommandMode {
|
||||||
ContinueOnly,
|
ContinueOnly,
|
||||||
@@ -390,6 +401,8 @@ private:
|
|||||||
|
|
||||||
QString m_gitQtcEditor;
|
QString m_gitQtcEditor;
|
||||||
QMap<Utils::FilePath, StashInfo> m_stashInfo;
|
QMap<Utils::FilePath, StashInfo> m_stashInfo;
|
||||||
|
QHash<Utils::FilePath, ModificationInfo> m_modifInfos;
|
||||||
|
std::unique_ptr<QTimer> m_timer;
|
||||||
QString m_diffCommit;
|
QString m_diffCommit;
|
||||||
Utils::FilePaths m_updatedSubmodules;
|
Utils::FilePaths m_updatedSubmodules;
|
||||||
bool m_disableEditor = false;
|
bool m_disableEditor = false;
|
||||||
|
@@ -155,6 +155,9 @@ public:
|
|||||||
FilePaths unmanagedFiles(const FilePaths &filePaths) const final;
|
FilePaths unmanagedFiles(const FilePaths &filePaths) const final;
|
||||||
|
|
||||||
bool isConfigured() const final;
|
bool isConfigured() const final;
|
||||||
|
bool hasModification(const Utils::FilePath &path) const final;
|
||||||
|
void monitorDirectory(const Utils::FilePath &path) final;
|
||||||
|
void stopMonitoringDirectory(const Utils::FilePath &path) final;
|
||||||
bool supportsOperation(Operation operation) const final;
|
bool supportsOperation(Operation operation) const final;
|
||||||
bool vcsOpen(const FilePath &filePath) final;
|
bool vcsOpen(const FilePath &filePath) final;
|
||||||
bool vcsAdd(const FilePath &filePath) final;
|
bool vcsAdd(const FilePath &filePath) final;
|
||||||
@@ -1714,6 +1717,22 @@ bool GitPluginPrivate::isConfigured() const
|
|||||||
return !gitClient().vcsBinary({}).isEmpty();
|
return !gitClient().vcsBinary({}).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitPluginPrivate::hasModification(const Utils::FilePath &path) const
|
||||||
|
{
|
||||||
|
const Utils::FilePath projectDir = gitClient().findRepositoryForDirectory(path.absolutePath());
|
||||||
|
return gitClient().hasModification(projectDir, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitPluginPrivate::monitorDirectory(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
gitClient().monitorDirectory(gitClient().findRepositoryForDirectory(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitPluginPrivate::stopMonitoringDirectory(const Utils::FilePath &path)
|
||||||
|
{
|
||||||
|
gitClient().stopMonitoring(gitClient().findRepositoryForDirectory(path));
|
||||||
|
}
|
||||||
|
|
||||||
bool GitPluginPrivate::supportsOperation(Operation operation) const
|
bool GitPluginPrivate::supportsOperation(Operation operation) const
|
||||||
{
|
{
|
||||||
if (!isConfigured())
|
if (!isConfigured())
|
||||||
@@ -1830,6 +1849,11 @@ void emitRepositoryChanged(const FilePath &r)
|
|||||||
emit dd->repositoryChanged(r);
|
emit dd->repositoryChanged(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void emitFileStatusChanged(const FilePath &r, const QStringList &l)
|
||||||
|
{
|
||||||
|
emit dd->updateFileStatus(r, l);
|
||||||
|
}
|
||||||
|
|
||||||
void startRebaseFromCommit(const FilePath &workingDirectory, const QString &commit)
|
void startRebaseFromCommit(const FilePath &workingDirectory, const QString &commit)
|
||||||
{
|
{
|
||||||
dd->startRebaseFromCommit(workingDirectory, commit);
|
dd->startRebaseFromCommit(workingDirectory, commit);
|
||||||
|
@@ -22,6 +22,7 @@ bool isCommitEditorOpen();
|
|||||||
|
|
||||||
void emitFilesChanged(const QStringList &);
|
void emitFilesChanged(const QStringList &);
|
||||||
void emitRepositoryChanged(const Utils::FilePath &);
|
void emitRepositoryChanged(const Utils::FilePath &);
|
||||||
|
void emitFileStatusChanged(const Utils::FilePath &repository, const QStringList &files);
|
||||||
void startRebaseFromCommit(const Utils::FilePath &workingDirectory, const QString &commit);
|
void startRebaseFromCommit(const Utils::FilePath &workingDirectory, const QString &commit);
|
||||||
void manageRemotes();
|
void manageRemotes();
|
||||||
void initRepository();
|
void initRepository();
|
||||||
|
@@ -241,6 +241,8 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const
|
|||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
case Qt::ForegroundRole:
|
case Qt::ForegroundRole:
|
||||||
|
if (fileNode && fileNode->hasModification())
|
||||||
|
return Utils::creatorColor(Utils::Theme::VcsBase_FileModified_TextColor);
|
||||||
return node->isEnabled() ? QVariant()
|
return node->isEnabled() ? QVariant()
|
||||||
: Utils::creatorColor(Utils::Theme::TextColorDisabled);
|
: Utils::creatorColor(Utils::Theme::TextColorDisabled);
|
||||||
case Project::FilePathRole:
|
case Project::FilePathRole:
|
||||||
@@ -447,12 +449,39 @@ void FlatModel::handleProjectAdded(Project *project)
|
|||||||
parsingStateChanged(project);
|
parsingStateChanged(project);
|
||||||
emit ProjectTree::instance()->nodeActionsChanged();
|
emit ProjectTree::instance()->nodeActionsChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const FilePath &rootPath = project->rootProjectDirectory();
|
||||||
|
IVersionControl *vc = VcsManager::findVersionControlForDirectory(rootPath);
|
||||||
|
if (!vc)
|
||||||
|
return;
|
||||||
|
vc->monitorDirectory(rootPath);
|
||||||
|
connect(vc, &IVersionControl::updateFileStatus, this, &FlatModel::updateVCStatusFor);
|
||||||
|
|
||||||
addOrRebuildProjectModel(project);
|
addOrRebuildProjectModel(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlatModel::updateVCStatusFor(const Utils::FilePath root, const QStringList &files)
|
||||||
|
{
|
||||||
|
std::for_each(std::begin(files), std::end(files), [root, this](const QString &file) {
|
||||||
|
const Node *node = ProjectTree::nodeForFile(root.pathAppended(file));
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const QModelIndex index = indexForNode(node);
|
||||||
|
emit dataChanged(index, index, {Qt::ForegroundRole});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void FlatModel::handleProjectRemoved(Project *project)
|
void FlatModel::handleProjectRemoved(Project *project)
|
||||||
{
|
{
|
||||||
destroyItem(nodeForProject(project));
|
destroyItem(nodeForProject(project));
|
||||||
|
|
||||||
|
if (!project)
|
||||||
|
return;
|
||||||
|
const FilePath &rootPath = project->rootProjectDirectory();
|
||||||
|
if (IVersionControl *vc = VcsManager::findVersionControlForDirectory(rootPath))
|
||||||
|
vc->stopMonitoringDirectory(rootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapperNode *FlatModel::nodeForProject(const Project *project) const
|
WrapperNode *FlatModel::nodeForProject(const Project *project) const
|
||||||
|
@@ -85,6 +85,7 @@ private:
|
|||||||
void rebuildModel();
|
void rebuildModel();
|
||||||
void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen);
|
void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen);
|
||||||
bool trimEmptyDirectories(WrapperNode *parent);
|
bool trimEmptyDirectories(WrapperNode *parent);
|
||||||
|
void updateVCStatusFor(const Utils::FilePath root, const QStringList &files);
|
||||||
|
|
||||||
ExpandData expandDataForNode(const Node *node) const;
|
ExpandData expandDataForNode(const Node *node) const;
|
||||||
void loadExpandData();
|
void loadExpandData();
|
||||||
|
@@ -261,6 +261,22 @@ void FileNode::setHasError(bool error) const
|
|||||||
m_hasError = error;
|
m_hasError = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileNode::hasModification() const
|
||||||
|
{
|
||||||
|
static const QSet<FileType> forbidden{FileType::Unknown, FileType::App, FileType::Lib,
|
||||||
|
FileType::FileTypeSize};
|
||||||
|
|
||||||
|
if (forbidden.contains(fileType()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const FilePath file = filePath();
|
||||||
|
const FilePath dir = file.absolutePath();
|
||||||
|
if (Core::IVersionControl *vc = Core::VcsManager::findVersionControlForDirectory(dir))
|
||||||
|
return vc->hasModification(file);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool FileNode::useUnavailableMarker() const
|
bool FileNode::useUnavailableMarker() const
|
||||||
{
|
{
|
||||||
return m_useUnavailableMarker;
|
return m_useUnavailableMarker;
|
||||||
|
@@ -201,6 +201,8 @@ public:
|
|||||||
void setHasError(const bool error);
|
void setHasError(const bool error);
|
||||||
void setHasError(const bool error) const;
|
void setHasError(const bool error) const;
|
||||||
|
|
||||||
|
bool hasModification() const;
|
||||||
|
|
||||||
QIcon icon() const;
|
QIcon icon() const;
|
||||||
void setIcon(const QIcon icon);
|
void setIcon(const QIcon icon);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user