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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.dircache;
  45. import java.io.BufferedInputStream;
  46. import java.io.BufferedOutputStream;
  47. import java.io.EOFException;
  48. import java.io.File;
  49. import java.io.FileInputStream;
  50. import java.io.FileNotFoundException;
  51. import java.io.IOException;
  52. import java.io.InputStream;
  53. import java.io.OutputStream;
  54. import java.io.UnsupportedEncodingException;
  55. import java.security.DigestOutputStream;
  56. import java.security.MessageDigest;
  57. import java.util.Arrays;
  58. import java.util.Comparator;
  59. import org.eclipse.jgit.errors.CorruptObjectException;
  60. import org.eclipse.jgit.errors.UnmergedPathException;
  61. import org.eclipse.jgit.lib.Constants;
  62. import org.eclipse.jgit.lib.LockFile;
  63. import org.eclipse.jgit.lib.ObjectId;
  64. import org.eclipse.jgit.lib.ObjectWriter;
  65. import org.eclipse.jgit.lib.Repository;
  66. import org.eclipse.jgit.util.IO;
  67. import org.eclipse.jgit.util.MutableInteger;
  68. import org.eclipse.jgit.util.NB;
  69. import org.eclipse.jgit.util.TemporaryBuffer;
  70. /**
  71. * Support for the Git dircache (aka index file).
  72. * <p>
  73. * The index file keeps track of which objects are currently checked out in the
  74. * working directory, and the last modified time of those working files. Changes
  75. * in the working directory can be detected by comparing the modification times
  76. * to the cached modification time within the index file.
  77. * <p>
  78. * Index files are also used during merges, where the merge happens within the
  79. * index file first, and the working directory is updated as a post-merge step.
  80. * Conflicts are stored in the index file to allow tool (and human) based
  81. * resolutions to be easily performed.
  82. */
  83. public class DirCache {
  84. private static final byte[] SIG_DIRC = { 'D', 'I', 'R', 'C' };
  85. private static final int EXT_TREE = 0x54524545 /* 'TREE' */;
  86. private static final int INFO_LEN = DirCacheEntry.INFO_LEN;
  87. private static final DirCacheEntry[] NO_ENTRIES = {};
  88. static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
  89. public int compare(final DirCacheEntry o1, final DirCacheEntry o2) {
  90. final int cr = cmp(o1, o2);
  91. if (cr != 0)
  92. return cr;
  93. return o1.getStage() - o2.getStage();
  94. }
  95. };
  96. static int cmp(final DirCacheEntry a, final DirCacheEntry b) {
  97. return cmp(a.path, a.path.length, b);
  98. }
  99. static int cmp(final byte[] aPath, final int aLen, final DirCacheEntry b) {
  100. return cmp(aPath, aLen, b.path, b.path.length);
  101. }
  102. static int cmp(final byte[] aPath, final int aLen, final byte[] bPath,
  103. final int bLen) {
  104. for (int cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
  105. final int cmp = (aPath[cPos] & 0xff) - (bPath[cPos] & 0xff);
  106. if (cmp != 0)
  107. return cmp;
  108. }
  109. return aLen - bLen;
  110. }
  111. /**
  112. * Create a new empty index which is never stored on disk.
  113. *
  114. * @return an empty cache which has no backing store file. The cache may not
  115. * be read or written, but it may be queried and updated (in
  116. * memory).
  117. */
  118. public static DirCache newInCore() {
  119. return new DirCache(null);
  120. }
  121. /**
  122. * Create a new in-core index representation and read an index from disk.
  123. * <p>
  124. * The new index will be read before it is returned to the caller. Read
  125. * failures are reported as exceptions and therefore prevent the method from
  126. * returning a partially populated index.
  127. *
  128. * @param indexLocation
  129. * location of the index file on disk.
  130. * @return a cache representing the contents of the specified index file (if
  131. * it exists) or an empty cache if the file does not exist.
  132. * @throws IOException
  133. * the index file is present but could not be read.
  134. * @throws CorruptObjectException
  135. * the index file is using a format or extension that this
  136. * library does not support.
  137. */
  138. public static DirCache read(final File indexLocation)
  139. throws CorruptObjectException, IOException {
  140. final DirCache c = new DirCache(indexLocation);
  141. c.read();
  142. return c;
  143. }
  144. /**
  145. * Create a new in-core index representation and read an index from disk.
  146. * <p>
  147. * The new index will be read before it is returned to the caller. Read
  148. * failures are reported as exceptions and therefore prevent the method from
  149. * returning a partially populated index.
  150. *
  151. * @param db
  152. * repository the caller wants to read the default index of.
  153. * @return a cache representing the contents of the specified index file (if
  154. * it exists) or an empty cache if the file does not exist.
  155. * @throws IOException
  156. * the index file is present but could not be read.
  157. * @throws CorruptObjectException
  158. * the index file is using a format or extension that this
  159. * library does not support.
  160. */
  161. public static DirCache read(final Repository db)
  162. throws CorruptObjectException, IOException {
  163. return read(new File(db.getDirectory(), "index"));
  164. }
  165. /**
  166. * Create a new in-core index representation, lock it, and read from disk.
  167. * <p>
  168. * The new index will be locked and then read before it is returned to the
  169. * caller. Read failures are reported as exceptions and therefore prevent
  170. * the method from returning a partially populated index. On read failure,
  171. * the lock is released.
  172. *
  173. * @param indexLocation
  174. * location of the index file on disk.
  175. * @return a cache representing the contents of the specified index file (if
  176. * it exists) or an empty cache if the file does not exist.
  177. * @throws IOException
  178. * the index file is present but could not be read, or the lock
  179. * could not be obtained.
  180. * @throws CorruptObjectException
  181. * the index file is using a format or extension that this
  182. * library does not support.
  183. */
  184. public static DirCache lock(final File indexLocation)
  185. throws CorruptObjectException, IOException {
  186. final DirCache c = new DirCache(indexLocation);
  187. if (!c.lock())
  188. throw new IOException("Cannot lock " + indexLocation);
  189. try {
  190. c.read();
  191. } catch (IOException e) {
  192. c.unlock();
  193. throw e;
  194. } catch (RuntimeException e) {
  195. c.unlock();
  196. throw e;
  197. } catch (Error e) {
  198. c.unlock();
  199. throw e;
  200. }
  201. return c;
  202. }
  203. /**
  204. * Create a new in-core index representation, lock it, and read from disk.
  205. * <p>
  206. * The new index will be locked and then read before it is returned to the
  207. * caller. Read failures are reported as exceptions and therefore prevent
  208. * the method from returning a partially populated index.
  209. *
  210. * @param db
  211. * repository the caller wants to read the default index of.
  212. * @return a cache representing the contents of the specified index file (if
  213. * it exists) or an empty cache if the file does not exist.
  214. * @throws IOException
  215. * the index file is present but could not be read, or the lock
  216. * could not be obtained.
  217. * @throws CorruptObjectException
  218. * the index file is using a format or extension that this
  219. * library does not support.
  220. */
  221. public static DirCache lock(final Repository db)
  222. throws CorruptObjectException, IOException {
  223. return lock(new File(db.getDirectory(), "index"));
  224. }
  225. /** Location of the current version of the index file. */
  226. private final File liveFile;
  227. /** Modification time of the file at the last read/write we did. */
  228. private long lastModified;
  229. /** Individual file index entries, sorted by path name. */
  230. private DirCacheEntry[] sortedEntries;
  231. /** Number of positions within {@link #sortedEntries} that are valid. */
  232. private int entryCnt;
  233. /** Cache tree for this index; null if the cache tree is not available. */
  234. private DirCacheTree tree;
  235. /** Our active lock (if we hold it); null if we don't have it locked. */
  236. private LockFile myLock;
  237. /**
  238. * Create a new in-core index representation.
  239. * <p>
  240. * The new index will be empty. Callers may wish to read from the on disk
  241. * file first with {@link #read()}.
  242. *
  243. * @param indexLocation
  244. * location of the index file on disk.
  245. */
  246. public DirCache(final File indexLocation) {
  247. liveFile = indexLocation;
  248. clear();
  249. }
  250. /**
  251. * Create a new builder to update this cache.
  252. * <p>
  253. * Callers should add all entries to the builder, then use
  254. * {@link DirCacheBuilder#finish()} to update this instance.
  255. *
  256. * @return a new builder instance for this cache.
  257. */
  258. public DirCacheBuilder builder() {
  259. return new DirCacheBuilder(this, entryCnt + 16);
  260. }
  261. /**
  262. * Create a new editor to recreate this cache.
  263. * <p>
  264. * Callers should add commands to the editor, then use
  265. * {@link DirCacheEditor#finish()} to update this instance.
  266. *
  267. * @return a new builder instance for this cache.
  268. */
  269. public DirCacheEditor editor() {
  270. return new DirCacheEditor(this, entryCnt + 16);
  271. }
  272. void replace(final DirCacheEntry[] e, final int cnt) {
  273. sortedEntries = e;
  274. entryCnt = cnt;
  275. tree = null;
  276. }
  277. /**
  278. * Read the index from disk, if it has changed on disk.
  279. * <p>
  280. * This method tries to avoid loading the index if it has not changed since
  281. * the last time we consulted it. A missing index file will be treated as
  282. * though it were present but had no file entries in it.
  283. *
  284. * @throws IOException
  285. * the index file is present but could not be read. This
  286. * DirCache instance may not be populated correctly.
  287. * @throws CorruptObjectException
  288. * the index file is using a format or extension that this
  289. * library does not support.
  290. */
  291. public void read() throws IOException, CorruptObjectException {
  292. if (liveFile == null)
  293. throw new IOException("DirCache does not have a backing file");
  294. if (!liveFile.exists())
  295. clear();
  296. else if (liveFile.lastModified() != lastModified) {
  297. try {
  298. final FileInputStream inStream = new FileInputStream(liveFile);
  299. try {
  300. clear();
  301. readFrom(inStream);
  302. } finally {
  303. try {
  304. inStream.close();
  305. } catch (IOException err2) {
  306. // Ignore any close failures.
  307. }
  308. }
  309. } catch (FileNotFoundException fnfe) {
  310. // Someone must have deleted it between our exists test
  311. // and actually opening the path. That's fine, its empty.
  312. //
  313. clear();
  314. }
  315. }
  316. }
  317. /** Empty this index, removing all entries. */
  318. public void clear() {
  319. lastModified = 0;
  320. sortedEntries = NO_ENTRIES;
  321. entryCnt = 0;
  322. tree = null;
  323. }
  324. private void readFrom(final FileInputStream inStream) throws IOException,
  325. CorruptObjectException {
  326. final BufferedInputStream in = new BufferedInputStream(inStream);
  327. final MessageDigest md = Constants.newMessageDigest();
  328. // Read the index header and verify we understand it.
  329. //
  330. final byte[] hdr = new byte[20];
  331. IO.readFully(in, hdr, 0, 12);
  332. md.update(hdr, 0, 12);
  333. if (!is_DIRC(hdr))
  334. throw new CorruptObjectException("Not a DIRC file.");
  335. final int ver = NB.decodeInt32(hdr, 4);
  336. if (ver != 2)
  337. throw new CorruptObjectException("Unknown DIRC version " + ver);
  338. entryCnt = NB.decodeInt32(hdr, 8);
  339. if (entryCnt < 0)
  340. throw new CorruptObjectException("DIRC has too many entries.");
  341. // Load the individual file entries.
  342. //
  343. final byte[] infos = new byte[INFO_LEN * entryCnt];
  344. sortedEntries = new DirCacheEntry[entryCnt];
  345. for (int i = 0; i < entryCnt; i++)
  346. sortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in, md);
  347. lastModified = liveFile.lastModified();
  348. // After the file entries are index extensions, and then a footer.
  349. //
  350. for (;;) {
  351. in.mark(21);
  352. IO.readFully(in, hdr, 0, 20);
  353. if (in.read() < 0) {
  354. // No extensions present; the file ended where we expected.
  355. //
  356. break;
  357. }
  358. in.reset();
  359. md.update(hdr, 0, 8);
  360. IO.skipFully(in, 8);
  361. long sz = NB.decodeUInt32(hdr, 4);
  362. switch (NB.decodeInt32(hdr, 0)) {
  363. case EXT_TREE: {
  364. if (Integer.MAX_VALUE < sz) {
  365. throw new CorruptObjectException("DIRC extension "
  366. + formatExtensionName(hdr) + " is too large at "
  367. + sz + " bytes.");
  368. }
  369. final byte[] raw = new byte[(int) sz];
  370. IO.readFully(in, raw, 0, raw.length);
  371. md.update(raw, 0, raw.length);
  372. tree = new DirCacheTree(raw, new MutableInteger(), null);
  373. break;
  374. }
  375. default:
  376. if (hdr[0] >= 'A' && hdr[0] <= 'Z') {
  377. // The extension is optional and is here only as
  378. // a performance optimization. Since we do not
  379. // understand it, we can safely skip past it, after
  380. // we include its data in our checksum.
  381. //
  382. skipOptionalExtension(in, md, hdr, sz);
  383. } else {
  384. // The extension is not an optimization and is
  385. // _required_ to understand this index format.
  386. // Since we did not trap it above we must abort.
  387. //
  388. throw new CorruptObjectException("DIRC extension "
  389. + formatExtensionName(hdr)
  390. + " not supported by this version.");
  391. }
  392. }
  393. }
  394. final byte[] exp = md.digest();
  395. if (!Arrays.equals(exp, hdr)) {
  396. throw new CorruptObjectException("DIRC checksum mismatch");
  397. }
  398. }
  399. private void skipOptionalExtension(final InputStream in,
  400. final MessageDigest md, final byte[] hdr, long sz)
  401. throws IOException {
  402. final byte[] b = new byte[4096];
  403. while (0 < sz) {
  404. int n = in.read(b, 0, (int) Math.min(b.length, sz));
  405. if (n < 0) {
  406. throw new EOFException("Short read of optional DIRC extension "
  407. + formatExtensionName(hdr) + "; expected another " + sz
  408. + " bytes within the section.");
  409. }
  410. md.update(b, 0, n);
  411. sz -= n;
  412. }
  413. }
  414. private static String formatExtensionName(final byte[] hdr)
  415. throws UnsupportedEncodingException {
  416. return "'" + new String(hdr, 0, 4, "ISO-8859-1") + "'";
  417. }
  418. private static boolean is_DIRC(final byte[] hdr) {
  419. if (hdr.length < SIG_DIRC.length)
  420. return false;
  421. for (int i = 0; i < SIG_DIRC.length; i++)
  422. if (hdr[i] != SIG_DIRC[i])
  423. return false;
  424. return true;
  425. }
  426. /**
  427. * Try to establish an update lock on the cache file.
  428. *
  429. * @return true if the lock is now held by the caller; false if it is held
  430. * by someone else.
  431. * @throws IOException
  432. * the output file could not be created. The caller does not
  433. * hold the lock.
  434. */
  435. public boolean lock() throws IOException {
  436. if (liveFile == null)
  437. throw new IOException("DirCache does not have a backing file");
  438. final LockFile tmp = new LockFile(liveFile);
  439. if (tmp.lock()) {
  440. tmp.setNeedStatInformation(true);
  441. myLock = tmp;
  442. return true;
  443. }
  444. return false;
  445. }
  446. /**
  447. * Write the entry records from memory to disk.
  448. * <p>
  449. * The cache must be locked first by calling {@link #lock()} and receiving
  450. * true as the return value. Applications are encouraged to lock the index,
  451. * then invoke {@link #read()} to ensure the in-memory data is current,
  452. * prior to updating the in-memory entries.
  453. * <p>
  454. * Once written the lock is closed and must be either committed with
  455. * {@link #commit()} or rolled back with {@link #unlock()}.
  456. *
  457. * @throws IOException
  458. * the output file could not be created. The caller no longer
  459. * holds the lock.
  460. */
  461. public void write() throws IOException {
  462. final LockFile tmp = myLock;
  463. requireLocked(tmp);
  464. try {
  465. writeTo(new BufferedOutputStream(tmp.getOutputStream()));
  466. } catch (IOException err) {
  467. tmp.unlock();
  468. throw err;
  469. } catch (RuntimeException err) {
  470. tmp.unlock();
  471. throw err;
  472. } catch (Error err) {
  473. tmp.unlock();
  474. throw err;
  475. }
  476. }
  477. private void writeTo(final OutputStream os) throws IOException {
  478. final MessageDigest foot = Constants.newMessageDigest();
  479. final DigestOutputStream dos = new DigestOutputStream(os, foot);
  480. // Write the header.
  481. //
  482. final byte[] tmp = new byte[128];
  483. System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
  484. NB.encodeInt32(tmp, 4, /* version */2);
  485. NB.encodeInt32(tmp, 8, entryCnt);
  486. dos.write(tmp, 0, 12);
  487. // Write the individual file entries.
  488. //
  489. if (lastModified <= 0) {
  490. // Write a new index, as no entries require smudging.
  491. //
  492. for (int i = 0; i < entryCnt; i++)
  493. sortedEntries[i].write(dos);
  494. } else {
  495. final int smudge_s = (int) (lastModified / 1000);
  496. final int smudge_ns = ((int) (lastModified % 1000)) * 1000000;
  497. for (int i = 0; i < entryCnt; i++) {
  498. final DirCacheEntry e = sortedEntries[i];
  499. if (e.mightBeRacilyClean(smudge_s, smudge_ns))
  500. e.smudgeRacilyClean();
  501. e.write(dos);
  502. }
  503. }
  504. if (tree != null) {
  505. final TemporaryBuffer bb = new TemporaryBuffer.LocalFile();
  506. tree.write(tmp, bb);
  507. bb.close();
  508. NB.encodeInt32(tmp, 0, EXT_TREE);
  509. NB.encodeInt32(tmp, 4, (int) bb.length());
  510. dos.write(tmp, 0, 8);
  511. bb.writeTo(dos, null);
  512. }
  513. os.write(foot.digest());
  514. os.close();
  515. }
  516. /**
  517. * Commit this change and release the lock.
  518. * <p>
  519. * If this method fails (returns false) the lock is still released.
  520. *
  521. * @return true if the commit was successful and the file contains the new
  522. * data; false if the commit failed and the file remains with the
  523. * old data.
  524. * @throws IllegalStateException
  525. * the lock is not held.
  526. */
  527. public boolean commit() {
  528. final LockFile tmp = myLock;
  529. requireLocked(tmp);
  530. myLock = null;
  531. if (!tmp.commit())
  532. return false;
  533. lastModified = tmp.getCommitLastModified();
  534. return true;
  535. }
  536. private void requireLocked(final LockFile tmp) {
  537. if (liveFile == null)
  538. throw new IllegalStateException("DirCache is not locked");
  539. if (tmp == null)
  540. throw new IllegalStateException("DirCache "
  541. + liveFile.getAbsolutePath() + " not locked.");
  542. }
  543. /**
  544. * Unlock this file and abort this change.
  545. * <p>
  546. * The temporary file (if created) is deleted before returning.
  547. */
  548. public void unlock() {
  549. final LockFile tmp = myLock;
  550. if (tmp != null) {
  551. myLock = null;
  552. tmp.unlock();
  553. }
  554. }
  555. /**
  556. * Locate the position a path's entry is at in the index.
  557. * <p>
  558. * If there is at least one entry in the index for this path the position of
  559. * the lowest stage is returned. Subsequent stages can be identified by
  560. * testing consecutive entries until the path differs.
  561. * <p>
  562. * If no path matches the entry -(position+1) is returned, where position is
  563. * the location it would have gone within the index.
  564. *
  565. * @param path
  566. * the path to search for.
  567. * @return if >= 0 then the return value is the position of the entry in the
  568. * index; pass to {@link #getEntry(int)} to obtain the entry
  569. * information. If < 0 the entry does not exist in the index.
  570. */
  571. public int findEntry(final String path) {
  572. final byte[] p = Constants.encode(path);
  573. return findEntry(p, p.length);
  574. }
  575. int findEntry(final byte[] p, final int pLen) {
  576. int low = 0;
  577. int high = entryCnt;
  578. while (low < high) {
  579. int mid = (low + high) >>> 1;
  580. final int cmp = cmp(p, pLen, sortedEntries[mid]);
  581. if (cmp < 0)
  582. high = mid;
  583. else if (cmp == 0) {
  584. while (mid > 0 && cmp(p, pLen, sortedEntries[mid - 1]) == 0)
  585. mid--;
  586. return mid;
  587. } else
  588. low = mid + 1;
  589. }
  590. return -(low + 1);
  591. }
  592. /**
  593. * Determine the next index position past all entries with the same name.
  594. * <p>
  595. * As index entries are sorted by path name, then stage number, this method
  596. * advances the supplied position to the first position in the index whose
  597. * path name does not match the path name of the supplied position's entry.
  598. *
  599. * @param position
  600. * entry position of the path that should be skipped.
  601. * @return position of the next entry whose path is after the input.
  602. */
  603. public int nextEntry(final int position) {
  604. DirCacheEntry last = sortedEntries[position];
  605. int nextIdx = position + 1;
  606. while (nextIdx < entryCnt) {
  607. final DirCacheEntry next = sortedEntries[nextIdx];
  608. if (cmp(last, next) != 0)
  609. break;
  610. last = next;
  611. nextIdx++;
  612. }
  613. return nextIdx;
  614. }
  615. int nextEntry(final byte[] p, final int pLen, int nextIdx) {
  616. while (nextIdx < entryCnt) {
  617. final DirCacheEntry next = sortedEntries[nextIdx];
  618. if (!DirCacheTree.peq(p, next.path, pLen))
  619. break;
  620. nextIdx++;
  621. }
  622. return nextIdx;
  623. }
  624. /**
  625. * Total number of file entries stored in the index.
  626. * <p>
  627. * This count includes unmerged stages for a file entry if the file is
  628. * currently conflicted in a merge. This means the total number of entries
  629. * in the index may be up to 3 times larger than the number of files in the
  630. * working directory.
  631. * <p>
  632. * Note that this value counts only <i>files</i>.
  633. *
  634. * @return number of entries available.
  635. * @see #getEntry(int)
  636. */
  637. public int getEntryCount() {
  638. return entryCnt;
  639. }
  640. /**
  641. * Get a specific entry.
  642. *
  643. * @param i
  644. * position of the entry to get.
  645. * @return the entry at position <code>i</code>.
  646. */
  647. public DirCacheEntry getEntry(final int i) {
  648. return sortedEntries[i];
  649. }
  650. /**
  651. * Get a specific entry.
  652. *
  653. * @param path
  654. * the path to search for.
  655. * @return the entry at position <code>i</code>.
  656. */
  657. public DirCacheEntry getEntry(final String path) {
  658. final int i = findEntry(path);
  659. return i < 0 ? null : sortedEntries[i];
  660. }
  661. /**
  662. * Recursively get all entries within a subtree.
  663. *
  664. * @param path
  665. * the subtree path to get all entries within.
  666. * @return all entries recursively contained within the subtree.
  667. */
  668. public DirCacheEntry[] getEntriesWithin(String path) {
  669. if (!path.endsWith("/"))
  670. path += "/";
  671. final byte[] p = Constants.encode(path);
  672. final int pLen = p.length;
  673. int eIdx = findEntry(p, pLen);
  674. if (eIdx < 0)
  675. eIdx = -(eIdx + 1);
  676. final int lastIdx = nextEntry(p, pLen, eIdx);
  677. final DirCacheEntry[] r = new DirCacheEntry[lastIdx - eIdx];
  678. System.arraycopy(sortedEntries, eIdx, r, 0, r.length);
  679. return r;
  680. }
  681. void toArray(final int i, final DirCacheEntry[] dst, final int off,
  682. final int cnt) {
  683. System.arraycopy(sortedEntries, i, dst, off, cnt);
  684. }
  685. /**
  686. * Obtain (or build) the current cache tree structure.
  687. * <p>
  688. * This method can optionally recreate the cache tree, without flushing the
  689. * tree objects themselves to disk.
  690. *
  691. * @param build
  692. * if true and the cache tree is not present in the index it will
  693. * be generated and returned to the caller.
  694. * @return the cache tree; null if there is no current cache tree available
  695. * and <code>build</code> was false.
  696. */
  697. public DirCacheTree getCacheTree(final boolean build) {
  698. if (build) {
  699. if (tree == null)
  700. tree = new DirCacheTree();
  701. tree.validate(sortedEntries, entryCnt, 0, 0);
  702. }
  703. return tree;
  704. }
  705. /**
  706. * Write all index trees to the object store, returning the root tree.
  707. *
  708. * @param ow
  709. * the writer to use when serializing to the store.
  710. * @return identity for the root tree.
  711. * @throws UnmergedPathException
  712. * one or more paths contain higher-order stages (stage > 0),
  713. * which cannot be stored in a tree object.
  714. * @throws IllegalStateException
  715. * one or more paths contain an invalid mode which should never
  716. * appear in a tree object.
  717. * @throws IOException
  718. * an unexpected error occurred writing to the object store.
  719. */
  720. public ObjectId writeTree(final ObjectWriter ow)
  721. throws UnmergedPathException, IOException {
  722. return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow);
  723. }
  724. /**
  725. * Tells whether this index contains unmerged paths.
  726. *
  727. * @return {@code true} if this index contains unmerged paths. Means: at
  728. * least one entry is of a stage different from 0. {@code false}
  729. * will be returned if all entries are of stage 0.
  730. */
  731. public boolean hasUnmergedPaths() {
  732. for (int i = 0; i < entryCnt; i++) {
  733. if (sortedEntries[i].getStage() > 0) {
  734. return true;
  735. }
  736. }
  737. return false;
  738. }
  739. }