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

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