Git: Pimpl BranchModel

Change-Id: I18b7fae8b47b449cf1f049a466efb6a1fbb7e522
Reviewed-by: André Hartmann <aha_1980@gmx.de>
This commit is contained in:
Orgad Shaneh
2018-11-10 21:14:21 +02:00
committed by Orgad Shaneh
parent 3b6f45a7a3
commit 1ef39405a4
2 changed files with 83 additions and 65 deletions

View File

@@ -188,25 +188,47 @@ public:
mutable QString toolTip; mutable QString toolTip;
}; };
class BranchModel::Private
{
public:
Private(GitClient *client) :
client(client),
rootNode(new BranchNode)
{
}
~Private()
{
delete rootNode;
}
GitClient *client;
QString workingDirectory;
BranchNode *rootNode;
BranchNode *currentBranch = nullptr;
QString currentSha;
QStringList obsoleteLocalBranches;
bool oldBranchesIncluded = false;
};
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// BranchModel: // BranchModel:
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
BranchModel::BranchModel(GitClient *client, QObject *parent) : BranchModel::BranchModel(GitClient *client, QObject *parent) :
QAbstractItemModel(parent), QAbstractItemModel(parent),
m_client(client), d(new Private(client))
m_rootNode(new BranchNode)
{ {
QTC_CHECK(m_client); QTC_CHECK(d->client);
// Abuse the sha field for ref prefix // Abuse the sha field for ref prefix
m_rootNode->append(new BranchNode(tr("Local Branches"), "refs/heads")); d->rootNode->append(new BranchNode(tr("Local Branches"), "refs/heads"));
m_rootNode->append(new BranchNode(tr("Remote Branches"), "refs/remotes")); d->rootNode->append(new BranchNode(tr("Remote Branches"), "refs/remotes"));
} }
BranchModel::~BranchModel() BranchModel::~BranchModel()
{ {
delete m_rootNode; delete d;
} }
QModelIndex BranchModel::index(int row, int column, const QModelIndex &parentIdx) const QModelIndex BranchModel::index(int row, int column, const QModelIndex &parentIdx) const
@@ -226,7 +248,7 @@ QModelIndex BranchModel::parent(const QModelIndex &index) const
return QModelIndex(); return QModelIndex();
BranchNode *node = indexToNode(index); BranchNode *node = indexToNode(index);
if (node->parent == m_rootNode) if (node->parent == d->rootNode)
return QModelIndex(); return QModelIndex();
return nodeToIndex(node->parent, 0); return nodeToIndex(node->parent, 0);
} }
@@ -281,7 +303,7 @@ QVariant BranchModel::data(const QModelIndex &index, int role) const
QFont font; QFont font;
if (!node->isLeaf()) { if (!node->isLeaf()) {
font.setBold(true); font.setBold(true);
} else if (node == m_currentBranch) { } else if (node == d->currentBranch) {
font.setBold(true); font.setBold(true);
font.setUnderline(true); font.setUnderline(true);
} }
@@ -325,15 +347,16 @@ Qt::ItemFlags BranchModel::flags(const QModelIndex &index) const
void BranchModel::clear() void BranchModel::clear()
{ {
for (BranchNode *root : qAsConst(m_rootNode->children)) { for (BranchNode *root : qAsConst(d->rootNode->children)) {
while (root->count()) while (root->count())
delete root->children.takeLast(); delete root->children.takeLast();
} }
if (hasTags()) if (hasTags())
m_rootNode->children.takeLast(); d->rootNode->children.takeLast();
m_currentBranch = nullptr; d->currentSha.clear();
m_obsoleteLocalBranches.clear(); d->currentBranch = nullptr;
d->obsoleteLocalBranches.clear();
} }
bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage) bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage)
@@ -345,23 +368,23 @@ bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage
return true; return true;
} }
m_currentSha = m_client->synchronousTopRevision(workingDirectory); d->currentSha = d->client->synchronousTopRevision(workingDirectory);
const QStringList args = {"--format=%(objectname)\t%(refname)\t%(upstream:short)\t" const QStringList args = {"--format=%(objectname)\t%(refname)\t%(upstream:short)\t"
"%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)"}; "%(*objectname)\t%(committerdate:raw)\t%(*committerdate:raw)"};
QString output; QString output;
if (!m_client->synchronousForEachRefCmd(workingDirectory, args, &output, errorMessage)) { if (!d->client->synchronousForEachRefCmd(workingDirectory, args, &output, errorMessage)) {
endResetModel(); endResetModel();
return false; return false;
} }
m_workingDirectory = workingDirectory; d->workingDirectory = workingDirectory;
const QStringList lines = output.split('\n'); const QStringList lines = output.split('\n');
for (const QString &l : lines) for (const QString &l : lines)
parseOutputLine(l); parseOutputLine(l);
if (m_currentBranch) { if (d->currentBranch) {
if (m_currentBranch->isLocal()) if (d->currentBranch->isLocal())
m_currentBranch = nullptr; d->currentBranch = nullptr;
setCurrentBranch(); setCurrentBranch();
} }
@@ -372,60 +395,60 @@ bool BranchModel::refresh(const QString &workingDirectory, QString *errorMessage
void BranchModel::setCurrentBranch() void BranchModel::setCurrentBranch()
{ {
QString currentBranch = m_client->synchronousCurrentLocalBranch(m_workingDirectory); QString currentBranch = d->client->synchronousCurrentLocalBranch(d->workingDirectory);
if (currentBranch.isEmpty()) if (currentBranch.isEmpty())
return; return;
BranchNode *local = m_rootNode->children.at(LocalBranches); BranchNode *local = d->rootNode->children.at(LocalBranches);
const QStringList branchParts = currentBranch.split('/'); const QStringList branchParts = currentBranch.split('/');
for (const QString &branchPart : branchParts) { for (const QString &branchPart : branchParts) {
local = local->childOfName(branchPart); local = local->childOfName(branchPart);
if (!local) if (!local)
return; return;
} }
m_currentBranch = local; d->currentBranch = local;
} }
void BranchModel::renameBranch(const QString &oldName, const QString &newName) void BranchModel::renameBranch(const QString &oldName, const QString &newName)
{ {
QString errorMessage; QString errorMessage;
QString output; QString output;
if (!m_client->synchronousBranchCmd(m_workingDirectory, {"-m", oldName, newName}, if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-m", oldName, newName},
&output, &errorMessage)) &output, &errorMessage))
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
else else
refresh(m_workingDirectory, &errorMessage); refresh(d->workingDirectory, &errorMessage);
} }
void BranchModel::renameTag(const QString &oldName, const QString &newName) void BranchModel::renameTag(const QString &oldName, const QString &newName)
{ {
QString errorMessage; QString errorMessage;
QString output; QString output;
if (!m_client->synchronousTagCmd(m_workingDirectory, {newName, oldName}, if (!d->client->synchronousTagCmd(d->workingDirectory, {newName, oldName},
&output, &errorMessage) &output, &errorMessage)
|| !m_client->synchronousTagCmd(m_workingDirectory, {"-d", oldName}, || !d->client->synchronousTagCmd(d->workingDirectory, {"-d", oldName},
&output, &errorMessage)) { &output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
} else { } else {
refresh(m_workingDirectory, &errorMessage); refresh(d->workingDirectory, &errorMessage);
} }
} }
QString BranchModel::workingDirectory() const QString BranchModel::workingDirectory() const
{ {
return m_workingDirectory; return d->workingDirectory;
} }
GitClient *BranchModel::client() const GitClient *BranchModel::client() const
{ {
return m_client; return d->client;
} }
QModelIndex BranchModel::currentBranch() const QModelIndex BranchModel::currentBranch() const
{ {
if (!m_currentBranch) if (!d->currentBranch)
return QModelIndex(); return QModelIndex();
return nodeToIndex(m_currentBranch, 0); return nodeToIndex(d->currentBranch, 0);
} }
QString BranchModel::fullName(const QModelIndex &idx, bool includePrefix) const QString BranchModel::fullName(const QModelIndex &idx, bool includePrefix) const
@@ -440,10 +463,10 @@ QString BranchModel::fullName(const QModelIndex &idx, bool includePrefix) const
QStringList BranchModel::localBranchNames() const QStringList BranchModel::localBranchNames() const
{ {
if (!m_rootNode || !m_rootNode->count()) if (!d->rootNode || !d->rootNode->count())
return QStringList(); return QStringList();
return m_rootNode->children.at(LocalBranches)->childrenNames() + m_obsoleteLocalBranches; return d->rootNode->children.at(LocalBranches)->childrenNames() + d->obsoleteLocalBranches;
} }
QString BranchModel::sha(const QModelIndex &idx) const QString BranchModel::sha(const QModelIndex &idx) const
@@ -464,7 +487,7 @@ QDateTime BranchModel::dateTime(const QModelIndex &idx) const
bool BranchModel::hasTags() const bool BranchModel::hasTags() const
{ {
return m_rootNode->children.count() > Tags; return d->rootNode->children.count() > Tags;
} }
bool BranchModel::isLocal(const QModelIndex &idx) const bool BranchModel::isLocal(const QModelIndex &idx) const
@@ -499,7 +522,7 @@ void BranchModel::removeBranch(const QModelIndex &idx)
QString errorMessage; QString errorMessage;
QString output; QString output;
if (!m_client->synchronousBranchCmd(m_workingDirectory, {"-D", branch}, &output, &errorMessage)) { if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-D", branch}, &output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
return; return;
} }
@@ -515,7 +538,7 @@ void BranchModel::removeTag(const QModelIndex &idx)
QString errorMessage; QString errorMessage;
QString output; QString output;
if (!m_client->synchronousTagCmd(m_workingDirectory, {"-d", tag}, &output, &errorMessage)) { if (!d->client->synchronousTagCmd(d->workingDirectory, {"-d", tag}, &output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
return; return;
} }
@@ -530,7 +553,7 @@ void BranchModel::checkoutBranch(const QModelIndex &idx)
// No StashGuard since this function for now is only used with clean working dir. // No StashGuard since this function for now is only used with clean working dir.
// If it is ever used from another place, please add StashGuard here // If it is ever used from another place, please add StashGuard here
m_client->checkout(m_workingDirectory, branch, GitClient::StashMode::NoStash); d->client->checkout(d->workingDirectory, branch, GitClient::StashMode::NoStash);
} }
bool BranchModel::branchIsMerged(const QModelIndex &idx) bool BranchModel::branchIsMerged(const QModelIndex &idx)
@@ -542,7 +565,7 @@ bool BranchModel::branchIsMerged(const QModelIndex &idx)
QString errorMessage; QString errorMessage;
QString output; QString output;
if (!m_client->synchronousBranchCmd(m_workingDirectory, {"-a", "--contains", sha(idx)}, if (!d->client->synchronousBranchCmd(d->workingDirectory, {"-a", "--contains", sha(idx)},
&output, &errorMessage)) { &output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
} }
@@ -570,7 +593,7 @@ static int positionForName(BranchNode *node, const QString &name)
QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModelIndex &startPoint) QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModelIndex &startPoint)
{ {
if (!m_rootNode || !m_rootNode->count()) if (!d->rootNode || !d->rootNode->count())
return QModelIndex(); return QModelIndex();
const QString trackedBranch = fullName(startPoint); const QString trackedBranch = fullName(startPoint);
@@ -589,7 +612,7 @@ QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModel
QString output; QString output;
QString errorMessage; QString errorMessage;
const QStringList arguments({"-n1", "--format=%H %ct"}); const QStringList arguments({"-n1", "--format=%H %ct"});
if (m_client->synchronousLog(m_workingDirectory, arguments, &output, &errorMessage, if (d->client->synchronousLog(d->workingDirectory, arguments, &output, &errorMessage,
VcsCommand::SuppressCommandLogging)) { VcsCommand::SuppressCommandLogging)) {
const QStringList values = output.split(' '); const QStringList values = output.split(' ');
startSha = values[0]; startSha = values[0];
@@ -597,12 +620,12 @@ QModelIndex BranchModel::addBranch(const QString &name, bool track, const QModel
} }
} }
if (!m_client->synchronousBranchCmd(m_workingDirectory, args, &output, &errorMessage)) { if (!d->client->synchronousBranchCmd(d->workingDirectory, args, &output, &errorMessage)) {
VcsOutputWindow::appendError(errorMessage); VcsOutputWindow::appendError(errorMessage);
return QModelIndex(); return QModelIndex();
} }
BranchNode *local = m_rootNode->children.at(LocalBranches); BranchNode *local = d->rootNode->children.at(LocalBranches);
const int slash = name.indexOf('/'); const int slash = name.indexOf('/');
const QString leafName = slash == -1 ? name : name.mid(slash + 1); const QString leafName = slash == -1 ? name : name.mid(slash + 1);
bool added = false; bool added = false;
@@ -637,19 +660,19 @@ void BranchModel::setRemoteTracking(const QModelIndex &trackingIndex)
const QString currentName = fullName(current); const QString currentName = fullName(current);
const QString shortTracking = fullName(trackingIndex); const QString shortTracking = fullName(trackingIndex);
const QString tracking = fullName(trackingIndex, true); const QString tracking = fullName(trackingIndex, true);
m_client->synchronousSetTrackingBranch(m_workingDirectory, currentName, tracking); d->client->synchronousSetTrackingBranch(d->workingDirectory, currentName, tracking);
m_currentBranch->tracking = shortTracking; d->currentBranch->tracking = shortTracking;
emit dataChanged(current, current); emit dataChanged(current, current);
} }
void BranchModel::setOldBranchesIncluded(bool value) void BranchModel::setOldBranchesIncluded(bool value)
{ {
m_oldBranchesIncluded = value; d->oldBranchesIncluded = value;
} }
Utils::optional<QString> BranchModel::remoteName(const QModelIndex &idx) const Utils::optional<QString> BranchModel::remoteName(const QModelIndex &idx) const
{ {
const BranchNode *remotesNode = m_rootNode->children.at(RemoteBranches); const BranchNode *remotesNode = d->rootNode->children.at(RemoteBranches);
const BranchNode *node = indexToNode(idx); const BranchNode *node = indexToNode(idx);
if (!node) if (!node)
return Utils::nullopt; return Utils::nullopt;
@@ -672,7 +695,7 @@ void BranchModel::parseOutputLine(const QString &line)
const QString fullName = lineParts.at(1); const QString fullName = lineParts.at(1);
const QString upstream = lineParts.at(2); const QString upstream = lineParts.at(2);
QDateTime dateTime; QDateTime dateTime;
const bool current = (sha == m_currentSha); const bool current = (sha == d->currentSha);
QString strDateTime = lineParts.at(5); QString strDateTime = lineParts.at(5);
if (strDateTime.isEmpty()) if (strDateTime.isEmpty())
strDateTime = lineParts.at(4); strDateTime = lineParts.at(4);
@@ -681,16 +704,16 @@ void BranchModel::parseOutputLine(const QString &line)
dateTime = QDateTime::fromSecsSinceEpoch(timeT); dateTime = QDateTime::fromSecsSinceEpoch(timeT);
} }
if (!m_oldBranchesIncluded && !current && dateTime.isValid()) { if (!d->oldBranchesIncluded && !current && dateTime.isValid()) {
const qint64 age = dateTime.daysTo(QDateTime::currentDateTime()); const qint64 age = dateTime.daysTo(QDateTime::currentDateTime());
if (age > Constants::OBSOLETE_COMMIT_AGE_IN_DAYS) { if (age > Constants::OBSOLETE_COMMIT_AGE_IN_DAYS) {
const QString heads = "refs/heads/"; const QString heads = "refs/heads/";
if (fullName.startsWith(heads)) if (fullName.startsWith(heads))
m_obsoleteLocalBranches.append(fullName.mid(heads.size())); d->obsoleteLocalBranches.append(fullName.mid(heads.size()));
return; return;
} }
} }
bool showTags = m_client->settings().boolValue(GitSettings::showTagsKey); bool showTags = d->client->settings().boolValue(GitSettings::showTagsKey);
// insert node into tree: // insert node into tree:
QStringList nameParts = fullName.split('/'); QStringList nameParts = fullName.split('/');
@@ -698,13 +721,13 @@ void BranchModel::parseOutputLine(const QString &line)
BranchNode *root = nullptr; BranchNode *root = nullptr;
if (nameParts.first() == "heads") { if (nameParts.first() == "heads") {
root = m_rootNode->children.at(LocalBranches); root = d->rootNode->children.at(LocalBranches);
} else if (nameParts.first() == "remotes") { } else if (nameParts.first() == "remotes") {
root = m_rootNode->children.at(RemoteBranches); root = d->rootNode->children.at(RemoteBranches);
} else if (showTags && nameParts.first() == "tags") { } else if (showTags && nameParts.first() == "tags") {
if (!hasTags()) // Tags is missing, add it if (!hasTags()) // Tags is missing, add it
m_rootNode->append(new BranchNode(tr("Tags"), "refs/tags")); d->rootNode->append(new BranchNode(tr("Tags"), "refs/tags"));
root = m_rootNode->children.at(Tags); root = d->rootNode->children.at(Tags);
} else { } else {
return; return;
} }
@@ -724,7 +747,7 @@ void BranchModel::parseOutputLine(const QString &line)
auto newNode = new BranchNode(name, sha, upstream, dateTime); auto newNode = new BranchNode(name, sha, upstream, dateTime);
root->insert(nameParts, newNode); root->insert(nameParts, newNode);
if (current) if (current)
m_currentBranch = newNode; d->currentBranch = newNode;
} }
BranchNode *BranchModel::indexToNode(const QModelIndex &index) const BranchNode *BranchModel::indexToNode(const QModelIndex &index) const
@@ -732,13 +755,13 @@ BranchNode *BranchModel::indexToNode(const QModelIndex &index) const
if (index.column() > 1) if (index.column() > 1)
return nullptr; return nullptr;
if (!index.isValid()) if (!index.isValid())
return m_rootNode; return d->rootNode;
return static_cast<BranchNode *>(index.internalPointer()); return static_cast<BranchNode *>(index.internalPointer());
} }
QModelIndex BranchModel::nodeToIndex(BranchNode *node, int column) const QModelIndex BranchModel::nodeToIndex(BranchNode *node, int column) const
{ {
if (node == m_rootNode) if (node == d->rootNode)
return QModelIndex(); return QModelIndex();
return createIndex(node->parent->rowOf(node), column, static_cast<void *>(node)); return createIndex(node->parent->rowOf(node), column, static_cast<void *>(node));
} }
@@ -747,7 +770,7 @@ void BranchModel::removeNode(const QModelIndex &idx)
{ {
QModelIndex nodeIndex = idx; // idx is a leaf, so count must be 0. QModelIndex nodeIndex = idx; // idx is a leaf, so count must be 0.
BranchNode *node = indexToNode(nodeIndex); BranchNode *node = indexToNode(nodeIndex);
while (node->count() == 0 && node->parent != m_rootNode) { while (node->count() == 0 && node->parent != d->rootNode) {
BranchNode *parentNode = node->parent; BranchNode *parentNode = node->parent;
const QModelIndex parentIndex = nodeToIndex(parentNode, 0); const QModelIndex parentIndex = nodeToIndex(parentNode, 0);
const int nodeRow = nodeIndex.row(); const int nodeRow = nodeIndex.row();
@@ -767,7 +790,7 @@ QString BranchModel::toolTip(const QString &sha) const
QString errorMessage; QString errorMessage;
QStringList arguments("-n1"); QStringList arguments("-n1");
arguments << sha; arguments << sha;
if (!m_client->synchronousLog(m_workingDirectory, arguments, &output, &errorMessage, if (!d->client->synchronousLog(d->workingDirectory, arguments, &output, &errorMessage,
VcsCommand::SuppressCommandLogging)) { VcsCommand::SuppressCommandLogging)) {
return errorMessage; return errorMessage;
} }

View File

@@ -94,13 +94,8 @@ private:
QString toolTip(const QString &sha) const; QString toolTip(const QString &sha) const;
GitClient *m_client; class Private;
QString m_workingDirectory; Private *d;
BranchNode *m_rootNode;
BranchNode *m_currentBranch = nullptr;
QString m_currentSha;
QStringList m_obsoleteLocalBranches;
bool m_oldBranchesIncluded = false;
}; };
} // namespace Internal } // namespace Internal