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.

Repository.java 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. /*
  2. * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3. * Copyright (C) 2008-2010, Google Inc.
  4. * Copyright (C) 2006-2010, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
  6. * and other copyright owners as documented in the project's IP log.
  7. *
  8. * This program and the accompanying materials are made available
  9. * under the terms of the Eclipse Distribution License v1.0 which
  10. * accompanies this distribution, is reproduced below, and is
  11. * available at http://www.eclipse.org/org/documents/edl-v10.php
  12. *
  13. * All rights reserved.
  14. *
  15. * Redistribution and use in source and binary forms, with or
  16. * without modification, are permitted provided that the following
  17. * conditions are met:
  18. *
  19. * - Redistributions of source code must retain the above copyright
  20. * notice, this list of conditions and the following disclaimer.
  21. *
  22. * - Redistributions in binary form must reproduce the above
  23. * copyright notice, this list of conditions and the following
  24. * disclaimer in the documentation and/or other materials provided
  25. * with the distribution.
  26. *
  27. * - Neither the name of the Eclipse Foundation, Inc. nor the
  28. * names of its contributors may be used to endorse or promote
  29. * products derived from this software without specific prior
  30. * written permission.
  31. *
  32. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  33. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  34. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  35. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  36. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  37. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  38. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  39. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  40. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  41. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  42. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  43. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  44. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  45. */
  46. package org.eclipse.jgit.lib;
  47. import java.io.File;
  48. import java.io.IOException;
  49. import java.util.ArrayList;
  50. import java.util.Collection;
  51. import java.util.Collections;
  52. import java.util.HashMap;
  53. import java.util.HashSet;
  54. import java.util.LinkedList;
  55. import java.util.List;
  56. import java.util.Map;
  57. import java.util.Set;
  58. import java.util.Vector;
  59. import java.util.concurrent.atomic.AtomicInteger;
  60. import org.eclipse.jgit.errors.ConfigInvalidException;
  61. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  62. import org.eclipse.jgit.errors.RevisionSyntaxException;
  63. import org.eclipse.jgit.util.FS;
  64. import org.eclipse.jgit.util.SystemReader;
  65. /**
  66. * Represents a Git repository. A repository holds all objects and refs used for
  67. * managing source code (could by any type of file, but source code is what
  68. * SCM's are typically used for).
  69. *
  70. * In Git terms all data is stored in GIT_DIR, typically a directory called
  71. * .git. A work tree is maintained unless the repository is a bare repository.
  72. * Typically the .git directory is located at the root of the work dir.
  73. *
  74. * <ul>
  75. * <li>GIT_DIR
  76. * <ul>
  77. * <li>objects/ - objects</li>
  78. * <li>refs/ - tags and heads</li>
  79. * <li>config - configuration</li>
  80. * <li>info/ - more configurations</li>
  81. * </ul>
  82. * </li>
  83. * </ul>
  84. * <p>
  85. * This class is thread-safe.
  86. * <p>
  87. * This implementation only handles a subtly undocumented subset of git features.
  88. *
  89. */
  90. public class Repository {
  91. private final AtomicInteger useCnt = new AtomicInteger(1);
  92. private final File gitDir;
  93. private final RepositoryConfig config;
  94. private final RefDatabase refs;
  95. private final ObjectDirectory objectDatabase;
  96. private GitIndex index;
  97. private final List<RepositoryListener> listeners = new Vector<RepositoryListener>(); // thread safe
  98. static private final List<RepositoryListener> allListeners = new Vector<RepositoryListener>(); // thread safe
  99. private File workDir;
  100. private File indexFile;
  101. /**
  102. * Construct a representation of a Git repository.
  103. *
  104. * The work tree, object directory, alternate object directories and index
  105. * file locations are deduced from the given git directory and the default
  106. * rules.
  107. *
  108. * @param d
  109. * GIT_DIR (the location of the repository metadata).
  110. * @throws IOException
  111. * the repository appears to already exist but cannot be
  112. * accessed.
  113. */
  114. public Repository(final File d) throws IOException {
  115. this(d, null, null, null, null); // go figure it out
  116. }
  117. /**
  118. * Construct a representation of a Git repository.
  119. *
  120. * The work tree, object directory, alternate object directories and index
  121. * file locations are deduced from the given git directory and the default
  122. * rules.
  123. *
  124. * @param d
  125. * GIT_DIR (the location of the repository metadata). May be
  126. * null work workTree is set
  127. * @param workTree
  128. * GIT_WORK_TREE (the root of the checkout). May be null for
  129. * default value.
  130. * @throws IOException
  131. * the repository appears to already exist but cannot be
  132. * accessed.
  133. */
  134. public Repository(final File d, final File workTree) throws IOException {
  135. this(d, workTree, null, null, null); // go figure it out
  136. }
  137. /**
  138. * Construct a representation of a Git repository using the given parameters
  139. * possibly overriding default conventions.
  140. *
  141. * @param d
  142. * GIT_DIR (the location of the repository metadata). May be null
  143. * for default value in which case it depends on GIT_WORK_TREE.
  144. * @param workTree
  145. * GIT_WORK_TREE (the root of the checkout). May be null for
  146. * default value if GIT_DIR is
  147. * @param objectDir
  148. * GIT_OBJECT_DIRECTORY (where objects and are stored). May be
  149. * null for default value. Relative names ares resolved against
  150. * GIT_WORK_TREE
  151. * @param alternateObjectDir
  152. * GIT_ALTERNATE_OBJECT_DIRECTORIES (where more objects are read
  153. * from). May be null for default value. Relative names ares
  154. * resolved against GIT_WORK_TREE
  155. * @param indexFile
  156. * GIT_INDEX_FILE (the location of the index file). May be null
  157. * for default value. Relative names ares resolved against
  158. * GIT_WORK_TREE.
  159. * @throws IOException
  160. * the repository appears to already exist but cannot be
  161. * accessed.
  162. */
  163. public Repository(final File d, final File workTree, final File objectDir,
  164. final File[] alternateObjectDir, final File indexFile) throws IOException {
  165. if (workTree != null) {
  166. workDir = workTree;
  167. if (d == null)
  168. gitDir = new File(workTree, Constants.DOT_GIT);
  169. else
  170. gitDir = d;
  171. } else {
  172. if (d != null)
  173. gitDir = d;
  174. else
  175. throw new IllegalArgumentException("Either GIT_DIR or GIT_WORK_TREE must be passed to Repository constructor");
  176. }
  177. final FileBasedConfig userConfig;
  178. userConfig = SystemReader.getInstance().openUserConfig();
  179. try {
  180. userConfig.load();
  181. } catch (ConfigInvalidException e1) {
  182. IOException e2 = new IOException("User config file "
  183. + userConfig.getFile().getAbsolutePath() + " invalid: "
  184. + e1);
  185. e2.initCause(e1);
  186. throw e2;
  187. }
  188. config = new RepositoryConfig(userConfig, FS.resolve(gitDir, "config"));
  189. try {
  190. getConfig().load();
  191. } catch (ConfigInvalidException e1) {
  192. IOException e2 = new IOException("Unknown repository format");
  193. e2.initCause(e1);
  194. throw e2;
  195. }
  196. if (workDir == null) {
  197. String workTreeConfig = getConfig().getString("core", null, "worktree");
  198. if (workTreeConfig != null) {
  199. workDir = FS.resolve(d, workTreeConfig);
  200. } else {
  201. workDir = gitDir.getParentFile();
  202. }
  203. }
  204. refs = new RefDirectory(this);
  205. if (objectDir != null)
  206. objectDatabase = new ObjectDirectory(FS.resolve(objectDir, ""),
  207. alternateObjectDir);
  208. else
  209. objectDatabase = new ObjectDirectory(FS.resolve(gitDir, "objects"),
  210. alternateObjectDir);
  211. if (indexFile != null)
  212. this.indexFile = indexFile;
  213. else
  214. this.indexFile = new File(gitDir, "index");
  215. if (objectDatabase.exists()) {
  216. final String repositoryFormatVersion = getConfig().getString(
  217. "core", null, "repositoryFormatVersion");
  218. if (!"0".equals(repositoryFormatVersion)) {
  219. throw new IOException("Unknown repository format \""
  220. + repositoryFormatVersion + "\"; expected \"0\".");
  221. }
  222. }
  223. }
  224. /**
  225. * Create a new Git repository initializing the necessary files and
  226. * directories. Repository with working tree is created using this method.
  227. *
  228. * @throws IOException
  229. * @see #create(boolean)
  230. */
  231. public synchronized void create() throws IOException {
  232. create(false);
  233. }
  234. /**
  235. * Create a new Git repository initializing the necessary files and
  236. * directories.
  237. *
  238. * @param bare
  239. * if true, a bare repository is created.
  240. *
  241. * @throws IOException
  242. * in case of IO problem
  243. */
  244. public void create(boolean bare) throws IOException {
  245. final RepositoryConfig cfg = getConfig();
  246. if (cfg.getFile().exists()) {
  247. throw new IllegalStateException("Repository already exists: "
  248. + gitDir);
  249. }
  250. gitDir.mkdirs();
  251. refs.create();
  252. objectDatabase.create();
  253. new File(gitDir, "branches").mkdir();
  254. RefUpdate head = updateRef(Constants.HEAD);
  255. head.disableRefLog();
  256. head.link(Constants.R_HEADS + Constants.MASTER);
  257. cfg.setInt("core", null, "repositoryformatversion", 0);
  258. cfg.setBoolean("core", null, "filemode", true);
  259. if (bare)
  260. cfg.setBoolean("core", null, "bare", true);
  261. cfg.setBoolean("core", null, "logallrefupdates", !bare);
  262. cfg.setBoolean("core", null, "autocrlf", false);
  263. cfg.save();
  264. }
  265. /**
  266. * @return GIT_DIR
  267. */
  268. public File getDirectory() {
  269. return gitDir;
  270. }
  271. /**
  272. * @return the directory containing the objects owned by this repository.
  273. */
  274. public File getObjectsDirectory() {
  275. return objectDatabase.getDirectory();
  276. }
  277. /**
  278. * @return the object database which stores this repository's data.
  279. */
  280. public ObjectDatabase getObjectDatabase() {
  281. return objectDatabase;
  282. }
  283. /** @return the reference database which stores the reference namespace. */
  284. public RefDatabase getRefDatabase() {
  285. return refs;
  286. }
  287. /**
  288. * @return the configuration of this repository
  289. */
  290. public RepositoryConfig getConfig() {
  291. return config;
  292. }
  293. /**
  294. * Construct a filename where the loose object having a specified SHA-1
  295. * should be stored. If the object is stored in a shared repository the path
  296. * to the alternative repo will be returned. If the object is not yet store
  297. * a usable path in this repo will be returned. It is assumed that callers
  298. * will look for objects in a pack first.
  299. *
  300. * @param objectId
  301. * @return suggested file name
  302. */
  303. public File toFile(final AnyObjectId objectId) {
  304. return objectDatabase.fileFor(objectId);
  305. }
  306. /**
  307. * @param objectId
  308. * @return true if the specified object is stored in this repo or any of the
  309. * known shared repositories.
  310. */
  311. public boolean hasObject(final AnyObjectId objectId) {
  312. return objectDatabase.hasObject(objectId);
  313. }
  314. /**
  315. * @param id
  316. * SHA-1 of an object.
  317. *
  318. * @return a {@link ObjectLoader} for accessing the data of the named
  319. * object, or null if the object does not exist.
  320. * @throws IOException
  321. */
  322. public ObjectLoader openObject(final AnyObjectId id)
  323. throws IOException {
  324. final WindowCursor wc = new WindowCursor();
  325. try {
  326. return openObject(wc, id);
  327. } finally {
  328. wc.release();
  329. }
  330. }
  331. /**
  332. * @param curs
  333. * temporary working space associated with the calling thread.
  334. * @param id
  335. * SHA-1 of an object.
  336. *
  337. * @return a {@link ObjectLoader} for accessing the data of the named
  338. * object, or null if the object does not exist.
  339. * @throws IOException
  340. */
  341. public ObjectLoader openObject(final WindowCursor curs, final AnyObjectId id)
  342. throws IOException {
  343. return objectDatabase.openObject(curs, id);
  344. }
  345. /**
  346. * Open object in all packs containing specified object.
  347. *
  348. * @param objectId
  349. * id of object to search for
  350. * @param curs
  351. * temporary working space associated with the calling thread.
  352. * @return collection of loaders for this object, from all packs containing
  353. * this object
  354. * @throws IOException
  355. */
  356. public Collection<PackedObjectLoader> openObjectInAllPacks(
  357. final AnyObjectId objectId, final WindowCursor curs)
  358. throws IOException {
  359. Collection<PackedObjectLoader> result = new LinkedList<PackedObjectLoader>();
  360. openObjectInAllPacks(objectId, result, curs);
  361. return result;
  362. }
  363. /**
  364. * Open object in all packs containing specified object.
  365. *
  366. * @param objectId
  367. * id of object to search for
  368. * @param resultLoaders
  369. * result collection of loaders for this object, filled with
  370. * loaders from all packs containing specified object
  371. * @param curs
  372. * temporary working space associated with the calling thread.
  373. * @throws IOException
  374. */
  375. void openObjectInAllPacks(final AnyObjectId objectId,
  376. final Collection<PackedObjectLoader> resultLoaders,
  377. final WindowCursor curs) throws IOException {
  378. objectDatabase.openObjectInAllPacks(resultLoaders, curs, objectId);
  379. }
  380. /**
  381. * @param id
  382. * SHA'1 of a blob
  383. * @return an {@link ObjectLoader} for accessing the data of a named blob
  384. * @throws IOException
  385. */
  386. public ObjectLoader openBlob(final ObjectId id) throws IOException {
  387. return openObject(id);
  388. }
  389. /**
  390. * @param id
  391. * SHA'1 of a tree
  392. * @return an {@link ObjectLoader} for accessing the data of a named tree
  393. * @throws IOException
  394. */
  395. public ObjectLoader openTree(final ObjectId id) throws IOException {
  396. return openObject(id);
  397. }
  398. /**
  399. * Access a Commit object using a symbolic reference. This reference may
  400. * be a SHA-1 or ref in combination with a number of symbols translating
  401. * from one ref or SHA1-1 to another, such as HEAD^ etc.
  402. *
  403. * @param revstr a reference to a git commit object
  404. * @return a Commit named by the specified string
  405. * @throws IOException for I/O error or unexpected object type.
  406. *
  407. * @see #resolve(String)
  408. */
  409. public Commit mapCommit(final String revstr) throws IOException {
  410. final ObjectId id = resolve(revstr);
  411. return id != null ? mapCommit(id) : null;
  412. }
  413. /**
  414. * Access any type of Git object by id and
  415. *
  416. * @param id
  417. * SHA-1 of object to read
  418. * @param refName optional, only relevant for simple tags
  419. * @return The Git object if found or null
  420. * @throws IOException
  421. */
  422. public Object mapObject(final ObjectId id, final String refName) throws IOException {
  423. final ObjectLoader or = openObject(id);
  424. if (or == null)
  425. return null;
  426. final byte[] raw = or.getBytes();
  427. switch (or.getType()) {
  428. case Constants.OBJ_TREE:
  429. return makeTree(id, raw);
  430. case Constants.OBJ_COMMIT:
  431. return makeCommit(id, raw);
  432. case Constants.OBJ_TAG:
  433. return makeTag(id, refName, raw);
  434. case Constants.OBJ_BLOB:
  435. return raw;
  436. default:
  437. throw new IncorrectObjectTypeException(id,
  438. "COMMIT nor TREE nor BLOB nor TAG");
  439. }
  440. }
  441. /**
  442. * Access a Commit by SHA'1 id.
  443. * @param id
  444. * @return Commit or null
  445. * @throws IOException for I/O error or unexpected object type.
  446. */
  447. public Commit mapCommit(final ObjectId id) throws IOException {
  448. final ObjectLoader or = openObject(id);
  449. if (or == null)
  450. return null;
  451. final byte[] raw = or.getBytes();
  452. if (Constants.OBJ_COMMIT == or.getType())
  453. return new Commit(this, id, raw);
  454. throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
  455. }
  456. private Commit makeCommit(final ObjectId id, final byte[] raw) {
  457. Commit ret = new Commit(this, id, raw);
  458. return ret;
  459. }
  460. /**
  461. * Access a Tree object using a symbolic reference. This reference may
  462. * be a SHA-1 or ref in combination with a number of symbols translating
  463. * from one ref or SHA1-1 to another, such as HEAD^{tree} etc.
  464. *
  465. * @param revstr a reference to a git commit object
  466. * @return a Tree named by the specified string
  467. * @throws IOException
  468. *
  469. * @see #resolve(String)
  470. */
  471. public Tree mapTree(final String revstr) throws IOException {
  472. final ObjectId id = resolve(revstr);
  473. return id != null ? mapTree(id) : null;
  474. }
  475. /**
  476. * Access a Tree by SHA'1 id.
  477. * @param id
  478. * @return Tree or null
  479. * @throws IOException for I/O error or unexpected object type.
  480. */
  481. public Tree mapTree(final ObjectId id) throws IOException {
  482. final ObjectLoader or = openObject(id);
  483. if (or == null)
  484. return null;
  485. final byte[] raw = or.getBytes();
  486. switch (or.getType()) {
  487. case Constants.OBJ_TREE:
  488. return new Tree(this, id, raw);
  489. case Constants.OBJ_COMMIT:
  490. return mapTree(ObjectId.fromString(raw, 5));
  491. default:
  492. throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);
  493. }
  494. }
  495. private Tree makeTree(final ObjectId id, final byte[] raw) throws IOException {
  496. Tree ret = new Tree(this, id, raw);
  497. return ret;
  498. }
  499. private Tag makeTag(final ObjectId id, final String refName, final byte[] raw) {
  500. Tag ret = new Tag(this, id, refName, raw);
  501. return ret;
  502. }
  503. /**
  504. * Access a tag by symbolic name.
  505. *
  506. * @param revstr
  507. * @return a Tag or null
  508. * @throws IOException on I/O error or unexpected type
  509. */
  510. public Tag mapTag(String revstr) throws IOException {
  511. final ObjectId id = resolve(revstr);
  512. return id != null ? mapTag(revstr, id) : null;
  513. }
  514. /**
  515. * Access a Tag by SHA'1 id
  516. * @param refName
  517. * @param id
  518. * @return Commit or null
  519. * @throws IOException for I/O error or unexpected object type.
  520. */
  521. public Tag mapTag(final String refName, final ObjectId id) throws IOException {
  522. final ObjectLoader or = openObject(id);
  523. if (or == null)
  524. return null;
  525. final byte[] raw = or.getBytes();
  526. if (Constants.OBJ_TAG == or.getType())
  527. return new Tag(this, id, refName, raw);
  528. return new Tag(this, id, refName, null);
  529. }
  530. /**
  531. * Create a command to update, create or delete a ref in this repository.
  532. *
  533. * @param ref
  534. * name of the ref the caller wants to modify.
  535. * @return an update command. The caller must finish populating this command
  536. * and then invoke one of the update methods to actually make a
  537. * change.
  538. * @throws IOException
  539. * a symbolic ref was passed in and could not be resolved back
  540. * to the base ref, as the symbolic ref could not be read.
  541. */
  542. public RefUpdate updateRef(final String ref) throws IOException {
  543. return updateRef(ref, false);
  544. }
  545. /**
  546. * Create a command to update, create or delete a ref in this repository.
  547. *
  548. * @param ref
  549. * name of the ref the caller wants to modify.
  550. * @param detach
  551. * true to create a detached head
  552. * @return an update command. The caller must finish populating this command
  553. * and then invoke one of the update methods to actually make a
  554. * change.
  555. * @throws IOException
  556. * a symbolic ref was passed in and could not be resolved back
  557. * to the base ref, as the symbolic ref could not be read.
  558. */
  559. public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
  560. return refs.newUpdate(ref, detach);
  561. }
  562. /**
  563. * Create a command to rename a ref in this repository
  564. *
  565. * @param fromRef
  566. * name of ref to rename from
  567. * @param toRef
  568. * name of ref to rename to
  569. * @return an update command that knows how to rename a branch to another.
  570. * @throws IOException
  571. * the rename could not be performed.
  572. *
  573. */
  574. public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
  575. return refs.newRename(fromRef, toRef);
  576. }
  577. /**
  578. * Parse a git revision string and return an object id.
  579. *
  580. * Currently supported is combinations of these.
  581. * <ul>
  582. * <li>SHA-1 - a SHA-1</li>
  583. * <li>refs/... - a ref name</li>
  584. * <li>ref^n - nth parent reference</li>
  585. * <li>ref~n - distance via parent reference</li>
  586. * <li>ref@{n} - nth version of ref</li>
  587. * <li>ref^{tree} - tree references by ref</li>
  588. * <li>ref^{commit} - commit references by ref</li>
  589. * </ul>
  590. *
  591. * Not supported is
  592. * <ul>
  593. * <li>timestamps in reflogs, ref@{full or relative timestamp}</li>
  594. * <li>abbreviated SHA-1's</li>
  595. * </ul>
  596. *
  597. * @param revstr A git object references expression
  598. * @return an ObjectId or null if revstr can't be resolved to any ObjectId
  599. * @throws IOException on serious errors
  600. */
  601. public ObjectId resolve(final String revstr) throws IOException {
  602. char[] rev = revstr.toCharArray();
  603. Object ref = null;
  604. ObjectId refId = null;
  605. for (int i = 0; i < rev.length; ++i) {
  606. switch (rev[i]) {
  607. case '^':
  608. if (refId == null) {
  609. String refstr = new String(rev,0,i);
  610. refId = resolveSimple(refstr);
  611. if (refId == null)
  612. return null;
  613. }
  614. if (i + 1 < rev.length) {
  615. switch (rev[i + 1]) {
  616. case '0':
  617. case '1':
  618. case '2':
  619. case '3':
  620. case '4':
  621. case '5':
  622. case '6':
  623. case '7':
  624. case '8':
  625. case '9':
  626. int j;
  627. ref = mapObject(refId, null);
  628. while (ref instanceof Tag) {
  629. Tag tag = (Tag)ref;
  630. refId = tag.getObjId();
  631. ref = mapObject(refId, null);
  632. }
  633. if (!(ref instanceof Commit))
  634. throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
  635. for (j=i+1; j<rev.length; ++j) {
  636. if (!Character.isDigit(rev[j]))
  637. break;
  638. }
  639. String parentnum = new String(rev, i+1, j-i-1);
  640. int pnum;
  641. try {
  642. pnum = Integer.parseInt(parentnum);
  643. } catch (NumberFormatException e) {
  644. throw new RevisionSyntaxException(
  645. "Invalid commit parent number",
  646. revstr);
  647. }
  648. if (pnum != 0) {
  649. final ObjectId parents[] = ((Commit) ref)
  650. .getParentIds();
  651. if (pnum > parents.length)
  652. refId = null;
  653. else
  654. refId = parents[pnum - 1];
  655. }
  656. i = j - 1;
  657. break;
  658. case '{':
  659. int k;
  660. String item = null;
  661. for (k=i+2; k<rev.length; ++k) {
  662. if (rev[k] == '}') {
  663. item = new String(rev, i+2, k-i-2);
  664. break;
  665. }
  666. }
  667. i = k;
  668. if (item != null)
  669. if (item.equals("tree")) {
  670. ref = mapObject(refId, null);
  671. while (ref instanceof Tag) {
  672. Tag t = (Tag)ref;
  673. refId = t.getObjId();
  674. ref = mapObject(refId, null);
  675. }
  676. if (ref instanceof Treeish)
  677. refId = ((Treeish)ref).getTreeId();
  678. else
  679. throw new IncorrectObjectTypeException(refId, Constants.TYPE_TREE);
  680. }
  681. else if (item.equals("commit")) {
  682. ref = mapObject(refId, null);
  683. while (ref instanceof Tag) {
  684. Tag t = (Tag)ref;
  685. refId = t.getObjId();
  686. ref = mapObject(refId, null);
  687. }
  688. if (!(ref instanceof Commit))
  689. throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
  690. }
  691. else if (item.equals("blob")) {
  692. ref = mapObject(refId, null);
  693. while (ref instanceof Tag) {
  694. Tag t = (Tag)ref;
  695. refId = t.getObjId();
  696. ref = mapObject(refId, null);
  697. }
  698. if (!(ref instanceof byte[]))
  699. throw new IncorrectObjectTypeException(refId, Constants.TYPE_BLOB);
  700. }
  701. else if (item.equals("")) {
  702. ref = mapObject(refId, null);
  703. while (ref instanceof Tag) {
  704. Tag t = (Tag)ref;
  705. refId = t.getObjId();
  706. ref = mapObject(refId, null);
  707. }
  708. }
  709. else
  710. throw new RevisionSyntaxException(revstr);
  711. else
  712. throw new RevisionSyntaxException(revstr);
  713. break;
  714. default:
  715. ref = mapObject(refId, null);
  716. if (ref instanceof Commit) {
  717. final ObjectId parents[] = ((Commit) ref)
  718. .getParentIds();
  719. if (parents.length == 0)
  720. refId = null;
  721. else
  722. refId = parents[0];
  723. } else
  724. throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
  725. }
  726. } else {
  727. ref = mapObject(refId, null);
  728. while (ref instanceof Tag) {
  729. Tag tag = (Tag)ref;
  730. refId = tag.getObjId();
  731. ref = mapObject(refId, null);
  732. }
  733. if (ref instanceof Commit) {
  734. final ObjectId parents[] = ((Commit) ref)
  735. .getParentIds();
  736. if (parents.length == 0)
  737. refId = null;
  738. else
  739. refId = parents[0];
  740. } else
  741. throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
  742. }
  743. break;
  744. case '~':
  745. if (ref == null) {
  746. String refstr = new String(rev,0,i);
  747. refId = resolveSimple(refstr);
  748. if (refId == null)
  749. return null;
  750. ref = mapObject(refId, null);
  751. }
  752. while (ref instanceof Tag) {
  753. Tag tag = (Tag)ref;
  754. refId = tag.getObjId();
  755. ref = mapObject(refId, null);
  756. }
  757. if (!(ref instanceof Commit))
  758. throw new IncorrectObjectTypeException(refId, Constants.TYPE_COMMIT);
  759. int l;
  760. for (l = i + 1; l < rev.length; ++l) {
  761. if (!Character.isDigit(rev[l]))
  762. break;
  763. }
  764. String distnum = new String(rev, i+1, l-i-1);
  765. int dist;
  766. try {
  767. dist = Integer.parseInt(distnum);
  768. } catch (NumberFormatException e) {
  769. throw new RevisionSyntaxException(
  770. "Invalid ancestry length", revstr);
  771. }
  772. while (dist > 0) {
  773. final ObjectId[] parents = ((Commit) ref).getParentIds();
  774. if (parents.length == 0) {
  775. refId = null;
  776. break;
  777. }
  778. refId = parents[0];
  779. ref = mapCommit(refId);
  780. --dist;
  781. }
  782. i = l - 1;
  783. break;
  784. case '@':
  785. int m;
  786. String time = null;
  787. for (m=i+2; m<rev.length; ++m) {
  788. if (rev[m] == '}') {
  789. time = new String(rev, i+2, m-i-2);
  790. break;
  791. }
  792. }
  793. if (time != null)
  794. throw new RevisionSyntaxException("reflogs not yet supported by revision parser", revstr);
  795. i = m - 1;
  796. break;
  797. default:
  798. if (refId != null)
  799. throw new RevisionSyntaxException(revstr);
  800. }
  801. }
  802. if (refId == null)
  803. refId = resolveSimple(revstr);
  804. return refId;
  805. }
  806. private ObjectId resolveSimple(final String revstr) throws IOException {
  807. if (ObjectId.isId(revstr))
  808. return ObjectId.fromString(revstr);
  809. final Ref r = refs.getRef(revstr);
  810. return r != null ? r.getObjectId() : null;
  811. }
  812. /** Increment the use counter by one, requiring a matched {@link #close()}. */
  813. public void incrementOpen() {
  814. useCnt.incrementAndGet();
  815. }
  816. /**
  817. * Close all resources used by this repository
  818. */
  819. public void close() {
  820. if (useCnt.decrementAndGet() == 0) {
  821. objectDatabase.close();
  822. refs.close();
  823. }
  824. }
  825. /**
  826. * Add a single existing pack to the list of available pack files.
  827. *
  828. * @param pack
  829. * path of the pack file to open.
  830. * @param idx
  831. * path of the corresponding index file.
  832. * @throws IOException
  833. * index file could not be opened, read, or is not recognized as
  834. * a Git pack file index.
  835. */
  836. public void openPack(final File pack, final File idx) throws IOException {
  837. objectDatabase.openPack(pack, idx);
  838. }
  839. public String toString() {
  840. return "Repository[" + getDirectory() + "]";
  841. }
  842. /**
  843. * Get the name of the reference that {@code HEAD} points to.
  844. * <p>
  845. * This is essentially the same as doing:
  846. *
  847. * <pre>
  848. * return getRef(Constants.HEAD).getTarget().getName()
  849. * </pre>
  850. *
  851. * Except when HEAD is detached, in which case this method returns the
  852. * current ObjectId in hexadecimal string format.
  853. *
  854. * @return name of current branch (for example {@code refs/heads/master}) or
  855. * an ObjectId in hex format if the current branch is detached.
  856. * @throws IOException
  857. */
  858. public String getFullBranch() throws IOException {
  859. Ref head = getRef(Constants.HEAD);
  860. if (head == null)
  861. return null;
  862. if (head.isSymbolic())
  863. return head.getTarget().getName();
  864. if (head.getObjectId() != null)
  865. return head.getObjectId().name();
  866. return null;
  867. }
  868. /**
  869. * Get the short name of the current branch that {@code HEAD} points to.
  870. * <p>
  871. * This is essentially the same as {@link #getFullBranch()}, except the
  872. * leading prefix {@code refs/heads/} is removed from the reference before
  873. * it is returned to the caller.
  874. *
  875. * @return name of current branch (for example {@code master}), or an
  876. * ObjectId in hex format if the current branch is detached.
  877. * @throws IOException
  878. */
  879. public String getBranch() throws IOException {
  880. String name = getFullBranch();
  881. if (name != null)
  882. return shortenRefName(name);
  883. return name;
  884. }
  885. /**
  886. * Get a ref by name.
  887. *
  888. * @param name
  889. * the name of the ref to lookup. May be a short-hand form, e.g.
  890. * "master" which is is automatically expanded to
  891. * "refs/heads/master" if "refs/heads/master" already exists.
  892. * @return the Ref with the given name, or null if it does not exist
  893. * @throws IOException
  894. */
  895. public Ref getRef(final String name) throws IOException {
  896. return refs.getRef(name);
  897. }
  898. /**
  899. * @return mutable map of all known refs (heads, tags, remotes).
  900. */
  901. public Map<String, Ref> getAllRefs() {
  902. try {
  903. return refs.getRefs(RefDatabase.ALL);
  904. } catch (IOException e) {
  905. return new HashMap<String, Ref>();
  906. }
  907. }
  908. /**
  909. * @return mutable map of all tags; key is short tag name ("v1.0") and value
  910. * of the entry contains the ref with the full tag name
  911. * ("refs/tags/v1.0").
  912. */
  913. public Map<String, Ref> getTags() {
  914. try {
  915. return refs.getRefs(Constants.R_TAGS);
  916. } catch (IOException e) {
  917. return new HashMap<String, Ref>();
  918. }
  919. }
  920. /**
  921. * Peel a possibly unpeeled reference to an annotated tag.
  922. * <p>
  923. * If the ref cannot be peeled (as it does not refer to an annotated tag)
  924. * the peeled id stays null, but {@link Ref#isPeeled()} will be true.
  925. *
  926. * @param ref
  927. * The ref to peel
  928. * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
  929. * new Ref object representing the same data as Ref, but isPeeled()
  930. * will be true and getPeeledObjectId will contain the peeled object
  931. * (or null).
  932. */
  933. public Ref peel(final Ref ref) {
  934. try {
  935. return refs.peel(ref);
  936. } catch (IOException e) {
  937. // Historical accident; if the reference cannot be peeled due
  938. // to some sort of repository access problem we claim that the
  939. // same as if the reference was not an annotated tag.
  940. return ref;
  941. }
  942. }
  943. /**
  944. * @return a map with all objects referenced by a peeled ref.
  945. */
  946. public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
  947. Map<String, Ref> allRefs = getAllRefs();
  948. Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
  949. for (Ref ref : allRefs.values()) {
  950. ref = peel(ref);
  951. AnyObjectId target = ref.getPeeledObjectId();
  952. if (target == null)
  953. target = ref.getObjectId();
  954. // We assume most Sets here are singletons
  955. Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  956. if (oset != null) {
  957. // that was not the case (rare)
  958. if (oset.size() == 1) {
  959. // Was a read-only singleton, we must copy to a new Set
  960. oset = new HashSet<Ref>(oset);
  961. }
  962. ret.put(target, oset);
  963. oset.add(ref);
  964. }
  965. }
  966. return ret;
  967. }
  968. /**
  969. * @return a representation of the index associated with this repo
  970. * @throws IOException
  971. */
  972. public GitIndex getIndex() throws IOException {
  973. if (index == null) {
  974. index = new GitIndex(this);
  975. index.read();
  976. } else {
  977. index.rereadIfNecessary();
  978. }
  979. return index;
  980. }
  981. /**
  982. * @return the index file location
  983. */
  984. public File getIndexFile() {
  985. return indexFile;
  986. }
  987. static byte[] gitInternalSlash(byte[] bytes) {
  988. if (File.separatorChar == '/')
  989. return bytes;
  990. for (int i=0; i<bytes.length; ++i)
  991. if (bytes[i] == File.separatorChar)
  992. bytes[i] = '/';
  993. return bytes;
  994. }
  995. /**
  996. * @return an important state
  997. */
  998. public RepositoryState getRepositoryState() {
  999. // Pre Git-1.6 logic
  1000. if (new File(getWorkDir(), ".dotest").exists())
  1001. return RepositoryState.REBASING;
  1002. if (new File(gitDir,".dotest-merge").exists())
  1003. return RepositoryState.REBASING_INTERACTIVE;
  1004. // From 1.6 onwards
  1005. if (new File(getDirectory(),"rebase-apply/rebasing").exists())
  1006. return RepositoryState.REBASING_REBASING;
  1007. if (new File(getDirectory(),"rebase-apply/applying").exists())
  1008. return RepositoryState.APPLY;
  1009. if (new File(getDirectory(),"rebase-apply").exists())
  1010. return RepositoryState.REBASING;
  1011. if (new File(getDirectory(),"rebase-merge/interactive").exists())
  1012. return RepositoryState.REBASING_INTERACTIVE;
  1013. if (new File(getDirectory(),"rebase-merge").exists())
  1014. return RepositoryState.REBASING_MERGE;
  1015. // Both versions
  1016. if (new File(gitDir,"MERGE_HEAD").exists())
  1017. return RepositoryState.MERGING;
  1018. if (new File(gitDir,"BISECT_LOG").exists())
  1019. return RepositoryState.BISECTING;
  1020. return RepositoryState.SAFE;
  1021. }
  1022. /**
  1023. * Check validity of a ref name. It must not contain character that has
  1024. * a special meaning in a Git object reference expression. Some other
  1025. * dangerous characters are also excluded.
  1026. *
  1027. * For portability reasons '\' is excluded
  1028. *
  1029. * @param refName
  1030. *
  1031. * @return true if refName is a valid ref name
  1032. */
  1033. public static boolean isValidRefName(final String refName) {
  1034. final int len = refName.length();
  1035. if (len == 0)
  1036. return false;
  1037. if (refName.endsWith(LockFile.SUFFIX))
  1038. return false;
  1039. int components = 1;
  1040. char p = '\0';
  1041. for (int i = 0; i < len; i++) {
  1042. final char c = refName.charAt(i);
  1043. if (c <= ' ')
  1044. return false;
  1045. switch (c) {
  1046. case '.':
  1047. switch (p) {
  1048. case '\0': case '/': case '.':
  1049. return false;
  1050. }
  1051. if (i == len -1)
  1052. return false;
  1053. break;
  1054. case '/':
  1055. if (i == 0 || i == len - 1)
  1056. return false;
  1057. components++;
  1058. break;
  1059. case '{':
  1060. if (p == '@')
  1061. return false;
  1062. break;
  1063. case '~': case '^': case ':':
  1064. case '?': case '[': case '*':
  1065. case '\\':
  1066. return false;
  1067. }
  1068. p = c;
  1069. }
  1070. return components > 1;
  1071. }
  1072. /**
  1073. * Strip work dir and return normalized repository path.
  1074. *
  1075. * @param workDir Work dir
  1076. * @param file File whose path shall be stripped of its workdir
  1077. * @return normalized repository relative path or the empty
  1078. * string if the file is not relative to the work directory.
  1079. */
  1080. public static String stripWorkDir(File workDir, File file) {
  1081. final String filePath = file.getPath();
  1082. final String workDirPath = workDir.getPath();
  1083. if (filePath.length() <= workDirPath.length() ||
  1084. filePath.charAt(workDirPath.length()) != File.separatorChar ||
  1085. !filePath.startsWith(workDirPath)) {
  1086. File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
  1087. File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1088. if (absWd == workDir && absFile == file)
  1089. return "";
  1090. return stripWorkDir(absWd, absFile);
  1091. }
  1092. String relName = filePath.substring(workDirPath.length() + 1);
  1093. if (File.separatorChar != '/')
  1094. relName = relName.replace(File.separatorChar, '/');
  1095. return relName;
  1096. }
  1097. /**
  1098. * @return the workdir file, i.e. where the files are checked out
  1099. */
  1100. public File getWorkDir() {
  1101. return workDir;
  1102. }
  1103. /**
  1104. * Override default workdir
  1105. *
  1106. * @param workTree
  1107. * the work tree directory
  1108. */
  1109. public void setWorkDir(File workTree) {
  1110. this.workDir = workTree;
  1111. }
  1112. /**
  1113. * Register a {@link RepositoryListener} which will be notified
  1114. * when ref changes are detected.
  1115. *
  1116. * @param l
  1117. */
  1118. public void addRepositoryChangedListener(final RepositoryListener l) {
  1119. listeners.add(l);
  1120. }
  1121. /**
  1122. * Remove a registered {@link RepositoryListener}
  1123. * @param l
  1124. */
  1125. public void removeRepositoryChangedListener(final RepositoryListener l) {
  1126. listeners.remove(l);
  1127. }
  1128. /**
  1129. * Register a global {@link RepositoryListener} which will be notified
  1130. * when a ref changes in any repository are detected.
  1131. *
  1132. * @param l
  1133. */
  1134. public static void addAnyRepositoryChangedListener(final RepositoryListener l) {
  1135. allListeners.add(l);
  1136. }
  1137. /**
  1138. * Remove a globally registered {@link RepositoryListener}
  1139. * @param l
  1140. */
  1141. public static void removeAnyRepositoryChangedListener(final RepositoryListener l) {
  1142. allListeners.remove(l);
  1143. }
  1144. void fireRefsChanged() {
  1145. final RefsChangedEvent event = new RefsChangedEvent(this);
  1146. List<RepositoryListener> all;
  1147. synchronized (listeners) {
  1148. all = new ArrayList<RepositoryListener>(listeners);
  1149. }
  1150. synchronized (allListeners) {
  1151. all.addAll(allListeners);
  1152. }
  1153. for (final RepositoryListener l : all) {
  1154. l.refsChanged(event);
  1155. }
  1156. }
  1157. void fireIndexChanged() {
  1158. final IndexChangedEvent event = new IndexChangedEvent(this);
  1159. List<RepositoryListener> all;
  1160. synchronized (listeners) {
  1161. all = new ArrayList<RepositoryListener>(listeners);
  1162. }
  1163. synchronized (allListeners) {
  1164. all.addAll(allListeners);
  1165. }
  1166. for (final RepositoryListener l : all) {
  1167. l.indexChanged(event);
  1168. }
  1169. }
  1170. /**
  1171. * Force a scan for changed refs.
  1172. *
  1173. * @throws IOException
  1174. */
  1175. public void scanForRepoChanges() throws IOException {
  1176. getAllRefs(); // This will look for changes to refs
  1177. getIndex(); // This will detect changes in the index
  1178. }
  1179. /**
  1180. * @param refName
  1181. *
  1182. * @return a more user friendly ref name
  1183. */
  1184. public String shortenRefName(String refName) {
  1185. if (refName.startsWith(Constants.R_HEADS))
  1186. return refName.substring(Constants.R_HEADS.length());
  1187. if (refName.startsWith(Constants.R_TAGS))
  1188. return refName.substring(Constants.R_TAGS.length());
  1189. if (refName.startsWith(Constants.R_REMOTES))
  1190. return refName.substring(Constants.R_REMOTES.length());
  1191. return refName;
  1192. }
  1193. /**
  1194. * @param refName
  1195. * @return a {@link ReflogReader} for the supplied refname, or null if the
  1196. * named ref does not exist.
  1197. * @throws IOException the ref could not be accessed.
  1198. */
  1199. public ReflogReader getReflogReader(String refName) throws IOException {
  1200. Ref ref = getRef(refName);
  1201. if (ref != null)
  1202. return new ReflogReader(this, ref.getName());
  1203. return null;
  1204. }
  1205. }