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