diff options
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java | 465 |
1 files changed, 302 insertions, 163 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index 6d9a32db92..c650d6e8e7 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -1,68 +1,38 @@ /* - * Copyright (C) 2008-2010, Google Inc. + * Copyright (C) 2008, 2010, Google Inc. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> - * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2011, 2020, Matthias Sohn <matthias.sohn@sap.com> and others * - * This program and the accompanying materials are made available - * under the terms of the Eclipse Distribution License v1.0 which - * accompanies this distribution, is reproduced below, and is - * available at http://www.eclipse.org/org/documents/edl-v10.php + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://www.eclipse.org/org/documents/edl-v10.php. * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * - Neither the name of the Eclipse Foundation, Inc. nor the - * names of its contributors may be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.dircache; +import static java.nio.charset.StandardCharsets.ISO_8859_1; + import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.EOFException; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.security.DigestOutputStream; import java.security.MessageDigest; import java.text.MessageFormat; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import org.eclipse.jgit.errors.CorruptObjectException; +import org.eclipse.jgit.errors.IndexReadException; import org.eclipse.jgit.errors.LockFailedException; import org.eclipse.jgit.errors.UnmergedPathException; import org.eclipse.jgit.events.IndexChangedEvent; @@ -70,19 +40,26 @@ import org.eclipse.jgit.events.IndexChangedListener; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.storage.file.FileSnapshot; import org.eclipse.jgit.internal.storage.file.LockFile; +import org.eclipse.jgit.internal.storage.io.NullMessageDigest; +import org.eclipse.jgit.lib.AnyObjectId; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Config.ConfigEnum; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.TreeWalk.OperationType; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.IO; import org.eclipse.jgit.util.MutableInteger; import org.eclipse.jgit.util.NB; import org.eclipse.jgit.util.TemporaryBuffer; -import org.eclipse.jgit.util.io.SafeBufferedOutputStream; +import org.eclipse.jgit.util.io.SilentFileInputStream; /** * Support for the Git dircache (aka index file). @@ -106,20 +83,19 @@ public class DirCache { private static final byte[] NO_CHECKSUM = {}; - static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() { - public int compare(final DirCacheEntry o1, final DirCacheEntry o2) { - final int cr = cmp(o1, o2); - if (cr != 0) - return cr; - return o1.getStage() - o2.getStage(); - } + static final Comparator<DirCacheEntry> ENT_CMP = (DirCacheEntry o1, + DirCacheEntry o2) -> { + final int cr = cmp(o1, o2); + if (cr != 0) + return cr; + return o1.getStage() - o2.getStage(); }; - static int cmp(final DirCacheEntry a, final DirCacheEntry b) { + static int cmp(DirCacheEntry a, DirCacheEntry b) { return cmp(a.path, a.path.length, b); } - static int cmp(final byte[] aPath, final int aLen, final DirCacheEntry b) { + static int cmp(byte[] aPath, int aLen, DirCacheEntry b) { return cmp(aPath, aLen, b.path, b.path.length); } @@ -145,6 +121,28 @@ public class DirCache { } /** + * Create a new in memory index read from the contents of a tree. + * + * @param reader + * reader to access the tree objects from a repository. + * @param treeId + * tree to read. Must identify a tree, not a tree-ish. + * @return a new cache which has no backing store file, but contains the + * contents of {@code treeId}. + * @throws java.io.IOException + * one or more trees not available from the ObjectReader. + * @since 4.2 + */ + public static DirCache read(ObjectReader reader, AnyObjectId treeId) + throws IOException { + DirCache d = newInCore(); + DirCacheBuilder b = d.builder(); + b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId); + b.finish(); + return d; + } + + /** * Create a new in-core index representation and read an index from disk. * <p> * The new index will be read before it is returned to the caller. Read @@ -155,13 +153,13 @@ public class DirCache { * repository containing the index to read * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */ - public static DirCache read(final Repository repository) + public static DirCache read(Repository repository) throws CorruptObjectException, IOException { final DirCache c = read(repository.getIndexFile(), repository.getFS()); c.repository = repository; @@ -182,13 +180,13 @@ public class DirCache { * certain file system operations. * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */ - public static DirCache read(final File indexLocation, final FS fs) + public static DirCache read(File indexLocation, FS fs) throws CorruptObjectException, IOException { final DirCache c = new DirCache(indexLocation, fs); c.read(); @@ -210,14 +208,14 @@ public class DirCache { * certain file system operations. * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read, or the lock * could not be obtained. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */ - public static DirCache lock(final File indexLocation, final FS fs) + public static DirCache lock(File indexLocation, FS fs) throws CorruptObjectException, IOException { final DirCache c = new DirCache(indexLocation, fs); if (!c.lock()) @@ -225,13 +223,7 @@ public class DirCache { try { c.read(); - } catch (IOException e) { - c.unlock(); - throw e; - } catch (RuntimeException e) { - c.unlock(); - throw e; - } catch (Error e) { + } catch (IOException | RuntimeException | Error e) { c.unlock(); throw e; } @@ -253,10 +245,10 @@ public class DirCache { * listener to be informed when DirCache is committed * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read, or the lock * could not be obtained. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. * @since 2.0 @@ -287,10 +279,10 @@ public class DirCache { * listener to be informed when DirCache is committed * @return a cache representing the contents of the specified index file (if * it exists) or an empty cache if the file does not exist. - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read, or the lock * could not be obtained. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */ @@ -318,9 +310,6 @@ public class DirCache { /** Our active lock (if we hold it); null if we don't have it locked. */ private LockFile myLock; - /** file system abstraction **/ - private final FS fs; - /** Keep track of whether the index has changed or not */ private FileSnapshot snapshot; @@ -336,6 +325,12 @@ public class DirCache { /** Repository containing this index */ private Repository repository; + /** If we read this index from disk, the original format. */ + private DirCacheVersion version; + + /** Whether to skip computing and checking the index checksum */ + private boolean skipHash; + /** * Create a new in-core index representation. * <p> @@ -348,9 +343,8 @@ public class DirCache { * the file system abstraction which will be necessary to perform * certain file system operations. */ - public DirCache(final File indexLocation, final FS fs) { + public DirCache(File indexLocation, FS fs) { liveFile = indexLocation; - this.fs = fs; clear(); } @@ -358,7 +352,8 @@ public class DirCache { * Create a new builder to update this cache. * <p> * Callers should add all entries to the builder, then use - * {@link DirCacheBuilder#finish()} to update this instance. + * {@link org.eclipse.jgit.dircache.DirCacheBuilder#finish()} to update this + * instance. * * @return a new builder instance for this cache. */ @@ -370,7 +365,8 @@ public class DirCache { * Create a new editor to recreate this cache. * <p> * Callers should add commands to the editor, then use - * {@link DirCacheEditor#finish()} to update this instance. + * {@link org.eclipse.jgit.dircache.DirCacheEditor#finish()} to update this + * instance. * * @return a new builder instance for this cache. */ @@ -378,7 +374,11 @@ public class DirCache { return new DirCacheEditor(this, entryCnt + 16); } - void replace(final DirCacheEntry[] e, final int cnt) { + DirCacheVersion getVersion() { + return version; + } + + void replace(DirCacheEntry[] e, int cnt) { sortedEntries = e; entryCnt = cnt; tree = null; @@ -391,10 +391,10 @@ public class DirCache { * the last time we consulted it. A missing index file will be treated as * though it were present but had no file entries in it. * - * @throws IOException + * @throws java.io.IOException * the index file is present but could not be read. This * DirCache instance may not be populated correctly. - * @throws CorruptObjectException + * @throws org.eclipse.jgit.errors.CorruptObjectException * the index file is using a format or extension that this * library does not support. */ @@ -404,19 +404,17 @@ public class DirCache { if (!liveFile.exists()) clear(); else if (snapshot == null || snapshot.isModified(liveFile)) { - try { - final FileInputStream inStream = new FileInputStream(liveFile); - try { - clear(); - readFrom(inStream); - } finally { - try { - inStream.close(); - } catch (IOException err2) { - // Ignore any close failures. - } - } + try (SilentFileInputStream inStream = new SilentFileInputStream( + liveFile)) { + clear(); + readFrom(inStream); } catch (FileNotFoundException fnfe) { + if (liveFile.exists()) { + // Panic: the index file exists but we can't read it + throw new IndexReadException( + MessageFormat.format(JGitText.get().cannotReadIndex, + liveFile.getAbsolutePath(), fnfe)); + } // Someone must have deleted it between our exists test // and actually opening the path. That's fine, its empty. // @@ -427,8 +425,11 @@ public class DirCache { } /** - * @return true if the memory state differs from the index file - * @throws IOException + * Whether the memory state differs from the index file + * + * @return {@code true} if the memory state differs from the index file + * @throws java.io.IOException + * if an IO error occurred */ public boolean isOutdated() throws IOException { if (liveFile == null || !liveFile.exists()) @@ -436,7 +437,9 @@ public class DirCache { return snapshot == null || snapshot.isModified(liveFile); } - /** Empty this index, removing all entries. */ + /** + * Empty this index, removing all entries. + */ public void clear() { snapshot = null; sortedEntries = NO_ENTRIES; @@ -445,10 +448,11 @@ public class DirCache { readIndexChecksum = NO_CHECKSUM; } - private void readFrom(final InputStream inStream) throws IOException, + private void readFrom(InputStream inStream) throws IOException, CorruptObjectException { final BufferedInputStream in = new BufferedInputStream(inStream); - final MessageDigest md = Constants.newMessageDigest(); + readConfig(); + MessageDigest md = newMessageDigest(); // Read the index header and verify we understand it. // @@ -457,20 +461,32 @@ public class DirCache { md.update(hdr, 0, 12); if (!is_DIRC(hdr)) throw new CorruptObjectException(JGitText.get().notADIRCFile); - final int ver = NB.decodeInt32(hdr, 4); + int versionCode = NB.decodeInt32(hdr, 4); + DirCacheVersion ver = DirCacheVersion.fromInt(versionCode); + if (ver == null) { + throw new CorruptObjectException( + MessageFormat.format(JGitText.get().unknownDIRCVersion, + Integer.valueOf(versionCode))); + } boolean extended = false; - if (ver == 3) + switch (ver) { + case DIRC_VERSION_MINIMUM: + break; + case DIRC_VERSION_EXTENDED: + case DIRC_VERSION_PATHCOMPRESS: extended = true; - else if (ver != 2) - throw new CorruptObjectException(MessageFormat.format( - JGitText.get().unknownDIRCVersion, Integer.valueOf(ver))); + break; + default: + throw new CorruptObjectException(MessageFormat + .format(JGitText.get().unknownDIRCVersion, ver)); + } + version = ver; entryCnt = NB.decodeInt32(hdr, 8); if (entryCnt < 0) throw new CorruptObjectException(JGitText.get().DIRCHasTooManyEntries); snapshot = FileSnapshot.save(liveFile); - int smudge_s = (int) (snapshot.lastModified() / 1000); - int smudge_ns = ((int) (snapshot.lastModified() % 1000)) * 1000000; + Instant smudge = snapshot.lastModifiedInstant(); // Load the individual file entries. // @@ -479,8 +495,10 @@ public class DirCache { sortedEntries = new DirCacheEntry[entryCnt]; final MutableInteger infoAt = new MutableInteger(); - for (int i = 0; i < entryCnt; i++) - sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge_s, smudge_ns); + for (int i = 0; i < entryCnt; i++) { + sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge, + version, i == 0 ? null : sortedEntries[i - 1]); + } // After the file entries are index extensions, and then a footer. // @@ -531,8 +549,12 @@ public class DirCache { } readIndexChecksum = md.digest(); - if (!Arrays.equals(readIndexChecksum, hdr)) { - throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch); + if (!(skipHash + || Arrays.equals(readIndexChecksum, hdr) + || Arrays.equals(NullMessageDigest.getInstance().digest(), + hdr))) { + throw new CorruptObjectException( + JGitText.get().DIRCChecksumMismatch); } } @@ -553,12 +575,11 @@ public class DirCache { } } - private static String formatExtensionName(final byte[] hdr) - throws UnsupportedEncodingException { - return "'" + new String(hdr, 0, 4, "ISO-8859-1") + "'"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + private static String formatExtensionName(byte[] hdr) { + return "'" + new String(hdr, 0, 4, ISO_8859_1) + "'"; //$NON-NLS-1$ //$NON-NLS-2$ } - private static boolean is_DIRC(final byte[] hdr) { + private static boolean is_DIRC(byte[] hdr) { if (hdr.length < SIG_DIRC.length) return false; for (int i = 0; i < SIG_DIRC.length; i++) @@ -572,14 +593,14 @@ public class DirCache { * * @return true if the lock is now held by the caller; false if it is held * by someone else. - * @throws IOException + * @throws java.io.IOException * the output file could not be created. The caller does not * hold the lock. */ public boolean lock() throws IOException { if (liveFile == null) throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile); - final LockFile tmp = new LockFile(liveFile, fs); + final LockFile tmp = new LockFile(liveFile); if (tmp.lock()) { tmp.setNeedStatInformation(true); myLock = tmp; @@ -599,48 +620,48 @@ public class DirCache { * Once written the lock is closed and must be either committed with * {@link #commit()} or rolled back with {@link #unlock()}. * - * @throws IOException + * @throws java.io.IOException * the output file could not be created. The caller no longer * holds the lock. */ public void write() throws IOException { final LockFile tmp = myLock; requireLocked(tmp); - try { - writeTo(liveFile.getParentFile(), - new SafeBufferedOutputStream(tmp.getOutputStream())); - } catch (IOException err) { - tmp.unlock(); - throw err; - } catch (RuntimeException err) { - tmp.unlock(); - throw err; - } catch (Error err) { + try (OutputStream o = tmp.getOutputStream(); + OutputStream bo = new BufferedOutputStream(o)) { + writeTo(liveFile.getParentFile(), bo); + } catch (IOException | RuntimeException | Error err) { tmp.unlock(); throw err; } } - void writeTo(File dir, final OutputStream os) throws IOException { - final MessageDigest foot = Constants.newMessageDigest(); - final DigestOutputStream dos = new DigestOutputStream(os, foot); - - boolean extended = false; - for (int i = 0; i < entryCnt; i++) - extended |= sortedEntries[i].isExtended(); + void writeTo(File dir, OutputStream os) throws IOException { + readConfig(); + MessageDigest foot = newMessageDigest(); + DigestOutputStream dos = new DigestOutputStream(os, foot); + if (version == null + || version == DirCacheVersion.DIRC_VERSION_MINIMUM) { + version = DirCacheVersion.DIRC_VERSION_MINIMUM; + for (int i = 0; i < entryCnt; i++) { + if (sortedEntries[i].isExtended()) { + version = DirCacheVersion.DIRC_VERSION_EXTENDED; + break; + } + } + } // Write the header. // final byte[] tmp = new byte[128]; System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length); - NB.encodeInt32(tmp, 4, extended ? 3 : 2); + NB.encodeInt32(tmp, 4, version.getVersionCode()); NB.encodeInt32(tmp, 8, entryCnt); dos.write(tmp, 0, 12); // Write the individual file entries. - final int smudge_s; - final int smudge_ns; + Instant smudge; if (myLock != null) { // For new files we need to smudge the index entry // if they have been modified "now". Ideally we'd @@ -648,12 +669,10 @@ public class DirCache { // so we use the current timestamp as a approximation. myLock.createCommitSnapshot(); snapshot = myLock.getCommitSnapshot(); - smudge_s = (int) (snapshot.lastModified() / 1000); - smudge_ns = ((int) (snapshot.lastModified() % 1000)) * 1000000; + smudge = snapshot.lastModifiedInstant(); } else { // Used in unit tests only - smudge_ns = 0; - smudge_s = 0; + smudge = Instant.EPOCH; } // Check if tree is non-null here since calling updateSmudgedEntries @@ -665,12 +684,15 @@ public class DirCache { for (int i = 0; i < entryCnt; i++) { final DirCacheEntry e = sortedEntries[i]; - if (e.mightBeRacilyClean(smudge_s, smudge_ns)) + if (e.mightBeRacilyClean(smudge)) { e.smudgeRacilyClean(); - e.write(dos); + } + e.write(dos, version, i == 0 ? null : sortedEntries[i - 1]); } if (writeTree) { + @SuppressWarnings("resource") // Explicitly closed in try block, and + // destroyed in finally TemporaryBuffer bb = new TemporaryBuffer.LocalFile(dir, 5 << 20); try { tree.write(tmp, bb); @@ -689,6 +711,22 @@ public class DirCache { os.close(); } + private void readConfig() { + if (version == null && this.repository != null) { + DirCacheConfig config = repository.getConfig() + .get(DirCacheConfig::new); + version = config.getIndexVersion(); + skipHash = config.isSkipHash(); + } + } + + private MessageDigest newMessageDigest() { + if (skipHash) { + return NullMessageDigest.getInstance(); + } + return Constants.newMessageDigest(); + } + /** * Commit this change and release the lock. * <p> @@ -697,23 +735,25 @@ public class DirCache { * @return true if the commit was successful and the file contains the new * data; false if the commit failed and the file remains with the * old data. - * @throws IllegalStateException + * @throws java.lang.IllegalStateException * the lock is not held. */ public boolean commit() { final LockFile tmp = myLock; requireLocked(tmp); myLock = null; - if (!tmp.commit()) + if (!tmp.commit()) { return false; + } snapshot = tmp.getCommitSnapshot(); if (indexChangedListener != null - && !Arrays.equals(readIndexChecksum, writeIndexChecksum)) - indexChangedListener.onIndexChanged(new IndexChangedEvent()); + && !Arrays.equals(readIndexChecksum, writeIndexChecksum)) { + indexChangedListener.onIndexChanged(new IndexChangedEvent(true)); + } return true; } - private void requireLocked(final LockFile tmp) { + private void requireLocked(LockFile tmp) { if (liveFile == null) throw new IllegalStateException(JGitText.get().dirCacheIsNotLocked); if (tmp == null) @@ -744,7 +784,7 @@ public class DirCache { * the index; pass to {@link #getEntry(int)} to obtain the entry * information. If < 0 the entry does not exist in the index. */ - public int findEntry(final String path) { + public int findEntry(String path) { final byte[] p = Constants.encode(path); return findEntry(p, p.length); } @@ -768,8 +808,11 @@ public class DirCache { * information. If < 0 the entry does not exist in the index. * @since 3.4 */ - public int findEntry(final byte[] p, final int pLen) { - int low = 0; + public int findEntry(byte[] p, int pLen) { + return findEntry(0, p, pLen); + } + + int findEntry(int low, byte[] p, int pLen) { int high = entryCnt; while (low < high) { int mid = (low + high) >>> 1; @@ -797,7 +840,7 @@ public class DirCache { * entry position of the path that should be skipped. * @return position of the next entry whose path is after the input. */ - public int nextEntry(final int position) { + public int nextEntry(int position) { DirCacheEntry last = sortedEntries[position]; int nextIdx = position + 1; while (nextIdx < entryCnt) { @@ -810,7 +853,7 @@ public class DirCache { return nextIdx; } - int nextEntry(final byte[] p, final int pLen, int nextIdx) { + int nextEntry(byte[] p, int pLen, int nextIdx) { while (nextIdx < entryCnt) { final DirCacheEntry next = sortedEntries[nextIdx]; if (!DirCacheTree.peq(p, next.path, pLen)) @@ -844,7 +887,7 @@ public class DirCache { * position of the entry to get. * @return the entry at position <code>i</code>. */ - public DirCacheEntry getEntry(final int i) { + public DirCacheEntry getEntry(int i) { return sortedEntries[i]; } @@ -855,7 +898,7 @@ public class DirCache { * the path to search for. * @return the entry for the given <code>path</code>. */ - public DirCacheEntry getEntry(final String path) { + public DirCacheEntry getEntry(String path) { final int i = findEntry(path); return i < 0 ? null : sortedEntries[i]; } @@ -869,8 +912,8 @@ public class DirCache { */ public DirCacheEntry[] getEntriesWithin(String path) { if (path.length() == 0) { - final DirCacheEntry[] r = new DirCacheEntry[sortedEntries.length]; - System.arraycopy(sortedEntries, 0, r, 0, sortedEntries.length); + DirCacheEntry[] r = new DirCacheEntry[entryCnt]; + System.arraycopy(sortedEntries, 0, r, 0, entryCnt); return r; } if (!path.endsWith("/")) //$NON-NLS-1$ @@ -904,7 +947,7 @@ public class DirCache { * @return the cache tree; null if there is no current cache tree available * and <code>build</code> was false. */ - public DirCacheTree getCacheTree(final boolean build) { + public DirCacheTree getCacheTree(boolean build) { if (build) { if (tree == null) tree = new DirCacheTree(); @@ -921,16 +964,16 @@ public class DirCache { * responsible for flushing the inserter before trying to use the * returned tree identity. * @return identity for the root tree. - * @throws UnmergedPathException + * @throws org.eclipse.jgit.errors.UnmergedPathException * one or more paths contain higher-order stages (stage > 0), * which cannot be stored in a tree object. - * @throws IllegalStateException + * @throws java.lang.IllegalStateException * one or more paths contain an invalid mode which should never * appear in a tree object. - * @throws IOException + * @throws java.io.IOException * an unexpected error occurred writing to the object store. */ - public ObjectId writeTree(final ObjectInserter ow) + public ObjectId writeTree(ObjectInserter ow) throws UnmergedPathException, IOException { return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow); } @@ -959,10 +1002,12 @@ public class DirCache { * Update any smudged entries with information from the working tree. * * @throws IOException + * if an IO error occurred */ private void updateSmudgedEntries() throws IOException { - List<String> paths = new ArrayList<String>(128); + List<String> paths = new ArrayList<>(128); try (TreeWalk walk = new TreeWalk(repository)) { + walk.setOperationType(OperationType.CHECKIN_OP); for (int i = 0; i < entryCnt; i++) if (sortedEntries[i].isSmudged()) paths.add(sortedEntries[i].getPathString()); @@ -974,6 +1019,7 @@ public class DirCache { FileTreeIterator fIter = new FileTreeIterator(repository); walk.addTree(iIter); walk.addTree(fIter); + fIter.setDirCacheIterator(walk, 0); walk.setRecursive(true); while (walk.next()) { iIter = walk.getTree(0, DirCacheIterator.class); @@ -985,9 +1031,102 @@ public class DirCache { DirCacheEntry entry = iIter.getDirCacheEntry(); if (entry.isSmudged() && iIter.idEqual(fIter)) { entry.setLength(fIter.getEntryLength()); - entry.setLastModified(fIter.getEntryLastModified()); + entry.setLastModified(fIter.getEntryLastModifiedInstant()); + } + } + } + } + + /** + * DirCache versions + * + * @since 7.2 + */ + public enum DirCacheVersion implements ConfigEnum { + + /** Minimum index version on-disk format that we support. */ + DIRC_VERSION_MINIMUM(2), + /** Version 3 supports extended flags. */ + DIRC_VERSION_EXTENDED(3), + /** + * Version 4 adds very simple "path compression": it strips out the + * common prefix between the last entry written and the current entry. + * Instead of writing two entries with paths "foo/bar/baz/a.txt" and + * "foo/bar/baz/b.txt" it only writes "b.txt" for the second entry. + * <p> + * It is also <em>not</em> padded. + * </p> + */ + DIRC_VERSION_PATHCOMPRESS(4); + + private final int version; + + private DirCacheVersion(int versionCode) { + this.version = versionCode; + } + + /** + * @return the version code for this version + */ + public int getVersionCode() { + return version; + } + + @Override + public String toConfigValue() { + return Integer.toString(version); + } + + @Override + public boolean matchConfigValue(String in) { + try { + return version == Integer.parseInt(in); + } catch (NumberFormatException e) { + return false; + } + } + + /** + * Create DirCacheVersion from integer value of the version code. + * + * @param val + * integer value of the version code. + * @return the DirCacheVersion instance of the version code. + */ + public static DirCacheVersion fromInt(int val) { + for (DirCacheVersion v : DirCacheVersion.values()) { + if (val == v.getVersionCode()) { + return v; } } + return null; + } + } + + private static class DirCacheConfig { + + private final DirCacheVersion indexVersion; + + private final boolean skipHash; + + public DirCacheConfig(Config cfg) { + boolean manyFiles = cfg.getBoolean( + ConfigConstants.CONFIG_FEATURE_SECTION, + ConfigConstants.CONFIG_KEY_MANYFILES, false); + indexVersion = cfg.getEnum(ConfigConstants.CONFIG_INDEX_SECTION, + null, ConfigConstants.CONFIG_KEY_VERSION, + manyFiles ? DirCacheVersion.DIRC_VERSION_PATHCOMPRESS + : DirCacheVersion.DIRC_VERSION_EXTENDED); + skipHash = cfg.getBoolean(ConfigConstants.CONFIG_INDEX_SECTION, + ConfigConstants.CONFIG_KEY_SKIPHASH, false); + } + + public DirCacheVersion getIndexVersion() { + return indexVersion; + } + + public boolean isSkipHash() { + return skipHash; } } } |