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