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

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