Provide git config commit.cleanup
Add an enumeration for the possible values, and a method to resolve the "default" value. Give CommitConfig a static method to process a text according to a given clean-up mode and comment character. (The core.commentChar is not yet handled by JGit; it's hard-coded as #.) Bug: 553065 Change-Id: If6e384522275f73b713fbc29ffcaa1753c239dea Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
parent
2b01ac3389
commit
318a25f0e6
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2022, Thomas Wolf <thomas.wolf@paranor.ch> and others
|
||||||
|
*
|
||||||
|
* 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 static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class CommitConfigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaults() throws Exception {
|
||||||
|
CommitConfig cfg = parse("");
|
||||||
|
assertEquals("Unexpected clean-up mode", CleanupMode.DEFAULT,
|
||||||
|
cfg.getCleanupMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommitCleanup() throws Exception {
|
||||||
|
String[] values = { "strip", "whitespace", "verbatim", "scissors",
|
||||||
|
"default" };
|
||||||
|
CleanupMode[] expected = { CleanupMode.STRIP, CleanupMode.WHITESPACE,
|
||||||
|
CleanupMode.VERBATIM, CleanupMode.SCISSORS,
|
||||||
|
CleanupMode.DEFAULT };
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
CommitConfig cfg = parse("[commit]\n\tcleanup = " + values[i]);
|
||||||
|
assertEquals("Unexpected clean-up mode", expected[i],
|
||||||
|
cfg.getCleanupMode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolve() throws Exception {
|
||||||
|
String[] values = { "strip", "whitespace", "verbatim", "scissors",
|
||||||
|
"default" };
|
||||||
|
CleanupMode[] expected = { CleanupMode.STRIP, CleanupMode.WHITESPACE,
|
||||||
|
CleanupMode.VERBATIM, CleanupMode.SCISSORS,
|
||||||
|
CleanupMode.DEFAULT };
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
CommitConfig cfg = parse("[commit]\n\tcleanup = " + values[i]);
|
||||||
|
for (CleanupMode mode : CleanupMode.values()) {
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
CleanupMode resolved = cfg.resolve(mode, j == 0);
|
||||||
|
if (mode != CleanupMode.DEFAULT) {
|
||||||
|
assertEquals("Clean-up mode should be unchanged", mode,
|
||||||
|
resolved);
|
||||||
|
} else if (i + 1 < values.length) {
|
||||||
|
assertEquals("Unexpected clean-up mode", expected[i],
|
||||||
|
resolved);
|
||||||
|
} else {
|
||||||
|
assertEquals("Unexpected clean-up mode",
|
||||||
|
j == 0 ? CleanupMode.STRIP
|
||||||
|
: CleanupMode.WHITESPACE,
|
||||||
|
resolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanDefaultThrows() throws Exception {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> CommitConfig
|
||||||
|
.cleanText("Whatever", CleanupMode.DEFAULT, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanVerbatim() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n";
|
||||||
|
assertEquals("Unexpected message change", message,
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.VERBATIM, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanWhitespace() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n";
|
||||||
|
assertEquals("Unexpected message change",
|
||||||
|
"Whatever\n\n# A comment\n\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.WHITESPACE, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanStrip() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n";
|
||||||
|
assertEquals("Unexpected message change", "Whatever\n\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.STRIP, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanStripCustomChar() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n";
|
||||||
|
assertEquals("Unexpected message change",
|
||||||
|
"Whatever\n\n# Not a comment\n\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.STRIP, '<'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissors() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n"
|
||||||
|
+ "# ------------------------ >8 ------------------------\n"
|
||||||
|
+ "More\nMore\n";
|
||||||
|
assertEquals("Unexpected message change",
|
||||||
|
"Whatever\n\n# Not a comment\n\n <A comment\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsCustomChar() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n"
|
||||||
|
+ "< ------------------------ >8 ------------------------\n"
|
||||||
|
+ "More\nMore\n";
|
||||||
|
assertEquals("Unexpected message change",
|
||||||
|
"Whatever\n\n# Not a comment\n\n <A comment\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '<'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsAtTop() throws Exception {
|
||||||
|
String message = "# ------------------------ >8 ------------------------\n"
|
||||||
|
+ "\n \nWhatever \n\n\n# Not a comment\n\n <A comment\nMore\t \n\n\n"
|
||||||
|
+ "More\nMore\n";
|
||||||
|
assertEquals("Unexpected message change", "",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsNoScissor() throws Exception {
|
||||||
|
String message = "\n \nWhatever \n\n\n# A comment\n\nMore\t \n\n\n";
|
||||||
|
assertEquals("Unexpected message change",
|
||||||
|
"Whatever\n\n# A comment\n\nMore\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsNoScissor2() throws Exception {
|
||||||
|
String message = "Text\n"
|
||||||
|
+ "## ------------------------ >8 ------------------------\n"
|
||||||
|
+ "More\nMore\n";
|
||||||
|
assertEquals("Unexpected message change", message,
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsNoScissor3() throws Exception {
|
||||||
|
String message = "Text\n"
|
||||||
|
// Wrong number of dashes
|
||||||
|
+ "# ----------------------- >8 ------------------------\n"
|
||||||
|
+ "More\nMore\n";
|
||||||
|
assertEquals("Unexpected message change", message,
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCleanScissorsAtEnd() throws Exception {
|
||||||
|
String message = "Text\n"
|
||||||
|
+ "# ------------------------ >8 ------------------------\n";
|
||||||
|
assertEquals("Unexpected message change", "Text\n",
|
||||||
|
CommitConfig.cleanText(message, CleanupMode.SCISSORS, '#'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CommitConfig parse(String content)
|
||||||
|
throws ConfigInvalidException {
|
||||||
|
Config c = new Config();
|
||||||
|
c.fromText(content);
|
||||||
|
return c.get(CommitConfig.KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,11 +18,13 @@
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.eclipse.jgit.annotations.NonNull;
|
import org.eclipse.jgit.annotations.NonNull;
|
||||||
import org.eclipse.jgit.annotations.Nullable;
|
import org.eclipse.jgit.annotations.Nullable;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.internal.JGitText;
|
import org.eclipse.jgit.internal.JGitText;
|
||||||
|
import org.eclipse.jgit.lib.Config.ConfigEnum;
|
||||||
import org.eclipse.jgit.lib.Config.SectionParser;
|
import org.eclipse.jgit.lib.Config.SectionParser;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
|
@ -34,22 +36,76 @@
|
||||||
* @since 5.13
|
* @since 5.13
|
||||||
*/
|
*/
|
||||||
public class CommitConfig {
|
public class CommitConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for {@link Config#get(SectionParser)}.
|
* Key for {@link Config#get(SectionParser)}.
|
||||||
*/
|
*/
|
||||||
public static final Config.SectionParser<CommitConfig> KEY = CommitConfig::new;
|
public static final Config.SectionParser<CommitConfig> KEY = CommitConfig::new;
|
||||||
|
|
||||||
|
private static final String CUT = " ------------------------ >8 ------------------------\n"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How to clean up commit messages when committing.
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public enum CleanupMode implements ConfigEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #WHITESPACE}, additionally remove comment lines.
|
||||||
|
*/
|
||||||
|
STRIP,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove trailing whitespace and leading and trailing empty lines;
|
||||||
|
* collapse multiple empty lines to a single one.
|
||||||
|
*/
|
||||||
|
WHITESPACE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make no changes.
|
||||||
|
*/
|
||||||
|
VERBATIM,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Omit everything from the first "scissor" line on, then apply
|
||||||
|
* {@link #WHITESPACE}.
|
||||||
|
*/
|
||||||
|
SCISSORS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use {@link #STRIP} for user-edited messages, otherwise
|
||||||
|
* {@link #WHITESPACE}, unless overridden by a git config setting other
|
||||||
|
* than DEFAULT.
|
||||||
|
*/
|
||||||
|
DEFAULT;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toConfigValue() {
|
||||||
|
return name().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matchConfigValue(String in) {
|
||||||
|
return toConfigValue().equals(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final static Charset DEFAULT_COMMIT_MESSAGE_ENCODING = StandardCharsets.UTF_8;
|
private final static Charset DEFAULT_COMMIT_MESSAGE_ENCODING = StandardCharsets.UTF_8;
|
||||||
|
|
||||||
private String i18nCommitEncoding;
|
private String i18nCommitEncoding;
|
||||||
|
|
||||||
private String commitTemplatePath;
|
private String commitTemplatePath;
|
||||||
|
|
||||||
|
private CleanupMode cleanupMode;
|
||||||
|
|
||||||
private CommitConfig(Config rc) {
|
private CommitConfig(Config rc) {
|
||||||
commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION,
|
commitTemplatePath = rc.getString(ConfigConstants.CONFIG_COMMIT_SECTION,
|
||||||
null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE);
|
null, ConfigConstants.CONFIG_KEY_COMMIT_TEMPLATE);
|
||||||
i18nCommitEncoding = rc.getString(ConfigConstants.CONFIG_SECTION_I18N,
|
i18nCommitEncoding = rc.getString(ConfigConstants.CONFIG_SECTION_I18N,
|
||||||
null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING);
|
null, ConfigConstants.CONFIG_KEY_COMMIT_ENCODING);
|
||||||
|
cleanupMode = rc.getEnum(ConfigConstants.CONFIG_COMMIT_SECTION, null,
|
||||||
|
ConfigConstants.CONFIG_KEY_CLEANUP, CleanupMode.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,6 +130,48 @@ public String getCommitEncoding() {
|
||||||
return i18nCommitEncoding;
|
return i18nCommitEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the {@link CleanupMode} as given by git config
|
||||||
|
* {@code commit.cleanup}.
|
||||||
|
*
|
||||||
|
* @return the {@link CleanupMode}; {@link CleanupMode#DEFAULT} if the git
|
||||||
|
* config is not set
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public CleanupMode getCleanupMode() {
|
||||||
|
return cleanupMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a non-default {@link CleanupMode} from the given mode and the
|
||||||
|
* git config.
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* {@link CleanupMode} to resolve
|
||||||
|
* @param defaultStrip
|
||||||
|
* if {@code true} return {@link CleanupMode#STRIP} if the git
|
||||||
|
* config is also "default", otherwise return
|
||||||
|
* {@link CleanupMode#WHITESPACE}
|
||||||
|
* @return the {@code mode}, if it is not {@link CleanupMode#DEFAULT},
|
||||||
|
* otherwise the resolved mode, which is never
|
||||||
|
* {@link CleanupMode#DEFAULT}
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public CleanupMode resolve(@NonNull CleanupMode mode,
|
||||||
|
boolean defaultStrip) {
|
||||||
|
if (CleanupMode.DEFAULT == mode) {
|
||||||
|
CleanupMode defaultMode = getCleanupMode();
|
||||||
|
if (CleanupMode.DEFAULT == defaultMode) {
|
||||||
|
return defaultStrip ? CleanupMode.STRIP
|
||||||
|
: CleanupMode.WHITESPACE;
|
||||||
|
}
|
||||||
|
return defaultMode;
|
||||||
|
}
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the content to the commit template as defined in
|
* Get the content to the commit template as defined in
|
||||||
* {@code commit.template}. If no {@code i18n.commitEncoding} is specified,
|
* {@code commit.template}. If no {@code i18n.commitEncoding} is specified,
|
||||||
|
@ -135,4 +233,86 @@ private Charset getEncoding() throws ConfigInvalidException {
|
||||||
|
|
||||||
return commitMessageEncoding;
|
return commitMessageEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a text according to the given {@link CleanupMode}.
|
||||||
|
*
|
||||||
|
* @param text
|
||||||
|
* text to process
|
||||||
|
* @param mode
|
||||||
|
* {@link CleanupMode} to use
|
||||||
|
* @param commentChar
|
||||||
|
* comment character (normally {@code #}) to use if {@code mode}
|
||||||
|
* is {@link CleanupMode#STRIP} or {@link CleanupMode#SCISSORS}
|
||||||
|
* @return the processed text
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if {@code mode} is {@link CleanupMode#DEFAULT} (use
|
||||||
|
* {@link #resolve(CleanupMode, boolean)} first)
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static String cleanText(@NonNull String text,
|
||||||
|
@NonNull CleanupMode mode, char commentChar) {
|
||||||
|
String toProcess = text;
|
||||||
|
boolean strip = false;
|
||||||
|
switch (mode) {
|
||||||
|
case VERBATIM:
|
||||||
|
return text;
|
||||||
|
case SCISSORS:
|
||||||
|
String cut = commentChar + CUT;
|
||||||
|
if (text.startsWith(cut)) {
|
||||||
|
return ""; //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
int cutPos = text.indexOf('\n' + cut);
|
||||||
|
if (cutPos >= 0) {
|
||||||
|
toProcess = text.substring(0, cutPos + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STRIP:
|
||||||
|
strip = true;
|
||||||
|
break;
|
||||||
|
case WHITESPACE:
|
||||||
|
break;
|
||||||
|
case DEFAULT:
|
||||||
|
default:
|
||||||
|
// Internal error; no translation
|
||||||
|
throw new IllegalArgumentException("Invalid clean-up mode " + mode); //$NON-NLS-1$
|
||||||
|
}
|
||||||
|
// WHITESPACE
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
boolean lastWasEmpty = true;
|
||||||
|
for (String line : toProcess.split("\n")) { //$NON-NLS-1$
|
||||||
|
line = line.stripTrailing();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
if (!lastWasEmpty) {
|
||||||
|
result.append('\n');
|
||||||
|
lastWasEmpty = true;
|
||||||
|
}
|
||||||
|
} else if (!strip || !isComment(line, commentChar)) {
|
||||||
|
lastWasEmpty = false;
|
||||||
|
result.append(line).append('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int bufferSize = result.length();
|
||||||
|
if (lastWasEmpty && bufferSize > 0) {
|
||||||
|
bufferSize--;
|
||||||
|
result.setLength(bufferSize);
|
||||||
|
}
|
||||||
|
if (bufferSize > 0 && !toProcess.endsWith("\n")) { //$NON-NLS-1$
|
||||||
|
if (result.charAt(bufferSize - 1) == '\n') {
|
||||||
|
result.setLength(bufferSize - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isComment(String text, char commentChar) {
|
||||||
|
int len = text.length();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
char ch = text.charAt(i);
|
||||||
|
if (!Character.isWhitespace(ch)) {
|
||||||
|
return ch == commentChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -181,6 +181,13 @@ public final class ConfigConstants {
|
||||||
*/
|
*/
|
||||||
public static final String CONFIG_TAG_SECTION = "tag";
|
public static final String CONFIG_TAG_SECTION = "tag";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "cleanup" key
|
||||||
|
*
|
||||||
|
* @since 6.1
|
||||||
|
*/
|
||||||
|
public static final String CONFIG_KEY_CLEANUP = "cleanup";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "gpgSign" key
|
* The "gpgSign" key
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue