Skip to content

Commit 7cac71d

Browse files
author
TheSnoozer
committed
move the the GitDirLocator from the git-commit-id-maven-plugin (commit 64d51eed7dd9d3d03f452ddfb4761947341bdb8c) to the core module
1 parent 49cee9c commit 7cac71d

File tree

5 files changed

+351
-6
lines changed

5 files changed

+351
-6
lines changed

src/main/java/pl/project13/core/GitCommitIdPlugin.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import pl.project13.core.git.GitDescribeConfig;
2222
import pl.project13.core.log.LogInterface;
2323
import pl.project13.core.util.BuildFileChangeListener;
24+
import pl.project13.core.util.GitDirLocator;
2425

2526
import javax.annotation.Nonnull;
2627
import javax.annotation.Nullable;
@@ -279,6 +280,8 @@ default Map<String, String> getSystemEnv() {
279280
Charset getPropertiesSourceCharset();
280281

281282
boolean shouldPropertiesEscapeUnicode();
283+
284+
boolean shouldFailOnNoGitDirectory();
282285
}
283286

284287
protected static final Pattern allowedCharactersForEvaluateOnCommit = Pattern.compile("[a-zA-Z0-9\\_\\-\\^\\/\\.]+");
@@ -341,16 +344,51 @@ protected static void loadGitData(@Nonnull Callback cb, @Nonnull Properties prop
341344
throw new GitCommitIdExecutionException("suspicious argument for evaluateOnCommit, aborting execution!");
342345
}
343346

347+
File dotGitDirectory = findDotGitDirectory(cb);
348+
if (dotGitDirectory != null) {
349+
cb.getLogInterface().info("dotGitDirectory '" + dotGitDirectory.getAbsolutePath() + "'");
350+
} else {
351+
cb.getLogInterface().info("dotGitDirectory is null, aborting execution!");
352+
return;
353+
}
354+
344355
if (cb.useNativeGit()) {
345-
loadGitDataWithNativeGit(cb, properties);
356+
loadGitDataWithNativeGit(cb, dotGitDirectory, properties);
346357
} else {
347-
loadGitDataWithJGit(cb, properties);
358+
loadGitDataWithJGit(cb, dotGitDirectory, properties);
348359
}
349360
}
350361

