Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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