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