diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java index 8de7ba6c1..171d80c3d 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/FSTest.java @@ -19,7 +19,6 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -182,7 +181,7 @@ public void testReadPipePosixCommandFailure() FS.readPipe(fs.userHome(), new String[] { "/bin/sh", "-c", "exit 1" }, - Charset.defaultCharset().name()); + SystemReader.getInstance().getDefaultCharset().name()); } @Test(expected = CommandFailedException.class) @@ -192,7 +191,7 @@ public void testReadPipeCommandStartFailure() FS.readPipe(fs.userHome(), new String[] { "this-command-does-not-exist" }, - Charset.defaultCharset().name()); + SystemReader.getInstance().getDefaultCharset().name()); } @Test diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index 3acceab09..74762a902 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -440,6 +440,7 @@ lockOnNotHeld=Lock on {0} not held. lockStreamClosed=Output to lock on {0} already closed lockStreamMultiple=Output to lock on {0} already opened logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}. +logInvalidDefaultCharset=System property "native.encoding" specifies unknown character set: {} logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement. logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}. logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java index ce3ad2239..dbfd41594 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java @@ -12,13 +12,13 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.nio.charset.Charset; import java.util.concurrent.Callable; import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.ProcessResult; +import org.eclipse.jgit.util.SystemReader; import org.eclipse.jgit.util.io.TeeOutputStream; /** @@ -171,7 +171,8 @@ protected void doRun() throws AbortedByHookException, IOException { getStdinArgs()); if (result.isExecutedWithError()) { handleError(new String(errorByteArray.toByteArray(), - Charset.defaultCharset().name()), result); + SystemReader.getInstance().getDefaultCharset().name()), + result); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 76340dabb..3d5d0607e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -468,6 +468,7 @@ public static JGitText get() { /***/ public String lockStreamClosed; /***/ public String lockStreamMultiple; /***/ public String logInconsistentFiletimeDiff; + /***/ public String logInvalidDefaultCharset; /***/ public String logLargerFiletimeDiff; /***/ public String logSmallerFiletime; /***/ public String logXDGConfigHomeInvalid; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java index 31a05933c..dd656e5f2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java @@ -23,7 +23,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; -import java.nio.charset.Charset; import java.nio.file.AccessDeniedException; import java.nio.file.FileStore; import java.nio.file.Files; @@ -1507,7 +1506,7 @@ protected File discoverGitSystemConfig() { try { v = readPipe(gitExe.getParentFile(), new String[] { gitExe.getPath(), "--version" }, //$NON-NLS-1$ - Charset.defaultCharset().name()); + SystemReader.getInstance().getDefaultCharset().name()); } catch (CommandFailedException e) { LOG.warn(e.getMessage()); return null; @@ -1527,7 +1526,7 @@ protected File discoverGitSystemConfig() { w = readPipe(gitExe.getParentFile(), new String[] { gitExe.getPath(), "config", "--system", //$NON-NLS-1$ //$NON-NLS-2$ "--edit" }, //$NON-NLS-1$ - Charset.defaultCharset().name(), env); + SystemReader.getInstance().getDefaultCharset().name(), env); } catch (CommandFailedException e) { LOG.warn(e.getMessage()); return null; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java index 946d81c73..1c113617f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java @@ -17,7 +17,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; -import java.nio.charset.Charset; import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileStore; import java.nio.file.FileSystemException; @@ -119,8 +118,8 @@ private static int readUmask() { new String[] { "sh", "-c", "umask" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ null, null); try (BufferedReader lineRead = new BufferedReader( - new InputStreamReader(p.getInputStream(), Charset - .defaultCharset().name()))) { + new InputStreamReader(p.getInputStream(), SystemReader + .getInstance().getDefaultCharset().name()))) { if (p.waitFor() == 0) { String s = lineRead.readLine(); if (s != null && s.matches("0?\\d{3}")) { //$NON-NLS-1$ @@ -150,7 +149,8 @@ protected File discoverGitExe() { try { String w = readPipe(userHome(), new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - Charset.defaultCharset().name()); + SystemReader.getInstance().getDefaultCharset() + .name()); if (!StringUtils.isEmptyOrNull(w)) { gitExe = new File(w); } @@ -168,7 +168,8 @@ protected File discoverGitExe() { try { String w = readPipe(userHome(), new String[] { "xcode-select", "-p" }, //$NON-NLS-1$ //$NON-NLS-2$ - Charset.defaultCharset().name()); + SystemReader.getInstance().getDefaultCharset() + .name()); if (StringUtils.isEmptyOrNull(w)) { gitExe = null; } else { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java index d44dc32d1..ff094f697 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32.java @@ -13,7 +13,6 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.file.FileVisitOption; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -150,8 +149,10 @@ protected File discoverGitExe() { String w; try { w = readPipe(userHome(), - new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - Charset.defaultCharset().name()); + new String[] { "bash", "--login", "-c", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "which git" }, // //$NON-NLS-1$ + SystemReader.getInstance().getDefaultCharset() + .name()); } catch (CommandFailedException e) { LOG.warn(e.getMessage()); return null; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java index df9c6c78f..93bf84719 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java @@ -1160,7 +1160,7 @@ public static String decodeNoFallback(final Charset cs, // Try the default character set. A small group of people // might actually use the same (or very similar) locale. - Charset defcs = Charset.defaultCharset(); + Charset defcs = SystemReader.getInstance().getDefaultCharset(); if (!defcs.equals(cs) && !defcs.equals(UTF_8)) { try { return decode(b, defcs); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java index 54fd539f6..16e257791 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SystemReader.java @@ -17,6 +17,9 @@ import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -198,6 +201,8 @@ public static void setInstance(SystemReader newReader) { private AtomicReference jgitConfig = new AtomicReference<>(); + private volatile Charset defaultCharset; + private void init() { // Creating ObjectChecker must be deferred. Unit tests change // behavior of is{Windows,MacOS} in constructor of subclass. @@ -438,6 +443,35 @@ public Locale getLocale() { return Locale.getDefault(); } + /** + * Retrieves the default {@link Charset} depending on the system locale. + * + * @return the {@link Charset} + * @since 6.0 + * @see JEP 400 + */ + public Charset getDefaultCharset() { + Charset result = defaultCharset; + if (result == null) { + // JEP 400: Java 18 populates this system property. + String encoding = getProperty("native.encoding"); //$NON-NLS-1$ + try { + if (!StringUtils.isEmptyOrNull(encoding)) { + result = Charset.forName(encoding); + } + } catch (IllegalCharsetNameException + | UnsupportedCharsetException e) { + LOG.error(JGitText.get().logInvalidDefaultCharset, encoding); + } + if (result == null) { + // This is always UTF-8 on Java >= 18. + result = Charset.defaultCharset(); + } + defaultCharset = result; + } + return result; + } + /** * Returns a simple date format instance as specified by the given pattern. *