/*
 * Copyright (C) 2008-2009, Google Inc.
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
 * Copyright (C) 2010, Matthias Sohn <matthias.sohn@sap.com>
 * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
 * and other copyright owners as documented in the project's IP log.
 *
 * 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
 *
 * 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.
 */

package org.eclipse.jgit.dircache;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.text.MessageFormat;
import java.time.Instant;
import java.util.Arrays;

import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.NB;
import org.eclipse.jgit.util.SystemReader;

A single file (or stage of a file) in a DirCache.

An entry represents exactly one stage of a file. If a file path is unmerged then multiple DirCacheEntry instances may appear for the same path name.

/** * A single file (or stage of a file) in a * {@link org.eclipse.jgit.dircache.DirCache}. * <p> * An entry represents exactly one stage of a file. If a file path is unmerged * then multiple DirCacheEntry instances may appear for the same path name. */
public class DirCacheEntry { private static final byte[] nullpad = new byte[8];
The standard (fully merged) stage for an entry.
/** The standard (fully merged) stage for an entry. */
public static final int STAGE_0 = 0;
The base tree revision for an entry.
/** The base tree revision for an entry. */
public static final int STAGE_1 = 1;
The first tree revision (usually called "ours").
/** The first tree revision (usually called "ours"). */
public static final int STAGE_2 = 2;
The second tree revision (usually called "theirs").
/** The second tree revision (usually called "theirs"). */
public static final int STAGE_3 = 3; private static final int P_CTIME = 0; // private static final int P_CTIME_NSEC = 4; private static final int P_MTIME = 8; // private static final int P_MTIME_NSEC = 12; // private static final int P_DEV = 16; // private static final int P_INO = 20; private static final int P_MODE = 24; // private static final int P_UID = 28; // private static final int P_GID = 32; private static final int P_SIZE = 36; private static final int P_OBJECTID = 40; private static final int P_FLAGS = 60; private static final int P_FLAGS2 = 62;
Mask applied to data in P_FLAGS to get the name length.
/** Mask applied to data in {@link #P_FLAGS} to get the name length. */
private static final int NAME_MASK = 0xfff; private static final int INTENT_TO_ADD = 0x20000000; private static final int SKIP_WORKTREE = 0x40000000; private static final int EXTENDED_FLAGS = (INTENT_TO_ADD | SKIP_WORKTREE); private static final int INFO_LEN = 62; private static final int INFO_LEN_EXTENDED = 64; private static final int EXTENDED = 0x40; private static final int ASSUME_VALID = 0x80;
In-core flag signaling that the entry should be considered as modified.
/** In-core flag signaling that the entry should be considered as modified. */
private static final int UPDATE_NEEDED = 0x1;
(Possibly shared) header information storage.
/** (Possibly shared) header information storage. */
private final byte[] info;
First location within info where our header starts.
/** First location within {@link #info} where our header starts. */
private final int infoOffset;
Our encoded path name, from the root of the repository.
/** Our encoded path name, from the root of the repository. */
final byte[] path;
Flags which are never stored to disk.
/** Flags which are never stored to disk. */
private byte inCoreFlags; DirCacheEntry(final byte[] sharedInfo, final MutableInteger infoAt, final InputStream in, final MessageDigest md, final Instant smudge) throws IOException { info = sharedInfo; infoOffset = infoAt.value; IO.readFully(in, info, infoOffset, INFO_LEN); final int len; if (isExtended()) { len = INFO_LEN_EXTENDED; IO.readFully(in, info, infoOffset + INFO_LEN, INFO_LEN_EXTENDED - INFO_LEN); if ((getExtendedFlags() & ~EXTENDED_FLAGS) != 0) throw new IOException(MessageFormat.format(JGitText.get() .DIRCUnrecognizedExtendedFlags, String.valueOf(getExtendedFlags()))); } else len = INFO_LEN; infoAt.value += len; md.update(info, infoOffset, len); int pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK; int skipped = 0; if (pathLen < NAME_MASK) { path = new byte[pathLen]; IO.readFully(in, path, 0, pathLen); md.update(path, 0, pathLen); } else { final ByteArrayOutputStream tmp = new ByteArrayOutputStream(); { final byte[] buf = new byte[NAME_MASK]; IO.readFully(in, buf, 0, NAME_MASK); tmp.write(buf); } for (;;) { final int c = in.read(); if (c < 0) throw new EOFException(JGitText.get().shortReadOfBlock); if (c == 0) break; tmp.write(c); } path = tmp.toByteArray(); pathLen = path.length; skipped = 1; // we already skipped 1 '\0' above to break the loop. md.update(path, 0, pathLen); md.update((byte) 0); } try { checkPath(path); } catch (InvalidPathException e) { CorruptObjectException p = new CorruptObjectException(e.getMessage()); if (e.getCause() != null) p.initCause(e.getCause()); throw p; } // Index records are padded out to the next 8 byte alignment // for historical reasons related to how C Git read the files. // final int actLen = len + pathLen; final int expLen = (actLen + 8) & ~7; final int padLen = expLen - actLen - skipped; if (padLen > 0) { IO.skipFully(in, padLen); md.update(nullpad, 0, padLen); } if (mightBeRacilyClean(smudge)) { smudgeRacilyClean(); } }
Create an empty entry at stage 0.
Params:
  • newPath – name of the cache entry.
Throws:
  • IllegalArgumentException – If the path starts or ends with "/", or contains "//" either "\0". These sequences are not permitted in a git tree object or DirCache file.
/** * Create an empty entry at stage 0. * * @param newPath * name of the cache entry. * @throws java.lang.IllegalArgumentException * If the path starts or ends with "/", or contains "//" either * "\0". These sequences are not permitted in a git tree object * or DirCache file. */
public DirCacheEntry(String newPath) { this(Constants.encode(newPath), STAGE_0); }
Create an empty entry at the specified stage.
Params:
  • newPath – name of the cache entry.
  • stage – the stage index of the new entry.
Throws:
  • IllegalArgumentException – If the path starts or ends with "/", or contains "//" either "\0". These sequences are not permitted in a git tree object or DirCache file. Or if stage is outside of the range 0..3, inclusive.
/** * Create an empty entry at the specified stage. * * @param newPath * name of the cache entry. * @param stage * the stage index of the new entry. * @throws java.lang.IllegalArgumentException * If the path starts or ends with "/", or contains "//" either * "\0". These sequences are not permitted in a git tree object * or DirCache file. Or if {@code stage} is outside of the * range 0..3, inclusive. */
public DirCacheEntry(String newPath, int stage) { this(Constants.encode(newPath), stage); }
Create an empty entry at stage 0.
Params:
  • newPath – name of the cache entry, in the standard encoding.
Throws:
  • IllegalArgumentException – If the path starts or ends with "/", or contains "//" either "\0". These sequences are not permitted in a git tree object or DirCache file.
/** * Create an empty entry at stage 0. * * @param newPath * name of the cache entry, in the standard encoding. * @throws java.lang.IllegalArgumentException * If the path starts or ends with "/", or contains "//" either * "\0". These sequences are not permitted in a git tree object * or DirCache file. */
public DirCacheEntry(byte[] newPath) { this(newPath, STAGE_0); }
Create an empty entry at the specified stage.
Params:
  • path – name of the cache entry, in the standard encoding.
  • stage – the stage index of the new entry.
Throws:
  • IllegalArgumentException – If the path starts or ends with "/", or contains "//" either "\0". These sequences are not permitted in a git tree object or DirCache file. Or if stage is outside of the range 0..3, inclusive.
/** * Create an empty entry at the specified stage. * * @param path * name of the cache entry, in the standard encoding. * @param stage * the stage index of the new entry. * @throws java.lang.IllegalArgumentException * If the path starts or ends with "/", or contains "//" either * "\0". These sequences are not permitted in a git tree object * or DirCache file. Or if {@code stage} is outside of the * range 0..3, inclusive. */
@SuppressWarnings("boxing") public DirCacheEntry(byte[] path, int stage) { checkPath(path); if (stage < 0 || 3 < stage) throw new IllegalArgumentException(MessageFormat.format( JGitText.get().invalidStageForPath, stage, toString(path))); info = new byte[INFO_LEN]; infoOffset = 0; this.path = path; int flags = ((stage & 0x3) << 12); if (path.length < NAME_MASK) flags |= path.length; else flags |= NAME_MASK; NB.encodeInt16(info, infoOffset + P_FLAGS, flags); }
Duplicate DirCacheEntry with same path and copied info.

The same path buffer is reused (avoiding copying), however a new info buffer is created and its contents are copied.

Params:
  • src – entry to clone.
Since:4.2
/** * Duplicate DirCacheEntry with same path and copied info. * <p> * The same path buffer is reused (avoiding copying), however a new info * buffer is created and its contents are copied. * * @param src * entry to clone. * @since 4.2 */
public DirCacheEntry(DirCacheEntry src) { path = src.path; info = new byte[INFO_LEN]; infoOffset = 0; System.arraycopy(src.info, src.infoOffset, info, 0, INFO_LEN); } void write(OutputStream os) throws IOException { final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN; final int pathLen = path.length; os.write(info, infoOffset, len); os.write(path, 0, pathLen); // Index records are padded out to the next 8 byte alignment // for historical reasons related to how C Git read the files. // final int actLen = len + pathLen; final int expLen = (actLen + 8) & ~7; if (actLen != expLen) os.write(nullpad, 0, expLen - actLen); }
Is it possible for this entry to be accidentally assumed clean?

The "racy git" problem happens when a work file can be updated faster than the filesystem records file modification timestamps. It is possible for an application to edit a work file, update the index, then edit it again before the filesystem will give the work file a new modification timestamp. This method tests to see if file was written out at the same time as the index.

Params:
  • smudge_s – seconds component of the index's last modified time.
  • smudge_ns – nanoseconds component of the index's last modified time.
Returns:true if extra careful checks should be used.
Deprecated:use mightBeRacilyClean(Instant) instead
/** * Is it possible for this entry to be accidentally assumed clean? * <p> * The "racy git" problem happens when a work file can be updated faster * than the filesystem records file modification timestamps. It is possible * for an application to edit a work file, update the index, then edit it * again before the filesystem will give the work file a new modification * timestamp. This method tests to see if file was written out at the same * time as the index. * * @param smudge_s * seconds component of the index's last modified time. * @param smudge_ns * nanoseconds component of the index's last modified time. * @return true if extra careful checks should be used. * @deprecated use {@link #mightBeRacilyClean(Instant)} instead */
@Deprecated public final boolean mightBeRacilyClean(int smudge_s, int smudge_ns) { return mightBeRacilyClean(Instant.ofEpochSecond(smudge_s, smudge_ns)); }
Is it possible for this entry to be accidentally assumed clean?

The "racy git" problem happens when a work file can be updated faster than the filesystem records file modification timestamps. It is possible for an application to edit a work file, update the index, then edit it again before the filesystem will give the work file a new modification timestamp. This method tests to see if file was written out at the same time as the index.

Params:
  • smudge – index's last modified time.
Returns:true if extra careful checks should be used.
Since:5.1.9
/** * Is it possible for this entry to be accidentally assumed clean? * <p> * The "racy git" problem happens when a work file can be updated faster * than the filesystem records file modification timestamps. It is possible * for an application to edit a work file, update the index, then edit it * again before the filesystem will give the work file a new modification * timestamp. This method tests to see if file was written out at the same * time as the index. * * @param smudge * index's last modified time. * @return true if extra careful checks should be used. * @since 5.1.9 */
public final boolean mightBeRacilyClean(Instant smudge) { // If the index has a modification time then it came from disk // and was not generated from scratch in memory. In such cases // the entry is 'racily clean' if the entry's cached modification // time is equal to or later than the index modification time. In // such cases the work file is too close to the index to tell if // it is clean or not based on the modification time alone. // final int base = infoOffset + P_MTIME; final int mtime = NB.decodeInt32(info, base); if ((int) smudge.getEpochSecond() == mtime) { return smudge.getNano() <= NB.decodeInt32(info, base + 4); } return false; }
Force this entry to no longer match its working tree file.

This avoids the "racy git" problem by making this index entry no longer match the file in the working directory. Later git will be forced to compare the file content to ensure the file matches the working tree.

/** * Force this entry to no longer match its working tree file. * <p> * This avoids the "racy git" problem by making this index entry no longer * match the file in the working directory. Later git will be forced to * compare the file content to ensure the file matches the working tree. */
public final void smudgeRacilyClean() { // To mark an entry racily clean we set its length to 0 (like native git // does). Entries which are not racily clean and have zero length can be // distinguished from racily clean entries by checking P_OBJECTID // against the SHA1 of empty content. When length is 0 and P_OBJECTID is // different from SHA1 of empty content we know the entry is marked // racily clean final int base = infoOffset + P_SIZE; Arrays.fill(info, base, base + 4, (byte) 0); }
Check whether this entry has been smudged or not

If a blob has length 0 we know its id, see Constants.EMPTY_BLOB_ID. If an entry has length 0 and an ID different from the one for empty blob we know this entry was smudged.

Returns:true if the entry is smudged, false otherwise
/** * Check whether this entry has been smudged or not * <p> * If a blob has length 0 we know its id, see * {@link org.eclipse.jgit.lib.Constants#EMPTY_BLOB_ID}. If an entry has * length 0 and an ID different from the one for empty blob we know this * entry was smudged. * * @return <code>true</code> if the entry is smudged, <code>false</code> * otherwise */
public final boolean isSmudged() { final int base = infoOffset + P_OBJECTID; return (getLength() == 0) && (Constants.EMPTY_BLOB_ID.compareTo(info, base) != 0); } final byte[] idBuffer() { return info; } final int idOffset() { return infoOffset + P_OBJECTID; }
Is this entry always thought to be unmodified?

Most entries in the index do not have this flag set. Users may however set them on if the file system stat() costs are too high on this working directory, such as on NFS or SMB volumes.

Returns:true if we must assume the entry is unmodified.
/** * Is this entry always thought to be unmodified? * <p> * Most entries in the index do not have this flag set. Users may however * set them on if the file system stat() costs are too high on this working * directory, such as on NFS or SMB volumes. * * @return true if we must assume the entry is unmodified. */
public boolean isAssumeValid() { return (info[infoOffset + P_FLAGS] & ASSUME_VALID) != 0; }
Set the assume valid flag for this entry,
Params:
  • assume – true to ignore apparent modifications; false to look at last modified to detect file modifications.
/** * Set the assume valid flag for this entry, * * @param assume * true to ignore apparent modifications; false to look at last * modified to detect file modifications. */
public void setAssumeValid(boolean assume) { if (assume) info[infoOffset + P_FLAGS] |= (byte) ASSUME_VALID; else info[infoOffset + P_FLAGS] &= (byte) ~ASSUME_VALID; }
Whether this entry should be checked for changes
Returns:true if this entry should be checked for changes
/** * Whether this entry should be checked for changes * * @return {@code true} if this entry should be checked for changes */
public boolean isUpdateNeeded() { return (inCoreFlags & UPDATE_NEEDED) != 0; }
Set whether this entry must be checked for changes
Params:
  • updateNeeded – whether this entry must be checked for changes
/** * Set whether this entry must be checked for changes * * @param updateNeeded * whether this entry must be checked for changes */
public void setUpdateNeeded(boolean updateNeeded) { if (updateNeeded) inCoreFlags |= (byte) UPDATE_NEEDED; else inCoreFlags &= (byte) ~UPDATE_NEEDED; }
Get the stage of this entry.

Entries have one of 4 possible stages: 0-3.

Returns:the stage of this entry.
/** * Get the stage of this entry. * <p> * Entries have one of 4 possible stages: 0-3. * * @return the stage of this entry. */
public int getStage() { return (info[infoOffset + P_FLAGS] >>> 4) & 0x3; }
Returns whether this entry should be skipped from the working tree.
Returns:true if this entry should be skipepd.
/** * Returns whether this entry should be skipped from the working tree. * * @return true if this entry should be skipepd. */
public boolean isSkipWorkTree() { return (getExtendedFlags() & SKIP_WORKTREE) != 0; }
Returns whether this entry is intent to be added to the Index.
Returns:true if this entry is intent to add.
/** * Returns whether this entry is intent to be added to the Index. * * @return true if this entry is intent to add. */
public boolean isIntentToAdd() { return (getExtendedFlags() & INTENT_TO_ADD) != 0; }
Returns whether this entry is in the fully-merged stage (0).
Returns:true if this entry is merged
Since:2.2
/** * Returns whether this entry is in the fully-merged stage (0). * * @return true if this entry is merged * @since 2.2 */
public boolean isMerged() { return getStage() == STAGE_0; }
Obtain the raw FileMode bits for this entry.
See Also:
Returns:mode bits for the entry.
/** * Obtain the raw {@link org.eclipse.jgit.lib.FileMode} bits for this entry. * * @return mode bits for the entry. * @see FileMode#fromBits(int) */
public int getRawMode() { return NB.decodeInt32(info, infoOffset + P_MODE); }
Obtain the FileMode for this entry.
Returns:the file mode singleton for this entry.
/** * Obtain the {@link org.eclipse.jgit.lib.FileMode} for this entry. * * @return the file mode singleton for this entry. */
public FileMode getFileMode() { return FileMode.fromBits(getRawMode()); }
Set the file mode for this entry.
Params:
  • mode – the new mode constant.
Throws:
/** * Set the file mode for this entry. * * @param mode * the new mode constant. * @throws java.lang.IllegalArgumentException * If {@code mode} is * {@link org.eclipse.jgit.lib.FileMode#MISSING}, * {@link org.eclipse.jgit.lib.FileMode#TREE}, or any other type * code not permitted in a tree object. */
public void setFileMode(FileMode mode) { switch (mode.getBits() & FileMode.TYPE_MASK) { case FileMode.TYPE_MISSING: case FileMode.TYPE_TREE: throw new IllegalArgumentException(MessageFormat.format( JGitText.get().invalidModeForPath, mode, getPathString())); } NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits()); } void setFileMode(int mode) { NB.encodeInt32(info, infoOffset + P_MODE, mode); }
Get the cached creation time of this file, in milliseconds.
Returns:cached creation time of this file, in milliseconds since the Java epoch (midnight Jan 1, 1970 UTC).
/** * Get the cached creation time of this file, in milliseconds. * * @return cached creation time of this file, in milliseconds since the * Java epoch (midnight Jan 1, 1970 UTC). */
public long getCreationTime() { return decodeTS(P_CTIME); }
Set the cached creation time of this file, using milliseconds.
Params:
  • when – new cached creation time of the file, in milliseconds.
/** * Set the cached creation time of this file, using milliseconds. * * @param when * new cached creation time of the file, in milliseconds. */
public void setCreationTime(long when) { encodeTS(P_CTIME, when); }
Get the cached last modification date of this file, in milliseconds.

One of the indicators that the file has been modified by an application changing the working tree is if the last modification time for the file differs from the time stored in this entry.

Returns:last modification time of this file, in milliseconds since the Java epoch (midnight Jan 1, 1970 UTC).
Deprecated:use getLastModifiedInstant() instead
/** * Get the cached last modification date of this file, in milliseconds. * <p> * One of the indicators that the file has been modified by an application * changing the working tree is if the last modification time for the file * differs from the time stored in this entry. * * @return last modification time of this file, in milliseconds since the * Java epoch (midnight Jan 1, 1970 UTC). * @deprecated use {@link #getLastModifiedInstant()} instead */
@Deprecated public long getLastModified() { return decodeTS(P_MTIME); }
Get the cached last modification date of this file.

One of the indicators that the file has been modified by an application changing the working tree is if the last modification time for the file differs from the time stored in this entry.

Returns:last modification time of this file.
Since:5.1.9
/** * Get the cached last modification date of this file. * <p> * One of the indicators that the file has been modified by an application * changing the working tree is if the last modification time for the file * differs from the time stored in this entry. * * @return last modification time of this file. * @since 5.1.9 */
public Instant getLastModifiedInstant() { return decodeTSInstant(P_MTIME); }
Set the cached last modification date of this file, using milliseconds.
Params:
  • when – new cached modification date of the file, in milliseconds.
Deprecated:use setLastModified(Instant) instead
/** * Set the cached last modification date of this file, using milliseconds. * * @param when * new cached modification date of the file, in milliseconds. * @deprecated use {@link #setLastModified(Instant)} instead */
@Deprecated public void setLastModified(long when) { encodeTS(P_MTIME, when); }
Set the cached last modification date of this file.
Params:
  • when – new cached modification date of the file.
Since:5.1.9
/** * Set the cached last modification date of this file. * * @param when * new cached modification date of the file. * @since 5.1.9 */
public void setLastModified(Instant when) { encodeTS(P_MTIME, when); }
Get the cached size (mod 4 GB) (in bytes) of this file.

One of the indicators that the file has been modified by an application changing the working tree is if the size of the file (in bytes) differs from the size stored in this entry.

Note that this is the length of the file in the working directory, which may differ from the size of the decompressed blob if work tree filters are being used, such as LF<->CRLF conversion.

Note also that for very large files, this is the size of the on-disk file truncated to 32 bits, i.e. modulo 4294967296. If that value is larger than 2GB, it will appear negative.

Returns:cached size of the working directory file, in bytes.
/** * Get the cached size (mod 4 GB) (in bytes) of this file. * <p> * One of the indicators that the file has been modified by an application * changing the working tree is if the size of the file (in bytes) differs * from the size stored in this entry. * <p> * Note that this is the length of the file in the working directory, which * may differ from the size of the decompressed blob if work tree filters * are being used, such as LF&lt;-&gt;CRLF conversion. * <p> * Note also that for very large files, this is the size of the on-disk file * truncated to 32 bits, i.e. modulo 4294967296. If that value is larger * than 2GB, it will appear negative. * * @return cached size of the working directory file, in bytes. */
public int getLength() { return NB.decodeInt32(info, infoOffset + P_SIZE); }
Set the cached size (in bytes) of this file.
Params:
  • sz – new cached size of the file, as bytes. If the file is larger than 2G, cast it to (int) before calling this method.
/** * Set the cached size (in bytes) of this file. * * @param sz * new cached size of the file, as bytes. If the file is larger * than 2G, cast it to (int) before calling this method. */
public void setLength(int sz) { NB.encodeInt32(info, infoOffset + P_SIZE, sz); }
Set the cached size (in bytes) of this file.
Params:
  • sz – new cached size of the file, as bytes.
/** * Set the cached size (in bytes) of this file. * * @param sz * new cached size of the file, as bytes. */
public void setLength(long sz) { setLength((int) sz); }
Obtain the ObjectId for the entry.

Using this method to compare ObjectId values between entries is inefficient as it causes memory allocation.

Returns:object identifier for the entry.
/** * Obtain the ObjectId for the entry. * <p> * Using this method to compare ObjectId values between entries is * inefficient as it causes memory allocation. * * @return object identifier for the entry. */
public ObjectId getObjectId() { return ObjectId.fromRaw(idBuffer(), idOffset()); }
Set the ObjectId for the entry.
Params:
  • id – new object identifier for the entry. May be ObjectId.zeroId() to remove the current identifier.
/** * Set the ObjectId for the entry. * * @param id * new object identifier for the entry. May be * {@link org.eclipse.jgit.lib.ObjectId#zeroId()} to remove the * current identifier. */
public void setObjectId(AnyObjectId id) { id.copyRawTo(idBuffer(), idOffset()); }
Set the ObjectId for the entry from the raw binary representation.
Params:
  • bs – the raw byte buffer to read from. At least 20 bytes after p must be available within this byte array.
  • p – position to read the first byte of data from.
/** * Set the ObjectId for the entry from the raw binary representation. * * @param bs * the raw byte buffer to read from. At least 20 bytes after p * must be available within this byte array. * @param p * position to read the first byte of data from. */
public void setObjectIdFromRaw(byte[] bs, int p) { final int n = Constants.OBJECT_ID_LENGTH; System.arraycopy(bs, p, idBuffer(), idOffset(), n); }
Get the entry's complete path.

This method is not very efficient and is primarily meant for debugging and final output generation. Applications should try to avoid calling it, and if invoked do so only once per interesting entry, where the name is absolutely required for correct function.

Returns:complete path of the entry, from the root of the repository. If the entry is in a subtree there will be at least one '/' in the returned string.
/** * Get the entry's complete path. * <p> * This method is not very efficient and is primarily meant for debugging * and final output generation. Applications should try to avoid calling it, * and if invoked do so only once per interesting entry, where the name is * absolutely required for correct function. * * @return complete path of the entry, from the root of the repository. If * the entry is in a subtree there will be at least one '/' in the * returned string. */
public String getPathString() { return toString(path); }
Get a copy of the entry's raw path bytes.
Returns:raw path bytes.
Since:3.4
/** * Get a copy of the entry's raw path bytes. * * @return raw path bytes. * @since 3.4 */
public byte[] getRawPath() { return path.clone(); }
{@inheritDoc}

Use for debugging only !

/** * {@inheritDoc} * <p> * Use for debugging only ! */
@SuppressWarnings("nls") @Override public String toString() { return getFileMode() + " " + getLength() + " " + getLastModifiedInstant() + " " + getObjectId() + " " + getStage() + " " + getPathString() + "\n"; }
Copy the ObjectId and other meta fields from an existing entry.

This method copies everything except the path from one entry to another, supporting renaming.

Params:
  • src – the entry to copy ObjectId and meta fields from.
/** * Copy the ObjectId and other meta fields from an existing entry. * <p> * This method copies everything except the path from one entry to another, * supporting renaming. * * @param src * the entry to copy ObjectId and meta fields from. */
public void copyMetaData(DirCacheEntry src) { copyMetaData(src, false); }
Copy the ObjectId and other meta fields from an existing entry.

This method copies everything except the path and possibly stage from one entry to another, supporting renaming.

Params:
  • src – the entry to copy ObjectId and meta fields from.
  • keepStage – if true, the stage attribute will not be copied
/** * Copy the ObjectId and other meta fields from an existing entry. * <p> * This method copies everything except the path and possibly stage from one * entry to another, supporting renaming. * * @param src * the entry to copy ObjectId and meta fields from. * @param keepStage * if true, the stage attribute will not be copied */
void copyMetaData(DirCacheEntry src, boolean keepStage) { int origflags = NB.decodeUInt16(info, infoOffset + P_FLAGS); int newflags = NB.decodeUInt16(src.info, src.infoOffset + P_FLAGS); System.arraycopy(src.info, src.infoOffset, info, infoOffset, INFO_LEN); final int pLen = origflags & NAME_MASK; final int SHIFTED_STAGE_MASK = 0x3 << 12; final int pStageShifted; if (keepStage) pStageShifted = origflags & SHIFTED_STAGE_MASK; else pStageShifted = newflags & SHIFTED_STAGE_MASK; NB.encodeInt16(info, infoOffset + P_FLAGS, pStageShifted | pLen | (newflags & ~NAME_MASK & ~SHIFTED_STAGE_MASK)); }
Returns:true if the entry contains extended flags.
/** * @return true if the entry contains extended flags. */
boolean isExtended() { return (info[infoOffset + P_FLAGS] & EXTENDED) != 0; } private long decodeTS(int pIdx) { final int base = infoOffset + pIdx; final int sec = NB.decodeInt32(info, base); final int ms = NB.decodeInt32(info, base + 4) / 1000000; return 1000L * sec + ms; } private Instant decodeTSInstant(int pIdx) { final int base = infoOffset + pIdx; final int sec = NB.decodeInt32(info, base); final int nano = NB.decodeInt32(info, base + 4); return Instant.ofEpochSecond(sec, nano); } private void encodeTS(int pIdx, long when) { final int base = infoOffset + pIdx; NB.encodeInt32(info, base, (int) (when / 1000)); NB.encodeInt32(info, base + 4, ((int) (when % 1000)) * 1000000); } private void encodeTS(int pIdx, Instant when) { final int base = infoOffset + pIdx; NB.encodeInt32(info, base, (int) when.getEpochSecond()); NB.encodeInt32(info, base + 4, when.getNano()); } private int getExtendedFlags() { if (isExtended()) return NB.decodeUInt16(info, infoOffset + P_FLAGS2) << 16; else return 0; } private static void checkPath(byte[] path) { try { SystemReader.getInstance().checkPath(path); } catch (CorruptObjectException e) { InvalidPathException p = new InvalidPathException(toString(path)); p.initCause(e); throw p; } } static String toString(byte[] path) { return UTF_8.decode(ByteBuffer.wrap(path)).toString(); } static int getMaximumInfoLength(boolean extended) { return extended ? INFO_LEN_EXTENDED : INFO_LEN; } }