aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit/api
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/api')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java89
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java53
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java91
9 files changed, 233 insertions, 40 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index c895dc9aaa..b4d1cab513 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com> and others
+ * Copyright (C) 2010, 2025 Stefan Lay <stefan.lay@sap.com> 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
@@ -17,6 +17,7 @@ import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
import java.io.IOException;
import java.io.InputStream;
+import java.text.MessageFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -59,8 +60,15 @@ public class AddCommand extends GitCommand<DirCache> {
private WorkingTreeIterator workingTreeIterator;
+ // Update only known index entries, don't add new ones. If there's no file
+ // for an index entry, remove it: stage deletions.
private boolean update = false;
+ // If TRUE, also stage deletions, otherwise only update and add index
+ // entries.
+ // If not set explicitly
+ private Boolean all;
+
// This defaults to true because it's what JGit has been doing
// traditionally. The C git default would be false.
private boolean renormalize = true;
@@ -82,6 +90,17 @@ public class AddCommand extends GitCommand<DirCache> {
* A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and
* <code>dir/file2</code>) can also be given to add all files in the
* directory, recursively. Fileglobs (e.g. *.c) are not yet supported.
+ * </p>
+ * <p>
+ * If a pattern {@code "."} is added, all changes in the git repository's
+ * working tree will be added.
+ * </p>
+ * <p>
+ * File patterns are required unless {@code isUpdate() == true} or
+ * {@link #setAll(boolean)} is called. If so and no file patterns are given,
+ * all changes will be added (i.e., a file pattern of {@code "."} is
+ * implied).
+ * </p>
*
* @param filepattern
* repository-relative path of file/directory to add (with
@@ -113,15 +132,41 @@ public class AddCommand extends GitCommand<DirCache> {
* Executes the {@code Add} command. Each instance of this class should only
* be used for one invocation of the command. Don't call this method twice
* on an instance.
+ * </p>
+ *
+ * @throws JGitInternalException
+ * on errors, but also if {@code isUpdate() == true} _and_
+ * {@link #setAll(boolean)} had been called
+ * @throws NoFilepatternException
+ * if no file patterns are given if {@code isUpdate() == false}
+ * and {@link #setAll(boolean)} was not called
*/
@Override
public DirCache call() throws GitAPIException, NoFilepatternException {
-
- if (filepatterns.isEmpty())
- throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
checkCallable();
+
+ if (update && all != null) {
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().illegalCombinationOfArguments,
+ "--update", "--all/--no-all")); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ boolean addAll;
+ if (filepatterns.isEmpty()) {
+ if (update || all != null) {
+ addAll = true;
+ } else {
+ throw new NoFilepatternException(
+ JGitText.get().atLeastOnePatternIsRequired);
+ }
+ } else {
+ addAll = filepatterns.contains("."); //$NON-NLS-1$
+ if (all == null && !update) {
+ all = Boolean.TRUE;
+ }
+ }
+ boolean stageDeletions = update || (all != null && all.booleanValue());
+
DirCache dc = null;
- boolean addAll = filepatterns.contains("."); //$NON-NLS-1$
try (ObjectInserter inserter = repo.newObjectInserter();
NameConflictTreeWalk tw = new NameConflictTreeWalk(repo)) {
@@ -181,7 +226,8 @@ public class AddCommand extends GitCommand<DirCache> {
if (f == null) { // working tree file does not exist
if (entry != null
- && (!update || GITLINK == entry.getFileMode())) {
+ && (!stageDeletions
+ || GITLINK == entry.getFileMode())) {
builder.add(entry);
}
continue;
@@ -252,7 +298,8 @@ public class AddCommand extends GitCommand<DirCache> {
}
/**
- * Set whether to only match against already tracked files
+ * Set whether to only match against already tracked files. If
+ * {@code update == true}, re-sets a previous {@link #setAll(boolean)}.
*
* @param update
* If set to true, the command only matches {@code filepattern}
@@ -314,4 +361,32 @@ public class AddCommand extends GitCommand<DirCache> {
public boolean isRenormalize() {
return renormalize;
}
+
+ /**
+ * Defines whether the command will use '--all' mode: update existing index
+ * entries, add new entries, and remove index entries for which there is no
+ * file. (In other words: also stage deletions.)
+ * <p>
+ * The setting is independent of {@link #setUpdate(boolean)}.
+ * </p>
+ *
+ * @param all
+ * whether to enable '--all' mode
+ * @return {@code this}
+ * @since 7.2
+ */
+ public AddCommand setAll(boolean all) {
+ this.all = Boolean.valueOf(all);
+ return this;
+ }
+
+ /**
+ * Tells whether '--all' has been set for this command.
+ *
+ * @return {@code true} if it was set; {@code false} otherwise
+ * @since 7.2
+ */
+ public boolean isAll() {
+ return all != null && all.booleanValue();
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 934245781f..d2526287f9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -76,6 +76,11 @@ public class DescribeCommand extends GitCommand<String> {
private List<FileNameMatcher> matchers = new ArrayList<>();
/**
+ * Pattern matchers to be applied to tags for exclusion.
+ */
+ private List<FileNameMatcher> excludeMatchers = new ArrayList<>();
+
+ /**
* Whether to use all refs in the refs/ namespace
*/
private boolean useAll;
@@ -263,6 +268,27 @@ public class DescribeCommand extends GitCommand<String> {
return this;
}
+ /**
+ * Sets one or more {@code glob(7)} patterns that tags must not match to be
+ * considered. If multiple patterns are provided, they will all be applied.
+ *
+ * @param patterns
+ * the {@code glob(7)} pattern or patterns
+ * @return {@code this}
+ * @throws org.eclipse.jgit.errors.InvalidPatternException
+ * if the pattern passed in was invalid.
+ * @see <a href=
+ * "https://www.kernel.org/pub/software/scm/git/docs/git-describe.html"
+ * >Git documentation about describe</a>
+ * @since 7.2
+ */
+ public DescribeCommand setExclude(String... patterns) throws InvalidPatternException {
+ for (String p : patterns) {
+ excludeMatchers.add(new FileNameMatcher(p, null));
+ }
+ return this;
+ }
+
private final Comparator<Ref> TAG_TIE_BREAKER = new Comparator<>() {
@Override
@@ -284,15 +310,18 @@ public class DescribeCommand extends GitCommand<String> {
private Optional<Ref> getBestMatch(List<Ref> tags) {
if (tags == null || tags.isEmpty()) {
return Optional.empty();
- } else if (matchers.isEmpty()) {
+ } else if (matchers.isEmpty() && excludeMatchers.isEmpty()) {
Collections.sort(tags, TAG_TIE_BREAKER);
return Optional.of(tags.get(0));
- } else {
+ }
+
+ Stream<Ref> matchingTags;
+ if (!matchers.isEmpty()) {
// Find the first tag that matches in the stream of all tags
// filtered by matchers ordered by tie break order
- Stream<Ref> matchingTags = Stream.empty();
+ matchingTags = Stream.empty();
for (FileNameMatcher matcher : matchers) {
- Stream<Ref> m = tags.stream().filter(
+ Stream<Ref> m = tags.stream().filter( //
tag -> {
matcher.append(formatRefName(tag.getName()));
boolean result = matcher.isMatch();
@@ -301,8 +330,22 @@ public class DescribeCommand extends GitCommand<String> {
});
matchingTags = Stream.of(matchingTags, m).flatMap(i -> i);
}
- return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
+ } else {
+ // If there are no matchers, there are only excluders
+ // Assume all tags match for now before applying excluders
+ matchingTags = tags.stream();
+ }
+
+ for (FileNameMatcher matcher : excludeMatchers) {
+ matchingTags = matchingTags.filter( //
+ tag -> {
+ matcher.append(formatRefName(tag.getName()));
+ boolean result = matcher.isMatch();
+ matcher.reset();
+ return !result;
+ });
}
+ return matchingTags.sorted(TAG_TIE_BREAKER).findFirst();
}
private ObjectId getObjectIdFromRef(Ref r) throws JGitInternalException {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
index 0713c38931..f24127bd51 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/FetchCommand.java
@@ -124,7 +124,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
FetchRecurseSubmodulesMode mode = repo.getConfig().getEnum(
FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
@@ -132,7 +132,7 @@ public class FetchCommand extends TransportCommand<FetchCommand, FetchResult> {
// Fall back to fetch.recurseSubmodules, if set
mode = repo.getConfig().getEnum(FetchRecurseSubmodulesMode.values(),
ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES, null);
+ ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES);
if (mode != null) {
return mode;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 5bc035a46a..a8b866d25b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -201,7 +201,24 @@ public class Git implements AutoCloseable {
this(repo, false);
}
- Git(Repository repo, boolean closeRepo) {
+ /**
+ * Construct a new {@link org.eclipse.jgit.api.Git} object which can
+ * interact with the specified git repository.
+ * <p>
+ * All command classes returned by methods of this class will always
+ * interact with this git repository.
+ * <p>
+ * If {@code closeRepo = false} the caller is responsible for closing the
+ * repository.
+ *
+ * @param repo
+ * the git repository this class is interacting with;
+ * {@code null} is not allowed.
+ * @param closeRepo
+ * whether to close the repository when this instance is closed
+ * @since 7.4
+ */
+ public Git(Repository repo, boolean closeRepo) {
this.repo = requireNonNull(repo);
this.closeRepo = closeRepo;
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
index 555e351d32..2a8d34ed68 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LogCommand.java
@@ -110,10 +110,10 @@ public class LogCommand extends GitCommand<Iterable<RevCommit>> {
}
if (!filters.isEmpty()) {
if (filters.size() == 1) {
- filters.add(TreeFilter.ANY_DIFF);
+ walk.setTreeFilter(filters.get(0));
+ } else {
+ walk.setTreeFilter(AndTreeFilter.create(filters));
}
- walk.setTreeFilter(AndTreeFilter.create(filters));
-
}
if (skip > -1 && maxCount > -1)
walk.setRevFilter(AndRevFilter.create(SkipRevFilter.create(skip),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
index 83ae0fc9d4..4b2cee45c2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PullCommand.java
@@ -533,9 +533,9 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
Config config) {
BranchRebaseMode mode = config.getEnum(BranchRebaseMode.values(),
ConfigConstants.CONFIG_BRANCH_SECTION,
- branchName, ConfigConstants.CONFIG_KEY_REBASE, null);
+ branchName, ConfigConstants.CONFIG_KEY_REBASE);
if (mode == null) {
- mode = config.getEnum(BranchRebaseMode.values(),
+ mode = config.getEnum(
ConfigConstants.CONFIG_PULL_SECTION, null,
ConfigConstants.CONFIG_KEY_REBASE, BranchRebaseMode.NONE);
}
@@ -549,7 +549,7 @@ public class PullCommand extends TransportCommand<PullCommand, PullResult> {
Config config = repo.getConfig();
Merge ffMode = config.getEnum(Merge.values(),
ConfigConstants.CONFIG_PULL_SECTION, null,
- ConfigConstants.CONFIG_KEY_FF, null);
+ ConfigConstants.CONFIG_KEY_FF);
return ffMode != null ? FastForwardMode.valueOf(ffMode) : null;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
index dead2749b7..a149649004 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/ReflogCommand.java
@@ -68,7 +68,7 @@ public class ReflogCommand extends GitCommand<Collection<ReflogEntry>> {
checkCallable();
try {
- ReflogReader reader = repo.getReflogReader(ref);
+ ReflogReader reader = repo.getRefDatabase().getReflogReader(ref);
if (reader == null)
throw new RefNotFoundException(MessageFormat.format(
JGitText.get().refNotResolved, ref));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
index 23fbe0197f..2dba0ef0f2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashDropCommand.java
@@ -165,7 +165,8 @@ public class StashDropCommand extends GitCommand<ObjectId> {
List<ReflogEntry> entries;
try {
- ReflogReader reader = repo.getReflogReader(R_STASH);
+ ReflogReader reader = repo.getRefDatabase()
+ .getReflogReader(R_STASH);
if (reader == null) {
throw new RefNotFoundException(MessageFormat
.format(JGitText.get().refNotResolved, stashRef));
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
index 3524984347..5e4b2ee0b7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -28,6 +28,7 @@ import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.internal.storage.file.LockFile;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -39,6 +40,7 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+import org.eclipse.jgit.util.FileUtils;
/**
* A class used to execute a submodule update command.
@@ -62,6 +64,8 @@ public class SubmoduleUpdateCommand extends
private boolean fetch = false;
+ private boolean clonedRestored;
+
/**
* <p>
* Constructor for SubmoduleUpdateCommand.
@@ -116,26 +120,77 @@ public class SubmoduleUpdateCommand extends
return this;
}
+ private static boolean submoduleExists(File gitDir) {
+ if (gitDir != null && gitDir.isDirectory()) {
+ File[] files = gitDir.listFiles();
+ return files != null && files.length != 0;
+ }
+ return false;
+ }
+
+ private static void restoreSubmodule(File gitDir, File workingTree)
+ throws IOException {
+ LockFile dotGitLock = new LockFile(
+ new File(workingTree, Constants.DOT_GIT));
+ if (dotGitLock.lock()) {
+ String content = Constants.GITDIR
+ + getRelativePath(gitDir, workingTree);
+ dotGitLock.write(Constants.encode(content));
+ dotGitLock.commit();
+ }
+ }
+
+ private static String getRelativePath(File gitDir, File workingTree) {
+ File relPath;
+ try {
+ relPath = workingTree.toPath().relativize(gitDir.toPath())
+ .toFile();
+ } catch (IllegalArgumentException e) {
+ relPath = gitDir;
+ }
+ return FileUtils.pathToString(relPath);
+ }
+
+ private String determineUpdateMode(String mode) {
+ if (clonedRestored) {
+ return ConfigConstants.CONFIG_KEY_CHECKOUT;
+ }
+ return mode;
+ }
+
private Repository getOrCloneSubmodule(SubmoduleWalk generator, String url)
throws IOException, GitAPIException {
Repository repository = generator.getRepository();
+ boolean restored = false;
+ boolean cloned = false;
if (repository == null) {
- if (callback != null) {
- callback.cloningSubmodule(generator.getPath());
- }
- CloneCommand clone = Git.cloneRepository();
- configure(clone);
- clone.setURI(url);
- clone.setDirectory(generator.getDirectory());
- clone.setGitDir(new File(
+ File gitDir = new File(
new File(repo.getCommonDirectory(), Constants.MODULES),
- generator.getPath()));
- clone.setRelativePaths(true);
- if (monitor != null) {
- clone.setProgressMonitor(monitor);
+ generator.getPath());
+ if (submoduleExists(gitDir)) {
+ restoreSubmodule(gitDir, generator.getDirectory());
+ restored = true;
+ clonedRestored = true;
+ repository = generator.getRepository();
+ } else {
+ if (callback != null) {
+ callback.cloningSubmodule(generator.getPath());
+ }
+ CloneCommand clone = Git.cloneRepository();
+ configure(clone);
+ clone.setURI(url);
+ clone.setDirectory(generator.getDirectory());
+ clone.setGitDir(gitDir);
+ clone.setRelativePaths(true);
+ if (monitor != null) {
+ clone.setProgressMonitor(monitor);
+ }
+ repository = clone.call().getRepository();
+ cloned = true;
+ clonedRestored = true;
}
- repository = clone.call().getRepository();
- } else if (this.fetch) {
+ }
+ if ((this.fetch || restored) && !cloned) {
if (fetchCallback != null) {
fetchCallback.fetchingSubmodule(generator.getPath());
}
@@ -172,15 +227,17 @@ public class SubmoduleUpdateCommand extends
continue;
// Skip submodules not registered in parent repository's config
String url = generator.getConfigUrl();
- if (url == null)
+ if (url == null) {
continue;
-
+ }
+ clonedRestored = false;
try (Repository submoduleRepo = getOrCloneSubmodule(generator,
url); RevWalk walk = new RevWalk(submoduleRepo)) {
RevCommit commit = walk
.parseCommit(generator.getObjectId());
- String update = generator.getConfigUpdate();
+ String update = determineUpdateMode(
+ generator.getConfigUpdate());
if (ConfigConstants.CONFIG_KEY_MERGE.equals(update)) {
MergeCommand merge = new MergeCommand(submoduleRepo);
merge.include(commit);