diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 4a7a43a689f..f0427e08268 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -822,14 +822,19 @@ FilePaths GitClient::unmanagedFiles(const FilePaths &filePaths) const return res; } +QTextCodec *GitClient::defaultCommitEncoding() const +{ + // Set default commit encoding to 'UTF-8', when it's not set, + // to solve displaying error of commit log with non-latin characters. + return QTextCodec::codecForName("UTF-8"); +} + QTextCodec *GitClient::encoding(GitClient::EncodingType encodingType, const FilePath &source) const { auto codec = [this](const FilePath &workingDirectory, const QString &configVar) { const QString codecName = readConfigValue(workingDirectory, configVar).trimmed(); - // Set default commit encoding to 'UTF-8', when it's not set, - // to solve displaying error of commit log with non-latin characters. if (codecName.isEmpty()) - return QTextCodec::codecForName("UTF-8"); + return defaultCommitEncoding(); return QTextCodec::codecForName(codecName.toUtf8()); }; @@ -2618,11 +2623,10 @@ bool GitClient::readDataFromCommit(const FilePath &repoDirectory, const QString return true; } -Author GitClient::getAuthor(const Utils::FilePath &workingDirectory) +Author GitClient::parseAuthor(const QString &authorInfo) { // The format is: // Joe Developer unixtimestamp +HHMM - const QString authorInfo = readGitVar(workingDirectory, "GIT_AUTHOR_IDENT"); int lt = authorInfo.lastIndexOf('<'); int gt = authorInfo.lastIndexOf('>'); if (gt == -1 || uint(lt) > uint(gt)) { @@ -2634,6 +2638,12 @@ Author GitClient::getAuthor(const Utils::FilePath &workingDirectory) return result; } +Author GitClient::getAuthor(const Utils::FilePath &workingDirectory) +{ + const QString authorInfo = readGitVar(workingDirectory, "GIT_AUTHOR_IDENT"); + return parseAuthor(authorInfo); +} + bool GitClient::getCommitData(const FilePath &workingDirectory, QString *commitTemplate, CommitData &commitData, @@ -3425,21 +3435,33 @@ QString GitClient::readGitVar(const FilePath &workingDirectory, const QString &c return readOneLine(workingDirectory, {"var", configVar}); } -QString GitClient::readOneLine(const FilePath &workingDirectory, const QStringList &arguments) const +static QTextCodec *configFileCodec() { // Git for Windows always uses UTF-8 for configuration: // https://github.com/msysgit/msysgit/wiki/Git-for-Windows-Unicode-Support#convert-config-files static QTextCodec *codec = HostOsInfo::isWindowsHost() ? QTextCodec::codecForName("UTF-8") : QTextCodec::codecForLocale(); + return codec; +} +QString GitClient::readOneLine(const FilePath &workingDirectory, const QStringList &arguments) const +{ const CommandResult result = vcsSynchronousExec(workingDirectory, arguments, - RunFlags::NoOutput, vcsTimeoutS(), codec); + RunFlags::NoOutput, vcsTimeoutS(), + configFileCodec()); if (result.result() == ProcessResult::FinishedWithSuccess) return result.cleanedStdOut().trimmed(); return {}; } +void GitClient::readConfigAsync(const FilePath &workingDirectory, const QStringList &arguments, + const CommandHandler &handler) const +{ + vcsExecWithHandler(workingDirectory, arguments, this, handler, RunFlags::NoOutput, + configFileCodec()); +} + static unsigned parseGitVersion(const QString &output) { // cut 'git version 1.6.5.1.sha' diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index 88c2f3870f8..398b1c1cc86 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -79,6 +79,12 @@ public: }; struct Author { + bool operator==(const Author &other) const { + return name == other.name && email == other.email; + } + bool operator!=(const Author &other) const { + return !operator==(other); + } QString name; QString email; }; @@ -343,11 +349,16 @@ public: Core::IEditor *openShowEditor(const Utils::FilePath &workingDirectory, const QString &ref, const Utils::FilePath &path, ShowEditor showSetting = ShowEditor::Always); + Author parseAuthor(const QString &authorInfo); Author getAuthor(const Utils::FilePath &workingDirectory); + QTextCodec *defaultCommitEncoding() const; enum EncodingType { EncodingSource, EncodingLogOutput, EncodingCommit, EncodingDefault }; QTextCodec *encoding(EncodingType encodingType, const Utils::FilePath &source = {}) const; + void readConfigAsync(const Utils::FilePath &workingDirectory, const QStringList &arguments, + const VcsBase::CommandHandler &handler) const; + private: static GitSettings &settings(); diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index e4fc95be8cb..c5f30fa036f 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -392,6 +392,7 @@ public: void setupInstantBlame(); void instantBlameOnce(); + void forceInstantBlame(); void instantBlame(); void stopInstantBlame(); bool refreshWorkingDirectory(const FilePath &workingDirectory); @@ -705,6 +706,7 @@ GitPluginPrivate::GitPluginPrivate() m_fileActions.reserve(10); m_projectActions.reserve(10); m_repositoryActions.reserve(50); + m_codec = GitClient::instance()->defaultCommitEncoding(); Context context(Constants::GIT_CONTEXT); @@ -1441,10 +1443,6 @@ void GitPluginPrivate::setupInstantBlame() return; } - const Utils::FilePath workingDirectory = GitPlugin::currentState().currentFileTopLevel(); - if (!refreshWorkingDirectory(workingDirectory)) - return; - const TextEditorWidget *widget = TextEditorWidget::fromEditor(editor); if (!widget) return; @@ -1452,6 +1450,10 @@ void GitPluginPrivate::setupInstantBlame() if (qobject_cast(widget)) return; // Skip in VCS editors like log or blame + const Utils::FilePath workingDirectory = GitPlugin::currentState().currentFileTopLevel(); + if (!refreshWorkingDirectory(workingDirectory)) + return; + m_blameCursorPosConn = connect(widget, &QPlainTextEdit::cursorPositionChanged, this, [this] { if (!settings().instantBlame.value()) { @@ -1461,8 +1463,7 @@ void GitPluginPrivate::setupInstantBlame() m_cursorPositionChangedTimer->start(500); }); - m_lastVisitedEditorLine = -1; - instantBlame(); + forceInstantBlame(); }; connect(&settings().instantBlame, &BoolAspect::valueChanged, this, @@ -1530,6 +1531,11 @@ void GitPluginPrivate::instantBlameOnce() return; } + forceInstantBlame(); +} + +void GitPluginPrivate::forceInstantBlame() +{ m_lastVisitedEditorLine = -1; instantBlame(); } @@ -1596,8 +1602,38 @@ bool GitPluginPrivate::refreshWorkingDirectory(const FilePath &workingDirectory) return true; m_workingDirectory = workingDirectory; - m_author = GitClient::instance()->getAuthor(workingDirectory); - m_codec = GitClient::instance()->encoding(GitClient::EncodingCommit, workingDirectory); + + const auto commitCodecHandler = [this, workingDirectory](const CommandResult &result) { + QTextCodec *codec = nullptr; + + if (result.result() == ProcessResult::FinishedWithSuccess) { + const QString codecName = result.cleanedStdOut().trimmed(); + codec = QTextCodec::codecForName(codecName.toUtf8()); + } else { + codec = GitClient::instance()->defaultCommitEncoding(); + } + + if (m_codec != codec) { + m_codec = codec; + forceInstantBlame(); + } + }; + GitClient::instance()->readConfigAsync(workingDirectory, {"config", "i18n.commitEncoding"}, + commitCodecHandler); + + const auto authorHandler = [this, workingDirectory](const CommandResult &result) { + if (result.result() == ProcessResult::FinishedWithSuccess) { + const QString authorInfo = result.cleanedStdOut().trimmed(); + const Author author = GitClient::instance()->parseAuthor(authorInfo); + + if (m_author != author) { + m_author = author; + forceInstantBlame(); + } + } + }; + GitClient::instance()->readConfigAsync(workingDirectory, {"var", "GIT_AUTHOR_IDENT"}, + authorHandler); return true; }