diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 2ea1692952..9e25b1d3d3 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -9,6 +9,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * Adds support for worktrees (fixes [#1765](https://github.com/diffplug/spotless/issues/1765)) * Bump default `google-java-format` version to latest `1.24.0` -> `1.28.0`. ([#2345](https://github.com/diffplug/spotless/pull/2345)) * Bump default `ktlint` version to latest `1.5.0` -> `1.7.1`. ([#2555](https://github.com/diffplug/spotless/pull/2555)) +### Fixed +* Respect system gitconfig when performing git operations ([#2404](https://github.com/diffplug/spotless/issues/2404)) ## [7.2.1] - 2025-07-21 ### Fixed @@ -34,8 +36,6 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * Make sure npm-based formatters use the correct `node_modules` directory when running in parallel. ([#2542](https://github.com/diffplug/spotless/pull/2542)) ### Changed * Bump internal dependencies for npm-based formatters ([#2542](https://github.com/diffplug/spotless/pull/2542)) - -### Changed * scalafmt: enforce version consistency between the version configured in Spotless and the version declared in Scalafmt config file ([#2460](https://github.com/diffplug/spotless/issues/2460)) ## [7.0.4] - 2025-05-27 diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java index b2c3adc459..c58f0d39c4 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GitRatchetGradle.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,14 @@ package com.diffplug.gradle.spotless; import java.io.File; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.SystemReader; @@ -28,20 +32,86 @@ /** Gradle implementation of GitRatchet. */ public class GitRatchetGradle extends GitRatchet { + private static final String[] GIT_EXEC_CANDIDATES = {"git", "git.exe", "git.cmd"}; + static { - preventJGitFromCallingExecutables(); + GitRatchetGradle.redirectJGitExecutions(); } - static void preventJGitFromCallingExecutables() { - SystemReader reader = SystemReader.getInstance(); - SystemReader.setInstance(new DelegatingSystemReader(reader) { + static void redirectJGitExecutions() { + SystemReader existing = SystemReader.getInstance(); + SystemReader.setInstance(new DelegatingSystemReader(existing) { + private AtomicReference systemConfig = new AtomicReference<>(); + + @Override + public StoredConfig getSystemConfig() throws ConfigInvalidException, IOException { + FileBasedConfig c = systemConfig.get(); + if (c == null) { + systemConfig.compareAndSet(null, + this.openSystemConfig(this.getJGitConfig(), FS.DETECTED)); + c = systemConfig.get(); + } + updateAll(c); + return c; + } + + // lifted from SystemReader since it's private + private void updateAll(Config config) throws ConfigInvalidException, IOException { + if (config == null) { + return; + } + + updateAll(config.getBaseConfig()); + if (config instanceof FileBasedConfig) { + FileBasedConfig cfg = (FileBasedConfig) config; + if (cfg.isOutdated()) { + cfg.load(); + } + } + } + @Override - public String getenv(String variable) { - if ("PATH".equals(variable)) { - return ""; - } else { - return super.getenv(variable); + public FileBasedConfig openSystemConfig(final Config parent, final FS fs) { + // cgit logic: https://git.kernel.org/pub/scm/git/git.git/tree/config.c#n1973 - in git_system_config() + // They check the GIT_CONFIG_SYSTEM env var first, then follow up with logic based on compile-time parameters + // We can't replicate this exactly so we'll do the closest approximation that Gradle will allow. + final String systemPath = this.getenv("GIT_CONFIG_SYSTEM"); + if (systemPath != null) { + fs.setGitSystemConfig(new File(systemPath).getAbsoluteFile()); + return super.openSystemConfig(parent, fs); + } + + // match FS.searchPath + File gitExec = null; + final String path = this.getenv("PATH"); + if (path != null) { + outer: for (final String p : path.split(File.pathSeparator)) { + for (final String name : GIT_EXEC_CANDIDATES) { + final File candidate = new File(p, name); + if (candidate.isFile() && candidate.canExecute()) { + gitExec = candidate.getAbsoluteFile(); + break outer; + } + } + } + } + + // Guess at common locations + if (gitExec != null) { + // If git exec is at /bin/git, this returns + File prefix = gitExec.getParentFile().getParentFile(); + + // Then we try to resolve a config + final File systemConfig = new File(prefix, "etc/gitconfig"); + if (systemConfig.exists()) { + fs.setGitSystemConfig(systemConfig); + return super.openSystemConfig(parent, fs); + } } + + // Fallback to the non-prefixed path (this is not the logic that cgit uses, but oh well) + fs.setGitSystemConfig(new File("/etc/gitconfig")); + return super.openSystemConfig(parent, fs); } }); } @@ -70,7 +140,7 @@ public String getHostname() { @Override public String getenv(String variable) { - return reader.getProperty(variable); + return reader.getenv(variable); } @Override