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