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.

GitIndex.java 27KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2007, Robin Rosenberg <me@lathund.dewire.com>
  4. * Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
  6. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  7. * and other copyright owners as documented in the project's IP log.
  8. *
  9. * This program and the accompanying materials are made available
  10. * under the terms of the Eclipse Distribution License v1.0 which
  11. * accompanies this distribution, is reproduced below, and is
  12. * available at http://www.eclipse.org/org/documents/edl-v10.php
  13. *
  14. * All rights reserved.
  15. *
  16. * Redistribution and use in source and binary forms, with or
  17. * without modification, are permitted provided that the following
  18. * conditions are met:
  19. *
  20. * - Redistributions of source code must retain the above copyright
  21. * notice, this list of conditions and the following disclaimer.
  22. *
  23. * - Redistributions in binary form must reproduce the above
  24. * copyright notice, this list of conditions and the following
  25. * disclaimer in the documentation and/or other materials provided
  26. * with the distribution.
  27. *
  28. * - Neither the name of the Eclipse Foundation, Inc. nor the
  29. * names of its contributors may be used to endorse or promote
  30. * products derived from this software without specific prior
  31. * written permission.
  32. *
  33. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  34. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  36. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  37. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  38. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  39. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  40. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  41. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  42. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  43. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  44. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  45. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  46. */
  47. package org.eclipse.jgit.lib;
  48. import java.io.File;
  49. import java.io.FileInputStream;
  50. import java.io.FileNotFoundException;
  51. import java.io.FileOutputStream;
  52. import java.io.IOException;
  53. import java.io.InputStream;
  54. import java.io.RandomAccessFile;
  55. import java.io.UnsupportedEncodingException;
  56. import java.nio.ByteBuffer;
  57. import java.nio.ByteOrder;
  58. import java.nio.channels.FileChannel;
  59. import java.security.MessageDigest;
  60. import java.text.MessageFormat;
  61. import java.util.Comparator;
  62. import java.util.Date;
  63. import java.util.Iterator;
  64. import java.util.Map;
  65. import java.util.Stack;
  66. import java.util.TreeMap;
  67. import org.eclipse.jgit.JGitText;
  68. import org.eclipse.jgit.dircache.DirCache;
  69. import org.eclipse.jgit.errors.CorruptObjectException;
  70. import org.eclipse.jgit.errors.NotSupportedException;
  71. import org.eclipse.jgit.events.IndexChangedEvent;
  72. import org.eclipse.jgit.util.RawParseUtils;
  73. /**
  74. * A representation of the Git index.
  75. *
  76. * The index points to the objects currently checked out or in the process of
  77. * being prepared for committing or objects involved in an unfinished merge.
  78. *
  79. * The abstract format is:<br/> path stage flags statdata SHA-1
  80. * <ul>
  81. * <li>Path is the relative path in the workdir</li>
  82. * <li>stage is 0 (normally), but when
  83. * merging 1 is the common ancestor version, 2 is 'our' version and 3 is 'their'
  84. * version. A fully resolved merge only contains stage 0.</li>
  85. * <li>flags is the object type and information of validity</li>
  86. * <li>statdata is the size of this object and some other file system specifics,
  87. * some of it ignored by JGit</li>
  88. * <li>SHA-1 represents the content of the references object</li>
  89. * </ul>
  90. *
  91. * An index can also contain a tree cache which we ignore for now. We drop the
  92. * tree cache when writing the index.
  93. *
  94. * @deprecated Use {@link DirCache} instead.
  95. */
  96. public class GitIndex {
  97. /** Stage 0 represents merged entries. */
  98. public static final int STAGE_0 = 0;
  99. private RandomAccessFile cache;
  100. private File cacheFile;
  101. // Index is modified
  102. private boolean changed;
  103. // Stat information updated
  104. private boolean statDirty;
  105. private Header header;
  106. private long lastCacheTime;
  107. private final Repository db;
  108. private Map<byte[], Entry> entries = new TreeMap<byte[], Entry>(new Comparator<byte[]>() {
  109. public int compare(byte[] o1, byte[] o2) {
  110. for (int i = 0; i < o1.length && i < o2.length; ++i) {
  111. int c = (o1[i] & 0xff) - (o2[i] & 0xff);
  112. if (c != 0)
  113. return c;
  114. }
  115. if (o1.length < o2.length)
  116. return -1;
  117. else if (o1.length > o2.length)
  118. return 1;
  119. return 0;
  120. }
  121. });
  122. /**
  123. * Construct a Git index representation.
  124. * @param db
  125. */
  126. public GitIndex(Repository db) {
  127. this.db = db;
  128. this.cacheFile = db.getIndexFile();
  129. }
  130. /**
  131. * @return true if we have modified the index in memory since reading it from disk
  132. */
  133. public boolean isChanged() {
  134. return changed || statDirty;
  135. }
  136. /**
  137. * Reread index data from disk if the index file has been changed
  138. * @throws IOException
  139. */
  140. public void rereadIfNecessary() throws IOException {
  141. if (cacheFile.exists() && cacheFile.lastModified() != lastCacheTime) {
  142. read();
  143. db.fireEvent(new IndexChangedEvent());
  144. }
  145. }
  146. /**
  147. * Add the content of a file to the index.
  148. *
  149. * @param wd workdir
  150. * @param f the file
  151. * @return a new or updated index entry for the path represented by f
  152. * @throws IOException
  153. */
  154. public Entry add(File wd, File f) throws IOException {
  155. byte[] key = makeKey(wd, f);
  156. Entry e = entries.get(key);
  157. if (e == null) {
  158. e = new Entry(key, f, 0);
  159. entries.put(key, e);
  160. } else {
  161. e.update(f);
  162. }
  163. return e;
  164. }
  165. /**
  166. * Add the content of a file to the index.
  167. *
  168. * @param wd
  169. * workdir
  170. * @param f
  171. * the file
  172. * @param content
  173. * content of the file
  174. * @return a new or updated index entry for the path represented by f
  175. * @throws IOException
  176. */
  177. public Entry add(File wd, File f, byte[] content) throws IOException {
  178. byte[] key = makeKey(wd, f);
  179. Entry e = entries.get(key);
  180. if (e == null) {
  181. e = new Entry(key, f, 0, content);
  182. entries.put(key, e);
  183. } else {
  184. e.update(f, content);
  185. }
  186. return e;
  187. }
  188. /**
  189. * Remove a path from the index.
  190. *
  191. * @param wd
  192. * workdir
  193. * @param f
  194. * the file whose path shall be removed.
  195. * @return true if such a path was found (and thus removed)
  196. * @throws IOException
  197. */
  198. public boolean remove(File wd, File f) throws IOException {
  199. byte[] key = makeKey(wd, f);
  200. return entries.remove(key) != null;
  201. }
  202. /**
  203. * Read the cache file into memory.
  204. *
  205. * @throws IOException
  206. */
  207. public void read() throws IOException {
  208. changed = false;
  209. statDirty = false;
  210. if (!cacheFile.exists()) {
  211. header = null;
  212. entries.clear();
  213. lastCacheTime = 0;
  214. return;
  215. }
  216. cache = new RandomAccessFile(cacheFile, "r");
  217. try {
  218. FileChannel channel = cache.getChannel();
  219. ByteBuffer buffer = ByteBuffer.allocateDirect((int) cacheFile.length());
  220. buffer.order(ByteOrder.BIG_ENDIAN);
  221. int j = channel.read(buffer);
  222. if (j != buffer.capacity())
  223. throw new IOException(MessageFormat.format(JGitText.get().couldNotReadIndexInOneGo
  224. , j, buffer.capacity()));
  225. buffer.flip();
  226. header = new Header(buffer);
  227. entries.clear();
  228. for (int i = 0; i < header.entries; ++i) {
  229. Entry entry = new Entry(buffer);
  230. final GitIndex.Entry existing = entries.get(entry.name);
  231. entries.put(entry.name, entry);
  232. if (existing != null) {
  233. entry.stages |= existing.stages;
  234. }
  235. }
  236. lastCacheTime = cacheFile.lastModified();
  237. } finally {
  238. cache.close();
  239. }
  240. }
  241. /**
  242. * Write content of index to disk.
  243. *
  244. * @throws IOException
  245. */
  246. public void write() throws IOException {
  247. checkWriteOk();
  248. File tmpIndex = new File(cacheFile.getAbsoluteFile() + ".tmp");
  249. File lock = new File(cacheFile.getAbsoluteFile() + ".lock");
  250. if (!lock.createNewFile())
  251. throw new IOException(JGitText.get().indexFileIsInUse);
  252. try {
  253. FileOutputStream fileOutputStream = new FileOutputStream(tmpIndex);
  254. FileChannel fc = fileOutputStream.getChannel();
  255. ByteBuffer buf = ByteBuffer.allocate(4096);
  256. MessageDigest newMessageDigest = Constants.newMessageDigest();
  257. header = new Header(entries);
  258. header.write(buf);
  259. buf.flip();
  260. newMessageDigest
  261. .update(buf.array(), buf.arrayOffset(), buf.limit());
  262. fc.write(buf);
  263. buf.flip();
  264. buf.clear();
  265. for (Iterator i = entries.values().iterator(); i.hasNext();) {
  266. Entry e = (Entry) i.next();
  267. e.write(buf);
  268. buf.flip();
  269. newMessageDigest.update(buf.array(), buf.arrayOffset(), buf
  270. .limit());
  271. fc.write(buf);
  272. buf.flip();
  273. buf.clear();
  274. }
  275. buf.put(newMessageDigest.digest());
  276. buf.flip();
  277. fc.write(buf);
  278. fc.close();
  279. fileOutputStream.close();
  280. if (cacheFile.exists())
  281. if (!cacheFile.delete())
  282. throw new IOException(
  283. JGitText.get().couldNotRenameDeleteOldIndex);
  284. if (!tmpIndex.renameTo(cacheFile))
  285. throw new IOException(
  286. JGitText.get().couldNotRenameTemporaryIndexFileToIndex);
  287. changed = false;
  288. statDirty = false;
  289. lastCacheTime = cacheFile.lastModified();
  290. db.fireEvent(new IndexChangedEvent());
  291. } finally {
  292. if (!lock.delete())
  293. throw new IOException(
  294. JGitText.get().couldNotDeleteLockFileShouldNotHappen);
  295. if (tmpIndex.exists() && !tmpIndex.delete())
  296. throw new IOException(
  297. JGitText.get().couldNotDeleteTemporaryIndexFileShouldNotHappen);
  298. }
  299. }
  300. private void checkWriteOk() throws IOException {
  301. for (Iterator i = entries.values().iterator(); i.hasNext();) {
  302. Entry e = (Entry) i.next();
  303. if (e.getStage() != 0) {
  304. throw new NotSupportedException(JGitText.get().cannotWorkWithOtherStagesThanZeroRightNow);
  305. }
  306. }
  307. }
  308. private boolean File_canExecute(File f){
  309. return db.getFS().canExecute(f);
  310. }
  311. private boolean File_setExecute(File f, boolean value) {
  312. return db.getFS().setExecute(f, value);
  313. }
  314. private boolean File_hasExecute() {
  315. return db.getFS().supportsExecute();
  316. }
  317. static byte[] makeKey(File wd, File f) {
  318. if (!f.getPath().startsWith(wd.getPath()))
  319. throw new Error(JGitText.get().pathIsNotInWorkingDir);
  320. String relName = Repository.stripWorkDir(wd, f);
  321. return Constants.encode(relName);
  322. }
  323. Boolean filemode;
  324. private boolean config_filemode() {
  325. // temporary til we can actually set parameters. We need to be able
  326. // to change this for testing.
  327. if (filemode != null)
  328. return filemode.booleanValue();
  329. Config config = db.getConfig();
  330. filemode = Boolean.valueOf(config.getBoolean("core", null, "filemode", true));
  331. return filemode.booleanValue();
  332. }
  333. /** An index entry */
  334. public class Entry {
  335. private long ctime;
  336. private long mtime;
  337. private int dev;
  338. private int ino;
  339. private int mode;
  340. private int uid;
  341. private int gid;
  342. private int size;
  343. private ObjectId sha1;
  344. private short flags;
  345. private byte[] name;
  346. private int stages;
  347. Entry(byte[] key, File f, int stage)
  348. throws IOException {
  349. ctime = f.lastModified() * 1000000L;
  350. mtime = ctime; // we use same here
  351. dev = -1;
  352. ino = -1;
  353. if (config_filemode() && File_canExecute(f))
  354. mode = FileMode.EXECUTABLE_FILE.getBits();
  355. else
  356. mode = FileMode.REGULAR_FILE.getBits();
  357. uid = -1;
  358. gid = -1;
  359. size = (int) f.length();
  360. ObjectWriter writer = new ObjectWriter(db);
  361. sha1 = writer.writeBlob(f);
  362. name = key;
  363. flags = (short) ((stage << 12) | name.length); // TODO: fix flags
  364. stages = (1 >> getStage());
  365. }
  366. Entry(byte[] key, File f, int stage, byte[] newContent)
  367. throws IOException {
  368. ctime = f.lastModified() * 1000000L;
  369. mtime = ctime; // we use same here
  370. dev = -1;
  371. ino = -1;
  372. if (config_filemode() && File_canExecute(f))
  373. mode = FileMode.EXECUTABLE_FILE.getBits();
  374. else
  375. mode = FileMode.REGULAR_FILE.getBits();
  376. uid = -1;
  377. gid = -1;
  378. size = newContent.length;
  379. ObjectWriter writer = new ObjectWriter(db);
  380. sha1 = writer.writeBlob(newContent);
  381. name = key;
  382. flags = (short) ((stage << 12) | name.length); // TODO: fix flags
  383. stages = (1 >> getStage());
  384. }
  385. Entry(TreeEntry f, int stage) {
  386. ctime = -1; // hmm
  387. mtime = -1;
  388. dev = -1;
  389. ino = -1;
  390. mode = f.getMode().getBits();
  391. uid = -1;
  392. gid = -1;
  393. try {
  394. size = (int) db.open(f.getId(), Constants.OBJ_BLOB).getSize();
  395. } catch (IOException e) {
  396. e.printStackTrace();
  397. size = -1;
  398. }
  399. sha1 = f.getId();
  400. name = Constants.encode(f.getFullName());
  401. flags = (short) ((stage << 12) | name.length); // TODO: fix flags
  402. stages = (1 >> getStage());
  403. }
  404. Entry(ByteBuffer b) {
  405. int startposition = b.position();
  406. ctime = b.getInt() * 1000000000L + (b.getInt() % 1000000000L);
  407. mtime = b.getInt() * 1000000000L + (b.getInt() % 1000000000L);
  408. dev = b.getInt();
  409. ino = b.getInt();
  410. mode = b.getInt();
  411. uid = b.getInt();
  412. gid = b.getInt();
  413. size = b.getInt();
  414. byte[] sha1bytes = new byte[Constants.OBJECT_ID_LENGTH];
  415. b.get(sha1bytes);
  416. sha1 = ObjectId.fromRaw(sha1bytes);
  417. flags = b.getShort();
  418. stages = (1 << getStage());
  419. name = new byte[flags & 0xFFF];
  420. b.get(name);
  421. b
  422. .position(startposition
  423. + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2
  424. + name.length + 8) & ~7));
  425. }
  426. /**
  427. * Update this index entry with stat and SHA-1 information if it looks
  428. * like the file has been modified in the workdir.
  429. *
  430. * @param f
  431. * file in work dir
  432. * @return true if a change occurred
  433. * @throws IOException
  434. */
  435. public boolean update(File f) throws IOException {
  436. long lm = f.lastModified() * 1000000L;
  437. boolean modified = mtime != lm;
  438. mtime = lm;
  439. if (size != f.length())
  440. modified = true;
  441. if (config_filemode()) {
  442. if (File_canExecute(f) != FileMode.EXECUTABLE_FILE.equals(mode)) {
  443. mode = FileMode.EXECUTABLE_FILE.getBits();
  444. modified = true;
  445. }
  446. }
  447. if (modified) {
  448. size = (int) f.length();
  449. ObjectWriter writer = new ObjectWriter(db);
  450. ObjectId newsha1 = writer.writeBlob(f);
  451. if (!newsha1.equals(sha1))
  452. modified = true;
  453. sha1 = newsha1;
  454. }
  455. return modified;
  456. }
  457. /**
  458. * Update this index entry with stat and SHA-1 information if it looks
  459. * like the file has been modified in the workdir.
  460. *
  461. * @param f
  462. * file in work dir
  463. * @param newContent
  464. * the new content of the file
  465. * @return true if a change occurred
  466. * @throws IOException
  467. */
  468. public boolean update(File f, byte[] newContent) throws IOException {
  469. boolean modified = false;
  470. size = newContent.length;
  471. ObjectWriter writer = new ObjectWriter(db);
  472. ObjectId newsha1 = writer.writeBlob(newContent);
  473. if (!newsha1.equals(sha1))
  474. modified = true;
  475. sha1 = newsha1;
  476. return modified;
  477. }
  478. void write(ByteBuffer buf) {
  479. int startposition = buf.position();
  480. buf.putInt((int) (ctime / 1000000000L));
  481. buf.putInt((int) (ctime % 1000000000L));
  482. buf.putInt((int) (mtime / 1000000000L));
  483. buf.putInt((int) (mtime % 1000000000L));
  484. buf.putInt(dev);
  485. buf.putInt(ino);
  486. buf.putInt(mode);
  487. buf.putInt(uid);
  488. buf.putInt(gid);
  489. buf.putInt(size);
  490. sha1.copyRawTo(buf);
  491. buf.putShort(flags);
  492. buf.put(name);
  493. int end = startposition
  494. + ((8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 20 + 2 + name.length + 8) & ~7);
  495. int remain = end - buf.position();
  496. while (remain-- > 0)
  497. buf.put((byte) 0);
  498. }
  499. /**
  500. * Check if an entry's content is different from the cache,
  501. *
  502. * File status information is used and status is same we
  503. * consider the file identical to the state in the working
  504. * directory. Native git uses more stat fields than we
  505. * have accessible in Java.
  506. *
  507. * @param wd working directory to compare content with
  508. * @return true if content is most likely different.
  509. */
  510. public boolean isModified(File wd) {
  511. return isModified(wd, false);
  512. }
  513. /**
  514. * Check if an entry's content is different from the cache,
  515. *
  516. * File status information is used and status is same we
  517. * consider the file identical to the state in the working
  518. * directory. Native git uses more stat fields than we
  519. * have accessible in Java.
  520. *
  521. * @param wd working directory to compare content with
  522. * @param forceContentCheck True if the actual file content
  523. * should be checked if modification time differs.
  524. *
  525. * @return true if content is most likely different.
  526. */
  527. public boolean isModified(File wd, boolean forceContentCheck) {
  528. if (isAssumedValid())
  529. return false;
  530. if (isUpdateNeeded())
  531. return true;
  532. File file = getFile(wd);
  533. long length = file.length();
  534. if (length == 0) {
  535. if (!file.exists())
  536. return true;
  537. }
  538. if (length != size)
  539. return true;
  540. // JDK1.6 has file.canExecute
  541. // if (file.canExecute() != FileMode.EXECUTABLE_FILE.equals(mode))
  542. // return true;
  543. final int exebits = FileMode.EXECUTABLE_FILE.getBits()
  544. ^ FileMode.REGULAR_FILE.getBits();
  545. if (config_filemode() && FileMode.EXECUTABLE_FILE.equals(mode)) {
  546. if (!File_canExecute(file)&& File_hasExecute())
  547. return true;
  548. } else {
  549. if (FileMode.REGULAR_FILE.equals(mode&~exebits)) {
  550. if (!file.isFile())
  551. return true;
  552. if (config_filemode() && File_canExecute(file) && File_hasExecute())
  553. return true;
  554. } else {
  555. if (FileMode.SYMLINK.equals(mode)) {
  556. return true;
  557. } else {
  558. if (FileMode.TREE.equals(mode)) {
  559. if (!file.isDirectory())
  560. return true;
  561. } else {
  562. System.out.println(MessageFormat.format(JGitText.get().doesNotHandleMode
  563. , mode, file));
  564. return true;
  565. }
  566. }
  567. }
  568. }
  569. // Git under windows only stores seconds so we round the timestamp
  570. // Java gives us if it looks like the timestamp in index is seconds
  571. // only. Otherwise we compare the timestamp at millisecond prevision.
  572. long javamtime = mtime / 1000000L;
  573. long lastm = file.lastModified();
  574. if (javamtime % 1000 == 0)
  575. lastm = lastm - lastm % 1000;
  576. if (lastm != javamtime) {
  577. if (!forceContentCheck)
  578. return true;
  579. try {
  580. InputStream is = new FileInputStream(file);
  581. try {
  582. ObjectWriter objectWriter = new ObjectWriter(db);
  583. ObjectId newId = objectWriter.computeBlobSha1(file
  584. .length(), is);
  585. boolean ret = !newId.equals(sha1);
  586. return ret;
  587. } catch (IOException e) {
  588. e.printStackTrace();
  589. } finally {
  590. try {
  591. is.close();
  592. } catch (IOException e) {
  593. // can't happen, but if it does we ignore it
  594. e.printStackTrace();
  595. }
  596. }
  597. } catch (FileNotFoundException e) {
  598. // should not happen because we already checked this
  599. e.printStackTrace();
  600. throw new Error(e);
  601. }
  602. }
  603. return false;
  604. }
  605. /**
  606. * Returns the stages in which the entry's file is recorded in the index.
  607. * The stages are bit-encoded: bit N is set if the file is present
  608. * in stage N. In particular, the N-th bit will be set if this entry
  609. * itself is in stage N (see getStage()).
  610. *
  611. * @return flags denoting stages
  612. * @see #getStage()
  613. */
  614. public int getStages() {
  615. return stages;
  616. }
  617. // for testing
  618. void forceRecheck() {
  619. mtime = -1;
  620. }
  621. private File getFile(File wd) {
  622. return new File(wd, getName());
  623. }
  624. public String toString() {
  625. return getName() + "/SHA-1(" + sha1.name() + ")/M:"
  626. + new Date(ctime / 1000000L) + "/C:"
  627. + new Date(mtime / 1000000L) + "/d" + dev + "/i" + ino
  628. + "/m" + Integer.toString(mode, 8) + "/u" + uid + "/g"
  629. + gid + "/s" + size + "/f" + flags + "/@" + getStage();
  630. }
  631. /**
  632. * @return path name for this entry
  633. */
  634. public String getName() {
  635. return RawParseUtils.decode(name);
  636. }
  637. /**
  638. * @return path name for this entry as byte array, hopefully UTF-8 encoded
  639. */
  640. public byte[] getNameUTF8() {
  641. return name;
  642. }
  643. /**
  644. * @return SHA-1 of the entry managed by this index
  645. */
  646. public ObjectId getObjectId() {
  647. return sha1;
  648. }
  649. /**
  650. * @return the stage this entry is in
  651. */
  652. public int getStage() {
  653. return (flags & 0x3000) >> 12;
  654. }
  655. /**
  656. * @return size of disk object
  657. */
  658. public int getSize() {
  659. return size;
  660. }
  661. /**
  662. * @return true if this entry shall be assumed valid
  663. */
  664. public boolean isAssumedValid() {
  665. return (flags & 0x8000) != 0;
  666. }
  667. /**
  668. * @return true if this entry should be checked for changes
  669. */
  670. public boolean isUpdateNeeded() {
  671. return (flags & 0x4000) != 0;
  672. }
  673. /**
  674. * Set whether to always assume this entry valid
  675. *
  676. * @param assumeValid true to ignore changes
  677. */
  678. public void setAssumeValid(boolean assumeValid) {
  679. if (assumeValid)
  680. flags |= 0x8000;
  681. else
  682. flags &= ~0x8000;
  683. }
  684. /**
  685. * Set whether this entry must be checked
  686. *
  687. * @param updateNeeded
  688. */
  689. public void setUpdateNeeded(boolean updateNeeded) {
  690. if (updateNeeded)
  691. flags |= 0x4000;
  692. else
  693. flags &= ~0x4000;
  694. }
  695. /**
  696. * Return raw file mode bits. See {@link FileMode}
  697. * @return file mode bits
  698. */
  699. public int getModeBits() {
  700. return mode;
  701. }
  702. }
  703. static class Header {
  704. private int signature;
  705. private int version;
  706. int entries;
  707. Header(ByteBuffer map) throws CorruptObjectException {
  708. read(map);
  709. }
  710. private void read(ByteBuffer buf) throws CorruptObjectException {
  711. signature = buf.getInt();
  712. version = buf.getInt();
  713. entries = buf.getInt();
  714. if (signature != 0x44495243)
  715. throw new CorruptObjectException(MessageFormat.format(
  716. JGitText.get().indexSignatureIsInvalid, signature));
  717. if (version != 2)
  718. throw new CorruptObjectException(MessageFormat.format(
  719. JGitText.get().unknownIndexVersionOrCorruptIndex, version));
  720. }
  721. void write(ByteBuffer buf) {
  722. buf.order(ByteOrder.BIG_ENDIAN);
  723. buf.putInt(signature);
  724. buf.putInt(version);
  725. buf.putInt(entries);
  726. }
  727. Header(Map entryset) {
  728. signature = 0x44495243;
  729. version = 2;
  730. entries = entryset.size();
  731. }
  732. }
  733. /**
  734. * Read a Tree recursively into the index
  735. *
  736. * @param t The tree to read
  737. *
  738. * @throws IOException
  739. */
  740. public void readTree(Tree t) throws IOException {
  741. entries.clear();
  742. readTree("", t);
  743. }
  744. void readTree(String prefix, Tree t) throws IOException {
  745. TreeEntry[] members = t.members();
  746. for (int i = 0; i < members.length; ++i) {
  747. TreeEntry te = members[i];
  748. String name;
  749. if (prefix.length() > 0)
  750. name = prefix + "/" + te.getName();
  751. else
  752. name = te.getName();
  753. if (te instanceof Tree) {
  754. readTree(name, (Tree) te);
  755. } else {
  756. Entry e = new Entry(te, 0);
  757. entries.put(Constants.encode(name), e);
  758. }
  759. }
  760. }
  761. /**
  762. * Add tree entry to index
  763. * @param te tree entry
  764. * @return new or modified index entry
  765. * @throws IOException
  766. */
  767. public Entry addEntry(TreeEntry te) throws IOException {
  768. byte[] key = Constants.encode(te.getFullName());
  769. Entry e = new Entry(te, 0);
  770. entries.put(key, e);
  771. return e;
  772. }
  773. /**
  774. * Check out content of the content represented by the index
  775. *
  776. * @param wd
  777. * workdir
  778. * @throws IOException
  779. */
  780. public void checkout(File wd) throws IOException {
  781. for (Entry e : entries.values()) {
  782. if (e.getStage() != 0)
  783. continue;
  784. checkoutEntry(wd, e);
  785. }
  786. }
  787. /**
  788. * Check out content of the specified index entry
  789. *
  790. * @param wd workdir
  791. * @param e index entry
  792. * @throws IOException
  793. */
  794. public void checkoutEntry(File wd, Entry e) throws IOException {
  795. ObjectLoader ol = db.open(e.sha1, Constants.OBJ_BLOB);
  796. byte[] bytes = ol.getBytes();
  797. File file = new File(wd, e.getName());
  798. file.delete();
  799. file.getParentFile().mkdirs();
  800. FileChannel channel = new FileOutputStream(file).getChannel();
  801. ByteBuffer buffer = ByteBuffer.wrap(bytes);
  802. int j = channel.write(buffer);
  803. if (j != bytes.length)
  804. throw new IOException(MessageFormat.format(JGitText.get().couldNotWriteFile, file));
  805. channel.close();
  806. if (config_filemode() && File_hasExecute()) {
  807. if (FileMode.EXECUTABLE_FILE.equals(e.mode)) {
  808. if (!File_canExecute(file))
  809. File_setExecute(file, true);
  810. } else {
  811. if (File_canExecute(file))
  812. File_setExecute(file, false);
  813. }
  814. }
  815. e.mtime = file.lastModified() * 1000000L;
  816. e.ctime = e.mtime;
  817. }
  818. /**
  819. * Construct and write tree out of index.
  820. *
  821. * @return SHA-1 of the constructed tree
  822. *
  823. * @throws IOException
  824. */
  825. public ObjectId writeTree() throws IOException {
  826. checkWriteOk();
  827. ObjectWriter writer = new ObjectWriter(db);
  828. Tree current = new Tree(db);
  829. Stack<Tree> trees = new Stack<Tree>();
  830. trees.push(current);
  831. String[] prevName = new String[0];
  832. for (Entry e : entries.values()) {
  833. if (e.getStage() != 0)
  834. continue;
  835. String[] newName = splitDirPath(e.getName());
  836. int c = longestCommonPath(prevName, newName);
  837. while (c < trees.size() - 1) {
  838. current.setId(writer.writeTree(current));
  839. trees.pop();
  840. current = trees.isEmpty() ? null : (Tree) trees.peek();
  841. }
  842. while (trees.size() < newName.length) {
  843. if (!current.existsTree(newName[trees.size() - 1])) {
  844. current = new Tree(current, Constants.encode(newName[trees.size() - 1]));
  845. current.getParent().addEntry(current);
  846. trees.push(current);
  847. } else {
  848. current = (Tree) current.findTreeMember(newName[trees
  849. .size() - 1]);
  850. trees.push(current);
  851. }
  852. }
  853. FileTreeEntry ne = new FileTreeEntry(current, e.sha1,
  854. Constants.encode(newName[newName.length - 1]),
  855. (e.mode & FileMode.EXECUTABLE_FILE.getBits()) == FileMode.EXECUTABLE_FILE.getBits());
  856. current.addEntry(ne);
  857. }
  858. while (!trees.isEmpty()) {
  859. current.setId(writer.writeTree(current));
  860. trees.pop();
  861. if (!trees.isEmpty())
  862. current = trees.peek();
  863. }
  864. return current.getTreeId();
  865. }
  866. String[] splitDirPath(String name) {
  867. String[] tmp = new String[name.length() / 2 + 1];
  868. int p0 = -1;
  869. int p1;
  870. int c = 0;
  871. while ((p1 = name.indexOf('/', p0 + 1)) != -1) {
  872. tmp[c++] = name.substring(p0 + 1, p1);
  873. p0 = p1;
  874. }
  875. tmp[c++] = name.substring(p0 + 1);
  876. String[] ret = new String[c];
  877. for (int i = 0; i < c; ++i) {
  878. ret[i] = tmp[i];
  879. }
  880. return ret;
  881. }
  882. int longestCommonPath(String[] a, String[] b) {
  883. int i;
  884. for (i = 0; i < a.length && i < b.length; ++i)
  885. if (!a[i].equals(b[i]))
  886. return i;
  887. return i;
  888. }
  889. /**
  890. * Return the members of the index sorted by the unsigned byte
  891. * values of the path names.
  892. *
  893. * Small beware: Unaccounted for are unmerged entries. You may want
  894. * to abort if members with stage != 0 are found if you are doing
  895. * any updating operations. All stages will be found after one another
  896. * here later. Currently only one stage per name is returned.
  897. *
  898. * @return The index entries sorted
  899. */
  900. public Entry[] getMembers() {
  901. return entries.values().toArray(new Entry[entries.size()]);
  902. }
  903. /**
  904. * Look up an entry with the specified path.
  905. *
  906. * @param path
  907. * @return index entry for the path or null if not in index.
  908. * @throws UnsupportedEncodingException
  909. */
  910. public Entry getEntry(String path) throws UnsupportedEncodingException {
  911. return entries.get(Repository.gitInternalSlash(Constants.encode(path)));
  912. }
  913. /**
  914. * @return The repository holding this index.
  915. */
  916. public Repository getRepository() {
  917. return db;
  918. }
  919. }