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