diff --git a/scripts/lib/devtool/upgrade.py b/scripts/lib/devtool/upgrade.py
index 495a5b8217..6883c99cce 100644
--- a/scripts/lib/devtool/upgrade.py
+++ b/scripts/lib/devtool/upgrade.py
@@ -589,6 +589,18 @@ def _resolve_rst_includes(content, srctree):
     return ''.join(result)
 
 
+def _git_show_file(srctree, ref, fname):
+    """Return the content of fname at ref. Changelogs occasionally contain
+    non-UTF-8 bytes (e.g. Latin-1 author names), which bb.process.run()
+    can't handle since it always decodes command output as UTF-8;
+    redirecting to a temp file and reading it back leniently avoids that
+    restriction."""
+    with tempfile.NamedTemporaryFile(prefix='devtool-changelog') as tmpf:
+        _run('git show %s > %s' % (shlex.quote('%s:%s' % (ref, fname)), shlex.quote(tmpf.name)), srctree)
+        with open(tmpf.name, 'r', errors='replace') as f:
+            return f.read()
+
+
 def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspace_path, is_git_source):
     """Extract changelog between old and new version using devtool git tags."""
     changelog_content = None
@@ -612,10 +624,10 @@ def _extract_changelog(srctree, pn, old_ver, new_ver, old_tag, new_tag, workspac
                 continue
             if re.search(r'(releas|relnote|change|news|migrat)', fname, re.IGNORECASE):
                 try:
-                    file_content, _ = _run('git show %s' % shlex.quote('%s:%s' % (new_tag, fname)), srctree)
+                    file_content = _git_show_file(srctree, new_tag, fname)
                 except bb.process.ExecutionError:
                     try:
-                        file_content, _ = _run('git show %s' % shlex.quote('%s:%s' % (old_tag, fname)), srctree)
+                        file_content = _git_show_file(srctree, old_tag, fname)
                     except bb.process.ExecutionError:
                         continue
                 if file_content.strip():