351-
private static void loadGitDataWithNativeGit(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException {
362+
private static File findDotGitDirectory(@Nonnull Callback cb) throws GitCommitIdExecutionException {
363+
File dotGitDirectory = lookupGitDirectory(cb);
364+
if (cb.shouldFailOnNoGitDirectory() && !directoryExists(dotGitDirectory)) {
365+
throw new GitCommitIdExecutionException(
366+
".git directory is not found! Please specify a valid [dotGitDirectory] in your"
367+
+ " project");
368+
}
369+
return dotGitDirectory;
370+
}
371+
372+
private static boolean directoryExists(@Nullable File fileLocation) {
373+
return fileLocation != null && fileLocation.exists() && fileLocation.isDirectory();
374+
}
375+
376+
/**
377+
* Find the git directory of the currently used project. If it's not already specified, this
378+
* method will try to find it.
379+
*
380+
* @return the File representation of the .git directory
381+
*/
382+
private static File lookupGitDirectory(@Nonnull Callback cb) throws GitCommitIdExecutionException {
383+
return new GitDirLocator(cb.getProjectBaseDir()).lookupGitDirectory(cb.getDotGitDirectory());
384+
}
385+
386+
private static void loadGitDataWithNativeGit(
387+
@Nonnull Callback cb,
388+
@Nonnull File dotGitDirectory,
389+
@Nonnull Properties properties) throws GitCommitIdExecutionException {
352390
GitDataProvider nativeGitProvider = NativeGitProvider
353-
.on(cb.getDotGitDirectory().getParentFile(), cb.getNativeGitTimeoutInMs(), cb.getLogInterface())
391+
.on(dotGitDirectory.getParentFile(), cb.getNativeGitTimeoutInMs(), cb.getLogInterface())
354392
.setPrefixDot(cb.getPrefixDot())
355393
.setAbbrevLength(cb.getAbbrevLength())
356394
.setDateFormat(cb.getDateFormat())
@@ -365,9 +403,12 @@ private static void loadGitDataWithNativeGit(@Nonnull Callback cb, @Nonnull Prop
365403
nativeGitProvider.loadGitData(cb.getEvaluateOnCommit(), cb.getSystemEnv(), properties);
366404
}
367405

368-
private static void loadGitDataWithJGit(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException {
406+
private static void loadGitDataWithJGit(
407+
@Nonnull Callback cb,
408+
@Nonnull File dotGitDirectory,
409+
@Nonnull Properties properties) throws GitCommitIdExecutionException {
369410
GitDataProvider jGitProvider = JGitProvider
370-
.on(cb.getDotGitDirectory(), cb.getLogInterface())
411+
.on(dotGitDirectory, cb.getLogInterface())
371412
.setPrefixDot(cb.getPrefixDot())
372413
.setAbbrevLength(cb.getAbbrevLength())
373414
.setDateFormat(cb.getDateFormat())
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
* This file is part of git-commit-id-plugin-core by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
3+
*
4+
* git-commit-id-plugin-core is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* git-commit-id-plugin-core is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with git-commit-id-plugin-core. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
package pl.project13.core.util;
19+
20+
import java.io.BufferedReader;
21+
import java.io.File;
22+
import java.io.FileReader;
23+
import java.io.IOException;
24+
import java.nio.file.Path;
25+
import javax.annotation.Nonnull;
26+
import javax.annotation.Nullable;
27+
import org.eclipse.jgit.lib.Constants;
28+
29+
/**
30+
* This class encapsulates logic to locate a valid .git directory of the currently used project. If
31+
* it's not already specified, this logic will try to find it.
32+
*/
33+
public class GitDirLocator {
34+
final File projectBasedir;
35+
36+
/**
37+
* Constructor to encapsulates all references required to locate a valid .git directory
38+
*
39+
* @param projectBasedir The project basedir that will be used as last resort to search
40+
* the parent project hierarchy until a .git directory is found.
41+
*/
42+
public GitDirLocator(File projectBasedir) {
43+
this.projectBasedir = projectBasedir;
44+
}
45+
46+
/**
47+
* Attempts to lookup a valid .git directory of the currently used project.
48+
*
49+
* @param manuallyConfiguredDir A user has the ability to configure a git-directory with the
50+
* {@code dotGitDirectory} configuration setting. By default it should be simply {@code
51+
* ${project.basedir}/.git}
52+
* @return A valid .git directory, or {@code null} if none could be found under the user specified
53+
* location or within the project or it's reactor projects.
54+
*/
55+
@Nullable
56+
public File lookupGitDirectory(@Nonnull File manuallyConfiguredDir) {
57+
if (manuallyConfiguredDir.exists()) {
58+
59+
// If manuallyConfiguredDir is a directory then we can use it as the git path.
60+
if (manuallyConfiguredDir.isDirectory()) {
61+
return manuallyConfiguredDir;
62+
}
63+
64+
// If the path exists but is not a directory it might be a git submodule "gitdir" link.
65+
File gitDirLinkPath = processGitDirFile(manuallyConfiguredDir);
66+
67+
// If the linkPath was found from the file and it exists then use it.
68+
if (isExistingDirectory(gitDirLinkPath)) {
69+
return gitDirLinkPath;
70+
}
71+
72+
/*
73+
* FIXME: I think we should fail here because a manual path was set and it was not found
74+
* but I'm leaving it falling back to searching for the git path because that is the current
75+
* behaviour - Unluckypixie.
76+
*/
77+
}
78+
79+
return findProjectGitDirectory();
80+
}
81+
82+
/**
83+
* Search up all the parent project hierarchy until a .git directory is found.
84+
*
85+
* @return File which represents the location of the .git directory or NULL if none found.
86+
*/
87+
@Nullable
88+
private File findProjectGitDirectory() {
89+
File basedir = this.projectBasedir;
90+
while (basedir != null) {
91+
File gitdir = new File(basedir, Constants.DOT_GIT);
92+
if (gitdir.exists()) {
93+
if (gitdir.isDirectory()) {
94+
return gitdir;
95+
} else if (gitdir.isFile()) {
96+
return processGitDirFile(gitdir);
97+
} else {
98+
return null;
99+
}
100+
}
101+
basedir = basedir.getParentFile();
102+
}
103+
return null;
104+
}
105+
106+
/**
107+
* Load a ".git" git submodule file and read the gitdir path from it.
108+
*
109+
* @return File object with path loaded or null
110+
*/
111+
private File processGitDirFile(@Nonnull File file) {
112+
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
113+
// There should be just one line in the file, e.g.
114+
// "gitdir: /usr/local/src/parentproject/.git/modules/submodule"
115+
String line = reader.readLine();
116+
if (line == null) {
117+
return null;
118+
}
119+
// Separate the key and the value in the string.
120+
String[] parts = line.split(": ");
121+
122+
// If we don't have 2 parts or if the key is not gitdir then give up.
123+
if (parts.length != 2 || !parts[0].equals("gitdir")) {
124+
return null;
125+
}
126+
127+
// All seems ok so return the "gitdir" value read from the file.
128+
String extractFromConfig = parts[1];
129+
File gitDir = resolveWorktree(new File(extractFromConfig));
130+
if (gitDir.isAbsolute()) {
131+
// gitdir value is an absolute path. Return as-is
132+
return gitDir;
133+
} else {
134+
// gitdir value is relative.
135+
return new File(file.getParentFile(), extractFromConfig);
136+
}
137+
} catch (IOException e) {
138+
return null;
139+
}
140+
}
141+
142+
/**
143+
* Attempts to resolve the actual location of the .git folder for a given
144+
* worktree.
145+
* For example for a worktree like {@code a/.git/worktrees/X} structure would
146+
* return {@code a/.git}.
147+
*
148+
* If the conditions for a git worktree like file structure are met simply return the provided
149+
* argument as is.
150+
*/
151+
static File resolveWorktree(File fileLocation) {
152+
Path parent = fileLocation.toPath().getParent();
153+
if (parent == null) {
154+
return fileLocation;
155+
}
156+
if (parent.endsWith(Path.of(".git", "worktrees"))) {
157+
return parent.getParent().toFile();
158+
}
159+
return fileLocation;
160+
}
161+
162+
/**
163+
* Helper method to validate that the specified {@code File} is an existing directory.
164+
*
165+
* @param fileLocation The {@code File} that should be checked if it's actually an existing
166+
* directory.
167+
* @return {@code true} if the specified {@code File} is an existing directory, {@false}
168+
* otherwise.
169+
*/
170+
private static boolean isExistingDirectory(@Nullable File fileLocation) {
171+
return fileLocation != null && fileLocation.exists() && fileLocation.isDirectory();
172+
}
173+
}

src/test/java/pl/project13/core/GitCommitIdPluginIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ public void shouldFailWithExceptionWhenNoGitRepoFound(boolean useNativeGit) thro
414414
new GitCommitIdTestCallback()
415415
.setDotGitDirectory(emptyGitDir)
416416
.setUseNativeGit(useNativeGit)
417+
.setShouldFailOnNoGitDirectory(true)
417418
.build();
418419
Properties properties = new Properties();
419420

src/test/java/pl/project13/core/GitCommitIdTestCallback.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public class GitCommitIdTestCallback {
5757
private File generateGitPropertiesFilename;
5858
private Charset propertiesSourceCharset = StandardCharsets.UTF_8;
5959
private boolean shouldPropertiesEscapeUnicode = false;
60+
private boolean shouldFailOnNoGitDirectory = false;
6061

6162
public GitCommitIdTestCallback() {
6263
try {
@@ -193,6 +194,11 @@ public GitCommitIdTestCallback setShouldPropertiesEscapeUnicode(boolean shouldPr
193194
return this;
194195
}
195196

197+
public GitCommitIdTestCallback setShouldFailOnNoGitDirectory(boolean shouldFailOnNoGitDirectory) {
198+
this.shouldFailOnNoGitDirectory = shouldFailOnNoGitDirectory;
199+
return this;
200+
}
201+
196202
public GitCommitIdPlugin.Callback build() {
197203
return new GitCommitIdPlugin.Callback() {
198204
@Override
@@ -341,6 +347,11 @@ public Charset getPropertiesSourceCharset() {
341347
public boolean shouldPropertiesEscapeUnicode() {
342348
return shouldPropertiesEscapeUnicode;
343349
}
350+
351+
@Override
352+
public boolean shouldFailOnNoGitDirectory() {
353+
return shouldFailOnNoGitDirectory;
354+
}
344355
};
345356
}
346357

0 commit comments

Comments
 (0)