Index: src/TortoiseProc/LogDialog/LogDlg.cpp
===================================================================
--- src/TortoiseProc/LogDialog/LogDlg.cpp	(revision 29521)
+++ src/TortoiseProc/LogDialog/LogDlg.cpp	(working copy)
@@ -2060,14 +2060,33 @@
     }
     if (setSelectedRevisions.size() > 0)
     {
-        CString sRevisions;
+        // if the webviewer:revision property is set, copy revision numbers as clickable hyperlinks in CF_HTML format
+        // in addition to copying them in plain text format for the majority of applications that don't accept CF_HTML
+        CString sHyperlinkFormat;
+        if (!m_projectProperties.sWebViewerRev.IsEmpty())
+            sHyperlinkFormat.Format(L"<a href=\"%s\">%%REVISION%%</a>", static_cast<LPCWSTR>(m_projectProperties.sWebViewerRev));
+
+        CString sRevisionsText;
+        CString sRevisionsHtml;
         for (auto it = setSelectedRevisions.begin(); it != setSelectedRevisions.end(); ++it)
         {
-            if (!sRevisions.IsEmpty())
-                sRevisions += ", ";
-            sRevisions += SVNRev(*it).ToString();
+            if (!sRevisionsText.IsEmpty())
+                sRevisionsText += L", ";
+            sRevisionsText += SVNRev(*it).ToString();
+
+            if (!sHyperlinkFormat.IsEmpty())
+            {
+                CString sHyperlink = sHyperlinkFormat;
+                sHyperlink.Replace(L"%REVISION%", SVNRev(*it).ToString());
+                if (!sRevisionsHtml.IsEmpty())
+                    sRevisionsHtml += L", ";
+                sRevisionsHtml += sHyperlink;
+            }
         }
-        CStringUtils::WriteAsciiStringToClipboard(sRevisions, GetSafeHwnd());
+        if (!sRevisionsHtml.IsEmpty())
+            CStringUtils::WriteHtmlAndStringToClipboard(sRevisionsHtml, sRevisionsText, GetSafeHwnd());
+        else
+            CStringUtils::WriteAsciiStringToClipboard(sRevisionsText, GetSafeHwnd());
     }
 }
 
Index: src/Utils/StringUtils.cpp
===================================================================
--- src/Utils/StringUtils.cpp	(revision 29521)
+++ src/Utils/StringUtils.cpp	(working copy)
@@ -192,6 +192,106 @@
     return true;
 }
 
