diff --git a/Help/release/3.28.rst b/Help/release/3.28.rst
index 05918ec30..166bed93c 100644
--- a/Help/release/3.28.rst
+++ b/Help/release/3.28.rst
@@ -201,8 +201,8 @@ Updates
 
 Changes made since CMake 3.28.0 include the following.
 
-3.28.1, 3.28.2
---------------
+3.28.1, 3.28.2, 3.28.3
+----------------------
 
 * These versions made no changes to documented features or interfaces.
   Some implementation updates were made to support ecosystem changes
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 24f9aac86..54533faef 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 28)
-set(CMake_VERSION_PATCH 2)
+set(CMake_VERSION_PATCH 3)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
@@ -21,7 +21,7 @@ endif()
 
 if(NOT CMake_VERSION_NO_GIT)
   # If this source was exported by 'git archive', use its commit info.
-  set(git_info [==[1f25aa1a0a CMake 3.28.2]==])
+  set(git_info [==[5e984bb352 CMake 3.28.3]==])
 
   # Otherwise, try to identify the current development source version.
   if(NOT git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* "
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 5aaa0b8b9..d44863d16 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -1543,10 +1543,7 @@ bool cmGlobalGenerator::Compute()
   // so create the map from project name to vector of local generators
   this->FillProjectMap();
 
-  // Add automatically generated sources (e.g. unity build).
-  if (!this->AddAutomaticSources()) {
-    return false;
-  }
+  this->CreateFileGenerateOutputs();
 
   // Iterate through all targets and add verification targets for header sets
   if (!this->AddHeaderSetVerification()) {
@@ -1587,10 +1584,11 @@ bool cmGlobalGenerator::Compute()
     }
   }
 
+  // Add automatically generated sources (e.g. unity build).
   // Add unity sources after computing compile features.  Unity sources do
   // not change the set of languages or features, but we need to know them
   // to filter out sources that are scanned for C++ module dependencies.
-  if (!this->AddUnitySources()) {
+  if (!this->AddAutomaticSources()) {
     return false;
   }
 
@@ -1860,16 +1858,21 @@ bool cmGlobalGenerator::AddHeaderSetVerification()
   return true;
 }
 
-bool cmGlobalGenerator::AddAutomaticSources()
+void cmGlobalGenerator::CreateFileGenerateOutputs()
 {
   for (const auto& lg : this->LocalGenerators) {
     lg->CreateEvaluationFileOutputs();
   }
+}
+
+bool cmGlobalGenerator::AddAutomaticSources()
+{
   for (const auto& lg : this->LocalGenerators) {
     for (const auto& gt : lg->GetGeneratorTargets()) {
       if (!gt->CanCompileSources()) {
         continue;
       }
+      lg->AddUnityBuild(gt.get());
       lg->AddISPCDependencies(gt.get());
       // Targets that reuse a PCH are handled below.
       if (!gt->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
@@ -1889,29 +1892,8 @@ bool cmGlobalGenerator::AddAutomaticSources()
       }
     }
   }
-  // The above transformations may have changed the classification of sources.
-  // Clear the source list and classification cache (KindedSources) of all
-  // targets so that it will be recomputed correctly by the generators later
-  // now that the above transformations are done for all targets.
-  for (const auto& lg : this->LocalGenerators) {
-    for (const auto& gt : lg->GetGeneratorTargets()) {
-      gt->ClearSourcesCache();
-    }
-  }
-  return true;
-}
-
-bool cmGlobalGenerator::AddUnitySources()
-{
-  for (const auto& lg : this->LocalGenerators) {
-    for (const auto& gt : lg->GetGeneratorTargets()) {
-      if (!gt->CanCompileSources()) {
-        continue;
-      }
-      lg->AddUnityBuild(gt.get());
-    }
-  }
-  // The above transformation may have changed the classification of sources.
+  // The above transformations may have changed the classification of sources,
+  // e.g., sources that go into unity builds become SourceKindUnityBatched.
   // Clear the source list and classification cache (KindedSources) of all
   // targets so that it will be recomputed correctly by the generators later
   // now that the above transformations are done for all targets.
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 763136e38..bc80547e1 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -676,8 +676,8 @@ protected:
 
   bool AddHeaderSetVerification();
 
+  void CreateFileGenerateOutputs();
   bool AddAutomaticSources();
-  bool AddUnitySources();
 
   std::string SelectMakeProgram(const std::string& makeProgram,
                                 const std::string& makeDefault = "") const;
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 7337ea205..3e9834139 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -3131,6 +3131,7 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
 
   for (size_t ci = 0; ci < configs.size(); ++ci) {
     // FIXME: Refactor collection of sources to not evaluate object libraries.
+    // Their final set of object files might be transformed by unity builds.
     std::vector<cmSourceFile*> sources;
     target->GetSourceFiles(sources, configs[ci]);
     for (cmSourceFile* sf : sources) {
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity-check.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity-check.cmake
new file mode 100644
index 000000000..c17aaa06f
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity-check.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/PchInterface-check.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity.cmake b/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity.cmake
new file mode 100644
index 000000000..59c2523c6
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchInterfaceUnity.cmake
@@ -0,0 +1,2 @@
+set(CMAKE_UNITY_BUILD 1)
+include(PchInterface.cmake)
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
index b163369a2..c8a5c154c 100644
--- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ endfunction()
 run_cmake(DisabledPch)
 run_cmake(PchDebugGenex)
 run_test(PchInterface)
+run_test(PchInterfaceUnity)
 run_cmake(PchPrologueEpilogue)
 run_test(SkipPrecompileHeaders)
 run_test(CXXnotC)
diff --git a/Utilities/Scripts/update-zlib.bash b/Utilities/Scripts/update-zlib.bash
index 5913150f8..c18f11861 100755
--- a/Utilities/Scripts/update-zlib.bash
+++ b/Utilities/Scripts/update-zlib.bash
@@ -8,7 +8,7 @@ readonly name="zlib"
 readonly ownership="zlib upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmzlib"
 readonly repo="https://github.com/madler/zlib.git"
-readonly tag="v1.2.13"
+readonly tag="v1.2.13" # When updating, sync Copyright.txt below!
 readonly shortlog=false
 readonly paths="
   README
@@ -45,7 +45,7 @@ extract_source () {
     pushd "${extractdir}/${name}-reduced"
     echo "* -whitespace" > .gitattributes
     echo -n "'zlib' general purpose compression library
-version 1.2.12, March 27th, 2022
+version 1.2.13, October 13th, 2022
 
 Copyright " > Copyright.txt
     sed -n '/^ (C) 1995-/,+19 {s/^  \?//;p}' README >> Copyright.txt
diff --git a/Utilities/cmzlib/Copyright.txt b/Utilities/cmzlib/Copyright.txt
index 8f3d697df..85de4a10f 100644
--- a/Utilities/cmzlib/Copyright.txt
+++ b/Utilities/cmzlib/Copyright.txt
@@ -1,5 +1,5 @@
 'zlib' general purpose compression library
-version 1.2.12, March 27th, 2022
+version 1.2.13, October 13th, 2022
 
 Copyright (C) 1995-2022 Jean-loup Gailly and Mark Adler