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 33KB

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