[6.4.0] Fix unconditional Skyframe invalidation with --lockfile_mode=… (#19848)

…update

Ensure that `BazelLockFileModule` only updates `MODULE.bazel.lock` if
the content of the file needs to change. Every such update changes the
file's metadata, which results in Skyframe invalidation of, in
particular, all configurations. This broke `bazel config`, which uses
`MemoizingEvaluator#getDoneValues()` to directly observe Skyframe state.

Compared to the original commit
https://github.com/bazelbuild/bazel/commit/78db9ae9a545a9586dbb02d7831f5302594e01cb,
this cherry-pick does not include the change to `bazel config` as it may
not be backwards compatible (changes the exit code in certain
situations).

Fixes #19823

Closes #19842.

PiperOrigin-RevId: 574133346
Change-Id: I5886c91fc6b7b938a7dee59ea75aa7b8afb5b161

Fixes #19843
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
index a664ae3..4036eb3 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelLockFileModule.java
@@ -106,8 +106,13 @@
                 combineModuleExtensions(lockfile.getModuleExtensions(), oldExtensionUsages))
             .build();
 
-    // Write the new value to the file
-    updateLockfile(lockfilePath, lockfile);
+    // Write the new value to the file, but only if needed. This is not just a performance
+    // optimization: whenever the lockfile is updated, most Skyframe nodes will be marked as dirty
+    // on the next build, which breaks commands such as `bazel config` that rely on
+    // com.google.devtools.build.skyframe.MemoizingEvaluator#getDoneValues.
+    if (!lockfile.equals(oldLockfile)) {
+      updateLockfile(lockfilePath, lockfile);
+    }
     this.moduleResolutionEvent = null;
     this.extensionResolutionEventsMap.clear();
   }