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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847
  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-2012, Shawn O. Pearce <spearce@spearce.org>
  6. * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
  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.BufferedOutputStream;
  49. import java.io.File;
  50. import java.io.FileNotFoundException;
  51. import java.io.FileOutputStream;
  52. import java.io.IOException;
  53. import java.net.URISyntaxException;
  54. import java.text.MessageFormat;
  55. import java.util.Collection;
  56. import java.util.Collections;
  57. import java.util.HashMap;
  58. import java.util.HashSet;
  59. import java.util.LinkedList;
  60. import java.util.List;
  61. import java.util.Map;
  62. import java.util.Set;
  63. import java.util.concurrent.atomic.AtomicInteger;
  64. import java.util.concurrent.atomic.AtomicLong;
  65. import org.eclipse.jgit.annotations.NonNull;
  66. import org.eclipse.jgit.annotations.Nullable;
  67. import org.eclipse.jgit.attributes.AttributesNodeProvider;
  68. import org.eclipse.jgit.dircache.DirCache;
  69. import org.eclipse.jgit.errors.AmbiguousObjectException;
  70. import org.eclipse.jgit.errors.CorruptObjectException;
  71. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  72. import org.eclipse.jgit.errors.MissingObjectException;
  73. import org.eclipse.jgit.errors.NoWorkTreeException;
  74. import org.eclipse.jgit.errors.RevisionSyntaxException;
  75. import org.eclipse.jgit.events.IndexChangedEvent;
  76. import org.eclipse.jgit.events.IndexChangedListener;
  77. import org.eclipse.jgit.events.ListenerList;
  78. import org.eclipse.jgit.events.RepositoryEvent;
  79. import org.eclipse.jgit.internal.JGitText;
  80. import org.eclipse.jgit.revwalk.RevBlob;
  81. import org.eclipse.jgit.revwalk.RevCommit;
  82. import org.eclipse.jgit.revwalk.RevObject;
  83. import org.eclipse.jgit.revwalk.RevTree;
  84. import org.eclipse.jgit.revwalk.RevWalk;
  85. import org.eclipse.jgit.transport.RefSpec;
  86. import org.eclipse.jgit.transport.RemoteConfig;
  87. import org.eclipse.jgit.treewalk.TreeWalk;
  88. import org.eclipse.jgit.util.FS;
  89. import org.eclipse.jgit.util.FileUtils;
  90. import org.eclipse.jgit.util.IO;
  91. import org.eclipse.jgit.util.RawParseUtils;
  92. import org.eclipse.jgit.util.SystemReader;
  93. import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
  94. import org.slf4j.Logger;
  95. import org.slf4j.LoggerFactory;
  96. /**
  97. * Represents a Git repository.
  98. * <p>
  99. * A repository holds all objects and refs used for managing source code (could
  100. * be any type of file, but source code is what SCM's are typically used for).
  101. * <p>
  102. * This class is thread-safe.
  103. */
  104. public abstract class Repository implements AutoCloseable {
  105. private static Logger LOG = LoggerFactory.getLogger(Repository.class);
  106. private static final ListenerList globalListeners = new ListenerList();
  107. /** @return the global listener list observing all events in this JVM. */
  108. public static ListenerList getGlobalListenerList() {
  109. return globalListeners;
  110. }
  111. /** Use counter */
  112. final AtomicInteger useCnt = new AtomicInteger(1);
  113. final AtomicLong closedAt = new AtomicLong();
  114. /** Metadata directory holding the repository's critical files. */
  115. private final File gitDir;
  116. /** File abstraction used to resolve paths. */
  117. private final FS fs;
  118. private final ListenerList myListeners = new ListenerList();
  119. /** If not bare, the top level directory of the working files. */
  120. private final File workTree;
  121. /** If not bare, the index file caching the working file states. */
  122. private final File indexFile;
  123. /**
  124. * Initialize a new repository instance.
  125. *
  126. * @param options
  127. * options to configure the repository.
  128. */
  129. protected Repository(final BaseRepositoryBuilder options) {
  130. gitDir = options.getGitDir();
  131. fs = options.getFS();
  132. workTree = options.getWorkTree();
  133. indexFile = options.getIndexFile();
  134. }
  135. /** @return listeners observing only events on this repository. */
  136. @NonNull
  137. public ListenerList getListenerList() {
  138. return myListeners;
  139. }
  140. /**
  141. * Fire an event to all registered listeners.
  142. * <p>
  143. * The source repository of the event is automatically set to this
  144. * repository, before the event is delivered to any listeners.
  145. *
  146. * @param event
  147. * the event to deliver.
  148. */
  149. public void fireEvent(RepositoryEvent<?> event) {
  150. event.setRepository(this);
  151. myListeners.dispatch(event);
  152. globalListeners.dispatch(event);
  153. }
  154. /**
  155. * Create a new Git repository.
  156. * <p>
  157. * Repository with working tree is created using this method. This method is
  158. * the same as {@code create(false)}.
  159. *
  160. * @throws IOException
  161. * @see #create(boolean)
  162. */
  163. public void create() throws IOException {
  164. create(false);
  165. }
  166. /**
  167. * Create a new Git repository initializing the necessary files and
  168. * directories.
  169. *
  170. * @param bare
  171. * if true, a bare repository (a repository without a working
  172. * directory) is created.
  173. * @throws IOException
  174. * in case of IO problem
  175. */
  176. public abstract void create(boolean bare) throws IOException;
  177. /**
  178. * @return local metadata directory; {@code null} if repository isn't local.
  179. */
  180. /*
  181. * TODO This method should be annotated as Nullable, because in some
  182. * specific configurations metadata is not located in the local file system
  183. * (for example in memory databases). In "usual" repositories this
  184. * annotation would only cause compiler errors at places where the actual
  185. * directory can never be null.
  186. */
  187. public File getDirectory() {
  188. return gitDir;
  189. }
  190. /**
  191. * @return the object database which stores this repository's data.
  192. */
  193. @NonNull
  194. public abstract ObjectDatabase getObjectDatabase();
  195. /** @return a new inserter to create objects in {@link #getObjectDatabase()} */
  196. @NonNull
  197. public ObjectInserter newObjectInserter() {
  198. return getObjectDatabase().newInserter();
  199. }
  200. /** @return a new reader to read objects from {@link #getObjectDatabase()} */
  201. @NonNull
  202. public ObjectReader newObjectReader() {
  203. return getObjectDatabase().newReader();
  204. }
  205. /** @return the reference database which stores the reference namespace. */
  206. @NonNull
  207. public abstract RefDatabase getRefDatabase();
  208. /**
  209. * @return the configuration of this repository
  210. */
  211. @NonNull
  212. public abstract StoredConfig getConfig();
  213. /**
  214. * @return a new {@link AttributesNodeProvider}. This
  215. * {@link AttributesNodeProvider} is lazy loaded only once. It means
  216. * that it will not be updated after loading. Prefer creating new
  217. * instance for each use.
  218. * @since 4.2
  219. */
  220. @NonNull
  221. public abstract AttributesNodeProvider createAttributesNodeProvider();
  222. /**
  223. * @return the used file system abstraction, or or {@code null} if
  224. * repository isn't local.
  225. */
  226. /*
  227. * TODO This method should be annotated as Nullable, because in some
  228. * specific configurations metadata is not located in the local file system
  229. * (for example in memory databases). In "usual" repositories this
  230. * annotation would only cause compiler errors at places where the actual
  231. * directory can never be null.
  232. */
  233. public FS getFS() {
  234. return fs;
  235. }
  236. /**
  237. * @param objectId
  238. * @return true if the specified object is stored in this repo or any of the
  239. * known shared repositories.
  240. */
  241. public boolean hasObject(AnyObjectId objectId) {
  242. try {
  243. return getObjectDatabase().has(objectId);
  244. } catch (IOException e) {
  245. // Legacy API, assume error means "no"
  246. return false;
  247. }
  248. }
  249. /**
  250. * Open an object from this repository.
  251. * <p>
  252. * This is a one-shot call interface which may be faster than allocating a
  253. * {@link #newObjectReader()} to perform the lookup.
  254. *
  255. * @param objectId
  256. * identity of the object to open.
  257. * @return a {@link ObjectLoader} for accessing the object.
  258. * @throws MissingObjectException
  259. * the object does not exist.
  260. * @throws IOException
  261. * the object store cannot be accessed.
  262. */
  263. @NonNull
  264. public ObjectLoader open(final AnyObjectId objectId)
  265. throws MissingObjectException, IOException {
  266. return getObjectDatabase().open(objectId);
  267. }
  268. /**
  269. * Open an object from this repository.
  270. * <p>
  271. * This is a one-shot call interface which may be faster than allocating a
  272. * {@link #newObjectReader()} to perform the lookup.
  273. *
  274. * @param objectId
  275. * identity of the object to open.
  276. * @param typeHint
  277. * hint about the type of object being requested, e.g.
  278. * {@link Constants#OBJ_BLOB}; {@link ObjectReader#OBJ_ANY} if
  279. * the object type is not known, or does not matter to the
  280. * caller.
  281. * @return a {@link ObjectLoader} for accessing the object.
  282. * @throws MissingObjectException
  283. * the object does not exist.
  284. * @throws IncorrectObjectTypeException
  285. * typeHint was not OBJ_ANY, and the object's actual type does
  286. * not match typeHint.
  287. * @throws IOException
  288. * the object store cannot be accessed.
  289. */
  290. @NonNull
  291. public ObjectLoader open(AnyObjectId objectId, int typeHint)
  292. throws MissingObjectException, IncorrectObjectTypeException,
  293. IOException {
  294. return getObjectDatabase().open(objectId, typeHint);
  295. }
  296. /**
  297. * Create a command to update, create or delete a ref in this repository.
  298. *
  299. * @param ref
  300. * name of the ref the caller wants to modify.
  301. * @return an update command. The caller must finish populating this command
  302. * and then invoke one of the update methods to actually make a
  303. * change.
  304. * @throws IOException
  305. * a symbolic ref was passed in and could not be resolved back
  306. * to the base ref, as the symbolic ref could not be read.
  307. */
  308. @NonNull
  309. public RefUpdate updateRef(final String ref) throws IOException {
  310. return updateRef(ref, false);
  311. }
  312. /**
  313. * Create a command to update, create or delete a ref in this repository.
  314. *
  315. * @param ref
  316. * name of the ref the caller wants to modify.
  317. * @param detach
  318. * true to create a detached head
  319. * @return an update command. The caller must finish populating this command
  320. * and then invoke one of the update methods to actually make a
  321. * change.
  322. * @throws IOException
  323. * a symbolic ref was passed in and could not be resolved back
  324. * to the base ref, as the symbolic ref could not be read.
  325. */
  326. @NonNull
  327. public RefUpdate updateRef(final String ref, final boolean detach) throws IOException {
  328. return getRefDatabase().newUpdate(ref, detach);
  329. }
  330. /**
  331. * Create a command to rename a ref in this repository
  332. *
  333. * @param fromRef
  334. * name of ref to rename from
  335. * @param toRef
  336. * name of ref to rename to
  337. * @return an update command that knows how to rename a branch to another.
  338. * @throws IOException
  339. * the rename could not be performed.
  340. *
  341. */
  342. @NonNull
  343. public RefRename renameRef(final String fromRef, final String toRef) throws IOException {
  344. return getRefDatabase().newRename(fromRef, toRef);
  345. }
  346. /**
  347. * Parse a git revision string and return an object id.
  348. *
  349. * Combinations of these operators are supported:
  350. * <ul>
  351. * <li><b>HEAD</b>, <b>MERGE_HEAD</b>, <b>FETCH_HEAD</b></li>
  352. * <li><b>SHA-1</b>: a complete or abbreviated SHA-1</li>
  353. * <li><b>refs/...</b>: a complete reference name</li>
  354. * <li><b>short-name</b>: a short reference name under {@code refs/heads},
  355. * {@code refs/tags}, or {@code refs/remotes} namespace</li>
  356. * <li><b>tag-NN-gABBREV</b>: output from describe, parsed by treating
  357. * {@code ABBREV} as an abbreviated SHA-1.</li>
  358. * <li><i>id</i><b>^</b>: first parent of commit <i>id</i>, this is the same
  359. * as {@code id^1}</li>
  360. * <li><i>id</i><b>^0</b>: ensure <i>id</i> is a commit</li>
  361. * <li><i>id</i><b>^n</b>: n-th parent of commit <i>id</i></li>
  362. * <li><i>id</i><b>~n</b>: n-th historical ancestor of <i>id</i>, by first
  363. * parent. {@code id~3} is equivalent to {@code id^1^1^1} or {@code id^^^}.</li>
  364. * <li><i>id</i><b>:path</b>: Lookup path under tree named by <i>id</i></li>
  365. * <li><i>id</i><b>^{commit}</b>: ensure <i>id</i> is a commit</li>
  366. * <li><i>id</i><b>^{tree}</b>: ensure <i>id</i> is a tree</li>
  367. * <li><i>id</i><b>^{tag}</b>: ensure <i>id</i> is a tag</li>
  368. * <li><i>id</i><b>^{blob}</b>: ensure <i>id</i> is a blob</li>
  369. * </ul>
  370. *
  371. * <p>
  372. * The following operators are specified by Git conventions, but are not
  373. * supported by this method:
  374. * <ul>
  375. * <li><b>ref@{n}</b>: n-th version of ref as given by its reflog</li>
  376. * <li><b>ref@{time}</b>: value of ref at the designated time</li>
  377. * </ul>
  378. *
  379. * @param revstr
  380. * A git object references expression
  381. * @return an ObjectId or {@code null} if revstr can't be resolved to any
  382. * ObjectId
  383. * @throws AmbiguousObjectException
  384. * {@code revstr} contains an abbreviated ObjectId and this
  385. * repository contains more than one object which match to the
  386. * input abbreviation.
  387. * @throws IncorrectObjectTypeException
  388. * the id parsed does not meet the type required to finish
  389. * applying the operators in the expression.
  390. * @throws RevisionSyntaxException
  391. * the expression is not supported by this implementation, or
  392. * does not meet the standard syntax.
  393. * @throws IOException
  394. * on serious errors
  395. */
  396. @Nullable
  397. public ObjectId resolve(final String revstr)
  398. throws AmbiguousObjectException, IncorrectObjectTypeException,
  399. RevisionSyntaxException, IOException {
  400. try (RevWalk rw = new RevWalk(this)) {
  401. Object resolved = resolve(rw, revstr);
  402. if (resolved instanceof String) {
  403. final Ref ref = getRef((String)resolved);
  404. return ref != null ? ref.getLeaf().getObjectId() : null;
  405. } else {
  406. return (ObjectId) resolved;
  407. }
  408. }
  409. }
  410. /**
  411. * Simplify an expression, but unlike {@link #resolve(String)} it will not
  412. * resolve a branch passed or resulting from the expression, such as @{-}.
  413. * Thus this method can be used to process an expression to a method that
  414. * expects a branch or revision id.
  415. *
  416. * @param revstr
  417. * @return object id or ref name from resolved expression or {@code null} if
  418. * given expression cannot be resolved
  419. * @throws AmbiguousObjectException
  420. * @throws IOException
  421. */
  422. @Nullable
  423. public String simplify(final String revstr)
  424. throws AmbiguousObjectException, IOException {
  425. try (RevWalk rw = new RevWalk(this)) {
  426. Object resolved = resolve(rw, revstr);
  427. if (resolved != null)
  428. if (resolved instanceof String)
  429. return (String) resolved;
  430. else
  431. return ((AnyObjectId) resolved).getName();
  432. return null;
  433. }
  434. }
  435. @Nullable
  436. private Object resolve(final RevWalk rw, final String revstr)
  437. throws IOException {
  438. char[] revChars = revstr.toCharArray();
  439. RevObject rev = null;
  440. String name = null;
  441. int done = 0;
  442. for (int i = 0; i < revChars.length; ++i) {
  443. switch (revChars[i]) {
  444. case '^':
  445. if (rev == null) {
  446. if (name == null)
  447. if (done == 0)
  448. name = new String(revChars, done, i);
  449. else {
  450. done = i + 1;
  451. break;
  452. }
  453. rev = parseSimple(rw, name);
  454. name = null;
  455. if (rev == null)
  456. return null;
  457. }
  458. if (i + 1 < revChars.length) {
  459. switch (revChars[i + 1]) {
  460. case '0':
  461. case '1':
  462. case '2':
  463. case '3':
  464. case '4':
  465. case '5':
  466. case '6':
  467. case '7':
  468. case '8':
  469. case '9':
  470. int j;
  471. rev = rw.parseCommit(rev);
  472. for (j = i + 1; j < revChars.length; ++j) {
  473. if (!Character.isDigit(revChars[j]))
  474. break;
  475. }
  476. String parentnum = new String(revChars, i + 1, j - i
  477. - 1);
  478. int pnum;
  479. try {
  480. pnum = Integer.parseInt(parentnum);
  481. } catch (NumberFormatException e) {
  482. throw new RevisionSyntaxException(
  483. JGitText.get().invalidCommitParentNumber,
  484. revstr);
  485. }
  486. if (pnum != 0) {
  487. RevCommit commit = (RevCommit) rev;
  488. if (pnum > commit.getParentCount())
  489. rev = null;
  490. else
  491. rev = commit.getParent(pnum - 1);
  492. }
  493. i = j - 1;
  494. done = j;
  495. break;
  496. case '{':
  497. int k;
  498. String item = null;
  499. for (k = i + 2; k < revChars.length; ++k) {
  500. if (revChars[k] == '}') {
  501. item = new String(revChars, i + 2, k - i - 2);
  502. break;
  503. }
  504. }
  505. i = k;
  506. if (item != null)
  507. if (item.equals("tree")) { //$NON-NLS-1$
  508. rev = rw.parseTree(rev);
  509. } else if (item.equals("commit")) { //$NON-NLS-1$
  510. rev = rw.parseCommit(rev);
  511. } else if (item.equals("blob")) { //$NON-NLS-1$
  512. rev = rw.peel(rev);
  513. if (!(rev instanceof RevBlob))
  514. throw new IncorrectObjectTypeException(rev,
  515. Constants.TYPE_BLOB);
  516. } else if (item.equals("")) { //$NON-NLS-1$
  517. rev = rw.peel(rev);
  518. } else
  519. throw new RevisionSyntaxException(revstr);
  520. else
  521. throw new RevisionSyntaxException(revstr);
  522. done = k;
  523. break;
  524. default:
  525. rev = rw.peel(rev);
  526. if (rev instanceof RevCommit) {
  527. RevCommit commit = ((RevCommit) rev);
  528. if (commit.getParentCount() == 0)
  529. rev = null;
  530. else
  531. rev = commit.getParent(0);
  532. } else
  533. throw new IncorrectObjectTypeException(rev,
  534. Constants.TYPE_COMMIT);
  535. }
  536. } else {
  537. rev = rw.peel(rev);
  538. if (rev instanceof RevCommit) {
  539. RevCommit commit = ((RevCommit) rev);
  540. if (commit.getParentCount() == 0)
  541. rev = null;
  542. else
  543. rev = commit.getParent(0);
  544. } else
  545. throw new IncorrectObjectTypeException(rev,
  546. Constants.TYPE_COMMIT);
  547. }
  548. done = i + 1;
  549. break;
  550. case '~':
  551. if (rev == null) {
  552. if (name == null)
  553. if (done == 0)
  554. name = new String(revChars, done, i);
  555. else {
  556. done = i + 1;
  557. break;
  558. }
  559. rev = parseSimple(rw, name);
  560. name = null;
  561. if (rev == null)
  562. return null;
  563. }
  564. rev = rw.peel(rev);
  565. if (!(rev instanceof RevCommit))
  566. throw new IncorrectObjectTypeException(rev,
  567. Constants.TYPE_COMMIT);
  568. int l;
  569. for (l = i + 1; l < revChars.length; ++l) {
  570. if (!Character.isDigit(revChars[l]))
  571. break;
  572. }
  573. int dist;
  574. if (l - i > 1) {
  575. String distnum = new String(revChars, i + 1, l - i - 1);
  576. try {
  577. dist = Integer.parseInt(distnum);
  578. } catch (NumberFormatException e) {
  579. throw new RevisionSyntaxException(
  580. JGitText.get().invalidAncestryLength, revstr);
  581. }
  582. } else
  583. dist = 1;
  584. while (dist > 0) {
  585. RevCommit commit = (RevCommit) rev;
  586. if (commit.getParentCount() == 0) {
  587. rev = null;
  588. break;
  589. }
  590. commit = commit.getParent(0);
  591. rw.parseHeaders(commit);
  592. rev = commit;
  593. --dist;
  594. }
  595. i = l - 1;
  596. done = l;
  597. break;
  598. case '@':
  599. if (rev != null)
  600. throw new RevisionSyntaxException(revstr);
  601. if (i + 1 < revChars.length && revChars[i + 1] != '{')
  602. continue;
  603. int m;
  604. String time = null;
  605. for (m = i + 2; m < revChars.length; ++m) {
  606. if (revChars[m] == '}') {
  607. time = new String(revChars, i + 2, m - i - 2);
  608. break;
  609. }
  610. }
  611. if (time != null) {
  612. if (time.equals("upstream")) { //$NON-NLS-1$
  613. if (name == null)
  614. name = new String(revChars, done, i);
  615. if (name.equals("")) //$NON-NLS-1$
  616. // Currently checked out branch, HEAD if
  617. // detached
  618. name = Constants.HEAD;
  619. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  620. throw new RevisionSyntaxException(revstr);
  621. Ref ref = getRef(name);
  622. name = null;
  623. if (ref == null)
  624. return null;
  625. if (ref.isSymbolic())
  626. ref = ref.getLeaf();
  627. name = ref.getName();
  628. RemoteConfig remoteConfig;
  629. try {
  630. remoteConfig = new RemoteConfig(getConfig(),
  631. "origin"); //$NON-NLS-1$
  632. } catch (URISyntaxException e) {
  633. throw new RevisionSyntaxException(revstr);
  634. }
  635. String remoteBranchName = getConfig()
  636. .getString(
  637. ConfigConstants.CONFIG_BRANCH_SECTION,
  638. Repository.shortenRefName(ref.getName()),
  639. ConfigConstants.CONFIG_KEY_MERGE);
  640. List<RefSpec> fetchRefSpecs = remoteConfig
  641. .getFetchRefSpecs();
  642. for (RefSpec refSpec : fetchRefSpecs) {
  643. if (refSpec.matchSource(remoteBranchName)) {
  644. RefSpec expandFromSource = refSpec
  645. .expandFromSource(remoteBranchName);
  646. name = expandFromSource.getDestination();
  647. break;
  648. }
  649. }
  650. if (name == null)
  651. throw new RevisionSyntaxException(revstr);
  652. } else if (time.matches("^-\\d+$")) { //$NON-NLS-1$
  653. if (name != null)
  654. throw new RevisionSyntaxException(revstr);
  655. else {
  656. String previousCheckout = resolveReflogCheckout(-Integer
  657. .parseInt(time));
  658. if (ObjectId.isId(previousCheckout))
  659. rev = parseSimple(rw, previousCheckout);
  660. else
  661. name = previousCheckout;
  662. }
  663. } else {
  664. if (name == null)
  665. name = new String(revChars, done, i);
  666. if (name.equals("")) //$NON-NLS-1$
  667. name = Constants.HEAD;
  668. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  669. throw new RevisionSyntaxException(revstr);
  670. Ref ref = getRef(name);
  671. name = null;
  672. if (ref == null)
  673. return null;
  674. // @{n} means current branch, not HEAD@{1} unless
  675. // detached
  676. if (ref.isSymbolic())
  677. ref = ref.getLeaf();
  678. rev = resolveReflog(rw, ref, time);
  679. }
  680. i = m;
  681. } else
  682. throw new RevisionSyntaxException(revstr);
  683. break;
  684. case ':': {
  685. RevTree tree;
  686. if (rev == null) {
  687. if (name == null)
  688. name = new String(revChars, done, i);
  689. if (name.equals("")) //$NON-NLS-1$
  690. name = Constants.HEAD;
  691. rev = parseSimple(rw, name);
  692. name = null;
  693. }
  694. if (rev == null)
  695. return null;
  696. tree = rw.parseTree(rev);
  697. if (i == revChars.length - 1)
  698. return tree.copy();
  699. TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(),
  700. new String(revChars, i + 1, revChars.length - i - 1),
  701. tree);
  702. return tw != null ? tw.getObjectId(0) : null;
  703. }
  704. default:
  705. if (rev != null)
  706. throw new RevisionSyntaxException(revstr);
  707. }
  708. }
  709. if (rev != null)
  710. return rev.copy();
  711. if (name != null)
  712. return name;
  713. if (done == revstr.length())
  714. return null;
  715. name = revstr.substring(done);
  716. if (!Repository.isValidRefName("x/" + name)) //$NON-NLS-1$
  717. throw new RevisionSyntaxException(revstr);
  718. if (getRef(name) != null)
  719. return name;
  720. return resolveSimple(name);
  721. }
  722. private static boolean isHex(char c) {
  723. return ('0' <= c && c <= '9') //
  724. || ('a' <= c && c <= 'f') //
  725. || ('A' <= c && c <= 'F');
  726. }
  727. private static boolean isAllHex(String str, int ptr) {
  728. while (ptr < str.length()) {
  729. if (!isHex(str.charAt(ptr++)))
  730. return false;
  731. }
  732. return true;
  733. }
  734. @Nullable
  735. private RevObject parseSimple(RevWalk rw, String revstr) throws IOException {
  736. ObjectId id = resolveSimple(revstr);
  737. return id != null ? rw.parseAny(id) : null;
  738. }
  739. @Nullable
  740. private ObjectId resolveSimple(final String revstr) throws IOException {
  741. if (ObjectId.isId(revstr))
  742. return ObjectId.fromString(revstr);
  743. if (Repository.isValidRefName("x/" + revstr)) { //$NON-NLS-1$
  744. Ref r = getRefDatabase().getRef(revstr);
  745. if (r != null)
  746. return r.getObjectId();
  747. }
  748. if (AbbreviatedObjectId.isId(revstr))
  749. return resolveAbbreviation(revstr);
  750. int dashg = revstr.indexOf("-g"); //$NON-NLS-1$
  751. if ((dashg + 5) < revstr.length() && 0 <= dashg
  752. && isHex(revstr.charAt(dashg + 2))
  753. && isHex(revstr.charAt(dashg + 3))
  754. && isAllHex(revstr, dashg + 4)) {
  755. // Possibly output from git describe?
  756. String s = revstr.substring(dashg + 2);
  757. if (AbbreviatedObjectId.isId(s))
  758. return resolveAbbreviation(s);
  759. }
  760. return null;
  761. }
  762. @Nullable
  763. private String resolveReflogCheckout(int checkoutNo)
  764. throws IOException {
  765. ReflogReader reader = getReflogReader(Constants.HEAD);
  766. if (reader == null) {
  767. return null;
  768. }
  769. List<ReflogEntry> reflogEntries = reader.getReverseEntries();
  770. for (ReflogEntry entry : reflogEntries) {
  771. CheckoutEntry checkout = entry.parseCheckout();
  772. if (checkout != null)
  773. if (checkoutNo-- == 1)
  774. return checkout.getFromBranch();
  775. }
  776. return null;
  777. }
  778. private RevCommit resolveReflog(RevWalk rw, Ref ref, String time)
  779. throws IOException {
  780. int number;
  781. try {
  782. number = Integer.parseInt(time);
  783. } catch (NumberFormatException nfe) {
  784. throw new RevisionSyntaxException(MessageFormat.format(
  785. JGitText.get().invalidReflogRevision, time));
  786. }
  787. assert number >= 0;
  788. ReflogReader reader = getReflogReader(ref.getName());
  789. if (reader == null) {
  790. throw new RevisionSyntaxException(
  791. MessageFormat.format(JGitText.get().reflogEntryNotFound,
  792. Integer.valueOf(number), ref.getName()));
  793. }
  794. ReflogEntry entry = reader.getReverseEntry(number);
  795. if (entry == null)
  796. throw new RevisionSyntaxException(MessageFormat.format(
  797. JGitText.get().reflogEntryNotFound,
  798. Integer.valueOf(number), ref.getName()));
  799. return rw.parseCommit(entry.getNewId());
  800. }
  801. @Nullable
  802. private ObjectId resolveAbbreviation(final String revstr) throws IOException,
  803. AmbiguousObjectException {
  804. AbbreviatedObjectId id = AbbreviatedObjectId.fromString(revstr);
  805. try (ObjectReader reader = newObjectReader()) {
  806. Collection<ObjectId> matches = reader.resolve(id);
  807. if (matches.size() == 0)
  808. return null;
  809. else if (matches.size() == 1)
  810. return matches.iterator().next();
  811. else
  812. throw new AmbiguousObjectException(id, matches);
  813. }
  814. }
  815. /** Increment the use counter by one, requiring a matched {@link #close()}. */
  816. public void incrementOpen() {
  817. useCnt.incrementAndGet();
  818. }
  819. /** Decrement the use count, and maybe close resources. */
  820. public void close() {
  821. int newCount = useCnt.decrementAndGet();
  822. if (newCount == 0) {
  823. if (RepositoryCache.isCached(this)) {
  824. closedAt.set(System.currentTimeMillis());
  825. } else {
  826. doClose();
  827. }
  828. } else if (newCount == -1) {
  829. // should not happen, only log when useCnt became negative to
  830. // minimize number of log entries
  831. LOG.warn(JGitText.get().corruptUseCnt);
  832. if (LOG.isDebugEnabled()) {
  833. IllegalStateException e = new IllegalStateException();
  834. LOG.debug("", e); //$NON-NLS-1$
  835. }
  836. if (RepositoryCache.isCached(this)) {
  837. closedAt.set(System.currentTimeMillis());
  838. }
  839. }
  840. }
  841. /**
  842. * Invoked when the use count drops to zero during {@link #close()}.
  843. * <p>
  844. * The default implementation closes the object and ref databases.
  845. */
  846. protected void doClose() {
  847. getObjectDatabase().close();
  848. getRefDatabase().close();
  849. }
  850. @NonNull
  851. @SuppressWarnings("nls")
  852. public String toString() {
  853. String desc;
  854. File directory = getDirectory();
  855. if (directory != null)
  856. desc = directory.getPath();
  857. else
  858. desc = getClass().getSimpleName() + "-" //$NON-NLS-1$
  859. + System.identityHashCode(this);
  860. return "Repository[" + desc + "]"; //$NON-NLS-1$
  861. }
  862. /**
  863. * Get the name of the reference that {@code HEAD} points to.
  864. * <p>
  865. * This is essentially the same as doing:
  866. *
  867. * <pre>
  868. * return exactRef(Constants.HEAD).getTarget().getName()
  869. * </pre>
  870. *
  871. * Except when HEAD is detached, in which case this method returns the
  872. * current ObjectId in hexadecimal string format.
  873. *
  874. * @return name of current branch (for example {@code refs/heads/master}),
  875. * an ObjectId in hex format if the current branch is detached, or
  876. * {@code null} if the repository is corrupt and has no HEAD
  877. * reference.
  878. * @throws IOException
  879. */
  880. @Nullable
  881. public String getFullBranch() throws IOException {
  882. Ref head = exactRef(Constants.HEAD);
  883. if (head == null) {
  884. return null;
  885. }
  886. if (head.isSymbolic()) {
  887. return head.getTarget().getName();
  888. }
  889. ObjectId objectId = head.getObjectId();
  890. if (objectId != null) {
  891. return objectId.name();
  892. }
  893. return null;
  894. }
  895. /**
  896. * Get the short name of the current branch that {@code HEAD} points to.
  897. * <p>
  898. * This is essentially the same as {@link #getFullBranch()}, except the
  899. * leading prefix {@code refs/heads/} is removed from the reference before
  900. * it is returned to the caller.
  901. *
  902. * @return name of current branch (for example {@code master}), an ObjectId
  903. * in hex format if the current branch is detached, or {@code null}
  904. * if the repository is corrupt and has no HEAD reference.
  905. * @throws IOException
  906. */
  907. @Nullable
  908. public String getBranch() throws IOException {
  909. String name = getFullBranch();
  910. if (name != null)
  911. return shortenRefName(name);
  912. return null;
  913. }
  914. /**
  915. * Objects known to exist but not expressed by {@link #getAllRefs()}.
  916. * <p>
  917. * When a repository borrows objects from another repository, it can
  918. * advertise that it safely has that other repository's references, without
  919. * exposing any other details about the other repository. This may help
  920. * a client trying to push changes avoid pushing more than it needs to.
  921. *
  922. * @return unmodifiable collection of other known objects.
  923. */
  924. @NonNull
  925. public Set<ObjectId> getAdditionalHaves() {
  926. return Collections.emptySet();
  927. }
  928. /**
  929. * Get a ref by name.
  930. *
  931. * @param name
  932. * the name of the ref to lookup. May be a short-hand form, e.g.
  933. * "master" which is is automatically expanded to
  934. * "refs/heads/master" if "refs/heads/master" already exists.
  935. * @return the Ref with the given name, or {@code null} if it does not exist
  936. * @throws IOException
  937. * @deprecated Use {@link #exactRef(String)} or {@link #findRef(String)}
  938. * instead.
  939. */
  940. @Deprecated
  941. @Nullable
  942. public Ref getRef(final String name) throws IOException {
  943. return findRef(name);
  944. }
  945. /**
  946. * Get a ref by name.
  947. *
  948. * @param name
  949. * the name of the ref to lookup. Must not be a short-hand
  950. * form; e.g., "master" is not automatically expanded to
  951. * "refs/heads/master".
  952. * @return the Ref with the given name, or {@code null} if it does not exist
  953. * @throws IOException
  954. * @since 4.2
  955. */
  956. @Nullable
  957. public Ref exactRef(String name) throws IOException {
  958. return getRefDatabase().exactRef(name);
  959. }
  960. /**
  961. * Search for a ref by (possibly abbreviated) name.
  962. *
  963. * @param name
  964. * the name of the ref to lookup. May be a short-hand form, e.g.
  965. * "master" which is is automatically expanded to
  966. * "refs/heads/master" if "refs/heads/master" already exists.
  967. * @return the Ref with the given name, or {@code null} if it does not exist
  968. * @throws IOException
  969. * @since 4.2
  970. */
  971. @Nullable
  972. public Ref findRef(String name) throws IOException {
  973. return getRefDatabase().getRef(name);
  974. }
  975. /**
  976. * @return mutable map of all known refs (heads, tags, remotes).
  977. */
  978. @NonNull
  979. public Map<String, Ref> getAllRefs() {
  980. try {
  981. return getRefDatabase().getRefs(RefDatabase.ALL);
  982. } catch (IOException e) {
  983. return new HashMap<String, Ref>();
  984. }
  985. }
  986. /**
  987. * @return mutable map of all tags; key is short tag name ("v1.0") and value
  988. * of the entry contains the ref with the full tag name
  989. * ("refs/tags/v1.0").
  990. */
  991. @NonNull
  992. public Map<String, Ref> getTags() {
  993. try {
  994. return getRefDatabase().getRefs(Constants.R_TAGS);
  995. } catch (IOException e) {
  996. return new HashMap<String, Ref>();
  997. }
  998. }
  999. /**
  1000. * Peel a possibly unpeeled reference to an annotated tag.
  1001. * <p>
  1002. * If the ref cannot be peeled (as it does not refer to an annotated tag)
  1003. * the peeled id stays null, but {@link Ref#isPeeled()} will be true.
  1004. *
  1005. * @param ref
  1006. * The ref to peel
  1007. * @return <code>ref</code> if <code>ref.isPeeled()</code> is true; else a
  1008. * new Ref object representing the same data as Ref, but isPeeled()
  1009. * will be true and getPeeledObjectId will contain the peeled object
  1010. * (or null).
  1011. */
  1012. @NonNull
  1013. public Ref peel(final Ref ref) {
  1014. try {
  1015. return getRefDatabase().peel(ref);
  1016. } catch (IOException e) {
  1017. // Historical accident; if the reference cannot be peeled due
  1018. // to some sort of repository access problem we claim that the
  1019. // same as if the reference was not an annotated tag.
  1020. return ref;
  1021. }
  1022. }
  1023. /**
  1024. * @return a map with all objects referenced by a peeled ref.
  1025. */
  1026. @NonNull
  1027. public Map<AnyObjectId, Set<Ref>> getAllRefsByPeeledObjectId() {
  1028. Map<String, Ref> allRefs = getAllRefs();
  1029. Map<AnyObjectId, Set<Ref>> ret = new HashMap<AnyObjectId, Set<Ref>>(allRefs.size());
  1030. for (Ref ref : allRefs.values()) {
  1031. ref = peel(ref);
  1032. AnyObjectId target = ref.getPeeledObjectId();
  1033. if (target == null)
  1034. target = ref.getObjectId();
  1035. // We assume most Sets here are singletons
  1036. Set<Ref> oset = ret.put(target, Collections.singleton(ref));
  1037. if (oset != null) {
  1038. // that was not the case (rare)
  1039. if (oset.size() == 1) {
  1040. // Was a read-only singleton, we must copy to a new Set
  1041. oset = new HashSet<Ref>(oset);
  1042. }
  1043. ret.put(target, oset);
  1044. oset.add(ref);
  1045. }
  1046. }
  1047. return ret;
  1048. }
  1049. /**
  1050. * @return the index file location or {@code null} if repository isn't
  1051. * local.
  1052. * @throws NoWorkTreeException
  1053. * if this is bare, which implies it has no working directory.
  1054. * See {@link #isBare()}.
  1055. */
  1056. @NonNull
  1057. public File getIndexFile() throws NoWorkTreeException {
  1058. if (isBare())
  1059. throw new NoWorkTreeException();
  1060. return indexFile;
  1061. }
  1062. /**
  1063. * Create a new in-core index representation and read an index from disk.
  1064. * <p>
  1065. * The new index will be read before it is returned to the caller. Read
  1066. * failures are reported as exceptions and therefore prevent the method from
  1067. * returning a partially populated index.
  1068. *
  1069. * @return a cache representing the contents of the specified index file (if
  1070. * it exists) or an empty cache if the file does not exist.
  1071. * @throws NoWorkTreeException
  1072. * if this is bare, which implies it has no working directory.
  1073. * See {@link #isBare()}.
  1074. * @throws IOException
  1075. * the index file is present but could not be read.
  1076. * @throws CorruptObjectException
  1077. * the index file is using a format or extension that this
  1078. * library does not support.
  1079. */
  1080. @NonNull
  1081. public DirCache readDirCache() throws NoWorkTreeException,
  1082. CorruptObjectException, IOException {
  1083. return DirCache.read(this);
  1084. }
  1085. /**
  1086. * Create a new in-core index representation, lock it, and read from disk.
  1087. * <p>
  1088. * The new index will be locked and then read before it is returned to the
  1089. * caller. Read failures are reported as exceptions and therefore prevent
  1090. * the method from returning a partially populated index.
  1091. *
  1092. * @return a cache representing the contents of the specified index file (if
  1093. * it exists) or an empty cache if the file does not exist.
  1094. * @throws NoWorkTreeException
  1095. * if this is bare, which implies it has no working directory.
  1096. * See {@link #isBare()}.
  1097. * @throws IOException
  1098. * the index file is present but could not be read, or the lock
  1099. * could not be obtained.
  1100. * @throws CorruptObjectException
  1101. * the index file is using a format or extension that this
  1102. * library does not support.
  1103. */
  1104. @NonNull
  1105. public DirCache lockDirCache() throws NoWorkTreeException,
  1106. CorruptObjectException, IOException {
  1107. // we want DirCache to inform us so that we can inform registered
  1108. // listeners about index changes
  1109. IndexChangedListener l = new IndexChangedListener() {
  1110. public void onIndexChanged(IndexChangedEvent event) {
  1111. notifyIndexChanged();
  1112. }
  1113. };
  1114. return DirCache.lock(this, l);
  1115. }
  1116. static byte[] gitInternalSlash(byte[] bytes) {
  1117. if (File.separatorChar == '/')
  1118. return bytes;
  1119. for (int i=0; i<bytes.length; ++i)
  1120. if (bytes[i] == File.separatorChar)
  1121. bytes[i] = '/';
  1122. return bytes;
  1123. }
  1124. /**
  1125. * @return an important state
  1126. */
  1127. @NonNull
  1128. public RepositoryState getRepositoryState() {
  1129. if (isBare() || getDirectory() == null)
  1130. return RepositoryState.BARE;
  1131. // Pre Git-1.6 logic
  1132. if (new File(getWorkTree(), ".dotest").exists()) //$NON-NLS-1$
  1133. return RepositoryState.REBASING;
  1134. if (new File(getDirectory(), ".dotest-merge").exists()) //$NON-NLS-1$
  1135. return RepositoryState.REBASING_INTERACTIVE;
  1136. // From 1.6 onwards
  1137. if (new File(getDirectory(),"rebase-apply/rebasing").exists()) //$NON-NLS-1$
  1138. return RepositoryState.REBASING_REBASING;
  1139. if (new File(getDirectory(),"rebase-apply/applying").exists()) //$NON-NLS-1$
  1140. return RepositoryState.APPLY;
  1141. if (new File(getDirectory(),"rebase-apply").exists()) //$NON-NLS-1$
  1142. return RepositoryState.REBASING;
  1143. if (new File(getDirectory(),"rebase-merge/interactive").exists()) //$NON-NLS-1$
  1144. return RepositoryState.REBASING_INTERACTIVE;
  1145. if (new File(getDirectory(),"rebase-merge").exists()) //$NON-NLS-1$
  1146. return RepositoryState.REBASING_MERGE;
  1147. // Both versions
  1148. if (new File(getDirectory(), Constants.MERGE_HEAD).exists()) {
  1149. // we are merging - now check whether we have unmerged paths
  1150. try {
  1151. if (!readDirCache().hasUnmergedPaths()) {
  1152. // no unmerged paths -> return the MERGING_RESOLVED state
  1153. return RepositoryState.MERGING_RESOLVED;
  1154. }
  1155. } catch (IOException e) {
  1156. // Can't decide whether unmerged paths exists. Return
  1157. // MERGING state to be on the safe side (in state MERGING
  1158. // you are not allow to do anything)
  1159. }
  1160. return RepositoryState.MERGING;
  1161. }
  1162. if (new File(getDirectory(), "BISECT_LOG").exists()) //$NON-NLS-1$
  1163. return RepositoryState.BISECTING;
  1164. if (new File(getDirectory(), Constants.CHERRY_PICK_HEAD).exists()) {
  1165. try {
  1166. if (!readDirCache().hasUnmergedPaths()) {
  1167. // no unmerged paths
  1168. return RepositoryState.CHERRY_PICKING_RESOLVED;
  1169. }
  1170. } catch (IOException e) {
  1171. // fall through to CHERRY_PICKING
  1172. }
  1173. return RepositoryState.CHERRY_PICKING;
  1174. }
  1175. if (new File(getDirectory(), Constants.REVERT_HEAD).exists()) {
  1176. try {
  1177. if (!readDirCache().hasUnmergedPaths()) {
  1178. // no unmerged paths
  1179. return RepositoryState.REVERTING_RESOLVED;
  1180. }
  1181. } catch (IOException e) {
  1182. // fall through to REVERTING
  1183. }
  1184. return RepositoryState.REVERTING;
  1185. }
  1186. return RepositoryState.SAFE;
  1187. }
  1188. /**
  1189. * Check validity of a ref name. It must not contain character that has
  1190. * a special meaning in a Git object reference expression. Some other
  1191. * dangerous characters are also excluded.
  1192. *
  1193. * For portability reasons '\' is excluded
  1194. *
  1195. * @param refName
  1196. *
  1197. * @return true if refName is a valid ref name
  1198. */
  1199. public static boolean isValidRefName(final String refName) {
  1200. final int len = refName.length();
  1201. if (len == 0)
  1202. return false;
  1203. if (refName.endsWith(".lock")) //$NON-NLS-1$
  1204. return false;
  1205. // Refs may be stored as loose files so invalid paths
  1206. // on the local system must also be invalid refs.
  1207. try {
  1208. SystemReader.getInstance().checkPath(refName);
  1209. } catch (CorruptObjectException e) {
  1210. return false;
  1211. }
  1212. int components = 1;
  1213. char p = '\0';
  1214. for (int i = 0; i < len; i++) {
  1215. final char c = refName.charAt(i);
  1216. if (c <= ' ')
  1217. return false;
  1218. switch (c) {
  1219. case '.':
  1220. switch (p) {
  1221. case '\0': case '/': case '.':
  1222. return false;
  1223. }
  1224. if (i == len -1)
  1225. return false;
  1226. break;
  1227. case '/':
  1228. if (i == 0 || i == len - 1)
  1229. return false;
  1230. if (p == '/')
  1231. return false;
  1232. components++;
  1233. break;
  1234. case '{':
  1235. if (p == '@')
  1236. return false;
  1237. break;
  1238. case '~': case '^': case ':':
  1239. case '?': case '[': case '*':
  1240. case '\\':
  1241. case '\u007F':
  1242. return false;
  1243. }
  1244. p = c;
  1245. }
  1246. return components > 1;
  1247. }
  1248. /**
  1249. * Strip work dir and return normalized repository path.
  1250. *
  1251. * @param workDir Work dir
  1252. * @param file File whose path shall be stripped of its workdir
  1253. * @return normalized repository relative path or the empty
  1254. * string if the file is not relative to the work directory.
  1255. */
  1256. @NonNull
  1257. public static String stripWorkDir(File workDir, File file) {
  1258. final String filePath = file.getPath();
  1259. final String workDirPath = workDir.getPath();
  1260. if (filePath.length() <= workDirPath.length() ||
  1261. filePath.charAt(workDirPath.length()) != File.separatorChar ||
  1262. !filePath.startsWith(workDirPath)) {
  1263. File absWd = workDir.isAbsolute() ? workDir : workDir.getAbsoluteFile();
  1264. File absFile = file.isAbsolute() ? file : file.getAbsoluteFile();
  1265. if (absWd == workDir && absFile == file)
  1266. return ""; //$NON-NLS-1$
  1267. return stripWorkDir(absWd, absFile);
  1268. }
  1269. String relName = filePath.substring(workDirPath.length() + 1);
  1270. if (File.separatorChar != '/')
  1271. relName = relName.replace(File.separatorChar, '/');
  1272. return relName;
  1273. }
  1274. /**
  1275. * @return true if this is bare, which implies it has no working directory.
  1276. */
  1277. public boolean isBare() {
  1278. return workTree == null;
  1279. }
  1280. /**
  1281. * @return the root directory of the working tree, where files are checked
  1282. * out for viewing and editing.
  1283. * @throws NoWorkTreeException
  1284. * if this is bare, which implies it has no working directory.
  1285. * See {@link #isBare()}.
  1286. */
  1287. @NonNull
  1288. public File getWorkTree() throws NoWorkTreeException {
  1289. if (isBare())
  1290. throw new NoWorkTreeException();
  1291. return workTree;
  1292. }
  1293. /**
  1294. * Force a scan for changed refs.
  1295. *
  1296. * @throws IOException
  1297. */
  1298. public abstract void scanForRepoChanges() throws IOException;
  1299. /**
  1300. * Notify that the index changed
  1301. */
  1302. public abstract void notifyIndexChanged();
  1303. /**
  1304. * @param refName
  1305. *
  1306. * @return a more user friendly ref name
  1307. */
  1308. @NonNull
  1309. public static String shortenRefName(String refName) {
  1310. if (refName.startsWith(Constants.R_HEADS))
  1311. return refName.substring(Constants.R_HEADS.length());
  1312. if (refName.startsWith(Constants.R_TAGS))
  1313. return refName.substring(Constants.R_TAGS.length());
  1314. if (refName.startsWith(Constants.R_REMOTES))
  1315. return refName.substring(Constants.R_REMOTES.length());
  1316. return refName;
  1317. }
  1318. /**
  1319. * @param refName
  1320. * @return the remote branch name part of <code>refName</code>, i.e. without
  1321. * the <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1322. * <code>refName</code> represents a remote tracking branch;
  1323. * otherwise {@code null}.
  1324. * @since 3.4
  1325. */
  1326. @Nullable
  1327. public String shortenRemoteBranchName(String refName) {
  1328. for (String remote : getRemoteNames()) {
  1329. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1330. if (refName.startsWith(remotePrefix))
  1331. return refName.substring(remotePrefix.length());
  1332. }
  1333. return null;
  1334. }
  1335. /**
  1336. * @param refName
  1337. * @return the remote name part of <code>refName</code>, i.e. without the
  1338. * <code>refs/remotes/&lt;remote&gt;</code> prefix, if
  1339. * <code>refName</code> represents a remote tracking branch;
  1340. * otherwise {@code null}.
  1341. * @since 3.4
  1342. */
  1343. @Nullable
  1344. public String getRemoteName(String refName) {
  1345. for (String remote : getRemoteNames()) {
  1346. String remotePrefix = Constants.R_REMOTES + remote + "/"; //$NON-NLS-1$
  1347. if (refName.startsWith(remotePrefix))
  1348. return remote;
  1349. }
  1350. return null;
  1351. }
  1352. /**
  1353. * @param refName
  1354. * @return a {@link ReflogReader} for the supplied refname, or {@code null}
  1355. * if the named ref does not exist.
  1356. * @throws IOException
  1357. * the ref could not be accessed.
  1358. * @since 3.0
  1359. */
  1360. @Nullable
  1361. public abstract ReflogReader getReflogReader(String refName)
  1362. throws IOException;
  1363. /**
  1364. * Return the information stored in the file $GIT_DIR/MERGE_MSG. In this
  1365. * file operations triggering a merge will store a template for the commit
  1366. * message of the merge commit.
  1367. *
  1368. * @return a String containing the content of the MERGE_MSG file or
  1369. * {@code null} if this file doesn't exist
  1370. * @throws IOException
  1371. * @throws NoWorkTreeException
  1372. * if this is bare, which implies it has no working directory.
  1373. * See {@link #isBare()}.
  1374. */
  1375. @Nullable
  1376. public String readMergeCommitMsg() throws IOException, NoWorkTreeException {
  1377. return readCommitMsgFile(Constants.MERGE_MSG);
  1378. }
  1379. /**
  1380. * Write new content to the file $GIT_DIR/MERGE_MSG. In this file operations
  1381. * triggering a merge will store a template for the commit message of the
  1382. * merge commit. If <code>null</code> is specified as message the file will
  1383. * be deleted.
  1384. *
  1385. * @param msg
  1386. * the message which should be written or <code>null</code> to
  1387. * delete the file
  1388. *
  1389. * @throws IOException
  1390. */
  1391. public void writeMergeCommitMsg(String msg) throws IOException {
  1392. File mergeMsgFile = new File(gitDir, Constants.MERGE_MSG);
  1393. writeCommitMsg(mergeMsgFile, msg);
  1394. }
  1395. /**
  1396. * Return the information stored in the file $GIT_DIR/COMMIT_EDITMSG. In
  1397. * this file hooks triggered by an operation may read or modify the current
  1398. * commit message.
  1399. *
  1400. * @return a String containing the content of the COMMIT_EDITMSG file or
  1401. * {@code null} if this file doesn't exist
  1402. * @throws IOException
  1403. * @throws NoWorkTreeException
  1404. * if this is bare, which implies it has no working directory.
  1405. * See {@link #isBare()}.
  1406. * @since 4.0
  1407. */
  1408. @Nullable
  1409. public String readCommitEditMsg() throws IOException, NoWorkTreeException {
  1410. return readCommitMsgFile(Constants.COMMIT_EDITMSG);
  1411. }
  1412. /**
  1413. * Write new content to the file $GIT_DIR/COMMIT_EDITMSG. In this file hooks
  1414. * triggered by an operation may read or modify the current commit message.
  1415. * If {@code null} is specified as message the file will be deleted.
  1416. *
  1417. * @param msg
  1418. * the message which should be written or {@code null} to delete
  1419. * the file
  1420. *
  1421. * @throws IOException
  1422. * @since 4.0
  1423. */
  1424. public void writeCommitEditMsg(String msg) throws IOException {
  1425. File commiEditMsgFile = new File(gitDir, Constants.COMMIT_EDITMSG);
  1426. writeCommitMsg(commiEditMsgFile, msg);
  1427. }
  1428. /**
  1429. * Return the information stored in the file $GIT_DIR/MERGE_HEAD. In this
  1430. * file operations triggering a merge will store the IDs of all heads which
  1431. * should be merged together with HEAD.
  1432. *
  1433. * @return a list of commits which IDs are listed in the MERGE_HEAD file or
  1434. * {@code null} if this file doesn't exist. Also if the file exists
  1435. * but is empty {@code null} will be returned
  1436. * @throws IOException
  1437. * @throws NoWorkTreeException
  1438. * if this is bare, which implies it has no working directory.
  1439. * See {@link #isBare()}.
  1440. */
  1441. @Nullable
  1442. public List<ObjectId> readMergeHeads() throws IOException, NoWorkTreeException {
  1443. if (isBare() || getDirectory() == null)
  1444. throw new NoWorkTreeException();
  1445. byte[] raw = readGitDirectoryFile(Constants.MERGE_HEAD);
  1446. if (raw == null)
  1447. return null;
  1448. LinkedList<ObjectId> heads = new LinkedList<ObjectId>();
  1449. for (int p = 0; p < raw.length;) {
  1450. heads.add(ObjectId.fromString(raw, p));
  1451. p = RawParseUtils
  1452. .nextLF(raw, p + Constants.OBJECT_ID_STRING_LENGTH);
  1453. }
  1454. return heads;
  1455. }
  1456. /**
  1457. * Write new merge-heads into $GIT_DIR/MERGE_HEAD. In this file operations
  1458. * triggering a merge will store the IDs of all heads which should be merged
  1459. * together with HEAD. If <code>null</code> is specified as list of commits
  1460. * the file will be deleted
  1461. *
  1462. * @param heads
  1463. * a list of commits which IDs should be written to
  1464. * $GIT_DIR/MERGE_HEAD or <code>null</code> to delete the file
  1465. * @throws IOException
  1466. */
  1467. public void writeMergeHeads(List<? extends ObjectId> heads) throws IOException {
  1468. writeHeadsFile(heads, Constants.MERGE_HEAD);
  1469. }
  1470. /**
  1471. * Return the information stored in the file $GIT_DIR/CHERRY_PICK_HEAD.
  1472. *
  1473. * @return object id from CHERRY_PICK_HEAD file or {@code null} if this file
  1474. * doesn't exist. Also if the file exists but is empty {@code null}
  1475. * will be returned
  1476. * @throws IOException
  1477. * @throws NoWorkTreeException
  1478. * if this is bare, which implies it has no working directory.
  1479. * See {@link #isBare()}.
  1480. */
  1481. @Nullable
  1482. public ObjectId readCherryPickHead() throws IOException,
  1483. NoWorkTreeException {
  1484. if (isBare() || getDirectory() == null)
  1485. throw new NoWorkTreeException();
  1486. byte[] raw = readGitDirectoryFile(Constants.CHERRY_PICK_HEAD);
  1487. if (raw == null)
  1488. return null;
  1489. return ObjectId.fromString(raw, 0);
  1490. }
  1491. /**
  1492. * Return the information stored in the file $GIT_DIR/REVERT_HEAD.
  1493. *
  1494. * @return object id from REVERT_HEAD file or {@code null} if this file
  1495. * doesn't exist. Also if the file exists but is empty {@code null}
  1496. * will be returned
  1497. * @throws IOException
  1498. * @throws NoWorkTreeException
  1499. * if this is bare, which implies it has no working directory.
  1500. * See {@link #isBare()}.
  1501. */
  1502. @Nullable
  1503. public ObjectId readRevertHead() throws IOException, NoWorkTreeException {
  1504. if (isBare() || getDirectory() == null)
  1505. throw new NoWorkTreeException();
  1506. byte[] raw = readGitDirectoryFile(Constants.REVERT_HEAD);
  1507. if (raw == null)
  1508. return null;
  1509. return ObjectId.fromString(raw, 0);
  1510. }
  1511. /**
  1512. * Write cherry pick commit into $GIT_DIR/CHERRY_PICK_HEAD. This is used in
  1513. * case of conflicts to store the cherry which was tried to be picked.
  1514. *
  1515. * @param head
  1516. * an object id of the cherry commit or <code>null</code> to
  1517. * delete the file
  1518. * @throws IOException
  1519. */
  1520. public void writeCherryPickHead(ObjectId head) throws IOException {
  1521. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1522. : null;
  1523. writeHeadsFile(heads, Constants.CHERRY_PICK_HEAD);
  1524. }
  1525. /**
  1526. * Write revert commit into $GIT_DIR/REVERT_HEAD. This is used in case of
  1527. * conflicts to store the revert which was tried to be picked.
  1528. *
  1529. * @param head
  1530. * an object id of the revert commit or <code>null</code> to
  1531. * delete the file
  1532. * @throws IOException
  1533. */
  1534. public void writeRevertHead(ObjectId head) throws IOException {
  1535. List<ObjectId> heads = (head != null) ? Collections.singletonList(head)
  1536. : null;
  1537. writeHeadsFile(heads, Constants.REVERT_HEAD);
  1538. }
  1539. /**
  1540. * Write original HEAD commit into $GIT_DIR/ORIG_HEAD.
  1541. *
  1542. * @param head
  1543. * an object id of the original HEAD commit or <code>null</code>
  1544. * to delete the file
  1545. * @throws IOException
  1546. */
  1547. public void writeOrigHead(ObjectId head) throws IOException {
  1548. List<ObjectId> heads = head != null ? Collections.singletonList(head)
  1549. : null;
  1550. writeHeadsFile(heads, Constants.ORIG_HEAD);
  1551. }
  1552. /**
  1553. * Return the information stored in the file $GIT_DIR/ORIG_HEAD.
  1554. *
  1555. * @return object id from ORIG_HEAD file or {@code null} if this file
  1556. * doesn't exist. Also if the file exists but is empty {@code null}
  1557. * will be returned
  1558. * @throws IOException
  1559. * @throws NoWorkTreeException
  1560. * if this is bare, which implies it has no working directory.
  1561. * See {@link #isBare()}.
  1562. */
  1563. @Nullable
  1564. public ObjectId readOrigHead() throws IOException, NoWorkTreeException {
  1565. if (isBare() || getDirectory() == null)
  1566. throw new NoWorkTreeException();
  1567. byte[] raw = readGitDirectoryFile(Constants.ORIG_HEAD);
  1568. return raw != null ? ObjectId.fromString(raw, 0) : null;
  1569. }
  1570. /**
  1571. * Return the information stored in the file $GIT_DIR/SQUASH_MSG. In this
  1572. * file operations triggering a squashed merge will store a template for the
  1573. * commit message of the squash commit.
  1574. *
  1575. * @return a String containing the content of the SQUASH_MSG file or
  1576. * {@code null} if this file doesn't exist
  1577. * @throws IOException
  1578. * @throws NoWorkTreeException
  1579. * if this is bare, which implies it has no working directory.
  1580. * See {@link #isBare()}.
  1581. */
  1582. @Nullable
  1583. public String readSquashCommitMsg() throws IOException {
  1584. return readCommitMsgFile(Constants.SQUASH_MSG);
  1585. }
  1586. /**
  1587. * Write new content to the file $GIT_DIR/SQUASH_MSG. In this file
  1588. * operations triggering a squashed merge will store a template for the
  1589. * commit message of the squash commit. If <code>null</code> is specified as
  1590. * message the file will be deleted.
  1591. *
  1592. * @param msg
  1593. * the message which should be written or <code>null</code> to
  1594. * delete the file
  1595. *
  1596. * @throws IOException
  1597. */
  1598. public void writeSquashCommitMsg(String msg) throws IOException {
  1599. File squashMsgFile = new File(gitDir, Constants.SQUASH_MSG);
  1600. writeCommitMsg(squashMsgFile, msg);
  1601. }
  1602. @Nullable
  1603. private String readCommitMsgFile(String msgFilename) throws IOException {
  1604. if (isBare() || getDirectory() == null)
  1605. throw new NoWorkTreeException();
  1606. File mergeMsgFile = new File(getDirectory(), msgFilename);
  1607. try {
  1608. return RawParseUtils.decode(IO.readFully(mergeMsgFile));
  1609. } catch (FileNotFoundException e) {
  1610. if (mergeMsgFile.exists()) {
  1611. throw e;
  1612. }
  1613. // the file has disappeared in the meantime ignore it
  1614. return null;
  1615. }
  1616. }
  1617. private void writeCommitMsg(File msgFile, String msg) throws IOException {
  1618. if (msg != null) {
  1619. FileOutputStream fos = new FileOutputStream(msgFile);
  1620. try {
  1621. fos.write(msg.getBytes(Constants.CHARACTER_ENCODING));
  1622. } finally {
  1623. fos.close();
  1624. }
  1625. } else {
  1626. FileUtils.delete(msgFile, FileUtils.SKIP_MISSING);
  1627. }
  1628. }
  1629. /**
  1630. * Read a file from the git directory.
  1631. *
  1632. * @param filename
  1633. * @return the raw contents or {@code null} if the file doesn't exist or is
  1634. * empty
  1635. * @throws IOException
  1636. */
  1637. @Nullable
  1638. private byte[] readGitDirectoryFile(String filename) throws IOException {
  1639. File file = new File(getDirectory(), filename);
  1640. try {
  1641. byte[] raw = IO.readFully(file);
  1642. return raw.length > 0 ? raw : null;
  1643. } catch (FileNotFoundException notFound) {
  1644. if (file.exists()) {
  1645. throw notFound;
  1646. }
  1647. return null;
  1648. }
  1649. }
  1650. /**
  1651. * Write the given heads to a file in the git directory.
  1652. *
  1653. * @param heads
  1654. * a list of object ids to write or null if the file should be
  1655. * deleted.
  1656. * @param filename
  1657. * @throws FileNotFoundException
  1658. * @throws IOException
  1659. */
  1660. private void writeHeadsFile(List<? extends ObjectId> heads, String filename)
  1661. throws FileNotFoundException, IOException {
  1662. File headsFile = new File(getDirectory(), filename);
  1663. if (heads != null) {
  1664. BufferedOutputStream bos = new SafeBufferedOutputStream(
  1665. new FileOutputStream(headsFile));
  1666. try {
  1667. for (ObjectId id : heads) {
  1668. id.copyTo(bos);
  1669. bos.write('\n');
  1670. }
  1671. } finally {
  1672. bos.close();
  1673. }
  1674. } else {
  1675. FileUtils.delete(headsFile, FileUtils.SKIP_MISSING);
  1676. }
  1677. }
  1678. /**
  1679. * Read a file formatted like the git-rebase-todo file. The "done" file is
  1680. * also formatted like the git-rebase-todo file. These files can be found in
  1681. * .git/rebase-merge/ or .git/rebase-append/ folders.
  1682. *
  1683. * @param path
  1684. * path to the file relative to the repository's git-dir. E.g.
  1685. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1686. * @param includeComments
  1687. * <code>true</code> if also comments should be reported
  1688. * @return the list of steps
  1689. * @throws IOException
  1690. * @since 3.2
  1691. */
  1692. @NonNull
  1693. public List<RebaseTodoLine> readRebaseTodo(String path,
  1694. boolean includeComments)
  1695. throws IOException {
  1696. return new RebaseTodoFile(this).readRebaseTodo(path, includeComments);
  1697. }
  1698. /**
  1699. * Write a file formatted like a git-rebase-todo file.
  1700. *
  1701. * @param path
  1702. * path to the file relative to the repository's git-dir. E.g.
  1703. * "rebase-merge/git-rebase-todo" or "rebase-append/done"
  1704. * @param steps
  1705. * the steps to be written
  1706. * @param append
  1707. * whether to append to an existing file or to write a new file
  1708. * @throws IOException
  1709. * @since 3.2
  1710. */
  1711. public void writeRebaseTodoFile(String path, List<RebaseTodoLine> steps,
  1712. boolean append)
  1713. throws IOException {
  1714. new RebaseTodoFile(this).writeRebaseTodoFile(path, steps, append);
  1715. }
  1716. /**
  1717. * @return the names of all known remotes
  1718. * @since 3.4
  1719. */
  1720. @NonNull
  1721. public Set<String> getRemoteNames() {
  1722. return getConfig()
  1723. .getSubsections(ConfigConstants.CONFIG_REMOTE_SECTION);
  1724. }
  1725. }