You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirCache.java 31KB

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