+bool CStringUtils::WriteHtmlAndStringToClipboard(const CStringW& sHtml, const CStringW& sPlainText, HWND hOwningWnd)
+{
+    CClipboardHelper clipboardHelper;
+    if (!clipboardHelper.Open(hOwningWnd))
+        return false;
+
+    EmptyClipboard();
+
+    // Convert the HTML fragment to UTF-8 and generate a CF_HTML block for that fragment
+
+    // Example CF_HTML clipboard data:
+    //
+    // "Version:0.9\r\n"
+    // "StartHTML:0000000105\r\n"
+    // "EndHTML:0000000190\r\n"
+    // "StartFragment:0000000141\r\n"
+    // "EndFragment:0000000154\r\n"
+    // "<HTML>\r\n"
+    // "<BODY>\r\n"
+    // "<!--StartFragment-->HTML_FRAGMENT<!--EndFragment-->\r\n"
+    // "</BODY>\r\n"
+    // "</HTML>"
+
+    CStringA sHtmlUtf8 = CUnicodeUtils::GetUTF8(sHtml);
+
+    const char* descStartHTML = "Version:0.9\r\nStartHTML:";
+    const char* descEndHTML = "\r\nEndHTML:";
+    const char* descStartFragment = "\r\nStartFragment:";
+    const char* descEndFragment = "\r\nEndFragment:";
+    const char* descSuffix = "\r\n";
+    const char* htmlPrefix = "<HTML>\r\n<BODY>\r\n<!--StartFragment-->";
+    const char* htmlSuffix = "<!--EndFragment-->\r\n</BODY>\r\n</HTML>";
+
+    size_t offsetStartHTML =
+        strlen(descStartHTML) + 10 +
+        strlen(descEndHTML) + 10 +
+        strlen(descStartFragment) + 10 +
+        strlen(descEndFragment) + 10 +
+        strlen(descSuffix);
+    size_t offsetStartFragment = offsetStartHTML + strlen(htmlPrefix);
+    size_t offsetEndFragment = offsetStartFragment + sHtmlUtf8.GetLength();
+    size_t offsetEndHTML = offsetEndFragment + strlen(htmlSuffix);
+
+    CStringA sHtmlData;
+    sHtmlData.Format(
+        "%s%010u"
+        "%s%010u"
+        "%s%010u"
+        "%s%010u"
+        "%s"
+        "%s"
+        "%s"
+        "%s",
+        descStartHTML, (unsigned int)offsetStartHTML,
+        descEndHTML, (unsigned int)offsetEndHTML,
+        descStartFragment, (unsigned int)offsetStartFragment,
+        descEndFragment, (unsigned int)offsetEndFragment,
+        descSuffix,
+        htmlPrefix,
+        static_cast<const char*>(sHtmlUtf8),
+        htmlSuffix
+    );
+
+    HGLOBAL hHtmlData = CClipboardHelper::GlobalAlloc(sHtmlData.GetLength() + 1);
+    if (!hHtmlData)
+        return false;
+
+    char* pchHtmlData = static_cast<char*>(GlobalLock(hHtmlData));
+    if (!pchHtmlData)
+        return false;
+
+    strcpy_s(pchHtmlData, sHtmlData.GetLength() + 1, static_cast<LPCSTR>(sHtmlData));
+    GlobalUnlock(hHtmlData);
+
+    UINT htmlFormat = RegisterClipboardFormatA("HTML Format");
+    if (htmlFormat == 0)
+        return false;
+
+    if (!SetClipboardData(htmlFormat, hHtmlData))
+        return false;
+
+    // Also write a plain text block as CF_UNICODETEXT since most applications don't accept CF_HTML
+    HGLOBAL hClipboardData = CClipboardHelper::GlobalAlloc((sPlainText.GetLength() + 1) * sizeof(WCHAR));
+    if (!hClipboardData)
+        return false;
+
+    WCHAR* pchData = static_cast<WCHAR*>(GlobalLock(hClipboardData));
+    if (!pchData)
+        return false;
+
+    _tcscpy_s(pchData, sPlainText.GetLength() + 1, static_cast<LPCWSTR>(sPlainText));
+    GlobalUnlock(hClipboardData);
+    if (!SetClipboardData(CF_UNICODETEXT, hClipboardData))
+        return false;
+
+    // no need to also set CF_TEXT : the OS does this
+    // automatically.
+    return true;
+}
+
 bool CStringUtils::WriteDiffToClipboard(const CStringA& sClipdata, HWND hOwningWnd)
 {
     UINT cFormat = RegisterClipboardFormat(L"TSVN_UNIFIEDDIFF");
Index: src/Utils/StringUtils.h
===================================================================
--- src/Utils/StringUtils.h	(revision 29521)
+++ src/Utils/StringUtils.h	(working copy)
@@ -289,6 +289,14 @@
     static bool WriteAsciiStringToClipboard(const CStringW& sClipdata, HWND hOwningWnd = nullptr);
 
     /**
+     * Writes a fragment of HTML to the clipboard in CF_HTML format for applications that accept HTML,
+     * and as a plain text CString in CF_UNICODETEXT format since most applications don't accept CF_HTML.
+     * The HTML fragment must be valid Unicode HTML and will be embedded inside <HTML><BODY> blocks as explained here:
+     * https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
+     */
+    static bool WriteHtmlAndStringToClipboard(const CStringW& sHtml, const CStringW& sPlainText, HWND hOwningWnd = nullptr);
+
+    /**
     * Writes an ASCII CString to the clipboard in TSVN_UNIFIEDDIFF format, which is basically the patch file
     * as a ASCII string.
     */
