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

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