summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r--org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs1
-rw-r--r--org.eclipse.jgit/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java84
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java63
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java98
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java146
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java19
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java73
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java5
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java11
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java2
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java113
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java28
19 files changed, 556 insertions, 121 deletions
diff --git a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
index 45d6d2c4c0..bfaf736d6e 100644
--- a/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
+++ b/org.eclipse.jgit/.settings/org.eclipse.jdt.core.prefs
@@ -99,6 +99,7 @@ org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=error
org.eclipse.jdt.core.compiler.problem.unusedLabel=error
org.eclipse.jdt.core.compiler.problem.unusedLocal=error
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 2b412a7512..4a9b794668 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -138,8 +138,8 @@ Export-Package: org.eclipse.jgit.annotations;version="4.5.0",
org.ietf.jgss",
org.eclipse.jgit.util.io;version="4.5.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
-Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
Import-Package: com.googlecode.javaewah;version="[0.7.9,0.8.0)",
+ com.jcraft.jsch;version="[0.1.37,0.2.0)",
javax.crypto,
javax.net.ssl,
org.slf4j;version="[1.7.0,2.0.0)",
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 ebe1befee1..57b5b8d833 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -332,7 +332,9 @@ invalidBooleanValue=Invalid boolean value: {0}.{1}={2}
invalidChannel=Invalid channel {0}
invalidCharacterInBase64Data=Invalid character in Base64 data.
invalidCommitParentNumber=Invalid commit parent number
+invalidDepth=Invalid depth: {0}
invalidEncryption=Invalid encryption
+invalidExpandWildcard=ExpandFromSource on a refspec that can have mismatched wildcards does not make sense.
invalidGitdirRef = Invalid .git reference in file ''{0}''
invalidGitType=invalid git type: {0}
invalidId=Invalid id: {0}
@@ -500,6 +502,7 @@ pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
pushNotPermitted=push not permitted
pushOptionsNotSupported=Push options not supported; received {0}
rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
+readerIsRequired=Reader is required
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
readTimedOut=Read timed out after {0} ms
receivePackObjectTooLarge1=Object too large, rejecting the pack. Max object size limit is {0} bytes.
@@ -528,7 +531,6 @@ renamesFindingExact=Finding exact renames
renamesRejoiningModifies=Rejoining modified file pairs
repositoryAlreadyExists=Repository already exists: {0}
repositoryConfigFileInvalid=Repository config file {0} invalid {1}
-repositoryIsRequired=Repository is required.
repositoryNotFound=repository not found: {0}
repositoryState_applyMailbox=Apply mailbox
repositoryState_bare=Bare
@@ -556,6 +558,7 @@ sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm.
serviceNotEnabledNoName=Service not enabled
serviceNotPermitted={0} not permitted
shallowCommitsAlreadyInitialized=Shallow commits have already been initialized
+shallowPacksRequireDepthWalk=Shallow packs require a DepthWalk
shortCompressedStreamAt=Short compressed stream at {0}
shortReadOfBlock=Short read of block.
shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
index 5967128113..7e331fd844 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CleanCommand.java
@@ -43,6 +43,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.DOT_GIT;
+
import java.io.File;
import java.io.IOException;
import java.util.Collections;
@@ -73,6 +75,8 @@ public class CleanCommand extends GitCommand<Set<String>> {
private boolean ignore = true;
+ private boolean force = false;
+
/**
* @param repo
*/
@@ -121,25 +125,69 @@ public class CleanCommand extends GitCommand<Set<String>> {
for (String file : notIgnoredFiles)
if (paths.isEmpty() || paths.contains(file)) {
- if (!dryRun)
- FileUtils.delete(new File(repo.getWorkTree(), file));
- files.add(file);
+ files = cleanPath(file, files);
}
- if (directories)
- for (String dir : notIgnoredDirs)
- if (paths.isEmpty() || paths.contains(dir)) {
- if (!dryRun)
- FileUtils.delete(new File(repo.getWorkTree(), dir),
- FileUtils.RECURSIVE);
- files.add(dir + "/"); //$NON-NLS-1$
- }
+ for (String dir : notIgnoredDirs)
+ if (paths.isEmpty() || paths.contains(dir)) {
+ files = cleanPath(dir, files);
+ }
} catch (IOException e) {
throw new JGitInternalException(e.getMessage(), e);
}
return files;
}
+ /**
+ * When dryRun is false, deletes the specified path from disk. If dryRun
+ * is true, no paths are actually deleted. In both cases, the paths that
+ * would have been deleted are added to inFiles and returned.
+ *
+ * Paths that are directories are recursively deleted when
+ * {@link #directories} is true.
+ * Paths that are git repositories are recursively deleted when
+ * {@link #directories} and {@link #force} are both true.
+ *
+ * @param path
+ * The path to be cleaned
+ * @param inFiles
+ * A set of strings representing the files that have been cleaned
+ * already, the path to be cleaned will be added to this set
+ * before being returned.
+ *
+ * @return a set of strings with the cleaned path added to it
+ * @throws IOException
+ */
+ private Set<String> cleanPath(String path, Set<String> inFiles)
+ throws IOException {
+ File curFile = new File(repo.getWorkTree(), path);
+ if (curFile.isDirectory()) {
+ if (directories) {
+ // Is this directory a git repository?
+ if (new File(curFile, DOT_GIT).exists()) {
+ if (force) {
+ if (!dryRun) {
+ FileUtils.delete(curFile, FileUtils.RECURSIVE);
+ }
+ inFiles.add(path + "/"); //$NON-NLS-1$
+ }
+ } else {
+ if (!dryRun) {
+ FileUtils.delete(curFile, FileUtils.RECURSIVE);
+ }
+ inFiles.add(path + "/"); //$NON-NLS-1$
+ }
+ }
+ } else {
+ if (!dryRun) {
+ FileUtils.delete(curFile, FileUtils.NONE);
+ }
+ inFiles.add(path);
+ }
+
+ return inFiles;
+ }
+
private Set<String> filterIgnorePaths(Set<String> inputPaths,
Set<String> ignoredNotInIndex, boolean exact) {
if (ignore) {
@@ -196,6 +244,20 @@ public class CleanCommand extends GitCommand<Set<String>> {
}
/**
+ * If force is set, directories that are git repositories will also be
+ * deleted.
+ *
+ * @param force
+ * whether or not to delete git repositories
+ * @return {@code this}
+ * @since 4.5
+ */
+ public CleanCommand setForce(boolean force) {
+ this.force = force;
+ return this;
+ }
+
+ /**
* If dirs is set, in addition to files, also clean directories.
*
* @param dirs
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
index fc701f3a54..819442cbea 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java
@@ -73,6 +73,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
@@ -117,10 +118,10 @@ public class DiffFormatter implements AutoCloseable {
private final OutputStream out;
- private Repository db;
-
private ObjectReader reader;
+ private boolean closeReader;
+
private DiffConfig diffCfg;
private int context = 3;
@@ -172,28 +173,42 @@ public class DiffFormatter implements AutoCloseable {
* source repository holding referenced objects.
*/
public void setRepository(Repository repository) {
- if (reader != null)
- reader.close();
+ setReader(repository.newObjectReader(), repository.getConfig(), true);
+ }
- db = repository;
- reader = db.newObjectReader();
- diffCfg = db.getConfig().get(DiffConfig.KEY);
+ /**
+ * Set the repository the formatter can load object contents from.
+ *
+ * @param reader
+ * source reader holding referenced objects. Caller is responsible
+ * for closing the reader.
+ * @param cfg
+ * config specifying diff algorithm and rename detection options.
+ * @since 4.5
+ */
+ public void setReader(ObjectReader reader, Config cfg) {
+ setReader(reader, cfg, false);
+ }
+
+ private void setReader(ObjectReader reader, Config cfg, boolean closeReader) {
+ close();
+ this.closeReader = closeReader;
+ this.reader = reader;
+ this.diffCfg = cfg.get(DiffConfig.KEY);
ContentSource cs = ContentSource.create(reader);
source = new ContentSource.Pair(cs, cs);
- DiffConfig dc = db.getConfig().get(DiffConfig.KEY);
- if (dc.isNoPrefix()) {
+ if (diffCfg.isNoPrefix()) {
setOldPrefix(""); //$NON-NLS-1$
setNewPrefix(""); //$NON-NLS-1$
}
- setDetectRenames(dc.isRenameDetectionEnabled());
+ setDetectRenames(diffCfg.isRenameDetectionEnabled());
- diffAlgorithm = DiffAlgorithm.getAlgorithm(db.getConfig().getEnum(
+ diffAlgorithm = DiffAlgorithm.getAlgorithm(cfg.getEnum(
ConfigConstants.CONFIG_DIFF_SECTION, null,
ConfigConstants.CONFIG_KEY_ALGORITHM,
SupportedAlgorithm.HISTOGRAM));
-
}
/**
@@ -330,8 +345,8 @@ public class DiffFormatter implements AutoCloseable {
*/
public void setDetectRenames(boolean on) {
if (on && renameDetector == null) {
- assertHaveRepository();
- renameDetector = new RenameDetector(db);
+ assertHaveReader();
+ renameDetector = new RenameDetector(reader, diffCfg);
} else if (!on)
renameDetector = null;
}
@@ -387,8 +402,9 @@ public class DiffFormatter implements AutoCloseable {
*/
@Override
public void close() {
- if (reader != null)
+ if (reader != null && closeReader) {
reader.close();
+ }
}
/**
@@ -412,7 +428,7 @@ public class DiffFormatter implements AutoCloseable {
*/
public List<DiffEntry> scan(AnyObjectId a, AnyObjectId b)
throws IOException {
- assertHaveRepository();
+ assertHaveReader();
try (RevWalk rw = new RevWalk(reader)) {
RevTree aTree = a != null ? rw.parseTree(a) : null;
@@ -441,7 +457,7 @@ public class DiffFormatter implements AutoCloseable {
* trees cannot be read or file contents cannot be read.
*/
public List<DiffEntry> scan(RevTree a, RevTree b) throws IOException {
- assertHaveRepository();
+ assertHaveReader();
AbstractTreeIterator aIterator = makeIteratorFromTreeOrNull(a);
AbstractTreeIterator bIterator = makeIteratorFromTreeOrNull(b);
@@ -476,7 +492,7 @@ public class DiffFormatter implements AutoCloseable {
*/
public List<DiffEntry> scan(AbstractTreeIterator a, AbstractTreeIterator b)
throws IOException {
- assertHaveRepository();
+ assertHaveReader();
TreeWalk walk = new TreeWalk(reader);
walk.addTree(a);
@@ -674,7 +690,7 @@ public class DiffFormatter implements AutoCloseable {
}
private String format(AbbreviatedObjectId id) {
- if (id.isComplete() && db != null) {
+ if (id.isComplete() && reader != null) {
try {
id = reader.abbreviate(id.toObjectId(), abbreviationLength);
} catch (IOException cannotAbbreviate) {
@@ -940,7 +956,7 @@ public class DiffFormatter implements AutoCloseable {
type = PatchType.UNIFIED;
} else {
- assertHaveRepository();
+ assertHaveReader();
byte[] aRaw, bRaw;
@@ -987,9 +1003,10 @@ public class DiffFormatter implements AutoCloseable {
return diffAlgorithm.diff(comparator, a, b);
}
- private void assertHaveRepository() {
- if (db == null)
- throw new IllegalStateException(JGitText.get().repositoryIsRequired);
+ private void assertHaveReader() {
+ if (reader == null) {
+ throw new IllegalStateException(JGitText.get().readerIsRequired);
+ }
}
private byte[] open(DiffEntry.Side side, DiffEntry entry)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
index ca976a1a3a..9b7f094d0f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoCommand.java
@@ -107,7 +107,7 @@ import org.eclipse.jgit.util.FileUtils;
public class RepoCommand extends GitCommand<RevCommit> {
private String path;
private String uri;
- private String groups;
+ private String groupsParam;
private String branch;
private String targetBranch = Constants.HEAD;
private boolean recordRemoteBranch = false;
@@ -286,7 +286,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
* @return this command
*/
public RepoCommand setGroups(String groups) {
- this.groups = groups;
+ this.groupsParam = groups;
return this;
}
@@ -478,7 +478,7 @@ public class RepoCommand extends GitCommand<RevCommit> {
git = new Git(repo);
ManifestParser parser = new ManifestParser(
- includedReader, path, branch, uri, groups, repo);
+ includedReader, path, branch, uri, groupsParam, repo);
try {
parser.read(inputStream);
for (RepoProject proj : parser.getFilteredProjects()) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
index d29f6c01e2..ff4a3ed1c6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/gitrepo/RepoProject.java
@@ -167,14 +167,14 @@ public class RepoProject implements Comparable<RepoProject> {
* a SHA-1 or branch name or tag name
* @param remote
* name of the remote definition
- * @param groups
+ * @param groupsParam
* comma separated group list
*/
public RepoProject(String name, String path, String revision,
- String remote, String groups) {
+ String remote, String groupsParam) {
this(name, path, revision, remote, new HashSet<String>(), null);
- if (groups != null && groups.length() > 0)
- this.setGroups(groups);
+ if (groupsParam != null && groupsParam.length() > 0)
+ this.setGroups(groupsParam);
}
/**
@@ -191,14 +191,14 @@ public class RepoProject implements Comparable<RepoProject> {
/**
* Set the url of the sub repo.
*
- * @param groups
+ * @param groupsParam
* comma separated group list
* @return this for chaining.
* @since 4.4
*/
- public RepoProject setGroups(String groups) {
+ public RepoProject setGroups(String groupsParam) {
this.groups.clear();
- this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
+ this.groups.addAll(Arrays.asList(groupsParam.split(","))); //$NON-NLS-1$
return this;
}
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 313512f990..3a636a1538 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -391,7 +391,9 @@ public class JGitText extends TranslationBundle {
/***/ public String invalidChannel;
/***/ public String invalidCharacterInBase64Data;
/***/ public String invalidCommitParentNumber;
+ /***/ public String invalidDepth;
/***/ public String invalidEncryption;
+ /***/ public String invalidExpandWildcard;
/***/ public String invalidGitdirRef;
/***/ public String invalidGitType;
/***/ public String invalidId;
@@ -559,6 +561,7 @@ public class JGitText extends TranslationBundle {
/***/ public String pushNotPermitted;
/***/ public String pushOptionsNotSupported;
/***/ public String rawLogMessageDoesNotParseAsLogEntry;
+ /***/ public String readerIsRequired;
/***/ public String readingObjectsFromLocalRepositoryFailed;
/***/ public String readTimedOut;
/***/ public String receivePackObjectTooLarge1;
@@ -587,7 +590,6 @@ public class JGitText extends TranslationBundle {
/***/ public String renamesRejoiningModifies;
/***/ public String repositoryAlreadyExists;
/***/ public String repositoryConfigFileInvalid;
- /***/ public String repositoryIsRequired;
/***/ public String repositoryNotFound;
/***/ public String repositoryState_applyMailbox;
/***/ public String repositoryState_bare;
@@ -615,6 +617,7 @@ public class JGitText extends TranslationBundle {
/***/ public String serviceNotEnabledNoName;
/***/ public String serviceNotPermitted;
/***/ public String shallowCommitsAlreadyInitialized;
+ /***/ public String shallowPacksRequireDepthWalk;
/***/ public String shortCompressedStreamAt;
/***/ public String shortReadOfBlock;
/***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
index 3f45859ac7..f6e4c2391a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsObjDatabase.java
@@ -61,7 +61,17 @@ import org.eclipse.jgit.lib.ObjectReader;
/** Manages objects stored in {@link DfsPackFile} on a storage system. */
public abstract class DfsObjDatabase extends ObjectDatabase {
- private static final PackList NO_PACKS = new PackList(new DfsPackFile[0]);
+ private static final PackList NO_PACKS = new PackList(new DfsPackFile[0]) {
+ @Override
+ boolean dirty() {
+ return true;
+ }
+
+ @Override
+ public void markDirty() {
+ // Always dirty.
+ }
+ };
/** Sources for a pack file. */
public static enum PackSource {
@@ -173,7 +183,20 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
* the pack list cannot be initialized.
*/
public DfsPackFile[] getPacks() throws IOException {
- return scanPacks(NO_PACKS).packs;
+ return getPackList().packs;
+ }
+
+ /**
+ * Scan and list all available pack files in the repository.
+ *
+ * @return list of available packs, with some additional metadata. The
+ * returned array is shared with the implementation and must not be
+ * modified by the caller.
+ * @throws IOException
+ * the pack list cannot be initialized.
+ */
+ public PackList getPackList() throws IOException {
+ return scanPacks(NO_PACKS);
}
/** @return repository owning this object database. */
@@ -188,7 +211,18 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
* implementation and must not be modified by the caller.
*/
public DfsPackFile[] getCurrentPacks() {
- return packList.get().packs;
+ return getCurrentPackList().packs;
+ }
+
+ /**
+ * List currently known pack files in the repository, without scanning.
+ *
+ * @return list of available packs, with some additional metadata. The
+ * returned array is shared with the implementation and must not be
+ * modified by the caller.
+ */
+ public PackList getCurrentPackList() {
+ return packList.get();
}
/**
@@ -363,11 +397,11 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
DfsPackFile[] packs = new DfsPackFile[1 + o.packs.length];
packs[0] = newPack;
System.arraycopy(o.packs, 0, packs, 1, o.packs.length);
- n = new PackList(packs);
+ n = new PackListImpl(packs);
} while (!packList.compareAndSet(o, n));
}
- private PackList scanPacks(final PackList original) throws IOException {
+ PackList scanPacks(final PackList original) throws IOException {
PackList o, n;
synchronized (packList) {
do {
@@ -408,10 +442,10 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
for (DfsPackFile p : forReuse.values())
p.close();
if (list.isEmpty())
- return new PackList(NO_PACKS.packs);
+ return new PackListImpl(NO_PACKS.packs);
if (!foundNew)
return old;
- return new PackList(list.toArray(new DfsPackFile[list.size()]));
+ return new PackListImpl(list.toArray(new DfsPackFile[list.size()]));
}
private static Map<DfsPackDescription, DfsPackFile> reuseMap(PackList old) {
@@ -456,12 +490,56 @@ public abstract class DfsObjDatabase extends ObjectDatabase {
// p.close();
}
- private static final class PackList {
+ /** Snapshot of packs scanned in a single pass. */
+ public static abstract class PackList {
/** All known packs, sorted. */
- final DfsPackFile[] packs;
+ public final DfsPackFile[] packs;
+
+ private long lastModified = -1;
- PackList(final DfsPackFile[] packs) {
+ PackList(DfsPackFile[] packs) {
this.packs = packs;
}
+
+ /** @return last modified time of all packs, in milliseconds. */
+ public long getLastModified() {
+ if (lastModified < 0) {
+ long max = 0;
+ for (DfsPackFile pack : packs) {
+ max = Math.max(max, pack.getPackDescription().getLastModified());
+ }
+ lastModified = max;
+ }
+ return lastModified;
+ }
+
+ abstract boolean dirty();
+
+ /**
+ * Mark pack list as dirty.
+ * <p>
+ * Used when the caller knows that new data might have been written to the
+ * repository that could invalidate open readers depending on this pack list,
+ * for example if refs are newly scanned.
+ */
+ public abstract void markDirty();
+ }
+
+ private static final class PackListImpl extends PackList {
+ private volatile boolean dirty;
+
+ PackListImpl(DfsPackFile[] packs) {
+ super(packs);
+ }
+
+ @Override
+ boolean dirty() {
+ return dirty;
+ }
+
+ @Override
+ public void markDirty() {
+ dirty = true;
+ }
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
index 66421e2a88..cc2f350034 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReader.java
@@ -53,6 +53,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.zip.DataFormatException;
@@ -62,6 +63,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackList;
import org.eclipse.jgit.internal.storage.file.BitmapIndexImpl;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackIndex;
@@ -91,6 +93,8 @@ import org.eclipse.jgit.util.BlockList;
* reader is not thread safe.
*/
public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
+ private static final int MAX_RESOLVE_MATCHES = 256;
+
/** Temporary buffer large enough for at least one raw object id. */
final byte[] tempId = new byte[OBJECT_ID_LENGTH];
@@ -163,14 +167,25 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
return Collections.singleton(id.toObjectId());
boolean noGarbage = avoidUnreachable;
HashSet<ObjectId> matches = new HashSet<ObjectId>(4);
- for (DfsPackFile pack : db.getPacks()) {
- if (noGarbage && pack.isGarbage())
+ PackList packList = db.getPackList();
+ resolveImpl(packList, id, noGarbage, matches);
+ if (matches.size() < MAX_RESOLVE_MATCHES && packList.dirty()) {
+ resolveImpl(db.scanPacks(packList), id, noGarbage, matches);
+ }
+ return matches;
+ }
+
+ private void resolveImpl(PackList packList, AbbreviatedObjectId id,
+ boolean noGarbage, HashSet<ObjectId> matches) throws IOException {
+ for (DfsPackFile pack : packList.packs) {
+ if (noGarbage && pack.isGarbage()) {
continue;
- pack.resolve(this, matches, id, 256);
- if (256 <= matches.size())
+ }
+ pack.resolve(this, matches, id, MAX_RESOLVE_MATCHES);
+ if (matches.size() >= MAX_RESOLVE_MATCHES) {
break;
+ }
}
- return matches;
}
@Override
@@ -178,7 +193,18 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
if (last != null && last.hasObject(this, objectId))
return true;
boolean noGarbage = avoidUnreachable;
- for (DfsPackFile pack : db.getPacks()) {
+ PackList packList = db.getPackList();
+ if (hasImpl(packList, objectId, noGarbage)) {
+ return true;
+ } else if (packList.dirty()) {
+ return hasImpl(db.scanPacks(packList), objectId, noGarbage);
+ }
+ return false;
+ }
+
+ private boolean hasImpl(PackList packList, AnyObjectId objectId,
+ boolean noGarbage) throws IOException {
+ for (DfsPackFile pack : packList.packs) {
if (pack == last || (noGarbage && pack.isGarbage()))
continue;
if (pack.hasObject(this, objectId)) {
@@ -193,19 +219,22 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
public ObjectLoader open(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IncorrectObjectTypeException,
IOException {
+ ObjectLoader ldr;
if (last != null) {
- ObjectLoader ldr = last.get(this, objectId);
+ ldr = last.get(this, objectId);
if (ldr != null)
return ldr;
}
+ PackList packList = db.getPackList();
boolean noGarbage = avoidUnreachable;
- for (DfsPackFile pack : db.getPacks()) {
- if (pack == last || (noGarbage && pack.isGarbage()))
- continue;
- ObjectLoader ldr = pack.get(this, objectId);
+ ldr = openImpl(packList, objectId, noGarbage);
+ if (ldr != null) {
+ return ldr;
+ }
+ if (packList.dirty()) {
+ ldr = openImpl(db.scanPacks(packList), objectId, noGarbage);
if (ldr != null) {
- last = pack;
return ldr;
}
}
@@ -216,6 +245,21 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
throw new MissingObjectException(objectId.copy(), typeHint);
}
+ private ObjectLoader openImpl(PackList packList, AnyObjectId objectId,
+ boolean noGarbage) throws IOException {
+ for (DfsPackFile pack : packList.packs) {
+ if (pack == last || (noGarbage && pack.isGarbage())) {
+ continue;
+ }
+ ObjectLoader ldr = pack.get(this, objectId);
+ if (ldr != null) {
+ last = pack;
+ return ldr;
+ }
+ }
+ return null;
+ }
+
@Override
public Set<ObjectId> getShallowCommits() {
return Collections.emptySet();
@@ -253,39 +297,58 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
private <T extends ObjectId> Iterable<FoundObject<T>> findAll(
Iterable<T> objectIds) throws IOException {
- ArrayList<FoundObject<T>> r = new ArrayList<FoundObject<T>>();
- DfsPackFile[] packList = db.getPacks();
- if (packList.length == 0) {
- for (T t : objectIds)
- r.add(new FoundObject<T>(t));
- return r;
+ Collection<T> pending = new LinkedList<>();
+ for (T id : objectIds) {
+ pending.add(id);
}
+ PackList packList = db.getPackList();
+ List<FoundObject<T>> r = new ArrayList<>();
+ findAllImpl(packList, pending, r);
+ if (!pending.isEmpty() && packList.dirty()) {
+ findAllImpl(db.scanPacks(packList), pending, r);
+ }
+ for (T t : pending) {
+ r.add(new FoundObject<T>(t));
+ }
+ Collections.sort(r, FOUND_OBJECT_SORT);
+ return r;
+ }
+
+ private <T extends ObjectId> void findAllImpl(PackList packList,
+ Collection<T> pending, List<FoundObject<T>> r) {
+ DfsPackFile[] packs = packList.packs;
+ if (packs.length == 0) {
+ return;
+ }
int lastIdx = 0;
- DfsPackFile lastPack = packList[lastIdx];
+ DfsPackFile lastPack = packs[lastIdx];
boolean noGarbage = avoidUnreachable;
- OBJECT_SCAN: for (T t : objectIds) {
+ OBJECT_SCAN: for (Iterator<T> it = pending.iterator(); it.hasNext();) {
+ T t = it.next();
try {
long p = lastPack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<T>(t, lastIdx, lastPack, p));
+ it.remove();
continue;
}
} catch (IOException e) {
// Fall though and try to examine other packs.
}
- for (int i = 0; i < packList.length; i++) {
+ for (int i = 0; i < packs.length; i++) {
if (i == lastIdx)
continue;
- DfsPackFile pack = packList[i];
+ DfsPackFile pack = packs[i];
if (noGarbage && pack.isGarbage())
continue;
try {
long p = pack.findOffset(this, t);
if (0 < p) {
r.add(new FoundObject<T>(t, i, pack, p));
+ it.remove();
lastIdx = i;
lastPack = pack;
continue OBJECT_SCAN;
@@ -294,13 +357,9 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
// Examine other packs.
}
}
-
- r.add(new FoundObject<T>(t));
}
- Collections.sort(r, FOUND_OBJECT_SORT);
last = lastPack;
- return r;
}
@Override
@@ -418,26 +477,45 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
IOException {
if (last != null) {
long sz = last.getObjectSize(this, objectId);
- if (0 <= sz)
+ if (0 <= sz) {
return sz;
+ }
}
- for (DfsPackFile pack : db.getPacks()) {
- if (pack == last)
- continue;
- long sz = pack.getObjectSize(this, objectId);
+ PackList packList = db.getPackList();
+ long sz = getObjectSizeImpl(packList, objectId);
+ if (0 <= sz) {
+ return sz;
+ }
+ if (packList.dirty()) {
+ sz = getObjectSizeImpl(packList, objectId);
if (0 <= sz) {
- last = pack;
return sz;
}
}
- if (typeHint == OBJ_ANY)
+ if (typeHint == OBJ_ANY) {
throw new MissingObjectException(objectId.copy(),
JGitText.get().unknownObjectType2);
+ }
throw new MissingObjectException(objectId.copy(), typeHint);
}
+ private long getObjectSizeImpl(PackList packList, AnyObjectId objectId)
+ throws IOException {
+ for (DfsPackFile pack : packList.packs) {
+ if (pack == last) {
+ continue;
+ }
+ long sz = pack.getObjectSize(this, objectId);
+ if (0 <= sz) {
+ last = pack;
+ return sz;
+ }
+ }
+ return -1;
+ }
+
public DfsObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
return new DfsObjectToPack(objectId, type);
}
@@ -451,6 +529,8 @@ public final class DfsReader extends ObjectReader implements ObjectReuseAsIs {
public void selectObjectRepresentation(PackWriter packer,
ProgressMonitor monitor, Iterable<ObjectToPack> objects)
throws IOException, MissingObjectException {
+ // Don't check dirty bit on PackList; assume ObjectToPacks all came from the
+ // current list.
for (DfsPackFile pack : db.getPacks()) {
List<DfsObjectToPack> tmp = findAllFromPack(pack, objects);
if (tmp.isEmpty())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
index de18eadb22..2e1c90d3ce 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/InMemoryRepository.java
@@ -310,6 +310,7 @@ public class InMemoryRepository extends DfsRepository {
}
ids.sort();
sym.sort();
+ objdb.getCurrentPackList().markDirty();
return new RefCache(ids.toRefList(), sym.toRefList());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
index 4bb2982b48..e5ca736824 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/RefDirectory.java
@@ -688,7 +688,7 @@ public class RefDirectory extends RefDatabase {
newLoose = curLoose.remove(idx);
} while (!looseRefs.compareAndSet(curLoose, newLoose));
int levels = levelsIn(refName) - 2;
- delete(fileFor(refName), levels);
+ delete(refFile, levels, rLck);
}
} finally {
rLck.unlock();
@@ -1062,13 +1062,24 @@ public class RefDirectory extends RefDatabase {
}
static void delete(final File file, final int depth) throws IOException {
- if (!file.delete() && file.isFile())
- throw new IOException(MessageFormat.format(JGitText.get().fileCannotBeDeleted, file));
+ delete(file, depth, null);
+ }
+ private static void delete(final File file, final int depth, LockFile rLck)
+ throws IOException {
+ if (!file.delete() && file.isFile()) {
+ throw new IOException(MessageFormat.format(
+ JGitText.get().fileCannotBeDeleted, file));
+ }
+
+ if (rLck != null) {
+ rLck.unlock(); // otherwise cannot delete dir below
+ }
File dir = file.getParentFile();
for (int i = 0; i < depth; ++i) {
- if (!dir.delete())
+ if (!dir.delete()) {
break; // ignore problem here
+ }
dir = dir.getParentFile();
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
index 525f9aecc7..691867aba8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/pack/PackWriter.java
@@ -564,7 +564,8 @@ public class PackWriter implements AutoCloseable {
* Configure this pack for a shallow clone.
*
* @param depth
- * maximum depth to traverse the commit graph
+ * maximum depth of history to return. 1 means return only the
+ * "wants".
* @param unshallow
* objects which used to be shallow on the client, but are being
* extended as part of this fetch
@@ -709,11 +710,52 @@ public class PackWriter implements AutoCloseable {
public void preparePack(ProgressMonitor countingMonitor,
@NonNull Set<? extends ObjectId> want,
@NonNull Set<? extends ObjectId> have) throws IOException {
+ preparePack(countingMonitor,
+ want, have, Collections.<ObjectId> emptySet());
+ }
+
+ /**
+ * Prepare the list of objects to be written to the pack stream.
+ * <p>
+ * Like {@link #preparePack(ProgressMonitor, Set, Set)} but also allows
+ * specifying commits that should not be walked past ("shallow" commits).
+ * The caller is responsible for filtering out commits that should not
+ * be shallow any more ("unshallow" commits as in {@link #setShallowPack})
+ * from the shallow set.
+ *
+ * @param countingMonitor
+ * progress during object enumeration.
+ * @param want
+ * objects of interest, ancestors of which will be included in
+ * the pack. Must not be {@code null}.
+ * @param have
+ * objects whose ancestors (up to and including
+ * {@code shallow} commits) do not need to be included in the
+ * pack because they are already available from elsewhere.
+ * Must not be {@code null}.
+ * @param shallow
+ * commits indicating the boundary of the history marked with
+ * {@code have}. Shallow commits have parents but those
+ * parents are considered not to be already available.
+ * Parents of {@code shallow} commits and earlier generations
+ * will be included in the pack if requested by {@code want}.
+ * Must not be {@code null}.
+ * @throws IOException
+ * an I/O problem occured while reading objects.
+ *
+ * @since 4.5
+ */
+ public void preparePack(ProgressMonitor countingMonitor,
+ @NonNull Set<? extends ObjectId> want,
+ @NonNull Set<? extends ObjectId> have,
+ @NonNull Set<? extends ObjectId> shallow) throws IOException {
ObjectWalk ow;
- if (shallowPack)
- ow = new DepthWalk.ObjectWalk(reader, depth);
- else
+ if (shallowPack) {
+ ow = new DepthWalk.ObjectWalk(reader, depth - 1);
+ } else {
ow = new ObjectWalk(reader);
+ }
+ ow.assumeShallow(shallow);
preparePack(countingMonitor, ow, want, have);
}
@@ -752,7 +794,8 @@ public class PackWriter implements AutoCloseable {
if (countingMonitor == null)
countingMonitor = NullProgressMonitor.INSTANCE;
if (shallowPack && !(walk instanceof DepthWalk.ObjectWalk))
- walk = new DepthWalk.ObjectWalk(reader, depth);
+ throw new IllegalArgumentException(
+ JGitText.get().shallowPacksRequireDepthWalk);
findObjectsToPack(countingMonitor, walk, interestingObjects,
uninterestingObjects);
}
@@ -1653,6 +1696,8 @@ public class PackWriter implements AutoCloseable {
List<RevObject> haveObjs = new ArrayList<RevObject>(haveEst);
List<RevTag> wantTags = new ArrayList<RevTag>(want.size());
+ // Retrieve the RevWalk's versions of "want" and "have" objects to
+ // maintain any state previously set in the RevWalk.
AsyncRevObjectQueue q = walker.parseAny(all, true);
try {
for (;;) {
@@ -1695,11 +1740,25 @@ public class PackWriter implements AutoCloseable {
if (walker instanceof DepthWalk.ObjectWalk) {
DepthWalk.ObjectWalk depthWalk = (DepthWalk.ObjectWalk) walker;
- for (RevObject obj : wantObjs)
+ for (RevObject obj : wantObjs) {
depthWalk.markRoot(obj);
+ }
+ // Mark the tree objects associated with "have" commits as
+ // uninteresting to avoid writing redundant blobs. A normal RevWalk
+ // lazily propagates the "uninteresting" state from a commit to its
+ // tree during the walk, but DepthWalks can terminate early so
+ // preemptively propagate that state here.
+ for (RevObject obj : haveObjs) {
+ if (obj instanceof RevCommit) {
+ RevTree t = ((RevCommit) obj).getTree();
+ depthWalk.markUninteresting(t);
+ }
+ }
+
if (unshallowObjects != null) {
- for (ObjectId id : unshallowObjects)
+ for (ObjectId id : unshallowObjects) {
depthWalk.markUnshallow(walker.parseAny(id));
+ }
}
} else {
for (RevObject obj : wantObjs)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
index 7ec24998b4..aba5242b14 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Repository.java
@@ -880,10 +880,11 @@ public abstract class Repository implements AutoCloseable {
} else if (newCount == -1) {
// should not happen, only log when useCnt became negative to
// minimize number of log entries
- LOG.warn(JGitText.get().corruptUseCnt);
if (LOG.isDebugEnabled()) {
IllegalStateException e = new IllegalStateException();
- LOG.debug("", e); //$NON-NLS-1$
+ LOG.debug(JGitText.get().corruptUseCnt, e);
+ } else {
+ LOG.warn(JGitText.get().corruptUseCnt);
}
if (RepositoryCache.isCached(this)) {
closedAt.set(System.currentTimeMillis());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
index 977f95341f..ee6095aa71 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/MergeFormatter.java
@@ -102,6 +102,7 @@ public class MergeFormatter {
* metadata
* @throws IOException
*/
+ @SuppressWarnings("unchecked")
public void formatMerge(OutputStream out, MergeResult res, String baseName,
String oursName, String theirsName, String charsetName) throws IOException {
List<String> names = new ArrayList<String>(3);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
index 89d05dba59..59a360f867 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/DepthWalk.java
@@ -149,6 +149,17 @@ public interface DepthWalk {
public RevFlag getReinterestingFlag() {
return REINTERESTING;
}
+
+ /**
+ * @since 4.5
+ */
+ @Override
+ public ObjectWalk toObjectWalkWithSameObjects() {
+ ObjectWalk ow = new ObjectWalk(reader, depth);
+ ow.objects = objects;
+ ow.freeFlags = freeFlags;
+ return ow;
+ }
}
/** Subclass of ObjectWalk that performs depth filtering. */
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index c8504937a7..a7f7cd4635 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -172,7 +172,7 @@ public class RevWalk implements Iterable<RevCommit>, AutoCloseable {
ObjectIdOwnerMap<RevObject> objects;
- private int freeFlags = APP_FLAGS;
+ int freeFlags = APP_FLAGS;
private int delayFreeFlags;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
index 0e803bdaf7..1440b83cf7 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/RefSpec.java
@@ -82,6 +82,30 @@ public class RefSpec implements Serializable {
/** Is this specification actually a wildcard match? */
private boolean wildcard;
+ /**
+ * How strict to be about wildcards.
+ *
+ * @since 4.5
+ */
+ public enum WildcardMode {
+ /**
+ * Reject refspecs with an asterisk on the source side and not the
+ * destination side or vice versa. This is the mode used by FetchCommand
+ * and PushCommand to create a one-to-one mapping between source and
+ * destination refs.
+ */
+ REQUIRE_MATCH,
+ /**
+ * Allow refspecs with an asterisk on only one side. This can create a
+ * many-to-one mapping between source and destination refs, so
+ * expandFromSource and expandFromDestination are not usable in this
+ * mode.
+ */
+ ALLOW_MISMATCH
+ }
+ /** Whether a wildcard is allowed on one side but not the other. */
+ private WildcardMode allowMismatchedWildcards;
+
/** Name of the ref(s) we would copy from. */
private String srcName;
@@ -99,6 +123,7 @@ public class RefSpec implements Serializable {
wildcard = false;
srcName = Constants.HEAD;
dstName = null;
+ allowMismatchedWildcards = WildcardMode.REQUIRE_MATCH;
}
/**
@@ -116,12 +141,24 @@ public class RefSpec implements Serializable {
* <li><code>:refs/heads/master</code></li>
* </ul>
*
+ * If the wildcard mode allows mismatches, then these ref specs are also
+ * valid:
+ * <ul>
+ * <li><code>refs/heads/*</code></li>
+ * <li><code>refs/heads/*:refs/heads/master</code></li>
+ * </ul>
+ *
* @param spec
* string describing the specification.
+ * @param mode
+ * whether to allow a wildcard on one side without a wildcard on
+ * the other.
* @throws IllegalArgumentException
* the specification is invalid.
+ * @since 4.5
*/
- public RefSpec(final String spec) {
+ public RefSpec(String spec, WildcardMode mode) {
+ this.allowMismatchedWildcards = mode;
String s = spec;
if (s.startsWith("+")) { //$NON-NLS-1$
force = true;
@@ -131,8 +168,13 @@ public class RefSpec implements Serializable {
final int c = s.lastIndexOf(':');
if (c == 0) {
s = s.substring(1);
- if (isWildcard(s))
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
+ if (isWildcard(s)) {
+ wildcard = true;
+ if (mode == WildcardMode.REQUIRE_MATCH) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidWildcards, spec));
+ }
+ }
dstName = checkValid(s);
} else if (c > 0) {
String src = s.substring(0, c);
@@ -141,24 +183,55 @@ public class RefSpec implements Serializable {
// Both contain wildcard
wildcard = true;
} else if (isWildcard(src) || isWildcard(dst)) {
- // If either source or destination has wildcard, the other one
- // must have as well.
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
+ wildcard = true;
+ if (mode == WildcardMode.REQUIRE_MATCH)
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidWildcards, spec));
}
srcName = checkValid(src);
dstName = checkValid(dst);
} else {
- if (isWildcard(s))
- throw new IllegalArgumentException(MessageFormat.format(JGitText.get().invalidWildcards, spec));
+ if (isWildcard(s)) {
+ if (mode == WildcardMode.REQUIRE_MATCH) {
+ throw new IllegalArgumentException(MessageFormat
+ .format(JGitText.get().invalidWildcards, spec));
+ }
+ wildcard = true;
+ }
srcName = checkValid(s);
}
}
+ /**
+ * Parse a ref specification for use during transport operations.
+ * <p>
+ * Specifications are typically one of the following forms:
+ * <ul>
+ * <li><code>refs/heads/master</code></li>
+ * <li><code>refs/heads/master:refs/remotes/origin/master</code></li>
+ * <li><code>refs/heads/*:refs/remotes/origin/*</code></li>
+ * <li><code>+refs/heads/master</code></li>
+ * <li><code>+refs/heads/master:refs/remotes/origin/master</code></li>
+ * <li><code>+refs/heads/*:refs/remotes/origin/*</code></li>
+ * <li><code>+refs/pull/&#42;/head:refs/remotes/origin/pr/*</code></li>
+ * <li><code>:refs/heads/master</code></li>
+ * </ul>
+ *
+ * @param spec
+ * string describing the specification.
+ * @throws IllegalArgumentException
+ * the specification is invalid.
+ */
+ public RefSpec(final String spec) {
+ this(spec, WildcardMode.REQUIRE_MATCH);
+ }
+
private RefSpec(final RefSpec p) {
force = p.isForceUpdate();
wildcard = p.isWildcard();
srcName = p.getSource();
dstName = p.getDestination();
+ allowMismatchedWildcards = p.allowMismatchedWildcards;
}
/**
@@ -348,8 +421,15 @@ public class RefSpec implements Serializable {
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
+ * @throws IllegalStateException
+ * when the RefSpec was constructed with wildcard mode that
+ * doesn't require matching wildcards.
*/
public RefSpec expandFromSource(final String r) {
+ if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) {
+ throw new IllegalStateException(
+ JGitText.get().invalidExpandWildcard);
+ }
return isWildcard() ? new RefSpec(this).expandFromSourceImp(r) : this;
}
@@ -373,6 +453,9 @@ public class RefSpec implements Serializable {
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
+ * @throws IllegalStateException
+ * when the RefSpec was constructed with wildcard mode that
+ * doesn't require matching wildcards.
*/
public RefSpec expandFromSource(final Ref r) {
return expandFromSource(r.getName());
@@ -390,8 +473,15 @@ public class RefSpec implements Serializable {
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
+ * @throws IllegalStateException
+ * when the RefSpec was constructed with wildcard mode that
+ * doesn't require matching wildcards.
*/
public RefSpec expandFromDestination(final String r) {
+ if (allowMismatchedWildcards != WildcardMode.REQUIRE_MATCH) {
+ throw new IllegalStateException(
+ JGitText.get().invalidExpandWildcard);
+ }
return isWildcard() ? new RefSpec(this).expandFromDstImp(r) : this;
}
@@ -414,6 +504,9 @@ public class RefSpec implements Serializable {
* @return a new specification expanded from provided ref name. Result
* specification is wildcard if and only if provided ref name is
* wildcard.
+ * @throws IllegalStateException
+ * when the RefSpec was constructed with wildcard mode that
+ * doesn't require matching wildcards.
*/
public RefSpec expandFromDestination(final Ref r) {
return expandFromDestination(r.getName());
@@ -422,7 +515,7 @@ public class RefSpec implements Serializable {
private boolean match(final String name, final String s) {
if (s == null)
return false;
- if (isWildcard()) {
+ if (isWildcard(s)) {
int wildcardIndex = s.indexOf('*');
String prefix = s.substring(0, wildcardIndex);
String suffix = s.substring(wildcardIndex + 1);
@@ -453,6 +546,8 @@ public class RefSpec implements Serializable {
return false;
if (s.contains("//")) //$NON-NLS-1$
return false;
+ if (s.endsWith("/")) //$NON-NLS-1$
+ return false;
int i = s.indexOf('*');
if (i != -1) {
if (s.indexOf('*', i + 1) > i)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
index e1770f282b..d1fd67e977 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java
@@ -790,8 +790,9 @@ public class UploadPack {
}
private void processShallow() throws IOException {
+ int walkDepth = depth - 1;
try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
- walk.getObjectReader(), depth)) {
+ walk.getObjectReader(), walkDepth)) {
// Find all the commits which will be shallow
for (ObjectId o : wantIds) {
@@ -808,12 +809,14 @@ public class UploadPack {
// Commits at the boundary which aren't already shallow in
// the client need to be marked as such
- if (c.getDepth() == depth && !clientShallowCommits.contains(c))
+ if (c.getDepth() == walkDepth
+ && !clientShallowCommits.contains(c))
pckOut.writeString("shallow " + o.name()); //$NON-NLS-1$
// Commits not on the boundary which are shallow in the client
// need to become unshallowed
- if (c.getDepth() < depth && clientShallowCommits.remove(c)) {
+ if (c.getDepth() < walkDepth
+ && clientShallowCommits.remove(c)) {
unshallowCommits.add(c.copy());
pckOut.writeString("unshallow " + c.name()); //$NON-NLS-1$
}
@@ -948,6 +951,11 @@ public class UploadPack {
if (line.startsWith("deepen ")) { //$NON-NLS-1$
depth = Integer.parseInt(line.substring(7));
+ if (depth <= 0) {
+ throw new PackProtocolException(
+ MessageFormat.format(JGitText.get().invalidDepth,
+ Integer.valueOf(depth)));
+ }
continue;
}
@@ -974,7 +982,8 @@ public class UploadPack {
}
/**
- * Returns the clone/fetch depth. Valid only after calling recvWants().
+ * Returns the clone/fetch depth. Valid only after calling recvWants(). A
+ * depth of 1 means return only the wants.
*
* @return the depth requested by the client, or 0 if unbounded.
* @since 4.0
@@ -1484,16 +1493,19 @@ public class UploadPack {
pw.setTagTargets(tagTargets);
}
- if (depth > 0)
+ RevWalk rw = walk;
+ if (depth > 0) {
pw.setShallowPack(depth, unshallowCommits);
+ rw = new DepthWalk.RevWalk(walk.getObjectReader(), depth - 1);
+ rw.assumeShallow(clientShallowCommits);
+ }
- RevWalk rw = walk;
if (wantAll.isEmpty()) {
- pw.preparePack(pm, wantIds, commonBase);
+ pw.preparePack(pm, wantIds, commonBase, clientShallowCommits);
} else {
walk.reset();
- ObjectWalk ow = walk.toObjectWalkWithSameObjects();
+ ObjectWalk ow = rw.toObjectWalkWithSameObjects();
pw.preparePack(pm, ow, wantAll, commonBase);
rw = ow;
}