diff --git a/pom.xml b/pom.xml
index af424fa..b43c962 100644
--- a/pom.xml
+++ b/pom.xml
@@ -83,9 +83,9 @@
provided
- io.github.java-diff-utils
- java-diff-utils
- 4.12
+ org.eclipse.jgit
+ org.eclipse.jgit
+ 5.13.5.202508271544-r
@@ -133,6 +133,7 @@
apache-20-license.template.txt
apache-20-license.template.txt
+ src/it/apply-patch-existing-file/src/**
@@ -291,6 +292,33 @@
true
+
+ org.apache.maven.plugins
+ maven-invoker-plugin
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.14.4
+
+
+
+ ${project.build.directory}/it
+ ${project.build.directory}/it-repo
+ verify
+ true
+
+
+
+ integration-tests
+
+ install
+ run
+
+
+
+
+
diff --git a/src/it/apply-patch-existing-file/pom.xml b/src/it/apply-patch-existing-file/pom.xml
new file mode 100644
index 0000000..9d25611
--- /dev/null
+++ b/src/it/apply-patch-existing-file/pom.xml
@@ -0,0 +1,50 @@
+
+
+ 4.0.0
+ com.evolvedbinary.maven.plugins.it.patch-maven-plugin
+ apply-patch-existing-file
+ 1.0.0
+ jar
+
+ Test Applying a Patch to an Existing File
+
+
+
+
+
+ com.evolvedbinary.maven.plugins
+ patch-maven-plugin
+ @project.version@
+
+
+ apply-patch
+ process-resources
+ apply
+
+ ${project.build.outputDirectory}
+ ${project.basedir}/src/main/patches
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/it/apply-patch-existing-file/src/main/patches/0001-greeting-change.patch b/src/it/apply-patch-existing-file/src/main/patches/0001-greeting-change.patch
new file mode 100644
index 0000000..8b6f024
--- /dev/null
+++ b/src/it/apply-patch-existing-file/src/main/patches/0001-greeting-change.patch
@@ -0,0 +1,5 @@
+--- a/greeting.txt
++++ b/greeting.txt
+@@ -1 +1 @@
+-Hello, World!
++Hello, Elemental!
\ No newline at end of file
diff --git a/src/it/apply-patch-existing-file/src/main/resources/greeting.txt b/src/it/apply-patch-existing-file/src/main/resources/greeting.txt
new file mode 100644
index 0000000..b45ef6f
--- /dev/null
+++ b/src/it/apply-patch-existing-file/src/main/resources/greeting.txt
@@ -0,0 +1 @@
+Hello, World!
\ No newline at end of file
diff --git a/src/it/apply-patch-existing-file/verify.groovy b/src/it/apply-patch-existing-file/verify.groovy
new file mode 100644
index 0000000..ffb80ee
--- /dev/null
+++ b/src/it/apply-patch-existing-file/verify.groovy
@@ -0,0 +1,36 @@
+import java.nio.file.Files
+import java.nio.file.Path
+
+import static org.junit.jupiter.api.Assertions.assertTrue
+import static org.junit.jupiter.api.Assertions.fail
+
+
+/*
+ * Copyright [2024] [Lukas Mansour]
+ * Modifications: Copyright (C) 2026, Evolved Binary Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+
+//open the file and check if the content(hello elemental) is truly there
+final Path patchedFile = basedir.toPath().resolve("target/classes/greeting.txt")
+if (!Files.exists(patchedFile)) {
+ fail("Patched file is missing: " + patchedFile)
+}
+
+final String content = new String(Files.readAllBytes(patchedFile))
+assertTrue(content.contains("Hello, Elemental!"), "Expected content was not found in: " + patchedFile)
+return true
+
diff --git a/src/it/apply-patch-new-file/pom.xml b/src/it/apply-patch-new-file/pom.xml
new file mode 100644
index 0000000..a08a963
--- /dev/null
+++ b/src/it/apply-patch-new-file/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+ com.evolvedbinary.maven.plugins.it.patch-maven-plugin
+ apply-patch-new-file
+ 1.0.0
+ jar
+
+ Test Applying a Patch that Creates a New File
+
+
+
+
+ com.evolvedbinary.maven.plugins
+ patch-maven-plugin
+ @project.version@
+
+
+ apply-patch
+ process-resources
+ apply
+
+ ${project.build.outputDirectory}
+ ${project.basedir}/src/main/patches
+ true
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/it/apply-patch-new-file/src/main/patches/0001-new-file-add.patch b/src/it/apply-patch-new-file/src/main/patches/0001-new-file-add.patch
new file mode 100644
index 0000000..ddf2bb4
--- /dev/null
+++ b/src/it/apply-patch-new-file/src/main/patches/0001-new-file-add.patch
@@ -0,0 +1,5 @@
+--- /dev/null
++++ b/new-file.txt
+@@ -0,0 +1,2 @@
++Hello, this is a
++brand new file!
\ No newline at end of file
diff --git a/src/it/apply-patch-new-file/src/main/resources/.gitkeep b/src/it/apply-patch-new-file/src/main/resources/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/it/apply-patch-new-file/verify.groovy b/src/it/apply-patch-new-file/verify.groovy
new file mode 100644
index 0000000..06592f3
--- /dev/null
+++ b/src/it/apply-patch-new-file/verify.groovy
@@ -0,0 +1,33 @@
+import java.nio.file.Files
+import java.nio.file.Path
+
+import static org.junit.jupiter.api.Assertions.assertTrue
+import static org.junit.jupiter.api.Assertions.fail
+
+
+/*
+ * Copyright [2024] [Lukas Mansour]
+ * Modifications: Copyright (C) 2026, Evolved Binary Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+final Path createdFile = basedir.toPath().resolve("target/classes/new-file.txt")
+if (!Files.exists(createdFile)) {
+ fail("New file was not created by the patch: " + createdFile)
+}
+
+final String content = new String(Files.readAllBytes(createdFile))
+assertTrue(content.contains("brand new file"), "Expected content was not found in: " + createdFile)
+return true
\ No newline at end of file
diff --git a/src/main/java/com/evolvedbinary/maven/plugins/patch/PatchMojo.java b/src/main/java/com/evolvedbinary/maven/plugins/patch/PatchMojo.java
index d5c7d17..f7c4f49 100644
--- a/src/main/java/com/evolvedbinary/maven/plugins/patch/PatchMojo.java
+++ b/src/main/java/com/evolvedbinary/maven/plugins/patch/PatchMojo.java
@@ -16,25 +16,23 @@
*/
package com.evolvedbinary.maven.plugins.patch;
-import com.github.difflib.patch.PatchFailedException;
-import com.github.difflib.unifieddiff.UnifiedDiff;
-import com.github.difflib.unifieddiff.UnifiedDiffFile;
-import com.github.difflib.unifieddiff.UnifiedDiffReader;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.DirectoryStream;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
+
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.jgit.api.ApplyResult;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
/**
* Applies unified diff patches.
@@ -51,67 +49,85 @@ public class PatchMojo extends AbstractMojo {
@Parameter(property = "patch.failOnFailedPatch", defaultValue = "false")
private boolean failOnFailedPatch;
+ @Override
public void execute() throws MojoExecutionException {
- getLog().info(targetDirectory.toString());
+ final Path targetDirectoryPath = targetDirectory.toPath();
+ final Path patchDirectoryPath = patchDirectory.toPath();
+
+ getLog().info(targetDirectoryPath.toString());
getLog().info("Applying patches...");
- File[] patchFiles = patchDirectory.listFiles(
- (dir, name) -> name.endsWith(".diff") || name.endsWith(".patch"));
- if (patchFiles == null || !patchDirectory.isDirectory()) {
+ if (!Files.isDirectory(patchDirectoryPath)) {
throw new MojoExecutionException("'patchDirectory' must be a directory.");
}
- if (!targetDirectory.isDirectory()) {
+ if (!Files.isDirectory(targetDirectoryPath)) {
throw new MojoExecutionException("'targetDirectory' must be a directory.");
}
+ final Path tempGitDir;
+ try {
+ tempGitDir = Files.createTempDirectory("jgit-temp-folder-");
+ } catch (IOException e) {
+ throw new MojoExecutionException("Could not create a temporary directory for JGit", e);
+ }
+
try {
- for (File patchFile : patchFiles) {
- String patchFileName = patchFile.getName();
- getLog().info(String.format("Applying patch '%s'...", patchFile.getName()));
-
- UnifiedDiff diff = UnifiedDiffReader.parseUnifiedDiff(
- new FileInputStream(patchFile));
-
- Map> writeResults = new HashMap<>();
- for (UnifiedDiffFile file : diff.getFiles()) {
- Path targetFile = targetDirectory.toPath().resolve(file.getToFile());
- String targetFileName = targetFile.getFileName().toString();
-
- try {
- List results = file.getPatch().applyTo(
- Files.readAllLines(targetFile)
- );
-
- writeResults.put(targetFile, results);
-
- getLog().info(
- String.format("Applied diff to '%s' successfully.", targetFileName));
- } catch (PatchFailedException pfe1) {
- String failureMessage = String.format(
- "Failed to apply patch file '%s' to file '%s'. (It may already have been applied!)",
- patchFileName, targetFileName);
-
- if (failOnFailedPatch) {
- throw new MojoExecutionException(failureMessage);
- } else {
- getLog().warn(failureMessage);
+
+ try (Repository repository = new FileRepositoryBuilder()
+ .setGitDir(tempGitDir.toFile())
+ .setWorkTree(targetDirectoryPath.toFile())
+ .build()) {
+
+ repository.create();
+
+ try (Git git = new Git(repository);
+ DirectoryStream patchFiles = Files.newDirectoryStream(patchDirectoryPath, "*.{diff,patch}")) {
+ for (final Path patchFile : patchFiles) {
+ final String patchFileName = patchFile.getFileName().toString();
+ getLog().info("Apply: " + patchFileName);
+
+
+ try (final InputStream patchStream = Files.newInputStream(patchFile)) {
+ final ApplyResult result = git.apply()
+ .setPatch(patchStream)
+ .call();
+
+ for (final File updatedFile : result.getUpdatedFiles()) {
+ getLog().info("Applied diff successfully: " + updatedFile.getName());
+ }
+
+ } catch (GitAPIException | IOException applyException) {
+
+ if (failOnFailedPatch) {
+ final String failureMessage = "Failed to apply patch file " + patchFileName +
+ " to directory " + targetDirectoryPath + " (It may already have been applied)";
+ throw new MojoExecutionException(failureMessage, applyException);
+ } else {
+ getLog().warn("Failed to apply patch: " + patchFileName + " It may already have been applied.");
+ }
}
- break;
+ getLog().info("Finished applying patch: " + patchFileName);
}
- getLog().info(String.format("Finished applying diff to '%s'.", targetFileName));
}
- for (Entry> entry : writeResults.entrySet()) {
- Files.write(entry.getKey(), entry.getValue());
- }
- getLog().info(String.format("Finished applying patch '%s'.", patchFileName));
+ } catch (IOException e) {
+ throw new MojoExecutionException("Something went wrong with IO Operations. ", e);
}
+ } finally {
+ deleteDirectoryQuietly(tempGitDir);
+ }
+ getLog().info("Finished applying patches");
+ }
- } catch (NoSuchFileException nsfe) {
- throw new MojoExecutionException(
- String.format("Could not find the file '%s' for patching. ", nsfe.getFile()));
+ private void deleteDirectoryQuietly(final Path directory) {
+ try (DirectoryStream children = Files.newDirectoryStream(directory)) {
+ for (final Path child : children) {
+ deleteDirectoryQuietly(child);
+ }
+ } catch (IOException e) {
+ }
+ try {
+ Files.delete(directory);
} catch (IOException e) {
- throw new MojoExecutionException("Something went wrong with IO Operations.", e);
}
- getLog().info("Finished applying patches.");
}
-}
+}
\ No newline at end of file