View Javadoc
1   /*
2    * Copyright (C) 2008-2010, Google Inc.
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4    * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com>
5    * and other copyright owners as documented in the project's IP log.
6    *
7    * This program and the accompanying materials are made available
8    * under the terms of the Eclipse Distribution License v1.0 which
9    * accompanies this distribution, is reproduced below, and is
10   * available at http://www.eclipse.org/org/documents/edl-v10.php
11   *
12   * All rights reserved.
13   *
14   * Redistribution and use in source and binary forms, with or
15   * without modification, are permitted provided that the following
16   * conditions are met:
17   *
18   * - Redistributions of source code must retain the above copyright
19   *   notice, this list of conditions and the following disclaimer.
20   *
21   * - Redistributions in binary form must reproduce the above
22   *   copyright notice, this list of conditions and the following
23   *   disclaimer in the documentation and/or other materials provided
24   *   with the distribution.
25   *
26   * - Neither the name of the Eclipse Foundation, Inc. nor the
27   *   names of its contributors may be used to endorse or promote
28   *   products derived from this software without specific prior
29   *   written permission.
30   *
31   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44   */
45  
46  package org.eclipse.jgit.dircache;
47  
48  import static java.nio.charset.StandardCharsets.ISO_8859_1;
49  
50  import java.io.BufferedInputStream;
51  import java.io.BufferedOutputStream;
52  import java.io.EOFException;
53  import java.io.File;
54  import java.io.FileNotFoundException;
55  import java.io.IOException;
56  import java.io.InputStream;
57  import java.io.OutputStream;
58  import java.security.DigestOutputStream;
59  import java.security.MessageDigest;
60  import java.text.MessageFormat;
61  import java.time.Instant;
62  import java.util.ArrayList;
63  import java.util.Arrays;
64  import java.util.Comparator;
65  import java.util.List;
66  
67  import org.eclipse.jgit.errors.CorruptObjectException;
68  import org.eclipse.jgit.errors.IndexReadException;
69  import org.eclipse.jgit.errors.LockFailedException;
70  import org.eclipse.jgit.errors.UnmergedPathException;
71  import org.eclipse.jgit.events.IndexChangedEvent;
72  import org.eclipse.jgit.events.IndexChangedListener;
73  import org.eclipse.jgit.internal.JGitText;
74  import org.eclipse.jgit.internal.storage.file.FileSnapshot;
75  import org.eclipse.jgit.internal.storage.file.LockFile;
76  import org.eclipse.jgit.lib.AnyObjectId;
77  import org.eclipse.jgit.lib.Constants;
78  import org.eclipse.jgit.lib.ObjectId;
79  import org.eclipse.jgit.lib.ObjectInserter;
80  import org.eclipse.jgit.lib.ObjectReader;
81  import org.eclipse.jgit.lib.Repository;
82  import org.eclipse.jgit.treewalk.FileTreeIterator;
83  import org.eclipse.jgit.treewalk.TreeWalk;
84  import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
85  import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
86  import org.eclipse.jgit.util.FS;
87  import org.eclipse.jgit.util.IO;
88  import org.eclipse.jgit.util.MutableInteger;
89  import org.eclipse.jgit.util.NB;
90  import org.eclipse.jgit.util.TemporaryBuffer;
91  import org.eclipse.jgit.util.io.SilentFileInputStream;
92  
93  /**
94   * Support for the Git dircache (aka index file).
95   * <p>
96   * The index file keeps track of which objects are currently checked out in the
97   * working directory, and the last modified time of those working files. Changes
98   * in the working directory can be detected by comparing the modification times
99   * to the cached modification time within the index file.
100  * <p>
101  * Index files are also used during merges, where the merge happens within the
102  * index file first, and the working directory is updated as a post-merge step.
103  * Conflicts are stored in the index file to allow tool (and human) based
104  * resolutions to be easily performed.
105  */
106 public class DirCache {
107 	private static final byte[] SIG_DIRC = { 'D', 'I', 'R', 'C' };
108 
109 	private static final int EXT_TREE = 0x54524545 /* 'TREE' */;
110 
111 	private static final DirCacheEntry[] NO_ENTRIES = {};
112 
113 	private static final byte[] NO_CHECKSUM = {};
114 
115 	static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
116 		@Override
117 		public int compare(DirCacheEntry./../../../org/eclipse/jgit/dircache/DirCacheEntry.html#DirCacheEntry">DirCacheEntry o1, DirCacheEntry o2) {
118 			final int cr = cmp(o1, o2);
119 			if (cr != 0)
120 				return cr;
121 			return o1.getStage() - o2.getStage();
122 		}
123 	};
124 
125 	static int cmp(DirCacheEntry../../../../org/eclipse/jgit/dircache/DirCacheEntry.html#DirCacheEntry">DirCacheEntry a, DirCacheEntry b) {
126 		return cmp(a.path, a.path.length, b);
127 	}
128 
129 	static int cmp(byte[] aPath, int aLen, DirCacheEntry b) {
130 		return cmp(aPath, aLen, b.path, b.path.length);
131 	}
132 
133 	static int cmp(final byte[] aPath, final int aLen, final byte[] bPath,
134 			final int bLen) {
135 		for (int cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
136 			final int cmp = (aPath[cPos] & 0xff) - (bPath[cPos] & 0xff);
137 			if (cmp != 0)
138 				return cmp;
139 		}
140 		return aLen - bLen;
141 	}
142 
143 	/**
144 	 * Create a new empty index which is never stored on disk.
145 	 *
146 	 * @return an empty cache which has no backing store file. The cache may not
147 	 *         be read or written, but it may be queried and updated (in
148 	 *         memory).
149 	 */
150 	public static DirCache newInCore() {
151 		return new DirCache(null, null);
152 	}
153 
154 	/**
155 	 * Create a new in memory index read from the contents of a tree.
156 	 *
157 	 * @param reader
158 	 *            reader to access the tree objects from a repository.
159 	 * @param treeId
160 	 *            tree to read. Must identify a tree, not a tree-ish.
161 	 * @return a new cache which has no backing store file, but contains the
162 	 *         contents of {@code treeId}.
163 	 * @throws java.io.IOException
164 	 *             one or more trees not available from the ObjectReader.
165 	 * @since 4.2
166 	 */
167 	public static DirCache read(ObjectReader reader, AnyObjectId treeId)
168 			throws IOException {
169 		DirCache d = newInCore();
170 		DirCacheBuilder b = d.builder();
171 		b.addTree(null, DirCacheEntry.STAGE_0, reader, treeId);
172 		b.finish();
173 		return d;
174 	}
175 
176 	/**
177 	 * Create a new in-core index representation and read an index from disk.
178 	 * <p>
179 	 * The new index will be read before it is returned to the caller. Read
180 	 * failures are reported as exceptions and therefore prevent the method from
181 	 * returning a partially populated index.
182 	 *
183 	 * @param repository
184 	 *            repository containing the index to read
185 	 * @return a cache representing the contents of the specified index file (if
186 	 *         it exists) or an empty cache if the file does not exist.
187 	 * @throws java.io.IOException
188 	 *             the index file is present but could not be read.
189 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
190 	 *             the index file is using a format or extension that this
191 	 *             library does not support.
192 	 */
193 	public static DirCache read(Repository repository)
194 			throws CorruptObjectException, IOException {
195 		final DirCache c = read(repository.getIndexFile(), repository.getFS());
196 		c.repository = repository;
197 		return c;
198 	}
199 
200 	/**
201 	 * Create a new in-core index representation and read an index from disk.
202 	 * <p>
203 	 * The new index will be read before it is returned to the caller. Read
204 	 * failures are reported as exceptions and therefore prevent the method from
205 	 * returning a partially populated index.
206 	 *
207 	 * @param indexLocation
208 	 *            location of the index file on disk.
209 	 * @param fs
210 	 *            the file system abstraction which will be necessary to perform
211 	 *            certain file system operations.
212 	 * @return a cache representing the contents of the specified index file (if
213 	 *         it exists) or an empty cache if the file does not exist.
214 	 * @throws java.io.IOException
215 	 *             the index file is present but could not be read.
216 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
217 	 *             the index file is using a format or extension that this
218 	 *             library does not support.
219 	 */
220 	public static DirCache read(File indexLocation, FS fs)
221 			throws CorruptObjectException, IOException {
222 		final DirCache/DirCache.html#DirCache">DirCache c = new DirCache(indexLocation, fs);
223 		c.read();
224 		return c;
225 	}
226 
227 	/**
228 	 * Create a new in-core index representation, lock it, and read from disk.
229 	 * <p>
230 	 * The new index will be locked and then read before it is returned to the
231 	 * caller. Read failures are reported as exceptions and therefore prevent
232 	 * the method from returning a partially populated index. On read failure,
233 	 * the lock is released.
234 	 *
235 	 * @param indexLocation
236 	 *            location of the index file on disk.
237 	 * @param fs
238 	 *            the file system abstraction which will be necessary to perform
239 	 *            certain file system operations.
240 	 * @return a cache representing the contents of the specified index file (if
241 	 *         it exists) or an empty cache if the file does not exist.
242 	 * @throws java.io.IOException
243 	 *             the index file is present but could not be read, or the lock
244 	 *             could not be obtained.
245 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
246 	 *             the index file is using a format or extension that this
247 	 *             library does not support.
248 	 */
249 	public static DirCache lock(File indexLocation, FS fs)
250 			throws CorruptObjectException, IOException {
251 		final DirCache/DirCache.html#DirCache">DirCache c = new DirCache(indexLocation, fs);
252 		if (!c.lock())
253 			throw new LockFailedException(indexLocation);
254 
255 		try {
256 			c.read();
257 		} catch (IOException e) {
258 			c.unlock();
259 			throw e;
260 		} catch (RuntimeException e) {
261 			c.unlock();
262 			throw e;
263 		} catch (Error e) {
264 			c.unlock();
265 			throw e;
266 		}
267 
268 		return c;
269 	}
270 
271 	/**
272 	 * Create a new in-core index representation, lock it, and read from disk.
273 	 * <p>
274 	 * The new index will be locked and then read before it is returned to the
275 	 * caller. Read failures are reported as exceptions and therefore prevent
276 	 * the method from returning a partially populated index. On read failure,
277 	 * the lock is released.
278 	 *
279 	 * @param repository
280 	 *            repository containing the index to lock and read
281 	 * @param indexChangedListener
282 	 *            listener to be informed when DirCache is committed
283 	 * @return a cache representing the contents of the specified index file (if
284 	 *         it exists) or an empty cache if the file does not exist.
285 	 * @throws java.io.IOException
286 	 *             the index file is present but could not be read, or the lock
287 	 *             could not be obtained.
288 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
289 	 *             the index file is using a format or extension that this
290 	 *             library does not support.
291 	 * @since 2.0
292 	 */
293 	public static DirCache lock(final Repository repository,
294 			final IndexChangedListener indexChangedListener)
295 			throws CorruptObjectException, IOException {
296 		DirCache c = lock(repository.getIndexFile(), repository.getFS(),
297 				indexChangedListener);
298 		c.repository = repository;
299 		return c;
300 	}
301 
302 	/**
303 	 * Create a new in-core index representation, lock it, and read from disk.
304 	 * <p>
305 	 * The new index will be locked and then read before it is returned to the
306 	 * caller. Read failures are reported as exceptions and therefore prevent
307 	 * the method from returning a partially populated index. On read failure,
308 	 * the lock is released.
309 	 *
310 	 * @param indexLocation
311 	 *            location of the index file on disk.
312 	 * @param fs
313 	 *            the file system abstraction which will be necessary to perform
314 	 *            certain file system operations.
315 	 * @param indexChangedListener
316 	 *            listener to be informed when DirCache is committed
317 	 * @return a cache representing the contents of the specified index file (if
318 	 *         it exists) or an empty cache if the file does not exist.
319 	 * @throws java.io.IOException
320 	 *             the index file is present but could not be read, or the lock
321 	 *             could not be obtained.
322 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
323 	 *             the index file is using a format or extension that this
324 	 *             library does not support.
325 	 */
326 	public static DirCache lock(final File indexLocation, final FS fs,
327 			IndexChangedListener indexChangedListener)
328 			throws CorruptObjectException,
329 			IOException {
330 		DirCache c = lock(indexLocation, fs);
331 		c.registerIndexChangedListener(indexChangedListener);
332 		return c;
333 	}
334 
335 	/** Location of the current version of the index file. */
336 	private final File liveFile;
337 
338 	/** Individual file index entries, sorted by path name. */
339 	private DirCacheEntry[] sortedEntries;
340 
341 	/** Number of positions within {@link #sortedEntries} that are valid. */
342 	private int entryCnt;
343 
344 	/** Cache tree for this index; null if the cache tree is not available. */
345 	private DirCacheTree tree;
346 
347 	/** Our active lock (if we hold it); null if we don't have it locked. */
348 	private LockFile myLock;
349 
350 	/** Keep track of whether the index has changed or not */
351 	private FileSnapshot snapshot;
352 
353 	/** index checksum when index was read from disk */
354 	private byte[] readIndexChecksum;
355 
356 	/** index checksum when index was written to disk */
357 	private byte[] writeIndexChecksum;
358 
359 	/** listener to be informed on commit */
360 	private IndexChangedListener indexChangedListener;
361 
362 	/** Repository containing this index */
363 	private Repository repository;
364 
365 	/**
366 	 * Create a new in-core index representation.
367 	 * <p>
368 	 * The new index will be empty. Callers may wish to read from the on disk
369 	 * file first with {@link #read()}.
370 	 *
371 	 * @param indexLocation
372 	 *            location of the index file on disk.
373 	 * @param fs
374 	 *            the file system abstraction which will be necessary to perform
375 	 *            certain file system operations.
376 	 */
377 	public DirCache(File indexLocation, FS fs) {
378 		liveFile = indexLocation;
379 		clear();
380 	}
381 
382 	/**
383 	 * Create a new builder to update this cache.
384 	 * <p>
385 	 * Callers should add all entries to the builder, then use
386 	 * {@link org.eclipse.jgit.dircache.DirCacheBuilder#finish()} to update this
387 	 * instance.
388 	 *
389 	 * @return a new builder instance for this cache.
390 	 */
391 	public DirCacheBuilder builder() {
392 		return new DirCacheBuilder(this, entryCnt + 16);
393 	}
394 
395 	/**
396 	 * Create a new editor to recreate this cache.
397 	 * <p>
398 	 * Callers should add commands to the editor, then use
399 	 * {@link org.eclipse.jgit.dircache.DirCacheEditor#finish()} to update this
400 	 * instance.
401 	 *
402 	 * @return a new builder instance for this cache.
403 	 */
404 	public DirCacheEditor editor() {
405 		return new DirCacheEditor(this, entryCnt + 16);
406 	}
407 
408 	void replace(DirCacheEntry[] e, int cnt) {
409 		sortedEntries = e;
410 		entryCnt = cnt;
411 		tree = null;
412 	}
413 
414 	/**
415 	 * Read the index from disk, if it has changed on disk.
416 	 * <p>
417 	 * This method tries to avoid loading the index if it has not changed since
418 	 * the last time we consulted it. A missing index file will be treated as
419 	 * though it were present but had no file entries in it.
420 	 *
421 	 * @throws java.io.IOException
422 	 *             the index file is present but could not be read. This
423 	 *             DirCache instance may not be populated correctly.
424 	 * @throws org.eclipse.jgit.errors.CorruptObjectException
425 	 *             the index file is using a format or extension that this
426 	 *             library does not support.
427 	 */
428 	public void read() throws IOException, CorruptObjectException {
429 		if (liveFile == null)
430 			throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile);
431 		if (!liveFile.exists())
432 			clear();
433 		else if (snapshot == null || snapshot.isModified(liveFile)) {
434 			try (SilentFileInputStreamm.html#SilentFileInputStream">SilentFileInputStream inStream = new SilentFileInputStream(
435 					liveFile)) {
436 				clear();
437 				readFrom(inStream);
438 			} catch (FileNotFoundException fnfe) {
439 				if (liveFile.exists()) {
440 					// Panic: the index file exists but we can't read it
441 					throw new IndexReadException(
442 							MessageFormat.format(JGitText.get().cannotReadIndex,
443 									liveFile.getAbsolutePath(), fnfe));
444 				}
445 				// Someone must have deleted it between our exists test
446 				// and actually opening the path. That's fine, its empty.
447 				//
448 				clear();
449 			}
450 			snapshot = FileSnapshot.save(liveFile);
451 		}
452 	}
453 
454 	/**
455 	 * Whether the memory state differs from the index file
456 	 *
457 	 * @return {@code true} if the memory state differs from the index file
458 	 * @throws java.io.IOException
459 	 */
460 	public boolean isOutdated() throws IOException {
461 		if (liveFile == null || !liveFile.exists())
462 			return false;
463 		return snapshot == null || snapshot.isModified(liveFile);
464 	}
465 
466 	/**
467 	 * Empty this index, removing all entries.
468 	 */
469 	public void clear() {
470 		snapshot = null;
471 		sortedEntries = NO_ENTRIES;
472 		entryCnt = 0;
473 		tree = null;
474 		readIndexChecksum = NO_CHECKSUM;
475 	}
476 
477 	private void readFrom(InputStream inStream) throws IOException,
478 			CorruptObjectException {
479 		final BufferedInputStream in = new BufferedInputStream(inStream);
480 		final MessageDigest md = Constants.newMessageDigest();
481 
482 		// Read the index header and verify we understand it.
483 		//
484 		final byte[] hdr = new byte[20];
485 		IO.readFully(in, hdr, 0, 12);
486 		md.update(hdr, 0, 12);
487 		if (!is_DIRC(hdr))
488 			throw new CorruptObjectException(JGitText.get().notADIRCFile);
489 		final int ver = NB.decodeInt32(hdr, 4);
490 		boolean extended = false;
491 		if (ver == 3)
492 			extended = true;
493 		else if (ver != 2)
494 			throw new CorruptObjectException(MessageFormat.format(
495 					JGitText.get().unknownDIRCVersion, Integer.valueOf(ver)));
496 		entryCnt = NB.decodeInt32(hdr, 8);
497 		if (entryCnt < 0)
498 			throw new CorruptObjectException(JGitText.get().DIRCHasTooManyEntries);
499 
500 		snapshot = FileSnapshot.save(liveFile);
501 		Instant smudge = snapshot.lastModifiedInstant();
502 
503 		// Load the individual file entries.
504 		//
505 		final int infoLength = DirCacheEntry.getMaximumInfoLength(extended);
506 		final byte[] infos = new byte[infoLength * entryCnt];
507 		sortedEntries = new DirCacheEntry[entryCnt];
508 
509 		final MutableInteger.html#MutableInteger">MutableInteger infoAt = new MutableInteger();
510 		for (int i = 0; i < entryCnt; i++) {
511 			sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md, smudge);
512 		}
513 
514 		// After the file entries are index extensions, and then a footer.
515 		//
516 		for (;;) {
517 			in.mark(21);
518 			IO.readFully(in, hdr, 0, 20);
519 			if (in.read() < 0) {
520 				// No extensions present; the file ended where we expected.
521 				//
522 				break;
523 			}
524 
525 			in.reset();
526 			md.update(hdr, 0, 8);
527 			IO.skipFully(in, 8);
528 
529 			long sz = NB.decodeUInt32(hdr, 4);
530 			switch (NB.decodeInt32(hdr, 0)) {
531 			case EXT_TREE: {
532 				if (Integer.MAX_VALUE < sz) {
533 					throw new CorruptObjectException(MessageFormat.format(
534 							JGitText.get().DIRCExtensionIsTooLargeAt,
535 							formatExtensionName(hdr), Long.valueOf(sz)));
536 				}
537 				final byte[] raw = new byte[(int) sz];
538 				IO.readFully(in, raw, 0, raw.length);
539 				md.update(raw, 0, raw.length);
540 				tree = new DirCacheTree(raw, new MutableInteger(), null);
541 				break;
542 			}
543 			default:
544 				if (hdr[0] >= 'A' && hdr[0] <= 'Z') {
545 					// The extension is optional and is here only as
546 					// a performance optimization. Since we do not
547 					// understand it, we can safely skip past it, after
548 					// we include its data in our checksum.
549 					//
550 					skipOptionalExtension(in, md, hdr, sz);
551 				} else {
552 					// The extension is not an optimization and is
553 					// _required_ to understand this index format.
554 					// Since we did not trap it above we must abort.
555 					//
556 					throw new CorruptObjectException(MessageFormat.format(JGitText.get().DIRCExtensionNotSupportedByThisVersion
557 							, formatExtensionName(hdr)));
558 				}
559 			}
560 		}
561 
562 		readIndexChecksum = md.digest();
563 		if (!Arrays.equals(readIndexChecksum, hdr)) {
564 			throw new CorruptObjectException(JGitText.get().DIRCChecksumMismatch);
565 		}
566 	}
567 
568 	private void skipOptionalExtension(final InputStream in,
569 			final MessageDigest md, final byte[] hdr, long sz)
570 			throws IOException {
571 		final byte[] b = new byte[4096];
572 		while (0 < sz) {
573 			int n = in.read(b, 0, (int) Math.min(b.length, sz));
574 			if (n < 0) {
575 				throw new EOFException(
576 						MessageFormat.format(
577 								JGitText.get().shortReadOfOptionalDIRCExtensionExpectedAnotherBytes,
578 								formatExtensionName(hdr), Long.valueOf(sz)));
579 			}
580 			md.update(b, 0, n);
581 			sz -= n;
582 		}
583 	}
584 
585 	private static String formatExtensionName(byte[] hdr) {
586 		return "'" + new String(hdr, 0, 4, ISO_8859_1) + "'"; //$NON-NLS-1$ //$NON-NLS-2$
587 	}
588 
589 	private static boolean is_DIRC(byte[] hdr) {
590 		if (hdr.length < SIG_DIRC.length)
591 			return false;
592 		for (int i = 0; i < SIG_DIRC.length; i++)
593 			if (hdr[i] != SIG_DIRC[i])
594 				return false;
595 		return true;
596 	}
597 
598 	/**
599 	 * Try to establish an update lock on the cache file.
600 	 *
601 	 * @return true if the lock is now held by the caller; false if it is held
602 	 *         by someone else.
603 	 * @throws java.io.IOException
604 	 *             the output file could not be created. The caller does not
605 	 *             hold the lock.
606 	 */
607 	public boolean lock() throws IOException {
608 		if (liveFile == null)
609 			throw new IOException(JGitText.get().dirCacheDoesNotHaveABackingFile);
610 		final LockFiletorage/file/LockFile.html#LockFile">LockFile tmp = new LockFile(liveFile);
611 		if (tmp.lock()) {
612 			tmp.setNeedStatInformation(true);
613 			myLock = tmp;
614 			return true;
615 		}
616 		return false;
617 	}
618 
619 	/**
620 	 * Write the entry records from memory to disk.
621 	 * <p>
622 	 * The cache must be locked first by calling {@link #lock()} and receiving
623 	 * true as the return value. Applications are encouraged to lock the index,
624 	 * then invoke {@link #read()} to ensure the in-memory data is current,
625 	 * prior to updating the in-memory entries.
626 	 * <p>
627 	 * Once written the lock is closed and must be either committed with
628 	 * {@link #commit()} or rolled back with {@link #unlock()}.
629 	 *
630 	 * @throws java.io.IOException
631 	 *             the output file could not be created. The caller no longer
632 	 *             holds the lock.
633 	 */
634 	public void write() throws IOException {
635 		final LockFile tmp = myLock;
636 		requireLocked(tmp);
637 		try (OutputStream o = tmp.getOutputStream();
638 				OutputStream bo = new BufferedOutputStream(o)) {
639 			writeTo(liveFile.getParentFile(), bo);
640 		} catch (IOException err) {
641 			tmp.unlock();
642 			throw err;
643 		} catch (RuntimeException err) {
644 			tmp.unlock();
645 			throw err;
646 		} catch (Error err) {
647 			tmp.unlock();
648 			throw err;
649 		}
650 	}
651 
652 	void writeTo(File dir, OutputStream os) throws IOException {
653 		final MessageDigest foot = Constants.newMessageDigest();
654 		final DigestOutputStream dos = new DigestOutputStream(os, foot);
655 
656 		boolean extended = false;
657 		for (int i = 0; i < entryCnt; i++) {
658 			if (sortedEntries[i].isExtended()) {
659 				extended = true;
660 				break;
661 			}
662 		}
663 
664 		// Write the header.
665 		//
666 		final byte[] tmp = new byte[128];
667 		System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
668 		NB.encodeInt32(tmp, 4, extended ? 3 : 2);
669 		NB.encodeInt32(tmp, 8, entryCnt);
670 		dos.write(tmp, 0, 12);
671 
672 		// Write the individual file entries.
673 
674 		Instant smudge;
675 		if (myLock != null) {
676 			// For new files we need to smudge the index entry
677 			// if they have been modified "now". Ideally we'd
678 			// want the timestamp when we're done writing the index,
679 			// so we use the current timestamp as a approximation.
680 			myLock.createCommitSnapshot();
681 			snapshot = myLock.getCommitSnapshot();
682 			smudge = snapshot.lastModifiedInstant();
683 		} else {
684 			// Used in unit tests only
685 			smudge = Instant.EPOCH;
686 		}
687 
688 		// Check if tree is non-null here since calling updateSmudgedEntries
689 		// will automatically build it via creating a DirCacheIterator
690 		final boolean writeTree = tree != null;
691 
692 		if (repository != null && entryCnt > 0)
693 			updateSmudgedEntries();
694 
695 		for (int i = 0; i < entryCnt; i++) {
696 			final DirCacheEntry e = sortedEntries[i];
697 			if (e.mightBeRacilyClean(smudge)) {
698 				e.smudgeRacilyClean();
699 			}
700 			e.write(dos);
701 		}
702 
703 		if (writeTree) {
704 			@SuppressWarnings("resource") // Explicitly closed in try block, and
705 											// destroyed in finally
706 			TemporaryBuffer bb = new TemporaryBuffer.LocalFile(dir, 5 << 20);
707 			try {
708 				tree.write(tmp, bb);
709 				bb.close();
710 
711 				NB.encodeInt32(tmp, 0, EXT_TREE);
712 				NB.encodeInt32(tmp, 4, (int) bb.length());
713 				dos.write(tmp, 0, 8);
714 				bb.writeTo(dos, null);
715 			} finally {
716 				bb.destroy();
717 			}
718 		}
719 		writeIndexChecksum = foot.digest();
720 		os.write(writeIndexChecksum);
721 		os.close();
722 	}
723 
724 	/**
725 	 * Commit this change and release the lock.
726 	 * <p>
727 	 * If this method fails (returns false) the lock is still released.
728 	 *
729 	 * @return true if the commit was successful and the file contains the new
730 	 *         data; false if the commit failed and the file remains with the
731 	 *         old data.
732 	 * @throws java.lang.IllegalStateException
733 	 *             the lock is not held.
734 	 */
735 	public boolean commit() {
736 		final LockFile tmp = myLock;
737 		requireLocked(tmp);
738 		myLock = null;
739 		if (!tmp.commit()) {
740 			return false;
741 		}
742 		snapshot = tmp.getCommitSnapshot();
743 		if (indexChangedListener != null
744 				&& !Arrays.equals(readIndexChecksum, writeIndexChecksum)) {
745 			indexChangedListener.onIndexChanged(new IndexChangedEvent(true));
746 		}
747 		return true;
748 	}
749 
750 	private void requireLocked(LockFile tmp) {
751 		if (liveFile == null)
752 			throw new IllegalStateException(JGitText.get().dirCacheIsNotLocked);
753 		if (tmp == null)
754 			throw new IllegalStateException(MessageFormat.format(JGitText.get().dirCacheFileIsNotLocked
755 					, liveFile.getAbsolutePath()));
756 	}
757 
758 	/**
759 	 * Unlock this file and abort this change.
760 	 * <p>
761 	 * The temporary file (if created) is deleted before returning.
762 	 */
763 	public void unlock() {
764 		final LockFile tmp = myLock;
765 		if (tmp != null) {
766 			myLock = null;
767 			tmp.unlock();
768 		}
769 	}
770 
771 	/**
772 	 * Locate the position a path's entry is at in the index. For details refer
773 	 * to #findEntry(byte[], int).
774 	 *
775 	 * @param path
776 	 *            the path to search for.
777 	 * @return if &gt;= 0 then the return value is the position of the entry in
778 	 *         the index; pass to {@link #getEntry(int)} to obtain the entry
779 	 *         information. If &lt; 0 the entry does not exist in the index.
780 	 */
781 	public int findEntry(String path) {
782 		final byte[] p = Constants.encode(path);
783 		return findEntry(p, p.length);
784 	}
785 
786 	/**
787 	 * Locate the position a path's entry is at in the index.
788 	 * <p>
789 	 * If there is at least one entry in the index for this path the position of
790 	 * the lowest stage is returned. Subsequent stages can be identified by
791 	 * testing consecutive entries until the path differs.
792 	 * <p>
793 	 * If no path matches the entry -(position+1) is returned, where position is
794 	 * the location it would have gone within the index.
795 	 *
796 	 * @param p
797 	 *            the byte array starting with the path to search for.
798 	 * @param pLen
799 	 *            the length of the path in bytes
800 	 * @return if &gt;= 0 then the return value is the position of the entry in
801 	 *         the index; pass to {@link #getEntry(int)} to obtain the entry
802 	 *         information. If &lt; 0 the entry does not exist in the index.
803 	 * @since 3.4
804 	 */
805 	public int findEntry(byte[] p, int pLen) {
806 		return findEntry(0, p, pLen);
807 	}
808 
809 	int findEntry(int low, byte[] p, int pLen) {
810 		int high = entryCnt;
811 		while (low < high) {
812 			int mid = (low + high) >>> 1;
813 			final int cmp = cmp(p, pLen, sortedEntries[mid]);
814 			if (cmp < 0)
815 				high = mid;
816 			else if (cmp == 0) {
817 				while (mid > 0 && cmp(p, pLen, sortedEntries[mid - 1]) == 0)
818 					mid--;
819 				return mid;
820 			} else
821 				low = mid + 1;
822 		}
823 		return -(low + 1);
824 	}
825 
826 	/**
827 	 * Determine the next index position past all entries with the same name.
828 	 * <p>
829 	 * As index entries are sorted by path name, then stage number, this method
830 	 * advances the supplied position to the first position in the index whose
831 	 * path name does not match the path name of the supplied position's entry.
832 	 *
833 	 * @param position
834 	 *            entry position of the path that should be skipped.
835 	 * @return position of the next entry whose path is after the input.
836 	 */
837 	public int nextEntry(int position) {
838 		DirCacheEntry last = sortedEntries[position];
839 		int nextIdx = position + 1;
840 		while (nextIdx < entryCnt) {
841 			final DirCacheEntry next = sortedEntries[nextIdx];
842 			if (cmp(last, next) != 0)
843 				break;
844 			last = next;
845 			nextIdx++;
846 		}
847 		return nextIdx;
848 	}
849 
850 	int nextEntry(byte[] p, int pLen, int nextIdx) {
851 		while (nextIdx < entryCnt) {
852 			final DirCacheEntry next = sortedEntries[nextIdx];
853 			if (!DirCacheTree.peq(p, next.path, pLen))
854 				break;
855 			nextIdx++;
856 		}
857 		return nextIdx;
858 	}
859 
860 	/**
861 	 * Total number of file entries stored in the index.
862 	 * <p>
863 	 * This count includes unmerged stages for a file entry if the file is
864 	 * currently conflicted in a merge. This means the total number of entries
865 	 * in the index may be up to 3 times larger than the number of files in the
866 	 * working directory.
867 	 * <p>
868 	 * Note that this value counts only <i>files</i>.
869 	 *
870 	 * @return number of entries available.
871 	 * @see #getEntry(int)
872 	 */
873 	public int getEntryCount() {
874 		return entryCnt;
875 	}
876 
877 	/**
878 	 * Get a specific entry.
879 	 *
880 	 * @param i
881 	 *            position of the entry to get.
882 	 * @return the entry at position <code>i</code>.
883 	 */
884 	public DirCacheEntry getEntry(int i) {
885 		return sortedEntries[i];
886 	}
887 
888 	/**
889 	 * Get a specific entry.
890 	 *
891 	 * @param path
892 	 *            the path to search for.
893 	 * @return the entry for the given <code>path</code>.
894 	 */
895 	public DirCacheEntry getEntry(String path) {
896 		final int i = findEntry(path);
897 		return i < 0 ? null : sortedEntries[i];
898 	}
899 
900 	/**
901 	 * Recursively get all entries within a subtree.
902 	 *
903 	 * @param path
904 	 *            the subtree path to get all entries within.
905 	 * @return all entries recursively contained within the subtree.
906 	 */
907 	public DirCacheEntry[] getEntriesWithin(String path) {
908 		if (path.length() == 0) {
909 			DirCacheEntry[] r = new DirCacheEntry[entryCnt];
910 			System.arraycopy(sortedEntries, 0, r, 0, entryCnt);
911 			return r;
912 		}
913 		if (!path.endsWith("/")) //$NON-NLS-1$
914 			path += "/"; //$NON-NLS-1$
915 		final byte[] p = Constants.encode(path);
916 		final int pLen = p.length;
917 
918 		int eIdx = findEntry(p, pLen);
919 		if (eIdx < 0)
920 			eIdx = -(eIdx + 1);
921 		final int lastIdx = nextEntry(p, pLen, eIdx);
922 		final DirCacheEntryheEntry.html#DirCacheEntry">DirCacheEntry[] r = new DirCacheEntry[lastIdx - eIdx];
923 		System.arraycopy(sortedEntries, eIdx, r, 0, r.length);
924 		return r;
925 	}
926 
927 	void toArray(final int i, final DirCacheEntry[] dst, final int off,
928 			final int cnt) {
929 		System.arraycopy(sortedEntries, i, dst, off, cnt);
930 	}
931 
932 	/**
933 	 * Obtain (or build) the current cache tree structure.
934 	 * <p>
935 	 * This method can optionally recreate the cache tree, without flushing the
936 	 * tree objects themselves to disk.
937 	 *
938 	 * @param build
939 	 *            if true and the cache tree is not present in the index it will
940 	 *            be generated and returned to the caller.
941 	 * @return the cache tree; null if there is no current cache tree available
942 	 *         and <code>build</code> was false.
943 	 */
944 	public DirCacheTree getCacheTree(boolean build) {
945 		if (build) {
946 			if (tree == null)
947 				tree = new DirCacheTree();
948 			tree.validate(sortedEntries, entryCnt, 0, 0);
949 		}
950 		return tree;
951 	}
952 
953 	/**
954 	 * Write all index trees to the object store, returning the root tree.
955 	 *
956 	 * @param ow
957 	 *            the writer to use when serializing to the store. The caller is
958 	 *            responsible for flushing the inserter before trying to use the
959 	 *            returned tree identity.
960 	 * @return identity for the root tree.
961 	 * @throws org.eclipse.jgit.errors.UnmergedPathException
962 	 *             one or more paths contain higher-order stages (stage &gt; 0),
963 	 *             which cannot be stored in a tree object.
964 	 * @throws java.lang.IllegalStateException
965 	 *             one or more paths contain an invalid mode which should never
966 	 *             appear in a tree object.
967 	 * @throws java.io.IOException
968 	 *             an unexpected error occurred writing to the object store.
969 	 */
970 	public ObjectId writeTree(ObjectInserter ow)
971 			throws UnmergedPathException, IOException {
972 		return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow);
973 	}
974 
975 	/**
976 	 * Tells whether this index contains unmerged paths.
977 	 *
978 	 * @return {@code true} if this index contains unmerged paths. Means: at
979 	 *         least one entry is of a stage different from 0. {@code false}
980 	 *         will be returned if all entries are of stage 0.
981 	 */
982 	public boolean hasUnmergedPaths() {
983 		for (int i = 0; i < entryCnt; i++) {
984 			if (sortedEntries[i].getStage() > 0) {
985 				return true;
986 			}
987 		}
988 		return false;
989 	}
990 
991 	private void registerIndexChangedListener(IndexChangedListener listener) {
992 		this.indexChangedListener = listener;
993 	}
994 
995 	/**
996 	 * Update any smudged entries with information from the working tree.
997 	 *
998 	 * @throws IOException
999 	 */
1000 	private void updateSmudgedEntries() throws IOException {
1001 		List<String> paths = new ArrayList<>(128);
1002 		try (TreeWalkeeWalk.html#TreeWalk">TreeWalk walk = new TreeWalk(repository)) {
1003 			walk.setOperationType(OperationType.CHECKIN_OP);
1004 			for (int i = 0; i < entryCnt; i++)
1005 				if (sortedEntries[i].isSmudged())
1006 					paths.add(sortedEntries[i].getPathString());
1007 			if (paths.isEmpty())
1008 				return;
1009 			walk.setFilter(PathFilterGroup.createFromStrings(paths));
1010 
1011 			DirCacheIterator iIter = new DirCacheIterator(this);
1012 			FileTreeIterator fIter = new FileTreeIterator(repository);
1013 			walk.addTree(iIter);
1014 			walk.addTree(fIter);
1015 			fIter.setDirCacheIterator(walk, 0);
1016 			walk.setRecursive(true);
1017 			while (walk.next()) {
1018 				iIter = walk.getTree(0, DirCacheIterator.class);
1019 				if (iIter == null)
1020 					continue;
1021 				fIter = walk.getTree(1, FileTreeIterator.class);
1022 				if (fIter == null)
1023 					continue;
1024 				DirCacheEntry entry = iIter.getDirCacheEntry();
1025 				if (entry.isSmudged() && iIter.idEqual(fIter)) {
1026 					entry.setLength(fIter.getEntryLength());
1027 					entry.setLastModified(fIter.getEntryLastModifiedInstant());
1028 				}
1029 			}
1030 		}
1031 	}
1032 }