forked from qt-creator/qt-creator
Add support for annotation of any given revision
- Parent commits are also accessible from Annotate context menu - The client functionality was added in `fossil v2.4` Change-Id: Ia6096432cb1151388b5aebca30a6d25c1c6079f4 Reviewed-by: Eike Ziller <eike.ziller@qt.io> Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
@@ -99,7 +99,7 @@ public:
|
|||||||
// This way the annotated line number would not get offset by the version list.
|
// This way the annotated line number would not get offset by the version list.
|
||||||
settings.setValue(FossilSettings::annotateListVersionsKey, false);
|
settings.setValue(FossilSettings::annotateListVersionsKey, false);
|
||||||
|
|
||||||
mapSetting(addToggleButton(QLatin1String("--log"), tr("List Versions")),
|
mapSetting(addToggleButton("--log", tr("List Versions")),
|
||||||
settings.boolPointer(FossilSettings::annotateListVersionsKey));
|
settings.boolPointer(FossilSettings::annotateListVersionsKey));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -342,7 +342,23 @@ QList<BranchInfo> FossilClient::synchronousBranchQuery(const QString &workingDir
|
|||||||
return branches;
|
return branches;
|
||||||
}
|
}
|
||||||
|
|
||||||
RevisionInfo FossilClient::synchronousRevisionQuery(const QString &workingDirectory, const QString &id)
|
QStringList FossilClient::parseRevisionCommentLine(const QString &commentLine)
|
||||||
|
{
|
||||||
|
// "comment: This is a (test) commit message (user: the.name)"
|
||||||
|
|
||||||
|
const QRegularExpression commentRx("^comment:\\s+(.*)\\s\\(user:\\s(.*)\\)$",
|
||||||
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
|
QTC_ASSERT(commentRx.isValid(), return QStringList());
|
||||||
|
|
||||||
|
const QRegularExpressionMatch match = commentRx.match(commentLine);
|
||||||
|
if (!match.hasMatch())
|
||||||
|
return QStringList();
|
||||||
|
|
||||||
|
return QStringList({match.captured(1), match.captured(2)});
|
||||||
|
}
|
||||||
|
|
||||||
|
RevisionInfo FossilClient::synchronousRevisionQuery(const QString &workingDirectory, const QString &id,
|
||||||
|
bool getCommentMsg) const
|
||||||
{
|
{
|
||||||
// Query details of the given revision/check-out id,
|
// Query details of the given revision/check-out id,
|
||||||
// if none specified, provide information about current revision
|
// if none specified, provide information about current revision
|
||||||
@@ -361,6 +377,9 @@ RevisionInfo FossilClient::synchronousRevisionQuery(const QString &workingDirect
|
|||||||
|
|
||||||
QString revisionId;
|
QString revisionId;
|
||||||
QString parentId;
|
QString parentId;
|
||||||
|
QStringList mergeParentIds;
|
||||||
|
QString commentMsg;
|
||||||
|
QString committer;
|
||||||
|
|
||||||
const QRegularExpression idRx("([0-9a-f]{5,40})");
|
const QRegularExpression idRx("([0-9a-f]{5,40})");
|
||||||
QTC_ASSERT(idRx.isValid(), return RevisionInfo());
|
QTC_ASSERT(idRx.isValid(), return RevisionInfo());
|
||||||
@@ -376,6 +395,15 @@ RevisionInfo FossilClient::synchronousRevisionQuery(const QString &workingDirect
|
|||||||
const QRegularExpressionMatch idMatch = idRx.match(l);
|
const QRegularExpressionMatch idMatch = idRx.match(l);
|
||||||
if (idMatch.hasMatch())
|
if (idMatch.hasMatch())
|
||||||
parentId = idMatch.captured(1);
|
parentId = idMatch.captured(1);
|
||||||
|
} else if (l.startsWith("merged-from: ", Qt::CaseInsensitive)) {
|
||||||
|
const QRegularExpressionMatch idMatch = idRx.match(l);
|
||||||
|
if (idMatch.hasMatch())
|
||||||
|
mergeParentIds.append(idMatch.captured(1));
|
||||||
|
} else if (getCommentMsg
|
||||||
|
&& l.startsWith("comment: ", Qt::CaseInsensitive)) {
|
||||||
|
const QStringList commentLineParts = parseRevisionCommentLine(l);
|
||||||
|
commentMsg = commentLineParts.value(0);
|
||||||
|
committer = commentLineParts.value(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,7 +413,7 @@ RevisionInfo FossilClient::synchronousRevisionQuery(const QString &workingDirect
|
|||||||
if (parentId.isEmpty())
|
if (parentId.isEmpty())
|
||||||
parentId = revisionId; // root
|
parentId = revisionId; // root
|
||||||
|
|
||||||
return RevisionInfo(revisionId, parentId);
|
return RevisionInfo(revisionId, parentId, mergeParentIds, commentMsg, committer);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList FossilClient::synchronousTagQuery(const QString &workingDirectory, const QString &id)
|
QStringList FossilClient::synchronousTagQuery(const QString &workingDirectory, const QString &id)
|
||||||
@@ -452,8 +480,7 @@ RepositorySettings FossilClient::synchronousSettingsQuery(const QString &working
|
|||||||
|| lcValue == "2")
|
|| lcValue == "2")
|
||||||
repoSettings.autosync = RepositorySettings::AutosyncPullOnly;
|
repoSettings.autosync = RepositorySettings::AutosyncPullOnly;
|
||||||
}
|
}
|
||||||
|
else if (property == "ssl-identity") {
|
||||||
if (property == "ssl-identity") {
|
|
||||||
repoSettings.sslIdentityFile = value;
|
repoSettings.sslIdentityFile = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -582,6 +609,7 @@ QString FossilClient::synchronousTopic(const QString &workingDirectory)
|
|||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
// return current branch name
|
// return current branch name
|
||||||
|
|
||||||
const BranchInfo branchInfo = synchronousCurrentBranch(workingDirectory);
|
const BranchInfo branchInfo = synchronousCurrentBranch(workingDirectory);
|
||||||
if (branchInfo.name().isEmpty())
|
if (branchInfo.name().isEmpty())
|
||||||
return QString();
|
return QString();
|
||||||
@@ -726,7 +754,7 @@ VcsBase::VcsBaseEditorWidget *FossilClient::annotate(
|
|||||||
|
|
||||||
QString vcsCmdString = vcsCommandString(AnnotateCommand);
|
QString vcsCmdString = vcsCommandString(AnnotateCommand);
|
||||||
const Core::Id kind = vcsEditorKind(AnnotateCommand);
|
const Core::Id kind = vcsEditorKind(AnnotateCommand);
|
||||||
const QString id = VcsBase::VcsBaseEditor::getSource(workingDir, QStringList(file));
|
const QString id = VcsBase::VcsBaseEditor::getTitleId(workingDir, QStringList(file), revision);
|
||||||
const QString title = vcsEditorTitle(vcsCmdString, id);
|
const QString title = vcsEditorTitle(vcsCmdString, id);
|
||||||
const QString source = VcsBase::VcsBaseEditor::getSource(workingDir, file);
|
const QString source = VcsBase::VcsBaseEditor::getSource(workingDir, file);
|
||||||
|
|
||||||
@@ -734,13 +762,6 @@ VcsBase::VcsBaseEditorWidget *FossilClient::annotate(
|
|||||||
VcsBase::VcsBaseEditor::getCodec(source),
|
VcsBase::VcsBaseEditor::getCodec(source),
|
||||||
vcsCmdString.toLatin1().constData(), id);
|
vcsCmdString.toLatin1().constData(), id);
|
||||||
|
|
||||||
// We need to be able to re-query the configuration widget for the arguments
|
|
||||||
// each time the Annotate is requested from the main menu. This allows processing of
|
|
||||||
// the effective args controlled via configuration widget.
|
|
||||||
// However VcsBaseEditorWidget no longer stores the configuration widget and thus
|
|
||||||
// does not support configurationWidget() query.
|
|
||||||
// So we re-implement the configurationWidget() in FossilEditorWidget sub-class.
|
|
||||||
|
|
||||||
auto *fossilEditor = qobject_cast<FossilEditorWidget *>(editor);
|
auto *fossilEditor = qobject_cast<FossilEditorWidget *>(editor);
|
||||||
QTC_ASSERT(fossilEditor, return editor);
|
QTC_ASSERT(fossilEditor, return editor);
|
||||||
|
|
||||||
@@ -769,7 +790,11 @@ VcsBase::VcsBaseEditorWidget *FossilClient::annotate(
|
|||||||
effectiveArgs.removeAt(pos);
|
effectiveArgs.removeAt(pos);
|
||||||
}
|
}
|
||||||
QStringList args(vcsCmdString);
|
QStringList args(vcsCmdString);
|
||||||
args << revisionSpec(revision) << effectiveArgs << file;
|
if (!revision.isEmpty()
|
||||||
|
&& supportedFeatures().testFlag(AnnotateRevisionFeature))
|
||||||
|
args << "-r" << revision;
|
||||||
|
|
||||||
|
args << effectiveArgs << file;
|
||||||
|
|
||||||
// When version list requested, ignore the source line.
|
// When version list requested, ignore the source line.
|
||||||
if (args.contains("--log"))
|
if (args.contains("--log"))
|
||||||
@@ -805,7 +830,7 @@ bool FossilClient::managesFile(const QString &workingDirectory, const QString &f
|
|||||||
if (response.result != Utils::SynchronousProcessResponse::Finished)
|
if (response.result != Utils::SynchronousProcessResponse::Finished)
|
||||||
return false;
|
return false;
|
||||||
QString output = sanitizeFossilOutput(response.stdOut());
|
QString output = sanitizeFossilOutput(response.stdOut());
|
||||||
return !output.startsWith("no history for file");
|
return !output.startsWith("no history for file", Qt::CaseInsensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int FossilClient::binaryVersion() const
|
unsigned int FossilClient::binaryVersion() const
|
||||||
@@ -851,8 +876,10 @@ FossilClient::SupportedFeatures FossilClient::supportedFeatures() const
|
|||||||
|
|
||||||
const unsigned int version = binaryVersion();
|
const unsigned int version = binaryVersion();
|
||||||
|
|
||||||
if (version < 0x13000) {
|
if (version < 0x20400) {
|
||||||
features &= ~TimelinePathFeature;
|
features &= ~AnnotateRevisionFeature;
|
||||||
|
if (version < 0x13000)
|
||||||
|
features &= ~TimelinePathFeature;
|
||||||
if (version < 0x12900)
|
if (version < 0x12900)
|
||||||
features &= ~DiffIgnoreWhiteSpaceFeature;
|
features &= ~DiffIgnoreWhiteSpaceFeature;
|
||||||
if (version < 0x12800) {
|
if (version < 0x12800) {
|
||||||
@@ -860,6 +887,7 @@ FossilClient::SupportedFeatures FossilClient::supportedFeatures() const
|
|||||||
features &= ~TimelineWidthFeature;
|
features &= ~TimelineWidthFeature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return features;
|
return features;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,13 @@ public:
|
|||||||
TimelineWidthFeature = 0x4,
|
TimelineWidthFeature = 0x4,
|
||||||
DiffIgnoreWhiteSpaceFeature = 0x8,
|
DiffIgnoreWhiteSpaceFeature = 0x8,
|
||||||
TimelinePathFeature = 0x10,
|
TimelinePathFeature = 0x10,
|
||||||
|
AnnotateRevisionFeature = 0x20,
|
||||||
AllSupportedFeatures = // | all defined features
|
AllSupportedFeatures = // | all defined features
|
||||||
AnnotateBlameFeature
|
AnnotateBlameFeature
|
||||||
| TimelineWidthFeature
|
| TimelineWidthFeature
|
||||||
| DiffIgnoreWhiteSpaceFeature
|
| DiffIgnoreWhiteSpaceFeature
|
||||||
| TimelinePathFeature
|
| TimelinePathFeature
|
||||||
|
| AnnotateRevisionFeature
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(SupportedFeatures, SupportedFeature)
|
Q_DECLARE_FLAGS(SupportedFeatures, SupportedFeature)
|
||||||
|
|
||||||
@@ -64,7 +66,8 @@ public:
|
|||||||
unsigned int synchronousBinaryVersion() const;
|
unsigned int synchronousBinaryVersion() const;
|
||||||
BranchInfo synchronousCurrentBranch(const QString &workingDirectory);
|
BranchInfo synchronousCurrentBranch(const QString &workingDirectory);
|
||||||
QList<BranchInfo> synchronousBranchQuery(const QString &workingDirectory);
|
QList<BranchInfo> synchronousBranchQuery(const QString &workingDirectory);
|
||||||
RevisionInfo synchronousRevisionQuery(const QString &workingDirectory, const QString &id = QString());
|
RevisionInfo synchronousRevisionQuery(const QString &workingDirectory, const QString &id = QString(),
|
||||||
|
bool getCommentMsg = false) const;
|
||||||
QStringList synchronousTagQuery(const QString &workingDirectory, const QString &id = QString());
|
QStringList synchronousTagQuery(const QString &workingDirectory, const QString &id = QString());
|
||||||
RepositorySettings synchronousSettingsQuery(const QString &workingDirectory);
|
RepositorySettings synchronousSettingsQuery(const QString &workingDirectory);
|
||||||
bool synchronousSetSetting(const QString &workingDirectory, const QString &property,
|
bool synchronousSetSetting(const QString &workingDirectory, const QString &property,
|
||||||
@@ -113,6 +116,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static QList<BranchInfo> branchListFromOutput(const QString &output, const BranchInfo::BranchFlags defaultFlags = 0);
|
static QList<BranchInfo> branchListFromOutput(const QString &output, const BranchInfo::BranchFlags defaultFlags = 0);
|
||||||
|
static QStringList parseRevisionCommentLine(const QString &commentLine);
|
||||||
|
|
||||||
QString sanitizeFossilOutput(const QString &output) const;
|
QString sanitizeFossilOutput(const QString &output) const;
|
||||||
QString vcsCommandString(VcsCommandTag cmd) const final;
|
QString vcsCommandString(VcsCommandTag cmd) const final;
|
||||||
|
|||||||
@@ -124,8 +124,48 @@ QString FossilEditorWidget::changeUnderCursor(const QTextCursor &cursorIn) const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FossilEditorWidget::decorateVersion(const QString &revision) const
|
||||||
|
{
|
||||||
|
static const int shortChangesetIdSize(10);
|
||||||
|
static const int maxTextSize(120);
|
||||||
|
|
||||||
VcsBase::BaseAnnotationHighlighter *FossilEditorWidget::createAnnotationHighlighter(const QSet<QString> &changes) const
|
const QFileInfo fi(source());
|
||||||
|
const QString workingDirectory = fi.absolutePath();
|
||||||
|
FossilClient *client = FossilPlugin::instance()->client();
|
||||||
|
RevisionInfo revisionInfo =
|
||||||
|
client->synchronousRevisionQuery(workingDirectory, revision, true);
|
||||||
|
|
||||||
|
// format: 'revision (committer "comment...")'
|
||||||
|
QString output = revision.left(shortChangesetIdSize)
|
||||||
|
+ " (" + revisionInfo.committer
|
||||||
|
+ " \"" + revisionInfo.commentMsg.left(maxTextSize);
|
||||||
|
|
||||||
|
if (output.size() > maxTextSize) {
|
||||||
|
output.truncate(maxTextSize - 3);
|
||||||
|
output.append("...");
|
||||||
|
}
|
||||||
|
output.append("\")");
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FossilEditorWidget::annotationPreviousVersions(const QString &revision) const
|
||||||
|
{
|
||||||
|
QStringList revisions;
|
||||||
|
const QFileInfo fi(source());
|
||||||
|
const QString workingDirectory = fi.absolutePath();
|
||||||
|
FossilClient *client = FossilPlugin::instance()->client();
|
||||||
|
RevisionInfo revisionInfo =
|
||||||
|
client->synchronousRevisionQuery(workingDirectory, revision);
|
||||||
|
if (revisionInfo.parentId.isEmpty())
|
||||||
|
return QStringList();
|
||||||
|
|
||||||
|
revisions.append(revisionInfo.parentId);
|
||||||
|
revisions.append(revisionInfo.mergeParentIds);
|
||||||
|
return revisions;
|
||||||
|
}
|
||||||
|
|
||||||
|
VcsBase::BaseAnnotationHighlighter *FossilEditorWidget::createAnnotationHighlighter(
|
||||||
|
const QSet<QString> &changes) const
|
||||||
{
|
{
|
||||||
return new FossilAnnotationHighlighter(changes);
|
return new FossilAnnotationHighlighter(changes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ public:
|
|||||||
private:
|
private:
|
||||||
QSet<QString> annotationChanges() const final;
|
QSet<QString> annotationChanges() const final;
|
||||||
QString changeUnderCursor(const QTextCursor &cursor) const final;
|
QString changeUnderCursor(const QTextCursor &cursor) const final;
|
||||||
VcsBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const final;
|
QString decorateVersion(const QString &revision) const final;
|
||||||
|
QStringList annotationPreviousVersions(const QString &revision) const final;
|
||||||
|
VcsBase::BaseAnnotationHighlighter *createAnnotationHighlighter(
|
||||||
|
const QSet<QString> &changes) const final;
|
||||||
|
|
||||||
FossilEditorWidgetPrivate *d;
|
FossilEditorWidgetPrivate *d;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -285,8 +285,9 @@ void FossilPlugin::logCurrentFile()
|
|||||||
if (features.testFlag(FossilClient::TimelineWidthFeature))
|
if (features.testFlag(FossilClient::TimelineWidthFeature))
|
||||||
extraOptions << "-W" << QString::number(m_client->settings().intValue(FossilSettings::timelineWidthKey));
|
extraOptions << "-W" << QString::number(m_client->settings().intValue(FossilSettings::timelineWidthKey));
|
||||||
|
|
||||||
// annotate only supported for current revision, so disable context menu
|
// disable annotate context menu for older client versions, used to be supported for current revision only
|
||||||
bool enableAnnotationContextMenu = false;
|
bool enableAnnotationContextMenu = features.testFlag(FossilClient::AnnotateRevisionFeature);
|
||||||
|
|
||||||
m_client->logCurrentFile(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()),
|
m_client->logCurrentFile(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()),
|
||||||
extraOptions, enableAnnotationContextMenu);
|
extraOptions, enableAnnotationContextMenu);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,14 @@
|
|||||||
namespace Fossil {
|
namespace Fossil {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
RevisionInfo::RevisionInfo(const QString &revisionId, const QString &parent) :
|
RevisionInfo::RevisionInfo(const QString &revisionId, const QString &parent,
|
||||||
|
const QStringList &mergeParents, const QString &comment,
|
||||||
|
const QString &user) :
|
||||||
id(revisionId),
|
id(revisionId),
|
||||||
parentId(parent)
|
parentId(parent),
|
||||||
|
mergeParentIds(mergeParents),
|
||||||
|
commentMsg(comment),
|
||||||
|
committer(user)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -36,10 +36,15 @@ namespace Internal {
|
|||||||
class RevisionInfo
|
class RevisionInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit RevisionInfo(const QString &revisionId = QString(), const QString &parent = QString());
|
explicit RevisionInfo(const QString &revisionId = QString(), const QString &parent = QString(),
|
||||||
|
const QStringList &mergeParents = QStringList(),
|
||||||
|
const QString &comment = QString(), const QString &user = QString());
|
||||||
|
|
||||||
const QString id;
|
const QString id;
|
||||||
const QString parentId;
|
const QString parentId;
|
||||||
|
const QStringList mergeParentIds;
|
||||||
|
const QString commentMsg;
|
||||||
|
const QString committer;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user