From e2798413f9a2a8e7a701d1f7b5e5fac7399ed696 Mon Sep 17 00:00:00 2001 From: Julian Ruppel Date: Mon, 26 Apr 2021 10:24:05 +0200 Subject: [PATCH] Support commit.template config property Adds functionality to read the git commit.template property. The template content is read either via a default encoding or, if present, via encoding specified by i18n.commitEncoding property. Bug: 446355 Change-Id: I0c45db98e324ddff26a7e0262835f259d6528a86 Signed-off-by: Julian Ruppel --- .../tst/org/eclipse/jgit/lib/ConfigTest.java | 103 ++++++++++++++ .../eclipse/jgit/internal/JGitText.properties | 1 + .../org/eclipse/jgit/internal/JGitText.java | 1 + .../org/eclipse/jgit/lib/CommitConfig.java | 126 ++++++++++++++++++ .../org/eclipse/jgit/lib/ConfigConstants.java | 14 ++ 5 files changed, 245 insertions(+) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index 327b554b4..fe3c1db50 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -34,6 +34,7 @@ import static org.junit.Assert.fail; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.text.MessageFormat; @@ -50,6 +51,7 @@ import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.MockSystemReader; import org.eclipse.jgit.merge.MergeConfig; import org.eclipse.jgit.storage.file.FileBasedConfig; @@ -1463,6 +1465,107 @@ public void testWhitespaceContinuation() throws ConfigInvalidException { assertEquals("tr ue", parseEscapedValue("tr \\\r\n ue")); } + @Test + public void testCommitTemplateEmptyConfig() + throws ConfigInvalidException, IOException { + // no values defined nowhere + Config config = new Config(null); + assertNull(config.get(CommitConfig.KEY).getCommitTemplatePath()); + assertNull(config.get(CommitConfig.KEY).getCommitTemplateContent()); + } + + @Test + public void testCommitTemplateConfig() + throws ConfigInvalidException, IOException { + + File tempFile = tmp.newFile("testCommitTemplate-"); + String templateContent = "content of the template"; + JGitTestUtil.write(tempFile, templateContent); + String expectedTemplatePath = tempFile.getPath(); + + Config config = parse( + "[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + + String templatePath = config.get(CommitConfig.KEY) + .getCommitTemplatePath(); + String commitEncoding = config.get(CommitConfig.KEY) + .getCommitEncoding(); + assertEquals(expectedTemplatePath, templatePath); + assertEquals(templateContent, + config.get(CommitConfig.KEY).getCommitTemplateContent()); + assertNull("no commitEncoding has been set so it must be null", + commitEncoding); + } + + @Test + public void testCommitTemplateEncoding() + throws ConfigInvalidException, IOException { + Config config = new Config(null); + File tempFile = tmp.newFile("testCommitTemplate-"); + String templateContent = "content of the template"; + JGitTestUtil.write(tempFile, templateContent); + String expectedTemplatePath = tempFile.getPath(); + config = parse("[i18n]\n\tcommitEncoding = utf-8\n" + + "[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + assertEquals(templateContent, + config.get(CommitConfig.KEY).getCommitTemplateContent()); + String commitEncoding = config.get(CommitConfig.KEY) + .getCommitEncoding(); + assertEquals("commitEncoding has been set to utf-8 it must be utf-8", + "utf-8", commitEncoding); + } + + @Test + public void testCommitTemplatePathInHomeDirecory() + throws ConfigInvalidException, IOException { + Config config = new Config(null); + File tempFile = tmp.newFile("testCommitTemplate-"); + String templateContent = "content of the template"; + JGitTestUtil.write(tempFile, templateContent); + // proper evaluation of the ~/ directory + String homeDir = System.getProperty("user.home"); + File tempFileInHomeDirectory = File.createTempFile("fileInHomeFolder", + ".tmp", new File(homeDir)); + tempFileInHomeDirectory.deleteOnExit(); + JGitTestUtil.write(tempFileInHomeDirectory, templateContent); + String expectedTemplatePath = tempFileInHomeDirectory.getPath() + .replace(homeDir, "~"); + config = parse("[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + String templatePath = config.get(CommitConfig.KEY) + .getCommitTemplatePath(); + assertEquals(expectedTemplatePath, templatePath); + assertEquals(templateContent, + config.get(CommitConfig.KEY).getCommitTemplateContent()); + } + + @Test(expected = ConfigInvalidException.class) + public void testCommitTemplateWithInvalidEncoding() + throws ConfigInvalidException, IOException { + Config config = new Config(null); + File tempFile = tmp.newFile("testCommitTemplate-"); + String templateContent = "content of the template"; + JGitTestUtil.write(tempFile, templateContent); + config = parse("[i18n]\n\tcommitEncoding = invalidEcoding\n" + + "[commit]\n\ttemplate = " + tempFile.getPath() + "\n"); + config.get(CommitConfig.KEY).getCommitTemplateContent(); + } + + @Test(expected = FileNotFoundException.class) + public void testCommitTemplateWithInvalidPath() + throws ConfigInvalidException, IOException { + Config config = new Config(null); + File tempFile = tmp.newFile("testCommitTemplate-"); + String templateContent = "content of the template"; + JGitTestUtil.write(tempFile, templateContent); + // commit message encoding + String expectedTemplatePath = "nonExistingTemplate"; + config = parse("[commit]\n\ttemplate = " + expectedTemplatePath + "\n"); + String templatePath = config.get(CommitConfig.KEY) + .getCommitTemplatePath(); + assertEquals(expectedTemplatePath, templatePath); + config.get(CommitConfig.KEY).getCommitTemplateContent(); + } + private static void assertValueRoundTrip(String value) throws ConfigInvalidException { assertValueRoundTrip(value, value); 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 33e7452d9..3acceab09 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -365,6 +365,7 @@ invalidBooleanValue=Invalid boolean value: {0}.{1}={2} invalidChannel=Invalid channel {0} invalidCommitParentNumber=Invalid commit parent number invalidDepth=Invalid depth: {0} +invalidEncoding=Invalid encoding from git config i18n.commitEncoding: {0} invalidEncryption=Invalid encryption invalidExpandWildcard=ExpandFromSource on a refspec that can have mismatched wildcards does not make sense. invalidFilter=Invalid filter: {0} 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 8622e0a1a..76340dabb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -393,6 +393,7 @@ public static JGitText get() { /***/ public String invalidChannel; /***/ public String invalidCommitParentNumber; /***/ public String invalidDepth; + /***/ public String invalidEncoding; /***/ public String invalidEncryption; /***/ public String invalidExpandWildcard; /***/ public String invalidFilter; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java new file mode 100644 index 000000000..e4e7cd6e0 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CommitConfig.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Julian Ruppel + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package org.eclipse.jgit.lib; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.StandardCharsets; +import java.nio.charset.UnsupportedCharsetException; +import java.text.MessageFormat; +import org.eclipse.jgit.annotations.Nullable; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.internal.JGitText; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.util.IO; +import org.eclipse.jgit.util.RawParseUtils; + +/** + * The standard "commit" configuration parameters. + * + * @since 5.13 + */ +public class CommitConfig { + /** + * Key for {@link Config#get(SectionParser)}. + */ + public static final Config.SectionParser KEY = CommitConfig::new; + + private final static Charset DEFAULT_COMMIT_MESSAGE_ENCODING = StandardCharsets.UTF_8; + + private String i18nCommitEncoding; + + private String commitTemplatePath; + + private CommitConfig(Config rc) { + commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION, + null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE); + i18nCommitEncoding = rc.getString(ConfigConstants.CONFIG_SECTION_I18N, + null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING); + } + + /** + * Get the path to the commit template as defined in the git + * {@code commit.template} property. + * + * @return the path to commit template or {@code null} if not present. + */ + @Nullable + public String getCommitTemplatePath() { + return commitTemplatePath; + } + + /** + * Get the encoding of the commit as defined in the git + * {@code i18n.commitEncoding} property. + * + * @return the encoding or {@code null} if not present. + */ + @Nullable + public String getCommitEncoding() { + return i18nCommitEncoding; + } + + /** + * Get the content to the commit template as defined in + * {@code commit.template}. If no {@code i18n.commitEncoding} is specified, + * UTF-8 fallback is used. + * + * @return content of the commit template or {@code null} if not present. + * @throws IOException + * if the template file can not be read + * @throws FileNotFoundException + * if the template file does not exists + * @throws ConfigInvalidException + * if a {@code commitEncoding} is specified and is invalid + */ + @Nullable + public String getCommitTemplateContent() + throws FileNotFoundException, IOException, ConfigInvalidException { + + if (commitTemplatePath == null) { + return null; + } + + File commitTemplateFile; + if (commitTemplatePath.startsWith("~/")) { //$NON-NLS-1$ + commitTemplateFile = FS.DETECTED.resolve(FS.DETECTED.userHome(), + commitTemplatePath.substring(2)); + } else { + commitTemplateFile = FS.DETECTED.resolve(null, commitTemplatePath); + } + + Charset commitMessageEncoding = getEncoding(); + return RawParseUtils.decode(commitMessageEncoding, + IO.readFully(commitTemplateFile)); + + } + + private Charset getEncoding() throws ConfigInvalidException { + Charset commitMessageEncoding = DEFAULT_COMMIT_MESSAGE_ENCODING; + + if (i18nCommitEncoding == null) { + return null; + } + + try { + commitMessageEncoding = Charset.forName(i18nCommitEncoding); + } catch (IllegalCharsetNameException | UnsupportedCharsetException e) { + throw new ConfigInvalidException(MessageFormat.format( + JGitText.get().invalidEncoding, i18nCommitEncoding), e); + } + + return commitMessageEncoding; + } +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index b6f4798dc..24eebc6a1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -124,6 +124,13 @@ public final class ConfigConstants { */ public static final String CONFIG_COMMIT_SECTION = "commit"; + /** + * The "template" key + * + * @since 5.13 + */ + public static final String CONFIG_KEY_COMMIT_TEMPLATE = "template"; + /** * The "tag" section * @@ -517,6 +524,13 @@ public final class ConfigConstants { */ public static final String CONFIG_SECTION_I18N = "i18n"; + /** + * The "commitEncoding" key + * + * @since 5.13 + */ + public static final String CONFIG_KEY_COMMIT_ENCODING = "commitEncoding"; + /** * The "logOutputEncoding" key *