diff options
Diffstat (limited to 'org.eclipse.jgit')
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/*/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; } |